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;
91 create_unique_job_name(jcr, jcr->job->hdr.name);
92 set_jcr_job_status(jcr, JS_Created);
98 Dmsg0(50, "Open database\n");
99 jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
100 jcr->catalog->db_password, jcr->catalog->db_address,
101 jcr->catalog->db_port, jcr->catalog->db_socket,
102 jcr->catalog->mult_db_connections);
103 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
104 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
105 jcr->catalog->db_name);
107 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
111 Dmsg0(50, "DB opened\n");
116 init_jcr_job_record(jcr);
117 if (!db_create_job_record(jcr, jcr->db, &jcr->jr)) {
118 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
121 JobId = jcr->JobId = jcr->jr.JobId;
122 Dmsg4(100, "Created job record JobId=%d Name=%s Type=%c Level=%c\n",
123 jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel);
125 generate_daemon_event(jcr, "JobStart");
127 if (!get_or_create_client_record(jcr)) {
131 if (job_canceled(jcr)) {
135 Dmsg0(200, "Add jrc to work queue\n");
137 /* Queue the job to be run */
138 if ((stat = jobq_add(&job_queue, jcr)) != 0) {
140 Jmsg(jcr, M_FATAL, 0, _("Could not add job queue: ERR=%s\n"), be.strerror(stat));
144 Dmsg0(100, "Done run_job()\n");
150 free_memory(jcr->fname);
158 * This is the engine called by jobq.c:jobq_add() when we were pulled
159 * from the work queue.
160 * At this point, we are running in our own thread and all
161 * necessary resources are allocated -- see jobq.c
163 static void *job_thread(void *arg)
165 JCR *jcr = (JCR *)arg;
167 jcr->my_thread_id = pthread_self();
168 pthread_detach(jcr->my_thread_id);
169 sm_check(__FILE__, __LINE__, true);
171 Dmsg0(200, "=====Start Job=========\n");
172 jcr->start_time = time(NULL); /* set the real start time */
173 jcr->jr.StartTime = jcr->start_time;
175 if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
176 (utime_t)(jcr->start_time - jcr->sched_time)) {
177 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
178 set_jcr_job_status(jcr, JS_Canceled);
182 * Note, we continue, even if the job is canceled above. This
183 * will permit proper setting of the job start record and
184 * the error (cancel) will be picked up below.
187 generate_job_event(jcr, "JobInit");
188 set_jcr_job_status(jcr, JS_Running); /* this will be set only if no error */
191 jcr->fname = get_pool_memory(PM_FNAME);
195 * Now, do pre-run stuff, like setting job level (Inc/diff, ...)
196 * this allows us to setup a proper job start record for restarting
197 * in case of later errors.
199 switch (jcr->JobType) {
201 if (!do_backup_init(jcr)) {
202 backup_cleanup(jcr, JS_ErrorTerminated);
206 if (!do_verify_init(jcr)) {
207 verify_cleanup(jcr, JS_ErrorTerminated);
211 if (!do_restore_init(jcr)) {
212 restore_cleanup(jcr, JS_ErrorTerminated);
216 if (!do_admin_init(jcr)) {
217 admin_cleanup(jcr, JS_ErrorTerminated);
223 if (!do_mac_init(jcr)) { /* migration, archive, copy */
224 mac_cleanup(jcr, JS_ErrorTerminated);
228 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->JobType);
229 set_jcr_job_status(jcr, JS_ErrorTerminated);
233 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
234 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
237 if (job_canceled(jcr)) {
238 update_job_end_record(jcr);
243 if (jcr->job->RunBeforeJob) {
244 POOLMEM *before = get_pool_memory(PM_FNAME);
247 char line[MAXSTRING];
249 before = edit_job_codes(jcr, before, jcr->job->RunBeforeJob, "");
250 bpipe = open_bpipe(before, 0, "r");
251 free_pool_memory(before);
252 while (fgets(line, sizeof(line), bpipe->rfd)) {
253 Jmsg(jcr, M_INFO, 0, _("RunBefore: %s"), line);
255 status = close_bpipe(bpipe);
258 Jmsg(jcr, M_FATAL, 0, _("RunBeforeJob error: ERR=%s\n"), be.strerror(status));
259 set_jcr_job_status(jcr, JS_FatalError);
260 update_job_end_record(jcr);
266 * We re-update the job start record so that the start
267 * time is set after the run before job. This avoids
268 * that any files created by the run before job will
269 * be saved twice. They will be backed up in the current
270 * job, but not in the next one unless they are changed.
271 * Without this, they will be backed up in this job and
272 * in the next job run because in that case, their date
273 * is after the start of this run.
275 jcr->start_time = time(NULL);
276 jcr->jr.StartTime = jcr->start_time;
277 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
278 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
280 generate_job_event(jcr, "JobRun");
282 switch (jcr->JobType) {
284 if (do_backup(jcr)) {
287 backup_cleanup(jcr, JS_ErrorTerminated);
291 if (do_verify(jcr)) {
294 verify_cleanup(jcr, JS_ErrorTerminated);
298 if (do_restore(jcr)) {
301 restore_cleanup(jcr, JS_ErrorTerminated);
308 admin_cleanup(jcr, JS_ErrorTerminated);
314 if (do_mac(jcr)) { /* migration, archive, copy */
317 mac_cleanup(jcr, JS_ErrorTerminated);
321 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->JobType);
324 if ((jcr->job->RunAfterJob && jcr->JobStatus == JS_Terminated) ||
325 (jcr->job->RunAfterFailedJob && jcr->JobStatus != JS_Terminated)) {
326 POOLMEM *after = get_pool_memory(PM_FNAME);
329 char line[MAXSTRING];
331 if (jcr->JobStatus == JS_Terminated) {
332 after = edit_job_codes(jcr, after, jcr->job->RunAfterJob, "");
334 after = edit_job_codes(jcr, after, jcr->job->RunAfterFailedJob, "");
336 bpipe = open_bpipe(after, 0, "r");
337 free_pool_memory(after);
338 while (fgets(line, sizeof(line), bpipe->rfd)) {
339 Jmsg(jcr, M_INFO, 0, _("RunAfter: %s"), line);
341 status = close_bpipe(bpipe);
343 * Note, if we get an error here, do not mark the
344 * job in error, simply report the error condition.
348 if (jcr->JobStatus == JS_Terminated) {
349 Jmsg(jcr, M_WARNING, 0, _("RunAfterJob error: ERR=%s\n"), be.strerror(status));
351 Jmsg(jcr, M_FATAL, 0, _("RunAfterFailedJob error: ERR=%s\n"), be.strerror(status));
355 /* Send off any queued messages */
356 if (jcr->msg_queue->size() > 0) {
357 dequeue_messages(jcr);
362 generate_daemon_event(jcr, "JobEnd");
363 Dmsg1(50, "======== End Job stat=%c ==========\n", jcr->JobStatus);
364 sm_check(__FILE__, __LINE__, true);
370 * Cancel a job -- typically called by the UA (Console program), but may also
371 * be called by the job watchdog.
373 * Returns: true if cancel appears to be successful
374 * false on failure. Message sent to ua->jcr.
376 bool cancel_job(UAContext *ua, JCR *jcr)
380 set_jcr_job_status(jcr, JS_Canceled);
382 switch (jcr->JobStatus) {
385 case JS_WaitClientRes:
386 case JS_WaitStoreRes:
387 case JS_WaitPriority:
389 case JS_WaitStartTime:
390 bsendmsg(ua, _("JobId %d, Job %s marked to be canceled.\n"),
391 jcr->JobId, jcr->Job);
392 jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
397 /* Cancel File daemon */
398 if (jcr->file_bsock) {
399 ua->jcr->client = jcr->client;
400 if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
401 bsendmsg(ua, _("Failed to connect to File daemon.\n"));
404 Dmsg0(200, "Connected to file daemon\n");
405 fd = ua->jcr->file_bsock;
406 bnet_fsend(fd, "cancel Job=%s\n", jcr->Job);
407 while (bnet_recv(fd) >= 0) {
408 bsendmsg(ua, "%s", fd->msg);
410 bnet_sig(fd, BNET_TERMINATE);
412 ua->jcr->file_bsock = NULL;
415 /* Cancel Storage daemon */
416 if (jcr->store_bsock) {
417 if (!ua->jcr->storage) {
418 copy_storage(ua->jcr, jcr);
420 set_storage(ua->jcr, jcr->store);
422 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
423 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
426 Dmsg0(200, "Connected to storage daemon\n");
427 sd = ua->jcr->store_bsock;
428 bnet_fsend(sd, "cancel Job=%s\n", jcr->Job);
429 while (bnet_recv(sd) >= 0) {
430 bsendmsg(ua, "%s", sd->msg);
432 bnet_sig(sd, BNET_TERMINATE);
434 ua->jcr->store_bsock = NULL;
442 static void job_monitor_destructor(watchdog_t *self)
444 JCR *control_jcr = (JCR *)self->data;
446 free_jcr(control_jcr);
449 static void job_monitor_watchdog(watchdog_t *self)
451 JCR *control_jcr, *jcr;
453 control_jcr = (JCR *)self->data;
455 Dmsg1(800, "job_monitor_watchdog %p called\n", self);
460 if (jcr->JobId == 0) {
461 Dmsg2(800, "Skipping JCR %p (%s) with JobId 0\n",
466 /* check MaxWaitTime */
467 cancel = job_check_maxwaittime(control_jcr, jcr);
469 /* check MaxRunTime */
470 cancel |= job_check_maxruntime(control_jcr, jcr);
473 Dmsg3(800, "Cancelling JCR %p jobid %d (%s)\n",
474 jcr, jcr->JobId, jcr->Job);
476 UAContext *ua = new_ua_context(jcr);
477 ua->jcr = control_jcr;
481 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", jcr, jcr->JobId);
484 /* Keep reference counts correct */
490 * Check if the maxwaittime has expired and it is possible
493 static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr)
496 bool ok_to_cancel = false;
499 if (job->MaxWaitTime == 0 && job->FullMaxWaitTime == 0 &&
500 job->IncMaxWaitTime == 0 && job->DiffMaxWaitTime == 0) {
503 if (jcr->JobLevel == L_FULL && job->FullMaxWaitTime != 0 &&
504 (watchdog_time - jcr->start_time) >= job->FullMaxWaitTime) {
506 } else if (jcr->JobLevel == L_DIFFERENTIAL && job->DiffMaxWaitTime != 0 &&
507 (watchdog_time - jcr->start_time) >= job->DiffMaxWaitTime) {
509 } else if (jcr->JobLevel == L_INCREMENTAL && job->IncMaxWaitTime != 0 &&
510 (watchdog_time - jcr->start_time) >= job->IncMaxWaitTime) {
512 } else if (job->MaxWaitTime != 0 &&
513 (watchdog_time - jcr->start_time) >= job->MaxWaitTime) {
519 Dmsg3(800, "Job %d (%s): MaxWaitTime of %d seconds exceeded, "
521 jcr->JobId, jcr->Job, job->MaxWaitTime);
522 switch (jcr->JobStatus) {
527 case JS_WaitStoreRes:
528 case JS_WaitClientRes:
530 case JS_WaitPriority:
532 case JS_WaitStartTime:
534 Dmsg0(200, "JCR blocked in #1\n");
537 Dmsg0(800, "JCR running, checking SD status\n");
538 switch (jcr->SDJobStatus) {
543 Dmsg0(800, "JCR blocked in #2\n");
546 Dmsg0(800, "JCR not blocked in #2\n");
551 case JS_ErrorTerminated:
554 Dmsg0(800, "JCR already dead in #3\n");
557 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
560 Dmsg3(800, "MaxWaitTime result: %scancel JCR %p (%s)\n",
561 cancel ? "" : "do not ", jcr, jcr->job);
567 * Check if maxruntime has expired and if the job can be
570 static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr)
574 if (jcr->job->MaxRunTime == 0) {
577 if ((watchdog_time - jcr->start_time) < jcr->job->MaxRunTime) {
578 Dmsg3(200, "Job %p (%s) with MaxRunTime %d not expired\n",
579 jcr, jcr->Job, jcr->job->MaxRunTime);
583 switch (jcr->JobStatus) {
589 case JS_WaitStoreRes:
590 case JS_WaitClientRes:
592 case JS_WaitPriority:
594 case JS_WaitStartTime:
599 case JS_ErrorTerminated:
605 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
609 Dmsg3(200, "MaxRunTime result: %scancel JCR %p (%s)\n",
610 cancel ? "" : "do not ", jcr, jcr->job);
617 * Get or create a Client record for this Job
619 bool get_or_create_client_record(JCR *jcr)
623 memset(&cr, 0, sizeof(cr));
624 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
625 cr.AutoPrune = jcr->client->AutoPrune;
626 cr.FileRetention = jcr->client->FileRetention;
627 cr.JobRetention = jcr->client->JobRetention;
628 if (!jcr->client_name) {
629 jcr->client_name = get_pool_memory(PM_NAME);
631 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
632 if (!db_create_client_record(jcr, jcr->db, &cr)) {
633 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"),
634 db_strerror(jcr->db));
637 jcr->jr.ClientId = cr.ClientId;
639 if (!jcr->client_uname) {
640 jcr->client_uname = get_pool_memory(PM_NAME);
642 pm_strcpy(jcr->client_uname, cr.Uname);
644 Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name,
649 bool get_or_create_fileset_record(JCR *jcr)
653 * Get or Create FileSet record
655 memset(&fsr, 0, sizeof(FILESET_DBR));
656 bstrncpy(fsr.FileSet, jcr->fileset->hdr.name, sizeof(fsr.FileSet));
657 if (jcr->fileset->have_MD5) {
658 struct MD5Context md5c;
659 unsigned char signature[16];
660 memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
661 MD5Final(signature, &md5c);
662 bin_to_base64(fsr.MD5, (char *)signature, 16); /* encode 16 bytes */
663 bstrncpy(jcr->fileset->MD5, fsr.MD5, sizeof(jcr->fileset->MD5));
665 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 signature not found.\n"));
667 if (!jcr->fileset->ignore_fs_changes ||
668 !db_get_fileset_record(jcr, jcr->db, &fsr)) {
669 if (!db_create_fileset_record(jcr, jcr->db, &fsr)) {
670 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
671 fsr.FileSet, db_strerror(jcr->db));
675 jcr->jr.FileSetId = fsr.FileSetId;
676 bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime));
677 Dmsg2(119, "Created FileSet %s record %u\n", jcr->fileset->hdr.name,
682 void init_jcr_job_record(JCR *jcr)
684 jcr->jr.SchedTime = jcr->sched_time;
685 jcr->jr.StartTime = jcr->start_time;
686 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
687 jcr->jr.JobType = jcr->JobType;
688 jcr->jr.JobLevel = jcr->JobLevel;
689 jcr->jr.JobStatus = jcr->JobStatus;
690 jcr->jr.JobId = jcr->JobId;
691 bstrncpy(jcr->jr.Name, jcr->job->hdr.name, sizeof(jcr->jr.Name));
692 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
696 * Write status and such in DB
698 void update_job_end_record(JCR *jcr)
700 jcr->jr.EndTime = time(NULL);
701 jcr->end_time = jcr->jr.EndTime;
702 jcr->jr.JobId = jcr->JobId;
703 jcr->jr.JobStatus = jcr->JobStatus;
704 jcr->jr.JobFiles = jcr->JobFiles;
705 jcr->jr.JobBytes = jcr->JobBytes;
706 jcr->jr.VolSessionId = jcr->VolSessionId;
707 jcr->jr.VolSessionTime = jcr->VolSessionTime;
708 if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
709 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
710 db_strerror(jcr->db));
715 * Takes base_name and appends (unique) current
716 * date and time to form unique job name.
718 * Returns: unique job name in jcr->Job
719 * date/time in jcr->start_time
721 void create_unique_job_name(JCR *jcr, const char *base_name)
723 /* Job start mutex */
724 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
725 static time_t last_start_time = 0;
728 char dt[MAX_TIME_LENGTH];
729 char name[MAX_NAME_LENGTH];
732 /* Guarantee unique start time -- maximum one per second, and
733 * thus unique Job Name
735 P(mutex); /* lock creation of jobs */
737 while (now == last_start_time) {
738 bmicrosleep(0, 500000);
741 last_start_time = now;
742 V(mutex); /* allow creation of jobs */
743 jcr->start_time = now;
744 /* Form Unique JobName */
745 localtime_r(&now, &tm);
746 /* Use only characters that are permitted in Windows filenames */
747 strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
748 bstrncpy(name, base_name, sizeof(name));
749 name[sizeof(name)-22] = 0; /* truncate if too long */
750 bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s", name, dt); /* add date & time */
751 /* Convert spaces into underscores */
752 for (p=jcr->Job; *p; p++) {
759 /* Called directly from job rescheduling */
760 void dird_free_jcr_pointers(JCR *jcr)
762 if (jcr->sd_auth_key) {
763 free(jcr->sd_auth_key);
764 jcr->sd_auth_key = NULL;
770 if (jcr->file_bsock) {
771 Dmsg0(200, "Close File bsock\n");
772 bnet_close(jcr->file_bsock);
773 jcr->file_bsock = NULL;
775 if (jcr->store_bsock) {
776 Dmsg0(200, "Close Store bsock\n");
777 bnet_close(jcr->store_bsock);
778 jcr->store_bsock = NULL;
781 Dmsg0(200, "Free JCR fname\n");
782 free_pool_memory(jcr->fname);
786 Dmsg0(200, "Free JCR stime\n");
787 free_pool_memory(jcr->stime);
790 if (jcr->RestoreBootstrap) {
791 free(jcr->RestoreBootstrap);
792 jcr->RestoreBootstrap = NULL;
794 if (jcr->client_uname) {
795 free_pool_memory(jcr->client_uname);
796 jcr->client_uname = NULL;
798 if (jcr->term_wait_inited) {
799 pthread_cond_destroy(&jcr->term_wait);
800 jcr->term_wait_inited = false;
803 free_pool_memory(jcr->attr);
813 * Free the Job Control Record if no one is still using it.
814 * Called from main free_jcr() routine in src/lib/jcr.c so
815 * that we can do our Director specific cleanup of the jcr.
817 void dird_free_jcr(JCR *jcr)
819 Dmsg0(200, "Start dird free_jcr\n");
821 dird_free_jcr_pointers(jcr);
823 /* Delete lists setup to hold storage pointers */
827 jcr->job_end_push.destroy();
828 Dmsg0(200, "End dird free_jcr\n");
832 * Set some defaults in the JCR necessary to
833 * run. These items are pulled from the job
834 * definition as defaults, but can be overridden
835 * later either by the Run record in the Schedule resource,
836 * or by the Console program.
838 void set_jcr_defaults(JCR *jcr, JOB *job)
842 jcr->JobType = job->JobType;
843 switch (jcr->JobType) {
846 jcr->JobLevel = L_NONE;
849 jcr->JobLevel = job->JobLevel;
852 jcr->JobPriority = job->Priority;
853 /* Copy storage definitions -- deleted in dir_free_jcr above */
858 jcr->storage = New(alist(10, not_owned_by_alist));
859 foreach_alist(st, job->storage) {
860 jcr->storage->append(st);
864 jcr->store = (STORE *)jcr->storage->first();
866 jcr->client = job->client;
867 if (!jcr->client_name) {
868 jcr->client_name = get_pool_memory(PM_NAME);
870 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
871 jcr->pool = job->pool;
872 jcr->full_pool = job->full_pool;
873 jcr->inc_pool = job->inc_pool;
874 jcr->dif_pool = job->dif_pool;
875 jcr->catalog = job->client->catalog;
876 jcr->fileset = job->fileset;
877 jcr->messages = job->messages;
878 jcr->spool_data = job->spool_data;
879 jcr->write_part_after_job = job->write_part_after_job;
880 if (jcr->RestoreBootstrap) {
881 free(jcr->RestoreBootstrap);
882 jcr->RestoreBootstrap = NULL;
884 /* This can be overridden by Console program */
885 if (job->RestoreBootstrap) {
886 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
888 /* This can be overridden by Console program */
889 jcr->verify_job = job->verify_job;
890 /* If no default level given, set one */
891 if (jcr->JobLevel == 0) {
892 switch (jcr->JobType) {
894 jcr->JobLevel = L_VERIFY_CATALOG;
897 jcr->JobLevel = L_INCREMENTAL;
901 jcr->JobLevel = L_NONE;
910 * copy the storage definitions from an old JCR to a new one
912 void copy_storage(JCR *new_jcr, JCR *old_jcr)
914 if (old_jcr->storage) {
916 if (new_jcr->storage) {
917 delete new_jcr->storage;
919 new_jcr->storage = New(alist(10, not_owned_by_alist));
920 foreach_alist(st, old_jcr->storage) {
921 new_jcr->storage->append(st);
924 if (old_jcr->store) {
925 new_jcr->store = old_jcr->store;
926 } else if (new_jcr->storage) {
927 new_jcr->store = (STORE *)new_jcr->storage->first();
931 /* Set storage override */
932 void set_storage(JCR *jcr, STORE *store)
937 foreach_alist(storage, jcr->storage) {
938 if (store == storage) {
942 /* Store not in list, so add it */
943 jcr->storage->prepend(store);