1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *
21  *   Bacula Director -- User Agent Server
22  *
23  *     Kern Sibbald, September MM
24  *
25  */
26 
27 #include "bacula.h"
28 #include "dird.h"
29 
30 /* Imported variables */
31 
32 
33 /* Forward referenced functions */
34 extern "C" void *connect_thread(void *arg);
35 static void *handle_UA_client_request(void *arg);
36 
37 
38 /* Global variables */
39 static bool started = false;
40 static pthread_t server_tid;
41 static bool server_tid_valid = false;
42 static workq_t ua_workq;
43 
44 struct s_addr_port {
45    char *addr;
46    char *port;
47 };
48 
49 /* Called here by Director daemon to start UA (user agent)
50  * command thread. This routine creates the thread and then
51  * returns.
52  */
start_UA_server(dlist * addrs)53 void start_UA_server(dlist *addrs)
54 {
55    pthread_t thid;
56    int status;
57    static dlist *myaddrs = addrs;
58 
59    if ((status=pthread_create(&thid, NULL, connect_thread, (void *)myaddrs)) != 0) {
60       berrno be;
61       Emsg1(M_ABORT, 0, _("Cannot create UA thread: %s\n"), be.bstrerror(status));
62    }
63    started = true;
64    return;
65 }
66 
stop_UA_server()67 void stop_UA_server()
68 {
69    if (started && server_tid_valid) {
70       server_tid_valid = false;
71       bnet_stop_thread_server(server_tid);
72    }
73 }
74 
75 extern "C"
connect_thread(void * arg)76 void *connect_thread(void *arg)
77 {
78    pthread_detach(pthread_self());
79    set_jcr_in_tsd(INVALID_JCR);
80 
81    server_tid = pthread_self();
82    server_tid_valid = true;
83 
84    /* Permit MaxConsoleConnect console connections */
85    bnet_thread_server((dlist*)arg, director->MaxConsoleConnect, &ua_workq, handle_UA_client_request);
86    return NULL;
87 }
88 
89 /*
90  * Create a Job Control Record for a control "job",
91  *   filling in all the appropriate fields.
92  */
new_control_jcr(const char * base_name,int job_type)93 JCR *new_control_jcr(const char *base_name, int job_type)
94 {
95    JCR *jcr;
96    jcr = new_jcr(sizeof(JCR), dird_free_jcr);
97    /*
98     * The job and defaults are not really used, but
99     *  we set them up to ensure that everything is correctly
100     *  initialized.
101     */
102    LockRes();
103    jcr->job = (JOB *)GetNextRes(R_JOB, NULL);
104    set_jcr_defaults(jcr, jcr->job);
105    /* We use a resource, so we should count in the reload */
106    jcr->setJobType(job_type);
107    UnlockRes();
108 
109    jcr->sd_auth_key = bstrdup("dummy"); /* dummy Storage daemon key */
110    create_unique_job_name(jcr, base_name);
111    jcr->sched_time = jcr->start_time;
112    jcr->setJobLevel(L_NONE);
113    jcr->setJobStatus(JS_Running);
114    jcr->JobId = 0;
115    return jcr;
116 }
117 
118 /*
119  * Handle Director User Agent commands
120  *
121  */
handle_UA_client_request(void * arg)122 static void *handle_UA_client_request(void *arg)
123 {
124    int stat;
125    UAContext *ua;
126    JCR *jcr;
127    BSOCK *user = (BSOCK *)arg;
128 
129    pthread_detach(pthread_self());
130 
131    jcr = new_control_jcr("-Console-", JT_CONSOLE);
132 
133    ua = new_ua_context(jcr);
134    ua->UA_sock = user;
135    set_jcr_in_tsd(INVALID_JCR);
136 
137    user->recv();             /* Get first message */
138    if (!authenticate_user_agent(ua)) {
139       goto getout;
140    }
141 
142    while (!ua->quit) {
143       if (ua->api) user->signal(BNET_MAIN_PROMPT);
144       stat = user->recv();
145       if (stat >= 0) {
146          pm_strcpy(ua->cmd, ua->UA_sock->msg);
147          parse_ua_args(ua);
148          if (ua->argc > 0 && ua->argk[0][0] == '.') {
149             do_a_dot_command(ua);
150          } else {
151             do_a_command(ua);
152          }
153          dequeue_messages(ua->jcr);
154          if (!ua->quit) {
155             if (console_msg_pending && acl_access_ok(ua, Command_ACL, "messages", 8)) {
156                if (ua->auto_display_messages) {
157                   pm_strcpy(ua->cmd, "messages");
158                   qmessagescmd(ua, ua->cmd);
159                   ua->user_notified_msg_pending = false;
160                } else if (!ua->gui && !ua->user_notified_msg_pending && console_msg_pending) {
161                   if (ua->api) {
162                      user->signal(BNET_MSGS_PENDING);
163                   } else {
164                      bsendmsg(ua, _("You have messages.\n"));
165                   }
166                   ua->user_notified_msg_pending = true;
167                }
168             }
169             if (!ua->api) user->signal(BNET_EOD);     /* send end of command */
170          }
171       } else if (user->is_stop()) {
172          ua->quit = true;
173       } else { /* signal */
174          user->signal(BNET_POLL);
175       }
176 
177       /* At the end of each command, revert to the main shared SQL link */
178       ua->db = ua->shared_db;
179    }
180 
181 getout:
182    close_db(ua);
183    free_ua_context(ua);
184    free_jcr(jcr);
185 
186    return NULL;
187 }
188 
189 /*
190  * Create a UAContext for a Job that is running so that
191  *   it can the User Agent routines and
192  *   to ensure that the Job gets the proper output.
193  *   This is a sort of mini-kludge, and should be
194  *   unified at some point.
195  */
new_ua_context(JCR * jcr)196 UAContext *new_ua_context(JCR *jcr)
197 {
198    UAContext *ua;
199 
200    ua = (UAContext *)malloc(sizeof(UAContext));
201    memset(ua, 0, sizeof(UAContext));
202    ua->jcr = jcr;
203    ua->shared_db = ua->db = jcr->db;
204    ua->cmd = get_pool_memory(PM_FNAME);
205    ua->args = get_pool_memory(PM_FNAME);
206    ua->errmsg = get_pool_memory(PM_FNAME);
207    ua->verbose = true;
208    ua->automount = true;
209    return ua;
210 }
211 
free_ua_context(UAContext * ua)212 void free_ua_context(UAContext *ua)
213 {
214    if (ua->cmd) {
215       free_pool_memory(ua->cmd);
216    }
217    if (ua->args) {
218       free_pool_memory(ua->args);
219    }
220    if (ua->errmsg) {
221       free_pool_memory(ua->errmsg);
222    }
223    if (ua->prompt) {
224       free(ua->prompt);
225    }
226    if (ua->unique) {
227       free(ua->unique);
228    }
229    free_bsock(ua->UA_sock);
230    free(ua);
231 }
232