/*
Bacula® - The Network Backup Solution
- Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
+ Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
The main author of Bacula is Kern Sibbald, with contributions from
many others, a complete list can be found in the file AUTHORS.
This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
+ modify it under the terms of version three of the GNU Affero General Public
License as published by the Free Software Foundation and included
in the file LICENSE.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Affero General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
- Bacula® is a registered trademark of John Walker.
+ Bacula® is a registered trademark of Kern Sibbald.
The licensor of Bacula is the Free Software Foundation Europe
(FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
Switzerland, email:ftf@fsfeurope.org.
*
* Kern Sibbald, October MM
*
- * Version $Id$
*/
#include "bacula.h"
static void *job_thread(void *arg);
static void job_monitor_watchdog(watchdog_t *self);
static void job_monitor_destructor(watchdog_t *self);
-static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr);
-static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr);
+static bool job_check_maxwaittime(JCR *jcr);
+static bool job_check_maxruntime(JCR *jcr);
+static bool job_check_maxrunschedtime(JCR *jcr);
/* Imported subroutines */
extern void term_scheduler();
int errstat;
jcr->lock();
- sm_check(__FILE__, __LINE__, true);
+ Dsm_check(100);
init_msg(jcr, jcr->messages);
/* Initialize termination condition variable */
if ((errstat = pthread_cond_init(&jcr->term_wait, NULL)) != 0) {
berrno be;
Jmsg1(jcr, M_FATAL, 0, _("Unable to init job cond variable: ERR=%s\n"), be.bstrerror(errstat));
+ jcr->unlock();
goto bail_out;
}
jcr->term_wait_inited = true;
create_unique_job_name(jcr, jcr->job->name());
- set_jcr_job_status(jcr, JS_Created);
+ jcr->setJobStatus(JS_Created);
jcr->unlock();
/*
* Open database
*/
Dmsg0(100, "Open database\n");
- jcr->db=db_init(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
- jcr->catalog->db_user,
- jcr->catalog->db_password, jcr->catalog->db_address,
- jcr->catalog->db_port, jcr->catalog->db_socket,
- jcr->catalog->mult_db_connections);
+ jcr->db = db_init_database(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
+ jcr->catalog->db_user, jcr->catalog->db_password,
+ jcr->catalog->db_address, jcr->catalog->db_port,
+ jcr->catalog->db_socket, jcr->catalog->mult_db_connections,
+ jcr->catalog->disable_batch_insert);
if (!jcr->db || !db_open_database(jcr, jcr->db)) {
Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
jcr->catalog->db_name);
goto bail_out;
}
Dmsg0(150, "DB opened\n");
-
if (!jcr->fname) {
jcr->fname = get_pool_memory(PM_FNAME);
}
jcr->pool_source = get_pool_memory(PM_MESSAGE);
pm_strcpy(jcr->pool_source, _("unknown source"));
}
- Dmsg2(500, "pool=%s (From %s)\n", jcr->pool->name(), jcr->pool_source);
- if (jcr->JobType == JT_MIGRATE) {
+
+ if (jcr->JobReads()) {
if (!jcr->rpool_source) {
jcr->rpool_source = get_pool_memory(PM_MESSAGE);
pm_strcpy(jcr->rpool_source, _("unknown source"));
jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel);
generate_daemon_event(jcr, "JobStart");
+ new_plugins(jcr); /* instantiate plugins for this jcr */
+ generate_plugin_event(jcr, bDirEventJobStart);
if (job_canceled(jcr)) {
goto bail_out;
}
+ if (jcr->JobReads() && !jcr->rstorage) {
+ if (jcr->job->storage) {
+ copy_rwstorage(jcr, jcr->job->storage, _("Job resource"));
+ } else {
+ copy_rwstorage(jcr, jcr->job->pool->storage, _("Pool resource"));
+ }
+ }
+ if (!jcr->JobReads()) {
+ free_rstorage(jcr);
+ }
+
/*
* Now, do pre-run stuff, like setting job level (Inc/diff, ...)
* this allows us to setup a proper job start record for restarting
* in case of later errors.
*/
- switch (jcr->JobType) {
+ switch (jcr->getJobType()) {
case JT_BACKUP:
if (!do_backup_init(jcr)) {
backup_cleanup(jcr, JS_ErrorTerminated);
+ goto bail_out;
}
break;
case JT_VERIFY:
if (!do_verify_init(jcr)) {
verify_cleanup(jcr, JS_ErrorTerminated);
+ goto bail_out;
}
break;
case JT_RESTORE:
if (!do_restore_init(jcr)) {
restore_cleanup(jcr, JS_ErrorTerminated);
+ goto bail_out;
}
break;
case JT_ADMIN:
if (!do_admin_init(jcr)) {
admin_cleanup(jcr, JS_ErrorTerminated);
+ goto bail_out;
}
break;
+ case JT_COPY:
case JT_MIGRATE:
if (!do_migration_init(jcr)) {
migration_cleanup(jcr, JS_ErrorTerminated);
+ goto bail_out;
}
break;
default:
- Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->JobType);
- set_jcr_job_status(jcr, JS_ErrorTerminated);
- break;
+ Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->getJobType());
+ jcr->setJobStatus(JS_ErrorTerminated);
+ goto bail_out;
}
generate_job_event(jcr, "JobInit");
- Dsm_check(1);
+ generate_plugin_event(jcr, bDirEventJobInit);
+ Dsm_check(100);
return true;
bail_out:
void update_job_end(JCR *jcr, int TermCode)
{
dequeue_messages(jcr); /* display any queued messages */
- set_jcr_job_status(jcr, TermCode);
+ jcr->setJobStatus(TermCode);
update_job_end_record(jcr);
}
JCR *jcr = (JCR *)arg;
pthread_detach(pthread_self());
- Dsm_check(1);
+ Dsm_check(100);
Dmsg0(200, "=====Start Job=========\n");
- set_jcr_job_status(jcr, JS_Running); /* this will be set only if no error */
+ jcr->setJobStatus(JS_Running); /* this will be set only if no error */
jcr->start_time = time(NULL); /* set the real start time */
jcr->jr.StartTime = jcr->start_time;
if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
(utime_t)(jcr->start_time - jcr->sched_time)) {
- set_jcr_job_status(jcr, JS_Canceled);
+ jcr->setJobStatus(JS_Canceled);
Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
}
+ if (job_check_maxrunschedtime(jcr)) {
+ jcr->setJobStatus(JS_Canceled);
+ Jmsg(jcr, M_FATAL, 0, _("Job canceled because max run sched time exceeded.\n"));
+ }
+
/* TODO : check if it is used somewhere */
if (jcr->job->RunScripts == NULL) {
Dmsg0(200, "Warning, job->RunScripts is empty\n");
/* Run any script BeforeJob on dird */
run_scripts(jcr, jcr->job->RunScripts, "BeforeJob");
- if (job_canceled(jcr)) {
- update_job_end(jcr, jcr->JobStatus);
+ /*
+ * We re-update the job start record so that the start
+ * time is set after the run before job. This avoids
+ * that any files created by the run before job will
+ * be saved twice. They will be backed up in the current
+ * job, but not in the next one unless they are changed.
+ * Without this, they will be backed up in this job and
+ * in the next job run because in that case, their date
+ * is after the start of this run.
+ */
+ jcr->start_time = time(NULL);
+ jcr->jr.StartTime = jcr->start_time;
+ if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
+ Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
+ }
+ generate_job_event(jcr, "JobRun");
+ generate_plugin_event(jcr, bDirEventJobRun);
- } else {
- /*
- * We re-update the job start record so that the start
- * time is set after the run before job. This avoids
- * that any files created by the run before job will
- * be saved twice. They will be backed up in the current
- * job, but not in the next one unless they are changed.
- * Without this, they will be backed up in this job and
- * in the next job run because in that case, their date
- * is after the start of this run.
- */
- jcr->start_time = time(NULL);
- jcr->jr.StartTime = jcr->start_time;
- if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
- Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
+ switch (jcr->getJobType()) {
+ case JT_BACKUP:
+ if (!job_canceled(jcr) && do_backup(jcr)) {
+ do_autoprune(jcr);
+ } else {
+ backup_cleanup(jcr, JS_ErrorTerminated);
}
- generate_job_event(jcr, "JobRun");
-
- switch (jcr->JobType) {
- case JT_BACKUP:
- if (do_backup(jcr)) {
- do_autoprune(jcr);
- } else {
- backup_cleanup(jcr, JS_ErrorTerminated);
- }
- break;
- case JT_VERIFY:
- if (do_verify(jcr)) {
- do_autoprune(jcr);
- } else {
- verify_cleanup(jcr, JS_ErrorTerminated);
- }
- break;
- case JT_RESTORE:
- if (do_restore(jcr)) {
- do_autoprune(jcr);
- } else {
- restore_cleanup(jcr, JS_ErrorTerminated);
- }
- break;
- case JT_ADMIN:
- if (do_admin(jcr)) {
- do_autoprune(jcr);
- } else {
- admin_cleanup(jcr, JS_ErrorTerminated);
- }
- break;
- case JT_MIGRATE:
- case JT_COPY:
- case JT_ARCHIVE:
- if (do_migration(jcr)) {
- do_autoprune(jcr);
- } else {
- migration_cleanup(jcr, JS_ErrorTerminated);
- }
- break;
- default:
- Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->JobType);
- break;
+ break;
+ case JT_VERIFY:
+ if (!job_canceled(jcr) && do_verify(jcr)) {
+ do_autoprune(jcr);
+ } else {
+ verify_cleanup(jcr, JS_ErrorTerminated);
+ }
+ break;
+ case JT_RESTORE:
+ if (!job_canceled(jcr) && do_restore(jcr)) {
+ do_autoprune(jcr);
+ } else {
+ restore_cleanup(jcr, JS_ErrorTerminated);
+ }
+ break;
+ case JT_ADMIN:
+ if (!job_canceled(jcr) && do_admin(jcr)) {
+ do_autoprune(jcr);
+ } else {
+ admin_cleanup(jcr, JS_ErrorTerminated);
}
+ break;
+ case JT_COPY:
+ case JT_MIGRATE:
+ if (!job_canceled(jcr) && do_migration(jcr)) {
+ do_autoprune(jcr);
+ } else {
+ migration_cleanup(jcr, JS_ErrorTerminated);
+ }
+ break;
+ default:
+ Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->getJobType());
+ break;
}
run_scripts(jcr, jcr->job->RunScripts, "AfterJob");
}
generate_daemon_event(jcr, "JobEnd");
+ generate_plugin_event(jcr, bDirEventJobEnd);
Dmsg1(50, "======== End Job stat=%c ==========\n", jcr->JobStatus);
- sm_check(__FILE__, __LINE__, true);
+ Dsm_check(100);
return NULL;
}
+void sd_msg_thread_send_signal(JCR *jcr, int sig)
+{
+ jcr->lock();
+ if ( !jcr->sd_msg_thread_done
+ && jcr->SD_msg_chan
+ && !pthread_equal(jcr->SD_msg_chan, pthread_self()))
+ {
+ Dmsg1(800, "Send kill to SD msg chan jid=%d\n", jcr->JobId);
+ pthread_kill(jcr->SD_msg_chan, sig);
+ }
+ jcr->unlock();
+}
/*
* Cancel a job -- typically called by the UA (Console program), but may also
{
BSOCK *sd, *fd;
char ed1[50];
+ int32_t old_status = jcr->JobStatus;
- set_jcr_job_status(jcr, JS_Canceled);
+ jcr->setJobStatus(JS_Canceled);
- switch (jcr->JobStatus) {
+ switch (old_status) {
case JS_Created:
case JS_WaitJobRes:
case JS_WaitClientRes:
ua->info_msg(_("JobId %s, Job %s marked to be canceled.\n"),
edit_uint64(jcr->JobId, ed1), jcr->Job);
jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
- return true;
+ break;
default:
/* Cancel File daemon */
}
Dmsg0(200, "Connected to file daemon\n");
fd = ua->jcr->file_bsock;
- bnet_fsend(fd, "cancel Job=%s\n", jcr->Job);
- while (bnet_recv(fd) >= 0) {
+ fd->fsend("cancel Job=%s\n", jcr->Job);
+ while (fd->recv() >= 0) {
ua->send_msg("%s", fd->msg);
}
- bnet_sig(fd, BNET_TERMINATE);
- bnet_close(fd);
+ fd->signal(BNET_TERMINATE);
+ fd->close();
ua->jcr->file_bsock = NULL;
+ jcr->file_bsock->set_terminated();
+ jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
}
/* Cancel Storage daemon */
sd->signal(BNET_TERMINATE);
sd->close();
ua->jcr->store_bsock = NULL;
+ jcr->store_bsock->set_timed_out();
+ jcr->store_bsock->set_terminated();
+ sd_msg_thread_send_signal(jcr, TIMEOUT_SIGNAL);
+ jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
}
+ break;
}
return true;
void cancel_storage_daemon_job(JCR *jcr)
{
+ if (jcr->sd_canceled) {
+ return; /* cancel only once */
+ }
+
UAContext *ua = new_ua_context(jcr);
JCR *control_jcr = new_control_jcr("*JobCancel*", JT_SYSTEM);
BSOCK *sd;
sd->signal(BNET_TERMINATE);
sd->close();
ua->jcr->store_bsock = NULL;
+ jcr->sd_canceled = true;
+ jcr->store_bsock->set_timed_out();
+ jcr->store_bsock->set_terminated();
+ sd_msg_thread_send_signal(jcr, TIMEOUT_SIGNAL);
+ jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
}
bail_out:
free_jcr(control_jcr);
control_jcr = (JCR *)self->data;
- Dsm_check(1);
+ Dsm_check(100);
Dmsg1(800, "job_monitor_watchdog %p called\n", self);
foreach_jcr(jcr) {
bool cancel = false;
- if (jcr->JobId == 0 || job_canceled(jcr)) {
+ if (jcr->JobId == 0 || job_canceled(jcr) || jcr->no_maxtime) {
Dmsg2(800, "Skipping JCR=%p Job=%s\n", jcr, jcr->Job);
continue;
}
/* check MaxWaitTime */
- if (job_check_maxwaittime(control_jcr, jcr)) {
- set_jcr_job_status(jcr, JS_Canceled);
- Jmsg(jcr, M_FATAL, 0, _("Max wait time exceeded. Job canceled.\n"));
+ if (job_check_maxwaittime(jcr)) {
+ jcr->setJobStatus(JS_Canceled);
+ Qmsg(jcr, M_FATAL, 0, _("Max wait time exceeded. Job canceled.\n"));
cancel = true;
/* check MaxRunTime */
- } else if (job_check_maxruntime(control_jcr, jcr)) {
- set_jcr_job_status(jcr, JS_Canceled);
- Jmsg(jcr, M_FATAL, 0, _("Max run time exceeded. Job canceled.\n"));
+ } else if (job_check_maxruntime(jcr)) {
+ jcr->setJobStatus(JS_Canceled);
+ Qmsg(jcr, M_FATAL, 0, _("Max run time exceeded. Job canceled.\n"));
+ cancel = true;
+ /* check MaxRunSchedTime */
+ } else if (job_check_maxrunschedtime(jcr)) {
+ jcr->setJobStatus(JS_Canceled);
+ Qmsg(jcr, M_FATAL, 0, _("Max run sched time exceeded. Job canceled.\n"));
cancel = true;
}
* Check if the maxwaittime has expired and it is possible
* to cancel the job.
*/
-static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr)
+static bool job_check_maxwaittime(JCR *jcr)
{
bool cancel = false;
JOB *job = jcr->job;
+ utime_t current=0;
- if (job_canceled(jcr)) {
- return false; /* already canceled */
+ if (!job_waiting(jcr)) {
+ return false;
+ }
+
+ if (jcr->wait_time) {
+ current = watchdog_time - jcr->wait_time;
+ }
+
+ Dmsg2(200, "check maxwaittime %u >= %u\n",
+ current + jcr->wait_time_sum, job->MaxWaitTime);
+ if (job->MaxWaitTime != 0 &&
+ (current + jcr->wait_time_sum) >= job->MaxWaitTime) {
+ cancel = true;
+ }
+
+ return cancel;
+}
+
+/*
+ * Check if maxruntime has expired and if the job can be
+ * canceled.
+ */
+static bool job_check_maxruntime(JCR *jcr)
+{
+ bool cancel = false;
+ JOB *job = jcr->job;
+ utime_t run_time;
+
+ if (job_canceled(jcr) || jcr->JobStatus == JS_Created) {
+ return false;
}
- if (job->MaxWaitTime == 0 && job->FullMaxWaitTime == 0 &&
- job->IncMaxWaitTime == 0 && job->DiffMaxWaitTime == 0) {
+ if (jcr->job->MaxRunTime == 0 && job->FullMaxRunTime == 0 &&
+ job->IncMaxRunTime == 0 && job->DiffMaxRunTime == 0) {
return false;
- }
- if (jcr->JobLevel == L_FULL && job->FullMaxWaitTime != 0 &&
- (watchdog_time - jcr->start_time) >= job->FullMaxWaitTime) {
+ }
+ run_time = watchdog_time - jcr->start_time;
+ Dmsg7(200, "check_maxruntime %llu-%u=%llu >= %llu|%llu|%llu|%llu\n",
+ watchdog_time, jcr->start_time, run_time, job->MaxRunTime, job->FullMaxRunTime,
+ job->IncMaxRunTime, job->DiffMaxRunTime);
+
+ if (jcr->getJobLevel() == L_FULL && job->FullMaxRunTime != 0 &&
+ run_time >= job->FullMaxRunTime) {
+ Dmsg0(200, "check_maxwaittime: FullMaxcancel\n");
cancel = true;
- } else if (jcr->JobLevel == L_DIFFERENTIAL && job->DiffMaxWaitTime != 0 &&
- (watchdog_time - jcr->start_time) >= job->DiffMaxWaitTime) {
+ } else if (jcr->getJobLevel() == L_DIFFERENTIAL && job->DiffMaxRunTime != 0 &&
+ run_time >= job->DiffMaxRunTime) {
+ Dmsg0(200, "check_maxwaittime: DiffMaxcancel\n");
cancel = true;
- } else if (jcr->JobLevel == L_INCREMENTAL && job->IncMaxWaitTime != 0 &&
- (watchdog_time - jcr->start_time) >= job->IncMaxWaitTime) {
+ } else if (jcr->getJobLevel() == L_INCREMENTAL && job->IncMaxRunTime != 0 &&
+ run_time >= job->IncMaxRunTime) {
+ Dmsg0(200, "check_maxwaittime: IncMaxcancel\n");
cancel = true;
- } else if (job->MaxWaitTime != 0 &&
- (watchdog_time - jcr->start_time) >= job->MaxWaitTime) {
+ } else if (job->MaxRunTime > 0 && run_time >= job->MaxRunTime) {
+ Dmsg0(200, "check_maxwaittime: Maxcancel\n");
cancel = true;
}
-
+
return cancel;
}
/*
- * Check if maxruntime has expired and if the job can be
+ * Check if MaxRunSchedTime has expired and if the job can be
* canceled.
*/
-static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr)
+static bool job_check_maxrunschedtime(JCR *jcr)
{
- if (jcr->job->MaxRunTime == 0 || job_canceled(jcr) || jcr->JobStatus == JS_Created) {
+ if (jcr->MaxRunSchedTime == 0 || job_canceled(jcr)) {
return false;
}
- if ((watchdog_time - jcr->start_time) < jcr->job->MaxRunTime) {
- Dmsg3(200, "Job %p (%s) with MaxRunTime %d not expired\n",
- jcr, jcr->Job, jcr->job->MaxRunTime);
+ if ((watchdog_time - jcr->sched_time) < jcr->MaxRunSchedTime) {
+ Dmsg3(200, "Job %p (%s) with MaxRunSchedTime %d not expired\n",
+ jcr, jcr->Job, jcr->MaxRunSchedTime);
return false;
}
while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
/* Try to create the pool */
if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
- Jmsg(jcr, M_FATAL, 0, _("Pool %s not in database. %s"), pr.Name,
+ Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. ERR=%s"), pr.Name,
db_strerror(jcr->db));
return 0;
} else {
- Jmsg(jcr, M_INFO, 0, _("Pool %s created in database.\n"), pr.Name);
+ Jmsg(jcr, M_INFO, 0, _("Created database record for Pool \"%s\".\n"), pr.Name);
}
}
return pr.PoolId;
}
+/*
+ * Check for duplicate jobs.
+ * Returns: true if current job should continue
+ * false if current job should terminate
+ */
+bool allow_duplicate_job(JCR *jcr)
+{
+ JOB *job = jcr->job;
+ JCR *djcr; /* possible duplicate job */
+ bool cancel_dup = false;
+ bool cancel_me = false;
+
+ /*
+ * See if AllowDuplicateJobs is set or
+ * if duplicate checking is disabled for this job.
+ */
+ if (job->AllowDuplicateJobs || jcr->IgnoreDuplicateJobChecking) {
+ return true;
+ }
+
+ Dmsg0(800, "Enter allow_duplicate_job\n");
+
+ /*
+ * After this point, we do not want to allow any duplicate
+ * job to run.
+ */
+
+ foreach_jcr(djcr) {
+ if (jcr == djcr || djcr->JobId == 0) {
+ continue; /* do not cancel this job or consoles */
+ }
+
+ /*
+ * See if this Job has the IgnoreDuplicateJobChecking flag set, ignore it
+ * for any checking against other jobs.
+ */
+ if (djcr->IgnoreDuplicateJobChecking) {
+ continue;
+ }
+
+ if (strcmp(job->name(), djcr->job->name()) == 0) {
+ if (job->DuplicateJobProximity > 0) {
+ utime_t now = (utime_t)time(NULL);
+ if ((now - djcr->start_time) > job->DuplicateJobProximity) {
+ continue; /* not really a duplicate */
+ }
+ }
+ if (job->CancelLowerLevelDuplicates &&
+ djcr->getJobType() == 'B' && jcr->getJobType() == 'B') {
+ switch (jcr->getJobLevel()) {
+ case L_FULL:
+ if (djcr->getJobLevel() == L_DIFFERENTIAL ||
+ djcr->getJobLevel() == L_INCREMENTAL) {
+ cancel_dup = true;
+ }
+ break;
+ case L_DIFFERENTIAL:
+ if (djcr->getJobLevel() == L_INCREMENTAL) {
+ cancel_dup = true;
+ }
+ if (djcr->getJobLevel() == L_FULL) {
+ cancel_me = true;
+ }
+ break;
+ case L_INCREMENTAL:
+ if (djcr->getJobLevel() == L_FULL ||
+ djcr->getJobLevel() == L_DIFFERENTIAL) {
+ cancel_me = true;
+ }
+ }
+ /*
+ * cancel_dup will be done below
+ */
+ if (cancel_me) {
+ /* Zap current job */
+ Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"),
+ djcr->JobId);
+ break; /* get out of foreach_jcr */
+ }
+ }
+
+ /*
+ * Cancel one of the two jobs (me or dup)
+ * If CancelQueuedDuplicates is set do so only if job is queued.
+ */
+ if (job->CancelQueuedDuplicates) {
+ switch (djcr->JobStatus) {
+ case JS_Created:
+ case JS_WaitJobRes:
+ case JS_WaitClientRes:
+ case JS_WaitStoreRes:
+ case JS_WaitPriority:
+ case JS_WaitMaxJobs:
+ case JS_WaitStartTime:
+ cancel_dup = true; /* cancel queued duplicate */
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (cancel_dup || job->CancelRunningDuplicates) {
+ /*
+ * Zap the duplicated job djcr
+ */
+ UAContext *ua = new_ua_context(jcr);
+ Jmsg(jcr, M_INFO, 0, _("Cancelling duplicate JobId=%d.\n"), djcr->JobId);
+ cancel_job(ua, djcr);
+ bmicrosleep(0, 500000);
+ cancel_job(ua, djcr);
+ free_ua_context(ua);
+ Dmsg2(800, "Cancel dup %p JobId=%d\n", djcr, djcr->JobId);
+ } else {
+ /*
+ * Zap current job
+ */
+ Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"),
+ djcr->JobId);
+ Dmsg2(800, "Cancel me %p JobId=%d\n", jcr, jcr->JobId);
+ }
+ Dmsg4(800, "curJobId=%d use_cnt=%d dupJobId=%d use_cnt=%d\n",
+ jcr->JobId, jcr->use_count(), djcr->JobId, djcr->use_count());
+ break; /* did our work, get out of foreach loop */
+ }
+ }
+ endeach_jcr(djcr);
+
+ return true;
+}
+
void apply_pool_overrides(JCR *jcr)
{
+ bool pool_override = false;
+
if (jcr->run_pool_override) {
pm_strcpy(jcr->pool_source, _("Run pool override"));
}
/*
* Apply any level related Pool selections
*/
- switch (jcr->JobLevel) {
+ switch (jcr->getJobLevel()) {
case L_FULL:
if (jcr->full_pool) {
jcr->pool = jcr->full_pool;
+ pool_override = true;
if (jcr->run_full_pool_override) {
pm_strcpy(jcr->pool_source, _("Run FullPool override"));
} else {
case L_INCREMENTAL:
if (jcr->inc_pool) {
jcr->pool = jcr->inc_pool;
+ pool_override = true;
if (jcr->run_inc_pool_override) {
pm_strcpy(jcr->pool_source, _("Run IncPool override"));
} else {
case L_DIFFERENTIAL:
if (jcr->diff_pool) {
jcr->pool = jcr->diff_pool;
+ pool_override = true;
if (jcr->run_diff_pool_override) {
pm_strcpy(jcr->pool_source, _("Run DiffPool override"));
} else {
}
break;
}
+ /* Update catalog if pool overridden */
+ if (pool_override && jcr->pool->catalog) {
+ jcr->catalog = jcr->pool->catalog;
+ pm_strcpy(jcr->catalog_source, _("Pool resource"));
+ }
}
jcr->jr.SchedTime = jcr->sched_time;
jcr->jr.StartTime = jcr->start_time;
jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
- jcr->jr.JobType = jcr->JobType;
- jcr->jr.JobLevel = jcr->JobLevel;
+ jcr->jr.JobType = jcr->getJobType();
+ jcr->jr.JobLevel = jcr->getJobLevel();
jcr->jr.JobStatus = jcr->JobStatus;
jcr->jr.JobId = jcr->JobId;
bstrncpy(jcr->jr.Name, jcr->job->name(), sizeof(jcr->jr.Name));
jcr->jr.JobStatus = jcr->JobStatus;
jcr->jr.JobFiles = jcr->JobFiles;
jcr->jr.JobBytes = jcr->JobBytes;
+ jcr->jr.ReadBytes = jcr->ReadBytes;
jcr->jr.VolSessionId = jcr->VolSessionId;
jcr->jr.VolSessionTime = jcr->VolSessionTime;
- jcr->jr.JobErrors = jcr->Errors;
+ jcr->jr.JobErrors = jcr->JobErrors;
+ jcr->jr.HasBase = jcr->HasBase;
if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
db_strerror(jcr->db));
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static time_t last_start_time = 0;
static int seq = 0;
- time_t now;
+ time_t now = time(NULL);
struct tm tm;
char dt[MAX_TIME_LENGTH];
char name[MAX_NAME_LENGTH];
char *p;
+ int len;
/* Guarantee unique start time -- maximum one per second, and
* thus unique Job Name
*/
P(mutex); /* lock creation of jobs */
- now = time(NULL);
seq++;
if (seq > 59) { /* wrap as if it is seconds */
seq = 0;
/* Form Unique JobName */
(void)localtime_r(&now, &tm);
/* Use only characters that are permitted in Windows filenames */
- strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M", &tm);
+ strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
+ len = strlen(dt) + 5; /* dt + .%02d EOS */
bstrncpy(name, base_name, sizeof(name));
- name[sizeof(name)-22] = 0; /* truncate if too long */
- bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s.%02d", name, dt, seq); /* add date & time */
+ name[sizeof(name)-len] = 0; /* truncate if too long */
+ bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s_%02d", name, dt, seq); /* add date & time */
/* Convert spaces into underscores */
for (p=jcr->Job; *p; p++) {
if (*p == ' ') {
*p = '_';
}
}
+ Dmsg2(100, "JobId=%u created Job=%s\n", jcr->JobId, jcr->Job);
}
/* Called directly from job rescheduling */
void dird_free_jcr_pointers(JCR *jcr)
{
- if (jcr->sd_auth_key) {
- free(jcr->sd_auth_key);
- jcr->sd_auth_key = NULL;
- }
- if (jcr->where) {
- free(jcr->where);
- jcr->where = NULL;
- }
if (jcr->file_bsock) {
Dmsg0(200, "Close File bsock\n");
bnet_close(jcr->file_bsock);
bnet_close(jcr->store_bsock);
jcr->store_bsock = NULL;
}
- if (jcr->fname) {
- Dmsg0(200, "Free JCR fname\n");
- free_pool_memory(jcr->fname);
- jcr->fname = NULL;
- }
- if (jcr->RestoreBootstrap) {
- free(jcr->RestoreBootstrap);
- jcr->RestoreBootstrap = NULL;
- }
- if (jcr->client_uname) {
- free_pool_memory(jcr->client_uname);
- jcr->client_uname = NULL;
- }
- if (jcr->attr) {
- free_pool_memory(jcr->attr);
- jcr->attr = NULL;
- }
- if (jcr->ar) {
- free(jcr->ar);
- jcr->ar = NULL;
- }
+
+ bfree_and_null(jcr->sd_auth_key);
+ bfree_and_null(jcr->where);
+ bfree_and_null(jcr->RestoreBootstrap);
+ bfree_and_null(jcr->ar);
+
+ free_and_null_pool_memory(jcr->JobIds);
+ free_and_null_pool_memory(jcr->client_uname);
+ free_and_null_pool_memory(jcr->attr);
+ free_and_null_pool_memory(jcr->fname);
}
/*
pthread_cond_destroy(&jcr->term_wait);
jcr->term_wait_inited = false;
}
- if (jcr->db_batch && jcr->db_batch != jcr->db) {
+ if (jcr->db_batch) {
db_close_database(jcr, jcr->db_batch);
+ jcr->db_batch = NULL;
+ jcr->batch_started = false;
}
- jcr->db_batch = NULL;
if (jcr->db) {
db_close_database(jcr, jcr->db);
jcr->db = NULL;
}
- if (jcr->stime) {
- Dmsg0(200, "Free JCR stime\n");
- free_pool_memory(jcr->stime);
- jcr->stime = NULL;
- }
- if (jcr->fname) {
- Dmsg0(200, "Free JCR fname\n");
- free_pool_memory(jcr->fname);
- jcr->fname = NULL;
- }
- if (jcr->pool_source) {
- free_pool_memory(jcr->pool_source);
- jcr->pool_source = NULL;
- }
- if (jcr->rpool_source) {
- free_pool_memory(jcr->rpool_source);
- jcr->rpool_source = NULL;
- }
- if (jcr->wstore_source) {
- free_pool_memory(jcr->wstore_source);
- jcr->wstore_source = NULL;
- }
- if (jcr->rstore_source) {
- free_pool_memory(jcr->rstore_source);
- jcr->rstore_source = NULL;
- }
+
+ free_and_null_pool_memory(jcr->stime);
+ free_and_null_pool_memory(jcr->fname);
+ free_and_null_pool_memory(jcr->pool_source);
+ free_and_null_pool_memory(jcr->catalog_source);
+ free_and_null_pool_memory(jcr->rpool_source);
+ free_and_null_pool_memory(jcr->wstore_source);
+ free_and_null_pool_memory(jcr->rstore_source);
/* Delete lists setup to hold storage pointers */
free_rwstorage(jcr);
if (jcr->JobId != 0)
write_state_file(director->working_directory, "bacula-dir", get_first_port_host_order(director->DIRaddrs));
+ free_plugins(jcr); /* release instantiated plugins */
+
Dmsg0(200, "End dird free_jcr\n");
}
void set_jcr_defaults(JCR *jcr, JOB *job)
{
jcr->job = job;
- jcr->JobType = job->JobType;
+ jcr->setJobType(job->JobType);
jcr->JobStatus = JS_Created;
- switch (jcr->JobType) {
+
+ switch (jcr->getJobType()) {
case JT_ADMIN:
- case JT_RESTORE:
- jcr->JobLevel = L_NONE;
+ jcr->setJobLevel(L_NONE);
break;
- case JT_MIGRATE:
- if (!jcr->rpool_source) {
- jcr->rpool_source = get_pool_memory(PM_MESSAGE);
- pm_strcpy(jcr->rpool_source, _("unknown source"));
- }
- /* Fall-through wanted */
default:
- jcr->JobLevel = job->JobLevel;
+ jcr->setJobLevel(job->JobLevel);
break;
}
+
if (!jcr->fname) {
jcr->fname = get_pool_memory(PM_FNAME);
}
jcr->pool_source = get_pool_memory(PM_MESSAGE);
pm_strcpy(jcr->pool_source, _("unknown source"));
}
+ if (!jcr->catalog_source) {
+ jcr->catalog_source = get_pool_memory(PM_MESSAGE);
+ pm_strcpy(jcr->catalog_source, _("unknown source"));
+ }
jcr->JobPriority = job->Priority;
/* Copy storage definitions -- deleted in dir_free_jcr above */
jcr->full_pool = job->full_pool;
jcr->inc_pool = job->inc_pool;
jcr->diff_pool = job->diff_pool;
- jcr->catalog = job->client->catalog;
+ if (job->pool->catalog) {
+ jcr->catalog = job->pool->catalog;
+ pm_strcpy(jcr->catalog_source, _("Pool resource"));
+ } else {
+ jcr->catalog = job->client->catalog;
+ pm_strcpy(jcr->catalog_source, _("Client resource"));
+ }
jcr->fileset = job->fileset;
+ jcr->accurate = job->accurate;
jcr->messages = job->messages;
jcr->spool_data = job->spool_data;
jcr->spool_size = job->spool_size;
jcr->write_part_after_job = job->write_part_after_job;
- jcr->accurate = job->accurate;
+ jcr->IgnoreDuplicateJobChecking = job->IgnoreDuplicateJobChecking;
+ jcr->MaxRunSchedTime = job->MaxRunSchedTime;
if (jcr->RestoreBootstrap) {
free(jcr->RestoreBootstrap);
jcr->RestoreBootstrap = NULL;
/* This can be overridden by Console program */
jcr->verify_job = job->verify_job;
/* If no default level given, set one */
- if (jcr->JobLevel == 0) {
- switch (jcr->JobType) {
+ if (jcr->getJobLevel() == 0) {
+ switch (jcr->getJobType()) {
case JT_VERIFY:
- jcr->JobLevel = L_VERIFY_CATALOG;
+ jcr->setJobLevel(L_VERIFY_CATALOG);
break;
case JT_BACKUP:
- jcr->JobLevel = L_INCREMENTAL;
+ jcr->setJobLevel(L_INCREMENTAL);
break;
case JT_RESTORE:
case JT_ADMIN:
- jcr->JobLevel = L_NONE;
+ jcr->setJobLevel(L_NONE);
break;
default:
- jcr->JobLevel = L_FULL;
+ jcr->setJobLevel(L_FULL);
break;
}
}
*/
void copy_rwstorage(JCR *jcr, alist *storage, const char *where)
{
- switch(jcr->JobType) {
- case JT_RESTORE:
- case JT_VERIFY:
- case JT_MIGRATE:
+ if (jcr->JobReads()) {
copy_rstorage(jcr, storage, where);
- break;
- default:
- copy_wstorage(jcr, storage, where);
- break;
}
+ copy_wstorage(jcr, storage, where);
}
Jmsg(jcr, M_FATAL, 0, _("No storage specified.\n"));
return;
}
- switch(jcr->JobType) {
- case JT_RESTORE:
- case JT_VERIFY:
- case JT_MIGRATE:
+ if (jcr->JobReads()) {
set_rstorage(jcr, store);
- break;
- default:
- set_wstorage(jcr, store);
- break;
}
+ set_wstorage(jcr, store);
}
void free_rwstorage(JCR *jcr)
UAContext *ua = new_ua_context(jcr);
ua->batch = true;
foreach_alist(runcmd, job->run_cmds) {
- cmd = edit_job_codes(jcr, cmd, runcmd, "");
+ cmd = edit_job_codes(jcr, cmd, runcmd, "", job_code_callback_director);
Mmsg(ua->cmd, "run %s cloned=yes", cmd);
Dmsg1(900, "=============== Clone cmd=%s\n", ua->cmd);
parse_ua_args(ua); /* parse command */
int stat = run_cmd(ua, ua->cmd);
if (stat == 0) {
- Jmsg(jcr, M_ERROR, 0, _("Could not start clone job.\n"));
+ Jmsg(jcr, M_ERROR, 0, _("Could not start clone job: \"%s\".\n"),
+ ua->cmd);
} else {
Jmsg(jcr, M_INFO, 0, _("Clone JobId %d started.\n"), stat);
}
}
}
-bool create_restore_bootstrap_file(JCR *jcr)
+/*
+ * Given: a JobId in jcr->previous_jr.JobId,
+ * this subroutine writes a bsr file to restore that job.
+ * Returns: -1 on error
+ * number of files if OK
+ */
+int create_restore_bootstrap_file(JCR *jcr)
{
RESTORE_CTX rx;
UAContext *ua;
+ int files;
+
memset(&rx, 0, sizeof(rx));
rx.bsr = new_bsr();
rx.JobIds = (char *)"";
rx.bsr->JobId = jcr->previous_jr.JobId;
ua = new_ua_context(jcr);
- complete_bsr(ua, rx.bsr);
+ if (!complete_bsr(ua, rx.bsr)) {
+ files = -1;
+ goto bail_out;
+ }
rx.bsr->fi = new_findex();
rx.bsr->fi->findex = 1;
rx.bsr->fi->findex2 = jcr->previous_jr.JobFiles;
jcr->ExpectedFiles = write_bsr_file(ua, rx);
if (jcr->ExpectedFiles == 0) {
- free_ua_context(ua);
- free_bsr(rx.bsr);
- return false;
+ files = 0;
+ goto bail_out;
}
free_ua_context(ua);
free_bsr(rx.bsr);
jcr->needs_sd = true;
- return true;
+ return jcr->ExpectedFiles;
+
+bail_out:
+ free_ua_context(ua);
+ free_bsr(rx.bsr);
+ return files;
}
-bool run_console_command(JCR *jcr, const char *cmd){
+/* TODO: redirect command ouput to job log */
+bool run_console_command(JCR *jcr, const char *cmd)
+{
UAContext *ua;
bool ok;
-
- ua = new_ua_context(jcr);
+ JCR *ljcr = new_control_jcr("-RunScript-", JT_CONSOLE);
+ ua = new_ua_context(ljcr);
+ /* run from runscript and check if commands are autorized */
+ ua->runscript = true;
Mmsg(ua->cmd, "%s", cmd);
Dmsg1(100, "Console command: %s\n", ua->cmd);
parse_ua_args(ua);
ok= do_a_command(ua);
free_ua_context(ua);
+ free_jcr(ljcr);
return ok;
}