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