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;
105 create_unique_job_name(jcr, jcr->job->hdr.name);
106 set_jcr_job_status(jcr, JS_Created);
112 Dmsg0(50, "Open database\n");
113 jcr->db=db_init_database(jcr, jcr->catalog->db_name, jcr->catalog->db_user,
114 jcr->catalog->db_password, jcr->catalog->db_address,
115 jcr->catalog->db_port, jcr->catalog->db_socket,
116 jcr->catalog->mult_db_connections);
117 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
118 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
119 jcr->catalog->db_name);
121 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
125 Dmsg0(50, "DB opened\n");
130 init_jcr_job_record(jcr);
131 if (!db_create_job_record(jcr, jcr->db, &jcr->jr)) {
132 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
135 jcr->JobId = jcr->jr.JobId;
136 Dmsg4(100, "Created job record JobId=%d Name=%s Type=%c Level=%c\n",
137 jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel);
139 generate_daemon_event(jcr, "JobStart");
141 if (!get_or_create_client_record(jcr)) {
145 if (job_canceled(jcr)) {
149 Dmsg0(200, "Add jrc to work queue\n");
156 free_memory(jcr->fname);
164 * This is the engine called by jobq.c:jobq_add() when we were pulled
165 * from the work queue.
166 * At this point, we are running in our own thread and all
167 * necessary resources are allocated -- see jobq.c
169 static void *job_thread(void *arg)
171 JCR *jcr = (JCR *)arg;
173 jcr->my_thread_id = pthread_self();
174 pthread_detach(jcr->my_thread_id);
175 sm_check(__FILE__, __LINE__, true);
177 Dmsg0(200, "=====Start Job=========\n");
178 jcr->start_time = time(NULL); /* set the real start time */
179 jcr->jr.StartTime = jcr->start_time;
181 if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
182 (utime_t)(jcr->start_time - jcr->sched_time)) {
183 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
184 set_jcr_job_status(jcr, JS_Canceled);
187 /* TODO : check if it is used somewhere */
188 if (jcr->job->RunScripts == NULL)
190 Dmsg0(200, "Warning, job->RunScripts is empty\n");
191 jcr->job->RunScripts = New(alist(10, not_owned_by_alist));
195 * Note, we continue, even if the job is canceled above. This
196 * will permit proper setting of the job start record and
197 * the error (cancel) will be picked up below.
200 generate_job_event(jcr, "JobInit");
201 set_jcr_job_status(jcr, JS_Running); /* this will be set only if no error */
204 jcr->fname = get_pool_memory(PM_FNAME);
208 * Now, do pre-run stuff, like setting job level (Inc/diff, ...)
209 * this allows us to setup a proper job start record for restarting
210 * in case of later errors.
212 switch (jcr->JobType) {
214 if (!do_backup_init(jcr)) {
215 backup_cleanup(jcr, JS_ErrorTerminated);
219 if (!do_verify_init(jcr)) {
220 verify_cleanup(jcr, JS_ErrorTerminated);
224 if (!do_restore_init(jcr)) {
225 restore_cleanup(jcr, JS_ErrorTerminated);
229 if (!do_admin_init(jcr)) {
230 admin_cleanup(jcr, JS_ErrorTerminated);
234 if (!do_migration_init(jcr)) {
235 migration_cleanup(jcr, JS_ErrorTerminated);
239 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->JobType);
240 set_jcr_job_status(jcr, JS_ErrorTerminated);
244 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
245 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
248 if (job_canceled(jcr)) {
249 update_job_end_record(jcr);
253 /* Run any script BeforeJob on dird */
254 run_scripts(jcr, jcr->job->RunScripts, "BeforeJob");
257 * We re-update the job start record so that the start
258 * time is set after the run before job. This avoids
259 * that any files created by the run before job will
260 * be saved twice. They will be backed up in the current
261 * job, but not in the next one unless they are changed.
262 * Without this, they will be backed up in this job and
263 * in the next job run because in that case, their date
264 * is after the start of this run.
266 jcr->start_time = time(NULL);
267 jcr->jr.StartTime = jcr->start_time;
268 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
269 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
271 generate_job_event(jcr, "JobRun");
273 switch (jcr->JobType) {
275 if (do_backup(jcr)) {
278 backup_cleanup(jcr, JS_ErrorTerminated);
282 if (do_verify(jcr)) {
285 verify_cleanup(jcr, JS_ErrorTerminated);
289 if (do_restore(jcr)) {
292 restore_cleanup(jcr, JS_ErrorTerminated);
299 admin_cleanup(jcr, JS_ErrorTerminated);
305 if (do_migration(jcr)) {
308 migration_cleanup(jcr, JS_ErrorTerminated);
312 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->JobType);
316 run_scripts(jcr, jcr->job->RunScripts, "AfterJob");
318 /* Send off any queued messages */
319 if (jcr->msg_queue->size() > 0) {
320 dequeue_messages(jcr);
325 generate_daemon_event(jcr, "JobEnd");
326 Dmsg1(50, "======== End Job stat=%c ==========\n", jcr->JobStatus);
327 sm_check(__FILE__, __LINE__, true);
333 * Cancel a job -- typically called by the UA (Console program), but may also
334 * be called by the job watchdog.
336 * Returns: true if cancel appears to be successful
337 * false on failure. Message sent to ua->jcr.
339 bool cancel_job(UAContext *ua, JCR *jcr)
343 set_jcr_job_status(jcr, JS_Canceled);
345 switch (jcr->JobStatus) {
348 case JS_WaitClientRes:
349 case JS_WaitStoreRes:
350 case JS_WaitPriority:
352 case JS_WaitStartTime:
353 bsendmsg(ua, _("JobId %d, Job %s marked to be canceled.\n"),
354 jcr->JobId, jcr->Job);
355 jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
359 /* Cancel File daemon */
360 if (jcr->file_bsock) {
361 ua->jcr->client = jcr->client;
362 if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
363 bsendmsg(ua, _("Failed to connect to File daemon.\n"));
366 Dmsg0(200, "Connected to file daemon\n");
367 fd = ua->jcr->file_bsock;
368 bnet_fsend(fd, "cancel Job=%s\n", jcr->Job);
369 while (bnet_recv(fd) >= 0) {
370 bsendmsg(ua, "%s", fd->msg);
372 bnet_sig(fd, BNET_TERMINATE);
374 ua->jcr->file_bsock = NULL;
377 /* Cancel Storage daemon */
378 if (jcr->store_bsock) {
379 if (!ua->jcr->storage) {
380 copy_storage(ua->jcr, jcr->storage);
382 set_storage(ua->jcr, jcr->store);
384 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
385 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
388 Dmsg0(200, "Connected to storage daemon\n");
389 sd = ua->jcr->store_bsock;
390 bnet_fsend(sd, "cancel Job=%s\n", jcr->Job);
391 while (bnet_recv(sd) >= 0) {
392 bsendmsg(ua, "%s", sd->msg);
394 bnet_sig(sd, BNET_TERMINATE);
396 ua->jcr->store_bsock = NULL;
404 static void job_monitor_destructor(watchdog_t *self)
406 JCR *control_jcr = (JCR *)self->data;
408 free_jcr(control_jcr);
411 static void job_monitor_watchdog(watchdog_t *self)
413 JCR *control_jcr, *jcr;
415 control_jcr = (JCR *)self->data;
417 Dmsg1(800, "job_monitor_watchdog %p called\n", self);
422 if (jcr->JobId == 0) {
423 Dmsg2(800, "Skipping JCR %p (%s) with JobId 0\n",
428 /* check MaxWaitTime */
429 cancel = job_check_maxwaittime(control_jcr, jcr);
431 /* check MaxRunTime */
432 cancel |= job_check_maxruntime(control_jcr, jcr);
435 Dmsg3(800, "Cancelling JCR %p jobid %d (%s)\n",
436 jcr, jcr->JobId, jcr->Job);
438 UAContext *ua = new_ua_context(jcr);
439 ua->jcr = control_jcr;
443 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", jcr, jcr->JobId);
446 /* Keep reference counts correct */
452 * Check if the maxwaittime has expired and it is possible
455 static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr)
458 bool ok_to_cancel = false;
461 if (job->MaxWaitTime == 0 && job->FullMaxWaitTime == 0 &&
462 job->IncMaxWaitTime == 0 && job->DiffMaxWaitTime == 0) {
465 if (jcr->JobLevel == L_FULL && job->FullMaxWaitTime != 0 &&
466 (watchdog_time - jcr->start_time) >= job->FullMaxWaitTime) {
468 } else if (jcr->JobLevel == L_DIFFERENTIAL && job->DiffMaxWaitTime != 0 &&
469 (watchdog_time - jcr->start_time) >= job->DiffMaxWaitTime) {
471 } else if (jcr->JobLevel == L_INCREMENTAL && job->IncMaxWaitTime != 0 &&
472 (watchdog_time - jcr->start_time) >= job->IncMaxWaitTime) {
474 } else if (job->MaxWaitTime != 0 &&
475 (watchdog_time - jcr->start_time) >= job->MaxWaitTime) {
481 Dmsg3(800, "Job %d (%s): MaxWaitTime of %d seconds exceeded, "
483 jcr->JobId, jcr->Job, job->MaxWaitTime);
484 switch (jcr->JobStatus) {
489 case JS_WaitStoreRes:
490 case JS_WaitClientRes:
492 case JS_WaitPriority:
494 case JS_WaitStartTime:
496 Dmsg0(200, "JCR blocked in #1\n");
499 Dmsg0(800, "JCR running, checking SD status\n");
500 switch (jcr->SDJobStatus) {
505 Dmsg0(800, "JCR blocked in #2\n");
508 Dmsg0(800, "JCR not blocked in #2\n");
513 case JS_ErrorTerminated:
516 Dmsg0(800, "JCR already dead in #3\n");
519 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
522 Dmsg3(800, "MaxWaitTime result: %scancel JCR %p (%s)\n",
523 cancel ? "" : "do not ", jcr, jcr->job);
529 * Check if maxruntime has expired and if the job can be
532 static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr)
536 if (jcr->job->MaxRunTime == 0) {
539 if ((watchdog_time - jcr->start_time) < jcr->job->MaxRunTime) {
540 Dmsg3(200, "Job %p (%s) with MaxRunTime %d not expired\n",
541 jcr, jcr->Job, jcr->job->MaxRunTime);
545 switch (jcr->JobStatus) {
551 case JS_WaitStoreRes:
552 case JS_WaitClientRes:
554 case JS_WaitPriority:
556 case JS_WaitStartTime:
561 case JS_ErrorTerminated:
567 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
571 Dmsg3(200, "MaxRunTime result: %scancel JCR %p (%s)\n",
572 cancel ? "" : "do not ", jcr, jcr->job);
579 * Get or create a Client record for this Job
581 bool get_or_create_client_record(JCR *jcr)
585 memset(&cr, 0, sizeof(cr));
586 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
587 cr.AutoPrune = jcr->client->AutoPrune;
588 cr.FileRetention = jcr->client->FileRetention;
589 cr.JobRetention = jcr->client->JobRetention;
590 if (!jcr->client_name) {
591 jcr->client_name = get_pool_memory(PM_NAME);
593 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
594 if (!db_create_client_record(jcr, jcr->db, &cr)) {
595 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"),
596 db_strerror(jcr->db));
599 jcr->jr.ClientId = cr.ClientId;
601 if (!jcr->client_uname) {
602 jcr->client_uname = get_pool_memory(PM_NAME);
604 pm_strcpy(jcr->client_uname, cr.Uname);
606 Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name,
611 bool get_or_create_fileset_record(JCR *jcr)
615 * Get or Create FileSet record
617 memset(&fsr, 0, sizeof(FILESET_DBR));
618 bstrncpy(fsr.FileSet, jcr->fileset->hdr.name, sizeof(fsr.FileSet));
619 if (jcr->fileset->have_MD5) {
620 struct MD5Context md5c;
621 unsigned char digest[MD5HashSize];
622 memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
623 MD5Final(digest, &md5c);
624 bin_to_base64(fsr.MD5, (char *)digest, MD5HashSize);
625 bstrncpy(jcr->fileset->MD5, fsr.MD5, sizeof(jcr->fileset->MD5));
627 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n"));
629 if (!jcr->fileset->ignore_fs_changes ||
630 !db_get_fileset_record(jcr, jcr->db, &fsr)) {
631 if (!db_create_fileset_record(jcr, jcr->db, &fsr)) {
632 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
633 fsr.FileSet, db_strerror(jcr->db));
637 jcr->jr.FileSetId = fsr.FileSetId;
638 bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime));
639 Dmsg2(119, "Created FileSet %s record %u\n", jcr->fileset->hdr.name,
644 void init_jcr_job_record(JCR *jcr)
646 jcr->jr.SchedTime = jcr->sched_time;
647 jcr->jr.StartTime = jcr->start_time;
648 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
649 jcr->jr.JobType = jcr->JobType;
650 jcr->jr.JobLevel = jcr->JobLevel;
651 jcr->jr.JobStatus = jcr->JobStatus;
652 jcr->jr.JobId = jcr->JobId;
653 bstrncpy(jcr->jr.Name, jcr->job->hdr.name, sizeof(jcr->jr.Name));
654 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
658 * Write status and such in DB
660 void update_job_end_record(JCR *jcr)
662 jcr->jr.EndTime = time(NULL);
663 jcr->end_time = jcr->jr.EndTime;
664 jcr->jr.JobId = jcr->JobId;
665 jcr->jr.JobStatus = jcr->JobStatus;
666 jcr->jr.JobFiles = jcr->JobFiles;
667 jcr->jr.JobBytes = jcr->JobBytes;
668 jcr->jr.VolSessionId = jcr->VolSessionId;
669 jcr->jr.VolSessionTime = jcr->VolSessionTime;
670 if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
671 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
672 db_strerror(jcr->db));
677 * Takes base_name and appends (unique) current
678 * date and time to form unique job name.
680 * Returns: unique job name in jcr->Job
681 * date/time in jcr->start_time
683 void create_unique_job_name(JCR *jcr, const char *base_name)
685 /* Job start mutex */
686 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
687 static time_t last_start_time = 0;
690 char dt[MAX_TIME_LENGTH];
691 char name[MAX_NAME_LENGTH];
694 /* Guarantee unique start time -- maximum one per second, and
695 * thus unique Job Name
697 P(mutex); /* lock creation of jobs */
699 while (now == last_start_time) {
700 bmicrosleep(0, 500000);
703 last_start_time = now;
704 V(mutex); /* allow creation of jobs */
705 jcr->start_time = now;
706 /* Form Unique JobName */
707 localtime_r(&now, &tm);
708 /* Use only characters that are permitted in Windows filenames */
709 strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
710 bstrncpy(name, base_name, sizeof(name));
711 name[sizeof(name)-22] = 0; /* truncate if too long */
712 bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s", name, dt); /* add date & time */
713 /* Convert spaces into underscores */
714 for (p=jcr->Job; *p; p++) {
721 /* Called directly from job rescheduling */
722 void dird_free_jcr_pointers(JCR *jcr)
724 if (jcr->sd_auth_key) {
725 free(jcr->sd_auth_key);
726 jcr->sd_auth_key = NULL;
732 if (jcr->file_bsock) {
733 Dmsg0(200, "Close File bsock\n");
734 bnet_close(jcr->file_bsock);
735 jcr->file_bsock = NULL;
737 if (jcr->store_bsock) {
738 Dmsg0(200, "Close Store bsock\n");
739 bnet_close(jcr->store_bsock);
740 jcr->store_bsock = NULL;
743 Dmsg0(200, "Free JCR fname\n");
744 free_pool_memory(jcr->fname);
748 Dmsg0(200, "Free JCR stime\n");
749 free_pool_memory(jcr->stime);
752 if (jcr->RestoreBootstrap) {
753 free(jcr->RestoreBootstrap);
754 jcr->RestoreBootstrap = NULL;
756 if (jcr->client_uname) {
757 free_pool_memory(jcr->client_uname);
758 jcr->client_uname = NULL;
761 free_pool_memory(jcr->attr);
771 * Free the Job Control Record if no one is still using it.
772 * Called from main free_jcr() routine in src/lib/jcr.c so
773 * that we can do our Director specific cleanup of the jcr.
775 void dird_free_jcr(JCR *jcr)
777 Dmsg0(200, "Start dird free_jcr\n");
779 dird_free_jcr_pointers(jcr);
780 if (jcr->term_wait_inited) {
781 pthread_cond_destroy(&jcr->term_wait);
782 jcr->term_wait_inited = false;
785 /* Delete lists setup to hold storage pointers */
789 jcr->job_end_push.destroy();
790 Dmsg0(200, "End dird free_jcr\n");
794 * Set some defaults in the JCR necessary to
795 * run. These items are pulled from the job
796 * definition as defaults, but can be overridden
797 * later either by the Run record in the Schedule resource,
798 * or by the Console program.
800 void set_jcr_defaults(JCR *jcr, JOB *job)
803 jcr->JobType = job->JobType;
804 switch (jcr->JobType) {
807 jcr->JobLevel = L_NONE;
810 jcr->JobLevel = job->JobLevel;
813 jcr->JobPriority = job->Priority;
814 /* Copy storage definitions -- deleted in dir_free_jcr above */
815 copy_storage(jcr, job->storage);
816 jcr->client = job->client;
817 if (!jcr->client_name) {
818 jcr->client_name = get_pool_memory(PM_NAME);
820 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
821 jcr->pool = job->pool;
822 jcr->full_pool = job->full_pool;
823 jcr->inc_pool = job->inc_pool;
824 jcr->dif_pool = job->dif_pool;
825 jcr->catalog = job->client->catalog;
826 jcr->fileset = job->fileset;
827 jcr->messages = job->messages;
828 jcr->spool_data = job->spool_data;
829 jcr->write_part_after_job = job->write_part_after_job;
830 if (jcr->RestoreBootstrap) {
831 free(jcr->RestoreBootstrap);
832 jcr->RestoreBootstrap = NULL;
834 /* This can be overridden by Console program */
835 if (job->RestoreBootstrap) {
836 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
838 /* This can be overridden by Console program */
839 jcr->verify_job = job->verify_job;
840 /* If no default level given, set one */
841 if (jcr->JobLevel == 0) {
842 switch (jcr->JobType) {
844 jcr->JobLevel = L_VERIFY_CATALOG;
847 jcr->JobLevel = L_INCREMENTAL;
851 jcr->JobLevel = L_NONE;
861 * Copy the storage definitions from an alist to the JCR
863 void copy_storage(JCR *jcr, alist *storage)
870 jcr->storage = New(alist(10, not_owned_by_alist));
871 foreach_alist(st, storage) {
872 jcr->storage->append(st);
876 jcr->store = (STORE *)jcr->storage->first();
881 /* Set storage override */
882 void set_storage(JCR *jcr, STORE *store)
887 foreach_alist(storage, jcr->storage) {
888 if (store == storage) {
892 /* Store not in list, so add it */
893 jcr->storage->prepend(store);
896 void create_clones(JCR *jcr)
899 * Fire off any clone jobs (run directives)
901 Dmsg2(900, "cloned=%d run_cmds=%p\n", jcr->cloned, jcr->job->run_cmds);
902 if (!jcr->cloned && jcr->job->run_cmds) {
905 POOLMEM *cmd = get_pool_memory(PM_FNAME);
906 UAContext *ua = new_ua_context(jcr);
908 foreach_alist(runcmd, job->run_cmds) {
909 cmd = edit_job_codes(jcr, cmd, runcmd, "");
910 Mmsg(ua->cmd, "run %s cloned=yes", cmd);
911 Dmsg1(900, "=============== Clone cmd=%s\n", ua->cmd);
912 parse_ua_args(ua); /* parse command */
913 int stat = run_cmd(ua, ua->cmd);
915 Jmsg(jcr, M_ERROR, 0, _("Could not start clone job.\n"));
917 Jmsg(jcr, M_INFO, 0, _("Clone JobId %d started.\n"), stat);
921 free_pool_memory(cmd);
925 bool create_restore_bootstrap_file(JCR *jcr)
929 memset(&rx, 0, sizeof(rx));
932 rx.bsr->JobId = jcr->previous_jr.JobId;
933 ua = new_ua_context(jcr);
934 complete_bsr(ua, rx.bsr);
935 rx.bsr->fi = new_findex();
936 rx.bsr->fi->findex = 1;
937 rx.bsr->fi->findex2 = jcr->previous_jr.JobFiles;
938 jcr->ExpectedFiles = write_bsr_file(ua, rx);
939 if (jcr->ExpectedFiles == 0) {
946 jcr->needs_sd = true;