]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/fd_cmds.c
cf29751ba17a5259c18f2d396649ca4454bdb62f
[bacula/bacula] / bacula / src / dird / fd_cmds.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- fd_cmds.c -- send commands to File daemon
31  *
32  *     Kern Sibbald, October MM
33  *
34  *    This routine is run as a separate thread.  There may be more
35  *    work to be done to make it totally reentrant!!!!
36  *
37  *  Utility functions for sending info to File Daemon.
38  *   These functions are used by both backup and verify.
39  *
40  *   Version $Id$
41  */
42
43 #include "bacula.h"
44 #include "dird.h"
45 #include "findlib/find.h"
46
47 const int dbglvl = 400;
48
49 /* Commands sent to File daemon */
50 static char filesetcmd[]  = "fileset%s\n"; /* set full fileset */
51 static char jobcmd[]      = "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n";
52 /* Note, mtime_only is not used here -- implemented as file option */
53 static char levelcmd[]    = "level = %s%s%s mtime_only=%d\n";
54 static char runscript[]   = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
55 static char runbeforenow[]= "RunBeforeNow\n";
56
57 /* Responses received from File daemon */
58 static char OKinc[]          = "2000 OK include\n";
59 static char OKjob[]          = "2000 OK Job";
60 static char OKlevel[]        = "2000 OK level\n";
61 static char OKRunScript[]    = "2000 OK RunScript\n";
62 static char OKRunBeforeNow[] = "2000 OK RunBeforeNow\n";
63
64 /* Forward referenced functions */
65 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd);
66
67 /* External functions */
68 extern DIRRES *director;
69 extern int FDConnectTimeout;
70
71 #define INC_LIST 0
72 #define EXC_LIST 1
73
74 /*
75  * Open connection with File daemon.
76  * Try connecting every retry_interval (default 10 sec), and
77  *   give up after max_retry_time (default 30 mins).
78  */
79
80 int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time,
81                            int verbose)
82 {
83    BSOCK   *fd = new_bsock();
84    char ed1[30];
85    utime_t heart_beat;
86
87    if (jcr->client->heartbeat_interval) {
88       heart_beat = jcr->client->heartbeat_interval;
89    } else {           
90       heart_beat = director->heartbeat_interval;
91    }
92
93    if (!jcr->file_bsock) {
94       char name[MAX_NAME_LENGTH + 100];
95       bstrncpy(name, _("Client: "), sizeof(name));
96       bstrncat(name, jcr->client->name(), sizeof(name));
97
98       fd->set_source_address(director->DIRsrc_addr);
99       if (!fd->connect(jcr,retry_interval,max_retry_time, heart_beat, name, jcr->client->address,
100            NULL, jcr->client->FDport, verbose)) {
101         fd->destroy();
102         fd = NULL;
103       }
104
105       if (fd == NULL) {
106          set_jcr_job_status(jcr, JS_ErrorTerminated);
107          return 0;
108       }
109       Dmsg0(10, "Opened connection with File daemon\n");
110    } else {
111       fd = jcr->file_bsock;           /* use existing connection */
112    }
113    fd->res = (RES *)jcr->client;      /* save resource in BSOCK */
114    jcr->file_bsock = fd;
115    set_jcr_job_status(jcr, JS_Running);
116
117    if (!authenticate_file_daemon(jcr)) {
118       set_jcr_job_status(jcr, JS_ErrorTerminated);
119       return 0;
120    }
121
122    /*
123     * Now send JobId and authorization key
124     */
125    fd->fsend(jobcmd, edit_int64(jcr->JobId, ed1), jcr->Job, jcr->VolSessionId,
126       jcr->VolSessionTime, jcr->sd_auth_key);
127    if (strcmp(jcr->sd_auth_key, "dummy") != 0) {
128       memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
129    }
130    Dmsg1(100, ">filed: %s", fd->msg);
131    if (bget_dirmsg(fd) > 0) {
132        Dmsg1(110, "<filed: %s", fd->msg);
133        if (strncmp(fd->msg, OKjob, strlen(OKjob)) != 0) {
134           Jmsg(jcr, M_FATAL, 0, _("File daemon \"%s\" rejected Job command: %s\n"),
135              jcr->client->hdr.name, fd->msg);
136           set_jcr_job_status(jcr, JS_ErrorTerminated);
137           return 0;
138        } else if (jcr->db) {
139           CLIENT_DBR cr;
140           memset(&cr, 0, sizeof(cr));
141           bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
142           cr.AutoPrune = jcr->client->AutoPrune;
143           cr.FileRetention = jcr->client->FileRetention;
144           cr.JobRetention = jcr->client->JobRetention;
145           bstrncpy(cr.Uname, fd->msg+strlen(OKjob)+1, sizeof(cr.Uname));
146           if (!db_update_client_record(jcr, jcr->db, &cr)) {
147              Jmsg(jcr, M_WARNING, 0, _("Error updating Client record. ERR=%s\n"),
148                 db_strerror(jcr->db));
149           }
150        }
151    } else {
152       Jmsg(jcr, M_FATAL, 0, _("FD gave bad response to JobId command: %s\n"),
153          bnet_strerror(fd));
154       set_jcr_job_status(jcr, JS_ErrorTerminated);
155       return 0;
156    }
157    return 1;
158 }
159
160 /*
161  * This subroutine edits the last job start time into a
162  *   "since=date/time" buffer that is returned in the
163  *   variable since.  This is used for display purposes in
164  *   the job report.  The time in jcr->stime is later
165  *   passed to tell the File daemon what to do.
166  */
167 void get_level_since_time(JCR *jcr, char *since, int since_len)
168 {
169    int JobLevel;
170    bool have_full;
171    bool do_full = false;
172    bool do_diff = false;
173    utime_t now;
174    utime_t last_full_time;
175    utime_t last_diff_time;
176
177    since[0] = 0;
178    /* If job cloned and a since time already given, use it */
179    if (jcr->cloned && jcr->stime && jcr->stime[0]) {
180       bstrncpy(since, _(", since="), since_len);
181       bstrncat(since, jcr->stime, since_len);
182       return;
183    }
184    /* Make sure stime buffer is allocated */
185    if (!jcr->stime) {
186       jcr->stime = get_pool_memory(PM_MESSAGE);
187    } 
188    jcr->stime[0] = 0;
189    /*
190     * Lookup the last FULL backup job to get the time/date for a
191     * differential or incremental save.
192     */
193    switch (jcr->get_JobLevel()) {
194    case L_DIFFERENTIAL:
195    case L_INCREMENTAL:
196       POOLMEM *stime = get_pool_memory(PM_MESSAGE);
197       /* Look up start time of last Full job */
198       now = (utime_t)time(NULL);
199       jcr->jr.JobId = 0;     /* flag to return since time */
200       have_full = db_find_job_start_time(jcr, jcr->db, &jcr->jr, &jcr->stime);
201       if (have_full) {
202          last_full_time = str_to_utime(jcr->stime);
203       }
204       /* Make sure the last diff is recent enough */
205       if (have_full && jcr->get_JobLevel() == L_INCREMENTAL && jcr->job->MaxDiffInterval > 0) {
206          /* Lookup last diff job */
207          if (db_find_last_job_start_time(jcr, jcr->db, &jcr->jr, &stime, L_DIFFERENTIAL)) {
208             last_diff_time = str_to_utime(stime);
209          } else {
210             /* No last differential, so use last full time */
211             last_diff_time = last_full_time;
212          }
213          do_diff = ((now - last_diff_time) >= jcr->job->MaxDiffInterval);
214       }
215       /* Note, do_full takes precedence over do_diff */
216       if (have_full && jcr->job->MaxFullInterval > 0) {
217          do_full = ((now - last_full_time) >= jcr->job->MaxFullInterval);
218       } else {
219          do_full = true;
220       }
221       free_pool_memory(stime);
222
223       if (do_full) {
224          /* No recent Full job found, so upgrade this one to Full */
225          Jmsg(jcr, M_INFO, 0, "%s", db_strerror(jcr->db));
226          Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full backup found in catalog. Doing FULL backup.\n"));
227          bsnprintf(since, since_len, _(" (upgraded from %s)"),
228             level_to_str(jcr->get_JobLevel()));
229          jcr->set_JobLevel(jcr->jr.JobLevel = L_FULL);
230        } else if (do_diff) {
231          /* No recent diff job found, so upgrade this one to Diff */
232          Jmsg(jcr, M_INFO, 0, _("No prior or suitable Differential backup found in catalog. Doing Differential backup.\n"));
233          bsnprintf(since, since_len, _(" (upgraded from %s)"),
234             level_to_str(jcr->get_JobLevel()));
235          jcr->set_JobLevel(jcr->jr.JobLevel = L_DIFFERENTIAL);
236       } else {
237          if (jcr->job->rerun_failed_levels) {
238             if (db_find_failed_job_since(jcr, jcr->db, &jcr->jr, jcr->stime, JobLevel)) {
239                Jmsg(jcr, M_INFO, 0, _("Prior failed job found in catalog. Upgrading to %s.\n"),
240                   level_to_str(JobLevel));
241                bsnprintf(since, since_len, _(" (upgraded from %s)"),
242                   level_to_str(jcr->get_JobLevel()));
243                jcr->set_JobLevel(jcr->jr.JobLevel = JobLevel);
244                jcr->jr.JobId = jcr->JobId;
245                break;
246             }
247          }
248          bstrncpy(since, _(", since="), since_len);
249          bstrncat(since, jcr->stime, since_len);
250       }
251       jcr->jr.JobId = jcr->JobId;
252       break;
253    }
254    Dmsg2(100, "Level=%c last start time=%s\n", jcr->get_JobLevel(), jcr->stime);
255 }
256
257 static void send_since_time(JCR *jcr)
258 {
259    BSOCK   *fd = jcr->file_bsock;
260    utime_t stime;
261    char ed1[50];
262
263    stime = str_to_utime(jcr->stime);
264    fd->fsend(levelcmd, "", NT_("since_utime "), edit_uint64(stime, ed1), 0);
265    while (bget_dirmsg(fd) >= 0) {  /* allow him to poll us to sync clocks */
266       Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
267    }
268 }
269
270 /*
271  * Send level command to FD.
272  * Used for backup jobs and estimate command.
273  */
274 bool send_level_command(JCR *jcr)
275 {
276    BSOCK   *fd = jcr->file_bsock;
277    const char *accurate = jcr->job->accurate?"accurate_":"";
278    const char *not_accurate = "";
279    /*
280     * Send Level command to File daemon
281     */
282    switch (jcr->get_JobLevel()) {
283    case L_BASE:
284       fd->fsend(levelcmd, not_accurate, "base", " ", 0);
285       break;
286    /* L_NONE is the console, sending something off to the FD */
287    case L_NONE:
288    case L_FULL:
289       fd->fsend(levelcmd, not_accurate, "full", " ", 0);
290       break;
291    case L_DIFFERENTIAL:
292       fd->fsend(levelcmd, accurate, "differential", " ", 0);
293       send_since_time(jcr);
294       break;
295    case L_INCREMENTAL:
296       fd->fsend(levelcmd, accurate, "incremental", " ", 0);
297       send_since_time(jcr);
298       break;
299    case L_SINCE:
300    default:
301       Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"),
302          jcr->get_JobLevel(), jcr->get_JobLevel());
303       return 0;
304    }
305    Dmsg1(120, ">filed: %s", fd->msg);
306    if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) {
307       return 0;
308    }
309    return 1;
310 }
311
312 /*
313  * Send either an Included or an Excluded list to FD
314  */
315 static bool send_fileset(JCR *jcr)
316 {
317    FILESET *fileset = jcr->fileset;
318    BSOCK   *fd = jcr->file_bsock;
319    int num;
320    bool include = true;
321
322    for ( ;; ) {
323       if (include) {
324          num = fileset->num_includes;
325       } else {
326          num = fileset->num_excludes;
327       }
328       for (int i=0; i<num; i++) {
329          char *item;
330          INCEXE *ie;
331          int j, k;
332
333          if (include) {
334             ie = fileset->include_items[i];
335             fd->fsend("I\n");
336          } else {
337             ie = fileset->exclude_items[i];
338             fd->fsend("E\n");
339          }
340          for (j=0; j<ie->num_opts; j++) {
341             FOPTS *fo = ie->opts_list[j];
342             fd->fsend("O %s\n", fo->opts);
343
344             bool enhanced_wild = false;
345             for (k=0; fo->opts[k]!='\0'; k++) {
346                if (fo->opts[k]=='W') {
347                   enhanced_wild = true;
348                   break;
349                }
350             }
351
352             for (k=0; k<fo->regex.size(); k++) {
353                fd->fsend("R %s\n", fo->regex.get(k));
354             }
355             for (k=0; k<fo->regexdir.size(); k++) {
356                fd->fsend("RD %s\n", fo->regexdir.get(k));
357             }
358             for (k=0; k<fo->regexfile.size(); k++) {
359                fd->fsend("RF %s\n", fo->regexfile.get(k));
360             }
361             for (k=0; k<fo->wild.size(); k++) {
362                fd->fsend("W %s\n", fo->wild.get(k));
363             }
364             for (k=0; k<fo->wilddir.size(); k++) {
365                fd->fsend("WD %s\n", fo->wilddir.get(k));
366             }
367             for (k=0; k<fo->wildfile.size(); k++) {
368                fd->fsend("WF %s\n", fo->wildfile.get(k));
369             }
370             for (k=0; k<fo->wildbase.size(); k++) {
371                fd->fsend("W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
372             }
373             for (k=0; k<fo->base.size(); k++) {
374                fd->fsend("B %s\n", fo->base.get(k));
375             }
376             for (k=0; k<fo->fstype.size(); k++) {
377                fd->fsend("X %s\n", fo->fstype.get(k));
378             }
379             for (k=0; k<fo->drivetype.size(); k++) {
380                fd->fsend("XD %s\n", fo->drivetype.get(k));
381             }
382             if (fo->plugin) {
383                fd->fsend("G %s\n", fo->plugin);
384             }
385             if (fo->ignoredir) {
386                bnet_fsend(fd, "Z %s\n", fo->ignoredir);
387             }
388             if (fo->reader) {
389                fd->fsend("D %s\n", fo->reader);
390             }
391             if (fo->writer) {
392                fd->fsend("T %s\n", fo->writer);
393             }
394             fd->fsend("N\n");
395          }
396
397          for (j=0; j<ie->name_list.size(); j++) {
398             item = (char *)ie->name_list.get(j);
399             if (!send_list_item(jcr, "F ", item, fd)) {
400                goto bail_out;
401             }
402          }
403          fd->fsend("N\n");
404          for (j=0; j<ie->plugin_list.size(); j++) {
405             item = (char *)ie->plugin_list.get(j);
406             if (!send_list_item(jcr, "P ", item, fd)) {
407                goto bail_out;
408             }
409          }
410          fd->fsend("N\n");
411       }
412       if (!include) {                 /* If we just did excludes */
413          break;                       /*   all done */
414       }
415       include = false;                /* Now do excludes */
416    }
417
418    fd->signal(BNET_EOD);              /* end of data */
419    if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) {
420       goto bail_out;
421    }
422    return true;
423
424 bail_out:
425    set_jcr_job_status(jcr, JS_ErrorTerminated);
426    return false;
427
428 }
429
430 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd)
431 {
432    BPIPE *bpipe;
433    FILE *ffd;
434    char buf[2000];
435    int optlen, stat;
436    char *p = item;
437
438    switch (*p) {
439    case '|':
440       p++;                      /* skip over the | */
441       fd->msg = edit_job_codes(jcr, fd->msg, p, "");
442       bpipe = open_bpipe(fd->msg, 0, "r");
443       if (!bpipe) {
444          berrno be;
445          Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
446             p, be.bstrerror());
447          return false;
448       }
449       bstrncpy(buf, code, sizeof(buf));
450       Dmsg1(500, "code=%s\n", buf);
451       optlen = strlen(buf);
452       while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) {
453          fd->msglen = Mmsg(fd->msg, "%s", buf);
454          Dmsg2(500, "Inc/exc len=%d: %s", fd->msglen, fd->msg);
455          if (!bnet_send(fd)) {
456             Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
457             return false;
458          }
459       }
460       if ((stat=close_bpipe(bpipe)) != 0) {
461          berrno be;
462          Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
463             p, be.bstrerror(stat));
464          return false;
465       }
466       break;
467    case '<':
468       p++;                      /* skip over < */
469       if ((ffd = fopen(p, "rb")) == NULL) {
470          berrno be;
471          Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"),
472             p, be.bstrerror());
473          return false;
474       }
475       bstrncpy(buf, code, sizeof(buf));
476       Dmsg1(500, "code=%s\n", buf);
477       optlen = strlen(buf);
478       while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) {
479          fd->msglen = Mmsg(fd->msg, "%s", buf);
480          if (!bnet_send(fd)) {
481             Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
482             return false;
483          }
484       }
485       fclose(ffd);
486       break;
487    case '\\':
488       p++;                      /* skip over \ */
489       /* Note, fall through wanted */
490    default:
491       pm_strcpy(fd->msg, code);
492       fd->msglen = pm_strcat(fd->msg, p);
493       Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
494       if (!fd->send()) {
495          Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
496          return false;
497       }
498       break;
499    }
500    return true;
501 }            
502
503
504 /*
505  * Send include list to File daemon
506  */
507 bool send_include_list(JCR *jcr)
508 {
509    BSOCK *fd = jcr->file_bsock;
510    if (jcr->fileset->new_include) {
511       fd->fsend(filesetcmd, jcr->fileset->enable_vss ? " vss=1" : "");
512       return send_fileset(jcr);
513    }
514    return true;
515 }
516
517
518 /*
519  * Send exclude list to File daemon
520  *   Under the new scheme, the Exclude list
521  *   is part of the FileSet sent with the
522  *   "include_list" above.
523  */
524 bool send_exclude_list(JCR *jcr)
525 {
526    return true;
527 }
528
529
530 /*
531  * Send bootstrap file if any to the socket given (FD or SD).
532  *  This is used for restore, verify VolumeToCatalog, and
533  *  for migration.
534  */
535 bool send_bootstrap_file(JCR *jcr, BSOCK *sock)
536 {
537    FILE *bs;
538    char buf[1000];
539    const char *bootstrap = "bootstrap\n";
540
541    Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
542    if (!jcr->RestoreBootstrap) {
543       return true;
544    }
545    bs = fopen(jcr->RestoreBootstrap, "rb");
546    if (!bs) {
547       berrno be;
548       Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"),
549          jcr->RestoreBootstrap, be.bstrerror());
550       set_jcr_job_status(jcr, JS_ErrorTerminated);
551       return false;
552    }
553    sock->fsend(bootstrap);
554    while (fgets(buf, sizeof(buf), bs)) {
555       sock->fsend("%s", buf);
556    }
557    sock->signal(BNET_EOD);
558    fclose(bs);
559    if (jcr->unlink_bsr) {
560       unlink(jcr->RestoreBootstrap);
561       jcr->unlink_bsr = false;
562    }                         
563    return true;
564 }
565
566 /* TODO: drop this with runscript.old_proto in bacula 1.42 */
567 static char runbefore[]   = "RunBeforeJob %s\n";
568 static char runafter[]    = "RunAfterJob %s\n";
569 static char OKRunBefore[] = "2000 OK RunBefore\n";
570 static char OKRunAfter[]  = "2000 OK RunAfter\n";
571
572 int send_runscript_with_old_proto(JCR *jcr, int when, POOLMEM *msg)
573 {
574    int ret;
575    Dmsg1(120, "bdird: sending old runcommand to fd '%s'\n",msg);
576    if (when & SCRIPT_Before) {
577       bnet_fsend(jcr->file_bsock, runbefore, msg);
578       ret = response(jcr, jcr->file_bsock, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR);
579    } else {
580       bnet_fsend(jcr->file_bsock, runafter, msg);
581       ret = response(jcr, jcr->file_bsock, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR);
582    }
583    return ret;
584 } /* END OF TODO */
585
586 /*
587  * Send RunScripts to File daemon
588  * 1) We send all runscript to FD, they can be executed Before, After, or twice
589  * 2) Then, we send a "RunBeforeNow" command to the FD to tell him to do the
590  *    first run_script() call. (ie ClientRunBeforeJob)
591  */
592 int send_runscripts_commands(JCR *jcr)
593 {
594    POOLMEM *msg = get_pool_memory(PM_FNAME);
595    BSOCK *fd = jcr->file_bsock;
596    RUNSCRIPT *cmd;
597    bool launch_before_cmd = false;
598    POOLMEM *ehost = get_pool_memory(PM_FNAME);
599    int result;
600
601    Dmsg0(120, "bdird: sending runscripts to fd\n");
602    
603    foreach_alist(cmd, jcr->job->RunScripts) {
604       if (cmd->can_run_at_level(jcr->get_JobLevel()) && cmd->target) {
605          ehost = edit_job_codes(jcr, ehost, cmd->target, "");
606          Dmsg2(200, "bdird: runscript %s -> %s\n", cmd->target, ehost);
607
608          if (strcmp(ehost, jcr->client->name()) == 0) {
609             pm_strcpy(msg, cmd->command);
610             bash_spaces(msg);
611
612             Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", cmd->command);
613             
614             /* TODO: remove this with bacula 1.42 */
615             if (cmd->old_proto) {
616                result = send_runscript_with_old_proto(jcr, cmd->when, msg);
617
618             } else {
619                fd->fsend(runscript, cmd->on_success, 
620                                     cmd->on_failure,
621                                     cmd->fail_on_error,
622                                     cmd->when,
623                                     msg);
624
625                result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
626                launch_before_cmd = true;
627             }
628             
629             if (!result) {
630                goto bail_out;
631             }
632          }
633          /* TODO : we have to play with other client */
634          /*
635            else {
636            send command to an other client
637            }
638          */
639       }        
640    } 
641
642    /* Tell the FD to execute the ClientRunBeforeJob */
643    if (launch_before_cmd) {
644       fd->fsend(runbeforenow);
645       if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) {
646         goto bail_out;
647       }
648    }
649    free_pool_memory(msg);
650    free_pool_memory(ehost);
651    return 1;
652
653 bail_out:
654    Jmsg(jcr, M_FATAL, 0, _("Client \"%s\" RunScript failed.\n"), ehost);
655    free_pool_memory(msg);
656    free_pool_memory(ehost);
657    return 0;
658 }
659
660
661
662 /*
663  * Read the attributes from the File daemon for
664  * a Verify job and store them in the catalog.
665  */
666 int get_attributes_and_put_in_catalog(JCR *jcr)
667 {
668    BSOCK   *fd;
669    int n = 0;
670    ATTR_DBR *ar = NULL;
671    char digest[MAXSTRING];
672
673    fd = jcr->file_bsock;
674    jcr->jr.FirstIndex = 1;
675    jcr->FileIndex = 0;
676    /* Start transaction allocates jcr->attr and jcr->ar if needed */
677    db_start_transaction(jcr, jcr->db);     /* start transaction if not already open */
678    ar = jcr->ar;
679
680    Dmsg0(120, "bdird: waiting to receive file attributes\n");
681    /* Pickup file attributes and digest */
682    while (!fd->errors && (n = bget_dirmsg(fd)) > 0) {
683       uint32_t file_index;
684       int stream, len;
685       char *p, *fn;
686       char Digest[MAXSTRING];      /* either Verify opts or MD5/SHA1 digest */
687
688       if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Digest)) != 3) {
689          Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
690 "msglen=%d msg=%s\n"), len, fd->msglen, fd->msg);
691          set_jcr_job_status(jcr, JS_ErrorTerminated);
692          return 0;
693       }
694       p = fd->msg;
695       /* The following three fields were sscanf'ed above so skip them */
696       skip_nonspaces(&p);             /* skip FileIndex */
697       skip_spaces(&p);
698       skip_nonspaces(&p);             /* skip Stream */
699       skip_spaces(&p);
700       skip_nonspaces(&p);             /* skip Opts_Digest */
701       p++;                            /* skip space */
702       Dmsg1(dbglvl, "Stream=%d\n", stream);
703       if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
704          if (jcr->cached_attribute) {
705             Dmsg3(dbglvl, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname,
706                ar->attr);
707             if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
708                Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
709             }
710          }
711          /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
712          fn = jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
713          while (*p != 0) {
714             *fn++ = *p++;                /* copy filename */
715          }
716          *fn = *p++;                     /* term filename and point p to attribs */
717          pm_strcpy(jcr->attr, p);        /* save attributes */
718          jcr->JobFiles++;
719          jcr->FileIndex = file_index;
720          ar->attr = jcr->attr;
721          ar->fname = jcr->fname;
722          ar->FileIndex = file_index;
723          ar->Stream = stream;
724          ar->link = NULL;
725          ar->JobId = jcr->JobId;
726          ar->ClientId = jcr->ClientId;
727          ar->PathId = 0;
728          ar->FilenameId = 0;
729          ar->Digest = NULL;
730          ar->DigestType = CRYPTO_DIGEST_NONE;
731          jcr->cached_attribute = true;
732
733          Dmsg2(dbglvl, "dird<filed: stream=%d %s\n", stream, jcr->fname);
734          Dmsg1(dbglvl, "dird<filed: attr=%s\n", ar->attr);
735          jcr->FileId = ar->FileId;
736       /*
737        * First, get STREAM_UNIX_ATTRIBUTES and fill ATTR_DBR structure
738        * Next, we CAN have a CRYPTO_DIGEST, so we fill ATTR_DBR with it (or not)
739        * When we get a new STREAM_UNIX_ATTRIBUTES, we known that we can add file to the catalog
740        * At the end, we have to add the last file
741        */
742       } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
743          if (jcr->FileIndex != (uint32_t)file_index) {
744             Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"),
745                stream_to_ascii(stream), file_index, jcr->FileIndex);
746             continue;
747          }
748          ar->Digest = digest;
749          ar->DigestType = crypto_digest_stream_type(stream);
750          db_escape_string(jcr, jcr->db, digest, Digest, strlen(Digest));
751          Dmsg4(dbglvl, "stream=%d DigestLen=%d Digest=%s type=%d\n", stream,
752                strlen(digest), digest, ar->DigestType);
753       }
754       jcr->jr.JobFiles = jcr->JobFiles = file_index;
755       jcr->jr.LastIndex = file_index;
756    }
757    if (is_bnet_error(fd)) {
758       Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
759             fd->bstrerror());
760       return 0;
761    }
762    if (jcr->cached_attribute) {
763       Dmsg3(dbglvl, "Cached attr with digest. Stream=%d fname=%s attr=%s\n", ar->Stream,            
764          ar->fname, ar->attr);
765       if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
766          Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
767       }
768       jcr->cached_attribute = false; 
769    }
770    set_jcr_job_status(jcr, JS_Terminated);
771    return 1;
772 }