]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/fd_cmds.c
- Get VSS build scripts working with Thorsten's help.
[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    unlink(jcr->RestoreBootstrap);
453    if (!response(jcr, fd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) {
454       set_jcr_job_status(jcr, JS_ErrorTerminated);
455       return 0;
456    }
457    return 1;
458 }
459
460 /*
461  * Send ClientRunBeforeJob and ClientRunAfterJob to File daemon
462  */
463 int send_run_before_and_after_commands(JCR *jcr)
464 {
465    POOLMEM *msg = get_pool_memory(PM_FNAME);
466    BSOCK *fd = jcr->file_bsock;
467    if (jcr->job->ClientRunBeforeJob) {
468       pm_strcpy(msg, jcr->job->ClientRunBeforeJob);
469       bash_spaces(msg);
470       bnet_fsend(fd, runbefore, msg);
471       if (!response(jcr, fd, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR)) {
472          set_jcr_job_status(jcr, JS_ErrorTerminated);
473          free_pool_memory(msg);
474          return 0;
475       }
476    }
477    if (jcr->job->ClientRunAfterJob) {
478       fd->msglen = pm_strcpy(msg, jcr->job->ClientRunAfterJob);
479       bash_spaces(msg);
480       bnet_fsend(fd, runafter, msg);
481       if (!response(jcr, fd, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR)) {
482          set_jcr_job_status(jcr, JS_ErrorTerminated);
483          free_pool_memory(msg);
484          return 0;
485       }
486    }
487    free_pool_memory(msg);
488    return 1;
489 }
490
491
492 /*
493  * Read the attributes from the File daemon for
494  * a Verify job and store them in the catalog.
495  */
496 int get_attributes_and_put_in_catalog(JCR *jcr)
497 {
498    BSOCK   *fd;
499    int n = 0;
500    ATTR_DBR ar;
501
502    fd = jcr->file_bsock;
503    jcr->jr.FirstIndex = 1;
504    memset(&ar, 0, sizeof(ar));
505    jcr->FileIndex = 0;
506
507    Dmsg0(120, "bdird: waiting to receive file attributes\n");
508    /* Pickup file attributes and signature */
509    while (!fd->errors && (n = bget_dirmsg(fd)) > 0) {
510
511    /*****FIXME****** improve error handling to stop only on
512     * really fatal problems, or the number of errors is too
513     * large.
514     */
515       long file_index;
516       int stream, len;
517       char *attr, *p, *fn;
518       char Opts_SIG[MAXSTRING];      /* either Verify opts or MD5/SHA1 signature */
519       char SIG[MAXSTRING];
520
521       jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
522       if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Opts_SIG)) != 3) {
523          Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
524 "msglen=%d msg=%s\n"), len, fd->msglen, fd->msg);
525          set_jcr_job_status(jcr, JS_ErrorTerminated);
526          return 0;
527       }
528       p = fd->msg;
529       skip_nonspaces(&p);             /* skip FileIndex */
530       skip_spaces(&p);
531       skip_nonspaces(&p);             /* skip Stream */
532       skip_spaces(&p);
533       skip_nonspaces(&p);             /* skip Opts_SHA1 */
534       p++;                            /* skip space */
535       fn = jcr->fname;
536       while (*p != 0) {
537          *fn++ = *p++;                /* copy filename */
538       }
539       *fn = *p++;                     /* term filename and point to attribs */
540       attr = p;
541
542       if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
543          jcr->JobFiles++;
544          jcr->FileIndex = file_index;
545          ar.attr = attr;
546          ar.fname = jcr->fname;
547          ar.FileIndex = file_index;
548          ar.Stream = stream;
549          ar.link = NULL;
550          ar.JobId = jcr->JobId;
551          ar.ClientId = jcr->ClientId;
552          ar.PathId = 0;
553          ar.FilenameId = 0;
554          ar.Sig = NULL;
555          ar.SigType = 0;
556
557          Dmsg2(111, "dird<filed: stream=%d %s\n", stream, jcr->fname);
558          Dmsg1(120, "dird<filed: attr=%s\n", attr);
559
560          if (!db_create_file_attributes_record(jcr, jcr->db, &ar)) {
561             Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
562             set_jcr_job_status(jcr, JS_Error);
563             continue;
564          }
565          jcr->FileId = ar.FileId;
566       } else if (stream == STREAM_MD5_SIGNATURE || stream == STREAM_SHA1_SIGNATURE) {
567          if (jcr->FileIndex != (uint32_t)file_index) {
568             Jmsg2(jcr, M_ERROR, 0, _("MD5/SHA1 index %d not same as attributes %d\n"),
569                file_index, jcr->FileIndex);
570             set_jcr_job_status(jcr, JS_Error);
571             continue;
572          }
573          db_escape_string(SIG, Opts_SIG, strlen(Opts_SIG));
574          Dmsg2(120, "SIGlen=%d SIG=%s\n", strlen(SIG), SIG);
575          if (!db_add_SIG_to_file_record(jcr, jcr->db, jcr->FileId, SIG,
576                    stream==STREAM_MD5_SIGNATURE?MD5_SIG:SHA1_SIG)) {
577             Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
578             set_jcr_job_status(jcr, JS_Error);
579          }
580       }
581       jcr->jr.JobFiles = jcr->JobFiles = file_index;
582       jcr->jr.LastIndex = file_index;
583    }
584    if (is_bnet_error(fd)) {
585       Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
586                         bnet_strerror(fd));
587       set_jcr_job_status(jcr, JS_ErrorTerminated);
588       return 0;
589    }
590
591    set_jcr_job_status(jcr, JS_Terminated);
592    return 1;
593 }