]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/fd_cmds.c
9d6904861256afde3df86282e0b2a60bd70b89fc
[bacula/bacula] / bacula / src / dird / fd_cmds.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5    Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6
7    The original author of Bacula is Kern Sibbald, with contributions
8    from many others, a complete list can be found in the file AUTHORS.
9
10    You may use this file and others of this release according to the
11    license defined in the LICENSE file, which includes the Affero General
12    Public License, v3.0 ("AGPLv3") and some additional permissions and
13    terms pursuant to its AGPLv3 Section 7.
14
15    This notice must be preserved when any source code is 
16    conveyed and/or propagated.
17
18    Bacula(R) is a registered trademark of Kern Sibbald.
19 */
20 /*
21  *
22  *   Bacula Director -- fd_cmds.c -- send commands to File daemon
23  *
24  *     Kern Sibbald, October MM
25  *
26  *    This routine is run as a separate thread.  There may be more
27  *    work to be done to make it totally reentrant!!!!
28  *
29  *  Utility functions for sending info to File Daemon.
30  *   These functions are used by both backup and verify.
31  *
32  */
33
34 #include "bacula.h"
35 #include "dird.h"
36 #include "findlib/find.h"
37
38 const int dbglvl = 400;
39
40 /* Commands sent to File daemon */
41 static char filesetcmd[]  = "fileset%s%s\n"; /* set full fileset */
42 static char jobcmd[]      = "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n";
43 /* Note, mtime_only is not used here -- implemented as file option */
44 static char levelcmd[]    = "level = %s%s%s mtime_only=%d %s%s\n";
45 static char runscript[]   = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
46 static char runbeforenow[]= "RunBeforeNow\n";
47 static char bandwidthcmd[] = "setbandwidth=%lld Job=%s\n";
48 static char component_info[] = "component_info\n";
49
50 /* Responses received from File daemon */
51 static char OKinc[]          = "2000 OK include\n";
52 static char OKjob[]          = "2000 OK Job";
53 static char OKlevel[]        = "2000 OK level\n";
54 static char OKRunScript[]    = "2000 OK RunScript\n";
55 static char OKRunBeforeNow[] = "2000 OK RunBeforeNow\n";
56 static char OKRestoreObject[] = "2000 OK ObjectRestored\n";
57 static char OKComponentInfo[] = "2000 OK ComponentInfo\n";
58 static char OKBandwidth[]    = "2000 OK Bandwidth\n";
59
60 /* Forward referenced functions */
61 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd);
62
63 /* External functions */
64 extern DIRRES *director;
65 extern int FDConnectTimeout;
66
67 #define INC_LIST 0
68 #define EXC_LIST 1
69
70 /*
71  * Open connection with File daemon.
72  * Try connecting every retry_interval (default 10 sec), and
73  *   give up after max_retry_time (default 30 mins).
74  */
75
76 int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time,
77                            int verbose)
78 {
79    BSOCK   *fd = jcr->file_bsock;
80    char ed1[30];
81    utime_t heart_beat;
82
83    if (!jcr->client) {
84       Jmsg(jcr, M_FATAL, 0, _("File daemon not defined for current Job\n"));
85       Dmsg0(10, "No Client defined for the job.\n");
86       return 0;
87    }
88
89    if (jcr->client->heartbeat_interval) {
90       heart_beat = jcr->client->heartbeat_interval;
91    } else {
92       heart_beat = director->heartbeat_interval;
93    }
94
95    if (!is_bsock_open(jcr->file_bsock)) {
96       char name[MAX_NAME_LENGTH + 100];
97
98       if (!fd) {
99          fd = jcr->file_bsock = new_bsock();
100       }
101       bstrncpy(name, _("Client: "), sizeof(name));
102       bstrncat(name, jcr->client->name(), sizeof(name));
103
104       fd->set_source_address(director->DIRsrc_addr);
105       if (!fd->connect(jcr,retry_interval,max_retry_time, heart_beat, name, jcr->client->address,
106            NULL, jcr->client->FDport, verbose)) {
107          fd->close();
108          jcr->setJobStatus(JS_ErrorTerminated);
109          return 0;
110       }
111       Dmsg0(10, "Opened connection with File daemon\n");
112    }
113    fd->res = (RES *)jcr->client;      /* save resource in BSOCK */
114    jcr->setJobStatus(JS_Running);
115
116    if (!authenticate_file_daemon(jcr)) {
117       jcr->setJobStatus(JS_ErrorTerminated);
118       Dmsg0(10, "Authentication error with FD.\n");
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          fd->bstrerror());
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 bool send_bwlimit(JCR *jcr, const char *Job)
297 {
298    BSOCK *fd = jcr->file_bsock;
299    if (jcr->FDVersion >= 4) {
300       fd->fsend(bandwidthcmd, jcr->max_bandwidth, Job);
301       if (!response(jcr, fd, OKBandwidth, "Bandwidth", DISPLAY_ERROR)) {
302          jcr->max_bandwidth = 0;      /* can't set bandwidth limit */
303          return false;
304       }
305    }
306    return true;
307 }
308
309 /*
310  * Send level command to FD.
311  * Used for backup jobs and estimate command.
312  */
313 bool send_level_command(JCR *jcr)
314 {
315    BSOCK   *fd = jcr->file_bsock;
316    const char *accurate = jcr->accurate?"accurate_":"";
317    const char *not_accurate = "";
318    const char *rerunning = jcr->rerunning?" rerunning ":" ";
319    /*
320     * Send Level command to File daemon
321     */
322    switch (jcr->getJobLevel()) {
323    case L_BASE:
324       fd->fsend(levelcmd, not_accurate, "base", rerunning, 0, "", "");
325       break;
326    /* L_NONE is the console, sending something off to the FD */
327    case L_NONE:
328    case L_FULL:
329       fd->fsend(levelcmd, not_accurate, "full", rerunning, 0, "", "");
330       break;
331    case L_DIFFERENTIAL:
332       fd->fsend(levelcmd, accurate, "differential", rerunning, 0, "", "");
333       send_since_time(jcr);
334       break;
335    case L_INCREMENTAL:
336       fd->fsend(levelcmd, accurate, "incremental", rerunning, 0, "", "");
337       send_since_time(jcr);
338       break;
339    case L_SINCE:
340    default:
341       Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"),
342          jcr->getJobLevel(), jcr->getJobLevel());
343       return 0;
344    }
345    Dmsg1(120, ">filed: %s", fd->msg);
346    if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) {
347       return false;
348    }
349    return true;
350 }
351
352 /*
353  * Send either an Included or an Excluded list to FD
354  */
355 static bool send_fileset(JCR *jcr)
356 {
357    FILESET *fileset = jcr->fileset;
358    BSOCK   *fd = jcr->file_bsock;
359    STORE   *store = jcr->wstore;
360    int num;
361    bool include = true;
362
363    for ( ;; ) {
364       if (include) {
365          num = fileset->num_includes;
366       } else {
367          num = fileset->num_excludes;
368       }
369       for (int i=0; i<num; i++) {
370          char *item;
371          INCEXE *ie;
372          int j, k;
373
374          if (include) {
375             ie = fileset->include_items[i];
376             fd->fsend("I\n");
377          } else {
378             ie = fileset->exclude_items[i];
379             fd->fsend("E\n");
380          }
381          if (ie->ignoredir) {
382             fd->fsend("Z %s\n", ie->ignoredir);
383          }
384          for (j=0; j<ie->num_opts; j++) {
385             FOPTS *fo = ie->opts_list[j];
386             bool enhanced_wild = false;
387             bool stripped_opts = false;
388             bool compress_disabled = false;
389             char newopts[MAX_FOPTS];
390
391             for (k=0; fo->opts[k]!='\0'; k++) {
392                if (fo->opts[k]=='W') {
393                   enhanced_wild = true;
394                   break;
395                }
396             }
397
398             /*
399              * Strip out compression option Zn if disallowed
400              *  for this Storage.
401              * Strip out dedup option dn if old FD
402              */
403             bool strip_compress = store && !store->AllowCompress;
404             if (strip_compress || jcr->FDVersion >= 11) {
405                int j = 0;
406                for (k=0; fo->opts[k]!='\0'; k++) {
407                   /* Z compress option is followed by the single-digit compress level or 'o' */
408                   if (strip_compress && fo->opts[k]=='Z') {
409                      stripped_opts = true;
410                      compress_disabled = true;
411                      k++;                /* skip level */
412                   } else if (jcr->FDVersion < 11 && fo->opts[k]=='d') {
413                      stripped_opts = true;
414                      k++;              /* skip level */
415                   } else {
416                      newopts[j] = fo->opts[k];
417                      j++;
418                   }
419                }
420                newopts[j] = '\0';
421                if (compress_disabled) {
422                   Jmsg(jcr, M_INFO, 0,
423                       _("FD compression disabled for this Job because AllowCompress=No in Storage resource.\n") );
424                }
425             }
426             if (stripped_opts) {
427                /* Send the new trimmed option set without overwriting fo->opts */
428                fd->fsend("O %s\n", newopts);
429             } else {
430                /* Send the original options */
431                fd->fsend("O %s\n", fo->opts);
432             }
433             for (k=0; k<fo->regex.size(); k++) {
434                fd->fsend("R %s\n", fo->regex.get(k));
435             }
436             for (k=0; k<fo->regexdir.size(); k++) {
437                fd->fsend("RD %s\n", fo->regexdir.get(k));
438             }
439             for (k=0; k<fo->regexfile.size(); k++) {
440                fd->fsend("RF %s\n", fo->regexfile.get(k));
441             }
442             for (k=0; k<fo->wild.size(); k++) {
443                fd->fsend("W %s\n", fo->wild.get(k));
444             }
445             for (k=0; k<fo->wilddir.size(); k++) {
446                fd->fsend("WD %s\n", fo->wilddir.get(k));
447             }
448             for (k=0; k<fo->wildfile.size(); k++) {
449                fd->fsend("WF %s\n", fo->wildfile.get(k));
450             }
451             for (k=0; k<fo->wildbase.size(); k++) {
452                fd->fsend("W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
453             }
454             for (k=0; k<fo->base.size(); k++) {
455                fd->fsend("B %s\n", fo->base.get(k));
456             }
457             for (k=0; k<fo->fstype.size(); k++) {
458                fd->fsend("X %s\n", fo->fstype.get(k));
459             }
460             for (k=0; k<fo->drivetype.size(); k++) {
461                fd->fsend("XD %s\n", fo->drivetype.get(k));
462             }
463             if (fo->plugin) {
464                fd->fsend("G %s\n", fo->plugin);
465             }
466             if (fo->reader) {
467                fd->fsend("D %s\n", fo->reader);
468             }
469             if (fo->writer) {
470                fd->fsend("T %s\n", fo->writer);
471             }
472             fd->fsend("N\n");
473          }
474
475          for (j=0; j<ie->name_list.size(); j++) {
476             item = (char *)ie->name_list.get(j);
477             if (!send_list_item(jcr, "F ", item, fd)) {
478                goto bail_out;
479             }
480          }
481          fd->fsend("N\n");
482          for (j=0; j<ie->plugin_list.size(); j++) {
483             item = (char *)ie->plugin_list.get(j);
484             if (!send_list_item(jcr, "P ", item, fd)) {
485                goto bail_out;
486             }
487          }
488          fd->fsend("N\n");
489       }
490       if (!include) {                 /* If we just did excludes */
491          break;                       /*   all done */
492       }
493       include = false;                /* Now do excludes */
494    }
495
496    fd->signal(BNET_EOD);              /* end of data */
497    if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) {
498       goto bail_out;
499    }
500    return true;
501
502 bail_out:
503    jcr->setJobStatus(JS_ErrorTerminated);
504    return false;
505
506 }
507
508 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd)
509 {
510    BPIPE *bpipe;
511    FILE *ffd;
512    char buf[2000];
513    int optlen, stat;
514    char *p = item;
515
516    switch (*p) {
517    case '|':
518       p++;                      /* skip over the | */
519       fd->msg = edit_job_codes(jcr, fd->msg, p, "");
520       bpipe = open_bpipe(fd->msg, 0, "r");
521       if (!bpipe) {
522          berrno be;
523          Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
524             p, be.bstrerror());
525          return false;
526       }
527       bstrncpy(buf, code, sizeof(buf));
528       Dmsg1(500, "code=%s\n", buf);
529       optlen = strlen(buf);
530       while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) {
531          fd->msglen = Mmsg(fd->msg, "%s", buf);
532          Dmsg2(500, "Inc/exc len=%d: %s", fd->msglen, fd->msg);
533          if (!fd->send()) {
534             close_bpipe(bpipe);
535             Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
536             return false;
537          }
538       }
539       if ((stat=close_bpipe(bpipe)) != 0) {
540          berrno be;
541          Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
542             p, be.bstrerror(stat));
543          return false;
544       }
545       break;
546    case '<':
547       p++;                      /* skip over < */
548       if ((ffd = fopen(p, "rb")) == NULL) {
549          berrno be;
550          Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"),
551             p, be.bstrerror());
552          return false;
553       }
554       bstrncpy(buf, code, sizeof(buf));
555       Dmsg1(500, "code=%s\n", buf);
556       optlen = strlen(buf);
557       while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) {
558          fd->msglen = Mmsg(fd->msg, "%s", buf);
559          if (!fd->send()) {
560             fclose(ffd);
561             Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
562             return false;
563          }
564       }
565       fclose(ffd);
566       break;
567    case '\\':
568       p++;                      /* skip over \ */
569       /* Note, fall through wanted */
570    default:
571       pm_strcpy(fd->msg, code);
572       fd->msglen = pm_strcat(fd->msg, p);
573       Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
574       if (!fd->send()) {
575          Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
576          return false;
577       }
578       break;
579    }
580    return true;
581 }
582
583
584 /*
585  * Send include list to File daemon
586  */
587 bool send_include_list(JCR *jcr)
588 {
589    BSOCK *fd = jcr->file_bsock;
590    if (jcr->fileset->new_include) {
591       fd->fsend(filesetcmd,
592                 jcr->fileset->enable_vss ? " vss=1" : "",
593                 jcr->fileset->enable_snapshot ? " snap=1" : "");
594       return send_fileset(jcr);
595    }
596    return true;
597 }
598
599 /*
600  * Send exclude list to File daemon
601  *   Under the new scheme, the Exclude list
602  *   is part of the FileSet sent with the
603  *   "include_list" above.
604  */
605 bool send_exclude_list(JCR *jcr)
606 {
607    return true;
608 }
609
610 /* TODO: drop this with runscript.old_proto in bacula 1.42 */
611 static char runbefore[]   = "RunBeforeJob %s\n";
612 static char runafter[]    = "RunAfterJob %s\n";
613 static char OKRunBefore[] = "2000 OK RunBefore\n";
614 static char OKRunAfter[]  = "2000 OK RunAfter\n";
615
616 int send_runscript_with_old_proto(JCR *jcr, int when, POOLMEM *msg)
617 {
618    int ret;
619    Dmsg1(120, "bdird: sending old runcommand to fd '%s'\n",msg);
620    if (when & SCRIPT_Before) {
621       jcr->file_bsock->fsend(runbefore, msg);
622       ret = response(jcr, jcr->file_bsock, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR);
623    } else {
624       jcr->file_bsock->fsend(runafter, msg);
625       ret = response(jcr, jcr->file_bsock, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR);
626    }
627    return ret;
628 } /* END OF TODO */
629
630 /*
631  * Send RunScripts to File daemon
632  * 1) We send all runscript to FD, they can be executed Before, After, or twice
633  * 2) Then, we send a "RunBeforeNow" command to the FD to tell him to do the
634  *    first run_script() call. (ie ClientRunBeforeJob)
635  */
636 int send_runscripts_commands(JCR *jcr)
637 {
638    POOLMEM *msg = get_pool_memory(PM_FNAME);
639    BSOCK *fd = jcr->file_bsock;
640    RUNSCRIPT *cmd;
641    bool launch_before_cmd = false;
642    POOLMEM *ehost = get_pool_memory(PM_FNAME);
643    int result;
644
645    Dmsg0(120, "bdird: sending runscripts to fd\n");
646
647    foreach_alist(cmd, jcr->job->RunScripts) {
648       if (cmd->can_run_at_level(jcr->getJobLevel()) && cmd->target) {
649          ehost = edit_job_codes(jcr, ehost, cmd->target, "");
650          Dmsg2(200, "bdird: runscript %s -> %s\n", cmd->target, ehost);
651
652          if (strcmp(ehost, jcr->client->name()) == 0) {
653             pm_strcpy(msg, cmd->command);
654             bash_spaces(msg);
655
656             Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", cmd->command);
657
658             /* TODO: remove this with bacula 1.42 */
659             if (cmd->old_proto) {
660                result = send_runscript_with_old_proto(jcr, cmd->when, msg);
661
662             } else {
663                fd->fsend(runscript, cmd->on_success,
664                                     cmd->on_failure,
665                                     cmd->fail_on_error,
666                                     cmd->when,
667                                     msg);
668
669                result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
670                launch_before_cmd = true;
671             }
672
673             if (!result) {
674                goto bail_out;
675             }
676          }
677          /* TODO : we have to play with other client */
678          /*
679            else {
680            send command to an other client
681            }
682          */
683       }
684    }
685
686    /* Tell the FD to execute the ClientRunBeforeJob */
687    if (launch_before_cmd) {
688       fd->fsend(runbeforenow);
689       if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) {
690         goto bail_out;
691       }
692    }
693    free_pool_memory(msg);
694    free_pool_memory(ehost);
695    return 1;
696
697 bail_out:
698    Jmsg(jcr, M_FATAL, 0, _("Client \"%s\" RunScript failed.\n"), ehost);
699    free_pool_memory(msg);
700    free_pool_memory(ehost);
701    return 0;
702 }
703
704 struct OBJ_CTX {
705    JCR *jcr;
706    int count;
707 };
708
709 static int restore_object_handler(void *ctx, int num_fields, char **row)
710 {
711    OBJ_CTX *octx = (OBJ_CTX *)ctx;
712    JCR *jcr = octx->jcr;
713    BSOCK *fd;
714
715    fd = jcr->file_bsock;
716    if (jcr->is_job_canceled()) {
717       return 1;
718    }
719    /* Old File Daemon doesn't handle restore objects */
720    if (jcr->FDVersion < 3) {
721       Jmsg(jcr, M_WARNING, 0, _("Client \"%s\" may not be used to restore "
722                                 "this job. Please upgrade your client.\n"),
723            jcr->client->name());
724       return 1;
725    }
726
727    if (jcr->FDVersion < 5) {    /* Old version without PluginName */
728       fd->fsend("restoreobject JobId=%s %s,%s,%s,%s,%s,%s\n",
729                 row[0], row[1], row[2], row[3], row[4], row[5], row[6]);
730    } else {
731       /* bash spaces from PluginName */
732       bash_spaces(row[9]);
733       fd->fsend("restoreobject JobId=%s %s,%s,%s,%s,%s,%s,%s\n",
734                 row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[9]);
735    }
736    Dmsg1(010, "Send obj hdr=%s", fd->msg);
737
738    fd->msglen = pm_strcpy(fd->msg, row[7]);
739    fd->send();                            /* send Object name */
740
741    Dmsg1(010, "Send obj: %s\n", fd->msg);
742
743 //   fd->msglen = str_to_uint64(row[1]);   /* object length */
744 //   Dmsg1(000, "obj size: %lld\n", (uint64_t)fd->msglen);
745
746    /* object */
747    db_unescape_object(jcr, jcr->db,
748                       row[8],                /* Object  */
749                       str_to_uint64(row[1]), /* Object length */
750                       &fd->msg, &fd->msglen);
751    fd->send();                           /* send object */
752    octx->count++;
753
754    if (debug_level > 100) {
755       for (int i=0; i < fd->msglen; i++)
756          if (!fd->msg[i])
757             fd->msg[i] = ' ';
758       Dmsg1(000, "Send obj: %s\n", fd->msg);
759    }
760
761    return 0;
762 }
763
764 /*
765  * Send the plugin Restore Objects, which allow the
766  *  plugin to get information early in the restore
767  *  process.  The RestoreObjects were created during
768  *  the backup by the plugin.
769  */
770 bool send_restore_objects(JCR *jcr)
771 {
772    char ed1[50];
773    POOL_MEM query(PM_MESSAGE);
774    BSOCK *fd;
775    OBJ_CTX octx;
776
777    if (!jcr->JobIds || !jcr->JobIds[0]) {
778       return true;
779    }
780    octx.jcr = jcr;
781    octx.count = 0;
782
783    /* restore_object_handler is called for each file found */
784
785    /* send restore objects for all jobs involved  */
786    Mmsg(query, get_restore_objects, jcr->JobIds, FT_RESTORE_FIRST);
787    db_sql_query(jcr->db, query.c_str(), restore_object_handler, (void *)&octx);
788
789    /* send config objects for the current restore job */
790    Mmsg(query, get_restore_objects,
791         edit_uint64(jcr->JobId, ed1), FT_PLUGIN_CONFIG_FILLED);
792    db_sql_query(jcr->db, query.c_str(), restore_object_handler, (void *)&octx);
793
794    /*
795     * Send to FD only if we have at least one restore object.
796     * This permits backward compatibility with older FDs.
797     */
798    if (octx.count > 0) {
799       fd = jcr->file_bsock;
800       fd->fsend("restoreobject end\n");
801       if (!response(jcr, fd, OKRestoreObject, "RestoreObject", DISPLAY_ERROR)) {
802          Jmsg(jcr, M_FATAL, 0, _("RestoreObject failed.\n"));
803          return false;
804       }
805    }
806    return true;
807 }
808
809 /*
810  * Send the plugin a list of component info files.  These
811  *  were files that were created during the backup for
812  *  the VSS plugin.  The list is a list of those component
813  *  files that have been chosen for restore.  We
814  *  send them before the Restore Objects.
815  */
816 bool send_component_info(JCR *jcr)
817 {
818    BSOCK *fd;
819    char buf[2000];
820    bool ok = true;
821
822    if (!jcr->component_fd) {
823       return true;           /* nothing to send */
824    }
825    /* Don't send if old version FD */
826    if (jcr->FDVersion < 6) {
827       goto bail_out;
828    }
829
830    rewind(jcr->component_fd);
831    fd = jcr->file_bsock;
832    fd->fsend(component_info);
833    while (fgets(buf, sizeof(buf), jcr->component_fd)) {
834       fd->fsend("%s", buf);
835       Dmsg1(050, "Send component_info to FD: %s\n", buf);
836    }
837    fd->signal(BNET_EOD);
838    if (!response(jcr, fd, OKComponentInfo, "ComponentInfo", DISPLAY_ERROR)) {
839       Jmsg(jcr, M_FATAL, 0, _("ComponentInfo failed.\n"));
840       ok = false;
841    }
842
843 bail_out:
844    fclose(jcr->component_fd);
845    jcr->component_fd = NULL;
846    unlink(jcr->component_fname);
847    free_and_null_pool_memory(jcr->component_fname);
848    return ok;
849 }
850
851 /*
852  * Read the attributes from the File daemon for
853  * a Verify job and store them in the catalog.
854  */
855 int get_attributes_and_put_in_catalog(JCR *jcr)
856 {
857    BSOCK   *fd;
858    int n = 0;
859    ATTR_DBR *ar = NULL;
860    char digest[MAXSTRING];
861
862    fd = jcr->file_bsock;
863    jcr->jr.FirstIndex = 1;
864    jcr->FileIndex = 0;
865    /* Start transaction allocates jcr->attr and jcr->ar if needed */
866    db_start_transaction(jcr, jcr->db);     /* start transaction if not already open */
867    ar = jcr->ar;
868
869    Dmsg0(120, "bdird: waiting to receive file attributes\n");
870    /* Pickup file attributes and digest */
871    while (!fd->errors && (n = bget_dirmsg(fd)) > 0) {
872       uint32_t file_index;
873       int stream, len;
874       char *p, *fn;
875       char Digest[MAXSTRING];      /* either Verify opts or MD5/SHA1 digest */
876
877       /* Stop here if canceled */
878       if (jcr->is_job_canceled()) {
879          jcr->cached_attribute = false;
880          return 0;
881       }
882
883       if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Digest)) != 3) {
884          Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
885 "msglen=%d msg=%s\n"), len, fd->msglen, fd->msg);
886          jcr->setJobStatus(JS_ErrorTerminated);
887          jcr->cached_attribute = false;
888          return 0;
889       }
890       p = fd->msg;
891       /* The following three fields were sscanf'ed above so skip them */
892       skip_nonspaces(&p);             /* skip FileIndex */
893       skip_spaces(&p);
894       skip_nonspaces(&p);             /* skip Stream */
895       skip_spaces(&p);
896       skip_nonspaces(&p);             /* skip Opts_Digest */
897       p++;                            /* skip space */
898       Dmsg1(dbglvl, "Stream=%d\n", stream);
899       if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
900          if (jcr->cached_attribute) {
901             Dmsg3(dbglvl, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname,
902                ar->attr);
903             if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
904                Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
905             }
906             jcr->cached_attribute = false;
907          }
908          /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
909          fn = jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
910          while (*p != 0) {
911             *fn++ = *p++;                /* copy filename */
912          }
913          *fn = *p++;                     /* term filename and point p to attribs */
914          pm_strcpy(jcr->attr, p);        /* save attributes */
915          jcr->JobFiles++;
916          jcr->FileIndex = file_index;
917          ar->attr = jcr->attr;
918          ar->fname = jcr->fname;
919          ar->FileIndex = file_index;
920          ar->Stream = stream;
921          ar->link = NULL;
922          ar->JobId = jcr->JobId;
923          ar->ClientId = jcr->ClientId;
924          ar->PathId = 0;
925          ar->FilenameId = 0;
926          ar->Digest = NULL;
927          ar->DigestType = CRYPTO_DIGEST_NONE;
928          ar->DeltaSeq = 0;
929          jcr->cached_attribute = true;
930
931          Dmsg2(dbglvl, "dird<filed: stream=%d %s\n", stream, jcr->fname);
932          Dmsg1(dbglvl, "dird<filed: attr=%s\n", ar->attr);
933          jcr->FileId = ar->FileId;
934       /*
935        * First, get STREAM_UNIX_ATTRIBUTES and fill ATTR_DBR structure
936        * Next, we CAN have a CRYPTO_DIGEST, so we fill ATTR_DBR with it (or not)
937        * When we get a new STREAM_UNIX_ATTRIBUTES, we known that we can add file to the catalog
938        * At the end, we have to add the last file
939        */
940       } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
941          if (jcr->FileIndex != (uint32_t)file_index) {
942             Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"),
943                stream_to_ascii(stream), file_index, jcr->FileIndex);
944             continue;
945          }
946          ar->Digest = digest;
947          ar->DigestType = crypto_digest_stream_type(stream);
948          db_escape_string(jcr, jcr->db, digest, Digest, strlen(Digest));
949          Dmsg4(dbglvl, "stream=%d DigestLen=%d Digest=%s type=%d\n", stream,
950                strlen(digest), digest, ar->DigestType);
951       }
952       jcr->jr.JobFiles = jcr->JobFiles = file_index;
953       jcr->jr.LastIndex = file_index;
954    }
955    if (fd->is_error()) {
956       Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
957             fd->bstrerror());
958       jcr->cached_attribute = false;
959       return 0;
960    }
961    if (jcr->cached_attribute) {
962       Dmsg3(dbglvl, "Cached attr with digest. Stream=%d fname=%s attr=%s\n", ar->Stream,
963          ar->fname, ar->attr);
964       if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
965          Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
966       }
967       jcr->cached_attribute = false;
968    }
969    jcr->setJobStatus(JS_Terminated);
970    return 1;
971 }