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