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    /* If network connection, display it */
143    ua->send_events("DC0015",
144                    EVENTS_TYPE_CONNECTION,
145                    "Connection from %s:%d",
146                    user->host(), user->port());
147 
148    while (!ua->quit) {
149       if (ua->api) user->signal(BNET_MAIN_PROMPT);
150       stat = user->recv();
151       if (stat >= 0) {
152          pm_strcpy(ua->cmd, ua->UA_sock->msg);
153          parse_ua_args(ua);
154          if (ua->argc > 0 && ua->argk[0][0] == '.') {
155             do_a_dot_command(ua);
156          } else {
157             do_a_command(ua);
158          }
159          dequeue_messages(ua->jcr);
160          if (!ua->quit) {
161             if (console_msg_pending && acl_access_ok(ua, Command_ACL, "messages", 8)) {
162                if (ua->auto_display_messages) {
163                   pm_strcpy(ua->cmd, "messages");
164                   qmessagescmd(ua, ua->cmd);
165                   ua->user_notified_msg_pending = false;
166                } else if (!ua->gui && !ua->user_notified_msg_pending && console_msg_pending) {
167                   if (ua->api) {
168                      user->signal(BNET_MSGS_PENDING);
169                   } else {
170                      bsendmsg(ua, _("You have messages.\n"));
171                   }
172                   ua->user_notified_msg_pending = true;
173                }
174             }
175             if (!ua->api) user->signal(BNET_EOD);     /* send end of command */
176          }
177       } else if (user->is_stop()) {
178          ua->quit = true;
179       } else { /* signal */
180          user->signal(BNET_POLL);
181       }
182 
183       /* At the end of each command, revert to the main shared SQL link */
184       ua->db = ua->shared_db;
185    }
186 
187    /* If network connection, display it */
188    ua->send_events("DC0016",
189                    EVENTS_TYPE_CONNECTION,
190                    "Disconnection from %s:%d",
191                    user->host(), user->port());
192 
193 getout:
194    close_db(ua);
195    free_ua_context(ua);
196    free_jcr(jcr);
197 
198    return NULL;
199 }
200 
201 /*
202  * Create a UAContext for a Job that is running so that
203  *   it can the User Agent routines and
204  *   to ensure that the Job gets the proper output.
205  *   This is a sort of mini-kludge, and should be
206  *   unified at some point.
207  */
new_ua_context(JCR * jcr)208 UAContext *new_ua_context(JCR *jcr)
209 {
210    UAContext *ua;
211 
212    ua = (UAContext *)malloc(sizeof(UAContext));
213    memset(ua, 0, sizeof(UAContext));
214    ua->jcr = jcr;
215    ua->shared_db = ua->db = jcr->db;
216    ua->cmd = get_pool_memory(PM_FNAME);
217    ua->args = get_pool_memory(PM_FNAME);
218    ua->errmsg = get_pool_memory(PM_FNAME);
219    ua->verbose = true;
220    ua->automount = true;
221    bstrncpy(ua->name, "*Console*", sizeof(ua->name));
222    return ua;
223 }
224 
free_ua_context(UAContext * ua)225 void free_ua_context(UAContext *ua)
226 {
227    if (ua->cmd) {
228       free_pool_memory(ua->cmd);
229    }
230    if (ua->args) {
231       free_pool_memory(ua->args);
232    }
233    if (ua->errmsg) {
234       free_pool_memory(ua->errmsg);
235    }
236    if (ua->prompt) {
237       free(ua->prompt);
238    }
239    if (ua->unique) {
240       free(ua->unique);
241    }
242    free_bsock(ua->UA_sock);
243    free(ua);
244 }
245