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