2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
7 The original author of Bacula is Kern Sibbald, with contributions
8 from many others, a complete list can be found in the file AUTHORS.
10 You may use this file and others of this release according to the
11 license defined in the LICENSE file, which includes the Affero General
12 Public License, v3.0 ("AGPLv3") and some additional permissions and
13 terms pursuant to its AGPLv3 Section 7.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
21 * This file handles commands from the File daemon.
23 * Written by Kern Sibbald, MM
25 * We get here because the Director has initiated a Job with
26 * the Storage daemon, then done the same with the File daemon,
27 * then when the Storage daemon receives a proper connection from
28 * the File daemon, control is passed here to handle the
29 * subsequent File daemon commands.
37 /* Forward referenced functions */
38 static bool response(JCR *jcr, BSOCK *bs, const char *resp, const char *cmd);
40 /* Imported variables */
43 /* Static variables */
44 static char ferrmsg[] = "3900 Invalid command\n";
46 /* Imported functions */
47 extern bool do_append_data(JCR *jcr);
48 extern bool do_read_data(JCR *jcr);
49 extern bool do_backup_job(JCR *jcr);
51 /* Forward referenced FD commands */
52 static bool append_open_session(JCR *jcr);
53 static bool append_close_session(JCR *jcr);
54 static bool append_data_cmd(JCR *jcr);
55 static bool append_end_session(JCR *jcr);
56 static bool read_open_session(JCR *jcr);
57 static bool read_data_cmd(JCR *jcr);
58 static bool read_close_session(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 {NULL, NULL} /* list terminator */
82 /* Commands from the File daemon that require additional scanning */
83 static char read_open[] = "read open session = %127s %ld %ld %ld %ld %ld %ld\n";
85 /* Responses sent to the File daemon */
86 static char NO_open[] = "3901 Error session already open\n";
87 static char NOT_opened[] = "3902 Error session not opened\n";
88 static char ERROR_open[] = "3904 Error open session, bad parameters\n";
89 static char OK_end[] = "3000 OK end\n";
90 static char OK_close[] = "3000 OK close Status = %d\n";
91 static char OK_open[] = "3000 OK open ticket = %d\n";
92 static char ERROR_append[] = "3903 Error append data: %s\n";
94 /* Information sent to the Director */
95 static char Job_start[] = "3010 Job %s start\n";
97 "3099 Job %s end JobStatus=%d JobFiles=%d JobBytes=%s JobErrors=%u\n";
100 * Run a Client Job -- Client already authorized
101 * Note: this can be either a backup or restore or
104 * Basic task here is:
105 * - Read a command from the Client -- FD or SD
109 void run_job(JCR *jcr)
111 BSOCK *dir = jcr->dir_bsock;
115 Dmsg1(120, "Start run Job=%s\n", jcr->Job);
116 dir->fsend(Job_start, jcr->Job);
117 jcr->start_time = time(NULL);
118 jcr->run_time = jcr->start_time;
119 jcr->sendJobStatus(JS_Running);
121 * A migrate or copy job does both a restore (read_data) and
122 * a backup (append_data).
123 * Otherwise we do the commands that the client sends
124 * which are for normal backup or restore jobs.
126 Dmsg3(050, "==== JobType=%c run_job=%d sd_client=%d\n", jcr->getJobType(), jcr->JobId, jcr->sd_client);
127 if (jcr->is_JobType(JT_BACKUP) && jcr->sd_client) {
128 jcr->session_opened = true;
129 Dmsg0(050, "Do: receive for 3000 OK data then append\n");
130 if (!response(jcr, jcr->file_bsock, "3000 OK data\n", "Append data")) {
131 Dmsg1(050, "Expect: 3000 OK data, got: %s", jcr->file_bsock->msg);
132 Jmsg0(jcr, M_FATAL, 0, "Append data not accepted\n");
135 append_data_cmd(jcr);
136 append_end_session(jcr);
137 } else if (jcr->is_JobType(JT_MIGRATE) || jcr->is_JobType(JT_COPY)) {
138 jcr->session_opened = true;
140 Dmsg0(050, "Do: receive for 3000 OK data then read\n");
141 if (!response(jcr, jcr->file_bsock, "3000 OK data\n", "Data received")) {
142 Dmsg1(050, "Expect 3000 OK data, got: %s", jcr->file_bsock->msg);
143 Jmsg0(jcr, M_FATAL, 0, "Read data not accepted\n");
144 jcr->file_bsock->signal(BNET_EOD);
147 jcr->file_bsock->signal(BNET_EOD);
149 /* Either a Backup or Restore job */
150 Dmsg0(050, "Do: do_client_commands\n");
151 do_client_commands(jcr);
154 jcr->end_time = time(NULL);
155 flush_jobmedia_queue(jcr);
156 dequeue_messages(jcr); /* send any queued messages */
157 jcr->setJobStatus(JS_Terminated);
158 generate_daemon_event(jcr, "JobEnd");
159 generate_plugin_event(jcr, bsdEventJobEnd);
160 dir->fsend(Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles,
161 edit_uint64(jcr->JobBytes, ec1), jcr->JobErrors);
162 Dmsg1(100, "==== %s", dir->msg);
163 dir->signal(BNET_EOD); /* send EOD to Director daemon */
164 free_plugins(jcr); /* release instantiated plugins */
169 * Now talk to the Client (FD/SD) and do what he says
171 void do_client_commands(JCR *jcr)
175 BSOCK *fd = jcr->file_bsock;
178 for (quit=false; !quit;) {
181 /* Read command coming from the File daemon */
183 if (fd->is_stop()) { /* hard eof or error */
184 break; /* connection terminated */
187 continue; /* ignore signals and zero length msgs */
189 Dmsg1(110, "<filed: %s", fd->msg);
191 for (i=0; fd_cmds[i].cmd; i++) {
192 if (strncmp(fd_cmds[i].cmd, fd->msg, strlen(fd_cmds[i].cmd)) == 0) {
193 found = true; /* indicate command found */
195 if (!fd_cmds[i].func(jcr)) { /* do command */
196 /* Note fd->msg command may be destroyed by comm activity */
197 if (!job_canceled(jcr)) {
198 if (jcr->errmsg[0]) {
199 Jmsg1(jcr, M_FATAL, 0, _("Command error with FD, hanging up. ERR=%s\n"),
202 Jmsg0(jcr, M_FATAL, 0, _("Command error with FD, hanging up.\n"));
204 jcr->setJobStatus(JS_ErrorTerminated);
211 if (!found) { /* command not found */
212 if (!job_canceled(jcr)) {
213 Jmsg1(jcr, M_FATAL, 0, _("FD command not found: %s\n"), fd->msg);
214 Dmsg1(110, "<filed: Command not found: %s\n", fd->msg);
220 fd->signal(BNET_TERMINATE); /* signal to FD job is done */
224 * Append Data command
225 * Open Data Channel and receive Data for archiving
226 * Write the Data to the archive device
228 static bool append_data_cmd(JCR *jcr)
230 BSOCK *fd = jcr->file_bsock;
232 Dmsg1(120, "Append data: %s", fd->msg);
233 if (jcr->session_opened) {
234 Dmsg1(110, "<bfiled: %s", fd->msg);
235 jcr->setJobType(JT_BACKUP);
237 if (do_append_data(jcr)) {
240 fd->suppress_error_messages(true); /* ignore errors at this point */
241 fd->fsend(ERROR_append, jcr->errmsg);
244 pm_strcpy(jcr->errmsg, _("Attempt to append on non-open session.\n"));
245 fd->fsend(NOT_opened);
250 static bool append_end_session(JCR *jcr)
252 BSOCK *fd = jcr->file_bsock;
254 Dmsg1(120, ">filed: %s", fd->msg);
255 if (!jcr->session_opened) {
256 pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
257 fd->fsend(NOT_opened);
260 return fd->fsend(OK_end);
265 * Append Open session command
268 static bool append_open_session(JCR *jcr)
270 BSOCK *fd = jcr->file_bsock;
272 Dmsg1(120, "Append open session: %s", fd->msg);
273 if (jcr->session_opened) {
274 pm_strcpy(jcr->errmsg, _("Attempt to open already open session.\n"));
279 jcr->session_opened = true;
281 /* Send "Ticket" to File Daemon */
282 fd->fsend(OK_open, jcr->VolSessionId);
283 Dmsg1(110, ">filed: %s", fd->msg);
289 * Append Close session command
290 * Close the append session and send back Statistics
291 * (need to fix statistics)
293 static bool append_close_session(JCR *jcr)
295 BSOCK *fd = jcr->file_bsock;
297 Dmsg1(120, "<filed: %s", fd->msg);
298 if (!jcr->session_opened) {
299 pm_strcpy(jcr->errmsg, _("Attempt to close non-open session.\n"));
300 fd->fsend(NOT_opened);
303 /* Send final statistics to File daemon */
304 fd->fsend(OK_close, jcr->JobStatus);
305 Dmsg1(120, ">filed: %s", fd->msg);
307 fd->signal(BNET_EOD); /* send EOD to File daemon */
308 jcr->session_opened = false;
314 * Open Data Channel, read the data from
315 * the archive device and send to File
318 static bool read_data_cmd(JCR *jcr)
320 BSOCK *fd = jcr->file_bsock;
322 Dmsg1(120, "Read data: %s", fd->msg);
323 if (jcr->session_opened) {
324 Dmsg1(120, "<bfiled: %s", fd->msg);
325 return do_read_data(jcr);
327 pm_strcpy(jcr->errmsg, _("Attempt to read on non-open session.\n"));
328 fd->fsend(NOT_opened);
334 * Read Open session command
336 * We need to scan for the parameters of the job
339 static bool read_open_session(JCR *jcr)
341 BSOCK *fd = jcr->file_bsock;
343 Dmsg1(120, "%s", fd->msg);
344 if (jcr->session_opened) {
345 pm_strcpy(jcr->errmsg, _("Attempt to open an already open session.\n"));
350 if (sscanf(fd->msg, read_open, jcr->read_dcr->VolumeName, &jcr->read_VolSessionId,
351 &jcr->read_VolSessionTime, &jcr->read_StartFile, &jcr->read_EndFile,
352 &jcr->read_StartBlock, &jcr->read_EndBlock) == 7) {
353 Dmsg4(100, "read_open_session got: JobId=%d Vol=%s VolSessId=%ld VolSessT=%ld\n",
354 jcr->JobId, jcr->read_dcr->VolumeName, jcr->read_VolSessionId,
355 jcr->read_VolSessionTime);
356 Dmsg4(100, " StartF=%ld EndF=%ld StartB=%ld EndB=%ld\n",
357 jcr->read_StartFile, jcr->read_EndFile, jcr->read_StartBlock,
361 pm_strcpy(jcr->errmsg, _("Cannot open session, received bad parameters.\n"));
362 fd->fsend(ERROR_open);
366 jcr->session_opened = true;
367 jcr->setJobType(JT_RESTORE);
369 /* Send "Ticket" to File Daemon */
370 fd->fsend(OK_open, jcr->VolSessionId);
371 Dmsg1(110, ">filed: %s", fd->msg);
377 * Read Close session command
378 * Close the read session
380 static bool read_close_session(JCR *jcr)
382 BSOCK *fd = jcr->file_bsock;
384 Dmsg1(120, "Read close session: %s\n", fd->msg);
385 if (!jcr->session_opened) {
386 fd->fsend(NOT_opened);
389 /* Send final close msg to File daemon */
390 fd->fsend(OK_close, jcr->JobStatus);
391 Dmsg1(160, ">filed: %s\n", fd->msg);
393 fd->signal(BNET_EOD); /* send EOD to File daemon */
395 jcr->session_opened = false;
400 * Get response from FD or SD
401 * sent. Check that the response agrees with what we expect.
403 * Returns: false on failure
406 static bool response(JCR *jcr, BSOCK *bs, const char *resp, const char *cmd)
410 if (bs->is_error()) {
413 if ((n = bs->recv()) >= 0) {
414 if (strcmp(bs->msg, resp) == 0) {
417 Jmsg(jcr, M_FATAL, 0, _("Bad response to %s command: wanted %s, got %s\n"),
421 Jmsg(jcr, M_FATAL, 0, _("Socket error on %s command: ERR=%s\n"),
422 cmd, bs->bstrerror());