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