X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Ffiled%2Fjob.c;h=0c40531ad67dbe6019659b9740e64792941a4474;hb=99e6d4a3be04f173b4bcd6ec77e1d1d63a3f2153;hp=37e0d4b0982f5bc9f94f2dedfc6e3109f35fd48d;hpb=84907e655c6902b2bbfd155208e20ed15e889aab;p=bacula%2Fbacula diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index 37e0d4b098..0c40531ad6 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -7,7 +7,7 @@ * */ /* - Copyright (C) 2000-2003 Kern Sibbald and John Walker + Copyright (C) 2000-2005 Kern Sibbald This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -28,14 +28,14 @@ #include "bacula.h" #include "filed.h" -#include "host.h" extern char my_name[]; extern CLIENT *me; /* our client resource */ - + /* Imported functions */ extern int status_cmd(JCR *jcr); - +extern int qstatus_cmd(JCR *jcr); + /* Forward referenced functions */ static int backup_cmd(JCR *jcr); static int bootstrap_cmd(JCR *jcr); @@ -46,43 +46,53 @@ static int exclude_cmd(JCR *jcr); static int hello_cmd(JCR *jcr); static int job_cmd(JCR *jcr); static int include_cmd(JCR *jcr); +static int fileset_cmd(JCR *jcr); static int level_cmd(JCR *jcr); static int verify_cmd(JCR *jcr); static int restore_cmd(JCR *jcr); static int storage_cmd(JCR *jcr); static int session_cmd(JCR *jcr); -static int response(JCR *jcr, BSOCK *sd, char *resp, char *cmd); +static int response(JCR *jcr, BSOCK *sd, char *resp, const char *cmd); static void filed_free_jcr(JCR *jcr); static int open_sd_read_session(JCR *jcr); static int send_bootstrap_file(JCR *jcr); +static int runbefore_cmd(JCR *jcr); +static int runafter_cmd(JCR *jcr); +static int run_cmd(JCR *jcr, char *cmd, const char *name); +static void set_options(findFOPTS *fo, const char *opts); /* Exported functions */ struct s_cmds { - char *cmd; + const char *cmd; int (*func)(JCR *); + int monitoraccess; /* specify if monitors have access to this function */ }; -/* - * The following are the recognized commands from the Director. +/* + * The following are the recognized commands from the Director. */ static struct s_cmds cmds[] = { - {"backup", backup_cmd}, - {"cancel", cancel_cmd}, - {"setdebug=", setdebug_cmd}, - {"estimate", estimate_cmd}, - {"exclude", exclude_cmd}, - {"Hello", hello_cmd}, - {"include", include_cmd}, - {"JobId=", job_cmd}, - {"level = ", level_cmd}, - {"restore", restore_cmd}, - {"session", session_cmd}, - {"status", status_cmd}, - {"storage ", storage_cmd}, - {"verify", verify_cmd}, - {"bootstrap",bootstrap_cmd}, + {"backup", backup_cmd, 0}, + {"cancel", cancel_cmd, 0}, + {"setdebug=", setdebug_cmd, 0}, + {"estimate", estimate_cmd, 0}, + {"exclude", exclude_cmd, 0}, + {"Hello", hello_cmd, 1}, + {"include", include_cmd, 0}, + {"fileset", fileset_cmd, 0}, + {"JobId=", job_cmd, 0}, + {"level = ", level_cmd, 0}, + {"restore", restore_cmd, 0}, + {"session", session_cmd, 0}, + {"status", status_cmd, 1}, + {".status", qstatus_cmd, 1}, + {"storage ", storage_cmd, 0}, + {"verify", verify_cmd, 0}, + {"bootstrap", bootstrap_cmd, 0}, + {"RunBeforeJob", runbefore_cmd, 0}, + {"RunAfterJob", runafter_cmd, 0}, {NULL, NULL} /* list terminator */ }; @@ -90,15 +100,19 @@ static struct s_cmds cmds[] = { static char jobcmd[] = "JobId=%d Job=%127s SDid=%d SDtime=%d Authorization=%100s"; static char storaddr[] = "storage address=%s port=%d ssl=%d\n"; static char sessioncmd[] = "session %127s %ld %ld %ld %ld %ld %ld\n"; -static char restorecmd[] = "restore replace=%c where=%s\n"; -static char restorecmd1[] = "restore replace=%c where=\n"; +static char restorecmd[] = "restore replace=%c prelinks=%d where=%s\n"; +static char restorecmd1[] = "restore replace=%c prelinks=%d where=\n"; static char verifycmd[] = "verify level=%30s\n"; +static char estimatecmd[] = "estimate listing=%d\n"; +static char runbefore[] = "RunBeforeJob %s\n"; +static char runafter[] = "RunAfterJob %s\n"; /* Responses sent to Director */ static char errmsg[] = "2999 Invalid command\n"; static char no_auth[] = "2998 No Authorization\n"; +static char illegal_cmd[] = "2997 Illegal command for a Director with Monitor directive enabled\n"; static char OKinc[] = "2000 OK include\n"; -static char OKest[] = "2000 OK estimate files=%ld bytes=%ld\n"; +static char OKest[] = "2000 OK estimate files=%u bytes=%s\n"; static char OKexc[] = "2000 OK exclude\n"; static char OKlevel[] = "2000 OK level\n"; static char OKbackup[] = "2000 OK backup\n"; @@ -107,11 +121,12 @@ static char OKverify[] = "2000 OK verify\n"; static char OKrestore[] = "2000 OK restore\n"; static char OKsession[] = "2000 OK session\n"; static char OKstore[] = "2000 OK storage\n"; -static char OKjob[] = "2000 OK Job " FDHOST "," DISTNAME "," DISTVER; +static char OKjob[] = "2000 OK Job %s,%s,%s"; static char OKsetdebug[] = "2000 OK setdebug=%d\n"; static char BADjob[] = "2901 Bad Job\n"; -static char EndRestore[] = "2800 End Job TermCode=%d JobFiles=%u JobBytes=%" lld "\n"; -static char EndBackup[] = "2801 End Backup Job TermCode=%d JobFiles=%u ReadBytes=%" lld " JobBytes=%" lld "\n"; +static char EndJob[] = "2800 End Job TermCode=%d JobFiles=%u ReadBytes=%s JobBytes=%s Errors=%u\n"; +static char OKRunBefore[] = "2000 OK RunBefore\n"; +static char OKRunAfter[] = "2000 OK RunAfter\n"; /* Responses received from Storage Daemon */ static char OK_end[] = "3000 OK end\n"; @@ -131,7 +146,7 @@ static char read_open[] = "read open session = %s %ld %ld %ld %ld %ld %ld\n"; static char read_data[] = "read data %d\n"; static char read_close[] = "read close session %d\n"; -/* +/* * Accept requests from a Director * * NOTE! We are running as a separate thread @@ -150,52 +165,128 @@ static char read_close[] = "read close session %d\n"; */ void *handle_client_request(void *dirp) { - int i, found, quit; + int i; + bool found, quit; JCR *jcr; BSOCK *dir = (BSOCK *)dirp; jcr = new_jcr(sizeof(JCR), filed_free_jcr); /* create JCR */ jcr->dir_bsock = dir; jcr->ff = init_find_files(); - jcr->use_win_backup_api = 0; jcr->start_time = time(NULL); jcr->last_fname = get_pool_memory(PM_FNAME); jcr->last_fname[0] = 0; jcr->client_name = get_memory(strlen(my_name) + 1); - pm_strcpy(&jcr->client_name, my_name); - dir->jcr = (void *)jcr; + pm_strcpy(jcr->client_name, my_name); + dir->jcr = jcr; + enable_backup_privileges(NULL, 1 /* ignore_errors */); /**********FIXME******* add command handler error code */ - for (quit=0; !quit;) { + for (quit=false; !quit;) { /* Read command */ if (bnet_recv(dir) < 0) { - break; /* connection terminated */ + break; /* connection terminated */ } dir->msg[dir->msglen] = 0; Dmsg1(100, "msg); - found = FALSE; + found = false; for (i=0; cmds[i].cmd; i++) { if (strncmp(cmds[i].cmd, dir->msg, strlen(cmds[i].cmd)) == 0) { + found = true; /* indicate command found */ if (!jcr->authenticated && cmds[i].func != hello_cmd) { bnet_fsend(dir, no_auth); + bnet_sig(dir, BNET_EOD); + break; + } + if ((jcr->authenticated) && (!cmds[i].monitoraccess) && (jcr->director->monitor)) { + Dmsg1(100, "Command %s illegal.\n", cmds[i].cmd); + bnet_fsend(dir, illegal_cmd); + bnet_sig(dir, BNET_EOD); break; } - found = TRUE; /* indicate command found */ - if (!cmds[i].func(jcr)) { /* do command */ - quit = TRUE; /* error or fully terminated, get out */ - Pmsg0(20, "Command error\n"); + Dmsg1(100, "Executing %s command.\n", cmds[i].cmd); + if (!cmds[i].func(jcr)) { /* do command */ + quit = true; /* error or fully terminated, get out */ + Dmsg0(20, "Quit command loop due to command error or Job done.\n"); } break; } } - if (!found) { /* command not found */ + if (!found) { /* command not found */ bnet_fsend(dir, errmsg); - quit = TRUE; + quit = true; break; } } + + /* Inform Storage daemon that we are done */ + if (jcr->store_bsock) { + bnet_sig(jcr->store_bsock, BNET_TERMINATE); + } + + if (jcr->RunAfterJob && !job_canceled(jcr)) { + run_cmd(jcr, jcr->RunAfterJob, "ClientRunAfterJob"); + } + dequeue_messages(jcr); /* send any queued messages */ + + /* Inform Director that we are done */ + bnet_sig(dir, BNET_TERMINATE); + + /* Clean up fileset */ + FF_PKT *ff = (FF_PKT *)jcr->ff; + findFILESET *fileset = ff->fileset; + if (fileset) { + int i, j, k; + /* Delete FileSet Include lists */ + for (i=0; iinclude_list.size(); i++) { + findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i); + for (j=0; jopts_list.size(); j++) { + findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j); + for (k=0; kregex.size(); k++) { + regfree((regex_t *)fo->regex.get(k)); + } + fo->regex.destroy(); + fo->regexdir.destroy(); + fo->regexfile.destroy(); + fo->wild.destroy(); + fo->wilddir.destroy(); + fo->wildfile.destroy(); + fo->base.destroy(); + fo->fstype.destroy(); + if (fo->reader) { + free(fo->reader); + } + if (fo->writer) { + free(fo->writer); + } + } + incexe->opts_list.destroy(); + incexe->name_list.destroy(); + } + fileset->include_list.destroy(); + + /* Delete FileSet Exclude lists */ + for (i=0; iexclude_list.size(); i++) { + findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i); + for (j=0; jopts_list.size(); j++) { + findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j); + fo->regex.destroy(); + fo->regexdir.destroy(); + fo->regexfile.destroy(); + fo->wild.destroy(); + fo->wilddir.destroy(); + fo->wildfile.destroy(); + fo->base.destroy(); + fo->fstype.destroy(); + } + incexe->opts_list.destroy(); + incexe->name_list.destroy(); + } + fileset->exclude_list.destroy(); + free(fileset); + } Dmsg0(100, "Calling term_find_files\n"); term_find_files((FF_PKT *)jcr->ff); Dmsg0(100, "Done with term_find_files\n"); @@ -205,7 +296,7 @@ void *handle_client_request(void *dirp) } /* - * Hello from Director he must identify himself and provide his + * Hello from Director he must identify himself and provide his * password. */ static int hello_cmd(JCR *jcr) @@ -215,7 +306,7 @@ static int hello_cmd(JCR *jcr) return 0; } Dmsg0(120, "OK Authenticate\n"); - jcr->authenticated = TRUE; + jcr->authenticated = true; return 1; } @@ -232,12 +323,24 @@ static int cancel_cmd(JCR *jcr) if (!(cjcr=get_jcr_by_full_name(Job))) { bnet_fsend(dir, "2901 Job %s not found.\n", Job); } else { + if (cjcr->store_bsock) { + P(cjcr->mutex); + cjcr->store_bsock->timed_out = 1; + cjcr->store_bsock->terminated = 1; +/* + * #if !defined(HAVE_CYGWIN) && !defined(HAVE_WIN32) + */ +#if !defined(HAVE_CYGWIN) + pthread_kill(cjcr->my_thread_id, TIMEOUT_SIGNAL); +#endif + V(cjcr->mutex); + } set_jcr_job_status(cjcr, JS_Canceled); free_jcr(cjcr); - bnet_fsend(dir, "2001 Job %s marked to be canceled.\n", Job); + bnet_fsend(dir, _("2001 Job %s marked to be canceled.\n"), Job); } } else { - bnet_fsend(dir, "2902 Error scanning cancel command.\n"); + bnet_fsend(dir, _("2902 Error scanning cancel command.\n")); } bnet_sig(dir, BNET_EOD); return 1; @@ -251,14 +354,16 @@ static int cancel_cmd(JCR *jcr) static int setdebug_cmd(JCR *jcr) { BSOCK *dir = jcr->dir_bsock; - int level; + int level, trace_flag; Dmsg1(110, "setdebug_cmd: %s", dir->msg); - if (sscanf(dir->msg, "setdebug=%d", &level) != 1 || level < 0) { - bnet_fsend(dir, "2991 Bad setdebug command: %s\n", dir->msg); - return 0; + if (sscanf(dir->msg, "setdebug=%d trace=%d", &level, &trace_flag) != 2 || level < 0) { + pm_strcpy(jcr->errmsg, dir->msg); + bnet_fsend(dir, "2991 Bad setdebug command: %s\n", jcr->errmsg); + return 0; } debug_level = level; + set_trace(trace_flag); return bnet_fsend(dir, OKsetdebug, level); } @@ -266,8 +371,19 @@ static int setdebug_cmd(JCR *jcr) static int estimate_cmd(JCR *jcr) { BSOCK *dir = jcr->dir_bsock; + char ed2[50]; + + if (sscanf(dir->msg, estimatecmd, &jcr->listing) != 1) { + pm_strcpy(jcr->errmsg, dir->msg); + Jmsg(jcr, M_FATAL, 0, _("Bad estimate command: %s"), jcr->errmsg); + bnet_fsend(dir, "2992 Bad estimate command.\n"); + return 0; + } make_estimate(jcr); - return bnet_fsend(dir, OKest, jcr->num_files_examined, jcr->JobBytes); + bnet_fsend(dir, OKest, jcr->num_files_examined, + edit_uint64_with_commas(jcr->JobBytes, ed2)); + bnet_sig(dir, BNET_EOD); + return 1; } /* @@ -279,44 +395,203 @@ static int job_cmd(JCR *jcr) POOLMEM *sd_auth_key; sd_auth_key = get_memory(dir->msglen); - if (sscanf(dir->msg, jobcmd, &jcr->JobId, jcr->Job, + if (sscanf(dir->msg, jobcmd, &jcr->JobId, jcr->Job, &jcr->VolSessionId, &jcr->VolSessionTime, sd_auth_key) != 5) { + pm_strcpy(jcr->errmsg, dir->msg); + Jmsg(jcr, M_FATAL, 0, _("Bad Job Command: %s"), jcr->errmsg); bnet_fsend(dir, BADjob); - Jmsg(jcr, M_FATAL, 0, _("Bad Job Command: %s\n"), dir->msg); free_pool_memory(sd_auth_key); return 0; } jcr->sd_auth_key = bstrdup(sd_auth_key); free_pool_memory(sd_auth_key); - if (jcr->use_win_backup_api) { - SetServicePrivileges(jcr); - } Dmsg2(120, "JobId=%d Auth=%s\n", jcr->JobId, jcr->sd_auth_key); - return bnet_fsend(dir, OKjob); + return bnet_fsend(dir, OKjob, HOST_OS, DISTNAME, DISTVER); +} + +static int runbefore_cmd(JCR *jcr) +{ + int stat; + BSOCK *dir = jcr->dir_bsock; + POOLMEM *cmd = get_memory(dir->msglen+1); + + Dmsg1(100, "runbefore_cmd: %s", dir->msg); + if (sscanf(dir->msg, runbefore, cmd) != 1) { + pm_strcpy(jcr->errmsg, dir->msg); + Jmsg1(jcr, M_FATAL, 0, _("Bad RunBeforeJob command: %s\n"), jcr->errmsg); + bnet_fsend(dir, "2905 Bad RunBeforeJob command.\n"); + free_memory(cmd); + return 0; + } + unbash_spaces(cmd); + + /* Run the command now */ + stat = run_cmd(jcr, cmd, "ClientRunBeforeJob"); + free_memory(cmd); + if (stat) { + bnet_fsend(dir, OKRunBefore); + return 1; + } else { + bnet_fsend(dir, "2905 Bad RunBeforeJob command.\n"); + return 0; + } +} + +static int runafter_cmd(JCR *jcr) +{ + BSOCK *dir = jcr->dir_bsock; + POOLMEM *msg = get_memory(dir->msglen+1); + + Dmsg1(100, "runafter_cmd: %s", dir->msg); + if (sscanf(dir->msg, runafter, msg) != 1) { + pm_strcpy(jcr->errmsg, dir->msg); + Jmsg1(jcr, M_FATAL, 0, _("Bad RunAfter command: %s\n"), jcr->errmsg); + bnet_fsend(dir, "2905 Bad RunAfterJob command.\n"); + free_memory(msg); + return 0; + } + unbash_spaces(msg); + if (jcr->RunAfterJob) { + free_pool_memory(jcr->RunAfterJob); + } + jcr->RunAfterJob = get_pool_memory(PM_FNAME); + pm_strcpy(jcr->RunAfterJob, msg); + free_pool_memory(msg); + return bnet_fsend(dir, OKRunAfter); +} + +static int run_cmd(JCR *jcr, char *cmd, const char *name) +{ + POOLMEM *ecmd = get_pool_memory(PM_FNAME); + int status; + BPIPE *bpipe; + char line[MAXSTRING]; + + ecmd = edit_job_codes(jcr, ecmd, cmd, ""); + bpipe = open_bpipe(ecmd, 0, "r"); + free_pool_memory(ecmd); + if (bpipe == NULL) { + berrno be; + Jmsg(jcr, M_FATAL, 0, _("%s could not execute. ERR=%s\n"), name, + be.strerror()); + return 0; + } + while (fgets(line, sizeof(line), bpipe->rfd)) { + int len = strlen(line); + if (len > 0 && line[len-1] != '\n') { + bstrncat(line, "\n", sizeof(line)); + } + Jmsg(jcr, M_INFO, 0, _("%s: %s"), name, line); + } + status = close_bpipe(bpipe); + if (status != 0) { + berrno be; + Jmsg(jcr, M_FATAL, 0, _("%s returned non-zero status=%d. ERR=%s\n"), name, + status, be.strerror(status)); + return 0; + } + return 1; } + #define INC_LIST 0 #define EXC_LIST 1 static void add_fname_to_list(JCR *jcr, char *fname, int list) { - char *p; - if (list == INC_LIST) { - add_fname_to_include_list((FF_PKT *)jcr->ff, 1, fname); - } else { - /* Skip leading options -- currently ignored */ - for (p=fname; *p && *p != ' '; p++) - { } - /* Skip spaces */ - for ( ; *p && *p == ' '; p++) - { } - add_fname_to_exclude_list((FF_PKT *)jcr->ff, p); + char *p, *q; + BPIPE *bpipe; + POOLMEM *fn; + FILE *ffd; + char buf[1000]; + int optlen; + int stat; + + /* Skip leading options -- currently ignored */ + for (p=fname; *p && *p != ' '; p++) + { } + /* Skip spaces, and q points to first space */ + for (q=NULL; *p && *p == ' '; p++) { + if (!q) { + q = p; + } + } + + switch (*p) { + case '|': + p++; /* skip over | */ + fn = get_pool_memory(PM_FNAME); + fn = edit_job_codes(jcr, fn, p, ""); + bpipe = open_bpipe(fn, 0, "r"); + free_pool_memory(fn); + if (!bpipe) { + Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"), + p, strerror(errno)); + return; + } + /* Copy File options */ + if (list == INC_LIST) { + *q = 0; /* terminate options */ + strcpy(buf, fname); + strcat(buf, " "); + optlen = strlen(buf); + } else { + optlen = 0; + } + while (fgets(buf+optlen, sizeof(buf)-optlen, bpipe->rfd)) { + strip_trailing_junk(buf); + if (list == INC_LIST) { + add_fname_to_include_list((FF_PKT *)jcr->ff, 1, buf); + } else { + add_fname_to_exclude_list((FF_PKT *)jcr->ff, buf); + } + } + if ((stat=close_bpipe(bpipe)) != 0) { + Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. RtnStat=%d ERR=%s\n"), + p, stat, strerror(errno)); + return; + } + break; + case '<': + p++; /* skip over < */ + if ((ffd = fopen(p, "r")) == NULL) { + berrno be; + Jmsg(jcr, M_FATAL, 0, _("Cannot open %s file: %s. ERR=%s\n"), + list==INC_LIST?"included":"excluded", p, be.strerror()); + return; + } + /* Copy File options */ + if (list == INC_LIST) { + *q = 0; /* terminate options */ + strcpy(buf, fname); + strcat(buf, " "); + optlen = strlen(buf); + } else { + optlen = 0; + } + while (fgets(buf+optlen, sizeof(buf)-optlen, ffd)) { + strip_trailing_junk(buf); + if (list == INC_LIST) { + add_fname_to_include_list((FF_PKT *)jcr->ff, 1, buf); + } else { + add_fname_to_exclude_list((FF_PKT *)jcr->ff, buf); + } + } + fclose(ffd); + break; + default: + if (list == INC_LIST) { + add_fname_to_include_list((FF_PKT *)jcr->ff, 1, fname); + } else { + add_fname_to_exclude_list((FF_PKT *)jcr->ff, p); + } + break; } } -/* - * +/* + * * Get list of files/directories to include from Director * */ @@ -334,6 +609,436 @@ static int include_cmd(JCR *jcr) return bnet_fsend(dir, OKinc); } +static bool init_fileset(JCR *jcr) +{ + FF_PKT *ff; + findFILESET *fileset; + + if (!jcr->ff) { + return false; + } + ff = (FF_PKT *)jcr->ff; + if (ff->fileset) { + return false; + } + fileset = (findFILESET *)malloc(sizeof(findFILESET)); + memset(fileset, 0, sizeof(findFILESET)); + ff->fileset = fileset; + fileset->state = state_none; + fileset->include_list.init(1, true); + fileset->exclude_list.init(1, true); + return true; +} + +static findFOPTS *start_options(FF_PKT *ff) +{ + int state = ff->fileset->state; + findINCEXE *incexe = ff->fileset->incexe; + + if (state != state_options) { + ff->fileset->state = state_options; + findFOPTS *fo = (findFOPTS *)malloc(sizeof(findFOPTS)); + memset(fo, 0, sizeof(findFOPTS)); + fo->regex.init(1, true); + fo->regexdir.init(1, true); + fo->regexfile.init(1, true); + fo->wild.init(1, true); + fo->wilddir.init(1, true); + fo->wildfile.init(1, true); + fo->base.init(1, true); + fo->fstype.init(1, true); + incexe->current_opts = fo; + incexe->opts_list.append(fo); + } + return incexe->current_opts; + +} + +/* + * Add fname to include/exclude fileset list. First check for + * | and < and if necessary perform command. + */ +static void add_file_to_fileset(JCR *jcr, const char *fname, findFILESET *fileset) +{ + char *p; + BPIPE *bpipe; + POOLMEM *fn; + FILE *ffd; + char buf[1000]; + int stat; + + p = (char *)fname; + switch (*p) { + case '|': + p++; /* skip over | */ + fn = get_pool_memory(PM_FNAME); + fn = edit_job_codes(jcr, fn, p, ""); + bpipe = open_bpipe(fn, 0, "r"); + free_pool_memory(fn); + if (!bpipe) { + Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"), + p, strerror(errno)); + return; + } + while (fgets(buf, sizeof(buf), bpipe->rfd)) { + strip_trailing_junk(buf); + fileset->incexe->name_list.append(bstrdup(buf)); + } + if ((stat=close_bpipe(bpipe)) != 0) { + Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. RtnStat=%d ERR=%s\n"), + p, stat, strerror(errno)); + return; + } + break; + case '<': + p++; /* skip over < */ + if ((ffd = fopen(p, "r")) == NULL) { + berrno be; + Jmsg(jcr, M_FATAL, 0, _("Cannot open FileSet input file: %s. ERR=%s\n"), + p, be.strerror()); + return; + } + while (fgets(buf, sizeof(buf), ffd)) { + strip_trailing_junk(buf); + fileset->incexe->name_list.append(bstrdup(buf)); + } + fclose(ffd); + break; + default: + fileset->incexe->name_list.append(bstrdup(fname)); + break; + } +} + + +static void add_fileset(JCR *jcr, const char *item) +{ + FF_PKT *ff = (FF_PKT *)jcr->ff; + findFILESET *fileset = ff->fileset; + int state = fileset->state; + findFOPTS *current_opts; + + /* Get code, optional subcode, and position item past the dividing space */ + Dmsg1(100, "%s\n", item); + int code = item[0]; + if (code != '\0') { + ++item; + } + int subcode = ' '; /* A space is always a valid subcode */ + if (item[0] != '\0' && item[0] != ' ') { + subcode = item[0]; + ++item; + } + if (*item == ' ') { + ++item; + } + + /* Skip all lines we receive after an error */ + if (state == state_error) { + return; + } + + /* + * The switch tests the code for validity. + * The subcode is always good if it is a space, otherwise we must confirm. + * We set state to state_error first assuming the subcode is invalid, + * requiring state to be set in cases below that handle subcodes. + */ + if (subcode != ' ') { + state = state_error; + } + switch (code) { + case 'I': + /* New include */ + fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE)); + memset(fileset->incexe, 0, sizeof(findINCEXE)); + fileset->incexe->opts_list.init(1, true); + fileset->incexe->name_list.init(1, true); + fileset->include_list.append(fileset->incexe); + break; + case 'E': + /* New exclude */ + fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE)); + memset(fileset->incexe, 0, sizeof(findINCEXE)); + fileset->incexe->opts_list.init(1, true); + fileset->incexe->name_list.init(1, true); + fileset->exclude_list.append(fileset->incexe); + break; + case 'N': + state = state_none; + break; + case 'F': + /* File item to either include/include list */ + state = state_include; + add_file_to_fileset(jcr, item, fileset); + break; + case 'R': + current_opts = start_options(ff); + regex_t *preg; + int rc; + char prbuf[500]; + preg = (regex_t *)malloc(sizeof(regex_t)); + if (current_opts->flags & FO_IGNORECASE) { + rc = regcomp(preg, item, REG_EXTENDED|REG_ICASE); + } else { + rc = regcomp(preg, item, REG_EXTENDED); + } + if (rc != 0) { + regerror(rc, preg, prbuf, sizeof(prbuf)); + regfree(preg); + free(preg); + Jmsg(jcr, M_FATAL, 0, "REGEX %s compile error. ERR=%s\n", item, prbuf); + state = state_error; + break; + } + state = state_options; + if (subcode == ' ') { + current_opts->regex.append(preg); + } else if (subcode == 'D') { + current_opts->regexdir.append(preg); + } else if (subcode == 'F') { + current_opts->regexfile.append(preg); + } else { + state = state_error; + } + break; + case 'B': + current_opts = start_options(ff); + current_opts->base.append(bstrdup(item)); + state = state_options; + break; + case 'X': + current_opts = start_options(ff); + current_opts->fstype.append(bstrdup(item)); + state = state_options; + break; + case 'W': + current_opts = start_options(ff); + state = state_options; + if (subcode == ' ') { + current_opts->wild.append(bstrdup(item)); + } else if (subcode == 'D') { + current_opts->wilddir.append(bstrdup(item)); + } else if (subcode == 'F') { + current_opts->wildfile.append(bstrdup(item)); + } else { + state = state_error; + } + break; + case 'O': + current_opts = start_options(ff); + set_options(current_opts, item); + state = state_options; + break; + case 'D': + current_opts = start_options(ff); + current_opts->reader = bstrdup(item); + state = state_options; + break; + case 'T': + current_opts = start_options(ff); + current_opts->writer = bstrdup(item); + state = state_options; + break; + default: + Jmsg(jcr, M_FATAL, 0, "Invalid FileSet command: %s\n", item); + state = state_error; + break; + } + ff->fileset->state = state; +} + +static bool term_fileset(JCR *jcr) +{ + FF_PKT *ff = (FF_PKT *)jcr->ff; + findFILESET *fileset = ff->fileset; + int i, j, k; + + for (i=0; iinclude_list.size(); i++) { + findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i); + Dmsg0(400, "I\n"); + for (j=0; jopts_list.size(); j++) { + findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j); + for (k=0; kregex.size(); k++) { + Dmsg1(400, "R %s\n", (char *)fo->regex.get(k)); + } + for (k=0; kregexdir.size(); k++) { + Dmsg1(400, "RD %s\n", (char *)fo->regexdir.get(k)); + } + for (k=0; kregexfile.size(); k++) { + Dmsg1(400, "RF %s\n", (char *)fo->regexfile.get(k)); + } + for (k=0; kwild.size(); k++) { + Dmsg1(400, "W %s\n", (char *)fo->wild.get(k)); + } + for (k=0; kwilddir.size(); k++) { + Dmsg1(400, "WD %s\n", (char *)fo->wilddir.get(k)); + } + for (k=0; kwildfile.size(); k++) { + Dmsg1(400, "WF %s\n", (char *)fo->wildfile.get(k)); + } + for (k=0; kbase.size(); k++) { + Dmsg1(400, "B %s\n", (char *)fo->base.get(k)); + } + for (k=0; kfstype.size(); k++) { + Dmsg1(400, "X %s\n", (char *)fo->fstype.get(k)); + } + if (fo->reader) { + Dmsg1(400, "D %s\n", fo->reader); + } + if (fo->writer) { + Dmsg1(400, "T %s\n", fo->writer); + } + } + for (j=0; jname_list.size(); j++) { + Dmsg1(400, "F %s\n", (char *)incexe->name_list.get(j)); + } + } + for (i=0; iexclude_list.size(); i++) { + findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i); + Dmsg0(400, "E\n"); + for (j=0; jopts_list.size(); j++) { + findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j); + for (k=0; kregex.size(); k++) { + Dmsg1(400, "R %s\n", (char *)fo->regex.get(k)); + } + for (k=0; kregexdir.size(); k++) { + Dmsg1(400, "RD %s\n", (char *)fo->regexdir.get(k)); + } + for (k=0; kregexfile.size(); k++) { + Dmsg1(400, "RF %s\n", (char *)fo->regexfile.get(k)); + } + for (k=0; kwild.size(); k++) { + Dmsg1(400, "W %s\n", (char *)fo->wild.get(k)); + } + for (k=0; kwilddir.size(); k++) { + Dmsg1(400, "WD %s\n", (char *)fo->wilddir.get(k)); + } + for (k=0; kwildfile.size(); k++) { + Dmsg1(400, "WF %s\n", (char *)fo->wildfile.get(k)); + } + for (k=0; kbase.size(); k++) { + Dmsg1(400, "B %s\n", (char *)fo->base.get(k)); + } + for (k=0; kfstype.size(); k++) { + Dmsg1(400, "X %s\n", (char *)fo->fstype.get(k)); + } + } + for (j=0; jname_list.size(); j++) { + Dmsg1(400, "F %s\n", (char *)incexe->name_list.get(j)); + } + } + return ff->fileset->state != state_error; +} + + +/* + * As an optimization, we should do this during + * "compile" time in filed/job.c, and keep only a bit mask + * and the Verify options. + */ +static void set_options(findFOPTS *fo, const char *opts) +{ + int j; + const char *p; + + for (p=opts; *p; p++) { + switch (*p) { + case 'a': /* alway replace */ + case '0': /* no option */ + break; + case 'e': + fo->flags |= FO_EXCLUDE; + break; + case 'f': + fo->flags |= FO_MULTIFS; + break; + case 'h': /* no recursion */ + fo->flags |= FO_NO_RECURSION; + break; + case 'H': /* no hard link handling */ + fo->flags |= FO_NO_HARDLINK; + break; + case 'i': + fo->flags |= FO_IGNORECASE; + break; + case 'M': /* MD5 */ + fo->flags |= FO_MD5; + break; + case 'n': + fo->flags |= FO_NOREPLACE; + break; + case 'p': /* use portable data format */ + fo->flags |= FO_PORTABLE; + break; + case 'R': /* Resource forks and Finder Info */ + fo->flags |= FO_HFSPLUS; + case 'r': /* read fifo */ + fo->flags |= FO_READFIFO; + break; + case 'S': + fo->flags |= FO_SHA1; + break; + case 's': + fo->flags |= FO_SPARSE; + break; + case 'm': + fo->flags |= FO_MTIMEONLY; + break; + case 'k': + fo->flags |= FO_KEEPATIME; + break; + case 'A': + fo->flags |= FO_ACL; + break; + case 'V': /* verify options */ + /* Copy Verify Options */ + for (j=0; *p && *p != ':'; p++) { + fo->VerifyOpts[j] = *p; + if (j < (int)sizeof(fo->VerifyOpts) - 1) { + j++; + } + } + fo->VerifyOpts[j] = 0; + break; + case 'w': + fo->flags |= FO_IF_NEWER; + break; + case 'Z': /* gzip compression */ + fo->flags |= FO_GZIP; + fo->GZIP_level = *++p - '0'; + Dmsg1(200, "Compression level=%d\n", fo->GZIP_level); + break; + default: + Emsg1(M_ERROR, 0, "Unknown include/exclude option: %c\n", *p); + break; + } + } +} + + +/* + * Director is passing his Fileset + */ +static int fileset_cmd(JCR *jcr) +{ + BSOCK *dir = jcr->dir_bsock; + + if (!init_fileset(jcr)) { + return 0; + } + while (bnet_recv(dir) >= 0) { + strip_trailing_junk(dir->msg); + Dmsg1(400, "Fileset: %s\n", dir->msg); + add_fileset(jcr, dir->msg); + } + if (!term_fileset(jcr)) { + return 0; + } + return bnet_fsend(dir, OKinc); +} + + /* * Get list of files to exclude from Director * @@ -363,14 +1068,22 @@ static int bootstrap_cmd(JCR *jcr) unlink(jcr->RestoreBootstrap); free_pool_memory(jcr->RestoreBootstrap); } - Mmsg(&fname, "%s/%s.%s.bootstrap", me->working_directory, me->hdr.name, + Mmsg(fname, "%s/%s.%s.bootstrap", me->working_directory, me->hdr.name, jcr->Job); Dmsg1(400, "bootstrap=%s\n", fname); jcr->RestoreBootstrap = fname; bs = fopen(fname, "a+"); /* create file */ if (!bs) { + berrno be; + /* + * Suck up what he is sending to us so that he will then + * read our error message. + */ + while (bnet_recv(dir) >= 0) + { } + Jmsg(jcr, M_FATAL, 0, _("Could not create bootstrap file %s: ERR=%s\n"), - jcr->RestoreBootstrap, strerror(errno)); + jcr->RestoreBootstrap, be.strerror()); free_pool_memory(jcr->RestoreBootstrap); jcr->RestoreBootstrap = NULL; set_jcr_job_status(jcr, JS_ErrorTerminated); @@ -394,50 +1107,110 @@ static int bootstrap_cmd(JCR *jcr) static int level_cmd(JCR *jcr) { BSOCK *dir = jcr->dir_bsock; - POOLMEM *level; + POOLMEM *level, *buf = NULL; struct tm tm; time_t mtime; + int mtime_only; level = get_memory(dir->msglen+1); Dmsg1(110, "level_cmd: %s", dir->msg); if (sscanf(dir->msg, "level = %s ", level) != 1) { - Jmsg1(jcr, M_FATAL, 0, _("Bad level command: %s\n"), dir->msg); - free_memory(level); - return 0; + goto bail_out; } + /* Base backup requested? */ + if (strcmp(level, "base") == 0) { + jcr->JobLevel = L_BASE; + /* Full backup requested? */ + } else if (strcmp(level, "full") == 0) { + jcr->JobLevel = L_FULL; /* - * Full backup requested - */ - if (strcmp(level, "full") == 0) { - jcr->save_level = L_FULL; - /* * Backup requested since