]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/fd_cmds.c
Another fix for bug #1311 to get the correct last_full_time
[bacula/bacula] / bacula / src / dird / fd_cmds.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 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 = 0;
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       /*
201        * This is probably redundant, but some of the code below
202        * uses jcr->stime, so don't remove unless you are sure.
203        */
204       if (!db_find_job_start_time(jcr, jcr->db, &jcr->jr, &jcr->stime)) {
205          do_full = true;
206       }
207       have_full = db_find_last_job_start_time(jcr, jcr->db, &jcr->jr, &stime, L_FULL);
208       if (have_full) {
209          last_full_time = str_to_utime(stime);
210       } else {
211          do_full = true;               /* No full, upgrade to one */
212       }
213       Dmsg4(50, "have_full=%d do_full=%d now=%lld full_time=%lld\n", have_full, 
214             do_full, now, last_full_time);
215       /* Make sure the last diff is recent enough */
216       if (have_full && jcr->get_JobLevel() == L_INCREMENTAL && jcr->job->MaxDiffInterval > 0) {
217          /* Lookup last diff job */
218          if (db_find_last_job_start_time(jcr, jcr->db, &jcr->jr, &stime, L_DIFFERENTIAL)) {
219             last_diff_time = str_to_utime(stime);
220             /* If no Diff since Full, use Full time */
221             if (last_diff_time < last_full_time) {
222                last_diff_time = last_full_time;
223             }
224             Dmsg2(50, "last_diff_time=%lld last_full_time=%lld\n", last_diff_time,
225                   last_full_time);
226          } else {
227             /* No last differential, so use last full time */
228             last_diff_time = last_full_time;
229             Dmsg1(50, "No last_diff_time setting to full_time=%lld\n", last_full_time);
230          }
231          do_diff = ((now - last_diff_time) >= jcr->job->MaxDiffInterval);
232          Dmsg2(50, "do_diff=%d diffInter=%lld\n", do_diff, jcr->job->MaxDiffInterval);
233       }
234       /* Note, do_full takes precedence over do_diff */
235       if (have_full && jcr->job->MaxFullInterval > 0) {
236          do_full = ((now - last_full_time) >= jcr->job->MaxFullInterval);
237       }
238       free_pool_memory(stime);
239
240       if (do_full) {
241          /* No recent Full job found, so upgrade this one to Full */
242          Jmsg(jcr, M_INFO, 0, "%s", db_strerror(jcr->db));
243          Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full backup found in catalog. Doing FULL backup.\n"));
244          bsnprintf(since, since_len, _(" (upgraded from %s)"),
245             level_to_str(jcr->get_JobLevel()));
246          jcr->set_JobLevel(jcr->jr.JobLevel = L_FULL);
247        } else if (do_diff) {
248          /* No recent diff job found, so upgrade this one to Diff */
249          Jmsg(jcr, M_INFO, 0, _("No prior or suitable Differential backup found in catalog. Doing Differential backup.\n"));
250          bsnprintf(since, since_len, _(" (upgraded from %s)"),
251             level_to_str(jcr->get_JobLevel()));
252          jcr->set_JobLevel(jcr->jr.JobLevel = L_DIFFERENTIAL);
253       } else {
254          if (jcr->job->rerun_failed_levels) {
255             if (db_find_failed_job_since(jcr, jcr->db, &jcr->jr, jcr->stime, JobLevel)) {
256                Jmsg(jcr, M_INFO, 0, _("Prior failed job found in catalog. Upgrading to %s.\n"),
257                   level_to_str(JobLevel));
258                bsnprintf(since, since_len, _(" (upgraded from %s)"),
259                   level_to_str(jcr->get_JobLevel()));
260                jcr->set_JobLevel(jcr->jr.JobLevel = JobLevel);
261                jcr->jr.JobId = jcr->JobId;
262                break;
263             }
264          }
265          bstrncpy(since, _(", since="), since_len);
266          bstrncat(since, jcr->stime, since_len);
267       }
268       jcr->jr.JobId = jcr->JobId;
269       break;
270    }
271    Dmsg2(100, "Level=%c last start time=%s\n", jcr->get_JobLevel(), jcr->stime);
272 }
273
274 static void send_since_time(JCR *jcr)
275 {
276    BSOCK   *fd = jcr->file_bsock;
277    utime_t stime;
278    char ed1[50];
279
280    stime = str_to_utime(jcr->stime);
281    fd->fsend(levelcmd, "", NT_("since_utime "), edit_uint64(stime, ed1), 0);
282    while (bget_dirmsg(fd) >= 0) {  /* allow him to poll us to sync clocks */
283       Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
284    }
285 }
286
287 /*
288  * Send level command to FD.
289  * Used for backup jobs and estimate command.
290  */
291 bool send_level_command(JCR *jcr)
292 {
293    BSOCK   *fd = jcr->file_bsock;
294    const char *accurate = jcr->job->accurate?"accurate_":"";
295    const char *not_accurate = "";
296    /*
297     * Send Level command to File daemon
298     */
299    switch (jcr->get_JobLevel()) {
300    case L_BASE:
301       fd->fsend(levelcmd, not_accurate, "base", " ", 0);
302       break;
303    /* L_NONE is the console, sending something off to the FD */
304    case L_NONE:
305    case L_FULL:
306       fd->fsend(levelcmd, not_accurate, "full", " ", 0);
307       break;
308    case L_DIFFERENTIAL:
309       fd->fsend(levelcmd, accurate, "differential", " ", 0);
310       send_since_time(jcr);
311       break;
312    case L_INCREMENTAL:
313       fd->fsend(levelcmd, accurate, "incremental", " ", 0);
314       send_since_time(jcr);
315       break;
316    case L_SINCE:
317    default:
318       Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"),
319          jcr->get_JobLevel(), jcr->get_JobLevel());
320       return 0;
321    }
322    Dmsg1(120, ">filed: %s", fd->msg);
323    if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) {
324       return 0;
325    }
326    return 1;
327 }
328
329 /*
330  * Send either an Included or an Excluded list to FD
331  */
332 static bool send_fileset(JCR *jcr)
333 {
334    FILESET *fileset = jcr->fileset;
335    BSOCK   *fd = jcr->file_bsock;
336    int num;
337    bool include = true;
338
339    for ( ;; ) {
340       if (include) {
341          num = fileset->num_includes;
342       } else {
343          num = fileset->num_excludes;
344       }
345       for (int i=0; i<num; i++) {
346          char *item;
347          INCEXE *ie;
348          int j, k;
349
350          if (include) {
351             ie = fileset->include_items[i];
352             fd->fsend("I\n");
353          } else {
354             ie = fileset->exclude_items[i];
355             fd->fsend("E\n");
356          }
357          for (j=0; j<ie->num_opts; j++) {
358             FOPTS *fo = ie->opts_list[j];
359             fd->fsend("O %s\n", fo->opts);
360
361             bool enhanced_wild = false;
362             for (k=0; fo->opts[k]!='\0'; k++) {
363                if (fo->opts[k]=='W') {
364                   enhanced_wild = true;
365                   break;
366                }
367             }
368
369             for (k=0; k<fo->regex.size(); k++) {
370                fd->fsend("R %s\n", fo->regex.get(k));
371             }
372             for (k=0; k<fo->regexdir.size(); k++) {
373                fd->fsend("RD %s\n", fo->regexdir.get(k));
374             }
375             for (k=0; k<fo->regexfile.size(); k++) {
376                fd->fsend("RF %s\n", fo->regexfile.get(k));
377             }
378             for (k=0; k<fo->wild.size(); k++) {
379                fd->fsend("W %s\n", fo->wild.get(k));
380             }
381             for (k=0; k<fo->wilddir.size(); k++) {
382                fd->fsend("WD %s\n", fo->wilddir.get(k));
383             }
384             for (k=0; k<fo->wildfile.size(); k++) {
385                fd->fsend("WF %s\n", fo->wildfile.get(k));
386             }
387             for (k=0; k<fo->wildbase.size(); k++) {
388                fd->fsend("W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
389             }
390             for (k=0; k<fo->base.size(); k++) {
391                fd->fsend("B %s\n", fo->base.get(k));
392             }
393             for (k=0; k<fo->fstype.size(); k++) {
394                fd->fsend("X %s\n", fo->fstype.get(k));
395             }
396             for (k=0; k<fo->drivetype.size(); k++) {
397                fd->fsend("XD %s\n", fo->drivetype.get(k));
398             }
399             if (fo->plugin) {
400                fd->fsend("G %s\n", fo->plugin);
401             }
402             if (fo->ignoredir) {
403                bnet_fsend(fd, "Z %s\n", fo->ignoredir);
404             }
405             if (fo->reader) {
406                fd->fsend("D %s\n", fo->reader);
407             }
408             if (fo->writer) {
409                fd->fsend("T %s\n", fo->writer);
410             }
411             fd->fsend("N\n");
412          }
413
414          for (j=0; j<ie->name_list.size(); j++) {
415             item = (char *)ie->name_list.get(j);
416             if (!send_list_item(jcr, "F ", item, fd)) {
417                goto bail_out;
418             }
419          }
420          fd->fsend("N\n");
421          for (j=0; j<ie->plugin_list.size(); j++) {
422             item = (char *)ie->plugin_list.get(j);
423             if (!send_list_item(jcr, "P ", item, fd)) {
424                goto bail_out;
425             }
426          }
427          fd->fsend("N\n");
428       }
429       if (!include) {                 /* If we just did excludes */
430          break;                       /*   all done */
431       }
432       include = false;                /* Now do excludes */
433    }
434
435    fd->signal(BNET_EOD);              /* end of data */
436    if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) {
437       goto bail_out;
438    }
439    return true;
440
441 bail_out:
442    set_jcr_job_status(jcr, JS_ErrorTerminated);
443    return false;
444
445 }
446
447 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd)
448 {
449    BPIPE *bpipe;
450    FILE *ffd;
451    char buf[2000];
452    int optlen, stat;
453    char *p = item;
454
455    switch (*p) {
456    case '|':
457       p++;                      /* skip over the | */
458       fd->msg = edit_job_codes(jcr, fd->msg, p, "");
459       bpipe = open_bpipe(fd->msg, 0, "r");
460       if (!bpipe) {
461          berrno be;
462          Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
463             p, be.bstrerror());
464          return false;
465       }
466       bstrncpy(buf, code, sizeof(buf));
467       Dmsg1(500, "code=%s\n", buf);
468       optlen = strlen(buf);
469       while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) {
470          fd->msglen = Mmsg(fd->msg, "%s", buf);
471          Dmsg2(500, "Inc/exc len=%d: %s", fd->msglen, fd->msg);
472          if (!bnet_send(fd)) {
473             Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
474             return false;
475          }
476       }
477       if ((stat=close_bpipe(bpipe)) != 0) {
478          berrno be;
479          Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
480             p, be.bstrerror(stat));
481          return false;
482       }
483       break;
484    case '<':
485       p++;                      /* skip over < */
486       if ((ffd = fopen(p, "rb")) == NULL) {
487          berrno be;
488          Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"),
489             p, be.bstrerror());
490          return false;
491       }
492       bstrncpy(buf, code, sizeof(buf));
493       Dmsg1(500, "code=%s\n", buf);
494       optlen = strlen(buf);
495       while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) {
496          fd->msglen = Mmsg(fd->msg, "%s", buf);
497          if (!bnet_send(fd)) {
498             Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
499             return false;
500          }
501       }
502       fclose(ffd);
503       break;
504    case '\\':
505       p++;                      /* skip over \ */
506       /* Note, fall through wanted */
507    default:
508       pm_strcpy(fd->msg, code);
509       fd->msglen = pm_strcat(fd->msg, p);
510       Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
511       if (!fd->send()) {
512          Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
513          return false;
514       }
515       break;
516    }
517    return true;
518 }            
519
520
521 /*
522  * Send include list to File daemon
523  */
524 bool send_include_list(JCR *jcr)
525 {
526    BSOCK *fd = jcr->file_bsock;
527    if (jcr->fileset->new_include) {
528       fd->fsend(filesetcmd, jcr->fileset->enable_vss ? " vss=1" : "");
529       return send_fileset(jcr);
530    }
531    return true;
532 }
533
534
535 /*
536  * Send exclude list to File daemon
537  *   Under the new scheme, the Exclude list
538  *   is part of the FileSet sent with the
539  *   "include_list" above.
540  */
541 bool send_exclude_list(JCR *jcr)
542 {
543    return true;
544 }
545
546
547 /*
548  * Send bootstrap file if any to the socket given (FD or SD).
549  *  This is used for restore, verify VolumeToCatalog, and
550  *  for migration.
551  */
552 bool send_bootstrap_file(JCR *jcr, BSOCK *sock)
553 {
554    FILE *bs;
555    char buf[1000];
556    const char *bootstrap = "bootstrap\n";
557
558    Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
559    if (!jcr->RestoreBootstrap) {
560       return true;
561    }
562    bs = fopen(jcr->RestoreBootstrap, "rb");
563    if (!bs) {
564       berrno be;
565       Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"),
566          jcr->RestoreBootstrap, be.bstrerror());
567       set_jcr_job_status(jcr, JS_ErrorTerminated);
568       return false;
569    }
570    sock->fsend(bootstrap);
571    while (fgets(buf, sizeof(buf), bs)) {
572       sock->fsend("%s", buf);
573    }
574    sock->signal(BNET_EOD);
575    fclose(bs);
576    if (jcr->unlink_bsr) {
577       unlink(jcr->RestoreBootstrap);
578       jcr->unlink_bsr = false;
579    }                         
580    return true;
581 }
582
583 /* TODO: drop this with runscript.old_proto in bacula 1.42 */
584 static char runbefore[]   = "RunBeforeJob %s\n";
585 static char runafter[]    = "RunAfterJob %s\n";
586 static char OKRunBefore[] = "2000 OK RunBefore\n";
587 static char OKRunAfter[]  = "2000 OK RunAfter\n";
588
589 int send_runscript_with_old_proto(JCR *jcr, int when, POOLMEM *msg)
590 {
591    int ret;
592    Dmsg1(120, "bdird: sending old runcommand to fd '%s'\n",msg);
593    if (when & SCRIPT_Before) {
594       bnet_fsend(jcr->file_bsock, runbefore, msg);
595       ret = response(jcr, jcr->file_bsock, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR);
596    } else {
597       bnet_fsend(jcr->file_bsock, runafter, msg);
598       ret = response(jcr, jcr->file_bsock, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR);
599    }
600    return ret;
601 } /* END OF TODO */
602
603 /*
604  * Send RunScripts to File daemon
605  * 1) We send all runscript to FD, they can be executed Before, After, or twice
606  * 2) Then, we send a "RunBeforeNow" command to the FD to tell him to do the
607  *    first run_script() call. (ie ClientRunBeforeJob)
608  */
609 int send_runscripts_commands(JCR *jcr)
610 {
611    POOLMEM *msg = get_pool_memory(PM_FNAME);
612    BSOCK *fd = jcr->file_bsock;
613    RUNSCRIPT *cmd;
614    bool launch_before_cmd = false;
615    POOLMEM *ehost = get_pool_memory(PM_FNAME);
616    int result;
617
618    Dmsg0(120, "bdird: sending runscripts to fd\n");
619    
620    foreach_alist(cmd, jcr->job->RunScripts) {
621       if (cmd->can_run_at_level(jcr->get_JobLevel()) && cmd->target) {
622          ehost = edit_job_codes(jcr, ehost, cmd->target, "");
623          Dmsg2(200, "bdird: runscript %s -> %s\n", cmd->target, ehost);
624
625          if (strcmp(ehost, jcr->client->name()) == 0) {
626             pm_strcpy(msg, cmd->command);
627             bash_spaces(msg);
628
629             Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", cmd->command);
630             
631             /* TODO: remove this with bacula 1.42 */
632             if (cmd->old_proto) {
633                result = send_runscript_with_old_proto(jcr, cmd->when, msg);
634
635             } else {
636                fd->fsend(runscript, cmd->on_success, 
637                                     cmd->on_failure,
638                                     cmd->fail_on_error,
639                                     cmd->when,
640                                     msg);
641
642                result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
643                launch_before_cmd = true;
644             }
645             
646             if (!result) {
647                goto bail_out;
648             }
649          }
650          /* TODO : we have to play with other client */
651          /*
652            else {
653            send command to an other client
654            }
655          */
656       }        
657    } 
658
659    /* Tell the FD to execute the ClientRunBeforeJob */
660    if (launch_before_cmd) {
661       fd->fsend(runbeforenow);
662       if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) {
663         goto bail_out;
664       }
665    }
666    free_pool_memory(msg);
667    free_pool_memory(ehost);
668    return 1;
669
670 bail_out:
671    Jmsg(jcr, M_FATAL, 0, _("Client \"%s\" RunScript failed.\n"), ehost);
672    free_pool_memory(msg);
673    free_pool_memory(ehost);
674    return 0;
675 }
676
677
678
679 /*
680  * Read the attributes from the File daemon for
681  * a Verify job and store them in the catalog.
682  */
683 int get_attributes_and_put_in_catalog(JCR *jcr)
684 {
685    BSOCK   *fd;
686    int n = 0;
687    ATTR_DBR *ar = NULL;
688    char digest[MAXSTRING];
689
690    fd = jcr->file_bsock;
691    jcr->jr.FirstIndex = 1;
692    jcr->FileIndex = 0;
693    /* Start transaction allocates jcr->attr and jcr->ar if needed */
694    db_start_transaction(jcr, jcr->db);     /* start transaction if not already open */
695    ar = jcr->ar;
696
697    Dmsg0(120, "bdird: waiting to receive file attributes\n");
698    /* Pickup file attributes and digest */
699    while (!fd->errors && (n = bget_dirmsg(fd)) > 0) {
700       uint32_t file_index;
701       int stream, len;
702       char *p, *fn;
703       char Digest[MAXSTRING];      /* either Verify opts or MD5/SHA1 digest */
704
705       if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Digest)) != 3) {
706          Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
707 "msglen=%d msg=%s\n"), len, fd->msglen, fd->msg);
708          set_jcr_job_status(jcr, JS_ErrorTerminated);
709          return 0;
710       }
711       p = fd->msg;
712       /* The following three fields were sscanf'ed above so skip them */
713       skip_nonspaces(&p);             /* skip FileIndex */
714       skip_spaces(&p);
715       skip_nonspaces(&p);             /* skip Stream */
716       skip_spaces(&p);
717       skip_nonspaces(&p);             /* skip Opts_Digest */
718       p++;                            /* skip space */
719       Dmsg1(dbglvl, "Stream=%d\n", stream);
720       if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
721          if (jcr->cached_attribute) {
722             Dmsg3(dbglvl, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname,
723                ar->attr);
724             if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
725                Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
726             }
727          }
728          /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
729          fn = jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
730          while (*p != 0) {
731             *fn++ = *p++;                /* copy filename */
732          }
733          *fn = *p++;                     /* term filename and point p to attribs */
734          pm_strcpy(jcr->attr, p);        /* save attributes */
735          jcr->JobFiles++;
736          jcr->FileIndex = file_index;
737          ar->attr = jcr->attr;
738          ar->fname = jcr->fname;
739          ar->FileIndex = file_index;
740          ar->Stream = stream;
741          ar->link = NULL;
742          ar->JobId = jcr->JobId;
743          ar->ClientId = jcr->ClientId;
744          ar->PathId = 0;
745          ar->FilenameId = 0;
746          ar->Digest = NULL;
747          ar->DigestType = CRYPTO_DIGEST_NONE;
748          jcr->cached_attribute = true;
749
750          Dmsg2(dbglvl, "dird<filed: stream=%d %s\n", stream, jcr->fname);
751          Dmsg1(dbglvl, "dird<filed: attr=%s\n", ar->attr);
752          jcr->FileId = ar->FileId;
753       /*
754        * First, get STREAM_UNIX_ATTRIBUTES and fill ATTR_DBR structure
755        * Next, we CAN have a CRYPTO_DIGEST, so we fill ATTR_DBR with it (or not)
756        * When we get a new STREAM_UNIX_ATTRIBUTES, we known that we can add file to the catalog
757        * At the end, we have to add the last file
758        */
759       } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
760          if (jcr->FileIndex != (uint32_t)file_index) {
761             Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"),
762                stream_to_ascii(stream), file_index, jcr->FileIndex);
763             continue;
764          }
765          ar->Digest = digest;
766          ar->DigestType = crypto_digest_stream_type(stream);
767          db_escape_string(jcr, jcr->db, digest, Digest, strlen(Digest));
768          Dmsg4(dbglvl, "stream=%d DigestLen=%d Digest=%s type=%d\n", stream,
769                strlen(digest), digest, ar->DigestType);
770       }
771       jcr->jr.JobFiles = jcr->JobFiles = file_index;
772       jcr->jr.LastIndex = file_index;
773    }
774    if (is_bnet_error(fd)) {
775       Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
776             fd->bstrerror());
777       return 0;
778    }
779    if (jcr->cached_attribute) {
780       Dmsg3(dbglvl, "Cached attr with digest. Stream=%d fname=%s attr=%s\n", ar->Stream,            
781          ar->fname, ar->attr);
782       if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
783          Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
784       }
785       jcr->cached_attribute = false; 
786    }
787    set_jcr_job_status(jcr, JS_Terminated);
788    return 1;
789 }