3 * Bacula Director Job processing routines
5 * Kern Sibbald, October MM
10 Copyright (C) 2000-2006 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 amended 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)
78 /* Queue the job to be run */
79 if ((stat = jobq_add(&job_queue, jcr)) != 0) {
81 Jmsg(jcr, M_FATAL, 0, _("Could not add job queue: ERR=%s\n"), be.strerror(stat));
89 bool setup_job(JCR *jcr)
94 sm_check(__FILE__, __LINE__, true);
95 init_msg(jcr, jcr->messages);
97 /* Initialize termination condition variable */
98 if ((errstat = pthread_cond_init(&jcr->term_wait, NULL)) != 0) {
100 Jmsg1(jcr, M_FATAL, 0, _("Unable to init job cond variable: ERR=%s\n"), be.strerror(errstat));
103 jcr->term_wait_inited = true;
109 Dmsg0(50, "Open database\n");
110 jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
111 jcr->catalog->db_password, jcr->catalog->db_address,
112 jcr->catalog->db_port, jcr->catalog->db_socket,
113 jcr->catalog->mult_db_connections);
114 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
115 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
116 jcr->catalog->db_name);
118 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
122 Dmsg0(50, "DB opened\n");
127 create_unique_job_name(jcr, jcr->job->hdr.name);
128 set_jcr_job_status(jcr, JS_Created);
129 init_jcr_job_record(jcr);
130 if (!db_create_job_record(jcr, jcr->db, &jcr->jr)) {
131 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
134 jcr->JobId = jcr->jr.JobId;
135 Dmsg4(100, "Created job record JobId=%d Name=%s Type=%c Level=%c\n",
136 jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel);
138 generate_daemon_event(jcr, "JobStart");
140 if (!get_or_create_client_record(jcr)) {
144 if (job_canceled(jcr)) {
148 Dmsg0(200, "Add jrc to work queue\n");
156 free_memory(jcr->fname);
165 * This is the engine called by jobq.c:jobq_add() when we were pulled
166 * from the work queue.
167 * At this point, we are running in our own thread and all
168 * necessary resources are allocated -- see jobq.c
170 static void *job_thread(void *arg)
172 JCR *jcr = (JCR *)arg;
174 jcr->my_thread_id = pthread_self();
175 pthread_detach(jcr->my_thread_id);
176 sm_check(__FILE__, __LINE__, true);
178 Dmsg0(200, "=====Start Job=========\n");
179 jcr->start_time = time(NULL); /* set the real start time */
180 jcr->jr.StartTime = jcr->start_time;
182 if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
183 (utime_t)(jcr->start_time - jcr->sched_time)) {
184 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
185 set_jcr_job_status(jcr, JS_Canceled);
189 * Note, we continue, even if the job is canceled above. This
190 * will permit proper setting of the job start record and
191 * the error (cancel) will be picked up below.
194 generate_job_event(jcr, "JobInit");
195 set_jcr_job_status(jcr, JS_Running); /* this will be set only if no error */
198 jcr->fname = get_pool_memory(PM_FNAME);
202 * Now, do pre-run stuff, like setting job level (Inc/diff, ...)
203 * this allows us to setup a proper job start record for restarting
204 * in case of later errors.
206 switch (jcr->JobType) {
208 if (!do_backup_init(jcr)) {
209 backup_cleanup(jcr, JS_ErrorTerminated);
213 if (!do_verify_init(jcr)) {
214 verify_cleanup(jcr, JS_ErrorTerminated);
218 if (!do_restore_init(jcr)) {
219 restore_cleanup(jcr, JS_ErrorTerminated);
223 if (!do_admin_init(jcr)) {
224 admin_cleanup(jcr, JS_ErrorTerminated);
230 if (!do_mac_init(jcr)) { /* migration, archive, copy */
231 mac_cleanup(jcr, JS_ErrorTerminated);
235 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->JobType);
236 set_jcr_job_status(jcr, JS_ErrorTerminated);
240 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
241 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
244 if (job_canceled(jcr)) {
245 update_job_end_record(jcr);
250 if (jcr->job->RunBeforeJob) {
251 POOLMEM *before = get_pool_memory(PM_FNAME);
254 char line[MAXSTRING];
256 before = edit_job_codes(jcr, before, jcr->job->RunBeforeJob, "");
257 bpipe = open_bpipe(before, 0, "r");
258 free_pool_memory(before);
259 while (fgets(line, sizeof(line), bpipe->rfd)) {
260 Jmsg(jcr, M_INFO, 0, _("RunBefore: %s"), line);
262 status = close_bpipe(bpipe);
265 Jmsg(jcr, M_FATAL, 0, _("RunBeforeJob error: ERR=%s\n"), be.strerror(status));
266 set_jcr_job_status(jcr, JS_FatalError);
267 update_job_end_record(jcr);
272 generate_job_event(jcr, "JobRun");
274 switch (jcr->JobType) {
276 if (do_backup(jcr)) {
279 backup_cleanup(jcr, JS_ErrorTerminated);
283 if (do_verify(jcr)) {
286 verify_cleanup(jcr, JS_ErrorTerminated);
290 if (do_restore(jcr)) {
293 restore_cleanup(jcr, JS_ErrorTerminated);
300 admin_cleanup(jcr, JS_ErrorTerminated);
306 if (do_mac(jcr)) { /* migration, archive, copy */
309 mac_cleanup(jcr, JS_ErrorTerminated);
313 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->JobType);
316 if ((jcr->job->RunAfterJob && jcr->JobStatus == JS_Terminated) ||
317 (jcr->job->RunAfterFailedJob && jcr->JobStatus != JS_Terminated)) {
318 POOLMEM *after = get_pool_memory(PM_FNAME);
321 char line[MAXSTRING];
323 if (jcr->JobStatus == JS_Terminated) {
324 after = edit_job_codes(jcr, after, jcr->job->RunAfterJob, "");
326 after = edit_job_codes(jcr, after, jcr->job->RunAfterFailedJob, "");
328 bpipe = open_bpipe(after, 0, "r");
329 free_pool_memory(after);
330 while (fgets(line, sizeof(line), bpipe->rfd)) {
331 Jmsg(jcr, M_INFO, 0, _("RunAfter: %s"), line);
333 status = close_bpipe(bpipe);
335 * Note, if we get an error here, do not mark the
336 * job in error, simply report the error condition.
340 if (jcr->JobStatus == JS_Terminated) {
341 Jmsg(jcr, M_WARNING, 0, _("RunAfterJob error: ERR=%s\n"), be.strerror(status));
343 Jmsg(jcr, M_FATAL, 0, _("RunAfterFailedJob error: ERR=%s\n"), be.strerror(status));
347 /* Send off any queued messages */
348 if (jcr->msg_queue->size() > 0) {
349 dequeue_messages(jcr);
354 generate_daemon_event(jcr, "JobEnd");
355 Dmsg1(50, "======== End Job stat=%c ==========\n", jcr->JobStatus);
356 sm_check(__FILE__, __LINE__, true);
362 * Cancel a job -- typically called by the UA (Console program), but may also
363 * be called by the job watchdog.
365 * Returns: true if cancel appears to be successful
366 * false on failure. Message sent to ua->jcr.
368 bool cancel_job(UAContext *ua, JCR *jcr)
372 set_jcr_job_status(jcr, JS_Canceled);
374 switch (jcr->JobStatus) {
377 case JS_WaitClientRes:
378 case JS_WaitStoreRes:
379 case JS_WaitPriority:
381 case JS_WaitStartTime:
382 bsendmsg(ua, _("JobId %d, Job %s marked to be canceled.\n"),
383 jcr->JobId, jcr->Job);
384 jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
388 /* Cancel File daemon */
389 if (jcr->file_bsock) {
390 ua->jcr->client = jcr->client;
391 if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
392 bsendmsg(ua, _("Failed to connect to File daemon.\n"));
395 Dmsg0(200, "Connected to file daemon\n");
396 fd = ua->jcr->file_bsock;
397 bnet_fsend(fd, "cancel Job=%s\n", jcr->Job);
398 while (bnet_recv(fd) >= 0) {
399 bsendmsg(ua, "%s", fd->msg);
401 bnet_sig(fd, BNET_TERMINATE);
403 ua->jcr->file_bsock = NULL;
406 /* Cancel Storage daemon */
407 if (jcr->store_bsock) {
408 if (!ua->jcr->storage) {
409 copy_storage(ua->jcr, jcr);
411 set_storage(ua->jcr, jcr->store);
413 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
414 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
417 Dmsg0(200, "Connected to storage daemon\n");
418 sd = ua->jcr->store_bsock;
419 bnet_fsend(sd, "cancel Job=%s\n", jcr->Job);
420 while (bnet_recv(sd) >= 0) {
421 bsendmsg(ua, "%s", sd->msg);
423 bnet_sig(sd, BNET_TERMINATE);
425 ua->jcr->store_bsock = NULL;
433 static void job_monitor_destructor(watchdog_t *self)
435 JCR *control_jcr = (JCR *)self->data;
437 free_jcr(control_jcr);
440 static void job_monitor_watchdog(watchdog_t *self)
442 JCR *control_jcr, *jcr;
444 control_jcr = (JCR *)self->data;
446 Dmsg1(800, "job_monitor_watchdog %p called\n", self);
451 if (jcr->JobId == 0) {
452 Dmsg2(800, "Skipping JCR %p (%s) with JobId 0\n",
457 /* check MaxWaitTime */
458 cancel = job_check_maxwaittime(control_jcr, jcr);
460 /* check MaxRunTime */
461 cancel |= job_check_maxruntime(control_jcr, jcr);
464 Dmsg3(800, "Cancelling JCR %p jobid %d (%s)\n",
465 jcr, jcr->JobId, jcr->Job);
467 UAContext *ua = new_ua_context(jcr);
468 ua->jcr = control_jcr;
472 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", jcr, jcr->JobId);
475 /* Keep reference counts correct */
481 * Check if the maxwaittime has expired and it is possible
484 static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr)
487 bool ok_to_cancel = false;
490 if (job->MaxWaitTime == 0 && job->FullMaxWaitTime == 0 &&
491 job->IncMaxWaitTime == 0 && job->DiffMaxWaitTime == 0) {
494 if (jcr->JobLevel == L_FULL && job->FullMaxWaitTime != 0 &&
495 (watchdog_time - jcr->start_time) >= job->FullMaxWaitTime) {
497 } else if (jcr->JobLevel == L_DIFFERENTIAL && job->DiffMaxWaitTime != 0 &&
498 (watchdog_time - jcr->start_time) >= job->DiffMaxWaitTime) {
500 } else if (jcr->JobLevel == L_INCREMENTAL && job->IncMaxWaitTime != 0 &&
501 (watchdog_time - jcr->start_time) >= job->IncMaxWaitTime) {
503 } else if (job->MaxWaitTime != 0 &&
504 (watchdog_time - jcr->start_time) >= job->MaxWaitTime) {
510 Dmsg3(800, "Job %d (%s): MaxWaitTime of %d seconds exceeded, "
512 jcr->JobId, jcr->Job, job->MaxWaitTime);
513 switch (jcr->JobStatus) {
518 case JS_WaitStoreRes:
519 case JS_WaitClientRes:
521 case JS_WaitPriority:
523 case JS_WaitStartTime:
525 Dmsg0(200, "JCR blocked in #1\n");
528 Dmsg0(800, "JCR running, checking SD status\n");
529 switch (jcr->SDJobStatus) {
534 Dmsg0(800, "JCR blocked in #2\n");
537 Dmsg0(800, "JCR not blocked in #2\n");
542 case JS_ErrorTerminated:
545 Dmsg0(800, "JCR already dead in #3\n");
548 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
551 Dmsg3(800, "MaxWaitTime result: %scancel JCR %p (%s)\n",
552 cancel ? "" : "do not ", jcr, jcr->job);
558 * Check if maxruntime has expired and if the job can be
561 static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr)
565 if (jcr->job->MaxRunTime == 0) {
568 if ((watchdog_time - jcr->start_time) < jcr->job->MaxRunTime) {
569 Dmsg3(200, "Job %p (%s) with MaxRunTime %d not expired\n",
570 jcr, jcr->Job, jcr->job->MaxRunTime);
574 switch (jcr->JobStatus) {
580 case JS_WaitStoreRes:
581 case JS_WaitClientRes:
583 case JS_WaitPriority:
585 case JS_WaitStartTime:
590 case JS_ErrorTerminated:
596 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
600 Dmsg3(200, "MaxRunTime result: %scancel JCR %p (%s)\n",
601 cancel ? "" : "do not ", jcr, jcr->job);
608 * Get or create a Client record for this Job
610 bool get_or_create_client_record(JCR *jcr)
614 memset(&cr, 0, sizeof(cr));
615 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
616 cr.AutoPrune = jcr->client->AutoPrune;
617 cr.FileRetention = jcr->client->FileRetention;
618 cr.JobRetention = jcr->client->JobRetention;
619 if (!jcr->client_name) {
620 jcr->client_name = get_pool_memory(PM_NAME);
622 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
623 if (!db_create_client_record(jcr, jcr->db, &cr)) {
624 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"),
625 db_strerror(jcr->db));
628 jcr->jr.ClientId = cr.ClientId;
630 if (!jcr->client_uname) {
631 jcr->client_uname = get_pool_memory(PM_NAME);
633 pm_strcpy(jcr->client_uname, cr.Uname);
635 Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name,
640 bool get_or_create_fileset_record(JCR *jcr)
644 * Get or Create FileSet record
646 memset(&fsr, 0, sizeof(FILESET_DBR));
647 bstrncpy(fsr.FileSet, jcr->fileset->hdr.name, sizeof(fsr.FileSet));
648 if (jcr->fileset->have_MD5) {
649 struct MD5Context md5c;
650 unsigned char digest[MD5HashSize];
651 memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
652 MD5Final(digest, &md5c);
653 bin_to_base64(fsr.MD5, (char *)digest, MD5HashSize);
654 bstrncpy(jcr->fileset->MD5, fsr.MD5, sizeof(jcr->fileset->MD5));
656 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n"));
658 if (!jcr->fileset->ignore_fs_changes ||
659 !db_get_fileset_record(jcr, jcr->db, &fsr)) {
660 if (!db_create_fileset_record(jcr, jcr->db, &fsr)) {
661 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
662 fsr.FileSet, db_strerror(jcr->db));
666 jcr->jr.FileSetId = fsr.FileSetId;
667 bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime));
668 Dmsg2(119, "Created FileSet %s record %u\n", jcr->fileset->hdr.name,
673 void init_jcr_job_record(JCR *jcr)
675 jcr->jr.SchedTime = jcr->sched_time;
676 jcr->jr.StartTime = jcr->start_time;
677 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
678 jcr->jr.JobType = jcr->JobType;
679 jcr->jr.JobLevel = jcr->JobLevel;
680 jcr->jr.JobStatus = jcr->JobStatus;
681 jcr->jr.JobId = jcr->JobId;
682 bstrncpy(jcr->jr.Name, jcr->job->hdr.name, sizeof(jcr->jr.Name));
683 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
687 * Write status and such in DB
689 void update_job_end_record(JCR *jcr)
691 jcr->jr.EndTime = time(NULL);
692 jcr->end_time = jcr->jr.EndTime;
693 jcr->jr.JobId = jcr->JobId;
694 jcr->jr.JobStatus = jcr->JobStatus;
695 jcr->jr.JobFiles = jcr->JobFiles;
696 jcr->jr.JobBytes = jcr->JobBytes;
697 jcr->jr.VolSessionId = jcr->VolSessionId;
698 jcr->jr.VolSessionTime = jcr->VolSessionTime;
699 if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
700 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
701 db_strerror(jcr->db));
706 * Takes base_name and appends (unique) current
707 * date and time to form unique job name.
709 * Returns: unique job name in jcr->Job
710 * date/time in jcr->start_time
712 void create_unique_job_name(JCR *jcr, const char *base_name)
714 /* Job start mutex */
715 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
716 static time_t last_start_time = 0;
719 char dt[MAX_TIME_LENGTH];
720 char name[MAX_NAME_LENGTH];
723 /* Guarantee unique start time -- maximum one per second, and
724 * thus unique Job Name
726 P(mutex); /* lock creation of jobs */
728 while (now == last_start_time) {
729 bmicrosleep(0, 500000);
732 last_start_time = now;
733 V(mutex); /* allow creation of jobs */
734 jcr->start_time = now;
735 /* Form Unique JobName */
736 localtime_r(&now, &tm);
737 /* Use only characters that are permitted in Windows filenames */
738 strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
739 bstrncpy(name, base_name, sizeof(name));
740 name[sizeof(name)-22] = 0; /* truncate if too long */
741 bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s", name, dt); /* add date & time */
742 /* Convert spaces into underscores */
743 for (p=jcr->Job; *p; p++) {
750 /* Called directly from job rescheduling */
751 void dird_free_jcr_pointers(JCR *jcr)
753 if (jcr->sd_auth_key) {
754 free(jcr->sd_auth_key);
755 jcr->sd_auth_key = NULL;
761 if (jcr->file_bsock) {
762 Dmsg0(200, "Close File bsock\n");
763 bnet_close(jcr->file_bsock);
764 jcr->file_bsock = NULL;
766 if (jcr->store_bsock) {
767 Dmsg0(200, "Close Store bsock\n");
768 bnet_close(jcr->store_bsock);
769 jcr->store_bsock = NULL;
772 Dmsg0(200, "Free JCR fname\n");
773 free_pool_memory(jcr->fname);
777 Dmsg0(200, "Free JCR stime\n");
778 free_pool_memory(jcr->stime);
781 if (jcr->RestoreBootstrap) {
782 free(jcr->RestoreBootstrap);
783 jcr->RestoreBootstrap = NULL;
785 if (jcr->client_uname) {
786 free_pool_memory(jcr->client_uname);
787 jcr->client_uname = NULL;
789 if (jcr->term_wait_inited) {
790 pthread_cond_destroy(&jcr->term_wait);
791 jcr->term_wait_inited = false;
794 free_pool_memory(jcr->attr);
804 * Free the Job Control Record if no one is still using it.
805 * Called from main free_jcr() routine in src/lib/jcr.c so
806 * that we can do our Director specific cleanup of the jcr.
808 void dird_free_jcr(JCR *jcr)
810 Dmsg0(200, "Start dird free_jcr\n");
812 dird_free_jcr_pointers(jcr);
814 /* Delete lists setup to hold storage pointers */
818 jcr->job_end_push.destroy();
819 Dmsg0(200, "End dird free_jcr\n");
823 * Set some defaults in the JCR necessary to
824 * run. These items are pulled from the job
825 * definition as defaults, but can be overridden
826 * later either by the Run record in the Schedule resource,
827 * or by the Console program.
829 void set_jcr_defaults(JCR *jcr, JOB *job)
833 jcr->JobType = job->JobType;
834 switch (jcr->JobType) {
837 jcr->JobLevel = L_NONE;
840 jcr->JobLevel = job->JobLevel;
843 jcr->JobPriority = job->Priority;
844 /* Copy storage definitions -- deleted in dir_free_jcr above */
849 jcr->storage = New(alist(10, not_owned_by_alist));
850 foreach_alist(st, job->storage) {
851 jcr->storage->append(st);
855 jcr->store = (STORE *)jcr->storage->first();
857 jcr->client = job->client;
858 if (!jcr->client_name) {
859 jcr->client_name = get_pool_memory(PM_NAME);
861 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
862 jcr->pool = job->pool;
863 jcr->full_pool = job->full_pool;
864 jcr->inc_pool = job->inc_pool;
865 jcr->dif_pool = job->dif_pool;
866 jcr->catalog = job->client->catalog;
867 jcr->fileset = job->fileset;
868 jcr->messages = job->messages;
869 jcr->spool_data = job->spool_data;
870 jcr->write_part_after_job = job->write_part_after_job;
871 if (jcr->RestoreBootstrap) {
872 free(jcr->RestoreBootstrap);
873 jcr->RestoreBootstrap = NULL;
875 /* This can be overridden by Console program */
876 if (job->RestoreBootstrap) {
877 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
879 /* This can be overridden by Console program */
880 jcr->verify_job = job->verify_job;
881 /* If no default level given, set one */
882 if (jcr->JobLevel == 0) {
883 switch (jcr->JobType) {
885 jcr->JobLevel = L_VERIFY_CATALOG;
888 jcr->JobLevel = L_INCREMENTAL;
892 jcr->JobLevel = L_NONE;
901 * copy the storage definitions from an old JCR to a new one
903 void copy_storage(JCR *new_jcr, JCR *old_jcr)
905 if (old_jcr->storage) {
907 if (old_jcr->storage) {
908 delete old_jcr->storage;
910 new_jcr->storage = New(alist(10, not_owned_by_alist));
911 foreach_alist(st, old_jcr->storage) {
912 new_jcr->storage->append(st);
915 if (old_jcr->store) {
916 new_jcr->store = old_jcr->store;
917 } else if (new_jcr->storage) {
918 new_jcr->store = (STORE *)new_jcr->storage->first();
922 /* Set storage override */
923 void set_storage(JCR *jcr, STORE *store)
928 foreach_alist(storage, jcr->storage) {
929 if (store == storage) {
933 /* Store not in list, so add it */
934 jcr->storage->prepend(store);