]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/job.c
- Convert more atoi to str_to_int64() for DB.
[bacula/bacula] / bacula / src / filed / job.c
1 /*
2  *  Bacula File Daemon Job processing
3  *
4  *    Kern Sibbald, October MM
5  *
6  *   Version $Id$
7  *
8  */
9 /*
10    Copyright (C) 2000-2005 Kern Sibbald
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation; either version 2 of
15    the License, or (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20    General Public License for more details.
21
22    You should have received a copy of the GNU General Public
23    License along with this program; if not, write to the Free
24    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25    MA 02111-1307, USA.
26
27  */
28
29 #include "bacula.h"
30 #include "filed.h"
31
32 extern char my_name[];
33 extern CLIENT *me;                    /* our client resource */
34
35 /* Imported functions */
36 extern int status_cmd(JCR *jcr);
37 extern int qstatus_cmd(JCR *jcr);
38
39 /* Forward referenced functions */
40 static int backup_cmd(JCR *jcr);
41 static int bootstrap_cmd(JCR *jcr);
42 static int cancel_cmd(JCR *jcr);
43 static int setdebug_cmd(JCR *jcr);
44 static int estimate_cmd(JCR *jcr);
45 static int hello_cmd(JCR *jcr);
46 static int job_cmd(JCR *jcr);
47 static int fileset_cmd(JCR *jcr);
48 static int level_cmd(JCR *jcr);
49 static int verify_cmd(JCR *jcr);
50 static int restore_cmd(JCR *jcr);
51 static int storage_cmd(JCR *jcr);
52 static int session_cmd(JCR *jcr);
53 static int response(JCR *jcr, BSOCK *sd, char *resp, const char *cmd);
54 static void filed_free_jcr(JCR *jcr);
55 static int open_sd_read_session(JCR *jcr);
56 static int send_bootstrap_file(JCR *jcr);
57 static int runbefore_cmd(JCR *jcr);
58 static int runafter_cmd(JCR *jcr);
59 static bool run_cmd(JCR *jcr, char *cmd, const char *name);
60 static void set_options(findFOPTS *fo, const char *opts);
61
62
63 /* Exported functions */
64
65 struct s_cmds {
66    const char *cmd;
67    int (*func)(JCR *);
68    int monitoraccess; /* specify if monitors have access to this function */
69 };
70
71 /*
72  * The following are the recognized commands from the Director.
73  */
74 static struct s_cmds cmds[] = {
75    {"backup",       backup_cmd,    0},
76    {"cancel",       cancel_cmd,    0},
77    {"setdebug=",    setdebug_cmd,  0},
78    {"estimate",     estimate_cmd,  0},
79    {"Hello",        hello_cmd,     1},
80    {"fileset",      fileset_cmd,   0},
81    {"JobId=",       job_cmd,       0},
82    {"level = ",     level_cmd,     0},
83    {"restore",      restore_cmd,   0},
84    {"session",      session_cmd,   0},
85    {"status",       status_cmd,    1},
86    {".status",      qstatus_cmd,   1},
87    {"storage ",     storage_cmd,   0},
88    {"verify",       verify_cmd,    0},
89    {"bootstrap",    bootstrap_cmd, 0},
90    {"RunBeforeJob", runbefore_cmd, 0},
91    {"RunAfterJob",  runafter_cmd,  0},
92    {NULL,       NULL}                  /* list terminator */
93 };
94
95 /* Commands received from director that need scanning */
96 static char jobcmd[]      = "JobId=%d Job=%127s SDid=%d SDtime=%d Authorization=%100s";
97 static char storaddr[]    = "storage address=%s port=%d ssl=%d\n";
98 static char sessioncmd[]  = "session %127s %ld %ld %ld %ld %ld %ld\n";
99 static char restorecmd[]  = "restore replace=%c prelinks=%d where=%s\n";
100 static char restorecmd1[] = "restore replace=%c prelinks=%d where=\n";
101 static char verifycmd[]   = "verify level=%30s\n";
102 static char estimatecmd[] = "estimate listing=%d\n";
103 static char runbefore[]   = "RunBeforeJob %s\n";
104 static char runafter[]    = "RunAfterJob %s\n";
105
106 /* Responses sent to Director */
107 static char errmsg[]      = "2999 Invalid command\n";
108 static char no_auth[]     = "2998 No Authorization\n";
109 static char illegal_cmd[] = "2997 Illegal command for a Director with Monitor directive enabled\n";
110 static char OKinc[]       = "2000 OK include\n";
111 static char OKest[]       = "2000 OK estimate files=%u bytes=%s\n";
112 static char OKlevel[]     = "2000 OK level\n";
113 static char OKbackup[]    = "2000 OK backup\n";
114 static char OKbootstrap[] = "2000 OK bootstrap\n";
115 static char OKverify[]    = "2000 OK verify\n";
116 static char OKrestore[]   = "2000 OK restore\n";
117 static char OKsession[]   = "2000 OK session\n";
118 static char OKstore[]     = "2000 OK storage\n";
119 static char OKjob[]       = "2000 OK Job %s,%s,%s";
120 static char OKsetdebug[]  = "2000 OK setdebug=%d\n";
121 static char BADjob[]      = "2901 Bad Job\n";
122 static char EndJob[]      = "2800 End Job TermCode=%d JobFiles=%u ReadBytes=%s JobBytes=%s Errors=%u\n";
123 static char OKRunBefore[] = "2000 OK RunBefore\n";
124 static char OKRunAfter[]  = "2000 OK RunAfter\n";
125
126 /* Responses received from Storage Daemon */
127 static char OK_end[]       = "3000 OK end\n";
128 static char OK_close[]     = "3000 OK close Status = %d\n";
129 static char OK_open[]      = "3000 OK open ticket = %d\n";
130 static char OK_data[]      = "3000 OK data\n";
131 static char OK_append[]    = "3000 OK append data\n";
132 static char OKSDbootstrap[] = "3000 OK bootstrap\n";
133
134
135 /* Commands sent to Storage Daemon */
136 static char append_open[]  = "append open session\n";
137 static char append_data[]  = "append data %d\n";
138 static char append_end[]   = "append end session %d\n";
139 static char append_close[] = "append close session %d\n";
140 static char read_open[]    = "read open session = %s %ld %ld %ld %ld %ld %ld\n";
141 static char read_data[]    = "read data %d\n";
142 static char read_close[]   = "read close session %d\n";
143
144 /*
145  * Accept requests from a Director
146  *
147  * NOTE! We are running as a separate thread
148  *
149  * Send output one line
150  * at a time followed by a zero length transmission.
151  *
152  * Return when the connection is terminated or there
153  * is an error.
154  *
155  * Basic task here is:
156  *   Authenticate Director (during Hello command).
157  *   Accept commands one at a time from the Director
158  *     and execute them.
159  *
160  */
161 void *handle_client_request(void *dirp)
162 {
163    int i;
164    bool found, quit;
165    JCR *jcr;
166    BSOCK *dir = (BSOCK *)dirp;
167
168    jcr = new_jcr(sizeof(JCR), filed_free_jcr); /* create JCR */
169    jcr->dir_bsock = dir;
170    jcr->ff = init_find_files();
171    jcr->start_time = time(NULL);
172    jcr->last_fname = get_pool_memory(PM_FNAME);
173    jcr->last_fname[0] = 0;
174    jcr->client_name = get_memory(strlen(my_name) + 1);
175    pm_strcpy(jcr->client_name, my_name);
176    dir->jcr = jcr;
177    enable_backup_privileges(NULL, 1 /* ignore_errors */);
178
179    /**********FIXME******* add command handler error code */
180
181    for (quit=false; !quit;) {
182
183       /* Read command */
184       if (bnet_recv(dir) < 0) {
185          break;               /* connection terminated */
186       }
187       dir->msg[dir->msglen] = 0;
188       Dmsg1(100, "<dird: %s", dir->msg);
189       found = false;
190       for (i=0; cmds[i].cmd; i++) {
191          if (strncmp(cmds[i].cmd, dir->msg, strlen(cmds[i].cmd)) == 0) {
192             found = true;         /* indicate command found */
193             if (!jcr->authenticated && cmds[i].func != hello_cmd) {
194                bnet_fsend(dir, no_auth);
195                bnet_sig(dir, BNET_EOD);
196                break;
197             }
198             if ((jcr->authenticated) && (!cmds[i].monitoraccess) && (jcr->director->monitor)) {
199                Dmsg1(100, "Command %s illegal.\n", cmds[i].cmd);
200                bnet_fsend(dir, illegal_cmd);
201                bnet_sig(dir, BNET_EOD);
202                break;
203             }
204             Dmsg1(100, "Executing %s command.\n", cmds[i].cmd);
205             if (!cmds[i].func(jcr)) {         /* do command */
206                quit = true;         /* error or fully terminated, get out */
207                Dmsg0(20, "Quit command loop due to command error or Job done.\n");
208             }
209             break;
210          }
211       }
212       if (!found) {              /* command not found */
213          bnet_fsend(dir, errmsg);
214          quit = true;
215          break;
216       }
217    }
218
219    /* Inform Storage daemon that we are done */
220    if (jcr->store_bsock) {
221       bnet_sig(jcr->store_bsock, BNET_TERMINATE);
222    }
223
224    if (jcr->RunAfterJob && !job_canceled(jcr)) {
225       run_cmd(jcr, jcr->RunAfterJob, "ClientRunAfterJob");
226    }
227    dequeue_messages(jcr);             /* send any queued messages */
228
229    /* Inform Director that we are done */
230    bnet_sig(dir, BNET_TERMINATE);
231
232    /* Clean up fileset */
233    FF_PKT *ff = (FF_PKT *)jcr->ff;
234    findFILESET *fileset = ff->fileset;
235    if (fileset) {
236       int i, j, k;
237       /* Delete FileSet Include lists */
238       for (i=0; i<fileset->include_list.size(); i++) {
239          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
240          for (j=0; j<incexe->opts_list.size(); j++) {
241             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
242             for (k=0; k<fo->regex.size(); k++) {
243                regfree((regex_t *)fo->regex.get(k));
244             }
245             fo->regex.destroy();
246             fo->regexdir.destroy();
247             fo->regexfile.destroy();
248             fo->wild.destroy();
249             fo->wilddir.destroy();
250             fo->wildfile.destroy();
251             fo->base.destroy();
252             fo->fstype.destroy();
253             if (fo->reader) {
254                free(fo->reader);
255             }
256             if (fo->writer) {
257                free(fo->writer);
258             }
259          }
260          incexe->opts_list.destroy();
261          incexe->name_list.destroy();
262       }
263       fileset->include_list.destroy();
264
265       /* Delete FileSet Exclude lists */
266       for (i=0; i<fileset->exclude_list.size(); i++) {
267          findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
268          for (j=0; j<incexe->opts_list.size(); j++) {
269             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
270             fo->regex.destroy();
271             fo->regexdir.destroy();
272             fo->regexfile.destroy();
273             fo->wild.destroy();
274             fo->wilddir.destroy();
275             fo->wildfile.destroy();
276             fo->base.destroy();
277             fo->fstype.destroy();
278          }
279          incexe->opts_list.destroy();
280          incexe->name_list.destroy();
281       }
282       fileset->exclude_list.destroy();
283       free(fileset);
284    }
285    ff->fileset = NULL;
286    Dmsg0(100, "Calling term_find_files\n");
287    term_find_files((FF_PKT *)jcr->ff);
288    jcr->ff = NULL;
289    Dmsg0(100, "Done with term_find_files\n");
290    free_jcr(jcr);                     /* destroy JCR record */
291    Dmsg0(100, "Done with free_jcr\n");
292    return NULL;
293 }
294
295 /*
296  * Hello from Director he must identify himself and provide his
297  *  password.
298  */
299 static int hello_cmd(JCR *jcr)
300 {
301    Dmsg0(120, "Calling Authenticate\n");
302    if (!authenticate_director(jcr)) {
303       return 0;
304    }
305    Dmsg0(120, "OK Authenticate\n");
306    jcr->authenticated = true;
307    return 1;
308 }
309
310 /*
311  * Cancel a Job
312  */
313 static int cancel_cmd(JCR *jcr)
314 {
315    BSOCK *dir = jcr->dir_bsock;
316    char Job[MAX_NAME_LENGTH];
317    JCR *cjcr;
318
319    if (sscanf(dir->msg, "cancel Job=%127s", Job) == 1) {
320       if (!(cjcr=get_jcr_by_full_name(Job))) {
321          bnet_fsend(dir, "2901 Job %s not found.\n", Job);
322       } else {
323          if (cjcr->store_bsock) {
324             P(cjcr->mutex);
325             cjcr->store_bsock->timed_out = 1;
326             cjcr->store_bsock->terminated = 1;
327 /*
328  * #if !defined(HAVE_CYGWIN) && !defined(HAVE_WIN32)
329  */
330 #if !defined(HAVE_CYGWIN)
331             pthread_kill(cjcr->my_thread_id, TIMEOUT_SIGNAL);
332 #endif
333             V(cjcr->mutex);
334          }
335          set_jcr_job_status(cjcr, JS_Canceled);
336          free_jcr(cjcr);
337          bnet_fsend(dir, _("2001 Job %s marked to be canceled.\n"), Job);
338       }
339    } else {
340       bnet_fsend(dir, _("2902 Error scanning cancel command.\n"));
341    }
342    bnet_sig(dir, BNET_EOD);
343    return 1;
344 }
345
346
347 /*
348  * Set debug level as requested by the Director
349  *
350  */
351 static int setdebug_cmd(JCR *jcr)
352 {
353    BSOCK *dir = jcr->dir_bsock;
354    int level, trace_flag;
355
356    Dmsg1(110, "setdebug_cmd: %s", dir->msg);
357    if (sscanf(dir->msg, "setdebug=%d trace=%d", &level, &trace_flag) != 2 || level < 0) {
358       pm_strcpy(jcr->errmsg, dir->msg);
359       bnet_fsend(dir, "2991 Bad setdebug command: %s\n", jcr->errmsg);
360       return 0;
361    }
362    debug_level = level;
363    set_trace(trace_flag);
364    return bnet_fsend(dir, OKsetdebug, level);
365 }
366
367
368 static int estimate_cmd(JCR *jcr)
369 {
370    BSOCK *dir = jcr->dir_bsock;
371    char ed2[50];
372
373    if (sscanf(dir->msg, estimatecmd, &jcr->listing) != 1) {
374       pm_strcpy(jcr->errmsg, dir->msg);
375       Jmsg(jcr, M_FATAL, 0, _("Bad estimate command: %s"), jcr->errmsg);
376       bnet_fsend(dir, "2992 Bad estimate command.\n");
377       return 0;
378    }
379    make_estimate(jcr);
380    bnet_fsend(dir, OKest, jcr->num_files_examined,
381       edit_uint64_with_commas(jcr->JobBytes, ed2));
382    bnet_sig(dir, BNET_EOD);
383    return 1;
384 }
385
386 /*
387  * Get JobId and Storage Daemon Authorization key from Director
388  */
389 static int job_cmd(JCR *jcr)
390 {
391    BSOCK *dir = jcr->dir_bsock;
392    POOLMEM *sd_auth_key;
393
394    sd_auth_key = get_memory(dir->msglen);
395    if (sscanf(dir->msg, jobcmd,  &jcr->JobId, jcr->Job,
396               &jcr->VolSessionId, &jcr->VolSessionTime,
397               sd_auth_key) != 5) {
398       pm_strcpy(jcr->errmsg, dir->msg);
399       Jmsg(jcr, M_FATAL, 0, _("Bad Job Command: %s"), jcr->errmsg);
400       bnet_fsend(dir, BADjob);
401       free_pool_memory(sd_auth_key);
402       return 0;
403    }
404    jcr->sd_auth_key = bstrdup(sd_auth_key);
405    free_pool_memory(sd_auth_key);
406    Dmsg2(120, "JobId=%d Auth=%s\n", jcr->JobId, jcr->sd_auth_key);
407    return bnet_fsend(dir, OKjob, HOST_OS, DISTNAME, DISTVER);
408 }
409
410 static int runbefore_cmd(JCR *jcr)
411 {
412    bool ok;
413    BSOCK *dir = jcr->dir_bsock;
414    POOLMEM *cmd = get_memory(dir->msglen+1);
415
416    Dmsg1(100, "runbefore_cmd: %s", dir->msg);
417    if (sscanf(dir->msg, runbefore, cmd) != 1) {
418       pm_strcpy(jcr->errmsg, dir->msg);
419       Jmsg1(jcr, M_FATAL, 0, _("Bad RunBeforeJob command: %s\n"), jcr->errmsg);
420       bnet_fsend(dir, "2905 Bad RunBeforeJob command.\n");
421       free_memory(cmd);
422       return 0;
423    }
424    unbash_spaces(cmd);
425
426    /* Run the command now */
427    ok = run_cmd(jcr, cmd, "ClientRunBeforeJob");
428    free_memory(cmd);
429    if (ok) {
430       bnet_fsend(dir, OKRunBefore);
431       return 1;
432    } else {
433       bnet_fsend(dir, "2905 Bad RunBeforeJob command.\n");
434       return 0;
435    }
436 }
437
438 static int runafter_cmd(JCR *jcr)
439 {
440    BSOCK *dir = jcr->dir_bsock;
441    POOLMEM *msg = get_memory(dir->msglen+1);
442
443    Dmsg1(100, "runafter_cmd: %s", dir->msg);
444    if (sscanf(dir->msg, runafter, msg) != 1) {
445       pm_strcpy(jcr->errmsg, dir->msg);
446       Jmsg1(jcr, M_FATAL, 0, _("Bad RunAfter command: %s\n"), jcr->errmsg);
447       bnet_fsend(dir, "2905 Bad RunAfterJob command.\n");
448       free_memory(msg);
449       return 0;
450    }
451    unbash_spaces(msg);
452    if (jcr->RunAfterJob) {
453       free_pool_memory(jcr->RunAfterJob);
454    }
455    jcr->RunAfterJob = get_pool_memory(PM_FNAME);
456    pm_strcpy(jcr->RunAfterJob, msg);
457    free_pool_memory(msg);
458    return bnet_fsend(dir, OKRunAfter);
459 }
460
461 static bool run_cmd(JCR *jcr, char *cmd, const char *name)
462 {
463    POOLMEM *ecmd = get_pool_memory(PM_FNAME);
464    int status;
465    BPIPE *bpipe;
466    char line[MAXSTRING];
467
468    ecmd = edit_job_codes(jcr, ecmd, cmd, "");
469    bpipe = open_bpipe(ecmd, 0, "r");
470    free_pool_memory(ecmd);
471    if (bpipe == NULL) {
472       berrno be;
473       Jmsg(jcr, M_FATAL, 0, _("%s could not execute. ERR=%s\n"), name,
474          be.strerror());
475       return false;
476    }
477    while (fgets(line, sizeof(line), bpipe->rfd)) {
478       int len = strlen(line);
479       if (len > 0 && line[len-1] == '\n') {
480          line[len-1] = 0;
481       }
482       Jmsg(jcr, M_INFO, 0, _("%s: %s\n"), name, line);
483    }
484    status = close_bpipe(bpipe);
485    if (status != 0) {
486       berrno be;
487       Jmsg(jcr, M_FATAL, 0, _("%s returned non-zero status=%d. ERR=%s\n"), name,
488          status, be.strerror(status));
489       return false;
490    }
491    return true;
492 }
493
494 static bool init_fileset(JCR *jcr)
495 {
496    FF_PKT *ff;
497    findFILESET *fileset;
498
499    if (!jcr->ff) {
500       return false;
501    }
502    ff = (FF_PKT *)jcr->ff;
503    if (ff->fileset) {
504       return false;
505    }
506    fileset = (findFILESET *)malloc(sizeof(findFILESET));
507    memset(fileset, 0, sizeof(findFILESET));
508    ff->fileset = fileset;
509    fileset->state = state_none;
510    fileset->include_list.init(1, true);
511    fileset->exclude_list.init(1, true);
512    return true;
513 }
514
515 static findFOPTS *start_options(FF_PKT *ff)
516 {
517    int state = ff->fileset->state;
518    findINCEXE *incexe = ff->fileset->incexe;
519
520    if (state != state_options) {
521       ff->fileset->state = state_options;
522       findFOPTS *fo = (findFOPTS *)malloc(sizeof(findFOPTS));
523       memset(fo, 0, sizeof(findFOPTS));
524       fo->regex.init(1, true);
525       fo->regexdir.init(1, true);
526       fo->regexfile.init(1, true);
527       fo->wild.init(1, true);
528       fo->wilddir.init(1, true);
529       fo->wildfile.init(1, true);
530       fo->base.init(1, true);
531       fo->fstype.init(1, true);
532       incexe->current_opts = fo;
533       incexe->opts_list.append(fo);
534    }
535    return incexe->current_opts;
536
537 }
538
539 /*
540  * Add fname to include/exclude fileset list. First check for
541  * | and < and if necessary perform command.
542  */
543 static void add_file_to_fileset(JCR *jcr, const char *fname, findFILESET *fileset)
544 {
545    char *p;
546    BPIPE *bpipe;
547    POOLMEM *fn;
548    FILE *ffd;
549    char buf[1000];
550    int stat;
551
552    p = (char *)fname;
553    switch (*p) {
554    case '|':
555       p++;                            /* skip over | */
556       fn = get_pool_memory(PM_FNAME);
557       fn = edit_job_codes(jcr, fn, p, "");
558       bpipe = open_bpipe(fn, 0, "r");
559       free_pool_memory(fn);
560       if (!bpipe) {
561          Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
562             p, strerror(errno));
563          return;
564       }
565       while (fgets(buf, sizeof(buf), bpipe->rfd)) {
566          strip_trailing_junk(buf);
567          fileset->incexe->name_list.append(bstrdup(buf));
568       }
569       if ((stat=close_bpipe(bpipe)) != 0) {
570          Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. RtnStat=%d ERR=%s\n"),
571             p, stat, strerror(errno));
572          return;
573       }
574       break;
575    case '<':
576       Dmsg0(100, "Doing < include on client.\n");
577       p++;                      /* skip over < */
578       if ((ffd = fopen(p, "r")) == NULL) {
579          berrno be;
580          Jmsg(jcr, M_FATAL, 0, _("Cannot open FileSet input file: %s. ERR=%s\n"),
581             p, be.strerror());
582          return;
583       }
584       while (fgets(buf, sizeof(buf), ffd)) {
585          strip_trailing_junk(buf);
586          Dmsg1(100, "%s\n", buf);
587          fileset->incexe->name_list.append(bstrdup(buf));
588       }
589       fclose(ffd);
590       break;
591    default:
592       fileset->incexe->name_list.append(bstrdup(fname));
593       break;
594    }
595 }
596
597
598 static void add_fileset(JCR *jcr, const char *item)
599 {
600    FF_PKT *ff = (FF_PKT *)jcr->ff;
601    findFILESET *fileset = ff->fileset;
602    int state = fileset->state;
603    findFOPTS *current_opts;
604
605    /* Get code, optional subcode, and position item past the dividing space */
606    Dmsg1(100, "%s\n", item);
607    int code = item[0];
608    if (code != '\0') {
609       ++item;
610    }
611    int subcode = ' ';               /* A space is always a valid subcode */
612    if (item[0] != '\0' && item[0] != ' ') {
613       subcode = item[0];
614       ++item;
615    }
616    if (*item == ' ') {
617       ++item;
618    }
619
620    /* Skip all lines we receive after an error */
621    if (state == state_error) {
622       return;
623    }
624
625    /*
626     * The switch tests the code for validity.
627     * The subcode is always good if it is a space, otherwise we must confirm.
628     * We set state to state_error first assuming the subcode is invalid,
629     * requiring state to be set in cases below that handle subcodes.
630     */
631    if (subcode != ' ') {
632       state = state_error;
633    }
634    switch (code) {
635    case 'I':
636       /* New include */
637       fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
638       memset(fileset->incexe, 0, sizeof(findINCEXE));
639       fileset->incexe->opts_list.init(1, true);
640       fileset->incexe->name_list.init(1, true);
641       fileset->include_list.append(fileset->incexe);
642       break;
643    case 'E':
644       /* New exclude */
645       fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
646       memset(fileset->incexe, 0, sizeof(findINCEXE));
647       fileset->incexe->opts_list.init(1, true);
648       fileset->incexe->name_list.init(1, true);
649       fileset->exclude_list.append(fileset->incexe);
650       break;
651    case 'N':
652       state = state_none;
653       break;
654    case 'F':
655       /* File item to either include/include list */
656       state = state_include;
657       add_file_to_fileset(jcr, item, fileset);
658       break;
659    case 'R':
660       current_opts = start_options(ff);
661       regex_t *preg;
662       int rc;
663       char prbuf[500];
664       preg = (regex_t *)malloc(sizeof(regex_t));
665       if (current_opts->flags & FO_IGNORECASE) {
666          rc = regcomp(preg, item, REG_EXTENDED|REG_ICASE);
667       } else {
668          rc = regcomp(preg, item, REG_EXTENDED);
669       }
670       if (rc != 0) {
671          regerror(rc, preg, prbuf, sizeof(prbuf));
672          regfree(preg);
673          free(preg);
674          Jmsg(jcr, M_FATAL, 0, "REGEX %s compile error. ERR=%s\n", item, prbuf);
675          state = state_error;
676          break;
677       }
678       state = state_options;
679       if (subcode == ' ') {
680          current_opts->regex.append(preg);
681       } else if (subcode == 'D') {
682          current_opts->regexdir.append(preg);
683       } else if (subcode == 'F') {
684          current_opts->regexfile.append(preg);
685       } else {
686          state = state_error;
687       }
688       break;
689    case 'B':
690       current_opts = start_options(ff);
691       current_opts->base.append(bstrdup(item));
692       state = state_options;
693       break;
694    case 'X':
695       current_opts = start_options(ff);
696       current_opts->fstype.append(bstrdup(item));
697       state = state_options;
698       break;
699    case 'W':
700       current_opts = start_options(ff);
701       state = state_options;
702       if (subcode == ' ') {
703          current_opts->wild.append(bstrdup(item));
704       } else if (subcode == 'D') {
705          current_opts->wilddir.append(bstrdup(item));
706       } else if (subcode == 'F') {
707          current_opts->wildfile.append(bstrdup(item));
708       } else {
709          state = state_error;
710       }
711       break;
712    case 'O':
713       current_opts = start_options(ff);
714       set_options(current_opts, item);
715       state = state_options;
716       break;
717    case 'D':
718       current_opts = start_options(ff);
719       current_opts->reader = bstrdup(item);
720       state = state_options;
721       break;
722    case 'T':
723       current_opts = start_options(ff);
724       current_opts->writer = bstrdup(item);
725       state = state_options;
726       break;
727    default:
728       Jmsg(jcr, M_FATAL, 0, "Invalid FileSet command: %s\n", item);
729       state = state_error;
730       break;
731    }
732    ff->fileset->state = state;
733 }
734
735 static bool term_fileset(JCR *jcr)
736 {
737    FF_PKT *ff = (FF_PKT *)jcr->ff;
738    findFILESET *fileset = ff->fileset;
739    int i, j, k;
740
741    for (i=0; i<fileset->include_list.size(); i++) {
742       findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
743       Dmsg0(400, "I\n");
744       for (j=0; j<incexe->opts_list.size(); j++) {
745          findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
746          for (k=0; k<fo->regex.size(); k++) {
747             Dmsg1(400, "R %s\n", (char *)fo->regex.get(k));
748          }
749          for (k=0; k<fo->regexdir.size(); k++) {
750             Dmsg1(400, "RD %s\n", (char *)fo->regexdir.get(k));
751          }
752          for (k=0; k<fo->regexfile.size(); k++) {
753             Dmsg1(400, "RF %s\n", (char *)fo->regexfile.get(k));
754          }
755          for (k=0; k<fo->wild.size(); k++) {
756             Dmsg1(400, "W %s\n", (char *)fo->wild.get(k));
757          }
758          for (k=0; k<fo->wilddir.size(); k++) {
759             Dmsg1(400, "WD %s\n", (char *)fo->wilddir.get(k));
760          }
761          for (k=0; k<fo->wildfile.size(); k++) {
762             Dmsg1(400, "WF %s\n", (char *)fo->wildfile.get(k));
763          }
764          for (k=0; k<fo->base.size(); k++) {
765             Dmsg1(400, "B %s\n", (char *)fo->base.get(k));
766          }
767          for (k=0; k<fo->fstype.size(); k++) {
768             Dmsg1(400, "X %s\n", (char *)fo->fstype.get(k));
769          }
770          if (fo->reader) {
771             Dmsg1(400, "D %s\n", fo->reader);
772          }
773          if (fo->writer) {
774             Dmsg1(400, "T %s\n", fo->writer);
775          }
776       }
777       for (j=0; j<incexe->name_list.size(); j++) {
778          Dmsg1(400, "F %s\n", (char *)incexe->name_list.get(j));
779       }
780    }
781    for (i=0; i<fileset->exclude_list.size(); i++) {
782       findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
783       Dmsg0(400, "E\n");
784       for (j=0; j<incexe->opts_list.size(); j++) {
785          findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
786          for (k=0; k<fo->regex.size(); k++) {
787             Dmsg1(400, "R %s\n", (char *)fo->regex.get(k));
788          }
789          for (k=0; k<fo->regexdir.size(); k++) {
790             Dmsg1(400, "RD %s\n", (char *)fo->regexdir.get(k));
791          }
792          for (k=0; k<fo->regexfile.size(); k++) {
793             Dmsg1(400, "RF %s\n", (char *)fo->regexfile.get(k));
794          }
795          for (k=0; k<fo->wild.size(); k++) {
796             Dmsg1(400, "W %s\n", (char *)fo->wild.get(k));
797          }
798          for (k=0; k<fo->wilddir.size(); k++) {
799             Dmsg1(400, "WD %s\n", (char *)fo->wilddir.get(k));
800          }
801          for (k=0; k<fo->wildfile.size(); k++) {
802             Dmsg1(400, "WF %s\n", (char *)fo->wildfile.get(k));
803          }
804          for (k=0; k<fo->base.size(); k++) {
805             Dmsg1(400, "B %s\n", (char *)fo->base.get(k));
806          }
807          for (k=0; k<fo->fstype.size(); k++) {
808             Dmsg1(400, "X %s\n", (char *)fo->fstype.get(k));
809          }
810       }
811       for (j=0; j<incexe->name_list.size(); j++) {
812          Dmsg1(400, "F %s\n", (char *)incexe->name_list.get(j));
813       }
814    }
815    return ff->fileset->state != state_error;
816 }
817
818
819 /*
820  * As an optimization, we should do this during
821  *  "compile" time in filed/job.c, and keep only a bit mask
822  *  and the Verify options.
823  */
824 static void set_options(findFOPTS *fo, const char *opts)
825 {
826    int j;
827    const char *p;
828
829    for (p=opts; *p; p++) {
830       switch (*p) {
831       case 'a':                 /* alway replace */
832       case '0':                 /* no option */
833          break;
834       case 'e':
835          fo->flags |= FO_EXCLUDE;
836          break;
837       case 'f':
838          fo->flags |= FO_MULTIFS;
839          break;
840       case 'h':                 /* no recursion */
841          fo->flags |= FO_NO_RECURSION;
842          break;
843       case 'H':                 /* no hard link handling */
844          fo->flags |= FO_NO_HARDLINK;
845          break;
846       case 'i':
847          fo->flags |= FO_IGNORECASE;
848          break;
849       case 'M':                 /* MD5 */
850          fo->flags |= FO_MD5;
851          break;
852       case 'n':
853          fo->flags |= FO_NOREPLACE;
854          break;
855       case 'p':                 /* use portable data format */
856          fo->flags |= FO_PORTABLE;
857          break;
858       case 'R':                 /* Resource forks and Finder Info */
859          fo->flags |= FO_HFSPLUS;
860       case 'r':                 /* read fifo */
861          fo->flags |= FO_READFIFO;
862          break;
863       case 'S':
864          fo->flags |= FO_SHA1;
865          break;
866       case 's':
867          fo->flags |= FO_SPARSE;
868          break;
869       case 'm':
870          fo->flags |= FO_MTIMEONLY;
871          break;
872       case 'k':
873          fo->flags |= FO_KEEPATIME;
874          break;
875       case 'A':
876          fo->flags |= FO_ACL;
877          break;
878       case 'V':                  /* verify options */
879          /* Copy Verify Options */
880          for (j=0; *p && *p != ':'; p++) {
881             fo->VerifyOpts[j] = *p;
882             if (j < (int)sizeof(fo->VerifyOpts) - 1) {
883                j++;
884             }
885          }
886          fo->VerifyOpts[j] = 0;
887          break;
888       case 'w':
889          fo->flags |= FO_IF_NEWER;
890          break;
891       case 'Z':                 /* gzip compression */
892          fo->flags |= FO_GZIP;
893          fo->GZIP_level = *++p - '0';
894          Dmsg1(200, "Compression level=%d\n", fo->GZIP_level);
895          break;
896       default:
897          Emsg1(M_ERROR, 0, "Unknown include/exclude option: %c\n", *p);
898          break;
899       }
900    }
901 }
902
903
904 /*
905  * Director is passing his Fileset
906  */
907 static int fileset_cmd(JCR *jcr)
908 {
909    BSOCK *dir = jcr->dir_bsock;
910
911    if (!init_fileset(jcr)) {
912       return 0;
913    }
914    while (bnet_recv(dir) >= 0) {
915       strip_trailing_junk(dir->msg);
916       Dmsg1(400, "Fileset: %s\n", dir->msg);
917       add_fileset(jcr, dir->msg);
918    }
919    if (!term_fileset(jcr)) {
920       return 0;
921    }
922    return bnet_fsend(dir, OKinc);
923 }
924
925
926 static int bootstrap_cmd(JCR *jcr)
927 {
928    BSOCK *dir = jcr->dir_bsock;
929    POOLMEM *fname = get_pool_memory(PM_FNAME);
930    FILE *bs;
931
932    if (jcr->RestoreBootstrap) {
933       unlink(jcr->RestoreBootstrap);
934       free_pool_memory(jcr->RestoreBootstrap);
935    }
936    Mmsg(fname, "%s/%s.%s.bootstrap", me->working_directory, me->hdr.name,
937       jcr->Job);
938    Dmsg1(400, "bootstrap=%s\n", fname);
939    jcr->RestoreBootstrap = fname;
940    bs = fopen(fname, "a+");           /* create file */
941    if (!bs) {
942       berrno be;
943       /*
944        * Suck up what he is sending to us so that he will then
945        *   read our error message.
946        */
947       while (bnet_recv(dir) >= 0)
948         {  }
949
950       Jmsg(jcr, M_FATAL, 0, _("Could not create bootstrap file %s: ERR=%s\n"),
951          jcr->RestoreBootstrap, be.strerror());
952       free_pool_memory(jcr->RestoreBootstrap);
953       jcr->RestoreBootstrap = NULL;
954       set_jcr_job_status(jcr, JS_ErrorTerminated);
955       return 0;
956    }
957
958    while (bnet_recv(dir) >= 0) {
959        Dmsg1(200, "filed<dird: bootstrap file %s\n", dir->msg);
960        fputs(dir->msg, bs);
961    }
962    fclose(bs);
963
964    return bnet_fsend(dir, OKbootstrap);
965 }
966
967
968 /*
969  * Get backup level from Director
970  *
971  */
972 static int level_cmd(JCR *jcr)
973 {
974    BSOCK *dir = jcr->dir_bsock;
975    POOLMEM *level, *buf = NULL;
976    int mtime_only;
977
978    level = get_memory(dir->msglen+1);
979    Dmsg1(110, "level_cmd: %s", dir->msg);
980    if (sscanf(dir->msg, "level = %s ", level) != 1) {
981       goto bail_out;
982    }
983    /* Base backup requested? */
984    if (strcmp(level, "base") == 0) {
985       jcr->JobLevel = L_BASE;
986    /* Full backup requested? */
987    } else if (strcmp(level, "full") == 0) {
988       jcr->JobLevel = L_FULL;
989    } else if (strcmp(level, "differential") == 0) {
990       jcr->JobLevel = L_DIFFERENTIAL;
991       free_memory(level);
992       return 1;
993    } else if (strcmp(level, "incremental") == 0) {
994       jcr->JobLevel = L_INCREMENTAL;
995       free_memory(level);
996       return 1;   
997    /*
998     * We get his UTC since time, then sync the clocks and correct it
999     *   to agree with our clock.
1000     */
1001    } else if (strcmp(level, "since_utime") == 0) {
1002       buf = get_memory(dir->msglen+1);
1003       utime_t since_time, adj;
1004       btime_t his_time, bt_start, rt=0, bt_adj=0;
1005       if (jcr->JobLevel == L_NONE) {
1006          jcr->JobLevel = L_SINCE;     /* if no other job level set, do it now */
1007       }
1008       if (sscanf(dir->msg, "level = since_utime %s mtime_only=%d",
1009                  buf, &mtime_only) != 2) {
1010          goto bail_out;
1011       }
1012       since_time = str_to_uint64(buf);  /* this is the since time */
1013       char ed1[50], ed2[50];
1014       /*
1015        * Sync clocks by polling him for the time. We take
1016        *   10 samples of his time throwing out the first two.
1017        */
1018       for (int i=0; i<10; i++) {
1019          bt_start = get_current_btime();
1020          bnet_sig(dir, BNET_BTIME);   /* poll for time */
1021          if (bnet_recv(dir) <= 0) {   /* get response */
1022             goto bail_out;
1023          }
1024          if (sscanf(dir->msg, "btime %s", buf) != 1) {
1025             goto bail_out;
1026          }
1027          if (i < 2) {                 /* toss first two results */
1028             continue;
1029          }
1030          his_time = str_to_uint64(buf);
1031          rt = get_current_btime() - bt_start; /* compute round trip time */
1032          bt_adj -= his_time - bt_start - rt/2;
1033          Dmsg2(200, "rt=%s adj=%s\n", edit_uint64(rt, ed1), edit_uint64(bt_adj, ed2));
1034       }
1035
1036       bt_adj = bt_adj / 8;            /* compute average time */
1037       Dmsg2(100, "rt=%s adj=%s\n", edit_uint64(rt, ed1), edit_uint64(bt_adj, ed2));
1038       adj = btime_to_utime(bt_adj);
1039       since_time += adj;              /* adjust for clock difference */
1040       if (adj != 0) {
1041          Jmsg(jcr, M_INFO, 0, _("DIR and FD clocks differ by %d seconds, FD automatically adjusting.\n"), adj);
1042       }
1043       bnet_sig(dir, BNET_EOD);
1044
1045       Dmsg2(100, "adj = %d since_time=%d\n", (int)adj, (int)since_time);
1046       jcr->incremental = 1;           /* set incremental or decremental backup */
1047       jcr->mtime = (time_t)since_time; /* set since time */
1048    } else {
1049       Jmsg1(jcr, M_FATAL, 0, "Unknown backup level: %s\n", level);
1050       free_memory(level);
1051       return 0;
1052    }
1053    free_memory(level);
1054    if (buf) {
1055       free_memory(buf);
1056    }
1057    return bnet_fsend(dir, OKlevel);
1058
1059 bail_out:
1060    pm_strcpy(jcr->errmsg, dir->msg);
1061    Jmsg1(jcr, M_FATAL, 0, _("Bad level command: %s\n"), jcr->errmsg);
1062    free_memory(level);
1063    if (buf) {
1064       free_memory(buf);
1065    }
1066    return 0;
1067 }
1068
1069 /*
1070  * Get session parameters from Director -- this is for a Restore command
1071  */
1072 static int session_cmd(JCR *jcr)
1073 {
1074    BSOCK *dir = jcr->dir_bsock;
1075
1076    Dmsg1(100, "SessionCmd: %s", dir->msg);
1077    if (sscanf(dir->msg, sessioncmd, jcr->VolumeName,
1078               &jcr->VolSessionId, &jcr->VolSessionTime,
1079               &jcr->StartFile, &jcr->EndFile,
1080               &jcr->StartBlock, &jcr->EndBlock) != 7) {
1081       pm_strcpy(jcr->errmsg, dir->msg);
1082       Jmsg(jcr, M_FATAL, 0, "Bad session command: %s", jcr->errmsg);
1083       return 0;
1084    }
1085
1086    return bnet_fsend(dir, OKsession);
1087 }
1088
1089 /*
1090  * Get address of storage daemon from Director
1091  *
1092  */
1093 static int storage_cmd(JCR *jcr)
1094 {
1095    int stored_port;                /* storage daemon port */
1096    int enable_ssl;                 /* enable ssl to sd */
1097    BSOCK *dir = jcr->dir_bsock;
1098    BSOCK *sd;                         /* storage daemon bsock */
1099
1100    Dmsg1(100, "StorageCmd: %s", dir->msg);
1101    if (sscanf(dir->msg, storaddr, &jcr->stored_addr, &stored_port, &enable_ssl) != 3) {
1102       pm_strcpy(jcr->errmsg, dir->msg);
1103       Jmsg(jcr, M_FATAL, 0, _("Bad storage command: %s"), jcr->errmsg);
1104       return 0;
1105    }
1106    Dmsg3(110, "Open storage: %s:%d ssl=%d\n", jcr->stored_addr, stored_port, enable_ssl);
1107    /* Open command communications with Storage daemon */
1108    /* Try to connect for 1 hour at 10 second intervals */
1109    sd = bnet_connect(jcr, 10, (int)me->SDConnectTimeout, _("Storage daemon"),
1110                      jcr->stored_addr, NULL, stored_port, 1);
1111    if (sd == NULL) {
1112       Jmsg(jcr, M_FATAL, 0, _("Failed to connect to Storage daemon: %s:%d\n"),
1113           jcr->stored_addr, stored_port);
1114       Dmsg2(100, "Failed to connect to Storage daemon: %s:%d\n",
1115           jcr->stored_addr, stored_port);
1116       return 0;
1117    }
1118    Dmsg0(110, "Connection OK to SD.\n");
1119
1120    jcr->store_bsock = sd;
1121
1122    bnet_fsend(sd, "Hello Start Job %s\n", jcr->Job);
1123    if (!authenticate_storagedaemon(jcr)) {
1124       Jmsg(jcr, M_FATAL, 0, _("Failed to authenticate Storage daemon.\n"));
1125       return 0;
1126    }
1127    Dmsg0(110, "Authenticated with SD.\n");
1128
1129    /* Send OK to Director */
1130    return bnet_fsend(dir, OKstore);
1131 }
1132
1133
1134 /*
1135  * Do a backup. For now, we handle only Full and Incremental.
1136  */
1137 static int backup_cmd(JCR *jcr)
1138 {
1139    BSOCK *dir = jcr->dir_bsock;
1140    BSOCK *sd = jcr->store_bsock;
1141    int ok = 0;
1142    int SDJobStatus;
1143    char ed1[50], ed2[50];
1144
1145    set_jcr_job_status(jcr, JS_Blocked);
1146    jcr->JobType = JT_BACKUP;
1147    Dmsg1(100, "begin backup ff=%p\n", (FF_PKT *)jcr->ff);
1148
1149    if (sd == NULL) {
1150       Jmsg(jcr, M_FATAL, 0, _("Cannot contact Storage daemon\n"));
1151       goto cleanup;
1152    }
1153
1154    bnet_fsend(dir, OKbackup);
1155    Dmsg1(110, "bfiled>dird: %s", dir->msg);
1156
1157    /*
1158     * Send Append Open Session to Storage daemon
1159     */
1160    bnet_fsend(sd, append_open);
1161    Dmsg1(110, ">stored: %s", sd->msg);
1162    /*
1163     * Expect to receive back the Ticket number
1164     */
1165    if (bget_msg(sd) >= 0) {
1166       Dmsg1(110, "<stored: %s", sd->msg);
1167       if (sscanf(sd->msg, OK_open, &jcr->Ticket) != 1) {
1168          Jmsg(jcr, M_FATAL, 0, _("Bad response to append open: %s\n"), sd->msg);
1169          goto cleanup;
1170       }
1171       Dmsg1(110, "Got Ticket=%d\n", jcr->Ticket);
1172    } else {
1173       Jmsg(jcr, M_FATAL, 0, _("Bad response from stored to open command\n"));
1174       goto cleanup;
1175    }
1176
1177    /*
1178     * Send Append data command to Storage daemon
1179     */
1180    bnet_fsend(sd, append_data, jcr->Ticket);
1181    Dmsg1(110, ">stored: %s", sd->msg);
1182
1183    /*
1184     * Expect to get OK data
1185     */
1186    Dmsg1(110, "<stored: %s", sd->msg);
1187    if (!response(jcr, sd, OK_data, "Append Data")) {
1188       goto cleanup;
1189    }
1190
1191    /*
1192     * Send Files to Storage daemon
1193     */
1194    Dmsg1(110, "begin blast ff=%p\n", (FF_PKT *)jcr->ff);
1195    if (!blast_data_to_storage_daemon(jcr, NULL)) {
1196       set_jcr_job_status(jcr, JS_ErrorTerminated);
1197       bnet_suppress_error_messages(sd, 1);
1198       Dmsg0(110, "Error in blast_data.\n");
1199    } else {
1200       set_jcr_job_status(jcr, JS_Terminated);
1201       if (jcr->JobStatus != JS_Terminated) {
1202          bnet_suppress_error_messages(sd, 1);
1203          goto cleanup;                /* bail out now */
1204       }
1205       /*
1206        * Expect to get response to append_data from Storage daemon
1207        */
1208       if (!response(jcr, sd, OK_append, "Append Data")) {
1209          set_jcr_job_status(jcr, JS_ErrorTerminated);
1210          goto cleanup;
1211       }
1212
1213       /*
1214        * Send Append End Data to Storage daemon
1215        */
1216       bnet_fsend(sd, append_end, jcr->Ticket);
1217       /* Get end OK */
1218       if (!response(jcr, sd, OK_end, "Append End")) {
1219          set_jcr_job_status(jcr, JS_ErrorTerminated);
1220          goto cleanup;
1221       }
1222
1223       /*
1224        * Send Append Close to Storage daemon
1225        */
1226       bnet_fsend(sd, append_close, jcr->Ticket);
1227       while (bget_msg(sd) >= 0) {    /* stop on signal or error */
1228          if (sscanf(sd->msg, OK_close, &SDJobStatus) == 1) {
1229             ok = 1;
1230             Dmsg2(200, "SDJobStatus = %d %c\n", SDJobStatus, (char)SDJobStatus);
1231          }
1232       }
1233       if (!ok) {
1234          Jmsg(jcr, M_FATAL, 0, _("Append Close with SD failed.\n"));
1235          goto cleanup;
1236       }
1237       if (SDJobStatus != JS_Terminated) {
1238          Jmsg(jcr, M_FATAL, 0, _("Bad status %d returned from Storage Daemon.\n"),
1239             SDJobStatus);
1240       }
1241    }
1242
1243 cleanup:
1244    bnet_fsend(dir, EndJob, jcr->JobStatus, jcr->JobFiles,
1245       edit_uint64(jcr->ReadBytes, ed1),
1246       edit_uint64(jcr->JobBytes, ed2), jcr->Errors);
1247    Dmsg1(110, "End FD msg: %s\n", dir->msg);
1248
1249    return 0;                          /* return and stop command loop */
1250 }
1251
1252 /*
1253  * Do a Verify for Director
1254  *
1255  */
1256 static int verify_cmd(JCR *jcr)
1257 {
1258    BSOCK *dir = jcr->dir_bsock;
1259    BSOCK *sd  = jcr->store_bsock;
1260    char level[100], ed1[50], ed2[50];
1261
1262    jcr->JobType = JT_VERIFY;
1263    if (sscanf(dir->msg, verifycmd, level) != 1) {
1264       bnet_fsend(dir, "2994 Bad verify command: %s\n", dir->msg);
1265       return 0;
1266    }
1267    if (strcasecmp(level, "init") == 0) {
1268       jcr->JobLevel = L_VERIFY_INIT;
1269    } else if (strcasecmp(level, "catalog") == 0){
1270       jcr->JobLevel = L_VERIFY_CATALOG;
1271    } else if (strcasecmp(level, "volume") == 0){
1272       jcr->JobLevel = L_VERIFY_VOLUME_TO_CATALOG;
1273    } else if (strcasecmp(level, "data") == 0){
1274       jcr->JobLevel = L_VERIFY_DATA;
1275    } else if (strcasecmp(level, "disk_to_catalog") == 0) {
1276       jcr->JobLevel = L_VERIFY_DISK_TO_CATALOG;
1277    } else {
1278       bnet_fsend(dir, "2994 Bad verify level: %s\n", dir->msg);
1279       return 0;
1280    }
1281
1282    bnet_fsend(dir, OKverify);
1283    Dmsg1(110, "bfiled>dird: %s", dir->msg);
1284
1285    switch (jcr->JobLevel) {
1286    case L_VERIFY_INIT:
1287    case L_VERIFY_CATALOG:
1288       do_verify(jcr);
1289       break;
1290    case L_VERIFY_VOLUME_TO_CATALOG:
1291       if (!open_sd_read_session(jcr)) {
1292          return 0;
1293       }
1294       start_dir_heartbeat(jcr);
1295       do_verify_volume(jcr);
1296       stop_dir_heartbeat(jcr);
1297       /*
1298        * Send Close session command to Storage daemon
1299        */
1300       bnet_fsend(sd, read_close, jcr->Ticket);
1301       Dmsg1(130, "bfiled>stored: %s", sd->msg);
1302
1303       /* ****FIXME**** check response */
1304       bget_msg(sd);                      /* get OK */
1305
1306       /* Inform Storage daemon that we are done */
1307       bnet_sig(sd, BNET_TERMINATE);
1308
1309       break;
1310    case L_VERIFY_DISK_TO_CATALOG:
1311       do_verify(jcr);
1312       break;
1313    default:
1314       bnet_fsend(dir, "2994 Bad verify level: %s\n", dir->msg);
1315       return 0;
1316    }
1317
1318    bnet_sig(dir, BNET_EOD);
1319
1320    /* Send termination status back to Dir */
1321    bnet_fsend(dir, EndJob, jcr->JobStatus, jcr->JobFiles,
1322       edit_uint64(jcr->ReadBytes, ed1),
1323       edit_uint64(jcr->JobBytes, ed2), jcr->Errors);
1324
1325    /* Inform Director that we are done */
1326    bnet_sig(dir, BNET_TERMINATE);
1327    return 0;                          /* return and terminate command loop */
1328 }
1329
1330 /*
1331  * Do a Restore for Director
1332  *
1333  */
1334 static int restore_cmd(JCR *jcr)
1335 {
1336    BSOCK *dir = jcr->dir_bsock;
1337    BSOCK *sd = jcr->store_bsock;
1338    POOLMEM *where;
1339    int prefix_links;
1340    char replace;
1341    char ed1[50], ed2[50];
1342
1343    /*
1344     * Scan WHERE (base directory for restore) from command
1345     */
1346    Dmsg0(150, "restore command\n");
1347    /* Pickup where string */
1348    where = get_memory(dir->msglen+1);
1349    *where = 0;
1350
1351    if (sscanf(dir->msg, restorecmd, &replace, &prefix_links, where) != 3) {
1352       if (sscanf(dir->msg, restorecmd1, &replace, &prefix_links) != 2) {
1353          pm_strcpy(jcr->errmsg, dir->msg);
1354          Jmsg(jcr, M_FATAL, 0, _("Bad replace command. CMD=%s\n"), jcr->errmsg);
1355          return 0;
1356       }
1357       *where = 0;
1358    }
1359    /* Turn / into nothing */
1360    if (where[0] == '/' && where[1] == 0) {
1361       where[0] = 0;
1362    }
1363
1364    Dmsg2(150, "Got replace %c, where=%s\n", replace, where);
1365    unbash_spaces(where);
1366    jcr->where = bstrdup(where);
1367    free_pool_memory(where);
1368    jcr->replace = replace;
1369    jcr->prefix_links = prefix_links;
1370
1371    bnet_fsend(dir, OKrestore);
1372    Dmsg1(110, "bfiled>dird: %s", dir->msg);
1373
1374    jcr->JobType = JT_RESTORE;
1375
1376    set_jcr_job_status(jcr, JS_Blocked);
1377    if (!open_sd_read_session(jcr)) {
1378       set_jcr_job_status(jcr, JS_ErrorTerminated);
1379       goto bail_out;
1380    }
1381
1382    set_jcr_job_status(jcr, JS_Running);
1383
1384    /*
1385     * Do restore of files and data
1386     */
1387    start_dir_heartbeat(jcr);
1388    do_restore(jcr);
1389    stop_dir_heartbeat(jcr);
1390
1391    set_jcr_job_status(jcr, JS_Terminated);
1392    if (jcr->JobStatus != JS_Terminated) {
1393       bnet_suppress_error_messages(sd, 1);
1394    }
1395
1396    /*
1397     * Send Close session command to Storage daemon
1398     */
1399    bnet_fsend(sd, read_close, jcr->Ticket);
1400    Dmsg1(130, "bfiled>stored: %s", sd->msg);
1401
1402    bget_msg(sd);                      /* get OK */
1403
1404    /* Inform Storage daemon that we are done */
1405    bnet_sig(sd, BNET_TERMINATE);
1406
1407 bail_out:
1408
1409    if (jcr->Errors) {
1410       set_jcr_job_status(jcr, JS_ErrorTerminated);
1411    }
1412    /* Send termination status back to Dir */
1413    bnet_fsend(dir, EndJob, jcr->JobStatus, jcr->JobFiles,
1414       edit_uint64(jcr->ReadBytes, ed1),
1415       edit_uint64(jcr->JobBytes, ed2), jcr->Errors);
1416
1417    /* Inform Director that we are done */
1418    bnet_sig(dir, BNET_TERMINATE);
1419
1420    Dmsg0(130, "Done in job.c\n");
1421    return 0;                          /* return and terminate command loop */
1422 }
1423
1424 static int open_sd_read_session(JCR *jcr)
1425 {
1426    BSOCK *sd = jcr->store_bsock;
1427
1428    if (!sd) {
1429       Jmsg(jcr, M_FATAL, 0, _("Improper calling sequence.\n"));
1430       return 0;
1431    }
1432    Dmsg4(120, "VolSessId=%ld VolsessT=%ld SF=%ld EF=%ld\n",
1433       jcr->VolSessionId, jcr->VolSessionTime, jcr->StartFile, jcr->EndFile);
1434    Dmsg2(120, "JobId=%d vol=%s\n", jcr->JobId, "DummyVolume");
1435    /*
1436     * Open Read Session with Storage daemon
1437     */
1438    bnet_fsend(sd, read_open, jcr->VolumeName,
1439       jcr->VolSessionId, jcr->VolSessionTime, jcr->StartFile, jcr->EndFile,
1440       jcr->StartBlock, jcr->EndBlock);
1441    Dmsg1(110, ">stored: %s", sd->msg);
1442
1443    /*
1444     * Get ticket number
1445     */
1446    if (bget_msg(sd) >= 0) {
1447       Dmsg1(110, "bfiled<stored: %s", sd->msg);
1448       if (sscanf(sd->msg, OK_open, &jcr->Ticket) != 1) {
1449          Jmsg(jcr, M_FATAL, 0, _("Bad response to SD read open: %s\n"), sd->msg);
1450          return 0;
1451       }
1452       Dmsg1(110, "bfiled: got Ticket=%d\n", jcr->Ticket);
1453    } else {
1454       Jmsg(jcr, M_FATAL, 0, _("Bad response from stored to read open command\n"));
1455       return 0;
1456    }
1457
1458    if (!send_bootstrap_file(jcr)) {
1459       return 0;
1460    }
1461
1462    /*
1463     * Start read of data with Storage daemon
1464     */
1465    bnet_fsend(sd, read_data, jcr->Ticket);
1466    Dmsg1(110, ">stored: %s", sd->msg);
1467
1468    /*
1469     * Get OK data
1470     */
1471    if (!response(jcr, sd, OK_data, "Read Data")) {
1472       return 0;
1473    }
1474    return 1;
1475 }
1476
1477 /*
1478  * Destroy the Job Control Record and associated
1479  * resources (sockets).
1480  */
1481 static void filed_free_jcr(JCR *jcr)
1482 {
1483    if (jcr->store_bsock) {
1484       bnet_close(jcr->store_bsock);
1485    }
1486    if (jcr->RestoreBootstrap) {
1487       unlink(jcr->RestoreBootstrap);
1488       free_pool_memory(jcr->RestoreBootstrap);
1489       jcr->RestoreBootstrap = NULL;
1490    }
1491    if (jcr->last_fname) {
1492       free_pool_memory(jcr->last_fname);
1493    }
1494    if (jcr->RunAfterJob) {
1495       free_pool_memory(jcr->RunAfterJob);
1496    }
1497
1498
1499    return;
1500 }
1501
1502 /*
1503  * Get response from Storage daemon to a command we
1504  * sent. Check that the response is OK.
1505  *
1506  *  Returns: 0 on failure
1507  *           1 on success
1508  */
1509 int response(JCR *jcr, BSOCK *sd, char *resp, const char *cmd)
1510 {
1511    if (sd->errors) {
1512       return 0;
1513    }
1514    if (bget_msg(sd) > 0) {
1515       Dmsg0(110, sd->msg);
1516       if (strcmp(sd->msg, resp) == 0) {
1517          return 1;
1518       }
1519    }
1520    if (job_canceled(jcr)) {
1521       return 0;                       /* if canceled avoid useless error messages */
1522    }
1523    if (is_bnet_error(sd)) {
1524       Jmsg2(jcr, M_FATAL, 0, _("Comm error with SD. bad response to %s. ERR=%s\n"),
1525          cmd, bnet_strerror(sd));
1526    } else {
1527       Jmsg3(jcr, M_FATAL, 0, _("Bad response to %s command. Wanted %s, got %s\n"),
1528          cmd, resp, sd->msg);
1529    }
1530    return 0;
1531 }
1532
1533 static int send_bootstrap_file(JCR *jcr)
1534 {
1535    FILE *bs;
1536    char buf[2000];
1537    BSOCK *sd = jcr->store_bsock;
1538    const char *bootstrap = "bootstrap\n";
1539    int stat = 0;
1540
1541    Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
1542    if (!jcr->RestoreBootstrap) {
1543       return 1;
1544    }
1545    bs = fopen(jcr->RestoreBootstrap, "r");
1546    if (!bs) {
1547       berrno be;
1548       Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"),
1549          jcr->RestoreBootstrap, be.strerror());
1550       set_jcr_job_status(jcr, JS_ErrorTerminated);
1551       goto bail_out;
1552    }
1553    sd->msglen = pm_strcpy(sd->msg, bootstrap);
1554    bnet_send(sd);
1555    while (fgets(buf, sizeof(buf), bs)) {
1556       sd->msglen = Mmsg(sd->msg, "%s", buf);
1557       bnet_send(sd);
1558    }
1559    bnet_sig(sd, BNET_EOD);
1560    fclose(bs);
1561    if (!response(jcr, sd, OKSDbootstrap, "Bootstrap")) {
1562       set_jcr_job_status(jcr, JS_ErrorTerminated);
1563       goto bail_out;
1564    }
1565    stat = 1;
1566
1567 bail_out:
1568    if (jcr->RestoreBootstrap) {
1569       unlink(jcr->RestoreBootstrap);
1570       free_pool_memory(jcr->RestoreBootstrap);
1571       jcr->RestoreBootstrap = NULL;
1572    }
1573
1574    return stat;
1575 }