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);
251 /* Run any script BeforeJob on dird */
252 run_scripts(jcr, jcr->job->RunScripts, "BeforeJob");
255 * We re-update the job start record so that the start
256 * time is set after the run before job. This avoids
257 * that any files created by the run before job will
258 * be saved twice. They will be backed up in the current
259 * job, but not in the next one unless they are changed.
260 * Without this, they will be backed up in this job and
261 * in the next job run because in that case, their date
262 * is after the start of this run.
264 jcr->start_time = time(NULL);
265 jcr->jr.StartTime = jcr->start_time;
266 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
267 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
269 generate_job_event(jcr, "JobRun");
271 switch (jcr->JobType) {
273 if (do_backup(jcr)) {
276 backup_cleanup(jcr, JS_ErrorTerminated);
280 if (do_verify(jcr)) {
283 verify_cleanup(jcr, JS_ErrorTerminated);
287 if (do_restore(jcr)) {
290 restore_cleanup(jcr, JS_ErrorTerminated);
297 admin_cleanup(jcr, JS_ErrorTerminated);
303 if (do_migration(jcr)) {
306 migration_cleanup(jcr, JS_ErrorTerminated);
310 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->JobType);
314 run_scripts(jcr, jcr->job->RunScripts, "AfterJob");
316 /* Send off any queued messages */
317 if (jcr->msg_queue->size() > 0) {
318 dequeue_messages(jcr);
322 generate_daemon_event(jcr, "JobEnd");
323 Dmsg1(50, "======== End Job stat=%c ==========\n", jcr->JobStatus);
324 sm_check(__FILE__, __LINE__, true);
330 * Cancel a job -- typically called by the UA (Console program), but may also
331 * be called by the job watchdog.
333 * Returns: true if cancel appears to be successful
334 * false on failure. Message sent to ua->jcr.
336 bool cancel_job(UAContext *ua, JCR *jcr)
340 set_jcr_job_status(jcr, JS_Canceled);
342 switch (jcr->JobStatus) {
345 case JS_WaitClientRes:
346 case JS_WaitStoreRes:
347 case JS_WaitPriority:
349 case JS_WaitStartTime:
350 bsendmsg(ua, _("JobId %d, Job %s marked to be canceled.\n"),
351 jcr->JobId, jcr->Job);
352 jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
356 /* Cancel File daemon */
357 if (jcr->file_bsock) {
358 ua->jcr->client = jcr->client;
359 if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
360 bsendmsg(ua, _("Failed to connect to File daemon.\n"));
363 Dmsg0(200, "Connected to file daemon\n");
364 fd = ua->jcr->file_bsock;
365 bnet_fsend(fd, "cancel Job=%s\n", jcr->Job);
366 while (bnet_recv(fd) >= 0) {
367 bsendmsg(ua, "%s", fd->msg);
369 bnet_sig(fd, BNET_TERMINATE);
371 ua->jcr->file_bsock = NULL;
374 /* Cancel Storage daemon */
375 if (jcr->store_bsock) {
376 if (!ua->jcr->storage) {
377 copy_storage(ua->jcr, jcr->storage);
379 set_storage(ua->jcr, jcr->store);
381 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
382 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
385 Dmsg0(200, "Connected to storage daemon\n");
386 sd = ua->jcr->store_bsock;
387 bnet_fsend(sd, "cancel Job=%s\n", jcr->Job);
388 while (bnet_recv(sd) >= 0) {
389 bsendmsg(ua, "%s", sd->msg);
391 bnet_sig(sd, BNET_TERMINATE);
393 ua->jcr->store_bsock = NULL;
401 static void job_monitor_destructor(watchdog_t *self)
403 JCR *control_jcr = (JCR *)self->data;
405 free_jcr(control_jcr);
408 static void job_monitor_watchdog(watchdog_t *self)
410 JCR *control_jcr, *jcr;
412 control_jcr = (JCR *)self->data;
414 Dmsg1(800, "job_monitor_watchdog %p called\n", self);
419 if (jcr->JobId == 0) {
420 Dmsg2(800, "Skipping JCR %p (%s) with JobId 0\n",
425 /* check MaxWaitTime */
426 cancel = job_check_maxwaittime(control_jcr, jcr);
428 /* check MaxRunTime */
429 cancel |= job_check_maxruntime(control_jcr, jcr);
432 Dmsg3(800, "Cancelling JCR %p jobid %d (%s)\n",
433 jcr, jcr->JobId, jcr->Job);
435 UAContext *ua = new_ua_context(jcr);
436 ua->jcr = control_jcr;
440 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", jcr, jcr->JobId);
443 /* Keep reference counts correct */
449 * Check if the maxwaittime has expired and it is possible
452 static bool job_check_maxwaittime(JCR *control_jcr, JCR *jcr)
455 bool ok_to_cancel = false;
458 if (job->MaxWaitTime == 0 && job->FullMaxWaitTime == 0 &&
459 job->IncMaxWaitTime == 0 && job->DiffMaxWaitTime == 0) {
462 if (jcr->JobLevel == L_FULL && job->FullMaxWaitTime != 0 &&
463 (watchdog_time - jcr->start_time) >= job->FullMaxWaitTime) {
465 } else if (jcr->JobLevel == L_DIFFERENTIAL && job->DiffMaxWaitTime != 0 &&
466 (watchdog_time - jcr->start_time) >= job->DiffMaxWaitTime) {
468 } else if (jcr->JobLevel == L_INCREMENTAL && job->IncMaxWaitTime != 0 &&
469 (watchdog_time - jcr->start_time) >= job->IncMaxWaitTime) {
471 } else if (job->MaxWaitTime != 0 &&
472 (watchdog_time - jcr->start_time) >= job->MaxWaitTime) {
478 Dmsg3(800, "Job %d (%s): MaxWaitTime of %d seconds exceeded, "
480 jcr->JobId, jcr->Job, job->MaxWaitTime);
481 switch (jcr->JobStatus) {
486 case JS_WaitStoreRes:
487 case JS_WaitClientRes:
489 case JS_WaitPriority:
491 case JS_WaitStartTime:
493 Dmsg0(200, "JCR blocked in #1\n");
496 Dmsg0(800, "JCR running, checking SD status\n");
497 switch (jcr->SDJobStatus) {
502 Dmsg0(800, "JCR blocked in #2\n");
505 Dmsg0(800, "JCR not blocked in #2\n");
510 case JS_ErrorTerminated:
513 Dmsg0(800, "JCR already dead in #3\n");
516 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
519 Dmsg3(800, "MaxWaitTime result: %scancel JCR %p (%s)\n",
520 cancel ? "" : "do not ", jcr, jcr->job);
526 * Check if maxruntime has expired and if the job can be
529 static bool job_check_maxruntime(JCR *control_jcr, JCR *jcr)
533 if (jcr->job->MaxRunTime == 0) {
536 if ((watchdog_time - jcr->start_time) < jcr->job->MaxRunTime) {
537 Dmsg3(200, "Job %p (%s) with MaxRunTime %d not expired\n",
538 jcr, jcr->Job, jcr->job->MaxRunTime);
542 switch (jcr->JobStatus) {
548 case JS_WaitStoreRes:
549 case JS_WaitClientRes:
551 case JS_WaitPriority:
553 case JS_WaitStartTime:
558 case JS_ErrorTerminated:
564 Jmsg1(jcr, M_ERROR, 0, _("Unhandled job status code %d\n"),
568 Dmsg3(200, "MaxRunTime result: %scancel JCR %p (%s)\n",
569 cancel ? "" : "do not ", jcr, jcr->job);
576 * Get or create a Client record for this Job
578 bool get_or_create_client_record(JCR *jcr)
582 memset(&cr, 0, sizeof(cr));
583 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
584 cr.AutoPrune = jcr->client->AutoPrune;
585 cr.FileRetention = jcr->client->FileRetention;
586 cr.JobRetention = jcr->client->JobRetention;
587 if (!jcr->client_name) {
588 jcr->client_name = get_pool_memory(PM_NAME);
590 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
591 if (!db_create_client_record(jcr, jcr->db, &cr)) {
592 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"),
593 db_strerror(jcr->db));
596 jcr->jr.ClientId = cr.ClientId;
598 if (!jcr->client_uname) {
599 jcr->client_uname = get_pool_memory(PM_NAME);
601 pm_strcpy(jcr->client_uname, cr.Uname);
603 Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name,
608 bool get_or_create_fileset_record(JCR *jcr)
612 * Get or Create FileSet record
614 memset(&fsr, 0, sizeof(FILESET_DBR));
615 bstrncpy(fsr.FileSet, jcr->fileset->hdr.name, sizeof(fsr.FileSet));
616 if (jcr->fileset->have_MD5) {
617 struct MD5Context md5c;
618 unsigned char digest[MD5HashSize];
619 memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
620 MD5Final(digest, &md5c);
621 bin_to_base64(fsr.MD5, (char *)digest, MD5HashSize);
622 bstrncpy(jcr->fileset->MD5, fsr.MD5, sizeof(jcr->fileset->MD5));
624 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n"));
626 if (!jcr->fileset->ignore_fs_changes ||
627 !db_get_fileset_record(jcr, jcr->db, &fsr)) {
628 if (!db_create_fileset_record(jcr, jcr->db, &fsr)) {
629 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
630 fsr.FileSet, db_strerror(jcr->db));
634 jcr->jr.FileSetId = fsr.FileSetId;
635 bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime));
636 Dmsg2(119, "Created FileSet %s record %u\n", jcr->fileset->hdr.name,
641 void init_jcr_job_record(JCR *jcr)
643 jcr->jr.SchedTime = jcr->sched_time;
644 jcr->jr.StartTime = jcr->start_time;
645 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
646 jcr->jr.JobType = jcr->JobType;
647 jcr->jr.JobLevel = jcr->JobLevel;
648 jcr->jr.JobStatus = jcr->JobStatus;
649 jcr->jr.JobId = jcr->JobId;
650 bstrncpy(jcr->jr.Name, jcr->job->hdr.name, sizeof(jcr->jr.Name));
651 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
655 * Write status and such in DB
657 void update_job_end_record(JCR *jcr)
659 jcr->jr.EndTime = time(NULL);
660 jcr->end_time = jcr->jr.EndTime;
661 jcr->jr.JobId = jcr->JobId;
662 jcr->jr.JobStatus = jcr->JobStatus;
663 jcr->jr.JobFiles = jcr->JobFiles;
664 jcr->jr.JobBytes = jcr->JobBytes;
665 jcr->jr.VolSessionId = jcr->VolSessionId;
666 jcr->jr.VolSessionTime = jcr->VolSessionTime;
667 if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
668 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
669 db_strerror(jcr->db));
674 * Takes base_name and appends (unique) current
675 * date and time to form unique job name.
677 * Returns: unique job name in jcr->Job
678 * date/time in jcr->start_time
680 void create_unique_job_name(JCR *jcr, const char *base_name)
682 /* Job start mutex */
683 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
684 static time_t last_start_time = 0;
687 char dt[MAX_TIME_LENGTH];
688 char name[MAX_NAME_LENGTH];
691 /* Guarantee unique start time -- maximum one per second, and
692 * thus unique Job Name
694 P(mutex); /* lock creation of jobs */
696 while (now == last_start_time) {
697 bmicrosleep(0, 500000);
700 last_start_time = now;
701 V(mutex); /* allow creation of jobs */
702 jcr->start_time = now;
703 /* Form Unique JobName */
704 localtime_r(&now, &tm);
705 /* Use only characters that are permitted in Windows filenames */
706 strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
707 bstrncpy(name, base_name, sizeof(name));
708 name[sizeof(name)-22] = 0; /* truncate if too long */
709 bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s", name, dt); /* add date & time */
710 /* Convert spaces into underscores */
711 for (p=jcr->Job; *p; p++) {
718 /* Called directly from job rescheduling */
719 void dird_free_jcr_pointers(JCR *jcr)
721 if (jcr->sd_auth_key) {
722 free(jcr->sd_auth_key);
723 jcr->sd_auth_key = NULL;
729 if (jcr->file_bsock) {
730 Dmsg0(200, "Close File bsock\n");
731 bnet_close(jcr->file_bsock);
732 jcr->file_bsock = NULL;
734 if (jcr->store_bsock) {
735 Dmsg0(200, "Close Store bsock\n");
736 bnet_close(jcr->store_bsock);
737 jcr->store_bsock = NULL;
740 Dmsg0(200, "Free JCR fname\n");
741 free_pool_memory(jcr->fname);
745 Dmsg0(200, "Free JCR stime\n");
746 free_pool_memory(jcr->stime);
749 if (jcr->RestoreBootstrap) {
750 free(jcr->RestoreBootstrap);
751 jcr->RestoreBootstrap = NULL;
753 if (jcr->client_uname) {
754 free_pool_memory(jcr->client_uname);
755 jcr->client_uname = NULL;
758 free_pool_memory(jcr->attr);
768 * Free the Job Control Record if no one is still using it.
769 * Called from main free_jcr() routine in src/lib/jcr.c so
770 * that we can do our Director specific cleanup of the jcr.
772 void dird_free_jcr(JCR *jcr)
774 Dmsg0(200, "Start dird free_jcr\n");
776 dird_free_jcr_pointers(jcr);
777 if (jcr->term_wait_inited) {
778 pthread_cond_destroy(&jcr->term_wait);
779 jcr->term_wait_inited = false;
782 /* Delete lists setup to hold storage pointers */
786 jcr->job_end_push.destroy();
787 Dmsg0(200, "End dird free_jcr\n");
791 * Set some defaults in the JCR necessary to
792 * run. These items are pulled from the job
793 * definition as defaults, but can be overridden
794 * later either by the Run record in the Schedule resource,
795 * or by the Console program.
797 void set_jcr_defaults(JCR *jcr, JOB *job)
800 jcr->JobType = job->JobType;
801 switch (jcr->JobType) {
804 jcr->JobLevel = L_NONE;
807 jcr->JobLevel = job->JobLevel;
810 jcr->JobPriority = job->Priority;
811 /* Copy storage definitions -- deleted in dir_free_jcr above */
812 copy_storage(jcr, job->storage);
813 jcr->client = job->client;
814 if (!jcr->client_name) {
815 jcr->client_name = get_pool_memory(PM_NAME);
817 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
818 jcr->pool = job->pool;
819 jcr->full_pool = job->full_pool;
820 jcr->inc_pool = job->inc_pool;
821 jcr->dif_pool = job->dif_pool;
822 jcr->catalog = job->client->catalog;
823 jcr->fileset = job->fileset;
824 jcr->messages = job->messages;
825 jcr->spool_data = job->spool_data;
826 jcr->write_part_after_job = job->write_part_after_job;
827 if (jcr->RestoreBootstrap) {
828 free(jcr->RestoreBootstrap);
829 jcr->RestoreBootstrap = NULL;
831 /* This can be overridden by Console program */
832 if (job->RestoreBootstrap) {
833 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
835 /* This can be overridden by Console program */
836 jcr->verify_job = job->verify_job;
837 /* If no default level given, set one */
838 if (jcr->JobLevel == 0) {
839 switch (jcr->JobType) {
841 jcr->JobLevel = L_VERIFY_CATALOG;
844 jcr->JobLevel = L_INCREMENTAL;
848 jcr->JobLevel = L_NONE;
858 * Copy the storage definitions from an alist to the JCR
860 void copy_storage(JCR *jcr, alist *storage)
867 jcr->storage = New(alist(10, not_owned_by_alist));
868 foreach_alist(st, storage) {
869 jcr->storage->append(st);
873 jcr->store = (STORE *)jcr->storage->first();
878 /* Set storage override */
879 void set_storage(JCR *jcr, STORE *store)
884 foreach_alist(storage, jcr->storage) {
885 if (store == storage) {
889 /* Store not in list, so add it */
890 jcr->storage->prepend(store);
893 void create_clones(JCR *jcr)
896 * Fire off any clone jobs (run directives)
898 Dmsg2(900, "cloned=%d run_cmds=%p\n", jcr->cloned, jcr->job->run_cmds);
899 if (!jcr->cloned && jcr->job->run_cmds) {
902 POOLMEM *cmd = get_pool_memory(PM_FNAME);
903 UAContext *ua = new_ua_context(jcr);
905 foreach_alist(runcmd, job->run_cmds) {
906 cmd = edit_job_codes(jcr, cmd, runcmd, "");
907 Mmsg(ua->cmd, "run %s cloned=yes", cmd);
908 Dmsg1(900, "=============== Clone cmd=%s\n", ua->cmd);
909 parse_ua_args(ua); /* parse command */
910 int stat = run_cmd(ua, ua->cmd);
912 Jmsg(jcr, M_ERROR, 0, _("Could not start clone job.\n"));
914 Jmsg(jcr, M_INFO, 0, _("Clone JobId %d started.\n"), stat);
918 free_pool_memory(cmd);
922 bool create_restore_bootstrap_file(JCR *jcr)
926 memset(&rx, 0, sizeof(rx));
929 rx.bsr->JobId = jcr->previous_jr.JobId;
930 ua = new_ua_context(jcr);
931 complete_bsr(ua, rx.bsr);
932 rx.bsr->fi = new_findex();
933 rx.bsr->fi->findex = 1;
934 rx.bsr->fi->findex2 = jcr->previous_jr.JobFiles;
935 jcr->ExpectedFiles = write_bsr_file(ua, rx);
936 if (jcr->ExpectedFiles == 0) {
943 jcr->needs_sd = true;