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