2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Bacula File Daemon Job processing
31 * Kern Sibbald, October MM
40 #if defined(WIN32_VSS)
43 static pthread_mutex_t vss_mutex = PTHREAD_MUTEX_INITIALIZER;
44 static int enable_vss;
47 extern CLIENT *me; /* our client resource */
49 /* Imported functions */
50 extern int status_cmd(JCR *jcr);
51 extern int qstatus_cmd(JCR *jcr);
53 /* Forward referenced functions */
54 static int backup_cmd(JCR *jcr);
55 static int bootstrap_cmd(JCR *jcr);
56 static int cancel_cmd(JCR *jcr);
57 static int setdebug_cmd(JCR *jcr);
58 static int estimate_cmd(JCR *jcr);
59 static int hello_cmd(JCR *jcr);
60 static int job_cmd(JCR *jcr);
61 static int fileset_cmd(JCR *jcr);
62 static int level_cmd(JCR *jcr);
63 static int verify_cmd(JCR *jcr);
64 static int restore_cmd(JCR *jcr);
65 static int storage_cmd(JCR *jcr);
66 static int session_cmd(JCR *jcr);
67 static int response(JCR *jcr, BSOCK *sd, char *resp, const char *cmd);
68 static void filed_free_jcr(JCR *jcr);
69 static int open_sd_read_session(JCR *jcr);
70 static int send_bootstrap_file(JCR *jcr);
71 static int runscript_cmd(JCR *jcr);
72 static int runbefore_cmd(JCR *jcr);
73 static int runafter_cmd(JCR *jcr);
74 static int runbeforenow_cmd(JCR *jcr);
75 static void set_options(findFOPTS *fo, const char *opts);
78 /* Exported functions */
83 int monitoraccess; /* specify if monitors have access to this function */
87 * The following are the recognized commands from the Director.
89 static struct s_cmds cmds[] = {
90 {"backup", backup_cmd, 0},
91 {"cancel", cancel_cmd, 0},
92 {"setdebug=", setdebug_cmd, 0},
93 {"estimate", estimate_cmd, 0},
94 {"Hello", hello_cmd, 1},
95 {"fileset", fileset_cmd, 0},
96 {"JobId=", job_cmd, 0},
97 {"level = ", level_cmd, 0},
98 {"restore", restore_cmd, 0},
99 {"session", session_cmd, 0},
100 {"status", status_cmd, 1},
101 {".status", qstatus_cmd, 1},
102 {"storage ", storage_cmd, 0},
103 {"verify", verify_cmd, 0},
104 {"bootstrap", bootstrap_cmd, 0},
105 {"RunBeforeNow", runbeforenow_cmd, 0},
106 {"RunBeforeJob", runbefore_cmd, 0},
107 {"RunAfterJob", runafter_cmd, 0},
108 {"Run", runscript_cmd, 0},
109 {NULL, NULL} /* list terminator */
112 /* Commands received from director that need scanning */
113 static char jobcmd[] = "JobId=%d Job=%127s SDid=%d SDtime=%d Authorization=%100s";
114 static char storaddr[] = "storage address=%s port=%d ssl=%d";
115 static char sessioncmd[] = "session %127s %ld %ld %ld %ld %ld %ld\n";
116 static char restorecmd[] = "restore replace=%c prelinks=%d where=%s\n";
117 static char restorecmd1[] = "restore replace=%c prelinks=%d where=\n";
118 static char restorecmdR[] = "restore replace=%c prelinks=%d regexwhere=%s\n";
119 static char verifycmd[] = "verify level=%30s";
120 static char estimatecmd[] = "estimate listing=%d";
121 static char runbefore[] = "RunBeforeJob %s";
122 static char runafter[] = "RunAfterJob %s";
123 static char runscript[] = "Run OnSuccess=%d OnFailure=%d AbortOnError=%d When=%d Command=%s";
125 /* Responses sent to Director */
126 static char errmsg[] = "2999 Invalid command\n";
127 static char no_auth[] = "2998 No Authorization\n";
128 static char invalid_cmd[] = "2997 Invalid command for a Director with Monitor directive enabled.\n";
129 static char OKinc[] = "2000 OK include\n";
130 static char OKest[] = "2000 OK estimate files=%u bytes=%s\n";
131 static char OKlevel[] = "2000 OK level\n";
132 static char OKbackup[] = "2000 OK backup\n";
133 static char OKbootstrap[] = "2000 OK bootstrap\n";
134 static char OKverify[] = "2000 OK verify\n";
135 static char OKrestore[] = "2000 OK restore\n";
136 static char OKsession[] = "2000 OK session\n";
137 static char OKstore[] = "2000 OK storage\n";
138 static char OKjob[] = "2000 OK Job %s (%s) %s,%s,%s";
139 static char OKsetdebug[] = "2000 OK setdebug=%d\n";
140 static char BADjob[] = "2901 Bad Job\n";
141 static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u ReadBytes=%s"
142 " JobBytes=%s Errors=%u VSS=%d Encrypt=%d\n";
143 static char OKRunBefore[] = "2000 OK RunBefore\n";
144 static char OKRunBeforeNow[] = "2000 OK RunBeforeNow\n";
145 static char OKRunAfter[] = "2000 OK RunAfter\n";
146 static char OKRunScript[] = "2000 OK RunScript\n";
149 /* Responses received from Storage Daemon */
150 static char OK_end[] = "3000 OK end\n";
151 static char OK_close[] = "3000 OK close Status = %d\n";
152 static char OK_open[] = "3000 OK open ticket = %d\n";
153 static char OK_data[] = "3000 OK data\n";
154 static char OK_append[] = "3000 OK append data\n";
155 static char OKSDbootstrap[]= "3000 OK bootstrap\n";
158 /* Commands sent to Storage Daemon */
159 static char append_open[] = "append open session\n";
160 static char append_data[] = "append data %d\n";
161 static char append_end[] = "append end session %d\n";
162 static char append_close[] = "append close session %d\n";
163 static char read_open[] = "read open session = %s %ld %ld %ld %ld %ld %ld\n";
164 static char read_data[] = "read data %d\n";
165 static char read_close[] = "read close session %d\n";
168 * Accept requests from a Director
170 * NOTE! We are running as a separate thread
172 * Send output one line
173 * at a time followed by a zero length transmission.
175 * Return when the connection is terminated or there
178 * Basic task here is:
179 * Authenticate Director (during Hello command).
180 * Accept commands one at a time from the Director
183 * Concerning ClientRunBefore/After, the sequence of events
184 * is rather critical. If they are not done in the right
185 * order one can easily get FD->SD timeouts if the script
188 * The current sequence of events is:
189 * 1. Dir starts job with FD
190 * 2. Dir connects to SD
191 * 3. Dir connects to FD
192 * 4. FD connects to SD
193 * 5. FD gets/runs ClientRunBeforeJob and sends ClientRunAfterJob
194 * 6. Dir sends include/exclude
195 * 7. FD sends data to SD
196 * 8. SD/FD disconnects while SD despools data and attributes (optionnal)
197 * 9. FD runs ClientRunAfterJob
200 void *handle_client_request(void *dirp)
205 BSOCK *dir = (BSOCK *)dirp;
207 jcr = new_jcr(sizeof(JCR), filed_free_jcr); /* create JCR */
208 jcr->dir_bsock = dir;
209 jcr->ff = init_find_files();
210 jcr->start_time = time(NULL);
211 jcr->RunScripts = New(alist(10, not_owned_by_alist));
212 jcr->last_fname = get_pool_memory(PM_FNAME);
213 jcr->last_fname[0] = 0;
214 jcr->client_name = get_memory(strlen(my_name) + 1);
215 new_plugins(jcr); /* instantiate plugins for this jcr */
216 pm_strcpy(jcr->client_name, my_name);
217 jcr->crypto.pki_sign = me->pki_sign;
218 jcr->crypto.pki_encrypt = me->pki_encrypt;
219 jcr->crypto.pki_keypair = me->pki_keypair;
220 jcr->crypto.pki_signers = me->pki_signers;
221 jcr->crypto.pki_recipients = me->pki_recipients;
223 enable_backup_privileges(NULL, 1 /* ignore_errors */);
225 /**********FIXME******* add command handler error code */
227 for (quit=false; !quit;) {
230 if (bnet_recv(dir) < 0) {
231 break; /* connection terminated */
233 dir->msg[dir->msglen] = 0;
234 Dmsg1(100, "<dird: %s", dir->msg);
236 for (i=0; cmds[i].cmd; i++) {
237 if (strncmp(cmds[i].cmd, dir->msg, strlen(cmds[i].cmd)) == 0) {
238 found = true; /* indicate command found */
239 if (!jcr->authenticated && cmds[i].func != hello_cmd) {
240 bnet_fsend(dir, no_auth);
241 bnet_sig(dir, BNET_EOD);
244 if ((jcr->authenticated) && (!cmds[i].monitoraccess) && (jcr->director->monitor)) {
245 Dmsg1(100, "Command \"%s\" is invalid.\n", cmds[i].cmd);
246 bnet_fsend(dir, invalid_cmd);
247 bnet_sig(dir, BNET_EOD);
250 Dmsg1(100, "Executing %s command.\n", cmds[i].cmd);
251 if (!cmds[i].func(jcr)) { /* do command */
252 quit = true; /* error or fully terminated, get out */
253 Dmsg1(20, "Quit command loop. Canceled=%d\n", job_canceled(jcr));
258 if (!found) { /* command not found */
259 bnet_fsend(dir, errmsg);
265 /* Inform Storage daemon that we are done */
266 if (jcr->store_bsock) {
267 bnet_sig(jcr->store_bsock, BNET_TERMINATE);
270 /* Run the after job */
271 run_scripts(jcr, jcr->RunScripts, "ClientAfterJob");
273 if (jcr->JobId) { /* send EndJob if running a job */
274 char ed1[50], ed2[50];
275 /* Send termination status back to Dir */
276 bnet_fsend(dir, EndJob, jcr->JobStatus, jcr->JobFiles,
277 edit_uint64(jcr->ReadBytes, ed1),
278 edit_uint64(jcr->JobBytes, ed2), jcr->Errors, jcr->VSS,
279 jcr->crypto.pki_encrypt);
280 Dmsg1(110, "End FD msg: %s\n", dir->msg);
283 generate_daemon_event(jcr, "JobEnd");
284 generate_plugin_event(jcr, bEventJobEnd);
286 dequeue_messages(jcr); /* send any queued messages */
288 /* Inform Director that we are done */
289 dir->signal(BNET_TERMINATE);
291 free_plugins(jcr); /* release instantiated plugins */
293 /* Clean up fileset */
294 FF_PKT *ff = jcr->ff;
295 findFILESET *fileset = ff->fileset;
298 /* Delete FileSet Include lists */
299 for (i=0; i<fileset->include_list.size(); i++) {
300 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
301 for (j=0; j<incexe->opts_list.size(); j++) {
302 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
303 for (k=0; k<fo->regex.size(); k++) {
304 regfree((regex_t *)fo->regex.get(k));
307 fo->regexdir.destroy();
308 fo->regexfile.destroy();
310 fo->wilddir.destroy();
311 fo->wildfile.destroy();
312 fo->wildbase.destroy();
314 fo->fstype.destroy();
315 fo->drivetype.destroy();
323 incexe->opts_list.destroy();
324 incexe->name_list.destroy();
325 incexe->plugin_list.destroy();
327 fileset->include_list.destroy();
329 /* Delete FileSet Exclude lists */
330 for (i=0; i<fileset->exclude_list.size(); i++) {
331 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
332 for (j=0; j<incexe->opts_list.size(); j++) {
333 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
335 fo->regexdir.destroy();
336 fo->regexfile.destroy();
338 fo->wilddir.destroy();
339 fo->wildfile.destroy();
340 fo->wildbase.destroy();
342 fo->fstype.destroy();
343 fo->drivetype.destroy();
345 incexe->opts_list.destroy();
346 incexe->name_list.destroy();
347 incexe->plugin_list.destroy();
349 fileset->exclude_list.destroy();
353 Dmsg0(100, "Calling term_find_files\n");
354 term_find_files(jcr->ff);
356 Dmsg0(100, "Done with term_find_files\n");
357 free_jcr(jcr); /* destroy JCR record */
358 Dmsg0(100, "Done with free_jcr\n");
364 * Hello from Director he must identify himself and provide his
367 static int hello_cmd(JCR *jcr)
369 Dmsg0(120, "Calling Authenticate\n");
370 if (!authenticate_director(jcr)) {
373 Dmsg0(120, "OK Authenticate\n");
374 jcr->authenticated = true;
381 static int cancel_cmd(JCR *jcr)
383 BSOCK *dir = jcr->dir_bsock;
384 char Job[MAX_NAME_LENGTH];
387 if (sscanf(dir->msg, "cancel Job=%127s", Job) == 1) {
388 if (!(cjcr=get_jcr_by_full_name(Job))) {
389 dir->fsend(_("2901 Job %s not found.\n"), Job);
391 if (cjcr->store_bsock) {
392 cjcr->store_bsock->set_timed_out();
393 cjcr->store_bsock->set_terminated();
394 pthread_kill(cjcr->my_thread_id, TIMEOUT_SIGNAL);
396 set_jcr_job_status(cjcr, JS_Canceled);
398 dir->fsend(_("2001 Job %s marked to be canceled.\n"), Job);
401 dir->fsend(_("2902 Error scanning cancel command.\n"));
403 dir->signal(BNET_EOD);
409 * Set debug level as requested by the Director
412 static int setdebug_cmd(JCR *jcr)
414 BSOCK *dir = jcr->dir_bsock;
415 int level, trace_flag;
417 Dmsg1(110, "setdebug_cmd: %s", dir->msg);
418 if (sscanf(dir->msg, "setdebug=%d trace=%d", &level, &trace_flag) != 2 || level < 0) {
419 pm_strcpy(jcr->errmsg, dir->msg);
420 dir->fsend(_("2991 Bad setdebug command: %s\n"), jcr->errmsg);
424 set_trace(trace_flag);
425 return dir->fsend(OKsetdebug, level);
429 static int estimate_cmd(JCR *jcr)
431 BSOCK *dir = jcr->dir_bsock;
434 if (sscanf(dir->msg, estimatecmd, &jcr->listing) != 1) {
435 pm_strcpy(jcr->errmsg, dir->msg);
436 Jmsg(jcr, M_FATAL, 0, _("Bad estimate command: %s"), jcr->errmsg);
437 dir->fsend(_("2992 Bad estimate command.\n"));
441 dir->fsend(OKest, jcr->num_files_examined,
442 edit_uint64_with_commas(jcr->JobBytes, ed2));
443 dir->signal(BNET_EOD);
448 * Get JobId and Storage Daemon Authorization key from Director
450 static int job_cmd(JCR *jcr)
452 BSOCK *dir = jcr->dir_bsock;
453 POOLMEM *sd_auth_key;
455 sd_auth_key = get_memory(dir->msglen);
456 if (sscanf(dir->msg, jobcmd, &jcr->JobId, jcr->Job,
457 &jcr->VolSessionId, &jcr->VolSessionTime,
459 pm_strcpy(jcr->errmsg, dir->msg);
460 Jmsg(jcr, M_FATAL, 0, _("Bad Job Command: %s"), jcr->errmsg);
462 free_pool_memory(sd_auth_key);
465 jcr->sd_auth_key = bstrdup(sd_auth_key);
466 free_pool_memory(sd_auth_key);
467 Dmsg2(120, "JobId=%d Auth=%s\n", jcr->JobId, jcr->sd_auth_key);
468 return dir->fsend(OKjob, VERSION, LSMDATE, HOST_OS, DISTNAME, DISTVER);
471 static int runbefore_cmd(JCR *jcr)
474 BSOCK *dir = jcr->dir_bsock;
475 POOLMEM *cmd = get_memory(dir->msglen+1);
478 Dmsg1(100, "runbefore_cmd: %s", dir->msg);
479 if (sscanf(dir->msg, runbefore, cmd) != 1) {
480 pm_strcpy(jcr->errmsg, dir->msg);
481 Jmsg1(jcr, M_FATAL, 0, _("Bad RunBeforeJob command: %s\n"), jcr->errmsg);
482 dir->fsend(_("2905 Bad RunBeforeJob command.\n"));
488 /* Run the command now */
489 script = new_runscript();
490 script->set_command(cmd);
491 script->when = SCRIPT_Before;
492 ok = script->run(jcr, "ClientRunBeforeJob");
493 free_runscript(script);
497 dir->fsend(OKRunBefore);
500 dir->fsend(_("2905 Bad RunBeforeJob command.\n"));
505 static int runbeforenow_cmd(JCR *jcr)
507 BSOCK *dir = jcr->dir_bsock;
509 run_scripts(jcr, jcr->RunScripts, "ClientBeforeJob");
510 if (job_canceled(jcr)) {
511 dir->fsend(_("2905 Bad RunBeforeNow command.\n"));
512 Dmsg0(100, "Back from run_scripts ClientBeforeJob now: FAILED\n");
515 dir->fsend(OKRunBeforeNow);
516 Dmsg0(100, "Back from run_scripts ClientBeforeJob now: OK\n");
521 static int runafter_cmd(JCR *jcr)
523 BSOCK *dir = jcr->dir_bsock;
524 POOLMEM *msg = get_memory(dir->msglen+1);
527 Dmsg1(100, "runafter_cmd: %s", dir->msg);
528 if (sscanf(dir->msg, runafter, msg) != 1) {
529 pm_strcpy(jcr->errmsg, dir->msg);
530 Jmsg1(jcr, M_FATAL, 0, _("Bad RunAfter command: %s\n"), jcr->errmsg);
531 dir->fsend(_("2905 Bad RunAfterJob command.\n"));
537 cmd = new_runscript();
538 cmd->set_command(msg);
539 cmd->on_success = true;
540 cmd->on_failure = false;
541 cmd->when = SCRIPT_After;
543 jcr->RunScripts->append(cmd);
545 free_pool_memory(msg);
546 return dir->fsend(OKRunAfter);
549 static int runscript_cmd(JCR *jcr)
551 BSOCK *dir = jcr->dir_bsock;
552 POOLMEM *msg = get_memory(dir->msglen+1);
553 int on_success, on_failure, fail_on_error;
555 RUNSCRIPT *cmd = new_runscript() ;
557 Dmsg1(100, "runscript_cmd: '%s'\n", dir->msg);
558 /* Note, we cannot sscanf into bools */
559 if (sscanf(dir->msg, runscript, &on_success,
564 pm_strcpy(jcr->errmsg, dir->msg);
565 Jmsg1(jcr, M_FATAL, 0, _("Bad RunScript command: %s\n"), jcr->errmsg);
566 dir->fsend(_("2905 Bad RunScript command.\n"));
571 cmd->on_success = on_success;
572 cmd->on_failure = on_failure;
573 cmd->fail_on_error = fail_on_error;
576 cmd->set_command(msg);
578 jcr->RunScripts->append(cmd);
580 free_pool_memory(msg);
581 return dir->fsend(OKRunScript);
585 static bool init_fileset(JCR *jcr)
588 findFILESET *fileset;
597 fileset = (findFILESET *)malloc(sizeof(findFILESET));
598 memset(fileset, 0, sizeof(findFILESET));
599 ff->fileset = fileset;
600 fileset->state = state_none;
601 fileset->include_list.init(1, true);
602 fileset->exclude_list.init(1, true);
606 static findFOPTS *start_options(FF_PKT *ff)
608 int state = ff->fileset->state;
609 findINCEXE *incexe = ff->fileset->incexe;
611 if (state != state_options) {
612 ff->fileset->state = state_options;
613 findFOPTS *fo = (findFOPTS *)malloc(sizeof(findFOPTS));
614 memset(fo, 0, sizeof(findFOPTS));
615 fo->regex.init(1, true);
616 fo->regexdir.init(1, true);
617 fo->regexfile.init(1, true);
618 fo->wild.init(1, true);
619 fo->wilddir.init(1, true);
620 fo->wildfile.init(1, true);
621 fo->wildbase.init(1, true);
622 fo->base.init(1, true);
623 fo->fstype.init(1, true);
624 fo->drivetype.init(1, true);
625 incexe->current_opts = fo;
626 incexe->opts_list.append(fo);
628 return incexe->current_opts;
633 * Add fname to include/exclude fileset list. First check for
634 * | and < and if necessary perform command.
636 static void add_file_to_fileset(JCR *jcr, const char *fname, findFILESET *fileset,
651 p++; /* skip over | */
652 fn = get_pool_memory(PM_FNAME);
653 fn = edit_job_codes(jcr, fn, p, "");
654 bpipe = open_bpipe(fn, 0, "r");
657 Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
659 free_pool_memory(fn);
662 free_pool_memory(fn);
663 while (fgets(buf, sizeof(buf), bpipe->rfd)) {
664 strip_trailing_junk(buf);
666 fileset->incexe->name_list.append(new_dlistString(buf));
668 fileset->incexe->plugin_list.append(new_dlistString(buf));
671 if ((stat=close_bpipe(bpipe)) != 0) {
673 Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. stat=%d: ERR=%s\n"),
674 p, be.code(stat), be.bstrerror(stat));
679 Dmsg0(100, "Doing < include on client.\n");
680 p++; /* skip over < */
681 if ((ffd = fopen(p, "rb")) == NULL) {
683 Jmsg(jcr, M_FATAL, 0, _("Cannot open FileSet input file: %s. ERR=%s\n"),
687 while (fgets(buf, sizeof(buf), ffd)) {
688 strip_trailing_junk(buf);
689 Dmsg1(100, "%s\n", buf);
691 fileset->incexe->name_list.append(new_dlistString(buf));
693 fileset->incexe->plugin_list.append(new_dlistString(buf));
700 fileset->incexe->name_list.append(new_dlistString(fname));
702 fileset->incexe->plugin_list.append(new_dlistString(fname));
709 static void add_fileset(JCR *jcr, const char *item)
711 FF_PKT *ff = jcr->ff;
712 findFILESET *fileset = ff->fileset;
713 int state = fileset->state;
714 findFOPTS *current_opts;
716 /* Get code, optional subcode, and position item past the dividing space */
717 Dmsg1(100, "%s\n", item);
722 int subcode = ' '; /* A space is always a valid subcode */
723 if (item[0] != '\0' && item[0] != ' ') {
731 /* Skip all lines we receive after an error */
732 if (state == state_error) {
733 Dmsg0(100, "State=error return\n");
738 * The switch tests the code for validity.
739 * The subcode is always good if it is a space, otherwise we must confirm.
740 * We set state to state_error first assuming the subcode is invalid,
741 * requiring state to be set in cases below that handle subcodes.
743 if (subcode != ' ') {
745 Dmsg0(100, "Set state=error\n");
750 fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
751 memset(fileset->incexe, 0, sizeof(findINCEXE));
752 fileset->incexe->opts_list.init(1, true);
753 fileset->incexe->name_list.init(); /* for dlist; was 1,true for alist */
754 fileset->incexe->plugin_list.init();
755 fileset->include_list.append(fileset->incexe);
759 fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
760 memset(fileset->incexe, 0, sizeof(findINCEXE));
761 fileset->incexe->opts_list.init(1, true);
762 fileset->incexe->name_list.init();
763 fileset->incexe->plugin_list.init();
764 fileset->exclude_list.append(fileset->incexe);
770 /* File item to include or exclude list */
771 state = state_include;
772 add_file_to_fileset(jcr, item, fileset, true);
775 /* Plugin item to include list */
776 state = state_include;
777 add_file_to_fileset(jcr, item, fileset, false);
780 current_opts = start_options(ff);
784 preg = (regex_t *)malloc(sizeof(regex_t));
785 if (current_opts->flags & FO_IGNORECASE) {
786 rc = regcomp(preg, item, REG_EXTENDED|REG_ICASE);
788 rc = regcomp(preg, item, REG_EXTENDED);
791 regerror(rc, preg, prbuf, sizeof(prbuf));
794 Jmsg(jcr, M_FATAL, 0, _("REGEX %s compile error. ERR=%s\n"), item, prbuf);
798 state = state_options;
799 if (subcode == ' ') {
800 current_opts->regex.append(preg);
801 } else if (subcode == 'D') {
802 current_opts->regexdir.append(preg);
803 } else if (subcode == 'F') {
804 current_opts->regexfile.append(preg);
810 current_opts = start_options(ff);
811 current_opts->base.append(bstrdup(item));
812 state = state_options;
815 current_opts = start_options(ff);
816 state = state_options;
817 if (subcode == ' ') {
818 current_opts->fstype.append(bstrdup(item));
819 } else if (subcode == 'D') {
820 current_opts->drivetype.append(bstrdup(item));
826 current_opts = start_options(ff);
827 state = state_options;
828 if (subcode == ' ') {
829 current_opts->wild.append(bstrdup(item));
830 } else if (subcode == 'D') {
831 current_opts->wilddir.append(bstrdup(item));
832 } else if (subcode == 'F') {
833 current_opts->wildfile.append(bstrdup(item));
834 } else if (subcode == 'B') {
835 current_opts->wildbase.append(bstrdup(item));
841 current_opts = start_options(ff);
842 set_options(current_opts, item);
843 state = state_options;
846 current_opts = start_options(ff);
847 current_opts->reader = bstrdup(item);
848 state = state_options;
851 current_opts = start_options(ff);
852 current_opts->writer = bstrdup(item);
853 state = state_options;
856 Jmsg(jcr, M_FATAL, 0, _("Invalid FileSet command: %s\n"), item);
860 ff->fileset->state = state;
863 static bool term_fileset(JCR *jcr)
865 FF_PKT *ff = jcr->ff;
867 #ifdef xxx_DEBUG_CODE
868 findFILESET *fileset = ff->fileset;
871 for (i=0; i<fileset->include_list.size(); i++) {
872 findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
874 for (j=0; j<incexe->opts_list.size(); j++) {
875 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
876 for (k=0; k<fo->regex.size(); k++) {
877 Dmsg1(400, "R %s\n", (char *)fo->regex.get(k));
879 for (k=0; k<fo->regexdir.size(); k++) {
880 Dmsg1(400, "RD %s\n", (char *)fo->regexdir.get(k));
882 for (k=0; k<fo->regexfile.size(); k++) {
883 Dmsg1(400, "RF %s\n", (char *)fo->regexfile.get(k));
885 for (k=0; k<fo->wild.size(); k++) {
886 Dmsg1(400, "W %s\n", (char *)fo->wild.get(k));
888 for (k=0; k<fo->wilddir.size(); k++) {
889 Dmsg1(400, "WD %s\n", (char *)fo->wilddir.get(k));
891 for (k=0; k<fo->wildfile.size(); k++) {
892 Dmsg1(400, "WF %s\n", (char *)fo->wildfile.get(k));
894 for (k=0; k<fo->wildbase.size(); k++) {
895 Dmsg1(400, "WB %s\n", (char *)fo->wildbase.get(k));
897 for (k=0; k<fo->base.size(); k++) {
898 Dmsg1(400, "B %s\n", (char *)fo->base.get(k));
900 for (k=0; k<fo->fstype.size(); k++) {
901 Dmsg1(400, "X %s\n", (char *)fo->fstype.get(k));
903 for (k=0; k<fo->drivetype.size(); k++) {
904 Dmsg1(400, "XD %s\n", (char *)fo->drivetype.get(k));
907 Dmsg1(400, "D %s\n", fo->reader);
910 Dmsg1(400, "T %s\n", fo->writer);
914 foreach_dlist(node, &incexe->name_list) {
915 Dmsg1(400, "F %s\n", node->c_str());
917 foreach_dlist(node, &incexe->plugin_list) {
918 Dmsg1(400, "P %s\n", node->c_str());
921 for (i=0; i<fileset->exclude_list.size(); i++) {
922 findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
924 for (j=0; j<incexe->opts_list.size(); j++) {
925 findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
926 for (k=0; k<fo->regex.size(); k++) {
927 Dmsg1(400, "R %s\n", (char *)fo->regex.get(k));
929 for (k=0; k<fo->regexdir.size(); k++) {
930 Dmsg1(400, "RD %s\n", (char *)fo->regexdir.get(k));
932 for (k=0; k<fo->regexfile.size(); k++) {
933 Dmsg1(400, "RF %s\n", (char *)fo->regexfile.get(k));
935 for (k=0; k<fo->wild.size(); k++) {
936 Dmsg1(400, "W %s\n", (char *)fo->wild.get(k));
938 for (k=0; k<fo->wilddir.size(); k++) {
939 Dmsg1(400, "WD %s\n", (char *)fo->wilddir.get(k));
941 for (k=0; k<fo->wildfile.size(); k++) {
942 Dmsg1(400, "WF %s\n", (char *)fo->wildfile.get(k));
944 for (k=0; k<fo->wildbase.size(); k++) {
945 Dmsg1(400, "WB %s\n", (char *)fo->wildbase.get(k));
947 for (k=0; k<fo->base.size(); k++) {
948 Dmsg1(400, "B %s\n", (char *)fo->base.get(k));
950 for (k=0; k<fo->fstype.size(); k++) {
951 Dmsg1(400, "X %s\n", (char *)fo->fstype.get(k));
953 for (k=0; k<fo->drivetype.size(); k++) {
954 Dmsg1(400, "XD %s\n", (char *)fo->drivetype.get(k));
958 foreach_dlist(node, incexe->name_list) {
959 Dmsg1(400, "F %s\n", node->c_str());
961 foreach_dlist(node, &incexe->plugin_list) {
962 Dmsg1(400, "P %s\n", node->c_str());
966 return ff->fileset->state != state_error;
971 * As an optimization, we should do this during
972 * "compile" time in filed/job.c, and keep only a bit mask
973 * and the Verify options.
975 static void set_options(findFOPTS *fo, const char *opts)
981 // Commented out as it is not backward compatible - KES
983 // fo->flags |= FO_IGNORECASE; /* always ignorecase under windows */
986 for (p=opts; *p; p++) {
988 case 'a': /* alway replace */
989 case '0': /* no option */
992 fo->flags |= FO_EXCLUDE;
995 fo->flags |= FO_MULTIFS;
997 case 'h': /* no recursion */
998 fo->flags |= FO_NO_RECURSION;
1000 case 'H': /* no hard link handling */
1001 fo->flags |= FO_NO_HARDLINK;
1004 fo->flags |= FO_IGNORECASE;
1007 fo->flags |= FO_MD5;
1010 fo->flags |= FO_NOREPLACE;
1012 case 'p': /* use portable data format */
1013 fo->flags |= FO_PORTABLE;
1015 case 'R': /* Resource forks and Finder Info */
1016 fo->flags |= FO_HFSPLUS;
1017 case 'r': /* read fifo */
1018 fo->flags |= FO_READFIFO;
1023 fo->flags |= FO_SHA1;
1028 fo->flags |= FO_SHA256;
1032 fo->flags |= FO_SHA512;
1038 * If 2 or 3 is seen here, SHA2 is not configured, so
1039 * eat the option, and drop back to SHA-1.
1041 if (p[1] == '2' || p[1] == '3') {
1044 fo->flags |= FO_SHA1;
1049 fo->flags |= FO_SPARSE;
1052 fo->flags |= FO_MTIMEONLY;
1055 fo->flags |= FO_KEEPATIME;
1058 fo->flags |= FO_ACL;
1060 case 'V': /* verify options */
1061 /* Copy Verify Options */
1062 for (j=0; *p && *p != ':'; p++) {
1063 fo->VerifyOpts[j] = *p;
1064 if (j < (int)sizeof(fo->VerifyOpts) - 1) {
1068 fo->VerifyOpts[j] = 0;
1070 case 'P': /* strip path */
1073 for (j=0; *p && *p != ':'; p++) {
1075 if (j < (int)sizeof(strip) - 1) {
1080 fo->strip_path = atoi(strip);
1081 fo->flags |= FO_STRIPPATH;
1082 Dmsg2(100, "strip=%s strip_path=%d\n", strip, fo->strip_path);
1085 fo->flags |= FO_IF_NEWER;
1088 fo->flags |= FO_ENHANCEDWILD;
1090 case 'Z': /* gzip compression */
1091 fo->flags |= FO_GZIP;
1092 fo->GZIP_level = *++p - '0';
1095 fo->flags |= FO_NOATIME;
1098 fo->flags |= FO_CHKCHANGES;
1101 Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
1109 * Director is passing his Fileset
1111 static int fileset_cmd(JCR *jcr)
1113 BSOCK *dir = jcr->dir_bsock;
1115 #if defined(WIN32_VSS)
1118 sscanf(dir->msg, "fileset vss=%d", &vss);
1122 if (!init_fileset(jcr)) {
1125 while (dir->recv() >= 0) {
1126 strip_trailing_junk(dir->msg);
1127 Dmsg1(500, "Fileset: %s\n", dir->msg);
1128 add_fileset(jcr, dir->msg);
1130 if (!term_fileset(jcr)) {
1133 return bnet_fsend(dir, OKinc);
1136 static void free_bootstrap(JCR *jcr)
1138 if (jcr->RestoreBootstrap) {
1139 unlink(jcr->RestoreBootstrap);
1140 free_pool_memory(jcr->RestoreBootstrap);
1141 jcr->RestoreBootstrap = NULL;
1146 static pthread_mutex_t bsr_mutex = PTHREAD_MUTEX_INITIALIZER;
1147 static uint32_t bsr_uniq = 0;
1150 * The Director sends us the bootstrap file, which
1151 * we will in turn pass to the SD.
1153 static int bootstrap_cmd(JCR *jcr)
1155 BSOCK *dir = jcr->dir_bsock;
1156 POOLMEM *fname = get_pool_memory(PM_FNAME);
1159 free_bootstrap(jcr);
1162 Mmsg(fname, "%s/%s.%s.%d.bootstrap", me->working_directory, me->hdr.name,
1163 jcr->Job, bsr_uniq);
1165 Dmsg1(400, "bootstrap=%s\n", fname);
1166 jcr->RestoreBootstrap = fname;
1167 bs = fopen(fname, "a+b"); /* create file */
1170 Jmsg(jcr, M_FATAL, 0, _("Could not create bootstrap file %s: ERR=%s\n"),
1171 jcr->RestoreBootstrap, be.bstrerror());
1173 * Suck up what he is sending to us so that he will then
1174 * read our error message.
1176 while (dir->recv() >= 0)
1178 free_bootstrap(jcr);
1179 set_jcr_job_status(jcr, JS_ErrorTerminated);
1183 while (dir->recv() >= 0) {
1184 Dmsg1(200, "filed<dird: bootstrap file %s\n", dir->msg);
1185 fputs(dir->msg, bs);
1189 * Note, do not free the bootstrap yet -- it needs to be
1192 return dir->fsend(OKbootstrap);
1197 * Get backup level from Director
1200 static int level_cmd(JCR *jcr)
1202 BSOCK *dir = jcr->dir_bsock;
1203 POOLMEM *level, *buf = NULL;
1206 level = get_memory(dir->msglen+1);
1207 Dmsg1(110, "level_cmd: %s", dir->msg);
1208 if (sscanf(dir->msg, "level = %s ", level) != 1) {
1211 /* Base backup requested? */
1212 if (strcmp(level, "base") == 0) {
1213 jcr->JobLevel = L_BASE;
1214 /* Full backup requested? */
1215 } else if (strcmp(level, "full") == 0) {
1216 jcr->JobLevel = L_FULL;
1217 } else if (strcmp(level, "differential") == 0) {
1218 jcr->JobLevel = L_DIFFERENTIAL;
1221 } else if (strcmp(level, "incremental") == 0) {
1222 jcr->JobLevel = L_INCREMENTAL;
1226 * We get his UTC since time, then sync the clocks and correct it
1227 * to agree with our clock.
1229 } else if (strcmp(level, "since_utime") == 0) {
1230 buf = get_memory(dir->msglen+1);
1231 utime_t since_time, adj;
1232 btime_t his_time, bt_start, rt=0, bt_adj=0;
1233 if (jcr->JobLevel == L_NONE) {
1234 jcr->JobLevel = L_SINCE; /* if no other job level set, do it now */
1236 if (sscanf(dir->msg, "level = since_utime %s mtime_only=%d",
1237 buf, &mtime_only) != 2) {
1240 since_time = str_to_uint64(buf); /* this is the since time */
1241 Dmsg1(100, "since_time=%d\n", (int)since_time);
1242 char ed1[50], ed2[50];
1244 * Sync clocks by polling him for the time. We take
1245 * 10 samples of his time throwing out the first two.
1247 for (int i=0; i<10; i++) {
1248 bt_start = get_current_btime();
1249 dir->signal(BNET_BTIME); /* poll for time */
1250 if (dir->recv() <= 0) { /* get response */
1253 if (sscanf(dir->msg, "btime %s", buf) != 1) {
1256 if (i < 2) { /* toss first two results */
1259 his_time = str_to_uint64(buf);
1260 rt = get_current_btime() - bt_start; /* compute round trip time */
1261 Dmsg2(100, "Dirtime=%s FDtime=%s\n", edit_uint64(his_time, ed1),
1262 edit_uint64(bt_start, ed2));
1263 bt_adj += bt_start - his_time - rt/2;
1264 Dmsg2(100, "rt=%s adj=%s\n", edit_uint64(rt, ed1), edit_uint64(bt_adj, ed2));
1267 bt_adj = bt_adj / 8; /* compute average time */
1268 Dmsg2(100, "rt=%s adj=%s\n", edit_uint64(rt, ed1), edit_uint64(bt_adj, ed2));
1269 adj = btime_to_utime(bt_adj);
1270 since_time += adj; /* adjust for clock difference */
1271 /* Don't notify if time within 3 seconds */
1272 if (adj > 3 || adj < -3) {
1274 if (adj > 600 || adj < -600) {
1279 Jmsg(jcr, type, 0, _("DIR and FD clocks differ by %d seconds, FD automatically compensating.\n"), adj);
1281 dir->signal(BNET_EOD);
1283 Dmsg2(100, "adj = %d since_time=%d\n", (int)adj, (int)since_time);
1284 jcr->incremental = 1; /* set incremental or decremental backup */
1285 jcr->mtime = (time_t)since_time; /* set since time */
1287 Jmsg1(jcr, M_FATAL, 0, _("Unknown backup level: %s\n"), level);
1295 return dir->fsend(OKlevel);
1298 pm_strcpy(jcr->errmsg, dir->msg);
1299 Jmsg1(jcr, M_FATAL, 0, _("Bad level command: %s\n"), jcr->errmsg);
1308 * Get session parameters from Director -- this is for a Restore command
1310 static int session_cmd(JCR *jcr)
1312 BSOCK *dir = jcr->dir_bsock;
1314 Dmsg1(100, "SessionCmd: %s", dir->msg);
1315 if (sscanf(dir->msg, sessioncmd, jcr->VolumeName,
1316 &jcr->VolSessionId, &jcr->VolSessionTime,
1317 &jcr->StartFile, &jcr->EndFile,
1318 &jcr->StartBlock, &jcr->EndBlock) != 7) {
1319 pm_strcpy(jcr->errmsg, dir->msg);
1320 Jmsg(jcr, M_FATAL, 0, _("Bad session command: %s"), jcr->errmsg);
1324 return bnet_fsend(dir, OKsession);
1328 * Get address of storage daemon from Director
1331 static int storage_cmd(JCR *jcr)
1333 int stored_port; /* storage daemon port */
1334 int enable_ssl; /* enable ssl to sd */
1335 BSOCK *dir = jcr->dir_bsock;
1336 BSOCK *sd; /* storage daemon bsock */
1338 Dmsg1(100, "StorageCmd: %s", dir->msg);
1339 if (sscanf(dir->msg, storaddr, &jcr->stored_addr, &stored_port, &enable_ssl) != 3) {
1340 pm_strcpy(jcr->errmsg, dir->msg);
1341 Jmsg(jcr, M_FATAL, 0, _("Bad storage command: %s"), jcr->errmsg);
1344 Dmsg3(110, "Open storage: %s:%d ssl=%d\n", jcr->stored_addr, stored_port, enable_ssl);
1345 /* Open command communications with Storage daemon */
1346 /* Try to connect for 1 hour at 10 second intervals */
1347 sd = bnet_connect(jcr, 10, (int)me->SDConnectTimeout, me->heartbeat_interval,
1348 _("Storage daemon"), jcr->stored_addr, NULL, stored_port, 1);
1350 Jmsg(jcr, M_FATAL, 0, _("Failed to connect to Storage daemon: %s:%d\n"),
1351 jcr->stored_addr, stored_port);
1352 Dmsg2(100, "Failed to connect to Storage daemon: %s:%d\n",
1353 jcr->stored_addr, stored_port);
1356 Dmsg0(110, "Connection OK to SD.\n");
1358 jcr->store_bsock = sd;
1360 sd->fsend("Hello Start Job %s\n", jcr->Job);
1361 if (!authenticate_storagedaemon(jcr)) {
1362 Jmsg(jcr, M_FATAL, 0, _("Failed to authenticate Storage daemon.\n"));
1365 Dmsg0(110, "Authenticated with SD.\n");
1367 /* Send OK to Director */
1368 return dir->fsend(OKstore);
1375 static int backup_cmd(JCR *jcr)
1377 BSOCK *dir = jcr->dir_bsock;
1378 BSOCK *sd = jcr->store_bsock;
1382 #if defined(WIN32_VSS)
1383 // capture state here, if client is backed up by multiple directors
1384 // and one enables vss and the other does not then enable_vss can change
1385 // between here and where its evaluated after the job completes.
1386 jcr->VSS = g_pVSSClient && enable_vss;
1388 /* Run only one at a time */
1393 set_jcr_job_status(jcr, JS_Blocked);
1394 jcr->JobType = JT_BACKUP;
1395 Dmsg1(100, "begin backup ff=%p\n", jcr->ff);
1398 Jmsg(jcr, M_FATAL, 0, _("Cannot contact Storage daemon\n"));
1402 dir->fsend(OKbackup);
1403 Dmsg1(110, "bfiled>dird: %s", dir->msg);
1406 * Send Append Open Session to Storage daemon
1408 sd->fsend(append_open);
1409 Dmsg1(110, ">stored: %s", sd->msg);
1411 * Expect to receive back the Ticket number
1413 if (bget_msg(sd) >= 0) {
1414 Dmsg1(110, "<stored: %s", sd->msg);
1415 if (sscanf(sd->msg, OK_open, &jcr->Ticket) != 1) {
1416 Jmsg(jcr, M_FATAL, 0, _("Bad response to append open: %s\n"), sd->msg);
1419 Dmsg1(110, "Got Ticket=%d\n", jcr->Ticket);
1421 Jmsg(jcr, M_FATAL, 0, _("Bad response from stored to open command\n"));
1426 * Send Append data command to Storage daemon
1428 sd->fsend(append_data, jcr->Ticket);
1429 Dmsg1(110, ">stored: %s", sd->msg);
1432 * Expect to get OK data
1434 Dmsg1(110, "<stored: %s", sd->msg);
1435 if (!response(jcr, sd, OK_data, "Append Data")) {
1439 generate_daemon_event(jcr, "JobStart");
1440 generate_plugin_event(jcr, bEventJobStart);
1442 #if defined(WIN32_VSS)
1443 /* START VSS ON WIN 32 */
1445 if (g_pVSSClient->InitializeForBackup()) {
1446 /* tell vss which drives to snapshot */
1447 char szWinDriveLetters[27];
1448 if (get_win32_driveletters(jcr->ff, szWinDriveLetters)) {
1449 Jmsg(jcr, M_INFO, 0, _("Generate VSS snapshots. Driver=\"%s\", Drive(s)=\"%s\"\n"), g_pVSSClient->GetDriverName(), szWinDriveLetters);
1450 if (!g_pVSSClient->CreateSnapshots(szWinDriveLetters)) {
1451 Jmsg(jcr, M_WARNING, 0, _("Generate VSS snapshots failed.\n"));
1454 /* tell user if snapshot creation of a specific drive failed */
1456 for (i=0; i < (int)strlen(szWinDriveLetters); i++) {
1457 if (islower(szWinDriveLetters[i])) {
1458 Jmsg(jcr, M_WARNING, 0, _("Generate VSS snapshot of drive \"%c:\\\" failed. VSS support is disabled on this drive.\n"), szWinDriveLetters[i]);
1462 /* inform user about writer states */
1463 for (i=0; i < (int)g_pVSSClient->GetWriterCount(); i++)
1464 if (g_pVSSClient->GetWriterState(i) < 1) {
1465 Jmsg(jcr, M_WARNING, 0, _("VSS Writer (PrepareForBackup): %s\n"), g_pVSSClient->GetWriterInfo(i));
1470 Jmsg(jcr, M_INFO, 0, _("No drive letters found for generating VSS snapshots.\n"));
1474 Jmsg(jcr, M_WARNING, 0, _("VSS was not initialized properly. VSS support is disabled. ERR=%s\n"), be.bstrerror());
1480 * Send Files to Storage daemon
1482 Dmsg1(110, "begin blast ff=%p\n", (FF_PKT *)jcr->ff);
1483 if (!blast_data_to_storage_daemon(jcr, NULL)) {
1484 set_jcr_job_status(jcr, JS_ErrorTerminated);
1485 bnet_suppress_error_messages(sd, 1);
1486 bget_msg(sd); /* Read final response from append_data */
1487 Dmsg0(110, "Error in blast_data.\n");
1489 set_jcr_job_status(jcr, JS_Terminated);
1491 if (jcr->JobStatus != JS_Terminated) {
1492 bnet_suppress_error_messages(sd, 1);
1493 goto cleanup; /* bail out now */
1496 * Expect to get response to append_data from Storage daemon
1498 if (!response(jcr, sd, OK_append, "Append Data")) {
1499 set_jcr_job_status(jcr, JS_ErrorTerminated);
1504 * Send Append End Data to Storage daemon
1506 sd->fsend(append_end, jcr->Ticket);
1508 if (!response(jcr, sd, OK_end, "Append End")) {
1509 set_jcr_job_status(jcr, JS_ErrorTerminated);
1514 * Send Append Close to Storage daemon
1516 sd->fsend(append_close, jcr->Ticket);
1517 while (bget_msg(sd) >= 0) { /* stop on signal or error */
1518 if (sscanf(sd->msg, OK_close, &SDJobStatus) == 1) {
1520 Dmsg2(200, "SDJobStatus = %d %c\n", SDJobStatus, (char)SDJobStatus);
1524 Jmsg(jcr, M_FATAL, 0, _("Append Close with SD failed.\n"));
1527 if (SDJobStatus != JS_Terminated) {
1528 Jmsg(jcr, M_FATAL, 0, _("Bad status %d returned from Storage Daemon.\n"),
1534 #if defined(WIN32_VSS)
1535 /* STOP VSS ON WIN 32 */
1536 /* tell vss to close the backup session */
1538 if (g_pVSSClient->CloseBackup()) {
1539 /* inform user about writer states */
1540 for (int i=0; i<(int)g_pVSSClient->GetWriterCount(); i++) {
1541 int msg_type = M_INFO;
1542 if (g_pVSSClient->GetWriterState(i) < 1) {
1543 msg_type = M_WARNING;
1546 Jmsg(jcr, msg_type, 0, _("VSS Writer (BackupComplete): %s\n"), g_pVSSClient->GetWriterInfo(i));
1553 return 0; /* return and stop command loop */
1557 * Do a Verify for Director
1560 static int verify_cmd(JCR *jcr)
1562 BSOCK *dir = jcr->dir_bsock;
1563 BSOCK *sd = jcr->store_bsock;
1566 jcr->JobType = JT_VERIFY;
1567 if (sscanf(dir->msg, verifycmd, level) != 1) {
1568 dir->fsend(_("2994 Bad verify command: %s\n"), dir->msg);
1572 if (strcasecmp(level, "init") == 0) {
1573 jcr->JobLevel = L_VERIFY_INIT;
1574 } else if (strcasecmp(level, "catalog") == 0){
1575 jcr->JobLevel = L_VERIFY_CATALOG;
1576 } else if (strcasecmp(level, "volume") == 0){
1577 jcr->JobLevel = L_VERIFY_VOLUME_TO_CATALOG;
1578 } else if (strcasecmp(level, "data") == 0){
1579 jcr->JobLevel = L_VERIFY_DATA;
1580 } else if (strcasecmp(level, "disk_to_catalog") == 0) {
1581 jcr->JobLevel = L_VERIFY_DISK_TO_CATALOG;
1583 dir->fsend(_("2994 Bad verify level: %s\n"), dir->msg);
1587 dir->fsend(OKverify);
1589 generate_daemon_event(jcr, "JobStart");
1590 generate_plugin_event(jcr, bEventJobStart);
1592 Dmsg1(110, "bfiled>dird: %s", dir->msg);
1594 switch (jcr->JobLevel) {
1596 case L_VERIFY_CATALOG:
1599 case L_VERIFY_VOLUME_TO_CATALOG:
1600 if (!open_sd_read_session(jcr)) {
1603 start_dir_heartbeat(jcr);
1604 do_verify_volume(jcr);
1605 stop_dir_heartbeat(jcr);
1607 * Send Close session command to Storage daemon
1609 sd->fsend(read_close, jcr->Ticket);
1610 Dmsg1(130, "bfiled>stored: %s", sd->msg);
1612 /* ****FIXME**** check response */
1613 bget_msg(sd); /* get OK */
1615 /* Inform Storage daemon that we are done */
1616 sd->signal(BNET_TERMINATE);
1619 case L_VERIFY_DISK_TO_CATALOG:
1623 dir->fsend(_("2994 Bad verify level: %s\n"), dir->msg);
1627 dir->signal(BNET_EOD);
1629 return 0; /* return and terminate command loop */
1633 * Do a Restore for Director
1636 static int restore_cmd(JCR *jcr)
1638 BSOCK *dir = jcr->dir_bsock;
1639 BSOCK *sd = jcr->store_bsock;
1641 bool use_regexwhere=false;
1646 * Scan WHERE (base directory for restore) from command
1648 Dmsg0(150, "restore command\n");
1649 /* Pickup where string */
1650 args = get_memory(dir->msglen+1);
1653 if (sscanf(dir->msg, restorecmd, &replace, &prefix_links, args) != 3) {
1654 if (sscanf(dir->msg, restorecmdR, &replace, &prefix_links, args) != 3){
1655 if (sscanf(dir->msg, restorecmd1, &replace, &prefix_links) != 2) {
1656 pm_strcpy(jcr->errmsg, dir->msg);
1657 Jmsg(jcr, M_FATAL, 0, _("Bad replace command. CMD=%s\n"), jcr->errmsg);
1662 use_regexwhere = true;
1664 /* Turn / into nothing */
1665 if (IsPathSeparator(args[0]) && args[1] == '\0') {
1669 Dmsg2(150, "Got replace %c, where=%s\n", replace, args);
1670 unbash_spaces(args);
1672 if (use_regexwhere) {
1673 jcr->where_bregexp = get_bregexps(args);
1674 if (!jcr->where_bregexp) {
1675 Jmsg(jcr, M_FATAL, 0, _("Bad where regexp. where=%s\n"), args);
1676 free_pool_memory(args);
1680 jcr->where = bstrdup(args);
1683 free_pool_memory(args);
1684 jcr->replace = replace;
1685 jcr->prefix_links = prefix_links;
1687 dir->fsend(OKrestore);
1688 Dmsg1(110, "bfiled>dird: %s", dir->msg);
1690 jcr->JobType = JT_RESTORE;
1692 set_jcr_job_status(jcr, JS_Blocked);
1694 if (!open_sd_read_session(jcr)) {
1695 set_jcr_job_status(jcr, JS_ErrorTerminated);
1699 set_jcr_job_status(jcr, JS_Running);
1702 * Do restore of files and data
1704 start_dir_heartbeat(jcr);
1705 generate_daemon_event(jcr, "JobStart");
1706 generate_plugin_event(jcr, bEventJobStart);
1708 stop_dir_heartbeat(jcr);
1710 set_jcr_job_status(jcr, JS_Terminated);
1711 if (jcr->JobStatus != JS_Terminated) {
1712 bnet_suppress_error_messages(sd, 1);
1716 * Send Close session command to Storage daemon
1718 sd->fsend(read_close, jcr->Ticket);
1719 Dmsg1(130, "bfiled>stored: %s", sd->msg);
1721 bget_msg(sd); /* get OK */
1723 /* Inform Storage daemon that we are done */
1724 sd->signal(BNET_TERMINATE);
1729 set_jcr_job_status(jcr, JS_ErrorTerminated);
1732 Dmsg0(130, "Done in job.c\n");
1733 return 0; /* return and terminate command loop */
1736 static int open_sd_read_session(JCR *jcr)
1738 BSOCK *sd = jcr->store_bsock;
1741 Jmsg(jcr, M_FATAL, 0, _("Improper calling sequence.\n"));
1744 Dmsg4(120, "VolSessId=%ld VolsessT=%ld SF=%ld EF=%ld\n",
1745 jcr->VolSessionId, jcr->VolSessionTime, jcr->StartFile, jcr->EndFile);
1746 Dmsg2(120, "JobId=%d vol=%s\n", jcr->JobId, "DummyVolume");
1748 * Open Read Session with Storage daemon
1750 bnet_fsend(sd, read_open, "DummyVolume",
1751 jcr->VolSessionId, jcr->VolSessionTime, jcr->StartFile, jcr->EndFile,
1752 jcr->StartBlock, jcr->EndBlock);
1753 Dmsg1(110, ">stored: %s", sd->msg);
1758 if (bget_msg(sd) >= 0) {
1759 Dmsg1(110, "bfiled<stored: %s", sd->msg);
1760 if (sscanf(sd->msg, OK_open, &jcr->Ticket) != 1) {
1761 Jmsg(jcr, M_FATAL, 0, _("Bad response to SD read open: %s\n"), sd->msg);
1764 Dmsg1(110, "bfiled: got Ticket=%d\n", jcr->Ticket);
1766 Jmsg(jcr, M_FATAL, 0, _("Bad response from stored to read open command\n"));
1770 if (!send_bootstrap_file(jcr)) {
1775 * Start read of data with Storage daemon
1777 bnet_fsend(sd, read_data, jcr->Ticket);
1778 Dmsg1(110, ">stored: %s", sd->msg);
1783 if (!response(jcr, sd, OK_data, "Read Data")) {
1790 * Destroy the Job Control Record and associated
1791 * resources (sockets).
1793 static void filed_free_jcr(JCR *jcr)
1795 if (jcr->store_bsock) {
1796 bnet_close(jcr->store_bsock);
1798 free_bootstrap(jcr);
1799 if (jcr->last_fname) {
1800 free_pool_memory(jcr->last_fname);
1802 free_runscripts(jcr->RunScripts);
1803 delete jcr->RunScripts;
1809 * Get response from Storage daemon to a command we
1810 * sent. Check that the response is OK.
1812 * Returns: 0 on failure
1815 int response(JCR *jcr, BSOCK *sd, char *resp, const char *cmd)
1820 if (bget_msg(sd) > 0) {
1821 Dmsg0(110, sd->msg);
1822 if (strcmp(sd->msg, resp) == 0) {
1826 if (job_canceled(jcr)) {
1827 return 0; /* if canceled avoid useless error messages */
1829 if (is_bnet_error(sd)) {
1830 Jmsg2(jcr, M_FATAL, 0, _("Comm error with SD. bad response to %s. ERR=%s\n"),
1831 cmd, bnet_strerror(sd));
1833 Jmsg3(jcr, M_FATAL, 0, _("Bad response to %s command. Wanted %s, got %s\n"),
1834 cmd, resp, sd->msg);
1839 static int send_bootstrap_file(JCR *jcr)
1843 BSOCK *sd = jcr->store_bsock;
1844 const char *bootstrap = "bootstrap\n";
1847 Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
1848 if (!jcr->RestoreBootstrap) {
1851 bs = fopen(jcr->RestoreBootstrap, "rb");
1854 Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"),
1855 jcr->RestoreBootstrap, be.bstrerror());
1856 set_jcr_job_status(jcr, JS_ErrorTerminated);
1859 sd->msglen = pm_strcpy(sd->msg, bootstrap);
1861 while (fgets(buf, sizeof(buf), bs)) {
1862 sd->msglen = Mmsg(sd->msg, "%s", buf);
1865 bnet_sig(sd, BNET_EOD);
1867 if (!response(jcr, sd, OKSDbootstrap, "Bootstrap")) {
1868 set_jcr_job_status(jcr, JS_ErrorTerminated);
1874 free_bootstrap(jcr);