]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/fd_cmds.c
Fix couple of g++ warnings
[bacula/bacula] / bacula / src / dird / fd_cmds.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- fd_cmds.c -- send commands to File daemon
31  *
32  *     Kern Sibbald, October MM
33  *
34  *    This routine is run as a separate thread.  There may be more
35  *    work to be done to make it totally reentrant!!!!
36  *
37  *  Utility functions for sending info to File Daemon.
38  *   These functions are used by both backup and verify.
39  *
40  */
41
42 #include "bacula.h"
43 #include "dird.h"
44 #include "findlib/find.h"
45
46 const int dbglvl = 400;
47
48 /* Commands sent to File daemon */
49 static char filesetcmd[]  = "fileset%s\n"; /* set full fileset */
50 static char jobcmd[]      = "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n";
51 /* Note, mtime_only is not used here -- implemented as file option */
52 static char levelcmd[]    = "level = %s%s%s mtime_only=%d\n";
53 static char runscript[]   = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
54 static char runbeforenow[]= "RunBeforeNow\n";
55 static char bandwidthcmd[] = "setbandwidth=%lld Job=%s\n";
56
57 /* Responses received from File daemon */
58 static char OKinc[]          = "2000 OK include\n";
59 static char OKjob[]          = "2000 OK Job";
60 static char OKlevel[]        = "2000 OK level\n";
61 static char OKRunScript[]    = "2000 OK RunScript\n";
62 static char OKRunBeforeNow[] = "2000 OK RunBeforeNow\n";
63 static char OKRestoreObject[] = "2000 OK ObjectRestored\n";
64 static char OKBandwidth[]    = "2000 OK Bandwidth\n"; 
65
66 /* Forward referenced functions */
67 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd);
68
69 /* External functions */
70 extern DIRRES *director;
71 extern int FDConnectTimeout;
72
73 #define INC_LIST 0
74 #define EXC_LIST 1
75
76 /*
77  * Open connection with File daemon.
78  * Try connecting every retry_interval (default 10 sec), and
79  *   give up after max_retry_time (default 30 mins).
80  */
81
82 int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time,
83                            int verbose)
84 {
85    BSOCK   *fd = new_bsock();
86    char ed1[30];
87    utime_t heart_beat;
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 (!jcr->file_bsock) {
96       char name[MAX_NAME_LENGTH + 100];
97       bstrncpy(name, _("Client: "), sizeof(name));
98       bstrncat(name, jcr->client->name(), sizeof(name));
99
100       fd->set_source_address(director->DIRsrc_addr);
101       if (!fd->connect(jcr,retry_interval,max_retry_time, heart_beat, name, jcr->client->address,
102            NULL, jcr->client->FDport, verbose)) {
103         fd->destroy();
104         fd = NULL;
105       }
106
107       if (fd == NULL) {
108          jcr->setJobStatus(JS_ErrorTerminated);
109          return 0;
110       }
111       Dmsg0(10, "Opened connection with File daemon\n");
112    } else {
113       fd = jcr->file_bsock;           /* use existing connection */
114    }
115    fd->res = (RES *)jcr->client;      /* save resource in BSOCK */
116    jcr->file_bsock = fd;
117    jcr->setJobStatus(JS_Running);
118
119    if (!authenticate_file_daemon(jcr)) {
120       jcr->setJobStatus(JS_ErrorTerminated);
121       return 0;
122    }
123
124    /*
125     * Now send JobId and authorization key
126     */
127    if (jcr->sd_auth_key == NULL) {
128       jcr->sd_auth_key = bstrdup("dummy");
129    }
130    fd->fsend(jobcmd, edit_int64(jcr->JobId, ed1), jcr->Job, jcr->VolSessionId,
131              jcr->VolSessionTime, jcr->sd_auth_key);
132    if (!jcr->keep_sd_auth_key && strcmp(jcr->sd_auth_key, "dummy")) {
133       memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
134    }
135    Dmsg1(100, ">filed: %s", fd->msg);
136    if (bget_dirmsg(fd) > 0) {
137        Dmsg1(110, "<filed: %s", fd->msg);
138        if (strncmp(fd->msg, OKjob, strlen(OKjob)) != 0) {
139           Jmsg(jcr, M_FATAL, 0, _("File daemon \"%s\" rejected Job command: %s\n"),
140              jcr->client->hdr.name, fd->msg);
141           jcr->setJobStatus(JS_ErrorTerminated);
142           return 0;
143        } else if (jcr->db) {
144           CLIENT_DBR cr;
145           memset(&cr, 0, sizeof(cr));
146           bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
147           cr.AutoPrune = jcr->client->AutoPrune;
148           cr.FileRetention = jcr->client->FileRetention;
149           cr.JobRetention = jcr->client->JobRetention;
150           bstrncpy(cr.Uname, fd->msg+strlen(OKjob)+1, sizeof(cr.Uname));
151           if (!db_update_client_record(jcr, jcr->db, &cr)) {
152              Jmsg(jcr, M_WARNING, 0, _("Error updating Client record. ERR=%s\n"),
153                 db_strerror(jcr->db));
154           }
155        }
156    } else {
157       Jmsg(jcr, M_FATAL, 0, _("FD gave bad response to JobId command: %s\n"),
158          bnet_strerror(fd));
159       jcr->setJobStatus(JS_ErrorTerminated);
160       return 0;
161    }
162    return 1;
163 }
164
165 /*
166  * This subroutine edits the last job start time into a
167  *   "since=date/time" buffer that is returned in the
168  *   variable since.  This is used for display purposes in
169  *   the job report.  The time in jcr->stime is later
170  *   passed to tell the File daemon what to do.
171  */
172 void get_level_since_time(JCR *jcr, char *since, int since_len)
173 {
174    int JobLevel;
175    bool have_full;
176    bool do_full = false;
177    bool do_diff = false;
178    utime_t now;
179    utime_t last_full_time = 0;
180    utime_t last_diff_time;
181
182    since[0] = 0;
183    /* If job cloned and a since time already given, use it */
184    if (jcr->cloned && jcr->stime && jcr->stime[0]) {
185       bstrncpy(since, _(", since="), since_len);
186       bstrncat(since, jcr->stime, since_len);
187       return;
188    }
189    /* Make sure stime buffer is allocated */
190    if (!jcr->stime) {
191       jcr->stime = get_pool_memory(PM_MESSAGE);
192    } 
193    jcr->stime[0] = 0;
194    /*
195     * Lookup the last FULL backup job to get the time/date for a
196     * differential or incremental save.
197     */
198    switch (jcr->getJobLevel()) {
199    case L_DIFFERENTIAL:
200    case L_INCREMENTAL:
201       POOLMEM *stime = get_pool_memory(PM_MESSAGE);
202       /* Look up start time of last Full job */
203       now = (utime_t)time(NULL);
204       jcr->jr.JobId = 0;     /* flag to return since time */
205       /*
206        * This is probably redundant, but some of the code below
207        * uses jcr->stime, so don't remove unless you are sure.
208        */
209       if (!db_find_job_start_time(jcr, jcr->db, &jcr->jr, &jcr->stime)) {
210          do_full = true;
211       }
212       have_full = db_find_last_job_start_time(jcr, jcr->db, &jcr->jr, &stime, 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, &stime, 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             if (db_find_failed_job_since(jcr, jcr->db, &jcr->jr, jcr->stime, JobLevel)) {
261                Jmsg(jcr, M_INFO, 0, _("Prior failed job found in catalog. Upgrading to %s.\n"),
262                   level_to_str(JobLevel));
263                bsnprintf(since, since_len, _(" (upgraded from %s)"),
264                   level_to_str(jcr->getJobLevel()));
265                jcr->setJobLevel(jcr->jr.JobLevel = JobLevel);
266                jcr->jr.JobId = jcr->JobId;
267                break;
268             }
269          }
270          bstrncpy(since, _(", since="), since_len);
271          bstrncat(since, jcr->stime, since_len);
272       }
273       jcr->jr.JobId = jcr->JobId;
274       break;
275    }
276    Dmsg2(100, "Level=%c last start time=%s\n", jcr->getJobLevel(), jcr->stime);
277 }
278
279 static void send_since_time(JCR *jcr)
280 {
281    BSOCK   *fd = jcr->file_bsock;
282    utime_t stime;
283    char ed1[50];
284
285    stime = str_to_utime(jcr->stime);
286    fd->fsend(levelcmd, "", NT_("since_utime "), edit_uint64(stime, ed1), 0);
287    while (bget_dirmsg(fd) >= 0) {  /* allow him to poll us to sync clocks */
288       Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
289    }
290 }
291
292 bool send_bwlimit(JCR *jcr, const char *Job)
293 {
294    BSOCK *fd = jcr->file_bsock;
295    if (jcr->FDVersion >= 4) {
296       fd->fsend(bandwidthcmd, jcr->max_bandwidth, Job);
297       if (!response(jcr, fd, OKBandwidth, "Bandwidth", DISPLAY_ERROR)) {
298          jcr->max_bandwidth = 0;      /* can't set bandwidth limit */
299          return false;
300       }
301    }
302    return true;
303 }
304
305 /*
306  * Send level command to FD.
307  * Used for backup jobs and estimate command.
308  */
309 bool send_level_command(JCR *jcr)
310 {
311    BSOCK   *fd = jcr->file_bsock;
312    const char *accurate = jcr->accurate?"accurate_":"";
313    const char *not_accurate = "";
314    const char *rerunning = jcr->rerunning?" rerunning ":" ";
315    /*
316     * Send Level command to File daemon
317     */
318    switch (jcr->getJobLevel()) {
319    case L_BASE:
320       fd->fsend(levelcmd, not_accurate, "base", rerunning, 0);
321       break;
322    /* L_NONE is the console, sending something off to the FD */
323    case L_NONE:
324    case L_FULL:
325       fd->fsend(levelcmd, not_accurate, "full", rerunning, 0);
326       break;
327    case L_DIFFERENTIAL:
328       fd->fsend(levelcmd, accurate, "differential", rerunning, 0);
329       send_since_time(jcr);
330       break;
331    case L_INCREMENTAL:
332       fd->fsend(levelcmd, accurate, "incremental", rerunning, 0);
333       send_since_time(jcr);
334       break;
335    case L_SINCE:
336    default:
337       Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"),
338          jcr->getJobLevel(), jcr->getJobLevel());
339       return 0;
340    }
341    Dmsg1(120, ">filed: %s", fd->msg);
342    if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) {
343       return false;
344    }
345    return true;
346 }
347
348 /*
349  * Send either an Included or an Excluded list to FD
350  */
351 static bool send_fileset(JCR *jcr)
352 {
353    FILESET *fileset = jcr->fileset;
354    BSOCK   *fd = jcr->file_bsock;
355    STORE   *store = jcr->wstore;
356    int num;
357    bool include = true;
358
359    for ( ;; ) {
360       if (include) {
361          num = fileset->num_includes;
362       } else {
363          num = fileset->num_excludes;
364       }
365       for (int i=0; i<num; i++) {
366          char *item;
367          INCEXE *ie;
368          int j, k;
369
370          if (include) {
371             ie = fileset->include_items[i];
372             fd->fsend("I\n");
373          } else {
374             ie = fileset->exclude_items[i];
375             fd->fsend("E\n");
376          }
377          if (ie->ignoredir) {
378             bnet_fsend(fd, "Z %s\n", ie->ignoredir);
379          }
380          for (j=0; j<ie->num_opts; j++) {
381             FOPTS *fo = ie->opts_list[j];
382
383             bool enhanced_wild = false;
384             for (k=0; fo->opts[k]!='\0'; k++) {
385                if (fo->opts[k]=='W') {
386                   enhanced_wild = true;
387                   break;
388                }
389             }
390
391             /* Strip out compression option Zn if disallowed for this Storage */
392             if (store && !store->AllowCompress) {
393                char newopts[MAX_FOPTS];
394                bool done=false;         /* print warning only if compression enabled in FS */ 
395                int j = 0;
396                for (k=0; fo->opts[k]!='\0'; k++) {                   
397                  /* Z compress option is followed by the single-digit compress level */
398                  if (fo->opts[k]=='Z') {
399                     done=true;
400                     k++;                /* skip option and level */
401                  } else {
402                     newopts[j] = fo->opts[k];
403                     j++;
404                  }
405                }
406                newopts[j] = '\0';
407
408                if (done) {
409                   Jmsg(jcr, M_INFO, 0,
410                       _("FD compression disabled for this Job because AllowCompress=No in Storage resource.\n") );
411                }
412                /* Send the new trimmed option set without overwriting fo->opts */
413                fd->fsend("O %s\n", newopts);
414             } else {
415                /* Send the original options */
416                fd->fsend("O %s\n", fo->opts);
417             }
418
419             for (k=0; k<fo->regex.size(); k++) {
420                fd->fsend("R %s\n", fo->regex.get(k));
421             }
422             for (k=0; k<fo->regexdir.size(); k++) {
423                fd->fsend("RD %s\n", fo->regexdir.get(k));
424             }
425             for (k=0; k<fo->regexfile.size(); k++) {
426                fd->fsend("RF %s\n", fo->regexfile.get(k));
427             }
428             for (k=0; k<fo->wild.size(); k++) {
429                fd->fsend("W %s\n", fo->wild.get(k));
430             }
431             for (k=0; k<fo->wilddir.size(); k++) {
432                fd->fsend("WD %s\n", fo->wilddir.get(k));
433             }
434             for (k=0; k<fo->wildfile.size(); k++) {
435                fd->fsend("WF %s\n", fo->wildfile.get(k));
436             }
437             for (k=0; k<fo->wildbase.size(); k++) {
438                fd->fsend("W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
439             }
440             for (k=0; k<fo->base.size(); k++) {
441                fd->fsend("B %s\n", fo->base.get(k));
442             }
443             for (k=0; k<fo->fstype.size(); k++) {
444                fd->fsend("X %s\n", fo->fstype.get(k));
445             }
446             for (k=0; k<fo->drivetype.size(); k++) {
447                fd->fsend("XD %s\n", fo->drivetype.get(k));
448             }
449             if (fo->plugin) {
450                fd->fsend("G %s\n", fo->plugin);
451             }
452             if (fo->reader) {
453                fd->fsend("D %s\n", fo->reader);
454             }
455             if (fo->writer) {
456                fd->fsend("T %s\n", fo->writer);
457             }
458             fd->fsend("N\n");
459          }
460
461          for (j=0; j<ie->name_list.size(); j++) {
462             item = (char *)ie->name_list.get(j);
463             if (!send_list_item(jcr, "F ", item, fd)) {
464                goto bail_out;
465             }
466          }
467          fd->fsend("N\n");
468          for (j=0; j<ie->plugin_list.size(); j++) {
469             item = (char *)ie->plugin_list.get(j);
470             if (!send_list_item(jcr, "P ", item, fd)) {
471                goto bail_out;
472             }
473          }
474          fd->fsend("N\n");
475       }
476       if (!include) {                 /* If we just did excludes */
477          break;                       /*   all done */
478       }
479       include = false;                /* Now do excludes */
480    }
481
482    fd->signal(BNET_EOD);              /* end of data */
483    if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) {
484       goto bail_out;
485    }
486    return true;
487
488 bail_out:
489    jcr->setJobStatus(JS_ErrorTerminated);
490    return false;
491
492 }
493
494 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd)
495 {
496    BPIPE *bpipe;
497    FILE *ffd;
498    char buf[2000];
499    int optlen, stat;
500    char *p = item;
501
502    switch (*p) {
503    case '|':
504       p++;                      /* skip over the | */
505       fd->msg = edit_job_codes(jcr, fd->msg, p, "");
506       bpipe = open_bpipe(fd->msg, 0, "r");
507       if (!bpipe) {
508          berrno be;
509          Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
510             p, be.bstrerror());
511          return false;
512       }
513       bstrncpy(buf, code, sizeof(buf));
514       Dmsg1(500, "code=%s\n", buf);
515       optlen = strlen(buf);
516       while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) {
517          fd->msglen = Mmsg(fd->msg, "%s", buf);
518          Dmsg2(500, "Inc/exc len=%d: %s", fd->msglen, fd->msg);
519          if (!bnet_send(fd)) {
520             Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
521             return false;
522          }
523       }
524       if ((stat=close_bpipe(bpipe)) != 0) {
525          berrno be;
526          Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
527             p, be.bstrerror(stat));
528          return false;
529       }
530       break;
531    case '<':
532       p++;                      /* skip over < */
533       if ((ffd = fopen(p, "rb")) == NULL) {
534          berrno be;
535          Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"),
536             p, be.bstrerror());
537          return false;
538       }
539       bstrncpy(buf, code, sizeof(buf));
540       Dmsg1(500, "code=%s\n", buf);
541       optlen = strlen(buf);
542       while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) {
543          fd->msglen = Mmsg(fd->msg, "%s", buf);
544          if (!bnet_send(fd)) {
545             Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
546             return false;
547          }
548       }
549       fclose(ffd);
550       break;
551    case '\\':
552       p++;                      /* skip over \ */
553       /* Note, fall through wanted */
554    default:
555       pm_strcpy(fd->msg, code);
556       fd->msglen = pm_strcat(fd->msg, p);
557       Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
558       if (!fd->send()) {
559          Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
560          return false;
561       }
562       break;
563    }
564    return true;
565 }            
566
567
568 /*
569  * Send include list to File daemon
570  */
571 bool send_include_list(JCR *jcr)
572 {
573    BSOCK *fd = jcr->file_bsock;
574    if (jcr->fileset->new_include) {
575       fd->fsend(filesetcmd, jcr->fileset->enable_vss ? " vss=1" : "");
576       return send_fileset(jcr);
577    }
578    return true;
579 }
580
581
582 /*
583  * Send exclude list to File daemon
584  *   Under the new scheme, the Exclude list
585  *   is part of the FileSet sent with the
586  *   "include_list" above.
587  */
588 bool send_exclude_list(JCR *jcr)
589 {
590    return true;
591 }
592
593 /* TODO: drop this with runscript.old_proto in bacula 1.42 */
594 static char runbefore[]   = "RunBeforeJob %s\n";
595 static char runafter[]    = "RunAfterJob %s\n";
596 static char OKRunBefore[] = "2000 OK RunBefore\n";
597 static char OKRunAfter[]  = "2000 OK RunAfter\n";
598
599 int send_runscript_with_old_proto(JCR *jcr, int when, POOLMEM *msg)
600 {
601    int ret;
602    Dmsg1(120, "bdird: sending old runcommand to fd '%s'\n",msg);
603    if (when & SCRIPT_Before) {
604       bnet_fsend(jcr->file_bsock, runbefore, msg);
605       ret = response(jcr, jcr->file_bsock, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR);
606    } else {
607       bnet_fsend(jcr->file_bsock, runafter, msg);
608       ret = response(jcr, jcr->file_bsock, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR);
609    }
610    return ret;
611 } /* END OF TODO */
612
613 /*
614  * Send RunScripts to File daemon
615  * 1) We send all runscript to FD, they can be executed Before, After, or twice
616  * 2) Then, we send a "RunBeforeNow" command to the FD to tell him to do the
617  *    first run_script() call. (ie ClientRunBeforeJob)
618  */
619 int send_runscripts_commands(JCR *jcr)
620 {
621    POOLMEM *msg = get_pool_memory(PM_FNAME);
622    BSOCK *fd = jcr->file_bsock;
623    RUNSCRIPT *cmd;
624    bool launch_before_cmd = false;
625    POOLMEM *ehost = get_pool_memory(PM_FNAME);
626    int result;
627
628    Dmsg0(120, "bdird: sending runscripts to fd\n");
629    
630    foreach_alist(cmd, jcr->job->RunScripts) {
631       if (cmd->can_run_at_level(jcr->getJobLevel()) && cmd->target) {
632          ehost = edit_job_codes(jcr, ehost, cmd->target, "");
633          Dmsg2(200, "bdird: runscript %s -> %s\n", cmd->target, ehost);
634
635          if (strcmp(ehost, jcr->client->name()) == 0) {
636             pm_strcpy(msg, cmd->command);
637             bash_spaces(msg);
638
639             Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", cmd->command);
640             
641             /* TODO: remove this with bacula 1.42 */
642             if (cmd->old_proto) {
643                result = send_runscript_with_old_proto(jcr, cmd->when, msg);
644
645             } else {
646                fd->fsend(runscript, cmd->on_success, 
647                                     cmd->on_failure,
648                                     cmd->fail_on_error,
649                                     cmd->when,
650                                     msg);
651
652                result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
653                launch_before_cmd = true;
654             }
655             
656             if (!result) {
657                goto bail_out;
658             }
659          }
660          /* TODO : we have to play with other client */
661          /*
662            else {
663            send command to an other client
664            }
665          */
666       }        
667    } 
668
669    /* Tell the FD to execute the ClientRunBeforeJob */
670    if (launch_before_cmd) {
671       fd->fsend(runbeforenow);
672       if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) {
673         goto bail_out;
674       }
675    }
676    free_pool_memory(msg);
677    free_pool_memory(ehost);
678    return 1;
679
680 bail_out:
681    Jmsg(jcr, M_FATAL, 0, _("Client \"%s\" RunScript failed.\n"), ehost);
682    free_pool_memory(msg);
683    free_pool_memory(ehost);
684    return 0;
685 }
686
687 struct OBJ_CTX {
688    JCR *jcr;
689    int count;
690 };
691
692 static int restore_object_handler(void *ctx, int num_fields, char **row)
693 {
694    OBJ_CTX *octx = (OBJ_CTX *)ctx;
695    JCR *jcr = octx->jcr;
696    BSOCK *fd;
697
698    fd = jcr->file_bsock;
699    if (jcr->is_job_canceled()) {
700       return 1;
701    }
702    /* Old File Daemon doesn't handle restore objects */
703    if (jcr->FDVersion < 3) {
704       Jmsg(jcr, M_WARNING, 0, _("Client \"%s\" may not be used to restore "
705                                 "this job. Please upgrade your client.\n"), 
706            jcr->client->name());
707       return 1;
708    }
709
710    fd->fsend("restoreobject JobId=%s %s,%s,%s,%s,%s,%s\n",
711       row[0], row[1], row[2], row[3], row[4], row[5], row[6]);
712
713    Dmsg1(010, "Send obj hdr=%s", fd->msg);
714
715    fd->msglen = pm_strcpy(fd->msg, row[7]);
716    fd->send();                            /* send Object name */
717
718    Dmsg1(010, "Send obj: %s\n", fd->msg);
719
720 //   fd->msglen = str_to_uint64(row[1]);   /* object length */
721 //   Dmsg1(000, "obj size: %lld\n", (uint64_t)fd->msglen);
722
723    /* object */
724    db_unescape_object(jcr, jcr->db, 
725                       row[8],                /* Object  */
726                       str_to_uint64(row[1]), /* Object length */
727                       &fd->msg, &fd->msglen);
728    fd->send();                           /* send object */
729    octx->count++;
730
731    if (debug_level) {
732       for (int i=0; i < fd->msglen; i++)
733          if (!fd->msg[i]) 
734             fd->msg[i] = ' ';
735       Dmsg1(000, "Send obj: %s\n", fd->msg);
736    }
737
738    return 0;
739 }
740
741 bool send_restore_objects(JCR *jcr)
742 {
743    POOL_MEM query(PM_MESSAGE);
744    BSOCK *fd;
745    OBJ_CTX octx;
746
747    if (!jcr->JobIds || !jcr->JobIds[0]) {
748       return true;
749    }
750    octx.jcr = jcr;
751    octx.count = 0;
752    Mmsg(query, "SELECT JobId,ObjectLength,ObjectFullLength,ObjectIndex,"
753                       "ObjectType,ObjectCompression,FileIndex,ObjectName,"
754                       "RestoreObject "
755                "FROM RestoreObject "
756               "WHERE JobId IN (%s) "
757               "ORDER BY ObjectIndex ASC", jcr->JobIds);
758    
759    /* restore_object_handler is called for each file found */
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          jcr->cached_attribute = true;
849
850          Dmsg2(dbglvl, "dird<filed: stream=%d %s\n", stream, jcr->fname);
851          Dmsg1(dbglvl, "dird<filed: attr=%s\n", ar->attr);
852          jcr->FileId = ar->FileId;
853       /*
854        * First, get STREAM_UNIX_ATTRIBUTES and fill ATTR_DBR structure
855        * Next, we CAN have a CRYPTO_DIGEST, so we fill ATTR_DBR with it (or not)
856        * When we get a new STREAM_UNIX_ATTRIBUTES, we known that we can add file to the catalog
857        * At the end, we have to add the last file
858        */
859       } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
860          if (jcr->FileIndex != (uint32_t)file_index) {
861             Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"),
862                stream_to_ascii(stream), file_index, jcr->FileIndex);
863             continue;
864          }
865          ar->Digest = digest;
866          ar->DigestType = crypto_digest_stream_type(stream);
867          db_escape_string(jcr, jcr->db, digest, Digest, strlen(Digest));
868          Dmsg4(dbglvl, "stream=%d DigestLen=%d Digest=%s type=%d\n", stream,
869                strlen(digest), digest, ar->DigestType);
870       }
871       jcr->jr.JobFiles = jcr->JobFiles = file_index;
872       jcr->jr.LastIndex = file_index;
873    }
874    if (is_bnet_error(fd)) {
875       Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
876             fd->bstrerror());
877       return 0;
878    }
879    if (jcr->cached_attribute) {
880       Dmsg3(dbglvl, "Cached attr with digest. Stream=%d fname=%s attr=%s\n", ar->Stream,            
881          ar->fname, ar->attr);
882       if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
883          Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
884       }
885       jcr->cached_attribute = false; 
886    }
887    jcr->setJobStatus(JS_Terminated);
888    return 1;
889 }