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 * This file handles commands from the File daemon.
21 *
22 * Written by Kern Sibbald, MM
23 *
24 * We get here because the Director has initiated a Job with
25 * the Storage daemon, then done the same with the File daemon,
26 * then when the Storage daemon receives a proper connection from
27 * the File daemon, control is passed here to handle the
28 * subsequent File daemon commands.
29 */
30
31 #include "bacula.h"
32 #include "stored.h"
33
34 /* Forward referenced functions */
35 static bool response(JCR *jcr, BSOCK *bs, const char *resp, const char *cmd);
36
37 /* Imported variables */
38 extern STORES *me;
39
40 /* Static variables */
41 static char ferrmsg[] = "3900 Invalid command\n";
42 static char OK_data[] = "3000 OK data\n";
43
44 /* Imported functions */
45 extern bool do_append_data(JCR *jcr);
46 extern bool do_read_data(JCR *jcr);
47 extern bool do_backup_job(JCR *jcr);
48
49 /* Forward referenced FD commands */
50 static bool append_open_session(JCR *jcr);
51 static bool append_close_session(JCR *jcr);
52 static bool append_data_cmd(JCR *jcr);
53 static bool append_end_session(JCR *jcr);
54 static bool read_open_session(JCR *jcr);
55 static bool read_data_cmd(JCR *jcr);
56 static bool read_close_session(JCR *jcr);
57 static bool read_control_cmd(JCR *jcr);
58 static bool sd_testnetwork_cmd(JCR *jcr);
59
60 /* Exported function */
61 bool get_bootstrap_file(JCR *jcr, BSOCK *bs);
62
63 struct s_cmds {
64 const char *cmd;
65 bool (*func)(JCR *jcr);
66 };
67
68 /*
69 * The following are the recognized commands from the File daemon
70 */
71 static struct s_cmds fd_cmds[] = {
72 {"append open", append_open_session},
73 {"append data", append_data_cmd},
74 {"append end", append_end_session},
75 {"append close", append_close_session},
76 {"read open", read_open_session},
77 {"read data", read_data_cmd},
78 {"read close", read_close_session},
79 {"read control", read_control_cmd},
80 {"testnetwork", sd_testnetwork_cmd},
81 {NULL, NULL} /* list terminator */
82 };
83
84 /* Commands from the File daemon that require additional scanning */
85 static char read_open[] = "read open session = %127s %ld %ld %ld %ld %ld %ld\n";
86
87 /* Responses sent to the File daemon */
88 static char NO_open[] = "3901 Error session already open\n";
89 static char NOT_opened[] = "3902 Error session not opened\n";
90 static char ERROR_open[] = "3904 Error open session, bad parameters\n";
91 static char OK_end[] = "3000 OK end\n";
92 static char OK_close[] = "3000 OK close Status = %d\n";
93 static char OK_open[] = "3000 OK open ticket = %d\n";
94 static char ERROR_append[] = "3903 Error append data: %s\n";
95
96 /* Information sent to the Director */
97 static char Job_start[] = "3010 Job %s start\n";
98 char Job_end[] =
99 "3099 Job %s end JobStatus=%d JobFiles=%d JobBytes=%s JobErrors=%u ErrMsg=%s\n";
100
101 /*
102 * Run a Client Job -- Client already authorized
103 * Note: this can be either a backup or restore or
104 * migrate/copy job.
105 *
106 * Basic task here is:
107 * - Read a command from the Client -- FD or SD
108 * - Execute it
109 *
110 */
run_job(JCR * jcr)111 void run_job(JCR *jcr)
112 {
113 BSOCK *dir = jcr->dir_bsock;
114 char ec1[30];
115
116 dir->set_jcr(jcr);
117 Dmsg1(120, "Start run Job=%s\n", jcr->Job);
118 dir->fsend(Job_start, jcr->Job);
119 jcr->start_time = time(NULL);
120 jcr->run_time = jcr->start_time;
121 jcr->sendJobStatus(JS_Running);
122
123 /* TODO: Remove when the new match_all is well tested */
124 jcr->use_new_match_all = use_new_match_all;
125 /*
126 * A migrate or copy job does both a restore (read_data) and
127 * a backup (append_data).
128 * Otherwise we do the commands that the client sends
129 * which are for normal backup or restore jobs.
130 */
131 Dmsg3(050, "==== JobType=%c run_job=%d sd_client=%d\n", jcr->getJobType(), jcr->JobId, jcr->sd_client);
132 if (jcr->is_JobType(JT_BACKUP) && jcr->sd_client) {
133 jcr->session_opened = true;
134 Dmsg0(050, "Do: receive for 3000 OK data then append\n");
135 if (!response(jcr, jcr->file_bsock, "3000 OK data\n", "Append data")) {
136 Dmsg1(050, "Expect: 3000 OK data, got: %s", jcr->file_bsock->msg);
137 Jmsg0(jcr, M_FATAL, 0, "Append data not accepted\n");
138 goto bail_out;
139 }
140 append_data_cmd(jcr);
141 append_end_session(jcr);
142 } else if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY)) {
143 jcr->session_opened = true;
144 /*
145 * Send "3000 OK data" now to avoid a dead lock, the other side is also
146 * waiting for one. The old code was reading the "3000 OK" reply
147 * at the end of the backup (not really appropriate).
148 * dedup needs duplex communication with the other side and needs the
149 * "3000 OK" to be read, which is handled here by the code below.
150 */
151 Dmsg0(215, "send OK_data\n");
152 jcr->file_bsock->fsend(OK_data);
153 jcr->is_ok_data_sent = true;
154 Dmsg1(050, "Do: read_data_cmd file_bsock=%p\n", jcr->file_bsock);
155 Dmsg0(050, "Do: receive for 3000 OK data then read\n");
156 if (!response(jcr, jcr->file_bsock, "3000 OK data\n", "Data received")) {
157 Dmsg1(050, "Expect 3000 OK data, got: %s", jcr->file_bsock->msg);
158 Jmsg0(jcr, M_FATAL, 0, "Read data not accepted\n");
159 jcr->file_bsock->signal(BNET_EOD);
160 goto bail_out;
161 }
162 read_data_cmd(jcr);
163 jcr->file_bsock->signal(BNET_EOD);
164 } else {
165 /* Either a Backup or Restore job */
166 Dmsg0(050, "Do: do_client_commands\n");
167 do_client_commands(jcr);
168 }
169 bail_out:
170 jcr->end_time = time(NULL);
171 flush_jobmedia_queue(jcr);
172 dequeue_messages(jcr); /* send any queued messages */
173 jcr->setJobStatus(JS_Terminated);
174 generate_daemon_event(jcr, "JobEnd");
175 generate_plugin_event(jcr, bsdEventJobEnd);
176 bash_spaces(jcr->StatusErrMsg);
177 dir->fsend(Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles,
178 edit_uint64(jcr->JobBytes, ec1), jcr->JobErrors, jcr->StatusErrMsg);
179 Dmsg1(100, "==== %s", dir->msg);
180 unbash_spaces(jcr->StatusErrMsg);
181 dequeue_daemon_messages(jcr);
182 dir->signal(BNET_EOD); /* send EOD to Director daemon */
183 free_plugins(jcr); /* release instantiated plugins */
184 garbage_collect_memory_pool();
185 return;
186 }
187
188 /*
189 * Now talk to the Client (FD/SD) and do what he says
190 */
do_client_commands(JCR * jcr)191 void do_client_commands(JCR *jcr)
192 {
193 int i;
194 bool found, quit;
195 BSOCK *fd = jcr->file_bsock;
196
197 if (!fd) {
198 return;
199 }
200 fd->set_jcr(jcr);
201 for (quit=false; !quit;) {
202 int stat;
203
204 /* Read command coming from the File daemon */
205 stat = fd->recv();
206 if (fd->is_stop()) { /* hard eof or error */
207 break; /* connection terminated */
208 }
209 if (stat <= 0) {
210 continue; /* ignore signals and zero length msgs */
211 }
212 Dmsg1(110, "<filed: %s", fd->msg);
213 found = false;
214 for (i=0; fd_cmds[i].cmd; i++) {
215 if (strncmp(fd_cmds[i].cmd, fd->msg, strlen(fd_cmds[i].cmd)) == 0) {
216 found = true; /* indicate command found */
217 jcr->errmsg[0] = 0;
218 if (!fd_cmds[i].func(jcr)) { /* do command */
219 /* Note fd->msg command may be destroyed by comm activity */
220 if (!job_canceled(jcr)) {
221 strip_trailing_junk(fd->msg);
222 if (jcr->errmsg[0]) {
223 strip_trailing_junk(jcr->errmsg);
224 Jmsg2(jcr, M_FATAL, 0, _("Command error with FD msg=\"%s\", SD hanging up. ERR=%s\n"),
225 fd->msg, jcr->errmsg);
226 } else {
227 Jmsg1(jcr, M_FATAL, 0, _("Command error with FD msg=\"%s\", SD hanging up.\n"),
228 fd->msg);
229 }
230 jcr->setJobStatus(JS_ErrorTerminated);
231 }
232 quit = true;
233 }
234 break;
235 }
236 }
237 if (!found) { /* command not found */
238 if (!job_canceled(jcr)) {
239 Jmsg1(jcr, M_FATAL, 0, _("FD command not found: %s\n"), fd->msg);
240 Dmsg1(110, "<filed: Command not found: %s\n", fd->msg);
241 }
242 fd->fsend(ferrmsg);
243 break;
244 }
245 }
246 fd->signal(BNET_TERMINATE); /* signal to FD job is done */
247 }
248
249 /*
250 * Append Data command
251 * Open Data Channel and receive Data for archiving
252 * Write the Data to the archive device
253 */
append_data_cmd(JCR * jcr)254 static bool append_data_cmd(JCR *jcr)
255 {
256 BSOCK *fd = jcr->file_bsock;
257
258 Dmsg1(120, "Append data: %s", fd->msg);
259 if (jcr->session_opened) {
260 Dmsg1(110, "<bfiled: %s", fd->msg);
261 jcr->setJobType(JT_BACKUP);
262 jcr->errmsg[0] = 0;
263 if (do_append_data(jcr)) {
264 return true;
265 } else {
266 fd->suppress_error_messages(true); /* ignore errors at this point */
267 fd->fsend(ERROR_append, jcr->errmsg);
268 }
269 } else {
270 pm_strcpy(jcr->errmsg, _("Attempt to append on non-open session.\n"));
271 fd->fsend(NOT_opened);
272 }
273 return false;
274 }
275
append_end_session(JCR * jcr)276 static bool append_end_session(JCR *jcr)
277 {
278 BSOCK *fd = jcr->file_bsock;
279
280 Dmsg1(120, "store<file: %s", fd->msg);
281 if (!jcr->session_opened) {
282 pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
283 fd->fsend(NOT_opened);
284 return false;
285 }
286 return fd->fsend(OK_end);
287 }
288
289 /*
290 * Test the FD/SD connectivity
291 */
sd_testnetwork_cmd(JCR * jcr)292 static bool sd_testnetwork_cmd(JCR *jcr)
293 {
294 BSOCK *fd = jcr->file_bsock;
295 int64_t nb=0;
296 bool can_compress, ok=true;
297
298 if (sscanf(fd->msg, "testnetwork bytes=%lld", &nb) != 1) {
299 return false;
300 }
301 /* We disable the comline compression for this test */
302 can_compress = fd->can_compress();
303 fd->clear_compress();
304
305 /* First, get data from the FD */
306 while (fd->recv() > 0) { }
307
308 /* Then, send back data to the FD */
309 memset(fd->msg, 0xBB, sizeof_pool_memory(fd->msg));
310 fd->msglen = sizeof_pool_memory(fd->msg);
311
312 while(nb > 0 && ok) {
313 if (nb < fd->msglen) {
314 fd->msglen = nb;
315 }
316 ok = fd->send();
317 nb -= fd->msglen;
318 }
319 fd->signal(BNET_EOD);
320
321 if (can_compress) {
322 fd->set_compress();
323 }
324 return true;
325 }
326
327 /*
328 * Append Open session command
329 *
330 */
append_open_session(JCR * jcr)331 static bool append_open_session(JCR *jcr)
332 {
333 BSOCK *fd = jcr->file_bsock;
334
335 Dmsg1(120, "Append open session: %s", fd->msg);
336 if (jcr->session_opened) {
337 pm_strcpy(jcr->errmsg, _("Attempt to open already open session.\n"));
338 fd->fsend(NO_open);
339 return false;
340 }
341
342 jcr->session_opened = true;
343
344 /* Send "Ticket" to File Daemon */
345 fd->fsend(OK_open, jcr->VolSessionId);
346 Dmsg1(110, ">filed: %s", fd->msg);
347
348 return true;
349 }
350
351 /*
352 * Append Close session command
353 * Close the append session and send back Statistics
354 * (need to fix statistics)
355 */
append_close_session(JCR * jcr)356 static bool append_close_session(JCR *jcr)
357 {
358 BSOCK *fd = jcr->file_bsock;
359
360 Dmsg1(120, "<filed: %s", fd->msg);
361 if (!jcr->session_opened) {
362 pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
363 fd->fsend(NOT_opened);
364 return false;
365 }
366 /* Send final statistics to File daemon */
367 fd->fsend(OK_close, jcr->JobStatus);
368 Dmsg1(120, ">filed: %s", fd->msg);
369
370 fd->signal(BNET_EOD); /* send EOD to File daemon */
371
372 jcr->session_opened = false;
373 return true;
374 }
375
376 /*
377 * Read Data command
378 * Open Data Channel, read the data from
379 * the archive device and send to File
380 * daemon.
381 */
read_data_cmd(JCR * jcr)382 static bool read_data_cmd(JCR *jcr)
383 {
384 BSOCK *fd = jcr->file_bsock;
385
386 Dmsg1(120, "Read data: %s", fd->msg);
387 if (jcr->session_opened) {
388 Dmsg1(120, "<bfiled: %s", fd->msg);
389 return do_read_data(jcr);
390 } else {
391 pm_strcpy(jcr->errmsg, _("Attempt to read on non-open session.\n"));
392 fd->fsend(NOT_opened);
393 return false;
394 }
395 }
396
397 /*
398 * Read Open session command
399 *
400 * We need to scan for the parameters of the job
401 * to be restored.
402 */
read_open_session(JCR * jcr)403 static bool read_open_session(JCR *jcr)
404 {
405 BSOCK *fd = jcr->file_bsock;
406
407 Dmsg1(120, "%s", fd->msg);
408 if (jcr->session_opened) {
409 pm_strcpy(jcr->errmsg, _("Attempt to open an already open session.\n"));
410 fd->fsend(NO_open);
411 return false;
412 }
413
414 if (sscanf(fd->msg, read_open, jcr->read_dcr->VolumeName, &jcr->read_VolSessionId,
415 &jcr->read_VolSessionTime, &jcr->read_StartFile, &jcr->read_EndFile,
416 &jcr->read_StartBlock, &jcr->read_EndBlock) == 7) {
417 Dmsg4(100, "read_open_session got: JobId=%d Vol=%s VolSessId=%ld VolSessT=%ld\n",
418 jcr->JobId, jcr->read_dcr->VolumeName, jcr->read_VolSessionId,
419 jcr->read_VolSessionTime);
420 Dmsg4(100, " StartF=%ld EndF=%ld StartB=%ld EndB=%ld\n",
421 jcr->read_StartFile, jcr->read_EndFile, jcr->read_StartBlock,
422 jcr->read_EndBlock);
423
424 } else {
425 pm_strcpy(jcr->errmsg, _("Cannot open session, received bad parameters.\n"));
426 fd->fsend(ERROR_open);
427 return false;
428 }
429
430 jcr->session_opened = true;
431 jcr->setJobType(JT_RESTORE);
432
433 /* Send "Ticket" to File Daemon */
434 fd->fsend(OK_open, jcr->VolSessionId);
435 Dmsg1(110, ">filed: %s", fd->msg);
436
437 return true;
438 }
439
read_control_cmd(JCR * jcr)440 static bool read_control_cmd(JCR *jcr)
441 {
442 BSOCK *fd = jcr->file_bsock;
443
444 Dmsg1(120, "Read control: %s\n", fd->msg);
445 if (!jcr->session_opened) {
446 fd->fsend(NOT_opened);
447 return false;
448 }
449 jcr->interactive_session = true;
450 return true;
451 }
452
453 /*
454 * Read Close session command
455 * Close the read session
456 */
read_close_session(JCR * jcr)457 static bool read_close_session(JCR *jcr)
458 {
459 BSOCK *fd = jcr->file_bsock;
460
461 Dmsg1(120, "Read close session: %s\n", fd->msg);
462 if (!jcr->session_opened) {
463 fd->fsend(NOT_opened);
464 return false;
465 }
466 /* Send final close msg to File daemon */
467 fd->fsend(OK_close, jcr->JobStatus);
468 Dmsg1(160, ">filed: %s\n", fd->msg);
469
470 fd->signal(BNET_EOD); /* send EOD to File daemon */
471
472 jcr->session_opened = false;
473 return true;
474 }
475
476 /*
477 * Get response from FD or SD
478 * sent. Check that the response agrees with what we expect.
479 *
480 * Returns: false on failure
481 * true on success
482 */
response(JCR * jcr,BSOCK * bs,const char * resp,const char * cmd)483 static bool response(JCR *jcr, BSOCK *bs, const char *resp, const char *cmd)
484 {
485 int n;
486
487 if (bs->is_error()) {
488 return false;
489 }
490 if ((n = bs->recv()) >= 0) {
491 if (strcmp(bs->msg, resp) == 0) {
492 return true;
493 }
494 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
495 cmd, resp, bs->msg);
496 return false;
497 }
498 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
499 cmd, bs->bstrerror());
500 return false;
501 }
502