X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Ffd_cmds.c;h=3f7be74d69aa35e71849d324043f33e9dde17fee;hb=b8b2ed2a6db4fb8436647d438185a364951375fc;hp=155b1428a6d39180d553c6dd48684b20cce3fcc8;hpb=bc72c9d4b1040a77350c6c127a7c161f1f4dd8ed;p=bacula%2Fbacula diff --git a/bacula/src/dird/fd_cmds.c b/bacula/src/dird/fd_cmds.c index 155b1428a6..3f7be74d69 100644 --- a/bacula/src/dird/fd_cmds.c +++ b/bacula/src/dird/fd_cmds.c @@ -13,7 +13,7 @@ * Version $Id$ */ /* - Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker + Copyright (C) 2000-2003 Kern Sibbald and John Walker This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -39,12 +39,19 @@ static char inc[] = "include\n"; static char exc[] = "exclude\n"; static char jobcmd[] = "JobId=%d Job=%s SDid=%u SDtime=%u Authorization=%s\n"; +static char levelcmd[] = "level = %s%s mtime_only=%d\n"; +static char runbefore[] = "RunBeforeJob %s\n"; +static char runafter[] = "RunAfterJob %s\n"; /* Responses received from File daemon */ -static char OKinc[] = "2000 OK include\n"; -static char OKexc[] = "2000 OK exclude\n"; -static char OKjob[] = "2000 OK Job\n"; +static char OKinc[] = "2000 OK include\n"; +static char OKexc[] = "2000 OK exclude\n"; +static char OKjob[] = "2000 OK Job"; +static char OKbootstrap[] = "2000 OK bootstrap\n"; +static char OKlevel[] = "2000 OK level\n"; +static char OKRunBefore[] = "2000 OK RunBefore\n"; +static char OKRunAfter[] = "2000 OK RunAfter\n"; /* Forward referenced functions */ @@ -53,9 +60,13 @@ extern int debug_level; extern DIRRES *director; extern int FDConnectTimeout; +#define INC_LIST 0 +#define EXC_LIST 1 + /* * Open connection with File daemon. - * Try connecting every 10 seconds, give up after 1 hour. + * Try connecting every retry_interval (default 10 sec), and + * give up after max_retry_time (default 30 mins). */ int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time, @@ -67,16 +78,16 @@ int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time, _("File daemon"), jcr->client->address, NULL, jcr->client->FDport, verbose); if (fd == NULL) { - jcr->JobStatus = JS_ErrorTerminated; + set_jcr_job_status(jcr, JS_ErrorTerminated); return 0; } Dmsg0(10, "Opened connection with File daemon\n"); fd->res = (RES *)jcr->client; /* save resource in BSOCK */ jcr->file_bsock = fd; - jcr->JobStatus = JS_Running; + set_jcr_job_status(jcr, JS_Running); if (!authenticate_file_daemon(jcr)) { - jcr->JobStatus = JS_ErrorTerminated; + set_jcr_job_status(jcr, JS_ErrorTerminated); return 0; } @@ -85,96 +96,346 @@ int connect_to_file_daemon(JCR *jcr, int retry_interval, int max_retry_time, */ bnet_fsend(fd, jobcmd, jcr->JobId, jcr->Job, jcr->VolSessionId, jcr->VolSessionTime, jcr->sd_auth_key); - Dmsg1(110, ">filed: %s", fd->msg); - if (bnet_recv(fd) > 0) { + if (strcmp(jcr->sd_auth_key, "dummy") != 0) { + memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key)); + } + Dmsg1(100, ">filed: %s", fd->msg); + if (bget_dirmsg(fd) > 0) { Dmsg1(110, "msg); - if (strcmp(fd->msg, OKjob) != 0) { - Jmsg(jcr, M_FATAL, 0, _("File daemon rejected Job command: %s\n"), fd->msg); - jcr->JobStatus = JS_ErrorTerminated; + if (strncmp(fd->msg, OKjob, strlen(OKjob)) != 0) { + Jmsg(jcr, M_FATAL, 0, _("File daemon \"%s\" rejected Job command: %s\n"), + jcr->client->hdr.name, fd->msg); + set_jcr_job_status(jcr, JS_ErrorTerminated); return 0; - } + } else if (jcr->db) { + CLIENT_DBR cr; + memset(&cr, 0, sizeof(cr)); + bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name)); + cr.AutoPrune = jcr->client->AutoPrune; + cr.FileRetention = jcr->client->FileRetention; + cr.JobRetention = jcr->client->JobRetention; + bstrncpy(cr.Uname, fd->msg+strlen(OKjob)+1, sizeof(cr.Uname)); + if (!db_update_client_record(jcr, jcr->db, &cr)) { + Jmsg(jcr, M_WARNING, 0, _("Error updating Client record. ERR=%s\n"), + db_strerror(jcr->db)); + } + } } else { - Jmsg(jcr, M_FATAL, 0, _("JobStatus = JS_ErrorTerminated; + set_jcr_job_status(jcr, JS_ErrorTerminated); return 0; } return 1; } +/* + * This subroutine edits the last job start time into a + * "since=date/time" buffer that is returned in the + * variable since. This is used for display purposes in + * the job report. The time in jcr->stime is later + * passed to tell the File daemon what to do. + */ +void get_level_since_time(JCR *jcr, char *since, int since_len) +{ + /* Lookup the last + * FULL backup job to get the time/date for a + * differential or incremental save. + */ + if (!jcr->stime) { + jcr->stime = get_pool_memory(PM_MESSAGE); + } + jcr->stime[0] = 0; + since[0] = 0; + switch (jcr->JobLevel) { + case L_DIFFERENTIAL: + case L_INCREMENTAL: + /* Look up start time of last job */ + jcr->jr.JobId = 0; + if (!db_find_job_start_time(jcr, jcr->db, &jcr->jr, &jcr->stime)) { + Jmsg(jcr, M_INFO, 0, "%s", db_strerror(jcr->db)); + Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full backup found. Doing FULL backup.\n")); + bsnprintf(since, since_len, " (upgraded from %s)", + level_to_str(jcr->JobLevel)); + jcr->JobLevel = jcr->jr.Level = L_FULL; + } else { + bstrncpy(since, ", since=", since_len); + bstrncat(since, jcr->stime, since_len); + } + Dmsg1(100, "Last start time = %s\n", jcr->stime); + break; + } +} + /* - * Send include list to File daemon + * Send level command to FD. + * Used for backup jobs and estimate command. */ -int send_include_list(JCR *jcr) +int send_level_command(JCR *jcr) +{ + BSOCK *fd = jcr->file_bsock; + utime_t stime; + char ed1[50]; + /* + * Send Level command to File daemon + */ + switch (jcr->JobLevel) { + case L_BASE: + bnet_fsend(fd, levelcmd, "base", " ", 0); + break; + /* L_NONE is the console, sending something off to the FD */ + case L_NONE: + case L_FULL: + bnet_fsend(fd, levelcmd, "full", " ", 0); + break; + case L_DIFFERENTIAL: + case L_INCREMENTAL: +// bnet_fsend(fd, levelcmd, "since ", jcr->stime, 0); /* old code, deprecated */ + stime = str_to_utime(jcr->stime); + bnet_fsend(fd, levelcmd, "since_utime ", edit_uint64(stime, ed1), 0); + while (bget_dirmsg(fd) >= 0) { /* allow him to poll us to sync clocks */ + Jmsg(jcr, M_INFO, 0, "%s\n", fd->msg); + } + break; + case L_SINCE: + default: + Jmsg2(jcr, M_FATAL, 0, _("Unimplemented backup level %d %c\n"), + jcr->JobLevel, jcr->JobLevel); + return 0; + } + Dmsg1(120, ">filed: %s", fd->msg); + if (!response(jcr, fd, OKlevel, "Level", DISPLAY_ERROR)) { + return 0; + } + return 1; +} + + +/* + * Send either an Included or an Excluded list to FD + */ +static int send_list(JCR *jcr, int list) { FILESET *fileset; BSOCK *fd; - int i; - char *msgsave; + int num; fd = jcr->file_bsock; fileset = jcr->fileset; - msgsave = fd->msg; + if (list == INC_LIST) { + num = fileset->num_includes; + } else { + num = fileset->num_excludes; + } - fd->msglen = sprintf(fd->msg, inc); - bnet_send(fd); - for (i=0; i < fileset->num_includes; i++) { - fd->msglen = strlen(fileset->include_array[i]); - Dmsg1(120, "dird>filed: include file: %s\n", fileset->include_array[i]); - fd->msg = fileset->include_array[i]; - if (!bnet_send(fd)) { - fd->msg = msgsave; - Emsg0(M_FATAL, 0, _(">filed: write error on socket\n")); - jcr->JobStatus = JS_ErrorTerminated; - return 0; + for (int i=0; i < num; i++) { + BPIPE *bpipe; + FILE *ffd; + char buf[1000]; + char *p; + int optlen, stat; + INCEXE *ie; + FOPTS *fo; + + + if (list == INC_LIST) { + ie = fileset->include_items[i]; + } else { + ie = fileset->exclude_items[i]; + } + fo = ie->opts_list[0]; + for (int j=0; jmatch.size(); j++) { + Dmsg1(100, "Match=%s\n", fo->match.get(j)); + } + for (int j=0; jname_list.size(); j++) { + p = (char *)ie->name_list.get(j); + switch (*p) { + case '|': + p++; /* skip over the | */ + fd->msg = edit_job_codes(jcr, fd->msg, p, ""); + bpipe = open_bpipe(fd->msg, 0, "r"); + if (!bpipe) { + Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"), + p, strerror(errno)); + goto bail_out; + } + /* Copy File options */ + if (ie->num_opts) { + bstrncpy(buf, ie->opts_list[0]->opts, sizeof(buf)); + bstrncat(buf, " ", sizeof(buf)); + } else { + bstrncpy(buf, "0 ", sizeof(buf)); + } + Dmsg1(100, "Opts=%s\n", buf); + optlen = strlen(buf); + while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) { + fd->msglen = Mmsg(&fd->msg, "%s", buf); + Dmsg2(200, "Inc/exc len=%d: %s", fd->msglen, fd->msg); + if (!bnet_send(fd)) { + Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n")); + goto bail_out; + } + } + if ((stat=close_bpipe(bpipe)) != 0) { + Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. RtnStat=%d ERR=%s\n"), + p, stat, strerror(errno)); + goto bail_out; + } + break; + case '<': + p++; /* skip over < */ + if ((ffd = fopen(p, "r")) == NULL) { + Jmsg(jcr, M_FATAL, 0, _("Cannot open %s file: %s. ERR=%s\n"), + list==INC_LIST?"included":"excluded", p, strerror(errno)); + goto bail_out; + } + /* Copy File options */ + if (ie->num_opts) { + bstrncpy(buf, ie->opts_list[0]->opts, sizeof(buf)); + bstrncat(buf, " ", sizeof(buf)); + } else { + bstrncpy(buf, "0 ", sizeof(buf)); + } + Dmsg1(100, "Opts=%s\n", buf); + optlen = strlen(buf); + while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) { + fd->msglen = Mmsg(&fd->msg, "%s", buf); + if (!bnet_send(fd)) { + Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n")); + goto bail_out; + } + } + fclose(ffd); + break; + case '\\': + p++; /* skip over \ */ + /* Note, fall through wanted */ + default: + Dmsg2(100, "numopts=%d opts=%s\n", ie->num_opts, NPRT(ie->opts_list[0]->opts)); + if (ie->num_opts) { + pm_strcpy(&fd->msg, ie->opts_list[0]->opts); + pm_strcat(&fd->msg, " "); + } else { + pm_strcpy(&fd->msg, "0 "); + } + fd->msglen = pm_strcat(&fd->msg, p); + Dmsg1(100, "Inc/Exc name=%s\n", fd->msg); + if (!bnet_send(fd)) { + Jmsg(jcr, M_FATAL, 0, _(">filed: write error on socket\n")); + goto bail_out; + } + break; + } } } bnet_sig(fd, BNET_EOD); /* end of data */ - fd->msg = msgsave; - if (!response(fd, OKinc, "Include")) { - jcr->JobStatus = JS_ErrorTerminated; - return 0; + if (list == INC_LIST) { + if (!response(jcr, fd, OKinc, "Include", DISPLAY_ERROR)) { + goto bail_out; + } + } else if (!response(jcr, fd, OKexc, "Exclude", DISPLAY_ERROR)) { + goto bail_out; } return 1; + +bail_out: + set_jcr_job_status(jcr, JS_ErrorTerminated); + return 0; + +} + +/* + * Send include list to File daemon + */ +int send_include_list(JCR *jcr) +{ + BSOCK *fd = jcr->file_bsock; + fd->msglen = pm_strcpy(&fd->msg, inc); + bnet_send(fd); + return send_list(jcr, INC_LIST); } + /* * Send exclude list to File daemon */ int send_exclude_list(JCR *jcr) { - FILESET *fileset; - BSOCK *fd; - int i; - char *msgsave; + BSOCK *fd = jcr->file_bsock; + fd->msglen = pm_strcpy(&fd->msg, exc); + bnet_send(fd); + return send_list(jcr, EXC_LIST); +} - fd = jcr->file_bsock; - fileset = jcr->fileset; - msgsave = fd->msg; - fd->msglen = sprintf(fd->msg, exc); - bnet_send(fd); - for (i=0; i < fileset->num_excludes; i++) { - fd->msglen = strlen(fileset->exclude_array[i]); - Dmsg1(120, "dird>filed: exclude file: %s\n", fileset->exclude_array[i]); - fd->msg = fileset->exclude_array[i]; - if (!bnet_send(fd)) { - Emsg0(M_FATAL, 0, _(">filed: write error on socket\n")); - jcr->JobStatus = JS_ErrorTerminated; - return 0; - } +/* + * Send bootstrap file if any to the File daemon. + * This is used for restore and verify VolumeToCatalog + */ +int send_bootstrap_file(JCR *jcr) +{ + FILE *bs; + char buf[1000]; + BSOCK *fd = jcr->file_bsock; + char *bootstrap = "bootstrap\n"; + + Dmsg1(400, "send_bootstrap_file: %s\n", jcr->RestoreBootstrap); + if (!jcr->RestoreBootstrap) { + return 1; + } + bs = fopen(jcr->RestoreBootstrap, "r"); + if (!bs) { + Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"), + jcr->RestoreBootstrap, strerror(errno)); + set_jcr_job_status(jcr, JS_ErrorTerminated); + return 0; + } + bnet_fsend(fd, bootstrap); + while (fgets(buf, sizeof(buf), bs)) { + bnet_fsend(fd, "%s", buf); } bnet_sig(fd, BNET_EOD); - fd->msg = msgsave; - if (!response(fd, OKexc, "Exclude")) { - jcr->JobStatus = JS_ErrorTerminated; + fclose(bs); + if (!response(jcr, fd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) { + set_jcr_job_status(jcr, JS_ErrorTerminated); return 0; } return 1; } +/* + * Send ClientRunBeforeJob and ClientRunAfterJob to File daemon + */ +int send_run_before_and_after_commands(JCR *jcr) +{ + POOLMEM *msg = get_pool_memory(PM_FNAME); + BSOCK *fd = jcr->file_bsock; + if (jcr->job->ClientRunBeforeJob) { + pm_strcpy(&msg, jcr->job->ClientRunBeforeJob); + bash_spaces(msg); + bnet_fsend(fd, runbefore, msg); + if (!response(jcr, fd, OKRunBefore, "ClientRunBeforeJob", DISPLAY_ERROR)) { + set_jcr_job_status(jcr, JS_ErrorTerminated); + free_pool_memory(msg); + return 0; + } + } + if (jcr->job->ClientRunAfterJob) { + fd->msglen = pm_strcpy(&msg, jcr->job->ClientRunAfterJob); + bash_spaces(msg); + bnet_fsend(fd, runafter, msg); + if (!response(jcr, fd, OKRunAfter, "ClientRunAfterJob", DISPLAY_ERROR)) { + set_jcr_job_status(jcr, JS_ErrorTerminated); + free_pool_memory(msg); + return 0; + } + } + free_pool_memory(msg); + return 1; +} + /* * Read the attributes from the File daemon for @@ -193,7 +454,7 @@ int get_attributes_and_put_in_catalog(JCR *jcr) Dmsg0(120, "bdird: waiting to receive file attributes\n"); /* Pickup file attributes and signature */ - while (!fd->errors && (n = bget_msg(fd, 0)) > 0) { + while (!fd->errors && (n = bget_dirmsg(fd)) > 0) { /*****FIXME****** improve error handling to stop only on * really fatal problems, or the number of errors is too @@ -202,14 +463,14 @@ int get_attributes_and_put_in_catalog(JCR *jcr) long file_index; int stream, len; char *attr, *p, *fn; - char Opts_MD5[MAXSTRING]; /* either Verify opts or MD5 signature */ - char MD5[MAXSTRING]; + char Opts_SIG[MAXSTRING]; /* either Verify opts or MD5/SHA1 signature */ + char SIG[MAXSTRING]; jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen); - if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Opts_MD5)) != 3) { + if ((len = sscanf(fd->msg, "%ld %d %s", &file_index, &stream, Opts_SIG)) != 3) { Jmsg(jcr, M_FATAL, 0, _("msglen, fd->msg); - jcr->JobStatus = JS_ErrorTerminated; + set_jcr_job_status(jcr, JS_ErrorTerminated); return 0; } p = fd->msg; @@ -217,7 +478,7 @@ msglen=%d msg=%s\n"), len, fd->msglen, fd->msg); skip_spaces(&p); skip_nonspaces(&p); /* skip Stream */ skip_spaces(&p); - skip_nonspaces(&p); /* skip Opts_MD5 */ + skip_nonspaces(&p); /* skip Opts_SHA1 */ p++; /* skip space */ fn = jcr->fname; while (*p != 0) { @@ -226,7 +487,7 @@ msglen=%d msg=%s\n"), len, fd->msglen, fd->msg); *fn = *p++; /* term filename and point to attribs */ attr = p; - if (stream == STREAM_UNIX_ATTRIBUTES) { + if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) { jcr->JobFiles++; jcr->FileIndex = file_index; ar.attr = attr; @@ -242,37 +503,37 @@ msglen=%d msg=%s\n"), len, fd->msglen, fd->msg); Dmsg2(111, "dirdfname); Dmsg1(120, "dirddb, &ar)) { + if (!db_create_file_attributes_record(jcr, jcr->db, &ar)) { Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db)); - jcr->JobStatus = JS_Error; + set_jcr_job_status(jcr, JS_Error); continue; } jcr->FileId = ar.FileId; - } else if (stream == STREAM_MD5_SIGNATURE) { + } else if (stream == STREAM_MD5_SIGNATURE || stream == STREAM_SHA1_SIGNATURE) { if (jcr->FileIndex != (uint32_t)file_index) { - Jmsg2(jcr, M_ERROR, 0, _("MD5 index %d not same as attributes %d\n"), + Jmsg2(jcr, M_ERROR, 0, _("MD5/SHA1 index %d not same as attributes %d\n"), file_index, jcr->FileIndex); - jcr->JobStatus = JS_Error; + set_jcr_job_status(jcr, JS_Error); continue; } - db_escape_string(MD5, Opts_MD5, strlen(Opts_MD5)); - Dmsg2(120, "MD5len=%d MD5=%s\n", strlen(MD5), MD5); - if (!db_add_MD5_to_file_record(jcr->db, jcr->FileId, MD5)) { + db_escape_string(SIG, Opts_SIG, strlen(Opts_SIG)); + Dmsg2(120, "SIGlen=%d SIG=%s\n", strlen(SIG), SIG); + if (!db_add_SIG_to_file_record(jcr, jcr->db, jcr->FileId, SIG, + stream==STREAM_MD5_SIGNATURE?MD5_SIG:SHA1_SIG)) { Jmsg1(jcr, M_ERROR, 0, "%s", db_strerror(jcr->db)); - jcr->JobStatus = JS_Error; + set_jcr_job_status(jcr, JS_Error); } } jcr->jr.JobFiles = jcr->JobFiles = file_index; jcr->jr.LastIndex = file_index; } - if (n < 0) { + if (is_bnet_error(fd)) { Jmsg1(jcr, M_FATAL, 0, _("JobStatus = JS_ErrorTerminated; + set_jcr_job_status(jcr, JS_ErrorTerminated); return 0; } - jcr->JobStatus = JS_Terminated; + set_jcr_job_status(jcr, JS_Terminated); return 1; }