]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/fd_cmds.c
- Move Python variables from Job to Bacula. They are
[bacula/bacula] / bacula / src / dird / fd_cmds.c
1 /*
2  *
3  *   Bacula Director -- fd_cmds.c -- send commands to File daemon
4  *
5  *     Kern Sibbald, October MM
6  *
7  *    This routine is run as a separate thread.  There may be more
8  *    work to be done to make it totally reentrant!!!!
9  *
10  *  Utility functions for sending info to File Daemon.
11  *   These functions are used by both backup and verify.
12  *
13  *   Version $Id$
14  */
15 /*
16    Copyright (C) 2000-2005 Kern Sibbald
17
18    This program is free software; you can redistribute it and/or
19    modify it under the terms of the GNU General Public License
20    version 2 as amended with additional clauses defined in the
21    file LICENSE in the main source directory.
22
23    This program is distributed in the hope that it will be useful,
24    but WITHOUT ANY WARRANTY; without even the implied warranty of
25    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
26    the file LICENSE for additional details.
27
28  */
29
30 #include "bacula.h"
31 #include "dird.h"
32
33 /* Commands sent to File daemon */
34 static char filesetcmd[]  = "fileset%s\n"; /* set full fileset */
35 static char jobcmd[]      = "JobId=%d Job=%s SDid=%u SDtime=%u Authorization=%s\n";
36 /* Note, mtime_only is not used here -- implemented as file option */
37 static char levelcmd[]    = "level = %s%s mtime_only=%d\n";
38 static char runbefore[]   = "RunBeforeJob %s\n";
39 static char runafter[]    = "RunAfterJob %s\n";
40
41
42 /* Responses received from File daemon */
43 static char OKinc[]       = "2000 OK include\n";
44 static char OKjob[]       = "2000 OK Job";
45 static char OKbootstrap[] = "2000 OK bootstrap\n";
46 static char OKlevel[]     = "2000 OK level\n";
47 static char OKRunBefore[] = "2000 OK RunBefore\n";
48 static char OKRunAfter[]  = "2000 OK RunAfter\n";
49
50 /* Forward referenced functions */
51
52 /* External functions */
53 extern int debug_level;
54 extern DIRRES *director;
55 extern int FDConnectTimeout;
56
57 #define INC_LIST 0
58 #define EXC_LIST 1
59
60 /*
61  * Open connection with File daemon.
62  * Try connecting every retry_interval (default 10 sec), and
63  *   give up after max_retry_time (default 30 mins).
64  */
65
66 int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time,
67                            int verbose)
68 {
69    BSOCK   *fd;
70
71    if (!jcr->file_bsock) {
72       fd = bnet_connect(jcr, retry_interval, max_retry_time,
73            _("File daemon"), jcr->client->address,
74            NULL, jcr->client->FDport, verbose);
75       if (fd == NULL) {
76          set_jcr_job_status(jcr, JS_ErrorTerminated);
77          return 0;
78       }
79       Dmsg0(10, "Opened connection with File daemon\n");
80    } else {
81       fd = jcr->file_bsock;           /* use existing connection */
82    }
83    fd->res = (RES *)jcr->client;      /* save resource in BSOCK */
84    jcr->file_bsock = fd;
85    set_jcr_job_status(jcr, JS_Running);
86
87    if (!authenticate_file_daemon(jcr)) {
88       set_jcr_job_status(jcr, JS_ErrorTerminated);
89       return 0;
90    }
91
92    /*
93     * Now send JobId and authorization key
94     */
95    bnet_fsend(fd, jobcmd, jcr->JobId, jcr->Job, jcr->VolSessionId,
96       jcr->VolSessionTime, jcr->sd_auth_key);
97    if (strcmp(jcr->sd_auth_key, "dummy") != 0) {
98       memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
99    }
100    Dmsg1(100, ">filed: %s", fd->msg);
101    if (bget_dirmsg(fd) > 0) {
102        Dmsg1(110, "<filed: %s", fd->msg);
103        if (strncmp(fd->msg, OKjob, strlen(OKjob)) != 0) {
104           Jmsg(jcr, M_FATAL, 0, _("File daemon \"%s\" rejected Job command: %s\n"),
105              jcr->client->hdr.name, fd->msg);
106           set_jcr_job_status(jcr, JS_ErrorTerminated);
107           return 0;
108        } else if (jcr->db) {
109           CLIENT_DBR cr;
110           memset(&cr, 0, sizeof(cr));
111           bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
112           cr.AutoPrune = jcr->client->AutoPrune;
113           cr.FileRetention = jcr->client->FileRetention;
114           cr.JobRetention = jcr->client->JobRetention;
115           bstrncpy(cr.Uname, fd->msg+strlen(OKjob)+1, sizeof(cr.Uname));
116           if (!db_update_client_record(jcr, jcr->db, &cr)) {
117              Jmsg(jcr, M_WARNING, 0, _("Error updating Client record. ERR=%s\n"),
118                 db_strerror(jcr->db));
119           }
120        }
121    } else {
122       Jmsg(jcr, M_FATAL, 0, _("FD gave bad response to JobId command: %s\n"),
123          bnet_strerror(fd));
124       set_jcr_job_status(jcr, JS_ErrorTerminated);
125       return 0;
126    }
127    return 1;
128 }
129
130 /*
131  * This subroutine edits the last job start time into a
132  *   "since=date/time" buffer that is returned in the
133  *   variable since.  This is used for display purposes in
134  *   the job report.  The time in jcr->stime is later
135  *   passed to tell the File daemon what to do.
136  */
137 void get_level_since_time(JCR *jcr, char *since, int since_len)
138 {
139    int JobLevel;
140
141    since[0] = 0;
142    if (jcr->cloned) {
143       if ( jcr->stime && jcr->stime[0]) {
144          bstrncpy(since, ", since=", since_len);
145          bstrncat(since, jcr->stime, since_len);
146       }
147       return;
148    }
149    if (!jcr->stime) {
150       jcr->stime = get_pool_memory(PM_MESSAGE);
151    } 
152    jcr->stime[0] = 0;
153    /* Lookup the last FULL backup job to get the time/date for a
154     * differential or incremental save.
155     */
156    switch (jcr->JobLevel) {
157    case L_DIFFERENTIAL:
158    case L_INCREMENTAL:
159       /* Look up start time of last job */
160       jcr->jr.JobId = 0;     /* flag for db_find_job_start time */
161       if (!db_find_job_start_time(jcr, jcr->db, &jcr->jr, &jcr->stime)) {
162          /* No job found, so upgrade this one to Full */
163          Jmsg(jcr, M_INFO, 0, "%s", db_strerror(jcr->db));
164          Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full backup found. Doing FULL backup.\n"));
165          bsnprintf(since, since_len, " (upgraded from %s)",
166             level_to_str(jcr->JobLevel));
167          jcr->JobLevel = jcr->jr.JobLevel = L_FULL;
168       } else {
169          if (jcr->job->rerun_failed_levels) {
170             if (db_find_failed_job_since(jcr, jcr->db, &jcr->jr, jcr->stime, JobLevel)) {
171                Jmsg(jcr, M_INFO, 0, _("Prior failed job found. Upgrading to %s.\n"),
172                   level_to_str(JobLevel));
173                bsnprintf(since, since_len, " (upgraded from %s)",
174                   level_to_str(jcr->JobLevel));
175                jcr->JobLevel = jcr->jr.JobLevel = JobLevel;
176                jcr->jr.JobId = jcr->JobId;
177                break;
178             }
179          }
180          bstrncpy(since, ", since=", since_len);
181          bstrncat(since, jcr->stime, since_len);
182       }
183       jcr->jr.JobId = jcr->JobId;
184       break;
185    }
186    Dmsg2(100, "Level=%c last start time=%s\n", jcr->JobLevel, jcr->stime);
187 }
188
189 static void send_since_time(JCR *jcr)
190 {
191    BSOCK   *fd = jcr->file_bsock;
192    utime_t stime;
193    char ed1[50];
194
195    stime = str_to_utime(jcr->stime);
196    bnet_fsend(fd, levelcmd, "since_utime ", edit_uint64(stime, ed1), 0);
197    while (bget_dirmsg(fd) >= 0) {  /* allow him to poll us to sync clocks */
198       Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
199    }
200 }
201
202
203 /*
204  * Send level command to FD.
205  * Used for backup jobs and estimate command.
206  */
207 bool send_level_command(JCR *jcr)
208 {
209    BSOCK   *fd = jcr->file_bsock;
210    /*
211     * Send Level command to File daemon
212     */
213    switch (jcr->JobLevel) {
214    case L_BASE:
215       bnet_fsend(fd, levelcmd, "base", " ", 0);
216       break;
217    /* L_NONE is the console, sending something off to the FD */
218    case L_NONE:
219    case L_FULL:
220       bnet_fsend(fd, levelcmd, "full", " ", 0);
221       break;
222    case L_DIFFERENTIAL:
223       bnet_fsend(fd, levelcmd, "differential", " ", 0);
224       send_since_time(jcr);
225       break;
226    case L_INCREMENTAL:
227       bnet_fsend(fd, levelcmd, "incremental", " ", 0);
228       send_since_time(jcr);
229       break;
230    case L_SINCE:
231    default:
232       Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"),
233          jcr->JobLevel, jcr->JobLevel);
234       return 0;
235    }
236    Dmsg1(120, ">filed: %s", fd->msg);
237    if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) {
238       return 0;
239    }
240    return 1;
241 }
242
243 /*
244  * Send either an Included or an Excluded list to FD
245  */
246 static int send_fileset(JCR *jcr)
247 {
248    FILESET *fileset = jcr->fileset;
249    BSOCK   *fd = jcr->file_bsock;
250    int num;
251    bool include = true;
252
253    for ( ;; ) {
254       if (include) {
255          num = fileset->num_includes;
256       } else {
257          num = fileset->num_excludes;
258       }
259       for (int i=0; i<num; i++) {
260          BPIPE *bpipe;
261          FILE *ffd;
262          char buf[2000];
263          char *p;
264          int optlen, stat;
265          INCEXE *ie;
266          int j, k;
267
268          if (include) {
269             ie = fileset->include_items[i];
270             bnet_fsend(fd, "I\n");
271          } else {
272             ie = fileset->exclude_items[i];
273             bnet_fsend(fd, "E\n");
274          }
275          for (j=0; j<ie->num_opts; j++) {
276             FOPTS *fo = ie->opts_list[j];
277             bnet_fsend(fd, "O %s\n", fo->opts);
278             for (k=0; k<fo->regex.size(); k++) {
279                bnet_fsend(fd, "R %s\n", fo->regex.get(k));
280             }
281             for (k=0; k<fo->regexdir.size(); k++) {
282                bnet_fsend(fd, "RD %s\n", fo->regexdir.get(k));
283             }
284             for (k=0; k<fo->regexfile.size(); k++) {
285                bnet_fsend(fd, "RF %s\n", fo->regexfile.get(k));
286             }
287             for (k=0; k<fo->wild.size(); k++) {
288                bnet_fsend(fd, "W %s\n", fo->wild.get(k));
289             }
290             for (k=0; k<fo->wilddir.size(); k++) {
291                bnet_fsend(fd, "WD %s\n", fo->wilddir.get(k));
292             }
293             for (k=0; k<fo->wildfile.size(); k++) {
294                bnet_fsend(fd, "WF %s\n", fo->wildfile.get(k));
295             }
296             for (k=0; k<fo->base.size(); k++) {
297                bnet_fsend(fd, "B %s\n", fo->base.get(k));
298             }
299             for (k=0; k<fo->fstype.size(); k++) {
300                bnet_fsend(fd, "X %s\n", fo->fstype.get(k));
301             }
302             if (fo->reader) {
303                bnet_fsend(fd, "D %s\n", fo->reader);
304             }
305             if (fo->writer) {
306                bnet_fsend(fd, "T %s\n", fo->writer);
307             }
308             bnet_fsend(fd, "N\n");
309          }
310
311          for (j=0; j<ie->name_list.size(); j++) {
312             p = (char *)ie->name_list.get(j);
313             switch (*p) {
314             case '|':
315                p++;                      /* skip over the | */
316                fd->msg = edit_job_codes(jcr, fd->msg, p, "");
317                bpipe = open_bpipe(fd->msg, 0, "r");
318                if (!bpipe) {
319                   berrno be;
320                   Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
321                      p, be.strerror());
322                   goto bail_out;
323                }
324                bstrncpy(buf, "F ", sizeof(buf));
325                Dmsg1(500, "Opts=%s\n", buf);
326                optlen = strlen(buf);
327                while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) {
328                   fd->msglen = Mmsg(fd->msg, "%s", buf);
329                   Dmsg2(500, "Inc/exc len=%d: %s", fd->msglen, fd->msg);
330                   if (!bnet_send(fd)) {
331                      Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
332                      goto bail_out;
333                   }
334                }
335                if ((stat=close_bpipe(bpipe)) != 0) {
336                   berrno be;
337                   Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
338                      p, be.strerror(stat));
339                   goto bail_out;
340                }
341                break;
342             case '<':
343                p++;                      /* skip over < */
344                if ((ffd = fopen(p, "r")) == NULL) {
345                   berrno be;
346                   Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"),
347                      p, be.strerror());
348                   goto bail_out;
349                }
350                bstrncpy(buf, "F ", sizeof(buf));
351                Dmsg1(500, "Opts=%s\n", buf);
352                optlen = strlen(buf);
353                while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) {
354                   fd->msglen = Mmsg(fd->msg, "%s", buf);
355                   if (!bnet_send(fd)) {
356                      Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
357                      goto bail_out;
358                   }
359                }
360                fclose(ffd);
361                break;
362             case '\\':
363                p++;                      /* skip over \ */
364                /* Note, fall through wanted */
365             default:
366                pm_strcpy(fd->msg, "F ");
367                fd->msglen = pm_strcat(fd->msg, p);
368                Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
369                if (!bnet_send(fd)) {
370                   Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
371                   goto bail_out;
372                }
373                break;
374             }
375          }
376          bnet_fsend(fd, "N\n");
377       }
378       if (!include) {                 /* If we just did excludes */
379          break;                       /*   all done */
380       }
381       include = false;                /* Now do excludes */
382    }
383
384    bnet_sig(fd, BNET_EOD);            /* end of data */
385    if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) {
386       goto bail_out;
387    }
388    return 1;
389
390 bail_out:
391    set_jcr_job_status(jcr, JS_ErrorTerminated);
392    return 0;
393
394 }
395
396
397 /*
398  * Send include list to File daemon
399  */
400 bool send_include_list(JCR *jcr)
401 {
402    BSOCK *fd = jcr->file_bsock;
403    if (jcr->fileset->new_include) {
404       bnet_fsend(fd, filesetcmd, jcr->fileset->enable_vss ? " vss=1" : "");
405       return send_fileset(jcr);
406    }
407    return true;
408 }
409
410
411 /*
412  * Send exclude list to File daemon
413  *   Under the new scheme, the Exclude list
414  *   is part of the FileSet sent with the
415  *   "include_list" above.
416  */
417 bool send_exclude_list(JCR *jcr)
418 {
419    return true;
420 }
421
422
423 /*
424  * Send bootstrap file if any to the File daemon.
425  *  This is used for restore and verify VolumeToCatalog
426  */
427 bool send_bootstrap_file(JCR *jcr)
428 {
429    FILE *bs;
430    char buf[1000];
431    BSOCK *fd = jcr->file_bsock;
432    const char *bootstrap = "bootstrap\n";
433
434    Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
435    if (!jcr->RestoreBootstrap) {
436       return 1;
437    }
438    bs = fopen(jcr->RestoreBootstrap, "r");
439    if (!bs) {
440       berrno be;
441       Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"),
442          jcr->RestoreBootstrap, be.strerror());
443       set_jcr_job_status(jcr, JS_ErrorTerminated);
444       return 0;
445    }
446    bnet_fsend(fd, bootstrap);
447    while (fgets(buf, sizeof(buf), bs)) {
448       bnet_fsend(fd, "%s", buf);
449    }
450    bnet_sig(fd, BNET_EOD);
451    fclose(bs);
452    if (jcr->unlink_bsr) {
453       unlink(jcr->RestoreBootstrap);
454       jcr->unlink_bsr = false;
455    }                         
456    if (!response(jcr, fd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
457       set_jcr_job_status(jcr, JS_ErrorTerminated);
458       return 0;
459    }
460    return 1;
461 }
462
463 /*
464  * Send ClientRunBeforeJob and ClientRunAfterJob to File daemon
465  */
466 int send_run_before_and_after_commands(JCR *jcr)
467 {
468    POOLMEM *msg = get_pool_memory(PM_FNAME);
469    BSOCK *fd = jcr->file_bsock;
470    if (jcr->job->ClientRunBeforeJob) {
471       pm_strcpy(msg, jcr->job->ClientRunBeforeJob);
472       bash_spaces(msg);
473       bnet_fsend(fd, runbefore, msg);
474       if (!response(jcr, fd, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR)) {
475          set_jcr_job_status(jcr, JS_ErrorTerminated);
476          free_pool_memory(msg);
477          return 0;
478       }
479    }
480    if (jcr->job->ClientRunAfterJob) {
481       fd->msglen = pm_strcpy(msg, jcr->job->ClientRunAfterJob);
482       bash_spaces(msg);
483       bnet_fsend(fd, runafter, msg);
484       if (!response(jcr, fd, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR)) {
485          set_jcr_job_status(jcr, JS_ErrorTerminated);
486          free_pool_memory(msg);
487          return 0;
488       }
489    }
490    free_pool_memory(msg);
491    return 1;
492 }
493
494
495 /*
496  * Read the attributes from the File daemon for
497  * a Verify job and store them in the catalog.
498  */
499 int get_attributes_and_put_in_catalog(JCR *jcr)
500 {
501    BSOCK   *fd;
502    int n = 0;
503    ATTR_DBR ar;
504
505    fd = jcr->file_bsock;
506    jcr->jr.FirstIndex = 1;
507    memset(&ar, 0, sizeof(ar));
508    jcr->FileIndex = 0;
509
510    Dmsg0(120, "bdird: waiting to receive file attributes\n");
511    /* Pickup file attributes and signature */
512    while (!fd->errors && (n = bget_dirmsg(fd)) > 0) {
513
514    /*****FIXME****** improve error handling to stop only on
515     * really fatal problems, or the number of errors is too
516     * large.
517     */
518       long file_index;
519       int stream, len;
520       char *attr, *p, *fn;
521       char Opts_SIG[MAXSTRING];      /* either Verify opts or MD5/SHA1 signature */
522       char SIG[MAXSTRING];
523
524       jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
525       if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Opts_SIG)) != 3) {
526          Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
527 "msglen=%d msg=%s\n"), len, fd->msglen, fd->msg);
528          set_jcr_job_status(jcr, JS_ErrorTerminated);
529          return 0;
530       }
531       p = fd->msg;
532       skip_nonspaces(&p);             /* skip FileIndex */
533       skip_spaces(&p);
534       skip_nonspaces(&p);             /* skip Stream */
535       skip_spaces(&p);
536       skip_nonspaces(&p);             /* skip Opts_SHA1 */
537       p++;                            /* skip space */
538       fn = jcr->fname;
539       while (*p != 0) {
540          *fn++ = *p++;                /* copy filename */
541       }
542       *fn = *p++;                     /* term filename and point to attribs */
543       attr = p;
544
545       if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
546          jcr->JobFiles++;
547          jcr->FileIndex = file_index;
548          ar.attr = attr;
549          ar.fname = jcr->fname;
550          ar.FileIndex = file_index;
551          ar.Stream = stream;
552          ar.link = NULL;
553          ar.JobId = jcr->JobId;
554          ar.ClientId = jcr->ClientId;
555          ar.PathId = 0;
556          ar.FilenameId = 0;
557          ar.Sig = NULL;
558          ar.SigType = 0;
559
560          Dmsg2(111, "dird<filed: stream=%d %s\n", stream, jcr->fname);
561          Dmsg1(120, "dird<filed: attr=%s\n", attr);
562
563          if (!db_create_file_attributes_record(jcr, jcr->db, &ar)) {
564             Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
565             set_jcr_job_status(jcr, JS_Error);
566             continue;
567          }
568          jcr->FileId = ar.FileId;
569       } else if (stream == STREAM_MD5_SIGNATURE || stream == STREAM_SHA1_SIGNATURE) {
570          if (jcr->FileIndex != (uint32_t)file_index) {
571             Jmsg2(jcr, M_ERROR, 0, _("MD5/SHA1 index %d not same as attributes %d\n"),
572                file_index, jcr->FileIndex);
573             set_jcr_job_status(jcr, JS_Error);
574             continue;
575          }
576          db_escape_string(SIG, Opts_SIG, strlen(Opts_SIG));
577          Dmsg2(120, "SIGlen=%d SIG=%s\n", strlen(SIG), SIG);
578          if (!db_add_SIG_to_file_record(jcr, jcr->db, jcr->FileId, SIG,
579                    stream==STREAM_MD5_SIGNATURE?MD5_SIG:SHA1_SIG)) {
580             Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
581             set_jcr_job_status(jcr, JS_Error);
582          }
583       }
584       jcr->jr.JobFiles = jcr->JobFiles = file_index;
585       jcr->jr.LastIndex = file_index;
586    }
587    if (is_bnet_error(fd)) {
588       Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
589                         bnet_strerror(fd));
590       set_jcr_job_status(jcr, JS_ErrorTerminated);
591       return 0;
592    }
593
594    set_jcr_job_status(jcr, JS_Terminated);
595    return 1;
596 }