2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- fd_cmds.c -- send commands to File daemon
32 * Kern Sibbald, October MM
34 * This routine is run as a separate thread. There may be more
35 * work to be done to make it totally reentrant!!!!
37 * Utility functions for sending info to File Daemon.
38 * These functions are used by both backup and verify.
45 #include "findlib/find.h"
47 const int dbglvl = 400;
49 /* Commands sent to File daemon */
50 static char filesetcmd[] = "fileset%s\n"; /* set full fileset */
51 static char jobcmd[] = "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n";
52 /* Note, mtime_only is not used here -- implemented as file option */
53 static char levelcmd[] = "level = %s%s%s mtime_only=%d\n";
54 static char runscript[] = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
55 static char runbeforenow[]= "RunBeforeNow\n";
57 /* Responses received from File daemon */
58 static char OKinc[] = "2000 OK include\n";
59 static char OKjob[] = "2000 OK Job";
60 static char OKlevel[] = "2000 OK level\n";
61 static char OKRunScript[] = "2000 OK RunScript\n";
62 static char OKRunBeforeNow[] = "2000 OK RunBeforeNow\n";
64 /* Forward referenced functions */
65 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd);
67 /* External functions */
68 extern DIRRES *director;
69 extern int FDConnectTimeout;
75 * Open connection with File daemon.
76 * Try connecting every retry_interval (default 10 sec), and
77 * give up after max_retry_time (default 30 mins).
80 int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time,
87 if (jcr->client->heartbeat_interval) {
88 heart_beat = jcr->client->heartbeat_interval;
90 heart_beat = director->heartbeat_interval;
93 if (!jcr->file_bsock) {
94 char name[MAX_NAME_LENGTH + 100];
95 bstrncpy(name, _("Client: "), sizeof(name));
96 bstrncat(name, jcr->client->name(), sizeof(name));
97 fd = bnet_connect(jcr, retry_interval, max_retry_time, heart_beat,
98 name, jcr->client->address, NULL, jcr->client->FDport, verbose);
100 set_jcr_job_status(jcr, JS_ErrorTerminated);
103 Dmsg0(10, "Opened connection with File daemon\n");
105 fd = jcr->file_bsock; /* use existing connection */
107 fd->res = (RES *)jcr->client; /* save resource in BSOCK */
108 jcr->file_bsock = fd;
109 set_jcr_job_status(jcr, JS_Running);
111 if (!authenticate_file_daemon(jcr)) {
112 set_jcr_job_status(jcr, JS_ErrorTerminated);
117 * Now send JobId and authorization key
119 fd->fsend(jobcmd, edit_int64(jcr->JobId, ed1), jcr->Job, jcr->VolSessionId,
120 jcr->VolSessionTime, jcr->sd_auth_key);
121 if (strcmp(jcr->sd_auth_key, "dummy") != 0) {
122 memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
124 Dmsg1(100, ">filed: %s", fd->msg);
125 if (bget_dirmsg(fd) > 0) {
126 Dmsg1(110, "<filed: %s", fd->msg);
127 if (strncmp(fd->msg, OKjob, strlen(OKjob)) != 0) {
128 Jmsg(jcr, M_FATAL, 0, _("File daemon \"%s\" rejected Job command: %s\n"),
129 jcr->client->hdr.name, fd->msg);
130 set_jcr_job_status(jcr, JS_ErrorTerminated);
132 } else if (jcr->db) {
134 memset(&cr, 0, sizeof(cr));
135 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
136 cr.AutoPrune = jcr->client->AutoPrune;
137 cr.FileRetention = jcr->client->FileRetention;
138 cr.JobRetention = jcr->client->JobRetention;
139 bstrncpy(cr.Uname, fd->msg+strlen(OKjob)+1, sizeof(cr.Uname));
140 if (!db_update_client_record(jcr, jcr->db, &cr)) {
141 Jmsg(jcr, M_WARNING, 0, _("Error updating Client record. ERR=%s\n"),
142 db_strerror(jcr->db));
146 Jmsg(jcr, M_FATAL, 0, _("FD gave bad response to JobId command: %s\n"),
148 set_jcr_job_status(jcr, JS_ErrorTerminated);
155 * This subroutine edits the last job start time into a
156 * "since=date/time" buffer that is returned in the
157 * variable since. This is used for display purposes in
158 * the job report. The time in jcr->stime is later
159 * passed to tell the File daemon what to do.
161 void get_level_since_time(JCR *jcr, char *since, int since_len)
165 utime_t now, LastFull;
168 /* If job cloned and a since time already given, use it */
169 if (jcr->cloned && jcr->stime && jcr->stime[0]) {
170 bstrncpy(since, _(", since="), since_len);
171 bstrncat(since, jcr->stime, since_len);
174 /* Make sure stime buffer is allocated */
176 jcr->stime = get_pool_memory(PM_MESSAGE);
179 /* Lookup the last FULL backup job to get the time/date for a
180 * differential or incremental save.
182 switch (jcr->JobLevel) {
185 /* Look up start time of last job */
186 jcr->jr.JobId = 0; /* flag for db_find_job_start time */
187 FullOk = db_find_job_start_time(jcr, jcr->db, &jcr->jr, &jcr->stime);
188 /* If there was a successful job, make sure it is recent enough */
189 if (FullOk && jcr->job->MaxFullAge > 0) {
190 now = btime_to_utime(get_current_btime());
191 LastFull = str_to_utime(jcr->stime);
192 FullOk = ((now - LastFull) < jcr->job->MaxFullAge);
195 /* No recent job found, so upgrade this one to Full */
196 Jmsg(jcr, M_INFO, 0, "%s", db_strerror(jcr->db));
197 Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full backup found in catalog. Doing FULL backup.\n"));
198 bsnprintf(since, since_len, _(" (upgraded from %s)"),
199 level_to_str(jcr->JobLevel));
200 jcr->JobLevel = jcr->jr.JobLevel = L_FULL;
202 if (jcr->job->rerun_failed_levels) {
203 if (db_find_failed_job_since(jcr, jcr->db, &jcr->jr, jcr->stime, JobLevel)) {
204 Jmsg(jcr, M_INFO, 0, _("Prior failed job found in catalog. Upgrading to %s.\n"),
205 level_to_str(JobLevel));
206 bsnprintf(since, since_len, _(" (upgraded from %s)"),
207 level_to_str(jcr->JobLevel));
208 jcr->JobLevel = jcr->jr.JobLevel = JobLevel;
209 jcr->jr.JobId = jcr->JobId;
213 bstrncpy(since, _(", since="), since_len);
214 bstrncat(since, jcr->stime, since_len);
216 jcr->jr.JobId = jcr->JobId;
219 Dmsg2(100, "Level=%c last start time=%s\n", jcr->JobLevel, jcr->stime);
222 static void send_since_time(JCR *jcr)
224 BSOCK *fd = jcr->file_bsock;
228 stime = str_to_utime(jcr->stime);
229 fd->fsend(levelcmd, "", NT_("since_utime "), edit_uint64(stime, ed1), 0);
230 while (bget_dirmsg(fd) >= 0) { /* allow him to poll us to sync clocks */
231 Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
236 * Send level command to FD.
237 * Used for backup jobs and estimate command.
239 bool send_level_command(JCR *jcr)
241 BSOCK *fd = jcr->file_bsock;
242 const char *accurate=jcr->job->accurate?"accurate_":"";
243 const char *not_accurate="";
245 * Send Level command to File daemon
247 switch (jcr->JobLevel) {
249 fd->fsend(levelcmd, not_accurate, "base", " ", 0);
251 /* L_NONE is the console, sending something off to the FD */
254 fd->fsend(levelcmd, not_accurate, "full", " ", 0);
257 fd->fsend(levelcmd, accurate, "differential", " ", 0);
258 send_since_time(jcr);
261 fd->fsend(levelcmd, accurate, "incremental", " ", 0);
262 send_since_time(jcr);
266 Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"),
267 jcr->JobLevel, jcr->JobLevel);
270 Dmsg1(120, ">filed: %s", fd->msg);
271 if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) {
278 * Send either an Included or an Excluded list to FD
280 static bool send_fileset(JCR *jcr)
282 FILESET *fileset = jcr->fileset;
283 BSOCK *fd = jcr->file_bsock;
289 num = fileset->num_includes;
291 num = fileset->num_excludes;
293 for (int i=0; i<num; i++) {
299 ie = fileset->include_items[i];
302 ie = fileset->exclude_items[i];
305 for (j=0; j<ie->num_opts; j++) {
306 FOPTS *fo = ie->opts_list[j];
307 fd->fsend("O %s\n", fo->opts);
309 bool enhanced_wild = false;
310 for (k=0; fo->opts[k]!='\0'; k++) {
311 if (fo->opts[k]=='W') {
312 enhanced_wild = true;
317 for (k=0; k<fo->regex.size(); k++) {
318 fd->fsend("R %s\n", fo->regex.get(k));
320 for (k=0; k<fo->regexdir.size(); k++) {
321 fd->fsend("RD %s\n", fo->regexdir.get(k));
323 for (k=0; k<fo->regexfile.size(); k++) {
324 fd->fsend("RF %s\n", fo->regexfile.get(k));
326 for (k=0; k<fo->wild.size(); k++) {
327 fd->fsend("W %s\n", fo->wild.get(k));
329 for (k=0; k<fo->wilddir.size(); k++) {
330 fd->fsend("WD %s\n", fo->wilddir.get(k));
332 for (k=0; k<fo->wildfile.size(); k++) {
333 fd->fsend("WF %s\n", fo->wildfile.get(k));
335 for (k=0; k<fo->wildbase.size(); k++) {
336 fd->fsend("W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
338 for (k=0; k<fo->base.size(); k++) {
339 fd->fsend("B %s\n", fo->base.get(k));
341 for (k=0; k<fo->fstype.size(); k++) {
342 fd->fsend("X %s\n", fo->fstype.get(k));
344 for (k=0; k<fo->drivetype.size(); k++) {
345 fd->fsend("XD %s\n", fo->drivetype.get(k));
348 fd->fsend("G %s\n", fo->plugin);
351 fd->fsend("D %s\n", fo->reader);
354 fd->fsend("T %s\n", fo->writer);
359 for (j=0; j<ie->name_list.size(); j++) {
360 item = (char *)ie->name_list.get(j);
361 if (!send_list_item(jcr, "F ", item, fd)) {
366 for (j=0; j<ie->plugin_list.size(); j++) {
367 item = (char *)ie->plugin_list.get(j);
368 if (!send_list_item(jcr, "P ", item, fd)) {
374 if (!include) { /* If we just did excludes */
375 break; /* all done */
377 include = false; /* Now do excludes */
380 fd->signal(BNET_EOD); /* end of data */
381 if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) {
387 set_jcr_job_status(jcr, JS_ErrorTerminated);
392 static bool send_list_item(JCR *jcr, const char *code, char *item, BSOCK *fd)
402 p++; /* skip over the | */
403 fd->msg = edit_job_codes(jcr, fd->msg, p, "");
404 bpipe = open_bpipe(fd->msg, 0, "r");
407 Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
411 bstrncpy(buf, code, sizeof(buf));
412 Dmsg1(500, "code=%s\n", buf);
413 optlen = strlen(buf);
414 while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) {
415 fd->msglen = Mmsg(fd->msg, "%s", buf);
416 Dmsg2(500, "Inc/exc len=%d: %s", fd->msglen, fd->msg);
417 if (!bnet_send(fd)) {
418 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
422 if ((stat=close_bpipe(bpipe)) != 0) {
424 Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
425 p, be.bstrerror(stat));
430 p++; /* skip over < */
431 if ((ffd = fopen(p, "rb")) == NULL) {
433 Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"),
437 bstrncpy(buf, code, sizeof(buf));
438 Dmsg1(500, "code=%s\n", buf);
439 optlen = strlen(buf);
440 while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) {
441 fd->msglen = Mmsg(fd->msg, "%s", buf);
442 if (!bnet_send(fd)) {
443 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
450 p++; /* skip over \ */
451 /* Note, fall through wanted */
453 pm_strcpy(fd->msg, code);
454 fd->msglen = pm_strcat(fd->msg, p);
455 Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
457 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
467 * Send include list to File daemon
469 bool send_include_list(JCR *jcr)
471 BSOCK *fd = jcr->file_bsock;
472 if (jcr->fileset->new_include) {
473 fd->fsend(filesetcmd, jcr->fileset->enable_vss ? " vss=1" : "");
474 return send_fileset(jcr);
481 * Send exclude list to File daemon
482 * Under the new scheme, the Exclude list
483 * is part of the FileSet sent with the
484 * "include_list" above.
486 bool send_exclude_list(JCR *jcr)
493 * Send bootstrap file if any to the socket given (FD or SD).
494 * This is used for restore, verify VolumeToCatalog, and
497 bool send_bootstrap_file(JCR *jcr, BSOCK *sock)
501 const char *bootstrap = "bootstrap\n";
503 Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
504 if (!jcr->RestoreBootstrap) {
507 bs = fopen(jcr->RestoreBootstrap, "rb");
510 Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"),
511 jcr->RestoreBootstrap, be.bstrerror());
512 set_jcr_job_status(jcr, JS_ErrorTerminated);
515 sock->fsend(bootstrap);
516 while (fgets(buf, sizeof(buf), bs)) {
517 sock->fsend("%s", buf);
519 sock->signal(BNET_EOD);
521 if (jcr->unlink_bsr) {
522 unlink(jcr->RestoreBootstrap);
523 jcr->unlink_bsr = false;
528 /* TODO: drop this with runscript.old_proto in bacula 1.42 */
529 static char runbefore[] = "RunBeforeJob %s\n";
530 static char runafter[] = "RunAfterJob %s\n";
531 static char OKRunBefore[] = "2000 OK RunBefore\n";
532 static char OKRunAfter[] = "2000 OK RunAfter\n";
534 int send_runscript_with_old_proto(JCR *jcr, int when, POOLMEM *msg)
537 Dmsg1(120, "bdird: sending old runcommand to fd '%s'\n",msg);
538 if (when & SCRIPT_Before) {
539 bnet_fsend(jcr->file_bsock, runbefore, msg);
540 ret = response(jcr, jcr->file_bsock, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR);
542 bnet_fsend(jcr->file_bsock, runafter, msg);
543 ret = response(jcr, jcr->file_bsock, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR);
549 * Send RunScripts to File daemon
550 * 1) We send all runscript to FD, they can be executed Before, After, or twice
551 * 2) Then, we send a "RunBeforeNow" command to the FD to tell him to do the
552 * first run_script() call. (ie ClientRunBeforeJob)
554 int send_runscripts_commands(JCR *jcr)
556 POOLMEM *msg = get_pool_memory(PM_FNAME);
557 BSOCK *fd = jcr->file_bsock;
559 bool launch_before_cmd = false;
560 POOLMEM *ehost = get_pool_memory(PM_FNAME);
563 Dmsg0(120, "bdird: sending runscripts to fd\n");
565 foreach_alist(cmd, jcr->job->RunScripts) {
566 if (cmd->can_run_at_level(jcr->JobLevel) && cmd->target) {
567 ehost = edit_job_codes(jcr, ehost, cmd->target, "");
568 Dmsg2(200, "bdird: runscript %s -> %s\n", cmd->target, ehost);
570 if (strcmp(ehost, jcr->client->name()) == 0) {
571 pm_strcpy(msg, cmd->command);
574 Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", cmd->command);
576 /* TODO: remove this with bacula 1.42 */
577 if (cmd->old_proto) {
578 result = send_runscript_with_old_proto(jcr, cmd->when, msg);
581 fd->fsend(runscript, cmd->on_success,
587 result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
588 launch_before_cmd = true;
595 /* TODO : we have to play with other client */
598 send command to an other client
604 /* Tell the FD to execute the ClientRunBeforeJob */
605 if (launch_before_cmd) {
606 fd->fsend(runbeforenow);
607 if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) {
611 free_pool_memory(msg);
612 free_pool_memory(ehost);
616 Jmsg(jcr, M_FATAL, 0, _("Client \"%s\" RunScript failed.\n"), ehost);
617 free_pool_memory(msg);
618 free_pool_memory(ehost);
625 * Read the attributes from the File daemon for
626 * a Verify job and store them in the catalog.
628 int get_attributes_and_put_in_catalog(JCR *jcr)
633 char digest[MAXSTRING];
635 fd = jcr->file_bsock;
636 jcr->jr.FirstIndex = 1;
638 /* Start transaction allocates jcr->attr and jcr->ar if needed */
639 db_start_transaction(jcr, jcr->db); /* start transaction if not already open */
642 Dmsg0(120, "bdird: waiting to receive file attributes\n");
643 /* Pickup file attributes and digest */
644 while (!fd->errors && (n = bget_dirmsg(fd)) > 0) {
648 char Digest[MAXSTRING]; /* either Verify opts or MD5/SHA1 digest */
650 jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
651 if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Digest)) != 3) {
652 Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
653 "msglen=%d msg=%s\n"), len, fd->msglen, fd->msg);
654 set_jcr_job_status(jcr, JS_ErrorTerminated);
658 /* The following three fields were sscanf'ed above so skip them */
659 skip_nonspaces(&p); /* skip FileIndex */
661 skip_nonspaces(&p); /* skip Stream */
663 skip_nonspaces(&p); /* skip Opts_Digest */
664 p++; /* skip space */
665 Dmsg1(dbglvl, "Stream=%d\n", stream);
666 if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
667 if (jcr->cached_attribute) {
668 Dmsg3(dbglvl, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname,
670 if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
671 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
674 /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
677 *fn++ = *p++; /* copy filename */
679 *fn = *p++; /* term filename and point p to attribs */
680 pm_strcpy(jcr->attr, p); /* save attributes */
682 jcr->FileIndex = file_index;
683 ar->attr = jcr->attr;
684 ar->fname = jcr->fname;
685 ar->FileIndex = file_index;
688 ar->JobId = jcr->JobId;
689 ar->ClientId = jcr->ClientId;
693 ar->DigestType = CRYPTO_DIGEST_NONE;
694 jcr->cached_attribute = true;
696 Dmsg2(dbglvl, "dird<filed: stream=%d %s\n", stream, jcr->fname);
697 Dmsg1(dbglvl, "dird<filed: attr=%s\n", ar->attr);
698 jcr->FileId = ar->FileId;
700 * First, get STREAM_UNIX_ATTRIBUTES and fill ATTR_DBR structure
701 * Next, we CAN have a CRYPTO_DIGEST, so we fill ATTR_DBR with it (or not)
702 * When we get a new STREAM_UNIX_ATTRIBUTES, we known that we can add file to the catalog
703 * At the end, we have to add the last file
705 } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
706 if (jcr->FileIndex != (uint32_t)file_index) {
707 Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"),
708 stream_to_ascii(stream), file_index, jcr->FileIndex);
712 ar->DigestType = crypto_digest_stream_type(stream);
713 db_escape_string(jcr, jcr->db, digest, Digest, strlen(Digest));
714 Dmsg4(dbglvl, "stream=%d DigestLen=%d Digest=%s type=%d\n", stream,
715 strlen(digest), digest, ar->DigestType);
717 jcr->jr.JobFiles = jcr->JobFiles = file_index;
718 jcr->jr.LastIndex = file_index;
720 if (is_bnet_error(fd)) {
721 Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
725 if (jcr->cached_attribute) {
726 Dmsg3(dbglvl, "Cached attr with digest. Stream=%d fname=%s attr=%s\n", ar->Stream,
727 ar->fname, ar->attr);
728 if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
729 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
731 jcr->cached_attribute = false;
733 set_jcr_job_status(jcr, JS_Terminated);