]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/fd_cmds.c
- Add more jcr methods and make mutex and use_count private.
[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=%d 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 runbefore[]   = "RunBeforeJob %s\n";
40 static char runafter[]    = "RunAfterJob %s\n";
41
42
43 /* Responses received from File daemon */
44 static char OKinc[]       = "2000 OK include\n";
45 static char OKjob[]       = "2000 OK Job";
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 bool 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 true;
389
390 bail_out:
391    set_jcr_job_status(jcr, JS_ErrorTerminated);
392    return false;
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 socket given (FD or SD).
425  *  This is used for restore, verify VolumeToCatalog, and
426  *  for migration.
427  */
428 bool send_bootstrap_file(JCR *jcr, BSOCK *sock)
429 {
430    FILE *bs;
431    char buf[1000];
432    const char *bootstrap = "bootstrap\n";
433
434    Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
435    if (!jcr->RestoreBootstrap) {
436       return true;
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 false;
445    }
446    bnet_fsend(sock, bootstrap);
447    while (fgets(buf, sizeof(buf), bs)) {
448       bnet_fsend(sock, "%s", buf);
449    }
450    bnet_sig(sock, BNET_EOD);
451    fclose(bs);
452    if (jcr->unlink_bsr) {
453       unlink(jcr->RestoreBootstrap);
454       jcr->unlink_bsr = false;
455    }                         
456    return true;
457 }
458
459 /*
460  * Send ClientRunBeforeJob and ClientRunAfterJob to File daemon
461  */
462 int send_run_before_and_after_commands(JCR *jcr)
463 {
464    POOLMEM *msg = get_pool_memory(PM_FNAME);
465    BSOCK *fd = jcr->file_bsock;
466    if (jcr->job->ClientRunBeforeJob) {
467       pm_strcpy(msg, jcr->job->ClientRunBeforeJob);
468       bash_spaces(msg);
469       bnet_fsend(fd, runbefore, msg);
470       if (!response(jcr, fd, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR)) {
471          set_jcr_job_status(jcr, JS_ErrorTerminated);
472          free_pool_memory(msg);
473          return 0;
474       }
475    }
476    if (jcr->job->ClientRunAfterJob) {
477       fd->msglen = pm_strcpy(msg, jcr->job->ClientRunAfterJob);
478       bash_spaces(msg);
479       bnet_fsend(fd, runafter, msg);
480       if (!response(jcr, fd, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR)) {
481          set_jcr_job_status(jcr, JS_ErrorTerminated);
482          free_pool_memory(msg);
483          return 0;
484       }
485    }
486    free_pool_memory(msg);
487    return 1;
488 }
489
490
491 /*
492  * Read the attributes from the File daemon for
493  * a Verify job and store them in the catalog.
494  */
495 int get_attributes_and_put_in_catalog(JCR *jcr)
496 {
497    BSOCK   *fd;
498    int n = 0;
499    ATTR_DBR ar;
500
501    fd = jcr->file_bsock;
502    jcr->jr.FirstIndex = 1;
503    memset(&ar, 0, sizeof(ar));
504    jcr->FileIndex = 0;
505
506    Dmsg0(120, "bdird: waiting to receive file attributes\n");
507    /* Pickup file attributes and digest */
508    while (!fd->errors && (n = bget_dirmsg(fd)) > 0) {
509
510    /*****FIXME****** improve error handling to stop only on
511     * really fatal problems, or the number of errors is too
512     * large.
513     */
514       long file_index;
515       int stream, len;
516       char *attr, *p, *fn;
517       char Opts_Digest[MAXSTRING];      /* either Verify opts or MD5/SHA1 digest */
518       char digest[CRYPTO_DIGEST_MAX_SIZE];
519
520       jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
521       if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Opts_Digest)) != 3) {
522          Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
523 "msglen=%d msg=%s\n"), len, fd->msglen, fd->msg);
524          set_jcr_job_status(jcr, JS_ErrorTerminated);
525          return 0;
526       }
527       p = fd->msg;
528       skip_nonspaces(&p);             /* skip FileIndex */
529       skip_spaces(&p);
530       skip_nonspaces(&p);             /* skip Stream */
531       skip_spaces(&p);
532       skip_nonspaces(&p);             /* skip Opts_SHA1 */
533       p++;                            /* skip space */
534       fn = jcr->fname;
535       while (*p != 0) {
536          *fn++ = *p++;                /* copy filename */
537       }
538       *fn = *p++;                     /* term filename and point to attribs */
539       attr = p;
540
541       if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
542          jcr->JobFiles++;
543          jcr->FileIndex = file_index;
544          ar.attr = attr;
545          ar.fname = jcr->fname;
546          ar.FileIndex = file_index;
547          ar.Stream = stream;
548          ar.link = NULL;
549          ar.JobId = jcr->JobId;
550          ar.ClientId = jcr->ClientId;
551          ar.PathId = 0;
552          ar.FilenameId = 0;
553          ar.Digest = NULL;
554          ar.DigestType = CRYPTO_DIGEST_NONE;
555
556          Dmsg2(111, "dird<filed: stream=%d %s\n", stream, jcr->fname);
557          Dmsg1(120, "dird<filed: attr=%s\n", attr);
558
559          if (!db_create_file_attributes_record(jcr, jcr->db, &ar)) {
560             Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
561             set_jcr_job_status(jcr, JS_Error);
562             continue;
563          }
564          jcr->FileId = ar.FileId;
565       } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
566          if (jcr->FileIndex != (uint32_t)file_index) {
567             Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"),
568                stream_to_ascii(stream), file_index, jcr->FileIndex);
569             set_jcr_job_status(jcr, JS_Error);
570             continue;
571          }
572          db_escape_string(digest, Opts_Digest, strlen(Opts_Digest));
573          Dmsg2(120, "DigestLen=%d Digest=%s\n", strlen(digest), digest);
574          if (!db_add_digest_to_file_record(jcr, jcr->db, jcr->FileId, digest,
575                    crypto_digest_stream_type(stream))) {
576             Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
577             set_jcr_job_status(jcr, JS_Error);
578          }
579       }
580       jcr->jr.JobFiles = jcr->JobFiles = file_index;
581       jcr->jr.LastIndex = file_index;
582    }
583    if (is_bnet_error(fd)) {
584       Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
585                         bnet_strerror(fd));
586       set_jcr_job_status(jcr, JS_ErrorTerminated);
587       return 0;
588    }
589
590    set_jcr_job_status(jcr, JS_Terminated);
591    return 1;
592 }