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)
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: true if cancel appears to be successful
360 * false on failure. Message sent to ua->jcr.
362 bool 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 */
382 /* Cancel File daemon */
383 if (jcr->file_bsock) {
384 ua->jcr->client = jcr->client;
385 if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
386 bsendmsg(ua, _("Failed to connect to File daemon.\n"));
389 Dmsg0(200, "Connected to file daemon\n");
390 fd = ua->jcr->file_bsock;
391 bnet_fsend(fd, "cancel Job=%s\n", jcr->Job);
392 while (bnet_recv(fd) >= 0) {
393 bsendmsg(ua, "%s", fd->msg);
395 bnet_sig(fd, BNET_TERMINATE);
397 ua->jcr->file_bsock = NULL;
400 /* Cancel Storage daemon */
401 if (jcr->store_bsock) {
402 if (!ua->jcr->storage) {
403 copy_storage(ua->jcr, jcr);
405 set_storage(ua->jcr, jcr->store);
407 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
408 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
411 Dmsg0(200, "Connected to storage daemon\n");
412 sd = ua->jcr->store_bsock;
413 bnet_fsend(sd, "cancel Job=%s\n", jcr->Job);
414 while (bnet_recv(sd) >= 0) {
415 bsendmsg(ua, "%s", sd->msg);
417 bnet_sig(sd, BNET_TERMINATE);
419 ua->jcr->store_bsock = NULL;
427 static void job_monitor_destructor(watchdog_t *self)
429 JCR *control_jcr = (JCR *)self->data;
431 free_jcr(control_jcr);
434 static void job_monitor_watchdog(watchdog_t *self)
436 JCR *control_jcr, *jcr;
438 control_jcr = (JCR *)self->data;
440 Dmsg1(800, "job_monitor_watchdog %p called\n", self);
445 if (jcr->JobId == 0) {
446 Dmsg2(800, "Skipping JCR %p (%s) with JobId 0\n",
451 /* check MaxWaitTime */
452 cancel = job_check_maxwaittime(control_jcr, jcr);
454 /* check MaxRunTime */
455 cancel |= job_check_maxruntime(control_jcr, jcr);
458 Dmsg3(800, "Cancelling JCR %p jobid %d (%s)\n",
459 jcr, jcr->JobId, jcr->Job);
461 UAContext *ua = new_ua_context(jcr);
462 ua->jcr = control_jcr;
466 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", jcr, jcr->JobId);
469 /* Keep reference counts correct */
475 * Check if the maxwaittime has expired and it is possible
478 static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr)
481 bool ok_to_cancel = false;
484 if (job->MaxWaitTime == 0 && job->FullMaxWaitTime == 0 &&
485 job->IncMaxWaitTime == 0 && job->DiffMaxWaitTime == 0) {
488 if (jcr->JobLevel == L_FULL && job->FullMaxWaitTime != 0 &&
489 (watchdog_time - jcr->start_time) >= job->FullMaxWaitTime) {
491 } else if (jcr->JobLevel == L_DIFFERENTIAL && job->DiffMaxWaitTime != 0 &&
492 (watchdog_time - jcr->start_time) >= job->DiffMaxWaitTime) {
494 } else if (jcr->JobLevel == L_INCREMENTAL && job->IncMaxWaitTime != 0 &&
495 (watchdog_time - jcr->start_time) >= job->IncMaxWaitTime) {
497 } else if (job->MaxWaitTime != 0 &&
498 (watchdog_time - jcr->start_time) >= job->MaxWaitTime) {
504 Dmsg3(800, "Job %d (%s): MaxWaitTime of %d seconds exceeded, "
506 jcr->JobId, jcr->Job, job->MaxWaitTime);
507 switch (jcr->JobStatus) {
512 case JS_WaitStoreRes:
513 case JS_WaitClientRes:
515 case JS_WaitPriority:
517 case JS_WaitStartTime:
519 Dmsg0(200, "JCR blocked in #1\n");
522 Dmsg0(800, "JCR running, checking SD status\n");
523 switch (jcr->SDJobStatus) {
528 Dmsg0(800, "JCR blocked in #2\n");
531 Dmsg0(800, "JCR not blocked in #2\n");
536 case JS_ErrorTerminated:
539 Dmsg0(800, "JCR already dead in #3\n");
542 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
545 Dmsg3(800, "MaxWaitTime result: %scancel JCR %p (%s)\n",
546 cancel ? "" : "do not ", jcr, jcr->job);
552 * Check if maxruntime has expired and if the job can be
555 static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr)
559 if (jcr->job->MaxRunTime == 0) {
562 if ((watchdog_time - jcr->start_time) < jcr->job->MaxRunTime) {
563 Dmsg3(200, "Job %p (%s) with MaxRunTime %d not expired\n",
564 jcr, jcr->Job, jcr->job->MaxRunTime);
568 switch (jcr->JobStatus) {
574 case JS_WaitStoreRes:
575 case JS_WaitClientRes:
577 case JS_WaitPriority:
579 case JS_WaitStartTime:
584 case JS_ErrorTerminated:
590 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
594 Dmsg3(200, "MaxRunTime result: %scancel JCR %p (%s)\n",
595 cancel ? "" : "do not ", jcr, jcr->job);
602 * Get or create a Client record for this Job
604 bool get_or_create_client_record(JCR *jcr)
608 memset(&cr, 0, sizeof(cr));
609 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
610 cr.AutoPrune = jcr->client->AutoPrune;
611 cr.FileRetention = jcr->client->FileRetention;
612 cr.JobRetention = jcr->client->JobRetention;
613 if (!jcr->client_name) {
614 jcr->client_name = get_pool_memory(PM_NAME);
616 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
617 if (!db_create_client_record(jcr, jcr->db, &cr)) {
618 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"),
619 db_strerror(jcr->db));
622 jcr->jr.ClientId = cr.ClientId;
624 if (!jcr->client_uname) {
625 jcr->client_uname = get_pool_memory(PM_NAME);
627 pm_strcpy(jcr->client_uname, cr.Uname);
629 Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name,
634 bool get_or_create_fileset_record(JCR *jcr)
638 * Get or Create FileSet record
640 memset(&fsr, 0, sizeof(FILESET_DBR));
641 bstrncpy(fsr.FileSet, jcr->fileset->hdr.name, sizeof(fsr.FileSet));
642 if (jcr->fileset->have_MD5) {
643 struct MD5Context md5c;
644 unsigned char digest[MD5HashSize];
645 memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
646 MD5Final(digest, &md5c);
647 bin_to_base64(fsr.MD5, (char *)digest, MD5HashSize);
648 bstrncpy(jcr->fileset->MD5, fsr.MD5, sizeof(jcr->fileset->MD5));
650 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n"));
652 if (!jcr->fileset->ignore_fs_changes ||
653 !db_get_fileset_record(jcr, jcr->db, &fsr)) {
654 if (!db_create_fileset_record(jcr, jcr->db, &fsr)) {
655 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
656 fsr.FileSet, db_strerror(jcr->db));
660 jcr->jr.FileSetId = fsr.FileSetId;
661 bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime));
662 Dmsg2(119, "Created FileSet %s record %u\n", jcr->fileset->hdr.name,
667 void init_jcr_job_record(JCR *jcr)
669 jcr->jr.SchedTime = jcr->sched_time;
670 jcr->jr.StartTime = jcr->start_time;
671 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
672 jcr->jr.JobType = jcr->JobType;
673 jcr->jr.JobLevel = jcr->JobLevel;
674 jcr->jr.JobStatus = jcr->JobStatus;
675 jcr->jr.JobId = jcr->JobId;
676 bstrncpy(jcr->jr.Name, jcr->job->hdr.name, sizeof(jcr->jr.Name));
677 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
681 * Write status and such in DB
683 void update_job_end_record(JCR *jcr)
685 jcr->jr.EndTime = time(NULL);
686 jcr->end_time = jcr->jr.EndTime;
687 jcr->jr.JobId = jcr->JobId;
688 jcr->jr.JobStatus = jcr->JobStatus;
689 jcr->jr.JobFiles = jcr->JobFiles;
690 jcr->jr.JobBytes = jcr->JobBytes;
691 jcr->jr.VolSessionId = jcr->VolSessionId;
692 jcr->jr.VolSessionTime = jcr->VolSessionTime;
693 if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
694 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
695 db_strerror(jcr->db));
700 * Takes base_name and appends (unique) current
701 * date and time to form unique job name.
703 * Returns: unique job name in jcr->Job
704 * date/time in jcr->start_time
706 void create_unique_job_name(JCR *jcr, const char *base_name)
708 /* Job start mutex */
709 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
710 static time_t last_start_time = 0;
713 char dt[MAX_TIME_LENGTH];
714 char name[MAX_NAME_LENGTH];
717 /* Guarantee unique start time -- maximum one per second, and
718 * thus unique Job Name
720 P(mutex); /* lock creation of jobs */
722 while (now == last_start_time) {
723 bmicrosleep(0, 500000);
726 last_start_time = now;
727 V(mutex); /* allow creation of jobs */
728 jcr->start_time = now;
729 /* Form Unique JobName */
730 localtime_r(&now, &tm);
731 /* Use only characters that are permitted in Windows filenames */
732 strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
733 bstrncpy(name, base_name, sizeof(name));
734 name[sizeof(name)-22] = 0; /* truncate if too long */
735 bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s", name, dt); /* add date & time */
736 /* Convert spaces into underscores */
737 for (p=jcr->Job; *p; p++) {
744 /* Called directly from job rescheduling */
745 void dird_free_jcr_pointers(JCR *jcr)
747 if (jcr->sd_auth_key) {
748 free(jcr->sd_auth_key);
749 jcr->sd_auth_key = NULL;
755 if (jcr->file_bsock) {
756 Dmsg0(200, "Close File bsock\n");
757 bnet_close(jcr->file_bsock);
758 jcr->file_bsock = NULL;
760 if (jcr->store_bsock) {
761 Dmsg0(200, "Close Store bsock\n");
762 bnet_close(jcr->store_bsock);
763 jcr->store_bsock = NULL;
766 Dmsg0(200, "Free JCR fname\n");
767 free_pool_memory(jcr->fname);
771 Dmsg0(200, "Free JCR stime\n");
772 free_pool_memory(jcr->stime);
775 if (jcr->RestoreBootstrap) {
776 free(jcr->RestoreBootstrap);
777 jcr->RestoreBootstrap = NULL;
779 if (jcr->client_uname) {
780 free_pool_memory(jcr->client_uname);
781 jcr->client_uname = NULL;
783 if (jcr->term_wait_inited) {
784 pthread_cond_destroy(&jcr->term_wait);
785 jcr->term_wait_inited = false;
788 free_pool_memory(jcr->attr);
798 * Free the Job Control Record if no one is still using it.
799 * Called from main free_jcr() routine in src/lib/jcr.c so
800 * that we can do our Director specific cleanup of the jcr.
802 void dird_free_jcr(JCR *jcr)
804 Dmsg0(200, "Start dird free_jcr\n");
806 dird_free_jcr_pointers(jcr);
808 /* Delete lists setup to hold storage pointers */
812 jcr->job_end_push.destroy();
813 Dmsg0(200, "End dird free_jcr\n");
817 * Set some defaults in the JCR necessary to
818 * run. These items are pulled from the job
819 * definition as defaults, but can be overridden
820 * later either by the Run record in the Schedule resource,
821 * or by the Console program.
823 void set_jcr_defaults(JCR *jcr, JOB *job)
827 jcr->JobType = job->JobType;
828 switch (jcr->JobType) {
831 jcr->JobLevel = L_NONE;
834 jcr->JobLevel = job->JobLevel;
837 jcr->JobPriority = job->Priority;
838 /* Copy storage definitions -- deleted in dir_free_jcr above */
843 jcr->storage = New(alist(10, not_owned_by_alist));
844 foreach_alist(st, job->storage) {
845 jcr->storage->append(st);
849 jcr->store = (STORE *)jcr->storage->first();
851 jcr->client = job->client;
852 if (!jcr->client_name) {
853 jcr->client_name = get_pool_memory(PM_NAME);
855 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
856 jcr->pool = job->pool;
857 jcr->full_pool = job->full_pool;
858 jcr->inc_pool = job->inc_pool;
859 jcr->dif_pool = job->dif_pool;
860 jcr->catalog = job->client->catalog;
861 jcr->fileset = job->fileset;
862 jcr->messages = job->messages;
863 jcr->spool_data = job->spool_data;
864 jcr->write_part_after_job = job->write_part_after_job;
865 if (jcr->RestoreBootstrap) {
866 free(jcr->RestoreBootstrap);
867 jcr->RestoreBootstrap = NULL;
869 /* This can be overridden by Console program */
870 if (job->RestoreBootstrap) {
871 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
873 /* This can be overridden by Console program */
874 jcr->verify_job = job->verify_job;
875 /* If no default level given, set one */
876 if (jcr->JobLevel == 0) {
877 switch (jcr->JobType) {
879 jcr->JobLevel = L_VERIFY_CATALOG;
882 jcr->JobLevel = L_INCREMENTAL;
886 jcr->JobLevel = L_NONE;
895 * copy the storage definitions from an old JCR to a new one
897 void copy_storage(JCR *new_jcr, JCR *old_jcr)
899 if (old_jcr->storage) {
901 if (old_jcr->storage) {
902 delete old_jcr->storage;
904 new_jcr->storage = New(alist(10, not_owned_by_alist));
905 foreach_alist(st, old_jcr->storage) {
906 new_jcr->storage->append(st);
909 if (old_jcr->store) {
910 new_jcr->store = old_jcr->store;
911 } else if (new_jcr->storage) {
912 new_jcr->store = (STORE *)new_jcr->storage->first();
916 /* Set storage override */
917 void set_storage(JCR *jcr, STORE *store)
922 foreach_alist(storage, jcr->storage) {
923 if (store == storage) {
927 /* Store not in list, so add it */
928 jcr->storage->prepend(store);