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 plus additions
11 that are listed in the file LICENSE.
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 /* Commands sent to File daemon */
48 static char filesetcmd[] = "fileset%s\n"; /* set full fileset */
49 static char jobcmd[] = "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n";
50 /* Note, mtime_only is not used here -- implemented as file option */
51 static char levelcmd[] = "level = %s%s mtime_only=%d\n";
52 static char runscript[] = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n";
53 static char runbeforenow[]= "RunBeforeNow\n";
55 /* Responses received from File daemon */
56 static char OKinc[] = "2000 OK include\n";
57 static char OKjob[] = "2000 OK Job";
58 static char OKlevel[] = "2000 OK level\n";
59 static char OKRunScript[] = "2000 OK RunScript\n";
60 static char OKRunBeforeNow[] = "2000 OK RunBeforeNow\n";
62 /* Forward referenced functions */
64 /* External functions */
65 extern DIRRES *director;
66 extern int FDConnectTimeout;
72 * Open connection with File daemon.
73 * Try connecting every retry_interval (default 10 sec), and
74 * give up after max_retry_time (default 30 mins).
77 int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time,
83 if (!jcr->file_bsock) {
84 fd = bnet_connect(jcr, retry_interval, max_retry_time,
85 _("File daemon"), jcr->client->address,
86 NULL, jcr->client->FDport, verbose);
88 set_jcr_job_status(jcr, JS_ErrorTerminated);
91 Dmsg0(10, "Opened connection with File daemon\n");
93 fd = jcr->file_bsock; /* use existing connection */
95 fd->res = (RES *)jcr->client; /* save resource in BSOCK */
97 set_jcr_job_status(jcr, JS_Running);
99 if (!authenticate_file_daemon(jcr)) {
100 set_jcr_job_status(jcr, JS_ErrorTerminated);
105 * Now send JobId and authorization key
107 bnet_fsend(fd, jobcmd, edit_int64(jcr->JobId, ed1), jcr->Job, jcr->VolSessionId,
108 jcr->VolSessionTime, jcr->sd_auth_key);
109 if (strcmp(jcr->sd_auth_key, "dummy") != 0) {
110 memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
112 Dmsg1(100, ">filed: %s", fd->msg);
113 if (bget_dirmsg(fd) > 0) {
114 Dmsg1(110, "<filed: %s", fd->msg);
115 if (strncmp(fd->msg, OKjob, strlen(OKjob)) != 0) {
116 Jmsg(jcr, M_FATAL, 0, _("File daemon \"%s\" rejected Job command: %s\n"),
117 jcr->client->hdr.name, fd->msg);
118 set_jcr_job_status(jcr, JS_ErrorTerminated);
120 } else if (jcr->db) {
122 memset(&cr, 0, sizeof(cr));
123 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
124 cr.AutoPrune = jcr->client->AutoPrune;
125 cr.FileRetention = jcr->client->FileRetention;
126 cr.JobRetention = jcr->client->JobRetention;
127 bstrncpy(cr.Uname, fd->msg+strlen(OKjob)+1, sizeof(cr.Uname));
128 if (!db_update_client_record(jcr, jcr->db, &cr)) {
129 Jmsg(jcr, M_WARNING, 0, _("Error updating Client record. ERR=%s\n"),
130 db_strerror(jcr->db));
134 Jmsg(jcr, M_FATAL, 0, _("FD gave bad response to JobId command: %s\n"),
136 set_jcr_job_status(jcr, JS_ErrorTerminated);
143 * This subroutine edits the last job start time into a
144 * "since=date/time" buffer that is returned in the
145 * variable since. This is used for display purposes in
146 * the job report. The time in jcr->stime is later
147 * passed to tell the File daemon what to do.
149 void get_level_since_time(JCR *jcr, char *since, int since_len)
155 if (jcr->stime && jcr->stime[0]) {
156 bstrncpy(since, _(", since="), since_len);
157 bstrncat(since, jcr->stime, since_len);
162 jcr->stime = get_pool_memory(PM_MESSAGE);
165 /* Lookup the last FULL backup job to get the time/date for a
166 * differential or incremental save.
168 switch (jcr->JobLevel) {
171 /* Look up start time of last job */
172 jcr->jr.JobId = 0; /* flag for db_find_job_start time */
173 if (!db_find_job_start_time(jcr, jcr->db, &jcr->jr, &jcr->stime)) {
174 /* No job found, so upgrade this one to Full */
175 Jmsg(jcr, M_INFO, 0, "%s", db_strerror(jcr->db));
176 Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full backup found in catalog. Doing FULL backup.\n"));
177 bsnprintf(since, since_len, _(" (upgraded from %s)"),
178 level_to_str(jcr->JobLevel));
179 jcr->JobLevel = jcr->jr.JobLevel = L_FULL;
181 if (jcr->job->rerun_failed_levels) {
182 if (db_find_failed_job_since(jcr, jcr->db, &jcr->jr, jcr->stime, JobLevel)) {
183 Jmsg(jcr, M_INFO, 0, _("Prior failed job found in catalog. Upgrading to %s.\n"),
184 level_to_str(JobLevel));
185 bsnprintf(since, since_len, _(" (upgraded from %s)"),
186 level_to_str(jcr->JobLevel));
187 jcr->JobLevel = jcr->jr.JobLevel = JobLevel;
188 jcr->jr.JobId = jcr->JobId;
192 bstrncpy(since, _(", since="), since_len);
193 bstrncat(since, jcr->stime, since_len);
195 jcr->jr.JobId = jcr->JobId;
198 Dmsg2(100, "Level=%c last start time=%s\n", jcr->JobLevel, jcr->stime);
201 static void send_since_time(JCR *jcr)
203 BSOCK *fd = jcr->file_bsock;
207 stime = str_to_utime(jcr->stime);
208 bnet_fsend(fd, levelcmd, NT_("since_utime "), edit_uint64(stime, ed1), 0);
209 while (bget_dirmsg(fd) >= 0) { /* allow him to poll us to sync clocks */
210 Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg);
216 * Send level command to FD.
217 * Used for backup jobs and estimate command.
219 bool send_level_command(JCR *jcr)
221 BSOCK *fd = jcr->file_bsock;
223 * Send Level command to File daemon
225 switch (jcr->JobLevel) {
227 bnet_fsend(fd, levelcmd, "base", " ", 0);
229 /* L_NONE is the console, sending something off to the FD */
232 bnet_fsend(fd, levelcmd, "full", " ", 0);
235 bnet_fsend(fd, levelcmd, "differential", " ", 0);
236 send_since_time(jcr);
239 bnet_fsend(fd, levelcmd, "incremental", " ", 0);
240 send_since_time(jcr);
244 Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"),
245 jcr->JobLevel, jcr->JobLevel);
248 Dmsg1(120, ">filed: %s", fd->msg);
249 if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) {
256 * Send either an Included or an Excluded list to FD
258 static bool send_fileset(JCR *jcr)
260 FILESET *fileset = jcr->fileset;
261 BSOCK *fd = jcr->file_bsock;
267 num = fileset->num_includes;
269 num = fileset->num_excludes;
271 for (int i=0; i<num; i++) {
281 ie = fileset->include_items[i];
282 bnet_fsend(fd, "I\n");
284 ie = fileset->exclude_items[i];
285 bnet_fsend(fd, "E\n");
287 for (j=0; j<ie->num_opts; j++) {
288 FOPTS *fo = ie->opts_list[j];
289 bnet_fsend(fd, "O %s\n", fo->opts);
291 bool enhanced_wild = false;
292 for (k=0; fo->opts[k]!='\0'; k++) {
293 if (fo->opts[k]=='W') {
294 enhanced_wild = true;
299 for (k=0; k<fo->regex.size(); k++) {
300 bnet_fsend(fd, "R %s\n", fo->regex.get(k));
302 for (k=0; k<fo->regexdir.size(); k++) {
303 bnet_fsend(fd, "RD %s\n", fo->regexdir.get(k));
305 for (k=0; k<fo->regexfile.size(); k++) {
306 bnet_fsend(fd, "RF %s\n", fo->regexfile.get(k));
308 for (k=0; k<fo->wild.size(); k++) {
309 bnet_fsend(fd, "W %s\n", fo->wild.get(k));
311 for (k=0; k<fo->wilddir.size(); k++) {
312 bnet_fsend(fd, "WD %s\n", fo->wilddir.get(k));
314 for (k=0; k<fo->wildfile.size(); k++) {
315 bnet_fsend(fd, "WF %s\n", fo->wildfile.get(k));
317 for (k=0; k<fo->wildbase.size(); k++) {
318 bnet_fsend(fd, "W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
320 for (k=0; k<fo->base.size(); k++) {
321 bnet_fsend(fd, "B %s\n", fo->base.get(k));
323 for (k=0; k<fo->fstype.size(); k++) {
324 bnet_fsend(fd, "X %s\n", fo->fstype.get(k));
326 for (k=0; k<fo->drivetype.size(); k++) {
327 bnet_fsend(fd, "XD %s\n", fo->drivetype.get(k));
330 bnet_fsend(fd, "D %s\n", fo->reader);
333 bnet_fsend(fd, "T %s\n", fo->writer);
335 bnet_fsend(fd, "N\n");
338 for (j=0; j<ie->name_list.size(); j++) {
339 p = (char *)ie->name_list.get(j);
342 p++; /* skip over the | */
343 fd->msg = edit_job_codes(jcr, fd->msg, p, "");
344 bpipe = open_bpipe(fd->msg, 0, "r");
347 Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
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, bpipe->rfd)) {
355 fd->msglen = Mmsg(fd->msg, "%s", buf);
356 Dmsg2(500, "Inc/exc len=%d: %s", fd->msglen, fd->msg);
357 if (!bnet_send(fd)) {
358 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
362 if ((stat=close_bpipe(bpipe)) != 0) {
364 Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. ERR=%s\n"),
365 p, be.strerror(stat));
370 p++; /* skip over < */
371 if ((ffd = fopen(p, "rb")) == NULL) {
373 Jmsg(jcr, M_FATAL, 0, _("Cannot open included file: %s. ERR=%s\n"),
377 bstrncpy(buf, "F ", sizeof(buf));
378 Dmsg1(500, "Opts=%s\n", buf);
379 optlen = strlen(buf);
380 while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) {
381 fd->msglen = Mmsg(fd->msg, "%s", buf);
382 if (!bnet_send(fd)) {
383 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
390 p++; /* skip over \ */
391 /* Note, fall through wanted */
393 pm_strcpy(fd->msg, "F ");
394 fd->msglen = pm_strcat(fd->msg, p);
395 Dmsg1(500, "Inc/Exc name=%s\n", fd->msg);
396 if (!bnet_send(fd)) {
397 Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n"));
403 bnet_fsend(fd, "N\n");
405 if (!include) { /* If we just did excludes */
406 break; /* all done */
408 include = false; /* Now do excludes */
411 bnet_sig(fd, BNET_EOD); /* end of data */
412 if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) {
418 set_jcr_job_status(jcr, JS_ErrorTerminated);
425 * Send include list to File daemon
427 bool send_include_list(JCR *jcr)
429 BSOCK *fd = jcr->file_bsock;
430 if (jcr->fileset->new_include) {
431 bnet_fsend(fd, filesetcmd, jcr->fileset->enable_vss ? " vss=1" : "");
432 return send_fileset(jcr);
439 * Send exclude list to File daemon
440 * Under the new scheme, the Exclude list
441 * is part of the FileSet sent with the
442 * "include_list" above.
444 bool send_exclude_list(JCR *jcr)
451 * Send bootstrap file if any to the socket given (FD or SD).
452 * This is used for restore, verify VolumeToCatalog, and
455 bool send_bootstrap_file(JCR *jcr, BSOCK *sock)
459 const char *bootstrap = "bootstrap\n";
461 Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap);
462 if (!jcr->RestoreBootstrap) {
465 bs = fopen(jcr->RestoreBootstrap, "rb");
468 Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"),
469 jcr->RestoreBootstrap, be.strerror());
470 set_jcr_job_status(jcr, JS_ErrorTerminated);
473 bnet_fsend(sock, bootstrap);
474 while (fgets(buf, sizeof(buf), bs)) {
475 bnet_fsend(sock, "%s", buf);
477 bnet_sig(sock, BNET_EOD);
479 if (jcr->unlink_bsr) {
480 unlink(jcr->RestoreBootstrap);
481 jcr->unlink_bsr = false;
486 /* TODO: drop this with runscript.old_proto in bacula 1.42 */
487 static char runbefore[] = "RunBeforeJob %s\n";
488 static char runafter[] = "RunAfterJob %s\n";
489 static char OKRunBefore[] = "2000 OK RunBefore\n";
490 static char OKRunAfter[] = "2000 OK RunAfter\n";
492 int send_runscript_with_old_proto(JCR *jcr, int when, POOLMEM *msg)
495 Dmsg1(120, "bdird: sending old runcommand to fd '%s'\n",msg);
496 if (when & SCRIPT_Before) {
497 bnet_fsend(jcr->file_bsock, runbefore, msg);
498 ret = response(jcr, jcr->file_bsock, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR);
500 bnet_fsend(jcr->file_bsock, runafter, msg);
501 ret = response(jcr, jcr->file_bsock, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR);
507 * Send RunScripts to File daemon
508 * 1) We send all runscript to FD, they can be executed Before, After, or twice
509 * 2) Then, we send a "RunBeforeNow" command to the FD to tell him to do the
510 * first run_script() call. (ie ClientRunBeforeJob)
512 int send_runscripts_commands(JCR *jcr)
514 POOLMEM *msg = get_pool_memory(PM_FNAME);
515 BSOCK *fd = jcr->file_bsock;
517 bool launch_before_cmd = false;
518 POOLMEM *ehost = get_pool_memory(PM_FNAME);
521 Dmsg0(120, "bdird: sending runscripts to fd\n");
523 foreach_alist(cmd, jcr->job->RunScripts) {
525 if (cmd->can_run_at_level(jcr->JobLevel) && cmd->target) {
527 ehost = edit_job_codes(jcr, ehost, cmd->target, "");
528 Dmsg2(200, "bdird: runscript %s -> %s\n", cmd->target, ehost);
530 if (strcmp(ehost, jcr->client->hdr.name) == 0) {
531 pm_strcpy(msg, cmd->command);
534 Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", cmd->command);
536 /* TODO: remove this with bacula 1.42 */
537 if (cmd->old_proto) {
538 result = send_runscript_with_old_proto(jcr, cmd->when, msg);
541 bnet_fsend(fd, runscript, cmd->on_success,
547 result = response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR);
548 launch_before_cmd=true;
552 set_jcr_job_status(jcr, JS_ErrorTerminated);
553 free_pool_memory(msg);
554 free_pool_memory(ehost);
558 /* TODO : we have to play with other client */
561 send command to an other client
567 /* We tell to the FD that i can execute commands (ie ClientRunBeforeJob) */
568 if (launch_before_cmd) {
569 bnet_fsend(fd, runbeforenow);
570 if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) {
571 set_jcr_job_status(jcr, JS_ErrorTerminated);
572 free_pool_memory(msg);
573 free_pool_memory(ehost);
577 free_pool_memory(msg);
578 free_pool_memory(ehost);
584 * Read the attributes from the File daemon for
585 * a Verify job and store them in the catalog.
587 int get_attributes_and_put_in_catalog(JCR *jcr)
593 fd = jcr->file_bsock;
594 jcr->jr.FirstIndex = 1;
595 memset(&ar, 0, sizeof(ar));
598 Dmsg0(120, "bdird: waiting to receive file attributes\n");
599 /* Pickup file attributes and digest */
600 while (!fd->errors && (n = bget_dirmsg(fd)) > 0) {
602 /*****FIXME****** improve error handling to stop only on
603 * really fatal problems, or the number of errors is too
609 char Opts_Digest[MAXSTRING]; /* either Verify opts or MD5/SHA1 digest */
610 char digest[CRYPTO_DIGEST_MAX_SIZE];
612 jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
613 if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Opts_Digest)) != 3) {
614 Jmsg(jcr, M_FATAL, 0, _("<filed: bad attributes, expected 3 fields got %d\n"
615 "msglen=%d msg=%s\n"), len, fd->msglen, fd->msg);
616 set_jcr_job_status(jcr, JS_ErrorTerminated);
620 skip_nonspaces(&p); /* skip FileIndex */
622 skip_nonspaces(&p); /* skip Stream */
624 skip_nonspaces(&p); /* skip Opts_SHA1 */
625 p++; /* skip space */
628 *fn++ = *p++; /* copy filename */
630 *fn = *p++; /* term filename and point to attribs */
633 if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
635 jcr->FileIndex = file_index;
637 ar.fname = jcr->fname;
638 ar.FileIndex = file_index;
641 ar.JobId = jcr->JobId;
642 ar.ClientId = jcr->ClientId;
646 ar.DigestType = CRYPTO_DIGEST_NONE;
648 Dmsg2(111, "dird<filed: stream=%d %s\n", stream, jcr->fname);
649 Dmsg1(120, "dird<filed: attr=%s\n", attr);
651 if (!db_create_file_attributes_record(jcr, jcr->db, &ar)) {
652 Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
653 set_jcr_job_status(jcr, JS_Error);
656 jcr->FileId = ar.FileId;
657 } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
658 if (jcr->FileIndex != (uint32_t)file_index) {
659 Jmsg3(jcr, M_ERROR, 0, _("%s index %d not same as attributes %d\n"),
660 stream_to_ascii(stream), file_index, jcr->FileIndex);
661 set_jcr_job_status(jcr, JS_Error);
664 db_escape_string(digest, Opts_Digest, strlen(Opts_Digest));
665 Dmsg2(120, "DigestLen=%d Digest=%s\n", strlen(digest), digest);
666 if (!db_add_digest_to_file_record(jcr, jcr->db, jcr->FileId, digest,
667 crypto_digest_stream_type(stream))) {
668 Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db));
669 set_jcr_job_status(jcr, JS_Error);
672 jcr->jr.JobFiles = jcr->JobFiles = file_index;
673 jcr->jr.LastIndex = file_index;
675 if (is_bnet_error(fd)) {
676 Jmsg1(jcr, M_FATAL, 0, _("<filed: Network error getting attributes. ERR=%s\n"),
678 set_jcr_job_status(jcr, JS_ErrorTerminated);
682 set_jcr_job_status(jcr, JS_Terminated);