2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director Job processing routines
32 * Kern Sibbald, October MM
40 /* Forward referenced subroutines */
41 static void *job_thread(void *arg);
42 static void job_monitor_watchdog(watchdog_t *self);
43 static void job_monitor_destructor(watchdog_t *self);
44 static bool job_check_maxwaittime(JCR *jcr);
45 static bool job_check_maxruntime(JCR *jcr);
46 static bool job_check_maxschedruntime(JCR *jcr);
48 /* Imported subroutines */
49 extern void term_scheduler();
50 extern void term_ua_server();
52 /* Imported variables */
56 void init_job_server(int max_workers)
61 if ((stat = jobq_init(&job_queue, max_workers, job_thread)) != 0) {
63 Emsg1(M_ABORT, 0, _("Could not init job queue: ERR=%s\n"), be.bstrerror(stat));
66 wd->callback = job_monitor_watchdog;
67 wd->destructor = job_monitor_destructor;
70 wd->data = new_control_jcr("*JobMonitor*", JT_SYSTEM);
71 register_watchdog(wd);
74 void term_job_server()
76 jobq_destroy(&job_queue); /* ignore any errors */
80 * Run a job -- typically called by the scheduler, but may also
81 * be called by the UA (Console program).
83 * Returns: 0 on failure
87 JobId_t run_job(JCR *jcr)
91 Dmsg0(200, "Add jrc to work queue\n");
92 /* Queue the job to be run */
93 if ((stat = jobq_add(&job_queue, jcr)) != 0) {
95 Jmsg(jcr, M_FATAL, 0, _("Could not add job queue: ERR=%s\n"), be.bstrerror(stat));
103 bool setup_job(JCR *jcr)
108 sm_check(__FILE__, __LINE__, true);
109 init_msg(jcr, jcr->messages);
111 /* Initialize termination condition variable */
112 if ((errstat = pthread_cond_init(&jcr->term_wait, NULL)) != 0) {
114 Jmsg1(jcr, M_FATAL, 0, _("Unable to init job cond variable: ERR=%s\n"), be.bstrerror(errstat));
118 jcr->term_wait_inited = true;
120 create_unique_job_name(jcr, jcr->job->name());
121 set_jcr_job_status(jcr, JS_Created);
127 Dmsg0(100, "Open database\n");
128 jcr->db=db_init(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
129 jcr->catalog->db_user,
130 jcr->catalog->db_password, jcr->catalog->db_address,
131 jcr->catalog->db_port, jcr->catalog->db_socket,
132 jcr->catalog->mult_db_connections);
133 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
134 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
135 jcr->catalog->db_name);
137 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
138 db_close_database(jcr, jcr->db);
142 Dmsg0(150, "DB opened\n");
145 jcr->fname = get_pool_memory(PM_FNAME);
147 if (!jcr->pool_source) {
148 jcr->pool_source = get_pool_memory(PM_MESSAGE);
149 pm_strcpy(jcr->pool_source, _("unknown source"));
152 if (jcr->JobReads()) {
153 if (!jcr->rpool_source) {
154 jcr->rpool_source = get_pool_memory(PM_MESSAGE);
155 pm_strcpy(jcr->rpool_source, _("unknown source"));
162 init_jcr_job_record(jcr);
163 if (!get_or_create_client_record(jcr)) {
167 if (!db_create_job_record(jcr, jcr->db, &jcr->jr)) {
168 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
171 jcr->JobId = jcr->jr.JobId;
172 Dmsg4(100, "Created job record JobId=%d Name=%s Type=%c Level=%c\n",
173 jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel);
175 generate_daemon_event(jcr, "JobStart");
177 if (job_canceled(jcr)) {
181 if (jcr->JobReads() && !jcr->rstorage) {
182 if (jcr->job->storage) {
183 copy_rwstorage(jcr, jcr->job->storage, _("Job resource"));
185 copy_rwstorage(jcr, jcr->job->pool->storage, _("Pool resource"));
188 if (!jcr->JobReads()) {
193 * Now, do pre-run stuff, like setting job level (Inc/diff, ...)
194 * this allows us to setup a proper job start record for restarting
195 * in case of later errors.
197 switch (jcr->get_JobType()) {
199 if (!do_backup_init(jcr)) {
200 backup_cleanup(jcr, JS_ErrorTerminated);
204 if (!do_verify_init(jcr)) {
205 verify_cleanup(jcr, JS_ErrorTerminated);
209 if (!do_restore_init(jcr)) {
210 restore_cleanup(jcr, JS_ErrorTerminated);
214 if (!do_admin_init(jcr)) {
215 admin_cleanup(jcr, JS_ErrorTerminated);
220 if (!do_migration_init(jcr)) {
221 migration_cleanup(jcr, JS_ErrorTerminated);
225 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->get_JobType());
226 set_jcr_job_status(jcr, JS_ErrorTerminated);
230 generate_job_event(jcr, "JobInit");
238 void update_job_end(JCR *jcr, int TermCode)
240 dequeue_messages(jcr); /* display any queued messages */
241 set_jcr_job_status(jcr, TermCode);
242 update_job_end_record(jcr);
246 * This is the engine called by jobq.c:jobq_add() when we were pulled
247 * from the work queue.
248 * At this point, we are running in our own thread and all
249 * necessary resources are allocated -- see jobq.c
251 static void *job_thread(void *arg)
253 JCR *jcr = (JCR *)arg;
255 pthread_detach(pthread_self());
258 Dmsg0(200, "=====Start Job=========\n");
259 set_jcr_job_status(jcr, JS_Running); /* this will be set only if no error */
260 jcr->start_time = time(NULL); /* set the real start time */
261 jcr->jr.StartTime = jcr->start_time;
263 if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
264 (utime_t)(jcr->start_time - jcr->sched_time)) {
265 set_jcr_job_status(jcr, JS_Canceled);
266 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
269 if (job_check_maxschedruntime(jcr)) {
270 set_jcr_job_status(jcr, JS_Canceled);
271 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max sched run time exceeded.\n"));
274 /* TODO : check if it is used somewhere */
275 if (jcr->job->RunScripts == NULL) {
276 Dmsg0(200, "Warning, job->RunScripts is empty\n");
277 jcr->job->RunScripts = New(alist(10, not_owned_by_alist));
280 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
281 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
284 /* Run any script BeforeJob on dird */
285 run_scripts(jcr, jcr->job->RunScripts, "BeforeJob");
288 * We re-update the job start record so that the start
289 * time is set after the run before job. This avoids
290 * that any files created by the run before job will
291 * be saved twice. They will be backed up in the current
292 * job, but not in the next one unless they are changed.
293 * Without this, they will be backed up in this job and
294 * in the next job run because in that case, their date
295 * is after the start of this run.
297 jcr->start_time = time(NULL);
298 jcr->jr.StartTime = jcr->start_time;
299 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
300 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
302 generate_job_event(jcr, "JobRun");
304 switch (jcr->get_JobType()) {
306 if (!job_canceled(jcr) && do_backup(jcr)) {
309 backup_cleanup(jcr, JS_ErrorTerminated);
313 if (!job_canceled(jcr) && do_verify(jcr)) {
316 verify_cleanup(jcr, JS_ErrorTerminated);
320 if (!job_canceled(jcr) && do_restore(jcr)) {
323 restore_cleanup(jcr, JS_ErrorTerminated);
327 if (!job_canceled(jcr) && do_admin(jcr)) {
330 admin_cleanup(jcr, JS_ErrorTerminated);
335 if (!job_canceled(jcr) && do_migration(jcr)) {
338 migration_cleanup(jcr, JS_ErrorTerminated);
342 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->get_JobType());
346 run_scripts(jcr, jcr->job->RunScripts, "AfterJob");
348 /* Send off any queued messages */
349 if (jcr->msg_queue && jcr->msg_queue->size() > 0) {
350 dequeue_messages(jcr);
353 generate_daemon_event(jcr, "JobEnd");
354 Dmsg1(50, "======== End Job stat=%c ==========\n", jcr->JobStatus);
355 sm_check(__FILE__, __LINE__, true);
361 * Cancel a job -- typically called by the UA (Console program), but may also
362 * be called by the job watchdog.
364 * Returns: true if cancel appears to be successful
365 * false on failure. Message sent to ua->jcr.
367 bool cancel_job(UAContext *ua, JCR *jcr)
371 int32_t old_status = jcr->JobStatus;
373 set_jcr_job_status(jcr, JS_Canceled);
375 switch (old_status) {
378 case JS_WaitClientRes:
379 case JS_WaitStoreRes:
380 case JS_WaitPriority:
382 case JS_WaitStartTime:
383 ua->info_msg(_("JobId %s, Job %s marked to be canceled.\n"),
384 edit_uint64(jcr->JobId, ed1), jcr->Job);
385 jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
389 /* Cancel File daemon */
390 if (jcr->file_bsock) {
391 ua->jcr->client = jcr->client;
392 if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
393 ua->error_msg(_("Failed to connect to File daemon.\n"));
396 Dmsg0(200, "Connected to file daemon\n");
397 fd = ua->jcr->file_bsock;
398 fd->fsend("cancel Job=%s\n", jcr->Job);
399 while (fd->recv() >= 0) {
400 ua->send_msg("%s", fd->msg);
402 fd->signal(BNET_TERMINATE);
404 ua->jcr->file_bsock = NULL;
407 /* Cancel Storage daemon */
408 if (jcr->store_bsock) {
409 if (!ua->jcr->wstorage) {
411 copy_wstorage(ua->jcr, jcr->rstorage, _("Job resource"));
413 copy_wstorage(ua->jcr, jcr->wstorage, _("Job resource"));
418 store.store = jcr->rstore;
420 store.store = jcr->wstore;
422 set_wstorage(ua->jcr, &store);
425 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
426 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
429 Dmsg0(200, "Connected to storage daemon\n");
430 sd = ua->jcr->store_bsock;
431 sd->fsend("cancel Job=%s\n", jcr->Job);
432 while (sd->recv() >= 0) {
433 ua->send_msg("%s", sd->msg);
435 sd->signal(BNET_TERMINATE);
437 ua->jcr->store_bsock = NULL;
444 void cancel_storage_daemon_job(JCR *jcr)
446 UAContext *ua = new_ua_context(jcr);
447 JCR *control_jcr = new_control_jcr("*JobCancel*", JT_SYSTEM);
450 ua->jcr = control_jcr;
451 if (jcr->store_bsock) {
452 if (!ua->jcr->wstorage) {
454 copy_wstorage(ua->jcr, jcr->rstorage, _("Job resource"));
456 copy_wstorage(ua->jcr, jcr->wstorage, _("Job resource"));
461 store.store = jcr->rstore;
463 store.store = jcr->wstore;
465 set_wstorage(ua->jcr, &store);
468 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
471 Dmsg0(200, "Connected to storage daemon\n");
472 sd = ua->jcr->store_bsock;
473 sd->fsend("cancel Job=%s\n", jcr->Job);
474 while (sd->recv() >= 0) {
476 sd->signal(BNET_TERMINATE);
478 ua->jcr->store_bsock = NULL;
481 free_jcr(control_jcr);
485 static void job_monitor_destructor(watchdog_t *self)
487 JCR *control_jcr = (JCR *)self->data;
489 free_jcr(control_jcr);
492 static void job_monitor_watchdog(watchdog_t *self)
494 JCR *control_jcr, *jcr;
496 control_jcr = (JCR *)self->data;
499 Dmsg1(800, "job_monitor_watchdog %p called\n", self);
504 if (jcr->JobId == 0 || job_canceled(jcr)) {
505 Dmsg2(800, "Skipping JCR=%p Job=%s\n", jcr, jcr->Job);
509 /* check MaxWaitTime */
510 if (job_check_maxwaittime(jcr)) {
511 set_jcr_job_status(jcr, JS_Canceled);
512 Qmsg(jcr, M_FATAL, 0, _("Max wait time exceeded. Job canceled.\n"));
514 /* check MaxRunTime */
515 } else if (job_check_maxruntime(jcr)) {
516 set_jcr_job_status(jcr, JS_Canceled);
517 Qmsg(jcr, M_FATAL, 0, _("Max run time exceeded. Job canceled.\n"));
519 /* check MaxRunSchedTime */
520 } else if (job_check_maxschedruntime(jcr)) {
521 set_jcr_job_status(jcr, JS_Canceled);
522 Qmsg(jcr, M_FATAL, 0, _("Max sched run time exceeded. Job canceled.\n"));
527 Dmsg3(800, "Cancelling JCR %p jobid %d (%s)\n", jcr, jcr->JobId, jcr->Job);
528 UAContext *ua = new_ua_context(jcr);
529 ua->jcr = control_jcr;
532 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", jcr, jcr->JobId);
536 /* Keep reference counts correct */
541 * Check if the maxwaittime has expired and it is possible
544 static bool job_check_maxwaittime(JCR *jcr)
550 if (!job_waiting(jcr)) {
554 if (jcr->wait_time) {
555 current = watchdog_time - jcr->wait_time;
558 Dmsg2(200, "check maxwaittime %u >= %u\n",
559 current + jcr->wait_time_sum, job->MaxWaitTime);
560 if (job->MaxWaitTime != 0 &&
561 (current + jcr->wait_time_sum) >= job->MaxWaitTime) {
569 * Check if maxruntime has expired and if the job can be
572 static bool job_check_maxruntime(JCR *jcr)
577 if (job_canceled(jcr) || jcr->JobStatus == JS_Created) {
580 if (jcr->job->MaxRunTime == 0 && job->FullMaxRunTime == 0 &&
581 job->IncMaxRunTime == 0 && job->DiffMaxRunTime == 0) {
584 Dmsg6(200, "check_maxruntime %u - %u >= %u|%u|%u|%u\n\n",
585 watchdog_time, jcr->start_time, job->MaxRunTime, job->FullMaxRunTime,
586 job->IncMaxRunTime, job->DiffMaxRunTime);
588 if (jcr->get_JobLevel() == L_FULL && job->FullMaxRunTime != 0 &&
589 (watchdog_time - jcr->start_time) >= job->FullMaxRunTime) {
591 } else if (jcr->get_JobLevel() == L_DIFFERENTIAL && job->DiffMaxRunTime != 0 &&
592 (watchdog_time - jcr->start_time) >= job->DiffMaxRunTime) {
594 } else if (jcr->get_JobLevel() == L_INCREMENTAL && job->IncMaxRunTime != 0 &&
595 (watchdog_time - jcr->start_time) >= job->IncMaxRunTime) {
597 } else if ((watchdog_time - jcr->start_time) >= job->MaxRunTime) {
605 * Check if MaxRunSchedTime has expired and if the job can be
608 static bool job_check_maxschedruntime(JCR *jcr)
610 if (jcr->job->MaxRunSchedTime == 0 || job_canceled(jcr)) {
613 if ((watchdog_time - jcr->sched_time) < jcr->job->MaxRunSchedTime) {
614 Dmsg3(200, "Job %p (%s) with MaxRunSchedTime %d not expired\n",
615 jcr, jcr->Job, jcr->job->MaxRunSchedTime);
623 * Get or create a Pool record with the given name.
624 * Returns: 0 on error
627 DBId_t get_or_create_pool_record(JCR *jcr, char *pool_name)
631 memset(&pr, 0, sizeof(pr));
632 bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
633 Dmsg1(110, "get_or_create_pool=%s\n", pool_name);
635 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
636 /* Try to create the pool */
637 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
638 Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. ERR=%s"), pr.Name,
639 db_strerror(jcr->db));
642 Jmsg(jcr, M_INFO, 0, _("Created database record for Pool \"%s\".\n"), pr.Name);
649 * Check for duplicate jobs.
650 * Returns: true if current job should continue
651 * false if current job should terminate
653 bool allow_duplicate_job(JCR *jcr)
656 JCR *djcr; /* possible duplicate */
658 if (job->AllowDuplicateJobs) {
661 if (!job->AllowHigherDuplicates) {
664 if (strcmp(job->name(), djcr->job->name()) == 0) {
665 bool cancel_queued = false;
666 if (job->DuplicateJobProximity > 0) {
667 utime_t now = (utime_t)time(NULL);
668 if ((now - djcr->start_time) > job->DuplicateJobProximity) {
669 continue; /* not really a duplicate */
673 if (!(job->CancelQueuedDuplicates || job->CancelRunningDuplicates)) {
674 /* Zap current job */
675 Jmsg(jcr, M_FATAL, 0, _("Duplicate job not allowed. JobId=%s\n"),
676 edit_uint64(djcr->JobId, ec1));
679 /* If CancelQueuedDuplicates is set do so only if job is queued */
680 if (job->CancelQueuedDuplicates) {
681 switch (djcr->JobStatus) {
684 case JS_WaitClientRes:
685 case JS_WaitStoreRes:
686 case JS_WaitPriority:
688 case JS_WaitStartTime:
689 cancel_queued = true;
695 if (cancel_queued || job->CancelRunningDuplicates) {
696 UAContext *ua = new_ua_context(djcr);
697 Jmsg(jcr, M_INFO, 0, _("Cancelling duplicate JobId=%s.\n"),
698 edit_uint64(djcr->JobId, ec1));
700 cancel_job(ua, djcr);
702 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", djcr, djcr->JobId);
711 void apply_pool_overrides(JCR *jcr)
713 bool pool_override = false;
715 if (jcr->run_pool_override) {
716 pm_strcpy(jcr->pool_source, _("Run pool override"));
719 * Apply any level related Pool selections
721 switch (jcr->get_JobLevel()) {
723 if (jcr->full_pool) {
724 jcr->pool = jcr->full_pool;
725 pool_override = true;
726 if (jcr->run_full_pool_override) {
727 pm_strcpy(jcr->pool_source, _("Run FullPool override"));
729 pm_strcpy(jcr->pool_source, _("Job FullPool override"));
735 jcr->pool = jcr->inc_pool;
736 pool_override = true;
737 if (jcr->run_inc_pool_override) {
738 pm_strcpy(jcr->pool_source, _("Run IncPool override"));
740 pm_strcpy(jcr->pool_source, _("Job IncPool override"));
745 if (jcr->diff_pool) {
746 jcr->pool = jcr->diff_pool;
747 pool_override = true;
748 if (jcr->run_diff_pool_override) {
749 pm_strcpy(jcr->pool_source, _("Run DiffPool override"));
751 pm_strcpy(jcr->pool_source, _("Job DiffPool override"));
756 /* Update catalog if pool overridden */
757 if (pool_override && jcr->pool->catalog) {
758 jcr->catalog = jcr->pool->catalog;
759 pm_strcpy(jcr->catalog_source, _("Pool resource"));
765 * Get or create a Client record for this Job
767 bool get_or_create_client_record(JCR *jcr)
771 memset(&cr, 0, sizeof(cr));
772 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
773 cr.AutoPrune = jcr->client->AutoPrune;
774 cr.FileRetention = jcr->client->FileRetention;
775 cr.JobRetention = jcr->client->JobRetention;
776 if (!jcr->client_name) {
777 jcr->client_name = get_pool_memory(PM_NAME);
779 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
780 if (!db_create_client_record(jcr, jcr->db, &cr)) {
781 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"),
782 db_strerror(jcr->db));
785 jcr->jr.ClientId = cr.ClientId;
787 if (!jcr->client_uname) {
788 jcr->client_uname = get_pool_memory(PM_NAME);
790 pm_strcpy(jcr->client_uname, cr.Uname);
792 Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name,
797 bool get_or_create_fileset_record(JCR *jcr)
801 * Get or Create FileSet record
803 memset(&fsr, 0, sizeof(FILESET_DBR));
804 bstrncpy(fsr.FileSet, jcr->fileset->hdr.name, sizeof(fsr.FileSet));
805 if (jcr->fileset->have_MD5) {
806 struct MD5Context md5c;
807 unsigned char digest[MD5HashSize];
808 memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
809 MD5Final(digest, &md5c);
811 * Keep the flag (last arg) set to false otherwise old FileSets will
812 * get new MD5 sums and the user will get Full backups on everything
814 bin_to_base64(fsr.MD5, sizeof(fsr.MD5), (char *)digest, MD5HashSize, false);
815 bstrncpy(jcr->fileset->MD5, fsr.MD5, sizeof(jcr->fileset->MD5));
817 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n"));
819 if (!jcr->fileset->ignore_fs_changes ||
820 !db_get_fileset_record(jcr, jcr->db, &fsr)) {
821 if (!db_create_fileset_record(jcr, jcr->db, &fsr)) {
822 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
823 fsr.FileSet, db_strerror(jcr->db));
827 jcr->jr.FileSetId = fsr.FileSetId;
828 bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime));
829 Dmsg2(119, "Created FileSet %s record %u\n", jcr->fileset->hdr.name,
834 void init_jcr_job_record(JCR *jcr)
836 jcr->jr.SchedTime = jcr->sched_time;
837 jcr->jr.StartTime = jcr->start_time;
838 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
839 jcr->jr.JobType = jcr->get_JobType();
840 jcr->jr.JobLevel = jcr->get_JobLevel();
841 jcr->jr.JobStatus = jcr->JobStatus;
842 jcr->jr.JobId = jcr->JobId;
843 bstrncpy(jcr->jr.Name, jcr->job->name(), sizeof(jcr->jr.Name));
844 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
848 * Write status and such in DB
850 void update_job_end_record(JCR *jcr)
852 jcr->jr.EndTime = time(NULL);
853 jcr->end_time = jcr->jr.EndTime;
854 jcr->jr.JobId = jcr->JobId;
855 jcr->jr.JobStatus = jcr->JobStatus;
856 jcr->jr.JobFiles = jcr->JobFiles;
857 jcr->jr.JobBytes = jcr->JobBytes;
858 jcr->jr.VolSessionId = jcr->VolSessionId;
859 jcr->jr.VolSessionTime = jcr->VolSessionTime;
860 jcr->jr.JobErrors = jcr->Errors;
861 if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
862 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
863 db_strerror(jcr->db));
868 * Takes base_name and appends (unique) current
869 * date and time to form unique job name.
871 * Note, the seconds are actually a sequence number. This
872 * permits us to start a maximum fo 59 unique jobs a second, which
873 * should be sufficient.
875 * Returns: unique job name in jcr->Job
876 * date/time in jcr->start_time
878 void create_unique_job_name(JCR *jcr, const char *base_name)
880 /* Job start mutex */
881 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
882 static time_t last_start_time = 0;
884 time_t now = time(NULL);
886 char dt[MAX_TIME_LENGTH];
887 char name[MAX_NAME_LENGTH];
891 /* Guarantee unique start time -- maximum one per second, and
892 * thus unique Job Name
894 P(mutex); /* lock creation of jobs */
896 if (seq > 59) { /* wrap as if it is seconds */
898 while (now == last_start_time) {
899 bmicrosleep(0, 500000);
903 last_start_time = now;
904 V(mutex); /* allow creation of jobs */
905 jcr->start_time = now;
906 /* Form Unique JobName */
907 (void)localtime_r(&now, &tm);
908 /* Use only characters that are permitted in Windows filenames */
909 strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
910 len = strlen(dt) + 5; /* dt + .%02d EOS */
911 bstrncpy(name, base_name, sizeof(name));
912 name[sizeof(name)-len] = 0; /* truncate if too long */
913 bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s.%02d", name, dt, seq); /* add date & time */
914 /* Convert spaces into underscores */
915 for (p=jcr->Job; *p; p++) {
920 Dmsg2(100, "JobId=%u created Job=%s\n", jcr->JobId, jcr->Job);
923 /* Called directly from job rescheduling */
924 void dird_free_jcr_pointers(JCR *jcr)
926 if (jcr->sd_auth_key) {
927 free(jcr->sd_auth_key);
928 jcr->sd_auth_key = NULL;
934 if (jcr->file_bsock) {
935 Dmsg0(200, "Close File bsock\n");
936 bnet_close(jcr->file_bsock);
937 jcr->file_bsock = NULL;
939 if (jcr->store_bsock) {
940 Dmsg0(200, "Close Store bsock\n");
941 bnet_close(jcr->store_bsock);
942 jcr->store_bsock = NULL;
945 Dmsg0(200, "Free JCR fname\n");
946 free_pool_memory(jcr->fname);
949 if (jcr->RestoreBootstrap) {
950 free(jcr->RestoreBootstrap);
951 jcr->RestoreBootstrap = NULL;
953 if (jcr->client_uname) {
954 free_pool_memory(jcr->client_uname);
955 jcr->client_uname = NULL;
958 free_pool_memory(jcr->attr);
968 * Free the Job Control Record if no one is still using it.
969 * Called from main free_jcr() routine in src/lib/jcr.c so
970 * that we can do our Director specific cleanup of the jcr.
972 void dird_free_jcr(JCR *jcr)
974 Dmsg0(200, "Start dird free_jcr\n");
976 dird_free_jcr_pointers(jcr);
977 if (jcr->term_wait_inited) {
978 pthread_cond_destroy(&jcr->term_wait);
979 jcr->term_wait_inited = false;
982 db_close_database(jcr, jcr->db_batch);
983 jcr->db_batch = NULL;
984 jcr->batch_started = false;
987 db_close_database(jcr, jcr->db);
991 Dmsg0(200, "Free JCR stime\n");
992 free_pool_memory(jcr->stime);
996 Dmsg0(200, "Free JCR fname\n");
997 free_pool_memory(jcr->fname);
1000 if (jcr->pool_source) {
1001 free_pool_memory(jcr->pool_source);
1002 jcr->pool_source = NULL;
1004 if (jcr->catalog_source) {
1005 free_pool_memory(jcr->catalog_source);
1006 jcr->catalog_source = NULL;
1008 if (jcr->rpool_source) {
1009 free_pool_memory(jcr->rpool_source);
1010 jcr->rpool_source = NULL;
1012 if (jcr->wstore_source) {
1013 free_pool_memory(jcr->wstore_source);
1014 jcr->wstore_source = NULL;
1016 if (jcr->rstore_source) {
1017 free_pool_memory(jcr->rstore_source);
1018 jcr->rstore_source = NULL;
1021 /* Delete lists setup to hold storage pointers */
1022 free_rwstorage(jcr);
1024 jcr->job_end_push.destroy();
1026 if (jcr->JobId != 0)
1027 write_state_file(director->working_directory, "bacula-dir", get_first_port_host_order(director->DIRaddrs));
1029 Dmsg0(200, "End dird free_jcr\n");
1033 * The Job storage definition must be either in the Job record
1034 * or in the Pool record. The Pool record overrides the Job
1037 void get_job_storage(USTORE *store, JOB *job, RUN *run)
1039 if (run && run->pool && run->pool->storage) {
1040 store->store = (STORE *)run->pool->storage->first();
1041 pm_strcpy(store->store_source, _("Run pool override"));
1044 if (run && run->storage) {
1045 store->store = run->storage;
1046 pm_strcpy(store->store_source, _("Run storage override"));
1049 if (job->pool->storage) {
1050 store->store = (STORE *)job->pool->storage->first();
1051 pm_strcpy(store->store_source, _("Pool resource"));
1053 store->store = (STORE *)job->storage->first();
1054 pm_strcpy(store->store_source, _("Job resource"));
1059 * Set some defaults in the JCR necessary to
1060 * run. These items are pulled from the job
1061 * definition as defaults, but can be overridden
1062 * later either by the Run record in the Schedule resource,
1063 * or by the Console program.
1065 void set_jcr_defaults(JCR *jcr, JOB *job)
1068 jcr->set_JobType(job->JobType);
1069 jcr->JobStatus = JS_Created;
1071 switch (jcr->get_JobType()) {
1073 jcr->set_JobLevel(L_NONE);
1076 jcr->set_JobLevel(job->JobLevel);
1081 jcr->fname = get_pool_memory(PM_FNAME);
1083 if (!jcr->pool_source) {
1084 jcr->pool_source = get_pool_memory(PM_MESSAGE);
1085 pm_strcpy(jcr->pool_source, _("unknown source"));
1087 if (!jcr->catalog_source) {
1088 jcr->catalog_source = get_pool_memory(PM_MESSAGE);
1089 pm_strcpy(jcr->catalog_source, _("unknown source"));
1092 jcr->JobPriority = job->Priority;
1093 /* Copy storage definitions -- deleted in dir_free_jcr above */
1095 copy_rwstorage(jcr, job->storage, _("Job resource"));
1097 copy_rwstorage(jcr, job->pool->storage, _("Pool resource"));
1099 jcr->client = job->client;
1100 if (!jcr->client_name) {
1101 jcr->client_name = get_pool_memory(PM_NAME);
1103 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
1104 pm_strcpy(jcr->pool_source, _("Job resource"));
1105 jcr->pool = job->pool;
1106 jcr->full_pool = job->full_pool;
1107 jcr->inc_pool = job->inc_pool;
1108 jcr->diff_pool = job->diff_pool;
1109 if (job->pool->catalog) {
1110 jcr->catalog = job->pool->catalog;
1111 pm_strcpy(jcr->catalog_source, _("Pool resource"));
1113 jcr->catalog = job->client->catalog;
1114 pm_strcpy(jcr->catalog_source, _("Client resource"));
1116 jcr->fileset = job->fileset;
1117 jcr->messages = job->messages;
1118 jcr->spool_data = job->spool_data;
1119 jcr->spool_size = job->spool_size;
1120 jcr->write_part_after_job = job->write_part_after_job;
1121 jcr->accurate = job->accurate;
1122 if (jcr->RestoreBootstrap) {
1123 free(jcr->RestoreBootstrap);
1124 jcr->RestoreBootstrap = NULL;
1126 /* This can be overridden by Console program */
1127 if (job->RestoreBootstrap) {
1128 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
1130 /* This can be overridden by Console program */
1131 jcr->verify_job = job->verify_job;
1132 /* If no default level given, set one */
1133 if (jcr->get_JobLevel() == 0) {
1134 switch (jcr->get_JobType()) {
1136 jcr->set_JobLevel(L_VERIFY_CATALOG);
1139 jcr->set_JobLevel(L_INCREMENTAL);
1143 jcr->set_JobLevel(L_NONE);
1146 jcr->set_JobLevel(L_FULL);
1153 * Copy the storage definitions from an alist to the JCR
1155 void copy_rwstorage(JCR *jcr, alist *storage, const char *where)
1157 if (jcr->JobReads()) {
1158 copy_rstorage(jcr, storage, where);
1160 copy_wstorage(jcr, storage, where);
1164 /* Set storage override. Releases any previous storage definition */
1165 void set_rwstorage(JCR *jcr, USTORE *store)
1168 Jmsg(jcr, M_FATAL, 0, _("No storage specified.\n"));
1171 if (jcr->JobReads()) {
1172 set_rstorage(jcr, store);
1174 set_wstorage(jcr, store);
1177 void free_rwstorage(JCR *jcr)
1184 * Copy the storage definitions from an alist to the JCR
1186 void copy_rstorage(JCR *jcr, alist *storage, const char *where)
1190 if (jcr->rstorage) {
1191 delete jcr->rstorage;
1193 jcr->rstorage = New(alist(10, not_owned_by_alist));
1194 foreach_alist(st, storage) {
1195 jcr->rstorage->append(st);
1197 if (!jcr->rstore_source) {
1198 jcr->rstore_source = get_pool_memory(PM_MESSAGE);
1200 pm_strcpy(jcr->rstore_source, where);
1201 if (jcr->rstorage) {
1202 jcr->rstore = (STORE *)jcr->rstorage->first();
1208 /* Set storage override. Remove all previous storage */
1209 void set_rstorage(JCR *jcr, USTORE *store)
1213 if (!store->store) {
1216 if (jcr->rstorage) {
1219 if (!jcr->rstorage) {
1220 jcr->rstorage = New(alist(10, not_owned_by_alist));
1222 jcr->rstore = store->store;
1223 if (!jcr->rstore_source) {
1224 jcr->rstore_source = get_pool_memory(PM_MESSAGE);
1226 pm_strcpy(jcr->rstore_source, store->store_source);
1227 foreach_alist(storage, jcr->rstorage) {
1228 if (store->store == storage) {
1232 /* Store not in list, so add it */
1233 jcr->rstorage->prepend(store->store);
1236 void free_rstorage(JCR *jcr)
1238 if (jcr->rstorage) {
1239 delete jcr->rstorage;
1240 jcr->rstorage = NULL;
1246 * Copy the storage definitions from an alist to the JCR
1248 void copy_wstorage(JCR *jcr, alist *storage, const char *where)
1252 if (jcr->wstorage) {
1253 delete jcr->wstorage;
1255 jcr->wstorage = New(alist(10, not_owned_by_alist));
1256 foreach_alist(st, storage) {
1257 Dmsg1(100, "wstorage=%s\n", st->name());
1258 jcr->wstorage->append(st);
1260 if (!jcr->wstore_source) {
1261 jcr->wstore_source = get_pool_memory(PM_MESSAGE);
1263 pm_strcpy(jcr->wstore_source, where);
1264 if (jcr->wstorage) {
1265 jcr->wstore = (STORE *)jcr->wstorage->first();
1266 Dmsg2(100, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
1272 /* Set storage override. Remove all previous storage */
1273 void set_wstorage(JCR *jcr, USTORE *store)
1277 if (!store->store) {
1280 if (jcr->wstorage) {
1283 if (!jcr->wstorage) {
1284 jcr->wstorage = New(alist(10, not_owned_by_alist));
1286 jcr->wstore = store->store;
1287 if (!jcr->wstore_source) {
1288 jcr->wstore_source = get_pool_memory(PM_MESSAGE);
1290 pm_strcpy(jcr->wstore_source, store->store_source);
1291 Dmsg2(50, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
1292 foreach_alist(storage, jcr->wstorage) {
1293 if (store->store == storage) {
1297 /* Store not in list, so add it */
1298 jcr->wstorage->prepend(store->store);
1301 void free_wstorage(JCR *jcr)
1303 if (jcr->wstorage) {
1304 delete jcr->wstorage;
1305 jcr->wstorage = NULL;
1310 char *job_code_callback_clones(JCR *jcr, const char* param)
1312 if (param[0] == 'p') {
1313 return jcr->pool->name();
1318 void create_clones(JCR *jcr)
1321 * Fire off any clone jobs (run directives)
1323 Dmsg2(900, "cloned=%d run_cmds=%p\n", jcr->cloned, jcr->job->run_cmds);
1324 if (!jcr->cloned && jcr->job->run_cmds) {
1326 JOB *job = jcr->job;
1327 POOLMEM *cmd = get_pool_memory(PM_FNAME);
1328 UAContext *ua = new_ua_context(jcr);
1330 foreach_alist(runcmd, job->run_cmds) {
1331 cmd = edit_job_codes(jcr, cmd, runcmd, "", job_code_callback_clones);
1332 Mmsg(ua->cmd, "run %s cloned=yes", cmd);
1333 Dmsg1(900, "=============== Clone cmd=%s\n", ua->cmd);
1334 parse_ua_args(ua); /* parse command */
1335 int stat = run_cmd(ua, ua->cmd);
1337 Jmsg(jcr, M_ERROR, 0, _("Could not start clone job.\n"));
1339 Jmsg(jcr, M_INFO, 0, _("Clone JobId %d started.\n"), stat);
1342 free_ua_context(ua);
1343 free_pool_memory(cmd);
1348 * Given: a JobId in jcr->previous_jr.JobId,
1349 * this subroutine writes a bsr file to restore that job.
1351 bool create_restore_bootstrap_file(JCR *jcr)
1355 memset(&rx, 0, sizeof(rx));
1357 rx.JobIds = (char *)"";
1358 rx.bsr->JobId = jcr->previous_jr.JobId;
1359 ua = new_ua_context(jcr);
1360 complete_bsr(ua, rx.bsr);
1361 rx.bsr->fi = new_findex();
1362 rx.bsr->fi->findex = 1;
1363 rx.bsr->fi->findex2 = jcr->previous_jr.JobFiles;
1364 jcr->ExpectedFiles = write_bsr_file(ua, rx);
1365 if (jcr->ExpectedFiles == 0) {
1366 free_ua_context(ua);
1370 free_ua_context(ua);
1372 jcr->needs_sd = true;
1376 /* TODO: redirect command ouput to job log */
1377 bool run_console_command(JCR *jcr, const char *cmd){
1380 JCR *ljcr = new_control_jcr("-RunScript-", JT_CONSOLE);
1381 ua = new_ua_context(ljcr);
1382 /* run from runscript and check if commands are autorized */
1383 ua->runscript = true;
1384 Mmsg(ua->cmd, "%s", cmd);
1385 Dmsg1(100, "Console command: %s\n", ua->cmd);
1387 ok= do_a_command(ua);
1388 free_ua_context(ua);