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