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