3 * Bacula Director Job processing routines
5 * Kern Sibbald, October MM
10 Copyright (C) 2000-2005 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as ammended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
27 /* Forward referenced subroutines */
28 static void *job_thread(void *arg);
29 static void job_monitor_watchdog(watchdog_t *self);
30 static void job_monitor_destructor(watchdog_t *self);
31 static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr);
32 static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr);
34 /* Imported subroutines */
35 extern void term_scheduler();
36 extern void term_ua_server();
38 /* Imported variables */
39 extern time_t watchdog_time;
43 void init_job_server(int max_workers)
48 if ((stat = jobq_init(&job_queue, max_workers, job_thread)) != 0) {
50 Emsg1(M_ABORT, 0, _("Could not init job queue: ERR=%s\n"), be.strerror(stat));
53 wd->callback = job_monitor_watchdog;
54 wd->destructor = job_monitor_destructor;
57 wd->data = new_control_jcr("*JobMonitor*", JT_SYSTEM);
58 register_watchdog(wd);
61 void term_job_server()
63 jobq_destroy(&job_queue); /* ignore any errors */
67 * Run a job -- typically called by the scheduler, but may also
68 * be called by the UA (Console program).
70 * Returns: 0 on failure
74 JobId_t run_job(JCR *jcr)
80 sm_check(__FILE__, __LINE__, true);
81 init_msg(jcr, jcr->messages);
83 /* Initialize termination condition variable */
84 if ((errstat = pthread_cond_init(&jcr->term_wait, NULL)) != 0) {
86 Jmsg1(jcr, M_FATAL, 0, _("Unable to init job cond variable: ERR=%s\n"), be.strerror(errstat));
89 jcr->term_wait_inited = true;
95 Dmsg0(50, "Open database\n");
96 jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
97 jcr->catalog->db_password, jcr->catalog->db_address,
98 jcr->catalog->db_port, jcr->catalog->db_socket,
99 jcr->catalog->mult_db_connections);
100 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
101 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
102 jcr->catalog->db_name);
104 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
108 Dmsg0(50, "DB opened\n");
113 create_unique_job_name(jcr, jcr->job->hdr.name);
114 set_jcr_job_status(jcr, JS_Created);
115 init_jcr_job_record(jcr);
116 if (!db_create_job_record(jcr, jcr->db, &jcr->jr)) {
117 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
120 JobId = jcr->JobId = jcr->jr.JobId;
121 Dmsg4(100, "Created job record JobId=%d Name=%s Type=%c Level=%c\n",
122 jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel);
124 generate_daemon_event(jcr, "JobStart");
126 if (!get_or_create_client_record(jcr)) {
130 if (job_canceled(jcr)) {
134 Dmsg0(200, "Add jrc to work queue\n");
136 /* Queue the job to be run */
137 if ((stat = jobq_add(&job_queue, jcr)) != 0) {
139 Jmsg(jcr, M_FATAL, 0, _("Could not add job queue: ERR=%s\n"), be.strerror(stat));
143 Dmsg0(100, "Done run_job()\n");
150 free_memory(jcr->fname);
159 * This is the engine called by jobq.c:jobq_add() when we were pulled
160 * from the work queue.
161 * At this point, we are running in our own thread and all
162 * necessary resources are allocated -- see jobq.c
164 static void *job_thread(void *arg)
166 JCR *jcr = (JCR *)arg;
168 jcr->my_thread_id = pthread_self();
169 pthread_detach(jcr->my_thread_id);
170 sm_check(__FILE__, __LINE__, true);
172 Dmsg0(200, "=====Start Job=========\n");
173 jcr->start_time = time(NULL); /* set the real start time */
174 jcr->jr.StartTime = jcr->start_time;
176 if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
177 (utime_t)(jcr->start_time - jcr->sched_time)) {
178 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
179 set_jcr_job_status(jcr, JS_Canceled);
183 * Note, we continue, even if the job is canceled above. This
184 * will permit proper setting of the job start record and
185 * the error (cancel) will be picked up below.
188 generate_job_event(jcr, "JobInit");
189 set_jcr_job_status(jcr, JS_Running); /* this will be set only if no error */
192 jcr->fname = get_pool_memory(PM_FNAME);
196 * Now, do pre-run stuff, like setting job level (Inc/diff, ...)
197 * this allows us to setup a proper job start record for restarting
198 * in case of later errors.
200 switch (jcr->JobType) {
202 if (!do_backup_init(jcr)) {
203 backup_cleanup(jcr, JS_ErrorTerminated);
207 if (!do_verify_init(jcr)) {
208 verify_cleanup(jcr, JS_ErrorTerminated);
212 if (!do_restore_init(jcr)) {
213 restore_cleanup(jcr, JS_ErrorTerminated);
217 if (!do_admin_init(jcr)) {
218 admin_cleanup(jcr, JS_ErrorTerminated);
224 if (!do_mac_init(jcr)) { /* migration, archive, copy */
225 mac_cleanup(jcr, JS_ErrorTerminated);
229 Pmsg1(0, "Unimplemented job type: %d\n", jcr->JobType);
230 set_jcr_job_status(jcr, JS_ErrorTerminated);
234 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
235 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
238 if (job_canceled(jcr)) {
239 update_job_end_record(jcr);
244 if (jcr->job->RunBeforeJob) {
245 POOLMEM *before = get_pool_memory(PM_FNAME);
248 char line[MAXSTRING];
250 before = edit_job_codes(jcr, before, jcr->job->RunBeforeJob, "");
251 bpipe = open_bpipe(before, 0, "r");
252 free_pool_memory(before);
253 while (fgets(line, sizeof(line), bpipe->rfd)) {
254 Jmsg(jcr, M_INFO, 0, _("RunBefore: %s"), line);
256 status = close_bpipe(bpipe);
259 Jmsg(jcr, M_FATAL, 0, _("RunBeforeJob error: ERR=%s\n"), be.strerror(status));
260 set_jcr_job_status(jcr, JS_FatalError);
261 update_job_end_record(jcr);
266 generate_job_event(jcr, "JobRun");
268 switch (jcr->JobType) {
270 if (do_backup(jcr)) {
273 backup_cleanup(jcr, JS_ErrorTerminated);
277 if (do_verify(jcr)) {
280 verify_cleanup(jcr, JS_ErrorTerminated);
284 if (do_restore(jcr)) {
287 restore_cleanup(jcr, JS_ErrorTerminated);
294 admin_cleanup(jcr, JS_ErrorTerminated);
300 if (do_mac(jcr)) { /* migration, archive, copy */
303 mac_cleanup(jcr, JS_ErrorTerminated);
307 Pmsg1(0, "Unimplemented job type: %d\n", jcr->JobType);
310 if ((jcr->job->RunAfterJob && jcr->JobStatus == JS_Terminated) ||
311 (jcr->job->RunAfterFailedJob && jcr->JobStatus != JS_Terminated)) {
312 POOLMEM *after = get_pool_memory(PM_FNAME);
315 char line[MAXSTRING];
317 if (jcr->JobStatus == JS_Terminated) {
318 after = edit_job_codes(jcr, after, jcr->job->RunAfterJob, "");
320 after = edit_job_codes(jcr, after, jcr->job->RunAfterFailedJob, "");
322 bpipe = open_bpipe(after, 0, "r");
323 free_pool_memory(after);
324 while (fgets(line, sizeof(line), bpipe->rfd)) {
325 Jmsg(jcr, M_INFO, 0, _("RunAfter: %s"), line);
327 status = close_bpipe(bpipe);
329 * Note, if we get an error here, do not mark the
330 * job in error, simply report the error condition.
334 if (jcr->JobStatus == JS_Terminated) {
335 Jmsg(jcr, M_WARNING, 0, _("RunAfterJob error: ERR=%s\n"), be.strerror(status));
337 Jmsg(jcr, M_FATAL, 0, _("RunAfterFailedJob error: ERR=%s\n"), be.strerror(status));
341 /* Send off any queued messages */
342 if (jcr->msg_queue->size() > 0) {
343 dequeue_messages(jcr);
348 generate_daemon_event(jcr, "JobEnd");
349 Dmsg1(50, "======== End Job stat=%c ==========\n", jcr->JobStatus);
350 sm_check(__FILE__, __LINE__, true);
356 * Cancel a job -- typically called by the UA (Console program), but may also
357 * be called by the job watchdog.
359 * Returns: 1 if cancel appears to be successful
360 * 0 on failure. Message sent to ua->jcr.
362 int cancel_job(UAContext *ua, JCR *jcr)
366 set_jcr_job_status(jcr, JS_Canceled);
368 switch (jcr->JobStatus) {
371 case JS_WaitClientRes:
372 case JS_WaitStoreRes:
373 case JS_WaitPriority:
375 case JS_WaitStartTime:
376 bsendmsg(ua, _("JobId %d, Job %s marked to be canceled.\n"),
377 jcr->JobId, jcr->Job);
378 jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
383 /* Cancel File daemon */
384 if (jcr->file_bsock) {
385 ua->jcr->client = jcr->client;
386 if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
387 bsendmsg(ua, _("Failed to connect to File daemon.\n"));
390 Dmsg0(200, "Connected to file daemon\n");
391 fd = ua->jcr->file_bsock;
392 bnet_fsend(fd, "cancel Job=%s\n", jcr->Job);
393 while (bnet_recv(fd) >= 0) {
394 bsendmsg(ua, "%s", fd->msg);
396 bnet_sig(fd, BNET_TERMINATE);
398 ua->jcr->file_bsock = NULL;
401 /* Cancel Storage daemon */
402 if (jcr->store_bsock) {
403 if (!ua->jcr->storage) {
404 copy_storage(ua->jcr, jcr);
406 set_storage(ua->jcr, jcr->store);
408 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
409 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
412 Dmsg0(200, "Connected to storage daemon\n");
413 sd = ua->jcr->store_bsock;
414 bnet_fsend(sd, "cancel Job=%s\n", jcr->Job);
415 while (bnet_recv(sd) >= 0) {
416 bsendmsg(ua, "%s", sd->msg);
418 bnet_sig(sd, BNET_TERMINATE);
420 ua->jcr->store_bsock = NULL;
428 static void job_monitor_destructor(watchdog_t *self)
430 JCR *control_jcr = (JCR *) self->data;
432 free_jcr(control_jcr);
435 static void job_monitor_watchdog(watchdog_t *self)
437 JCR *control_jcr, *jcr;
439 control_jcr = (JCR *)self->data;
441 Dmsg1(800, "job_monitor_watchdog %p called\n", self);
446 if (jcr->JobId == 0) {
447 Dmsg2(800, "Skipping JCR %p (%s) with JobId 0\n",
449 /* Keep reference counts correct */
454 /* check MaxWaitTime */
455 cancel = job_check_maxwaittime(control_jcr, jcr);
457 /* check MaxRunTime */
458 cancel |= job_check_maxruntime(control_jcr, jcr);
461 Dmsg3(800, "Cancelling JCR %p jobid %d (%s)\n",
462 jcr, jcr->JobId, jcr->Job);
464 UAContext *ua = new_ua_context(jcr);
465 ua->jcr = control_jcr;
469 Dmsg1(800, "Have cancelled JCR %p\n", jcr);
472 /* Keep reference counts correct */
478 * Check if the maxwaittime has expired and it is possible
481 static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr)
484 bool ok_to_cancel = false;
487 if (job->MaxWaitTime == 0 && job->FullMaxWaitTime == 0 &&
488 job->IncMaxWaitTime == 0 && job->DiffMaxWaitTime == 0) {
491 if (jcr->JobLevel == L_FULL && job->FullMaxWaitTime != 0 &&
492 (watchdog_time - jcr->start_time) >= job->FullMaxWaitTime) {
494 } else if (jcr->JobLevel == L_DIFFERENTIAL && job->DiffMaxWaitTime != 0 &&
495 (watchdog_time - jcr->start_time) >= job->DiffMaxWaitTime) {
497 } else if (jcr->JobLevel == L_INCREMENTAL && job->IncMaxWaitTime != 0 &&
498 (watchdog_time - jcr->start_time) >= job->IncMaxWaitTime) {
500 } else if (job->MaxWaitTime != 0 &&
501 (watchdog_time - jcr->start_time) >= job->MaxWaitTime) {
507 Dmsg3(800, "Job %d (%s): MaxWaitTime of %d seconds exceeded, "
509 jcr->JobId, jcr->Job, job->MaxWaitTime);
510 switch (jcr->JobStatus) {
515 case JS_WaitStoreRes:
516 case JS_WaitClientRes:
518 case JS_WaitPriority:
520 case JS_WaitStartTime:
522 Dmsg0(200, "JCR blocked in #1\n");
525 Dmsg0(800, "JCR running, checking SD status\n");
526 switch (jcr->SDJobStatus) {
531 Dmsg0(800, "JCR blocked in #2\n");
534 Dmsg0(800, "JCR not blocked in #2\n");
539 case JS_ErrorTerminated:
542 Dmsg0(800, "JCR already dead in #3\n");
545 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
548 Dmsg3(800, "MaxWaitTime result: %scancel JCR %p (%s)\n",
549 cancel ? "" : "do not ", jcr, jcr->job);
555 * Check if maxruntime has expired and if the job can be
558 static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr)
562 if (jcr->job->MaxRunTime == 0) {
565 if ((watchdog_time - jcr->start_time) < jcr->job->MaxRunTime) {
566 Dmsg3(200, "Job %p (%s) with MaxRunTime %d not expired\n",
567 jcr, jcr->Job, jcr->job->MaxRunTime);
571 switch (jcr->JobStatus) {
577 case JS_WaitStoreRes:
578 case JS_WaitClientRes:
580 case JS_WaitPriority:
582 case JS_WaitStartTime:
587 case JS_ErrorTerminated:
593 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
597 Dmsg3(200, "MaxRunTime result: %scancel JCR %p (%s)\n",
598 cancel ? "" : "do not ", jcr, jcr->job);
605 * Get or create a Client record for this Job
607 bool get_or_create_client_record(JCR *jcr)
611 memset(&cr, 0, sizeof(cr));
612 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
613 cr.AutoPrune = jcr->client->AutoPrune;
614 cr.FileRetention = jcr->client->FileRetention;
615 cr.JobRetention = jcr->client->JobRetention;
616 if (!jcr->client_name) {
617 jcr->client_name = get_pool_memory(PM_NAME);
619 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
620 if (!db_create_client_record(jcr, jcr->db, &cr)) {
621 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"),
622 db_strerror(jcr->db));
625 jcr->jr.ClientId = cr.ClientId;
627 if (!jcr->client_uname) {
628 jcr->client_uname = get_pool_memory(PM_NAME);
630 pm_strcpy(jcr->client_uname, cr.Uname);
632 Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name,
637 bool get_or_create_fileset_record(JCR *jcr, FILESET_DBR *fsr)
640 * Get or Create FileSet record
642 memset(fsr, 0, sizeof(FILESET_DBR));
643 bstrncpy(fsr->FileSet, jcr->fileset->hdr.name, sizeof(fsr->FileSet));
644 if (jcr->fileset->have_MD5) {
645 struct MD5Context md5c;
646 unsigned char signature[16];
647 memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
648 MD5Final(signature, &md5c);
649 bin_to_base64(fsr->MD5, (char *)signature, 16); /* encode 16 bytes */
650 bstrncpy(jcr->fileset->MD5, fsr->MD5, sizeof(jcr->fileset->MD5));
652 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 signature not found.\n"));
654 if (!jcr->fileset->ignore_fs_changes ||
655 !db_get_fileset_record(jcr, jcr->db, fsr)) {
656 if (!db_create_fileset_record(jcr, jcr->db, fsr)) {
657 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
658 fsr->FileSet, db_strerror(jcr->db));
662 jcr->jr.FileSetId = fsr->FileSetId;
664 if (fsr->created && jcr != NULL) {
665 Jmsg(jcr, M_INFO, 0, _("Created new FileSet record \"%s\" %s\n"),
666 fsr->FileSet, fsr->cCreateTime);
669 Dmsg2(119, "Created FileSet %s record %u\n", jcr->fileset->hdr.name,
674 void init_jcr_job_record(JCR *jcr)
676 jcr->jr.SchedTime = jcr->sched_time;
677 jcr->jr.StartTime = jcr->start_time;
678 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
679 jcr->jr.JobType = jcr->JobType;
680 jcr->jr.JobLevel = jcr->JobLevel;
681 jcr->jr.JobStatus = jcr->JobStatus;
682 jcr->jr.JobId = jcr->JobId;
683 bstrncpy(jcr->jr.Name, jcr->job->hdr.name, sizeof(jcr->jr.Name));
684 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
688 * Write status and such in DB
690 void update_job_end_record(JCR *jcr)
692 jcr->jr.EndTime = time(NULL);
693 jcr->end_time = jcr->jr.EndTime;
694 jcr->jr.JobId = jcr->JobId;
695 jcr->jr.JobStatus = jcr->JobStatus;
696 jcr->jr.JobFiles = jcr->JobFiles;
697 jcr->jr.JobBytes = jcr->JobBytes;
698 jcr->jr.VolSessionId = jcr->VolSessionId;
699 jcr->jr.VolSessionTime = jcr->VolSessionTime;
700 if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
701 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
702 db_strerror(jcr->db));
707 * Takes base_name and appends (unique) current
708 * date and time to form unique job name.
710 * Returns: unique job name in jcr->Job
711 * date/time in jcr->start_time
713 void create_unique_job_name(JCR *jcr, const char *base_name)
715 /* Job start mutex */
716 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
717 static time_t last_start_time = 0;
720 char dt[MAX_TIME_LENGTH];
721 char name[MAX_NAME_LENGTH];
724 /* Guarantee unique start time -- maximum one per second, and
725 * thus unique Job Name
727 P(mutex); /* lock creation of jobs */
729 while (now == last_start_time) {
730 bmicrosleep(0, 500000);
733 last_start_time = now;
734 V(mutex); /* allow creation of jobs */
735 jcr->start_time = now;
736 /* Form Unique JobName */
737 localtime_r(&now, &tm);
738 /* Use only characters that are permitted in Windows filenames */
739 strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
740 bstrncpy(name, base_name, sizeof(name));
741 name[sizeof(name)-22] = 0; /* truncate if too long */
742 bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s", name, dt); /* add date & time */
743 /* Convert spaces into underscores */
744 for (p=jcr->Job; *p; p++) {
751 /* Called directly from job rescheduling */
752 void dird_free_jcr_pointers(JCR *jcr)
754 if (jcr->sd_auth_key) {
755 free(jcr->sd_auth_key);
756 jcr->sd_auth_key = NULL;
762 if (jcr->file_bsock) {
763 Dmsg0(200, "Close File bsock\n");
764 bnet_close(jcr->file_bsock);
765 jcr->file_bsock = NULL;
767 if (jcr->store_bsock) {
768 Dmsg0(200, "Close Store bsock\n");
769 bnet_close(jcr->store_bsock);
770 jcr->store_bsock = NULL;
773 Dmsg0(200, "Free JCR fname\n");
774 free_pool_memory(jcr->fname);
778 Dmsg0(200, "Free JCR stime\n");
779 free_pool_memory(jcr->stime);
782 if (jcr->RestoreBootstrap) {
783 free(jcr->RestoreBootstrap);
784 jcr->RestoreBootstrap = NULL;
786 if (jcr->client_uname) {
787 free_pool_memory(jcr->client_uname);
788 jcr->client_uname = NULL;
790 if (jcr->term_wait_inited) {
791 pthread_cond_destroy(&jcr->term_wait);
792 jcr->term_wait_inited = false;
795 free_pool_memory(jcr->attr);
805 * Free the Job Control Record if no one is still using it.
806 * Called from main free_jcr() routine in src/lib/jcr.c so
807 * that we can do our Director specific cleanup of the jcr.
809 void dird_free_jcr(JCR *jcr)
811 Dmsg0(200, "Start dird free_jcr\n");
813 dird_free_jcr_pointers(jcr);
815 /* Delete lists setup to hold storage pointers */
819 jcr->job_end_push.destroy();
820 Dmsg0(200, "End dird free_jcr\n");
824 * Set some defaults in the JCR necessary to
825 * run. These items are pulled from the job
826 * definition as defaults, but can be overridden
827 * later either by the Run record in the Schedule resource,
828 * or by the Console program.
830 void set_jcr_defaults(JCR *jcr, JOB *job)
834 jcr->JobType = job->JobType;
835 switch (jcr->JobType) {
838 jcr->JobLevel = L_NONE;
841 jcr->JobLevel = job->JobLevel;
844 jcr->JobPriority = job->Priority;
845 /* Copy storage definitions -- deleted in dir_free_jcr above */
850 jcr->storage = New(alist(10, not_owned_by_alist));
851 foreach_alist(st, job->storage) {
852 jcr->storage->append(st);
856 jcr->store = (STORE *)jcr->storage->first();
858 jcr->client = job->client;
859 if (!jcr->client_name) {
860 jcr->client_name = get_pool_memory(PM_NAME);
862 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
863 jcr->pool = job->pool;
864 jcr->full_pool = job->full_pool;
865 jcr->inc_pool = job->inc_pool;
866 jcr->dif_pool = job->dif_pool;
867 jcr->catalog = job->client->catalog;
868 jcr->fileset = job->fileset;
869 jcr->messages = job->messages;
870 jcr->spool_data = job->spool_data;
871 jcr->write_part_after_job = job->write_part_after_job;
872 if (jcr->RestoreBootstrap) {
873 free(jcr->RestoreBootstrap);
874 jcr->RestoreBootstrap = NULL;
876 /* This can be overridden by Console program */
877 if (job->RestoreBootstrap) {
878 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
880 /* This can be overridden by Console program */
881 jcr->verify_job = job->verify_job;
882 /* If no default level given, set one */
883 if (jcr->JobLevel == 0) {
884 switch (jcr->JobType) {
886 jcr->JobLevel = L_VERIFY_CATALOG;
889 jcr->JobLevel = L_INCREMENTAL;
893 jcr->JobLevel = L_NONE;
902 * copy the storage definitions from an old JCR to a new one
904 void copy_storage(JCR *new_jcr, JCR *old_jcr)
906 if (old_jcr->storage) {
908 if (old_jcr->storage) {
909 delete old_jcr->storage;
911 new_jcr->storage = New(alist(10, not_owned_by_alist));
912 foreach_alist(st, old_jcr->storage) {
913 new_jcr->storage->append(st);
916 if (old_jcr->store) {
917 new_jcr->store = old_jcr->store;
918 } else if (new_jcr->storage) {
919 new_jcr->store = (STORE *)new_jcr->storage->first();
923 /* Set storage override */
924 void set_storage(JCR *jcr, STORE *store)
929 foreach_alist(storage, jcr->storage) {
930 if (store == storage) {
934 /* Store not in list, so add it */
935 jcr->storage->prepend(store);