]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/fd_cmds.c
Fix SQL warning message about concurrency pointed out by Graham
[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                bool done=false;         /* print warning only if compression enabled in FS */ 
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                     done=true;
383                     k++;                /* skip option and level */
384                  } else {
385                     newopts[j] = fo->opts[k];
386                     j++;
387                  }
388                }
389                newopts[j] = '\0';
390
391                if (done) {
392                   Jmsg(jcr, M_INFO, 0,
393                       _("FD compression disabled for this Job because AllowCompress=No in Storage resource.\n") );
394                }
395                /* Send the new trimmed option set without overwriting fo->opts */
396                fd->fsend("O %s\n", newopts);
397             } else {
398                /* Send the original options */
399                fd->fsend("O %s\n", fo->opts);
400             }
401
402             for (k=0; k<fo->regex.size(); k++) {
403                fd->fsend("R %s\n", fo->regex.get(k));
404             }
405             for (k=0; k<fo->regexdir.size(); k++) {
406                fd->fsend("RD %s\n", fo->regexdir.get(k));
407             }
408             for (k=0; k<fo->regexfile.size(); k++) {
409                fd->fsend("RF %s\n", fo->regexfile.get(k));
410             }
411             for (k=0; k<fo->wild.size(); k++) {
412                fd->fsend("W %s\n", fo->wild.get(k));
413             }
414             for (k=0; k<fo->wilddir.size(); k++) {
415                fd->fsend("WD %s\n", fo->wilddir.get(k));
416             }
417             for (k=0; k<fo->wildfile.size(); k++) {
418                fd->fsend("WF %s\n", fo->wildfile.get(k));
419             }
420             for (k=0; k<fo->wildbase.size(); k++) {
421                fd->fsend("W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
422             }
423             for (k=0; k<fo->base.size(); k++) {
424                fd->fsend("B %s\n", fo->base.get(k));
425             }
426             for (k=0; k<fo->fstype.size(); k++) {
427                fd->fsend("X %s\n", fo->fstype.get(k));
428             }
429             for (k=0; k<fo->drivetype.size(); k++) {
430                fd->fsend("XD %s\n", fo->drivetype.get(k));
431             }
432             if (fo->plugin) {
433                fd->fsend("G %s\n", fo->plugin);
434             }
435             if (fo->reader) {
436                fd->fsend("D %s\n", fo->reader);
437             }
438             if (fo->writer) {
439                fd->fsend("T %s\n", fo->writer);
440             }
441             fd->fsend("N\n");
442          }
443
444          for (j=0; j<ie->name_list.size(); j++) {
445             item = (char *)ie->name_list.get(j);
446             if (!send_list_item(jcr, "F ", item, fd)) {
447                goto bail_out;
448             }
449          }
450          fd->fsend("N\n");
451          for (j=0; j<ie->plugin_list.size(); j++) {
452             item = (char *)ie->plugin_list.get(j);
453             if (!send_list_item(jcr, "P ", item, fd)) {
454                goto bail_out;
455             }
456          }
457          fd->fsend("N\n");
458       }
459       if (!include) {                 /* If we just did excludes */
460          break;                       /*   all done */
461       }
462       include = false;                /* Now do excludes */
463    }
464
465    fd->signal(BNET_EOD);              /* end of data */
466    if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) {
467       goto bail_out;
468    }
469    return true;
470
471 bail_out:
472    set_jcr_job_status(jcr, JS_ErrorTerminated);
473    return false;
474
475 }
476
477 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd)
478 {
479    BPIPE *bpipe;
480    FILE *ffd;
481    char buf[2000];
482    int optlen, stat;
483    char *p = item;
484
485    switch (*p) {
486    case '|':
487       p++;                      /* skip over the | */
488       fd->msg = edit_job_codes(jcr, fd->msg, p, "");
489       bpipe = open_bpipe(fd->msg, 0, "r");
490       if (!bpipe) {
491          berrno be;
492          Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
493             p, be.bstrerror());
494          return false;
495       }
496       bstrncpy(buf, code, sizeof(buf));
497       Dmsg1(500, "code=%s\n", buf);
498       optlen = strlen(buf);
499       while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) {
500          fd->msglen = Mmsg(fd->msg, "%s", buf);
501          Dmsg2(500, "Inc/exc len=%d: %s", fd->msglen, fd->msg);
502          if (!bnet_send(fd)) {
503             Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
504             return false;
505          }
506       }
507       if ((stat=close_bpipe(bpipe)) != 0) {
508          berrno be;
509          Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
510             p, be.bstrerror(stat));
511          return false;
512       }
513       break;
514    case '<':
515       p++;                      /* skip over < */
516       if ((ffd = fopen(p, "rb")) == NULL) {
517          berrno be;
518          Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"),
519             p, be.bstrerror());
520          return false;
521       }
522       bstrncpy(buf, code, sizeof(buf));
523       Dmsg1(500, "code=%s\n", buf);
524       optlen = strlen(buf);
525       while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) {
526          fd->msglen = Mmsg(fd->msg, "%s", buf);
527          if (!bnet_send(fd)) {
528             Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
529             return false;
530          }
531       }
532       fclose(ffd);
533       break;
534    case '\\':
535       p++;                      /* skip over \ */
536       /* Note, fall through wanted */
537    default:
538       pm_strcpy(fd->msg, code);
539       fd->msglen = pm_strcat(fd->msg, p);
540       Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
541       if (!fd->send()) {
542          Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
543          return false;
544       }
545       break;
546    }
547    return true;
548 }            
549
550
551 /*
552  * Send include list to File daemon
553  */
554 bool send_include_list(JCR *jcr)
555 {
556    BSOCK *fd = jcr->file_bsock;
557    if (jcr->fileset->new_include) {
558       fd->fsend(filesetcmd, jcr->fileset->enable_vss ? " vss=1" : "");
559       return send_fileset(jcr);
560    }
561    return true;
562 }
563
564
565 /*
566  * Send exclude list to File daemon
567  *   Under the new scheme, the Exclude list
568  *   is part of the FileSet sent with the
569  *   "include_list" above.
570  */
571 bool send_exclude_list(JCR *jcr)
572 {
573    return true;
574 }
575
576 /* TODO: drop this with runscript.old_proto in bacula 1.42 */
577 static char runbefore[]   = "RunBeforeJob %s\n";
578 static char runafter[]    = "RunAfterJob %s\n";
579 static char OKRunBefore[] = "2000 OK RunBefore\n";
580 static char OKRunAfter[]  = "2000 OK RunAfter\n";
581
582 int send_runscript_with_old_proto(JCR *jcr, int when, POOLMEM *msg)
583 {
584    int ret;
585    Dmsg1(120, "bdird: sending old runcommand to fd '%s'\n",msg);
586    if (when & SCRIPT_Before) {
587       bnet_fsend(jcr->file_bsock, runbefore, msg);
588       ret = response(jcr, jcr->file_bsock, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR);
589    } else {
590       bnet_fsend(jcr->file_bsock, runafter, msg);
591       ret = response(jcr, jcr->file_bsock, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR);
592    }
593    return ret;
594 } /* END OF TODO */
595
596 /*
597  * Send RunScripts to File daemon
598  * 1) We send all runscript to FD, they can be executed Before, After, or twice
599  * 2) Then, we send a "RunBeforeNow" command to the FD to tell him to do the
600  *    first run_script() call. (ie ClientRunBeforeJob)
601  */
602 int send_runscripts_commands(JCR *jcr)
603 {
604    POOLMEM *msg = get_pool_memory(PM_FNAME);
605    BSOCK *fd = jcr->file_bsock;
606    RUNSCRIPT *cmd;
607    bool launch_before_cmd = false;
608    POOLMEM *ehost = get_pool_memory(PM_FNAME);
609    int result;
610
611    Dmsg0(120, "bdird: sending runscripts to fd\n");
612    
613    foreach_alist(cmd, jcr->job->RunScripts) {
614       if (cmd->can_run_at_level(jcr->getJobLevel()) && cmd->target) {
615          ehost = edit_job_codes(jcr, ehost, cmd->target, "");
616          Dmsg2(200, "bdird: runscript %s -> %s\n", cmd->target, ehost);
617
618          if (strcmp(ehost, jcr->client->name()) == 0) {
619             pm_strcpy(msg, cmd->command);
620             bash_spaces(msg);
621
622             Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", cmd->command);
623             
624             /* TODO: remove this with bacula 1.42 */
625             if (cmd->old_proto) {
626                result = send_runscript_with_old_proto(jcr, cmd->when, msg);
627
628             } else {
629                fd->fsend(runscript, cmd->on_success, 
630                                     cmd->on_failure,
631                                     cmd->fail_on_error,
632                                     cmd->when,
633                                     msg);
634
635                result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
636                launch_before_cmd = true;
637             }
638             
639             if (!result) {
640                goto bail_out;
641             }
642          }
643          /* TODO : we have to play with other client */
644          /*
645            else {
646            send command to an other client
647            }
648          */
649       }        
650    } 
651
652    /* Tell the FD to execute the ClientRunBeforeJob */
653    if (launch_before_cmd) {
654       fd->fsend(runbeforenow);
655       if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) {
656         goto bail_out;
657       }
658    }
659    free_pool_memory(msg);
660    free_pool_memory(ehost);
661    return 1;
662
663 bail_out:
664    Jmsg(jcr, M_FATAL, 0, _("Client \"%s\" RunScript failed.\n"), ehost);
665    free_pool_memory(msg);
666    free_pool_memory(ehost);
667    return 0;
668 }
669
670
671
672 /*
673  * Read the attributes from the File daemon for
674  * a Verify job and store them in the catalog.
675  */
676 int get_attributes_and_put_in_catalog(JCR *jcr)
677 {
678    BSOCK   *fd;
679    int n = 0;
680    ATTR_DBR *ar = NULL;
681    char digest[MAXSTRING];
682
683    fd = jcr->file_bsock;
684    jcr->jr.FirstIndex = 1;
685    jcr->FileIndex = 0;
686    /* Start transaction allocates jcr->attr and jcr->ar if needed */
687    db_start_transaction(jcr, jcr->db);     /* start transaction if not already open */
688    ar = jcr->ar;
689
690    Dmsg0(120, "bdird: waiting to receive file attributes\n");
691    /* Pickup file attributes and digest */
692    while (!fd->errors && (n = bget_dirmsg(fd)) > 0) {
693       uint32_t file_index;
694       int stream, len;
695       char *p, *fn;
696       char Digest[MAXSTRING];      /* either Verify opts or MD5/SHA1 digest */
697
698       if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Digest)) != 3) {
699          Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
700 "msglen=%d msg=%s\n"), len, fd->msglen, fd->msg);
701          set_jcr_job_status(jcr, JS_ErrorTerminated);
702          return 0;
703       }
704       p = fd->msg;
705       /* The following three fields were sscanf'ed above so skip them */
706       skip_nonspaces(&p);             /* skip FileIndex */
707       skip_spaces(&p);
708       skip_nonspaces(&p);             /* skip Stream */
709       skip_spaces(&p);
710       skip_nonspaces(&p);             /* skip Opts_Digest */
711       p++;                            /* skip space */
712       Dmsg1(dbglvl, "Stream=%d\n", stream);
713       if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
714          if (jcr->cached_attribute) {
715             Dmsg3(dbglvl, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname,
716                ar->attr);
717             if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
718                Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
719             }
720          }
721          /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
722          fn = jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
723          while (*p != 0) {
724             *fn++ = *p++;                /* copy filename */
725          }
726          *fn = *p++;                     /* term filename and point p to attribs */
727          pm_strcpy(jcr->attr, p);        /* save attributes */
728          jcr->JobFiles++;
729          jcr->FileIndex = file_index;
730          ar->attr = jcr->attr;
731          ar->fname = jcr->fname;
732          ar->FileIndex = file_index;
733          ar->Stream = stream;
734          ar->link = NULL;
735          ar->JobId = jcr->JobId;
736          ar->ClientId = jcr->ClientId;
737          ar->PathId = 0;
738          ar->FilenameId = 0;
739          ar->Digest = NULL;
740          ar->DigestType = CRYPTO_DIGEST_NONE;
741          jcr->cached_attribute = true;
742
743          Dmsg2(dbglvl, "dird<filed: stream=%d %s\n", stream, jcr->fname);
744          Dmsg1(dbglvl, "dird<filed: attr=%s\n", ar->attr);
745          jcr->FileId = ar->FileId;
746       /*
747        * First, get STREAM_UNIX_ATTRIBUTES and fill ATTR_DBR structure
748        * Next, we CAN have a CRYPTO_DIGEST, so we fill ATTR_DBR with it (or not)
749        * When we get a new STREAM_UNIX_ATTRIBUTES, we known that we can add file to the catalog
750        * At the end, we have to add the last file
751        */
752       } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
753          if (jcr->FileIndex != (uint32_t)file_index) {
754             Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"),
755                stream_to_ascii(stream), file_index, jcr->FileIndex);
756             continue;
757          }
758          ar->Digest = digest;
759          ar->DigestType = crypto_digest_stream_type(stream);
760          db_escape_string(jcr, jcr->db, digest, Digest, strlen(Digest));
761          Dmsg4(dbglvl, "stream=%d DigestLen=%d Digest=%s type=%d\n", stream,
762                strlen(digest), digest, ar->DigestType);
763       }
764       jcr->jr.JobFiles = jcr->JobFiles = file_index;
765       jcr->jr.LastIndex = file_index;
766    }
767    if (is_bnet_error(fd)) {
768       Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
769             fd->bstrerror());
770       return 0;
771    }
772    if (jcr->cached_attribute) {
773       Dmsg3(dbglvl, "Cached attr with digest. Stream=%d fname=%s attr=%s\n", ar->Stream,            
774          ar->fname, ar->attr);
775       if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
776          Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
777       }
778       jcr->cached_attribute = false; 
779    }
780    set_jcr_job_status(jcr, JS_Terminated);
781    return 1;
782 }