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