From: Eric Bollengier Date: Wed, 14 Dec 2022 18:57:43 +0000 (+0100) Subject: Add runscript RunsWhen=Queue to create advanced job queue management scripts X-Git-Tag: Beta-15.0.0~262 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b2ad03cec03915674d8f31e4966d9c64cdb00c40;p=thirdparty%2Fbacula.git Add runscript RunsWhen=Queue to create advanced job queue management scripts --- diff --git a/bacula/src/dird/dird_conf.c b/bacula/src/dird/dird_conf.c index 1d4b0534b..bf4d7bfa0 100644 --- a/bacula/src/dird/dird_conf.c +++ b/bacula/src/dird/dird_conf.c @@ -2518,8 +2518,10 @@ static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass) *(uint32_t *)(item->value) = SCRIPT_AtJobCompletion; } else if (strcasecmp(lc->str, "always") == 0) { *(uint32_t *)(item->value) = SCRIPT_Any; + } else if (strcasecmp(lc->str, "queued") == 0) { + *(uint32_t *)(item->value) = SCRIPT_Queued; } else { - scan_err2(lc, _("Expect %s, got: %s"), "Before, AtJobCompletion, After, AfterVSS or Always", lc->str); + scan_err2(lc, _("Expect %s, got: %s"), "Before, AtJobCompletion, After, AfterVSS, Queued or Always", lc->str); } scan_to_eol(lc); } @@ -2653,6 +2655,16 @@ void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass) scan_to_eol(lc); } +/* Store a int32 in a bit field without modifing res_all.hdr + * We can also add an option to store_bool to skip res_all.hdr + */ +void store_runscript_int(LEX *lc, RES_ITEM *item, int index, int pass) +{ + lex_get_token(lc, T_PINT32); + *(int *)(item->value) = lc->pint32_val; + scan_to_eol(lc); +} + /* * new RunScript items * name handler value code flags default_value @@ -2667,6 +2679,7 @@ static RES_ITEM runscript_items[] = { {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_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 */ + {"timeout", store_runscript_int,{(char **)&res_runscript.wait_time}, 0, 0, 0}, {NULL, NULL, {0}, 0, 0, 0} }; diff --git a/bacula/src/dird/jobq.c b/bacula/src/dird/jobq.c index 5a9c40567..5bc30757a 100644 --- a/bacula/src/dird/jobq.c +++ b/bacula/src/dird/jobq.c @@ -398,14 +398,14 @@ void *jobq_server(void *arg) bool work = true; set_jcr_in_tsd(INVALID_JCR); - Dmsg0(2300, "Start jobq_server\n"); + Dmsg0(DT_SCHEDULER|50, "Start jobq_server\n"); P(jq->mutex); for (;;) { struct timeval tv; struct timezone tz; - Dmsg0(2300, "Top of for loop\n"); + Dmsg0(DT_SCHEDULER|50, "Top of for loop\n"); if (!work && !jq->quit) { gettimeofday(&tv, &tz); timeout.tv_nsec = 0; @@ -415,7 +415,7 @@ void *jobq_server(void *arg) /* * Wait 4 seconds, then if no more work, exit */ - Dmsg0(2300, "pthread_cond_timedwait()\n"); + Dmsg0(DT_SCHEDULER|50, "pthread_cond_timedwait()\n"); stat = pthread_cond_timedwait(&jq->work, &jq->mutex, &timeout); if (stat == ETIMEDOUT) { Dmsg0(2300, "timedwait timedout.\n"); @@ -423,7 +423,7 @@ void *jobq_server(void *arg) break; } else if (stat != 0) { /* This shouldn't happen */ - Dmsg0(2300, "This shouldn't happen\n"); + Dmsg0(DT_SCHEDULER|50, "This shouldn't happen\n"); jq->num_workers--; V(jq->mutex); return NULL; @@ -434,14 +434,14 @@ void *jobq_server(void *arg) /* * If anything is in the ready queue, run it */ - Dmsg0(2300, "Checking ready queue.\n"); + Dmsg0(DT_SCHEDULER|50, "Checking ready queue.\n"); while (!jq->ready_jobs->empty() && !jq->quit) { JCR *jcr; je = (jobq_item_t *)jq->ready_jobs->first(); jcr = je->jcr; jq->ready_jobs->remove(je); if (!jq->ready_jobs->empty()) { - Dmsg0(2300, "ready queue not empty start server\n"); + Dmsg0(DT_SCHEDULER|50, "ready queue not empty start server\n"); if (start_server(jq) != 0) { jq->num_workers--; V(jq->mutex); @@ -454,13 +454,13 @@ void *jobq_server(void *arg) jcr->my_thread_id = pthread_self(); jcr->set_killable(true); set_jcr_in_tsd(jcr); - Dmsg1(2300, "Took jobid=%d from ready and appended to run\n", jcr->JobId); + Dmsg1(DT_SCHEDULER|50, "Took jobid=%d from ready and appended to run\n", jcr->JobId); /* Release job queue lock */ V(jq->mutex); /* Call user's routine here */ - Dmsg3(2300, "Calling user engine for jobid=%d use=%d stat=%c\n", jcr->JobId, + Dmsg3(DT_SCHEDULER|50, "Calling user engine for jobid=%d use=%d stat=%c\n", jcr->JobId, jcr->use_count(), jcr->JobStatus); jq->engine(je->jcr); @@ -468,12 +468,12 @@ void *jobq_server(void *arg) remove_jcr_from_tsd(je->jcr); je->jcr->set_killable(false); - Dmsg2(2300, "Back from user engine jobid=%d use=%d.\n", jcr->JobId, + Dmsg2(DT_SCHEDULER|50, "Back from user engine jobid=%d use=%d.\n", jcr->JobId, jcr->use_count()); /* Reacquire job queue lock */ P(jq->mutex); - Dmsg0(200, "Done lock mutex after running job. Release locks.\n"); + Dmsg0(DT_SCHEDULER|50, "Done lock mutex after running job. Release locks.\n"); jq->running_jobs->remove(je); /* * Release locks if acquired. Note, they will not have @@ -493,7 +493,7 @@ void *jobq_server(void *arg) } /* Clean up and release old jcr */ - Dmsg2(2300, "====== Termination job=%d use_cnt=%d\n", jcr->JobId, jcr->use_count()); + Dmsg2(DT_SCHEDULER|50, "====== Termination job=%d use_cnt=%d\n", jcr->JobId, jcr->use_count()); jcr->SDJobStatus = 0; V(jq->mutex); /* release internal lock */ free_jcr(jcr); @@ -504,7 +504,7 @@ void *jobq_server(void *arg) * If any job in the wait queue can be run, * move it to the ready queue */ - Dmsg0(2300, "Done check ready, now check wait queue.\n"); + Dmsg0(DT_SCHEDULER|50, "Done check ready, now check wait queue.\n"); if (!jq->waiting_jobs->empty() && !jq->quit) { int Priority; bool running_allow_mix = false; @@ -512,11 +512,11 @@ void *jobq_server(void *arg) jobq_item_t *re = (jobq_item_t *)jq->running_jobs->first(); if (re) { Priority = re->jcr->JobPriority; - Dmsg2(2300, "JobId %d is running. Look for pri=%d\n", + Dmsg2(DT_SCHEDULER|50, "JobId %d is running. Look for pri=%d\n", re->jcr->JobId, Priority); running_allow_mix = true; for ( ; re; ) { - Dmsg2(2300, "JobId %d is also running with %s\n", + Dmsg2(DT_SCHEDULER|50, "JobId %d is also running with %s\n", re->jcr->JobId, re->jcr->job->allow_mixed_priority ? "mix" : "no mix"); if (!re->jcr->job->allow_mixed_priority) { @@ -525,11 +525,11 @@ void *jobq_server(void *arg) } re = (jobq_item_t *)jq->running_jobs->next(re); } - Dmsg1(2300, "The running job(s) %s mixing priorities.\n", + Dmsg1(DT_SCHEDULER|50, "The running job(s) %s mixing priorities.\n", running_allow_mix ? "allow" : "don't allow"); } else { Priority = je->jcr->JobPriority; - Dmsg1(2300, "No job running. Look for Job pri=%d\n", Priority); + Dmsg1(DT_SCHEDULER|50, "No job running. Look for Job pri=%d\n", Priority); } /* * Walk down the list of waiting jobs and attempt @@ -539,8 +539,55 @@ void *jobq_server(void *arg) /* je is current job item on the queue, jn is the next one */ JCR *jcr = je->jcr; jobq_item_t *jn = (jobq_item_t *)jq->waiting_jobs->next(je); - - Dmsg4(2300, "Examining Job=%d JobPri=%d want Pri=%d (%s)\n", + btime_t now = time(NULL); + int interval = 90; + /* When we debug, we can spend less time to wait */ + if (chk_dbglvl(DT_SCHEDULER)) { + interval = 10; + } + if (jcr->next_qrunscript_execution >= 0 && // first time + jcr->next_qrunscript_execution <= now) // we test again after some time + { + /* We test every 90s */ + jcr->next_qrunscript_execution = now + interval; + int runcode = run_scripts_get_code(jcr, jcr->job->RunScripts, NT_("Queued")); + /* We use the exit code of the runscripts to determine what to do now. + * we can wait, start the job, or do other things + */ + switch (runcode) { + case -1: // No script to run + jcr->next_qrunscript_execution = -1; + break; + case 0: + Jmsg(jcr, M_INFO, 0, _("User defined resources are available.\n")); + break; + case 1: + if (jcr->getJobStatus() != JS_WaitUser) { + Jmsg(jcr, M_INFO, 0, _("The Job will wait for user defined resources to be available.\n")); + } + jcr->setJobStatus(JS_WaitUser); + if (!job_canceled(jcr)) { + je = jn; /* point to next waiting job */ + continue; + } + break; + case 2: // skip priority checks + jcr->JobPriority = 9999; + Jmsg(jcr, M_INFO, 0, _("Job Priority adjusted.\n")); + break; + default: // Incorrect code, must review the script + jcr->next_qrunscript_execution = -1; + Jmsg(jcr, M_WARNING, 0, _("Incorrect return code %d for user defined resource checking script.\n"), runcode); + break; + } + } else if (jcr->next_qrunscript_execution > 0) { + /* We have to wait, the job is not ready to be tested again */ + if (!job_canceled(jcr)) { + je = jn; /* point to next waiting job */ + continue; + } + } + Dmsg4(DT_SCHEDULER|50, "Examining Job=%d JobPri=%d want Pri=%d (%s)\n", jcr->JobId, jcr->JobPriority, Priority, jcr->job->allow_mixed_priority ? "mix" : "no mix"); @@ -568,33 +615,33 @@ void *jobq_server(void *arg) */ jq->waiting_jobs->remove(je); jq->ready_jobs->append(je); - Dmsg1(2300, "moved JobId=%d from wait to ready queue\n", je->jcr->JobId); + Dmsg1(DT_SCHEDULER|50, "moved JobId=%d from wait to ready queue\n", je->jcr->JobId); je = jn; /* Point to next waiting job */ } /* end for loop */ } /* end if */ - Dmsg0(2300, "Done checking wait queue.\n"); + Dmsg0(DT_SCHEDULER|50, "Done checking wait queue.\n"); /* * If no more ready work and we are asked to quit, then do it */ if (jq->ready_jobs->empty() && jq->quit) { jq->num_workers--; if (jq->num_workers == 0) { - Dmsg0(2300, "Wake up destroy routine\n"); + Dmsg0(DT_SCHEDULER|50, "Wake up destroy routine\n"); /* Wake up destroy routine if he is waiting */ pthread_cond_broadcast(&jq->work); } break; } - Dmsg0(2300, "Check for work request\n"); + Dmsg0(DT_SCHEDULER|50, "Check for work request\n"); /* * If no more work requests, and we waited long enough, quit */ - Dmsg2(2300, "timedout=%d read empty=%d\n", timedout, + Dmsg2(DT_SCHEDULER|50, "timedout=%d read empty=%d\n", timedout, jq->ready_jobs->empty()); if (jq->ready_jobs->empty() && timedout) { - Dmsg0(2300, "break big loop\n"); + Dmsg0(DT_SCHEDULER|50, "break big loop\n"); jq->num_workers--; break; } @@ -613,12 +660,12 @@ void *jobq_server(void *arg) /* Recompute work as something may have changed in last 2 secs */ work = !jq->ready_jobs->empty() || !jq->waiting_jobs->empty(); } - Dmsg1(2300, "Loop again. work=%d\n", work); + Dmsg1(DT_SCHEDULER|50, "Loop again. work=%d\n", work); } /* end of big for loop */ - Dmsg0(200, "unlock mutex\n"); + Dmsg0(DT_SCHEDULER|50, "unlock mutex\n"); V(jq->mutex); - Dmsg0(2300, "End jobq_server\n"); + Dmsg0(DT_SCHEDULER|50, "End jobq_server\n"); return NULL; } @@ -661,7 +708,7 @@ static bool reschedule_job(JCR *jcr, jobq_t *jq, jobq_item_t *je) jcr->sched_time = now + jcr->job->RescheduleInterval; bstrftime(dt, sizeof(dt), now); bstrftime(dt2, sizeof(dt2), jcr->sched_time); - Dmsg4(2300, "Rescheduled Job %s to re-run in %d seconds.(now=%u,then=%u)\n", jcr->Job, + Dmsg4(DT_SCHEDULER|50, "Rescheduled Job %s to re-run in %d seconds.(now=%u,then=%u)\n", jcr->Job, (int)jcr->job->RescheduleInterval, now, jcr->sched_time); Jmsg(jcr, M_INFO, 0, _("Rescheduled Job %s at %s to re-run in %d seconds (%s).\n"), jcr->Job, dt, (int)jcr->job->RescheduleInterval, dt2); @@ -675,7 +722,7 @@ static bool reschedule_job(JCR *jcr, jobq_t *jq, jobq_item_t *je) } /* Only jobs with no output or Incomplete jobs can run on same JCR */ if (jcr->JobBytes == 0 || jcr->rerunning) { - Dmsg2(2300, "Requeue job=%d use=%d\n", jcr->JobId, jcr->use_count()); + Dmsg2(DT_SCHEDULER|50, "Requeue job=%d use=%d\n", jcr->JobId, jcr->use_count()); V(jq->mutex); /* * Special test here since a Virtual Full gets marked @@ -758,12 +805,12 @@ static bool reschedule_job(JCR *jcr, jobq_t *jq, jobq_item_t *je) njcr->messages = jcr->messages; njcr->spool_data = jcr->spool_data; njcr->write_part_after_job = jcr->write_part_after_job; - Dmsg0(2300, "Call to run new job\n"); + Dmsg0(DT_SCHEDULER|50, "Call to run new job\n"); V(jq->mutex); run_job(njcr); /* This creates a "new" job */ free_jcr(njcr); /* release "new" jcr */ P(jq->mutex); - Dmsg0(2300, "Back from running new job.\n"); + Dmsg0(DT_SCHEDULER|50, "Back from running new job.\n"); } return false; } diff --git a/bacula/src/dird/ua_status.c b/bacula/src/dird/ua_status.c index 6c31738fa..6b921913c 100644 --- a/bacula/src/dird/ua_status.c +++ b/bacula/src/dird/ua_status.c @@ -1412,6 +1412,9 @@ static void list_running_jobs(UAContext *ua) } msg = emsg.c_str(); break; + case JS_WaitUser: + msg = _("is waiting on queued user defined script"); + break; case JS_WaitStoreRes: msg = _("is waiting on max Storage jobs"); break; diff --git a/bacula/src/jcr.h b/bacula/src/jcr.h index 5fef5c234..2993f3904 100644 --- a/bacula/src/jcr.h +++ b/bacula/src/jcr.h @@ -90,6 +90,7 @@ #define JS_WaitStoreRes 's' /* Waiting for storage resource */ #define JS_WaitStartTime 't' /* Waiting for start time */ #define JS_CloudUpload 'u' /* Cloud upload */ +#define JS_WaitUser 'v' /* Waiting for User */ #define JS_CloudDownload 'w' /* Cloud download */ /* Helper for more descriptive job status @@ -97,7 +98,8 @@ */ enum { JOB_TASK_ZERO = 0, - JOB_TASK_BEFORE_SCRIPT = 100, + JOB_TASK_QUEUED = 100, + JOB_TASK_BEFORE_SCRIPT, JOB_TASK_ENDJOB_SCRIPT, JOB_TASK_AFTER_SCRIPT }; @@ -397,6 +399,7 @@ public: volatile int32_t FDJobStatus; /* File daemon Job Status */ uint32_t ExpectedFiles; /* Expected restore files */ uint32_t MediaId; /* DB record IDs associated with this job */ + btime_t next_qrunscript_execution;/* Next time we can execute the Queued runscript */ int32_t FileIndex; /* Last FileIndex processed */ utime_t MaxRunSchedTime; /* max run time in seconds from Initial Scheduled time */ POOLMEM *fname; /* name to put into catalog */ diff --git a/bacula/src/lib/jcr.c b/bacula/src/lib/jcr.c index 3e5afa4b7..600b37caf 100644 --- a/bacula/src/lib/jcr.c +++ b/bacula/src/lib/jcr.c @@ -85,7 +85,8 @@ const struct job_task job_task_map[] = { { JOB_TASK_ZERO, "" }, { JOB_TASK_BEFORE_SCRIPT, _("executing Before Job Scripts") }, { JOB_TASK_ENDJOB_SCRIPT, _("executing End Job Scripts") }, - { JOB_TASK_AFTER_SCRIPT, _("executing After Job Scripts") } + { JOB_TASK_AFTER_SCRIPT, _("executing After Job Scripts") }, + { JOB_TASK_QUEUED, _("executing Queued Job Scripts") } }; const uint32_t job_task_map_size = sizeof(job_task_map) / sizeof(job_task); @@ -840,6 +841,7 @@ static void update_wait_time(JCR *jcr, int newJobStatus) case JS_WaitClientRes: case JS_WaitMaxJobs: case JS_WaitPriority: + case JS_WaitUser: enter_in_waittime = true; break; default: @@ -862,6 +864,7 @@ static void update_wait_time(JCR *jcr, int newJobStatus) case JS_WaitClientRes: case JS_WaitMaxJobs: case JS_WaitPriority: + case JS_WaitUser: if (!enter_in_waittime) { /* we get out the wait time */ jcr->wait_time_sum += (time(NULL) - jcr->wait_time); jcr->wait_time = 0; diff --git a/bacula/src/lib/runscript.c b/bacula/src/lib/runscript.c index 22e5b6a6a..2f3b0d029 100644 --- a/bacula/src/lib/runscript.c +++ b/bacula/src/lib/runscript.c @@ -61,6 +61,7 @@ void RUNSCRIPT::reset_default(bool free_strings) on_failure = false; fail_on_error = true; when = SCRIPT_Never; + wait_time = 0; /* Infinite by default */ old_proto = false; /* TODO: drop this with bacula 1.42 */ job_code_callback = NULL; } @@ -109,6 +110,8 @@ int run_scripts(JCR *jcr, alist *runscripts, const char *label) when = SCRIPT_AfterVSS; } else if (bstrcmp(label, NT_("AtJobCompletion"))) { when = SCRIPT_AtJobCompletion; + } else if (bstrcmp(label, NT_("Queued"))) { + when = SCRIPT_Queued; } else { when = SCRIPT_After; } @@ -203,6 +206,67 @@ int run_scripts(JCR *jcr, alist *runscripts, const char *label) return ret; } + +/* Function similar to run_scripts() but we report the exit status of the script + * -1: no script to run + * 0: ok + * 1-255: exit code from the script + */ +int run_scripts_get_code(JCR *jcr, alist *runscripts, const char *label) +{ + Dmsg2(200, "runscript: running all RUNSCRIPT object (%s) JobStatus=%c\n", label, jcr->JobStatus); + + RUNSCRIPT *script; + bool runit; + int ret = -1; + int when; + + if (strstr(label, NT_("Queued"))) { + when = SCRIPT_Queued; + } else { + when = SCRIPT_Never; + } + + if (runscripts == NULL) { + Dmsg0(100, "runscript: WARNING RUNSCRIPTS list is NULL\n"); + return -1; + } + + foreach_alist(script, runscripts) { + Dmsg2(200, "runscript: try to run %s:%s\n", NPRT(script->target), NPRT(script->command)); + runit = false; + + if ((script->when & SCRIPT_Queued) && (when & SCRIPT_Queued)) { + if (jcr->job_started == false) { + Dmsg4(200, "runscript: Run it because SCRIPT_Queued (%s,%i,%i,%c)\n", + script->command, script->on_success, script->on_failure, + jcr->JobStatus ); + if (!script->wait_time) { + script->wait_time = 15; // Set a maximum of 15s to not block everything + } + /* Set job task code */ + jcr->job_task = JOB_TASK_QUEUED; + runit = true; + } + } + + if (!script->is_local()) { + runit = false; + } + + /* we execute it */ + if (runit) { + berrno be; + int aret = script->run_get_code(jcr, label); + ret = MAX(ret, be.code(aret)); // We return the one with the biggest return code + } + } + + /* Script ended, reset operation code */ + jcr->job_task = JOB_TASK_ZERO; + return ret; +} + bool RUNSCRIPT::is_local() { if (!target || (strcmp(target, "") == 0)) { @@ -245,11 +309,15 @@ void RUNSCRIPT::set_target(const char *client_name) pm_strcpy(target, client_name); } -bool RUNSCRIPT::run(JCR *jcr, const char *name) +/* + * -1: execution problem + * 0..255 excution done + */ +int RUNSCRIPT::run_get_code(JCR *jcr, const char *name) { Dmsg1(100, "runscript: running a RUNSCRIPT object type=%d\n", cmd_type); POOLMEM *ecmd = get_pool_memory(PM_FNAME); - int status; + int status = -1; BPIPE *bpipe; char line[MAXSTRING]; @@ -260,48 +328,46 @@ bool RUNSCRIPT::run(JCR *jcr, const char *name) switch (cmd_type) { case SHELL_CMD: - bpipe = open_bpipe(ecmd, 0, "r"); - if (bpipe == NULL) { - berrno be; - Jmsg(jcr, M_ERROR, 0, _("Runscript: %s could not execute. ERR=%s\n"), name, - be.bstrerror()); - goto bail_out; - } - while (fgets(line, sizeof(line), bpipe->rfd)) { - int len = strlen(line); - if (len > 0 && line[len-1] == '\n') { - line[len-1] = 0; + bpipe = open_bpipe(ecmd, wait_time, "r"); + if (bpipe) { + 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); } - Jmsg(jcr, M_INFO, 0, _("%s: %s\n"), name, line); + status = close_bpipe(bpipe); } - status = close_bpipe(bpipe); - if (status != 0) { - berrno be; - Jmsg(jcr, M_ERROR, 0, _("Runscript: %s returned non-zero status=%d. ERR=%s\n"), name, - be.code(status), be.bstrerror(status)); - goto bail_out; - } - Dmsg0(100, "runscript OK\n"); break; case CONSOLE_CMD: if (console_command) { /* can we run console command? */ if (!console_command(jcr, ecmd)) { /* yes, do so */ - goto bail_out; + status = 0; + } else { + status = 1; } } break; } + Dmsg1(100, "runscript status=%d\n", status); free_pool_memory(ecmd); - return true; + return status; +} -bail_out: - free_pool_memory(ecmd); - /* cancel running job properly */ - if (fail_on_error) { - jcr->setJobStatus(JS_ErrorTerminated); +bool RUNSCRIPT::run(JCR *jcr, const char *name) +{ + int code = run_get_code(jcr, name); + if (code != 0) { + berrno be; + Jmsg(jcr, M_ERROR, 0, _("Runscript: %s returned non-zero status=%d. ERR=%s\n"), name, + be.code(code), be.bstrerror(code)); + + if (fail_on_error) { + jcr->setJobStatus(JS_ErrorTerminated); + } } - Dmsg1(100, "runscript failed. fail_on_error=%d\n", fail_on_error); - return false; + return code == 0; } void free_runscripts(alist *runscripts) @@ -326,6 +392,7 @@ void RUNSCRIPT::debug() Dmsg1(200, _(" --> RunOnFailure=%u\n"), on_failure); Dmsg1(200, _(" --> FailJobOnError=%u\n"), fail_on_error); Dmsg1(200, _(" --> RunWhen=%u\n"), when); + Dmsg1(200, _(" --> Timeout=%u\n"), wait_time); } void RUNSCRIPT::set_job_code_callback(job_code_callback_t arg_job_code_callback) diff --git a/bacula/src/lib/runscript.h b/bacula/src/lib/runscript.h index a33154e9b..86a5d28fd 100644 --- a/bacula/src/lib/runscript.h +++ b/bacula/src/lib/runscript.h @@ -46,10 +46,11 @@ */ enum { SCRIPT_Never = 0, - SCRIPT_After = (1<<0), /* AfterJob */ - SCRIPT_Before = (1<<1), /* BeforeJob */ - SCRIPT_AfterVSS = (1<<2), /* BeforeJob and After VSS */ - SCRIPT_AtJobCompletion = (1<<3), /* Before AfterJob on the Director, take status in account */ + SCRIPT_After = (1<<0), /* AfterJob */ + SCRIPT_Before = (1<<1), /* BeforeJob */ + SCRIPT_AfterVSS = (1<<2), /* BeforeJob and After VSS */ + SCRIPT_AtJobCompletion = (1<<3), /* Before AfterJob on the Director, take status in account */ + SCRIPT_Queued = (1<<4), /* When the job is inside the JobQ */ SCRIPT_Any = SCRIPT_Before | SCRIPT_After }; @@ -67,6 +68,7 @@ public: POOLMEM *target; /* host target */ int when; /* SCRIPT_Before|Script_After BEFORE/AFTER JOB*/ int cmd_type; /* Command type -- Shell, Console */ + int wait_time; /* Seconds to wait for the execution */ char level; /* Base|Full|Incr...|All (NYI) */ bool on_success; /* execute command on job success (After) */ bool on_failure; /* execute command on job failure (After) */ @@ -77,6 +79,7 @@ public: /* Optional callback function passed to edit_job_code */ alist *commands; /* Use during parsing */ bool run(JCR *job, const char *name=""); /* name must contain "Before" or "After" keyword */ + int run_get_code(JCR *job, const char *name=""); /* name must contain "Before" or "After" keyword */ bool can_run_at_level(int JobLevel) { return true;}; /* TODO */ void set_command(const char *cmd, int cmd_type = SHELL_CMD); void set_target(const char *client_name); @@ -96,6 +99,9 @@ RUNSCRIPT *copy_runscript(RUNSCRIPT *src); /* launch each script from runscripts*/ int run_scripts(JCR *jcr, alist *runscripts, const char *name); +/* launch each script from runscripts, only for queued scripts */ +int run_scripts_get_code(JCR *jcr, alist *runscripts, const char *label); + /* free RUNSCRIPT (and all POOLMEM) */ void free_runscript(RUNSCRIPT *script);