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