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