From: Eric Bollengier Date: Sat, 27 May 2006 09:35:38 +0000 (+0000) Subject: Done with item 9 = Implement new {Client}Run{Before|After}Job feature. X-Git-Tag: Release-2.0.0~853 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=d8110e00d7a2640f2e14a62acba1b9f634a5be61;p=bacula%2Fbacula Done with item 9 = Implement new {Client}Run{Before|After}Job feature. git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@3031 91ce42f0-d328-0410-95d8-f526ca767f89 --- diff --git a/bacula/src/dird/backup.c b/bacula/src/dird/backup.c index dc889976ec..83e51fc1b9 100644 --- a/bacula/src/dird/backup.c +++ b/bacula/src/dird/backup.c @@ -222,7 +222,7 @@ bool do_backup(JCR *jcr) } - if (!send_run_before_and_after_commands(jcr)) { + if (!send_runscripts_commands(jcr)) { goto bail_out; } diff --git a/bacula/src/dird/dird.c b/bacula/src/dird/dird.c index 57b5fee1ed..a5f65266e8 100644 --- a/bacula/src/dird/dird.c +++ b/bacula/src/dird/dird.c @@ -555,6 +555,19 @@ static int check_resources() job->storage->append(st); } } + /* Handle RunScripts alists specifically */ + if (jobdefs->RunScripts) { + RUNSCRIPT *rs, *elt; + + if (!job->RunScripts) { + job->RunScripts = New(alist(10, not_owned_by_alist)); + } + + foreach_alist(rs, jobdefs->RunScripts) { + elt = copy_runscript(rs); + job->RunScripts->append(elt); /* we have to free it */ + } + } /* Transfer default items from JobDefs Resource */ for (i=0; job_items[i].name; i++) { diff --git a/bacula/src/dird/dird.h b/bacula/src/dird/dird.h index 2dbcd73d1f..ce83228b70 100644 --- a/bacula/src/dird/dird.h +++ b/bacula/src/dird/dird.h @@ -37,6 +37,7 @@ #include "protos.h" #include "jobq.h" +#include "lib/runscript.h" /* Globals that dird.c exports */ extern DIRRES *director; /* Director resource */ diff --git a/bacula/src/dird/dird_conf.c b/bacula/src/dird/dird_conf.c index 1ce92662d8..8a2ae6ef78 100644 --- a/bacula/src/dird/dird_conf.c +++ b/bacula/src/dird/dird_conf.c @@ -62,7 +62,10 @@ void store_replace(LEX *lc, RES_ITEM *item, int index, int pass); void store_acl(LEX *lc, RES_ITEM *item, int index, int pass); static void store_device(LEX *lc, RES_ITEM *item, int index, int pass); static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass); - +static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass); +static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass); +static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass); +static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass); /* We build the current resource here as we are * scanning the resource configuration definition, @@ -266,11 +269,11 @@ RES_ITEM job_items[] = { {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false}, {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false}, {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true}, - {"runbeforejob", store_str, ITEM(res_job.RunBeforeJob), 0, 0, 0}, - {"runafterjob", store_str, ITEM(res_job.RunAfterJob), 0, 0, 0}, - {"runafterfailedjob", store_str, ITEM(res_job.RunAfterFailedJob), 0, 0, 0}, - {"clientrunbeforejob", store_str, ITEM(res_job.ClientRunBeforeJob), 0, 0, 0}, - {"clientrunafterjob", store_str, ITEM(res_job.ClientRunAfterJob), 0, 0, 0}, + {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, + {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, + {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, + {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, + {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0}, {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1}, {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false}, {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30}, @@ -279,6 +282,7 @@ RES_ITEM job_items[] = { {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, false}, {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0}, {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0}, + {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0}, {NULL, NULL, NULL, 0, 0, 0} }; @@ -589,15 +593,6 @@ void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fm if (res->res_job.RestoreBootstrap) { sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap)); } - if (res->res_job.RunBeforeJob) { - sendit(sock, _(" --> RunBefore=%s\n"), NPRT(res->res_job.RunBeforeJob)); - } - if (res->res_job.RunAfterJob) { - sendit(sock, _(" --> RunAfter=%s\n"), NPRT(res->res_job.RunAfterJob)); - } - if (res->res_job.RunAfterFailedJob) { - sendit(sock, _(" --> RunAfterFailed=%s\n"), NPRT(res->res_job.RunAfterFailedJob)); - } if (res->res_job.WriteBootstrap) { sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap)); } @@ -608,6 +603,18 @@ void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fm dump_resource(-R_STORAGE, (RES *)store, sendit, sock); } } + if (res->res_job.RunScripts) { + RUNSCRIPT *script; + foreach_alist(script, res->res_job.RunScripts) { + sendit(sock, _(" --> RunScript\n")); + sendit(sock, _(" --> Command=%s\n"), NPRT(script->command)); + sendit(sock, _(" --> Target=%s\n"), NPRT(script->target)); + sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success); + sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure); + sendit(sock, _(" --> AbortJobOnError=%u\n"), script->abort_on_error); + sendit(sock, _(" --> RunWhen=%u\n"), script->when); + } + } if (res->res_job.pool) { sendit(sock, _(" --> ")); dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock); @@ -1096,21 +1103,6 @@ void free_resource(RES *sres, int type) if (res->res_job.WriteBootstrap) { free(res->res_job.WriteBootstrap); } - if (res->res_job.RunBeforeJob) { - free(res->res_job.RunBeforeJob); - } - if (res->res_job.RunAfterJob) { - free(res->res_job.RunAfterJob); - } - if (res->res_job.RunAfterFailedJob) { - free(res->res_job.RunAfterFailedJob); - } - if (res->res_job.ClientRunBeforeJob) { - free(res->res_job.ClientRunBeforeJob); - } - if (res->res_job.ClientRunAfterJob) { - free(res->res_job.ClientRunAfterJob); - } if (res->res_job.selection_pattern) { free(res->res_job.selection_pattern); } @@ -1120,6 +1112,10 @@ void free_resource(RES *sres, int type) if (res->res_job.storage) { delete res->res_job.storage; } + if (res->res_job.RunScripts) { + free_runscripts(res->res_job.RunScripts); + delete res->res_job.RunScripts; + } break; case R_MSGS: if (res->res_msgs.mail_cmd) { @@ -1255,6 +1251,7 @@ void save_resource(int type, RES_ITEM *items, int pass) res->res_job.verify_job = res_all.res_job.verify_job; res->res_job.jobdefs = res_all.res_job.jobdefs; res->res_job.run_cmds = res_all.res_job.run_cmds; + res->res_job.RunScripts = res_all.res_job.RunScripts; break; case R_COUNTER: if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) { @@ -1544,3 +1541,194 @@ void store_acl(LEX *lc, RES_ITEM *item, int index, int pass) } set_bit(index, res_all.hdr.item_present); } + + +/* Store a runscript->when in a bit field */ +static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass) +{ + lex_get_token(lc, T_NAME); + + if (strcasecmp(lc->str, "before") == 0) { + *(int *)(item->value) = SCRIPT_Before ; + } else if (strcasecmp(lc->str, "after") == 0) { + *(int *)(item->value) = SCRIPT_After; + } else if (strcasecmp(lc->str, "always") == 0) { + *(int *)(item->value) = SCRIPT_Any; + } else { + scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str); + } + scan_to_eol(lc); +} + +/* Store a runscript->target + * + */ +static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass) +{ + lex_get_token(lc, T_STRING); + + if (pass == 2) { + if (strcmp(lc->str, "%c") == 0) { + ((RUNSCRIPT*) item->value)->set_target(lc->str); + } else if (strcmp(lc->str, "yes") == 0) { + ((RUNSCRIPT*) item->value)->set_target("%c"); + } else if (strcmp(lc->str, "no") == 0) { + /* store nothing, run on director */ + } else { + RES *res = GetResWithName(R_CLIENT, lc->str); + if (res == NULL) { + scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"), + lc->str, lc->line_no, lc->line); + } + + ((RUNSCRIPT*) item->value)->set_target(lc->str); + } + } + scan_to_eol(lc); +} + +/* Store a runscript->command in a bit field + * + */ +static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass) +{ + lex_get_token(lc, T_STRING); + + if (pass == 2) { + ((RUNSCRIPT*) item->value)->set_command(lc->str); + } + scan_to_eol(lc); +} + +static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass) +{ + lex_get_token(lc, T_STRING); + alist **runscripts = (alist **)(item->value) ; + + if (pass == 2) { + RUNSCRIPT *script = new_runscript(); + + script->set_command(lc->str); + + if (strcmp(item->name, "runbeforejob") == 0) { + script->when = SCRIPT_Before; + script->abort_on_error = true; + + } else if (strcmp(item->name, "runafterjob") == 0) { + script->when = SCRIPT_After; + script->on_success = true; + script->on_failure = false; + + } else if (strcmp(item->name, "clientrunafterjob") == 0) { + script->when = SCRIPT_After; + script->set_target("%c"); + script->on_success = true; + script->on_failure = false; + + } else if (strcmp(item->name, "clientrunbeforejob") == 0) { + script->when = SCRIPT_Before; + script->set_target("%c"); + script->abort_on_error = true; + + } else if (strcmp(item->name, "runafterfailedjob") == 0) { + script->when = SCRIPT_After; + script->on_failure = true; + script->on_success = false; + } + + if (*runscripts == NULL) { + *runscripts = New(alist(10, not_owned_by_alist)); + } + + (*runscripts)->append(script); + script->debug(); + } + + scan_to_eol(lc); +} + +static RUNSCRIPT res_runscript; + +/* + * new RunScript items + * name handler value code flags default_value + */ +static RES_ITEM runscript_items[] = { + {"command", store_runscript_cmd, (char **)&res_runscript, 0, ITEM_REQUIRED, 0}, + {"target", store_runscript_target, (char **)&res_runscript, 0, 0, 0}, + {"runsonsuccess", store_bool, (char **)&res_runscript.on_success, 0, 0, 0}, + {"runsonfailure", store_bool, (char **)&res_runscript.on_failure, 0, 0, 0}, + {"abortjobonerror", store_bool, (char **)&res_runscript.abort_on_error, 0, 0, 0}, + {"runswhen", store_runscript_when, (char **)&res_runscript.when, 0, 0, 0}, + {"runsonclient", store_runscript_target, (char **)&res_runscript, 0, 0, 0}, /* TODO */ + + {NULL, NULL, NULL, 0, 0, 0} +}; + +/* + * Store RunScript info + * + * Note, when this routine is called, we are inside a Job + * resource. We treat the RunScript like a sort of + * mini-resource within the Job resource. + */ +static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass) +{ + int token, i; + alist **runscripts = (alist **)(item->value) ; + + Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass); + + res_runscript.reset_default(); /* setting on_success, on_failure, abort_on_error */ + + token = lex_get_token(lc, T_SKIP_EOL); + + if (token != T_BOB) { + scan_err1(lc, _("Expecting open brace. Got %s"), lc->str); + } + + while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) { + if (token == T_EOB) { + break; + } + if (token != T_IDENTIFIER) { + scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str); + } + for (i=0; runscript_items[i].name; i++) { + if (strcasecmp(runscript_items[i].name, lc->str) == 0) { + token = lex_get_token(lc, T_SKIP_EOL); + if (token != T_EQUALS) { + scan_err1(lc, _("expected an equals, got: %s"), lc->str); + } + + /* Call item handler */ + runscript_items[i].handler(lc, &runscript_items[i], i, pass); + i = -1; + break; + } + } + + if (i >=0) { + scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str); + } + } + + if (pass == 2) { + if (res_runscript.command == NULL) { + scan_err2(lc, _("%s item is required in %s resource, but not found.\n"), + "command", "runscript"); + } + RUNSCRIPT *script = new_runscript(); + memcpy(script, &res_runscript, sizeof(RUNSCRIPT)); + + if (*runscripts == NULL) { + *runscripts = New(alist(10, not_owned_by_alist)); + } + + (*runscripts)->append(script); + script->debug(); + } + + scan_to_eol(lc); + set_bit(index, res_all.hdr.item_present); +} diff --git a/bacula/src/dird/dird_conf.h b/bacula/src/dird/dird_conf.h index 7117c38fe1..3b67dbb06e 100644 --- a/bacula/src/dird/dird_conf.h +++ b/bacula/src/dird/dird_conf.h @@ -83,6 +83,7 @@ struct FILESET; struct POOL; struct RUN; struct DEVICE; +struct RUNSCRIPT; /* * Director Resource @@ -280,11 +281,7 @@ public: int RestoreJobId; /* What -- JobId to restore */ char *RestoreWhere; /* Where on disk to restore -- directory */ char *RestoreBootstrap; /* Bootstrap file */ - char *RunBeforeJob; /* Run program before Job */ - char *RunAfterJob; /* Run program after Job */ - char *RunAfterFailedJob; /* Run program after Job that errs */ - char *ClientRunBeforeJob; /* Run client program before Job */ - char *ClientRunAfterJob; /* Run client program after Job */ + alist *RunScripts; /* Run {client} program {after|before} Job */ union { char *WriteBootstrap; /* Where to write bootstrap Job updates */ char *WriteVerifyList; /* List of changed files */ diff --git a/bacula/src/dird/fd_cmds.c b/bacula/src/dird/fd_cmds.c index 6c262a7341..febd1873d8 100644 --- a/bacula/src/dird/fd_cmds.c +++ b/bacula/src/dird/fd_cmds.c @@ -36,16 +36,15 @@ static char filesetcmd[] = "fileset%s\n"; /* set full fileset */ static char jobcmd[] = "JobId=%s Job=%s SDid=%u SDtime=%u Authorization=%s\n"; /* Note, mtime_only is not used here -- implemented as file option */ static char levelcmd[] = "level = %s%s mtime_only=%d\n"; -static char runbefore[] = "RunBeforeJob %s\n"; -static char runafter[] = "RunAfterJob %s\n"; - +static char runscript[] = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n"; +static char runbeforenow[]= "RunBeforeNow\n"; /* Responses received from File daemon */ -static char OKinc[] = "2000 OK include\n"; -static char OKjob[] = "2000 OK Job"; -static char OKlevel[] = "2000 OK level\n"; -static char OKRunBefore[] = "2000 OK RunBefore\n"; -static char OKRunAfter[] = "2000 OK RunAfter\n"; +static char OKinc[] = "2000 OK include\n"; +static char OKjob[] = "2000 OK Job"; +static char OKlevel[] = "2000 OK level\n"; +static char OKRunScript[] = "2000 OK RunScript\n"; +static char OKRunBeforeNow[] = "2000 OK RunBeforeNow\n"; /* Forward referenced functions */ @@ -458,33 +457,64 @@ bool send_bootstrap_file(JCR *jcr, BSOCK *sock) } /* - * Send ClientRunBeforeJob and ClientRunAfterJob to File daemon + * Send RunScripts to File daemon */ -int send_run_before_and_after_commands(JCR *jcr) +int send_runscripts_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; - } + RUNSCRIPT *cmd; + bool launch_before_cmd = false; + POOLMEM *ehost = get_pool_memory(PM_FNAME); + + Dmsg0(120, "bdird: sending runscripts to fd\n"); + + foreach_alist(cmd, jcr->job->RunScripts) { + + if (cmd->can_run_at_level(jcr->JobLevel) && cmd->target) { + + ehost = edit_job_codes(jcr, ehost, cmd->target, ""); + Dmsg2(200, "bdird: runscript %s -> %s\n", cmd->target, ehost); + + if (strcmp(ehost, jcr->client->hdr.name) == 0) { + pm_strcpy(msg, cmd->command); + bash_spaces(msg); + bnet_fsend(fd, runscript, cmd->on_success, + cmd->on_failure, + cmd->abort_on_error, + cmd->when, + msg); + + Dmsg1(120, "bdird: sending runscripts to fd '%s'\n", cmd->command); + + if (!response(jcr, fd, OKRunScript, "RunScript", DISPLAY_ERROR)) { + set_jcr_job_status(jcr, JS_ErrorTerminated); + free_pool_memory(msg); + free_pool_memory(ehost); + return 0; + } + launch_before_cmd=true; + } + /* + else { + send command to an other client + } + */ + } } - 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; + + /* TODO : we have to play with other client */ + if (launch_before_cmd) { + bnet_fsend(fd, runbeforenow); + if (!response(jcr, fd, OKRunBeforeNow, "RunBeforeNow", DISPLAY_ERROR)) { + set_jcr_job_status(jcr, JS_ErrorTerminated); + free_pool_memory(msg); + free_pool_memory(ehost); + return 0; } } free_pool_memory(msg); + free_pool_memory(ehost); return 1; } diff --git a/bacula/src/dird/job.c b/bacula/src/dird/job.c index 511581d387..670270b7c8 100644 --- a/bacula/src/dird/job.c +++ b/bacula/src/dird/job.c @@ -184,6 +184,13 @@ static void *job_thread(void *arg) set_jcr_job_status(jcr, JS_Canceled); } + /* TODO : check if it is used somewhere */ + if (jcr->job->RunScripts == NULL) + { + Dmsg0(200, "Warning, job->RunScripts is empty\n"); + jcr->job->RunScripts = New(alist(10, not_owned_by_alist)); + } + /* * Note, we continue, even if the job is canceled above. This * will permit proper setting of the job start record and @@ -243,28 +250,9 @@ static void *job_thread(void *arg) } else { - /* Run Job */ - if (jcr->job->RunBeforeJob) { - POOLMEM *before = get_pool_memory(PM_FNAME); - int status; - BPIPE *bpipe; - char line[MAXSTRING]; - - before = edit_job_codes(jcr, before, jcr->job->RunBeforeJob, ""); - bpipe = open_bpipe(before, 0, "r"); - free_pool_memory(before); - while (fgets(line, sizeof(line), bpipe->rfd)) { - Jmsg(jcr, M_INFO, 0, _("RunBefore: %s"), line); - } - status = close_bpipe(bpipe); - if (status != 0) { - berrno be; - Jmsg(jcr, M_FATAL, 0, _("RunBeforeJob error: ERR=%s\n"), be.strerror(status)); - set_jcr_job_status(jcr, JS_FatalError); - update_job_end_record(jcr); - goto bail_out; - } - } + /* Run any script BeforeJob on dird */ + run_scripts(jcr, jcr->job->RunScripts, "BeforeJob"); + /* * We re-update the job start record so that the start * time is set after the run before job. This avoids @@ -324,37 +312,9 @@ static void *job_thread(void *arg) Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->JobType); break; } - if ((jcr->job->RunAfterJob && jcr->JobStatus == JS_Terminated) || - (jcr->job->RunAfterFailedJob && jcr->JobStatus != JS_Terminated)) { - POOLMEM *after = get_pool_memory(PM_FNAME); - int status; - BPIPE *bpipe; - char line[MAXSTRING]; - - if (jcr->JobStatus == JS_Terminated) { - after = edit_job_codes(jcr, after, jcr->job->RunAfterJob, ""); - } else { - after = edit_job_codes(jcr, after, jcr->job->RunAfterFailedJob, ""); - } - bpipe = open_bpipe(after, 0, "r"); - free_pool_memory(after); - while (fgets(line, sizeof(line), bpipe->rfd)) { - Jmsg(jcr, M_INFO, 0, _("RunAfter: %s"), line); - } - status = close_bpipe(bpipe); - /* - * Note, if we get an error here, do not mark the - * job in error, simply report the error condition. - */ - if (status != 0) { - berrno be; - if (jcr->JobStatus == JS_Terminated) { - Jmsg(jcr, M_WARNING, 0, _("RunAfterJob error: ERR=%s\n"), be.strerror(status)); - } else { - Jmsg(jcr, M_FATAL, 0, _("RunAfterFailedJob error: ERR=%s\n"), be.strerror(status)); - } - } - } + + run_scripts(jcr, jcr->job->RunScripts, "AfterJob"); + /* Send off any queued messages */ if (jcr->msg_queue->size() > 0) { dequeue_messages(jcr); diff --git a/bacula/src/dird/protos.h b/bacula/src/dird/protos.h index 03b4e4d45f..212abb247a 100644 --- a/bacula/src/dird/protos.h +++ b/bacula/src/dird/protos.h @@ -79,7 +79,7 @@ extern int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId); extern int put_file_into_catalog(JCR *jcr, long file_index, char *fname, char *link, char *attr, int stream); extern void get_level_since_time(JCR *jcr, char *since, int since_len); -extern int send_run_before_and_after_commands(JCR *jcr); +extern int send_runscripts_commands(JCR *jcr); /* getmsg.c */ enum e_prtmsg { diff --git a/bacula/src/dird/restore.c b/bacula/src/dird/restore.c index c3e193980c..90fdb42a63 100644 --- a/bacula/src/dird/restore.c +++ b/bacula/src/dird/restore.c @@ -149,7 +149,7 @@ bool do_restore(JCR *jcr) } - if (!send_run_before_and_after_commands(jcr)) { + if (!send_runscripts_commands(jcr)) { restore_cleanup(jcr, JS_ErrorTerminated); return false; } diff --git a/bacula/src/dird/verify.c b/bacula/src/dird/verify.c index f1ab677631..3a46402ba6 100644 --- a/bacula/src/dird/verify.c +++ b/bacula/src/dird/verify.c @@ -262,7 +262,7 @@ bool do_verify(JCR *jcr) return false; } - if (!send_run_before_and_after_commands(jcr)) { + if (!send_runscripts_commands(jcr)) { return false; } diff --git a/bacula/src/filed/filed.h b/bacula/src/filed/filed.h index 906aab7c6c..2c59f340c1 100644 --- a/bacula/src/filed/filed.h +++ b/bacula/src/filed/filed.h @@ -26,6 +26,7 @@ #include "jcr.h" #include "acl.h" #include "protos.h" /* file daemon prototypes */ +#include "lib/runscript.h" #ifdef HAVE_LIBZ #include /* compression headers */ #else diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index 755c55163b..1e6835c780 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -55,9 +55,10 @@ 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 runscript_cmd(JCR *jcr); static int runbefore_cmd(JCR *jcr); static int runafter_cmd(JCR *jcr); -static bool run_cmd(JCR *jcr, char *cmd, const char *name); +static int runbeforenow_cmd(JCR *jcr); static void set_options(findFOPTS *fo, const char *opts); @@ -88,8 +89,10 @@ static struct s_cmds cmds[] = { {"storage ", storage_cmd, 0}, {"verify", verify_cmd, 0}, {"bootstrap", bootstrap_cmd, 0}, + {"RunBeforeNow", runbeforenow_cmd, 0}, {"RunBeforeJob", runbefore_cmd, 0}, {"RunAfterJob", runafter_cmd, 0}, + {"Run", runscript_cmd, 0}, {NULL, NULL} /* list terminator */ }; @@ -103,7 +106,7 @@ 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"; - +static char runscript[] = "Run OnSuccess=%u OnFailure=%u AbortOnError=%u When=%u Command=%s\n"; /* Responses sent to Director */ static char errmsg[] = "2999 Invalid command\n"; static char no_auth[] = "2998 No Authorization\n"; @@ -122,7 +125,10 @@ static char OKsetdebug[] = "2000 OK setdebug=%d\n"; static char BADjob[] = "2901 Bad Job\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 OKRunBeforeNow[] = "2000 OK RunBeforeNow\n"; static char OKRunAfter[] = "2000 OK RunAfter\n"; +static char OKRunScript[] = "2000 OK RunScript\n"; + /* Responses received from Storage Daemon */ static char OK_end[] = "3000 OK end\n"; @@ -130,7 +136,7 @@ static char OK_close[] = "3000 OK close Status = %d\n"; static char OK_open[] = "3000 OK open ticket = %d\n"; static char OK_data[] = "3000 OK data\n"; static char OK_append[] = "3000 OK append data\n"; -static char OKSDbootstrap[] = "3000 OK bootstrap\n"; +static char OKSDbootstrap[]= "3000 OK bootstrap\n"; /* Commands sent to Storage Daemon */ @@ -170,6 +176,7 @@ void *handle_client_request(void *dirp) jcr->dir_bsock = dir; jcr->ff = init_find_files(); jcr->start_time = time(NULL); + jcr->RunScripts = New(alist(10, not_owned_by_alist)); jcr->last_fname = get_pool_memory(PM_FNAME); jcr->last_fname[0] = 0; jcr->client_name = get_memory(strlen(my_name) + 1); @@ -227,9 +234,9 @@ void *handle_client_request(void *dirp) bnet_sig(jcr->store_bsock, BNET_TERMINATE); } - if (jcr->RunAfterJob && !job_canceled(jcr)) { - run_cmd(jcr, jcr->RunAfterJob, "ClientRunAfterJob"); - } + /* run after job */ + run_scripts(jcr, jcr->RunScripts, "ClientAfterJob"); + generate_daemon_event(jcr, "JobEnd"); dequeue_messages(jcr); /* send any queued messages */ @@ -415,6 +422,7 @@ static int runbefore_cmd(JCR *jcr) bool ok; BSOCK *dir = jcr->dir_bsock; POOLMEM *cmd = get_memory(dir->msglen+1); + RUNSCRIPT *script; Dmsg1(100, "runbefore_cmd: %s", dir->msg); if (sscanf(dir->msg, runbefore, cmd) != 1) { @@ -427,7 +435,12 @@ static int runbefore_cmd(JCR *jcr) unbash_spaces(cmd); /* Run the command now */ - ok = run_cmd(jcr, cmd, "ClientRunBeforeJob"); + script = new_runscript(); + script->set_command(cmd); + script->when = SCRIPT_Before; + ok = script->run(jcr, "ClientRunBeforeJob"); + free_runscript(script); + free_memory(cmd); if (ok) { bnet_fsend(dir, OKRunBefore); @@ -438,10 +451,19 @@ static int runbefore_cmd(JCR *jcr) } } +static int runbeforenow_cmd(JCR *jcr) +{ + BSOCK *dir = jcr->dir_bsock; + + run_scripts(jcr, jcr->RunScripts, "ClientBeforeJob"); + return bnet_fsend(dir, OKRunBeforeNow); +} + static int runafter_cmd(JCR *jcr) { BSOCK *dir = jcr->dir_bsock; POOLMEM *msg = get_memory(dir->msglen+1); + RUNSCRIPT *cmd; Dmsg1(100, "runafter_cmd: %s", dir->msg); if (sscanf(dir->msg, runafter, msg) != 1) { @@ -452,48 +474,50 @@ static int runafter_cmd(JCR *jcr) 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); + + cmd = new_runscript(); + cmd->set_command(msg); + cmd->on_success = true; + cmd->on_failure = false; + cmd->when = SCRIPT_After; + + jcr->RunScripts->append(cmd); + free_pool_memory(msg); return bnet_fsend(dir, OKRunAfter); } -static bool run_cmd(JCR *jcr, char *cmd, const char *name) +static int runscript_cmd(JCR *jcr) { - POOLMEM *ecmd = get_pool_memory(PM_FNAME); - int status; - BPIPE *bpipe; - char line[MAXSTRING]; + BSOCK *dir = jcr->dir_bsock; + POOLMEM *msg = get_memory(dir->msglen+1); - 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 false; - } - while (fgets(line, sizeof(line), bpipe->rfd)) { - int len = strlen(line); - if (len > 0 && line[len-1] == '\n') { - line[len-1] = 0; - } - Jmsg(jcr, M_INFO, 0, _("%s: %s\n"), 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 false; + RUNSCRIPT *cmd = new_runscript() ; + + Dmsg1(100, "runscript_cmd: '%s'\n", dir->msg); + if (sscanf(dir->msg, runscript, &cmd->on_success, + &cmd->on_failure, + &cmd->abort_on_error, + &cmd->when, + msg) != 5) { + pm_strcpy(jcr->errmsg, dir->msg); + Jmsg1(jcr, M_FATAL, 0, _("Bad RunScript command: %s\n"), jcr->errmsg); + bnet_fsend(dir, _("2905 Bad RunScript command.\n")); + free_runscript(cmd); + free_memory(msg); + return 0; } - return true; + unbash_spaces(msg); + + cmd->set_command(msg); + cmd->debug(); + jcr->RunScripts->append(cmd); + + free_pool_memory(msg); + return bnet_fsend(dir, OKRunScript); } + static bool init_fileset(JCR *jcr) { FF_PKT *ff; @@ -1304,6 +1328,10 @@ static int backup_cmd(JCR *jcr) Dmsg0(110, "Error in blast_data.\n"); } else { set_jcr_job_status(jcr, JS_Terminated); + + /* run shortly after end of data transmission */ + run_scripts(jcr, jcr->RunScripts, "ClientAfterJobShort"); + if (jcr->JobStatus != JS_Terminated) { bnet_suppress_error_messages(sd, 1); goto cleanup; /* bail out now */ @@ -1618,10 +1646,8 @@ static void filed_free_jcr(JCR *jcr) if (jcr->last_fname) { free_pool_memory(jcr->last_fname); } - if (jcr->RunAfterJob) { - free_pool_memory(jcr->RunAfterJob); - } - + free_runscripts(jcr->RunScripts); + delete jcr->RunScripts; return; } diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index 3808952c04..39a570820f 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -254,7 +254,7 @@ public: pthread_t heartbeat_id; /* id of heartbeat thread */ volatile BSOCK *hb_bsock; /* duped SD socket */ volatile BSOCK *hb_dir_bsock; /* duped DIR socket */ - POOLMEM *RunAfterJob; /* Command to run after job */ + alist *RunScripts; /* Commands to run before and after job */ bool pki_sign; /* Enable PKI Signatures? */ bool pki_encrypt; /* Enable PKI Encryption? */ DIGEST *digest; /* Last file's digest context */ @@ -322,8 +322,6 @@ public: }; - - /* * Structure for all daemons that keeps some summary * info on the last job run. diff --git a/bacula/src/lib/Makefile.in b/bacula/src/lib/Makefile.in index b7693cfc3b..76097488f9 100644 --- a/bacula/src/lib/Makefile.in +++ b/bacula/src/lib/Makefile.in @@ -23,7 +23,7 @@ first_rule: all dummy: LIBSRCS = alloc.c attr.c base64.c berrno.c bsys.c bget_msg.c \ - bnet.c bnet_server.c \ + bnet.c bnet_server.c runscript.c \ bpipe.c bshm.c bsnprintf.c btime.c \ cram-md5.c crc32.c crypto.c daemon.c edit.c fnmatch.c \ hmac.c idcache.c jcr.c lex.c alist.c dlist.c \ @@ -36,7 +36,7 @@ LIBSRCS = alloc.c attr.c base64.c berrno.c bsys.c bget_msg.c \ LIBOBJS = alloc.o attr.o base64.o berrno.o bsys.o bget_msg.o \ - bnet.o bnet_server.o \ + bnet.o bnet_server.o runscript.o \ bpipe.o bshm.o bsnprintf.o btime.o \ cram-md5.o crc32.o crypto.o daemon.o edit.o fnmatch.o \ hmac.o idcache.o jcr.o lex.o alist.o dlist.o \