2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 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.
36 /* Forward referenced functions */
37 static bool response(JCR *jcr, BSOCK *bs, const char *resp, const char *cmd);
39 /* Imported variables */
42 /* Static variables */
43 static char ferrmsg[] = "3900 Invalid command\n";
45 /* Imported functions */
46 extern bool do_append_data(JCR *jcr);
47 extern bool do_read_data(JCR *jcr);
48 extern bool do_backup_job(JCR *jcr);
50 /* Forward referenced FD commands */
51 static bool append_open_session(JCR *jcr);
52 static bool append_close_session(JCR *jcr);
53 static bool append_data_cmd(JCR *jcr);
54 static bool append_end_session(JCR *jcr);
55 static bool read_open_session(JCR *jcr);
56 static bool read_data_cmd(JCR *jcr);
57 static bool read_close_session(JCR *jcr);
59 /* Exported function */
60 bool get_bootstrap_file(JCR *jcr, BSOCK *bs);
64 bool (*func)(JCR *jcr);
68 * The following are the recognized commands from the File daemon
70 static struct s_cmds fd_cmds[] = {
71 {"append open", append_open_session},
72 {"append data", append_data_cmd},
73 {"append end", append_end_session},
74 {"append close", append_close_session},
75 {"read open", read_open_session},
76 {"read data", read_data_cmd},
77 {"read close", read_close_session},
78 {NULL, NULL} /* list terminator */
81 /* Commands from the File daemon that require additional scanning */
82 static char read_open[] = "read open session = %127s %ld %ld %ld %ld %ld %ld\n";
84 /* Responses sent to the File daemon */
85 static char NO_open[] = "3901 Error session already open\n";
86 static char NOT_opened[] = "3902 Error session not opened\n";
87 static char ERROR_open[] = "3904 Error open session, bad parameters\n";
88 static char OK_end[] = "3000 OK end\n";
89 static char OK_close[] = "3000 OK close Status = %d\n";
90 static char OK_open[] = "3000 OK open ticket = %d\n";
91 static char ERROR_append[] = "3903 Error append data: %s\n";
93 /* Information sent to the Director */
94 static char Job_start[] = "3010 Job %s start\n";
96 "3099 Job %s end JobStatus=%d JobFiles=%d JobBytes=%s JobErrors=%u\n";
99 * Run a Client Job -- Client already authorized
100 * Note: this can be either a backup or restore or
103 * Basic task here is:
104 * - Read a command from the Client -- FD or SD
108 void run_job(JCR *jcr)
110 BSOCK *dir = jcr->dir_bsock;
114 Dmsg1(120, "Start run Job=%s\n", jcr->Job);
115 dir->fsend(Job_start, jcr->Job);
116 jcr->start_time = time(NULL);
117 jcr->run_time = jcr->start_time;
118 jcr->sendJobStatus(JS_Running);
120 * A migrate or copy job does both a restore (read_data) and
121 * a backup (append_data).
122 * Otherwise we do the commands that the client sends
123 * which are for normal backup or restore jobs.
125 Dmsg3(050, "==== JobType=%c run_job=%d sd_client=%d\n", jcr->getJobType(), jcr->JobId, jcr->sd_client);
126 if (jcr->is_JobType(JT_BACKUP) && jcr->sd_client) {
127 jcr->session_opened = true;
128 Dmsg0(050, "Do: receive for 3000 OK data then append\n");
129 if (!response(jcr, jcr->file_bsock, "3000 OK data\n", "Append data")) {
130 Dmsg1(050, "Expect: 3000 OK data, got: %s", jcr->file_bsock->msg);
131 Jmsg0(jcr, M_FATAL, 0, "Append data not accepted\n");
134 append_data_cmd(jcr);
135 append_end_session(jcr);
136 } else if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY)) {
137 jcr->session_opened = true;
139 Dmsg0(050, "Do: receive for 3000 OK data then read\n");
140 if (!response(jcr, jcr->file_bsock, "3000 OK data\n", "Data received")) {
141 Dmsg1(050, "Expect 3000 OK data, got: %s", jcr->file_bsock->msg);
142 Jmsg0(jcr, M_FATAL, 0, "Read data not accepted\n");
143 jcr->file_bsock->signal(BNET_EOD);
146 jcr->file_bsock->signal(BNET_EOD);
148 /* Either a Backup or Restore job */
149 Dmsg0(050, "Do: do_client_commands\n");
150 do_client_commands(jcr);
153 jcr->end_time = time(NULL);
154 flush_jobmedia_queue(jcr);
155 dequeue_messages(jcr); /* send any queued messages */
156 jcr->setJobStatus(JS_Terminated);
157 generate_daemon_event(jcr, "JobEnd");
158 generate_plugin_event(jcr, bsdEventJobEnd);
159 dir->fsend(Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles,
160 edit_uint64(jcr->JobBytes, ec1), jcr->JobErrors);
161 Dmsg1(100, "==== %s", dir->msg);
162 dir->signal(BNET_EOD); /* send EOD to Director daemon */
163 free_plugins(jcr); /* release instantiated plugins */
168 * Now talk to the Client (FD/SD) and do what he says
170 void do_client_commands(JCR *jcr)
174 BSOCK *fd = jcr->file_bsock;
177 for (quit=false; !quit;) {
180 /* Read command coming from the File daemon */
182 if (fd->is_stop()) { /* hard eof or error */
183 break; /* connection terminated */
186 continue; /* ignore signals and zero length msgs */
188 Dmsg1(110, "<filed: %s", fd->msg);
190 for (i=0; fd_cmds[i].cmd; i++) {
191 if (strncmp(fd_cmds[i].cmd, fd->msg, strlen(fd_cmds[i].cmd)) == 0) {
192 found = true; /* indicate command found */
194 if (!fd_cmds[i].func(jcr)) { /* do command */
195 /* Note fd->msg command may be destroyed by comm activity */
196 if (!job_canceled(jcr)) {
197 if (jcr->errmsg[0]) {
198 Jmsg1(jcr, M_FATAL, 0, _("Command error with FD, hanging up. ERR=%s\n"),
201 Jmsg0(jcr, M_FATAL, 0, _("Command error with FD, hanging up.\n"));
203 jcr->setJobStatus(JS_ErrorTerminated);
210 if (!found) { /* command not found */
211 if (!job_canceled(jcr)) {
212 Jmsg1(jcr, M_FATAL, 0, _("FD command not found: %s\n"), fd->msg);
213 Dmsg1(110, "<filed: Command not found: %s\n", fd->msg);
219 fd->signal(BNET_TERMINATE); /* signal to FD job is done */
223 * Append Data command
224 * Open Data Channel and receive Data for archiving
225 * Write the Data to the archive device
227 static bool append_data_cmd(JCR *jcr)
229 BSOCK *fd = jcr->file_bsock;
231 Dmsg1(120, "Append data: %s", fd->msg);
232 if (jcr->session_opened) {
233 Dmsg1(110, "<bfiled: %s", fd->msg);
234 jcr->setJobType(JT_BACKUP);
236 if (do_append_data(jcr)) {
239 fd->suppress_error_messages(true); /* ignore errors at this point */
240 fd->fsend(ERROR_append, jcr->errmsg);
243 pm_strcpy(jcr->errmsg, _("Attempt to append on non-open session.\n"));
244 fd->fsend(NOT_opened);
249 static bool append_end_session(JCR *jcr)
251 BSOCK *fd = jcr->file_bsock;
253 Dmsg1(120, ">filed: %s", fd->msg);
254 if (!jcr->session_opened) {
255 pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
256 fd->fsend(NOT_opened);
259 return fd->fsend(OK_end);
264 * Append Open session command
267 static bool append_open_session(JCR *jcr)
269 BSOCK *fd = jcr->file_bsock;
271 Dmsg1(120, "Append open session: %s", fd->msg);
272 if (jcr->session_opened) {
273 pm_strcpy(jcr->errmsg, _("Attempt to open already open session.\n"));
278 jcr->session_opened = true;
280 /* Send "Ticket" to File Daemon */
281 fd->fsend(OK_open, jcr->VolSessionId);
282 Dmsg1(110, ">filed: %s", fd->msg);
288 * Append Close session command
289 * Close the append session and send back Statistics
290 * (need to fix statistics)
292 static bool append_close_session(JCR *jcr)
294 BSOCK *fd = jcr->file_bsock;
296 Dmsg1(120, "<filed: %s", fd->msg);
297 if (!jcr->session_opened) {
298 pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
299 fd->fsend(NOT_opened);
302 /* Send final statistics to File daemon */
303 fd->fsend(OK_close, jcr->JobStatus);
304 Dmsg1(120, ">filed: %s", fd->msg);
306 fd->signal(BNET_EOD); /* send EOD to File daemon */
307 jcr->session_opened = false;
313 * Open Data Channel, read the data from
314 * the archive device and send to File
317 static bool read_data_cmd(JCR *jcr)
319 BSOCK *fd = jcr->file_bsock;
321 Dmsg1(120, "Read data: %s", fd->msg);
322 if (jcr->session_opened) {
323 Dmsg1(120, "<bfiled: %s", fd->msg);
324 return do_read_data(jcr);
326 pm_strcpy(jcr->errmsg, _("Attempt to read on non-open session.\n"));
327 fd->fsend(NOT_opened);
333 * Read Open session command
335 * We need to scan for the parameters of the job
338 static bool read_open_session(JCR *jcr)
340 BSOCK *fd = jcr->file_bsock;
342 Dmsg1(120, "%s", fd->msg);
343 if (jcr->session_opened) {
344 pm_strcpy(jcr->errmsg, _("Attempt to open an already open session.\n"));
349 if (sscanf(fd->msg, read_open, jcr->read_dcr->VolumeName, &jcr->read_VolSessionId,
350 &jcr->read_VolSessionTime, &jcr->read_StartFile, &jcr->read_EndFile,
351 &jcr->read_StartBlock, &jcr->read_EndBlock) == 7) {
352 Dmsg4(100, "read_open_session got: JobId=%d Vol=%s VolSessId=%ld VolSessT=%ld\n",
353 jcr->JobId, jcr->read_dcr->VolumeName, jcr->read_VolSessionId,
354 jcr->read_VolSessionTime);
355 Dmsg4(100, " StartF=%ld EndF=%ld StartB=%ld EndB=%ld\n",
356 jcr->read_StartFile, jcr->read_EndFile, jcr->read_StartBlock,
360 pm_strcpy(jcr->errmsg, _("Cannot open session, received bad parameters.\n"));
361 fd->fsend(ERROR_open);
365 jcr->session_opened = true;
366 jcr->setJobType(JT_RESTORE);
368 /* Send "Ticket" to File Daemon */
369 fd->fsend(OK_open, jcr->VolSessionId);
370 Dmsg1(110, ">filed: %s", fd->msg);
376 * Read Close session command
377 * Close the read session
379 static bool read_close_session(JCR *jcr)
381 BSOCK *fd = jcr->file_bsock;
383 Dmsg1(120, "Read close session: %s\n", fd->msg);
384 if (!jcr->session_opened) {
385 fd->fsend(NOT_opened);
388 /* Send final close msg to File daemon */
389 fd->fsend(OK_close, jcr->JobStatus);
390 Dmsg1(160, ">filed: %s\n", fd->msg);
392 fd->signal(BNET_EOD); /* send EOD to File daemon */
394 jcr->session_opened = false;
399 * Get response from FD or SD
400 * sent. Check that the response agrees with what we expect.
402 * Returns: false on failure
405 static bool response(JCR *jcr, BSOCK *bs, const char *resp, const char *cmd)
409 if (bs->is_error()) {
412 if ((n = bs->recv()) >= 0) {
413 if (strcmp(bs->msg, resp) == 0) {
416 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
420 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
421 cmd, bs->bstrerror());