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