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