2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * This file handles commands from the File daemon.
22 * Written by Kern Sibbald, MM
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.
34 /* Forward referenced functions */
35 static bool response(JCR *jcr, BSOCK *bs, const char *resp, const char *cmd);
37 /* Imported variables */
40 /* Static variables */
41 static char ferrmsg[] = "3900 Invalid command\n";
42 static char OK_data[] = "3000 OK data\n";
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);
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);
60 /* Exported function */
61 bool get_bootstrap_file(JCR *jcr, BSOCK *bs);
65 bool (*func)(JCR *jcr);
69 * The following are the recognized commands from the File daemon
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 */
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";
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";
96 /* Information sent to the Director */
97 static char Job_start[] = "3010 Job %s start\n";
99 "3099 Job %s end JobStatus=%d JobFiles=%d JobBytes=%s JobErrors=%u ErrMsg=%s\n";
102 * Run a Client Job -- Client already authorized
103 * Note: this can be either a backup or restore or
106 * Basic task here is:
107 * - Read a command from the Client -- FD or SD
111 void run_job(JCR *jcr)
113 BSOCK *dir = jcr->dir_bsock;
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);
123 /* TODO: Remove when the new match_all is well tested */
124 jcr->use_new_match_all = use_new_match_all;
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.
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");
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 /* send "3000 OK data" now to avoid a dead lock, the other side is also
145 * waiting for one. The old peace of code was reading the "3000 OK" reply
146 * at the end of the backup (not really appropriate).
147 * dedup need duplex communication with the other side and need the
148 * "3000 OK" to be out of the socket, and be handle here by the right
150 Dmsg0(215, "send OK_data\n");
151 jcr->file_bsock->fsend(OK_data);
152 jcr->is_ok_data_sent = true;
153 Dmsg1(050, "Do: read_data_cmd file_bsock=%p\n", jcr->file_bsock);
154 Dmsg0(050, "Do: receive for 3000 OK data then read\n");
155 if (!response(jcr, jcr->file_bsock, "3000 OK data\n", "Data received")) {
156 Dmsg1(050, "Expect 3000 OK data, got: %s", jcr->file_bsock->msg);
157 Jmsg0(jcr, M_FATAL, 0, "Read data not accepted\n");
158 jcr->file_bsock->signal(BNET_EOD);
162 jcr->file_bsock->signal(BNET_EOD);
164 /* Either a Backup or Restore job */
165 Dmsg0(050, "Do: do_client_commands\n");
166 do_client_commands(jcr);
169 jcr->end_time = time(NULL);
170 flush_jobmedia_queue(jcr);
171 dequeue_messages(jcr); /* send any queued messages */
172 jcr->setJobStatus(JS_Terminated);
173 generate_daemon_event(jcr, "JobEnd");
174 generate_plugin_event(jcr, bsdEventJobEnd);
175 bash_spaces(jcr->StatusErrMsg);
176 dir->fsend(Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles,
177 edit_uint64(jcr->JobBytes, ec1), jcr->JobErrors, jcr->StatusErrMsg);
178 Dmsg1(100, "==== %s", dir->msg);
179 unbash_spaces(jcr->StatusErrMsg);
180 dir->signal(BNET_EOD); /* send EOD to Director daemon */
181 free_plugins(jcr); /* release instantiated plugins */
182 garbage_collect_memory_pool();
187 * Now talk to the Client (FD/SD) and do what he says
189 void do_client_commands(JCR *jcr)
193 BSOCK *fd = jcr->file_bsock;
196 for (quit=false; !quit;) {
199 /* Read command coming from the File daemon */
201 if (fd->is_stop()) { /* hard eof or error */
202 break; /* connection terminated */
205 continue; /* ignore signals and zero length msgs */
207 Dmsg1(110, "<filed: %s", fd->msg);
209 for (i=0; fd_cmds[i].cmd; i++) {
210 if (strncmp(fd_cmds[i].cmd, fd->msg, strlen(fd_cmds[i].cmd)) == 0) {
211 found = true; /* indicate command found */
213 if (!fd_cmds[i].func(jcr)) { /* do command */
214 /* Note fd->msg command may be destroyed by comm activity */
215 if (!job_canceled(jcr)) {
216 strip_trailing_junk(fd->msg);
217 if (jcr->errmsg[0]) {
218 strip_trailing_junk(jcr->errmsg);
219 Jmsg2(jcr, M_FATAL, 0, _("Command error with FD msg=\"%s\", SD hanging up. ERR=%s\n"),
220 fd->msg, jcr->errmsg);
222 Jmsg1(jcr, M_FATAL, 0, _("Command error with FD msg=\"%s\", SD hanging up.\n"),
225 jcr->setJobStatus(JS_ErrorTerminated);
232 if (!found) { /* command not found */
233 if (!job_canceled(jcr)) {
234 Jmsg1(jcr, M_FATAL, 0, _("FD command not found: %s\n"), fd->msg);
235 Dmsg1(110, "<filed: Command not found: %s\n", fd->msg);
241 fd->signal(BNET_TERMINATE); /* signal to FD job is done */
245 * Append Data command
246 * Open Data Channel and receive Data for archiving
247 * Write the Data to the archive device
249 static bool append_data_cmd(JCR *jcr)
251 BSOCK *fd = jcr->file_bsock;
253 Dmsg1(120, "Append data: %s", fd->msg);
254 if (jcr->session_opened) {
255 Dmsg1(110, "<bfiled: %s", fd->msg);
256 jcr->setJobType(JT_BACKUP);
258 if (do_append_data(jcr)) {
261 fd->suppress_error_messages(true); /* ignore errors at this point */
262 fd->fsend(ERROR_append, jcr->errmsg);
265 pm_strcpy(jcr->errmsg, _("Attempt to append on non-open session.\n"));
266 fd->fsend(NOT_opened);
271 static bool append_end_session(JCR *jcr)
273 BSOCK *fd = jcr->file_bsock;
275 Dmsg1(120, "store<file: %s", fd->msg);
276 if (!jcr->session_opened) {
277 pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
278 fd->fsend(NOT_opened);
281 return fd->fsend(OK_end);
285 * Test the FD/SD connectivity
287 static bool sd_testnetwork_cmd(JCR *jcr)
289 BSOCK *fd = jcr->file_bsock;
291 bool can_compress, ok=true;
293 if (sscanf(fd->msg, "testnetwork bytes=%lld", &nb) != 1) {
296 /* We disable the comline compression for this test */
297 can_compress = fd->can_compress();
298 fd->clear_compress();
300 /* First, get data from the FD */
301 while (fd->recv() > 0) { }
303 /* Then, send back data to the FD */
304 memset(fd->msg, 0xBB, sizeof_pool_memory(fd->msg));
305 fd->msglen = sizeof_pool_memory(fd->msg);
307 while(nb > 0 && ok) {
308 if (nb < fd->msglen) {
314 fd->signal(BNET_EOD);
323 * Append Open session command
326 static bool append_open_session(JCR *jcr)
328 BSOCK *fd = jcr->file_bsock;
330 Dmsg1(120, "Append open session: %s", fd->msg);
331 if (jcr->session_opened) {
332 pm_strcpy(jcr->errmsg, _("Attempt to open already open session.\n"));
337 jcr->session_opened = true;
339 /* Send "Ticket" to File Daemon */
340 fd->fsend(OK_open, jcr->VolSessionId);
341 Dmsg1(110, ">filed: %s", fd->msg);
347 * Append Close session command
348 * Close the append session and send back Statistics
349 * (need to fix statistics)
351 static bool append_close_session(JCR *jcr)
353 BSOCK *fd = jcr->file_bsock;
355 Dmsg1(120, "<filed: %s", fd->msg);
356 if (!jcr->session_opened) {
357 pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
358 fd->fsend(NOT_opened);
361 /* Send final statistics to File daemon */
362 fd->fsend(OK_close, jcr->JobStatus);
363 Dmsg1(120, ">filed: %s", fd->msg);
365 fd->signal(BNET_EOD); /* send EOD to File daemon */
367 jcr->session_opened = false;
373 * Open Data Channel, read the data from
374 * the archive device and send to File
377 static bool read_data_cmd(JCR *jcr)
379 BSOCK *fd = jcr->file_bsock;
381 Dmsg1(120, "Read data: %s", fd->msg);
382 if (jcr->session_opened) {
383 Dmsg1(120, "<bfiled: %s", fd->msg);
384 return do_read_data(jcr);
386 pm_strcpy(jcr->errmsg, _("Attempt to read on non-open session.\n"));
387 fd->fsend(NOT_opened);
393 * Read Open session command
395 * We need to scan for the parameters of the job
398 static bool read_open_session(JCR *jcr)
400 BSOCK *fd = jcr->file_bsock;
402 Dmsg1(120, "%s", fd->msg);
403 if (jcr->session_opened) {
404 pm_strcpy(jcr->errmsg, _("Attempt to open an already open session.\n"));
409 if (sscanf(fd->msg, read_open, jcr->read_dcr->VolumeName, &jcr->read_VolSessionId,
410 &jcr->read_VolSessionTime, &jcr->read_StartFile, &jcr->read_EndFile,
411 &jcr->read_StartBlock, &jcr->read_EndBlock) == 7) {
412 Dmsg4(100, "read_open_session got: JobId=%d Vol=%s VolSessId=%ld VolSessT=%ld\n",
413 jcr->JobId, jcr->read_dcr->VolumeName, jcr->read_VolSessionId,
414 jcr->read_VolSessionTime);
415 Dmsg4(100, " StartF=%ld EndF=%ld StartB=%ld EndB=%ld\n",
416 jcr->read_StartFile, jcr->read_EndFile, jcr->read_StartBlock,
420 pm_strcpy(jcr->errmsg, _("Cannot open session, received bad parameters.\n"));
421 fd->fsend(ERROR_open);
425 jcr->session_opened = true;
426 jcr->setJobType(JT_RESTORE);
428 /* Send "Ticket" to File Daemon */
429 fd->fsend(OK_open, jcr->VolSessionId);
430 Dmsg1(110, ">filed: %s", fd->msg);
435 static bool read_control_cmd(JCR *jcr)
437 BSOCK *fd = jcr->file_bsock;
439 Dmsg1(120, "Read control: %s\n", fd->msg);
440 if (!jcr->session_opened) {
441 fd->fsend(NOT_opened);
444 jcr->interactive_session = true;
449 * Read Close session command
450 * Close the read session
452 static bool read_close_session(JCR *jcr)
454 BSOCK *fd = jcr->file_bsock;
456 Dmsg1(120, "Read close session: %s\n", fd->msg);
457 if (!jcr->session_opened) {
458 fd->fsend(NOT_opened);
461 /* Send final close msg to File daemon */
462 fd->fsend(OK_close, jcr->JobStatus);
463 Dmsg1(160, ">filed: %s\n", fd->msg);
465 fd->signal(BNET_EOD); /* send EOD to File daemon */
467 jcr->session_opened = false;
472 * Get response from FD or SD
473 * sent. Check that the response agrees with what we expect.
475 * Returns: false on failure
478 static bool response(JCR *jcr, BSOCK *bs, const char *resp, const char *cmd)
482 if (bs->is_error()) {
485 if ((n = bs->recv()) >= 0) {
486 if (strcmp(bs->msg, resp) == 0) {
489 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
493 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
494 cmd, bs->bstrerror());