2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 Bacula® is a registered trademark of Kern Sibbald.
18 * Bacula Director Job processing routines
20 * Kern Sibbald, October MM
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 *jcr);
32 static bool job_check_maxruntime(JCR *jcr);
33 static bool job_check_maxrunschedtime(JCR *jcr);
35 /* Imported subroutines */
36 extern void term_scheduler();
37 extern void term_ua_server();
39 /* Imported variables */
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.bstrerror(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 Dmsg0(200, "Add jrc to work queue\n");
79 /* Queue the job to be run */
80 if ((stat = jobq_add(&job_queue, jcr)) != 0) {
82 Jmsg(jcr, M_FATAL, 0, _("Could not add job queue: ERR=%s\n"), be.bstrerror(stat));
90 bool setup_job(JCR *jcr)
96 init_msg(jcr, jcr->messages, job_code_callback_director);
98 /* Initialize termination condition variable */
99 if ((errstat = pthread_cond_init(&jcr->term_wait, NULL)) != 0) {
101 Jmsg1(jcr, M_FATAL, 0, _("Unable to init job cond variable: ERR=%s\n"), be.bstrerror(errstat));
105 jcr->term_wait_inited = true;
107 create_unique_job_name(jcr, jcr->job->name());
108 jcr->setJobStatus(JS_Created);
114 Dmsg0(100, "Open database\n");
115 jcr->db = db_init_database(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
116 jcr->catalog->db_user, jcr->catalog->db_password,
117 jcr->catalog->db_address, jcr->catalog->db_port,
118 jcr->catalog->db_socket, jcr->catalog->mult_db_connections,
119 jcr->catalog->disable_batch_insert);
120 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
121 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
122 jcr->catalog->db_name);
124 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
125 db_close_database(jcr, jcr->db);
129 Dmsg0(150, "DB opened\n");
131 jcr->fname = get_pool_memory(PM_FNAME);
133 if (!jcr->pool_source) {
134 jcr->pool_source = get_pool_memory(PM_MESSAGE);
135 pm_strcpy(jcr->pool_source, _("unknown source"));
137 if (!jcr->next_pool_source) {
138 jcr->next_pool_source = get_pool_memory(PM_MESSAGE);
139 pm_strcpy(jcr->next_pool_source, _("unknown source"));
142 if (jcr->JobReads()) {
143 if (!jcr->rpool_source) {
144 jcr->rpool_source = get_pool_memory(PM_MESSAGE);
145 pm_strcpy(jcr->rpool_source, _("unknown source"));
152 init_jcr_job_record(jcr);
153 if (!get_or_create_client_record(jcr)) {
157 if (!db_create_job_record(jcr, jcr->db, &jcr->jr)) {
158 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
161 jcr->JobId = jcr->jr.JobId;
162 Dmsg4(100, "Created job record JobId=%d Name=%s Type=%c Level=%c\n",
163 jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel);
165 generate_daemon_event(jcr, "JobStart");
166 new_plugins(jcr); /* instantiate plugins for this jcr */
167 generate_plugin_event(jcr, bDirEventJobStart);
169 if (job_canceled(jcr)) {
173 if (jcr->JobReads() && !jcr->rstorage) {
174 if (jcr->job->storage) {
175 copy_rwstorage(jcr, jcr->job->storage, _("Job resource"));
177 copy_rwstorage(jcr, jcr->job->pool->storage, _("Pool resource"));
180 if (!jcr->JobReads()) {
185 * Now, do pre-run stuff, like setting job level (Inc/diff, ...)
186 * this allows us to setup a proper job start record for restarting
187 * in case of later errors.
189 switch (jcr->getJobType()) {
191 if (!do_backup_init(jcr)) {
192 backup_cleanup(jcr, JS_ErrorTerminated);
197 if (!do_verify_init(jcr)) {
198 verify_cleanup(jcr, JS_ErrorTerminated);
203 if (!do_restore_init(jcr)) {
204 restore_cleanup(jcr, JS_ErrorTerminated);
209 if (!do_admin_init(jcr)) {
210 admin_cleanup(jcr, JS_ErrorTerminated);
216 if (!do_mac_init(jcr)) {
217 mac_cleanup(jcr, JS_ErrorTerminated, JS_ErrorTerminated);
222 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->getJobType());
223 jcr->setJobStatus(JS_ErrorTerminated);
227 generate_plugin_event(jcr, bDirEventJobInit);
235 void update_job_end(JCR *jcr, int TermCode)
237 dequeue_messages(jcr); /* display any queued messages */
238 jcr->setJobStatus(TermCode);
239 update_job_end_record(jcr);
243 * This is the engine called by jobq.c:jobq_add() when we were pulled
244 * from the work queue.
245 * At this point, we are running in our own thread and all
246 * necessary resources are allocated -- see jobq.c
248 static void *job_thread(void *arg)
250 JCR *jcr = (JCR *)arg;
252 pthread_detach(pthread_self());
255 Dmsg0(200, "=====Start Job=========\n");
256 jcr->setJobStatus(JS_Running); /* this will be set only if no error */
257 jcr->start_time = time(NULL); /* set the real start time */
258 jcr->jr.StartTime = jcr->start_time;
260 if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
261 (utime_t)(jcr->start_time - jcr->sched_time)) {
262 jcr->setJobStatus(JS_Canceled);
263 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
266 if (job_check_maxrunschedtime(jcr)) {
267 jcr->setJobStatus(JS_Canceled);
268 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max run sched time exceeded.\n"));
271 /* TODO : check if it is used somewhere */
272 if (jcr->job->RunScripts == NULL) {
273 Dmsg0(200, "Warning, job->RunScripts is empty\n");
274 jcr->job->RunScripts = New(alist(10, not_owned_by_alist));
277 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
278 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
281 /* Run any script BeforeJob on dird */
282 run_scripts(jcr, jcr->job->RunScripts, "BeforeJob");
285 * We re-update the job start record so that the start
286 * time is set after the run before job. This avoids
287 * that any files created by the run before job will
288 * be saved twice. They will be backed up in the current
289 * job, but not in the next one unless they are changed.
290 * Without this, they will be backed up in this job and
291 * in the next job run because in that case, their date
292 * is after the start of this run.
294 jcr->start_time = time(NULL);
295 jcr->jr.StartTime = jcr->start_time;
296 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
297 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
299 generate_plugin_event(jcr, bDirEventJobRun);
301 switch (jcr->getJobType()) {
303 if (!job_canceled(jcr) && do_backup(jcr)) {
306 backup_cleanup(jcr, JS_ErrorTerminated);
310 if (!job_canceled(jcr) && do_verify(jcr)) {
313 verify_cleanup(jcr, JS_ErrorTerminated);
317 if (!job_canceled(jcr) && do_restore(jcr)) {
320 restore_cleanup(jcr, JS_ErrorTerminated);
324 if (!job_canceled(jcr) && do_admin(jcr)) {
327 admin_cleanup(jcr, JS_ErrorTerminated);
332 if (!job_canceled(jcr) && do_mac(jcr)) {
335 mac_cleanup(jcr, JS_ErrorTerminated, JS_ErrorTerminated);
339 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->getJobType());
343 run_scripts(jcr, jcr->job->RunScripts, "AfterJob");
345 /* Send off any queued messages */
346 if (jcr->msg_queue && jcr->msg_queue->size() > 0) {
347 dequeue_messages(jcr);
350 generate_daemon_event(jcr, "JobEnd");
351 generate_plugin_event(jcr, bDirEventJobEnd);
352 Dmsg1(50, "======== End Job stat=%c ==========\n", jcr->JobStatus);
357 void sd_msg_thread_send_signal(JCR *jcr, int sig)
360 if ( !jcr->sd_msg_thread_done
361 && jcr->SD_msg_chan_started
362 && !pthread_equal(jcr->SD_msg_chan, pthread_self()))
364 Dmsg1(800, "Send kill to SD msg chan jid=%d\n", jcr->JobId);
365 pthread_kill(jcr->SD_msg_chan, sig);
370 static bool cancel_file_daemon_job(UAContext *ua, const char *cmd, JCR *jcr)
375 Dmsg0(100, "No client to cancel\n");
378 old_client = ua->jcr->client;
379 ua->jcr->client = jcr->client;
380 if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
381 ua->error_msg(_("Failed to connect to File daemon.\n"));
382 ua->jcr->client = old_client;
385 Dmsg0(100, "Connected to file daemon\n");
386 BSOCK *fd = ua->jcr->file_bsock;
387 fd->fsend("%s Job=%s\n", cmd, jcr->Job);
388 while (fd->recv() >= 0) {
389 ua->send_msg("%s", fd->msg);
391 fd->signal(BNET_TERMINATE);
392 free_bsock(ua->jcr->file_bsock);
393 ua->jcr->client = old_client;
397 static bool cancel_sd_job(UAContext *ua, const char *cmd, JCR *jcr)
399 if (jcr->store_bsock) {
401 copy_wstorage(ua->jcr, jcr->rstorage, _("Job resource"));
403 copy_wstorage(ua->jcr, jcr->wstorage, _("Job resource"));
408 store.store = jcr->rstore;
410 store.store = jcr->wstore;
412 set_wstorage(ua->jcr, &store);
415 if (!ua->jcr->wstore) {
416 ua->error_msg(_("Failed to select Storage daemon.\n"));
420 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
421 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
424 Dmsg0(200, "Connected to storage daemon\n");
425 BSOCK *sd = ua->jcr->store_bsock;
426 sd->fsend("%s Job=%s\n", cmd, jcr->Job);
427 while (sd->recv() >= 0) {
428 ua->send_msg("%s", sd->msg);
430 sd->signal(BNET_TERMINATE);
431 free_bsock(ua->jcr->store_bsock);
435 /* The FD is not connected, so we try to complete JCR fields and send
436 * the cancel command.
438 static int cancel_inactive_job(UAContext *ua, JCR *jcr)
447 memset(&cr, 0, sizeof(cr));
449 /* User is kind enough to provide the client name */
450 if ((i = find_arg_with_value(ua, "client")) > 0) {
451 bstrncpy(cr.Name, ua->argv[i], sizeof(cr.Name));
454 memset(&jr, 0, sizeof(jr));
455 bstrncpy(jr.Job, jcr->Job, sizeof(jr.Job));
457 if (!open_client_db(ua)) {
460 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
463 cr.ClientId = jr.ClientId;
464 if (!cr.ClientId || !db_get_client_record(ua->jcr, ua->db, &cr)) {
469 if (acl_access_ok(ua, Client_ACL, cr.Name)) {
470 client = (CLIENT *)GetResWithName(R_CLIENT, cr.Name);
472 jcr->client = client;
474 Jmsg1(jcr, M_FATAL, 0, _("Client resource \"%s\" does not exist.\n"), cr.Name);
482 cancel_file_daemon_job(ua, "cancel", jcr);
484 /* At this time, we can't really guess the storage name from
487 store.store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
492 set_wstorage(ua->jcr, &store);
494 cancel_sd_job(ua, "cancel", jcr);
501 * Cancel a job -- typically called by the UA (Console program), but may also
502 * be called by the job watchdog.
504 * Returns: true if cancel appears to be successful
505 * false on failure. Message sent to ua->jcr.
507 bool cancel_job(UAContext *ua, JCR *jcr, bool cancel)
510 int32_t old_status = jcr->JobStatus;
512 const char *reason, *cmd;
513 bool force = find_arg(ua, "inactive") > 0;
514 JCR *wjcr = jcr->wjcr;
517 /* If the user explicitely ask, we can send the cancel command to
520 if (cancel && force) {
521 return cancel_inactive_job(ua, jcr);
524 status = JS_Canceled;
525 reason = _("canceled");
528 jcr->setJobStatus(status);
530 switch (old_status) {
533 case JS_WaitClientRes:
534 case JS_WaitStoreRes:
535 case JS_WaitPriority:
537 case JS_WaitStartTime:
539 ua->info_msg(_("JobId %s, Job %s marked to be %s.\n"),
540 edit_uint64(jcr->JobId, ed1), jcr->Job,
542 jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
547 /* Cancel File daemon */
548 if (jcr->file_bsock) {
549 /* do not return now, we want to try to cancel the sd */
550 cancel_file_daemon_job(ua, cmd, jcr);
553 /* We test file_bsock because the previous operation can take
556 if (jcr->file_bsock && cancel) {
557 jcr->file_bsock->set_terminated();
558 jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
561 /* Cancel Storage daemon */
562 if (jcr->store_bsock) {
563 /* do not return now, we want to try to cancel the sd socket */
564 cancel_sd_job(ua, cmd, jcr);
567 /* We test file_bsock because the previous operation can take
570 if (jcr->store_bsock && cancel) {
571 jcr->store_bsock->set_timed_out();
572 jcr->store_bsock->set_terminated();
573 sd_msg_thread_send_signal(jcr, TIMEOUT_SIGNAL);
574 jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
577 /* Cancel Copy/Migration Storage daemon */
578 if (wjcr && wjcr->store_bsock) {
579 /* do not return now, we want to try to cancel the sd socket */
580 cancel_sd_job(ua, cmd, wjcr);
582 /* We test file_bsock because the previous operation can take
585 if (wjcr->store_bsock && cancel) {
586 wjcr->store_bsock->set_timed_out();
587 wjcr->store_bsock->set_terminated();
588 sd_msg_thread_send_signal(wjcr, TIMEOUT_SIGNAL);
589 wjcr->my_thread_send_signal(TIMEOUT_SIGNAL);
598 void cancel_storage_daemon_job(JCR *jcr)
600 if (jcr->sd_canceled) {
601 return; /* cancel only once */
604 UAContext *ua = new_ua_context(jcr);
605 JCR *control_jcr = new_control_jcr("*JobCancel*", JT_SYSTEM);
608 ua->jcr = control_jcr;
609 if (jcr->store_bsock) {
610 if (!ua->jcr->wstorage) {
612 copy_wstorage(ua->jcr, jcr->rstorage, _("Job resource"));
614 copy_wstorage(ua->jcr, jcr->wstorage, _("Job resource"));
619 store.store = jcr->rstore;
621 store.store = jcr->wstore;
623 set_wstorage(ua->jcr, &store);
626 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
629 Dmsg0(200, "Connected to storage daemon\n");
630 sd = ua->jcr->store_bsock;
631 sd->fsend("cancel Job=%s\n", jcr->Job);
632 while (sd->recv() >= 0) {
634 sd->signal(BNET_TERMINATE);
635 free_bsock(ua->jcr->store_bsock);
636 jcr->sd_canceled = true;
637 jcr->store_bsock->set_timed_out();
638 jcr->store_bsock->set_terminated();
639 sd_msg_thread_send_signal(jcr, TIMEOUT_SIGNAL);
640 jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
643 free_jcr(control_jcr);
647 static void job_monitor_destructor(watchdog_t *self)
649 JCR *control_jcr = (JCR *)self->data;
651 free_jcr(control_jcr);
654 static void job_monitor_watchdog(watchdog_t *self)
656 JCR *control_jcr, *jcr;
658 control_jcr = (JCR *)self->data;
661 Dmsg1(800, "job_monitor_watchdog %p called\n", self);
666 if (jcr->JobId == 0 || job_canceled(jcr) || jcr->no_maxtime) {
667 Dmsg2(800, "Skipping JCR=%p Job=%s\n", jcr, jcr->Job);
671 /* check MaxWaitTime */
672 if (job_check_maxwaittime(jcr)) {
673 jcr->setJobStatus(JS_Canceled);
674 Qmsg(jcr, M_FATAL, 0, _("Max wait time exceeded. Job canceled.\n"));
676 /* check MaxRunTime */
677 } else if (job_check_maxruntime(jcr)) {
678 jcr->setJobStatus(JS_Canceled);
679 Qmsg(jcr, M_FATAL, 0, _("Max run time exceeded. Job canceled.\n"));
681 /* check MaxRunSchedTime */
682 } else if (job_check_maxrunschedtime(jcr)) {
683 jcr->setJobStatus(JS_Canceled);
684 Qmsg(jcr, M_FATAL, 0, _("Max run sched time exceeded. Job canceled.\n"));
689 Dmsg3(800, "Cancelling JCR %p jobid %d (%s)\n", jcr, jcr->JobId, jcr->Job);
690 UAContext *ua = new_ua_context(jcr);
691 ua->jcr = control_jcr;
694 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", jcr, jcr->JobId);
698 /* Keep reference counts correct */
703 * Check if the maxwaittime has expired and it is possible
706 static bool job_check_maxwaittime(JCR *jcr)
712 if (!job_waiting(jcr)) {
716 if (jcr->wait_time) {
717 current = watchdog_time - jcr->wait_time;
720 Dmsg2(200, "check maxwaittime %u >= %u\n",
721 current + jcr->wait_time_sum, job->MaxWaitTime);
722 if (job->MaxWaitTime != 0 &&
723 (current + jcr->wait_time_sum) >= job->MaxWaitTime) {
731 * Check if maxruntime has expired and if the job can be
734 static bool job_check_maxruntime(JCR *jcr)
740 if (job_canceled(jcr) || !jcr->job_started) {
743 if (jcr->job->MaxRunTime == 0 && job->FullMaxRunTime == 0 &&
744 job->IncMaxRunTime == 0 && job->DiffMaxRunTime == 0) {
747 run_time = watchdog_time - jcr->start_time;
748 Dmsg7(200, "check_maxruntime %llu-%u=%llu >= %llu|%llu|%llu|%llu\n",
749 watchdog_time, jcr->start_time, run_time, job->MaxRunTime, job->FullMaxRunTime,
750 job->IncMaxRunTime, job->DiffMaxRunTime);
752 if (jcr->getJobLevel() == L_FULL && job->FullMaxRunTime != 0 &&
753 run_time >= job->FullMaxRunTime) {
754 Dmsg0(200, "check_maxwaittime: FullMaxcancel\n");
756 } else if (jcr->getJobLevel() == L_DIFFERENTIAL && job->DiffMaxRunTime != 0 &&
757 run_time >= job->DiffMaxRunTime) {
758 Dmsg0(200, "check_maxwaittime: DiffMaxcancel\n");
760 } else if (jcr->getJobLevel() == L_INCREMENTAL && job->IncMaxRunTime != 0 &&
761 run_time >= job->IncMaxRunTime) {
762 Dmsg0(200, "check_maxwaittime: IncMaxcancel\n");
764 } else if (job->MaxRunTime > 0 && run_time >= job->MaxRunTime) {
765 Dmsg0(200, "check_maxwaittime: Maxcancel\n");
773 * Check if MaxRunSchedTime has expired and if the job can be
776 static bool job_check_maxrunschedtime(JCR *jcr)
778 if (jcr->MaxRunSchedTime == 0 || job_canceled(jcr)) {
781 if ((watchdog_time - jcr->initial_sched_time) < jcr->MaxRunSchedTime) {
782 Dmsg3(200, "Job %p (%s) with MaxRunSchedTime %d not expired\n",
783 jcr, jcr->Job, jcr->MaxRunSchedTime);
791 * Get or create a Pool record with the given name.
792 * Returns: 0 on error
795 DBId_t get_or_create_pool_record(JCR *jcr, char *pool_name)
799 memset(&pr, 0, sizeof(pr));
800 bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
801 Dmsg1(110, "get_or_create_pool=%s\n", pool_name);
803 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
804 /* Try to create the pool */
805 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
806 Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. ERR=%s"), pr.Name,
807 db_strerror(jcr->db));
810 Jmsg(jcr, M_INFO, 0, _("Created database record for Pool \"%s\".\n"), pr.Name);
817 * Check for duplicate jobs.
818 * Returns: true if current job should continue
819 * false if current job should terminate
821 bool allow_duplicate_job(JCR *jcr)
824 JCR *djcr; /* possible duplicate job */
825 bool cancel_dup = false;
826 bool cancel_me = false;
829 * See if AllowDuplicateJobs is set or
830 * if duplicate checking is disabled for this job.
832 if (job->AllowDuplicateJobs || jcr->IgnoreDuplicateJobChecking) {
836 Dmsg0(800, "Enter allow_duplicate_job\n");
839 * After this point, we do not want to allow any duplicate
844 if (jcr == djcr || djcr->JobId == 0) {
845 continue; /* do not cancel this job or consoles */
849 * See if this Job has the IgnoreDuplicateJobChecking flag set, ignore it
850 * for any checking against other jobs.
852 if (djcr->IgnoreDuplicateJobChecking) {
856 if (strcmp(job->name(), djcr->job->name()) == 0) {
857 if (job->DuplicateJobProximity > 0) {
858 utime_t now = (utime_t)time(NULL);
859 if ((now - djcr->start_time) > job->DuplicateJobProximity) {
860 continue; /* not really a duplicate */
863 if (job->CancelLowerLevelDuplicates &&
864 djcr->getJobType() == 'B' && jcr->getJobType() == 'B') {
865 switch (jcr->getJobLevel()) {
867 if (djcr->getJobLevel() == L_DIFFERENTIAL ||
868 djcr->getJobLevel() == L_INCREMENTAL) {
873 if (djcr->getJobLevel() == L_INCREMENTAL) {
876 if (djcr->getJobLevel() == L_FULL) {
881 if (djcr->getJobLevel() == L_FULL ||
882 djcr->getJobLevel() == L_DIFFERENTIAL) {
887 * cancel_dup will be done below
890 /* Zap current job */
891 jcr->setJobStatus(JS_Canceled);
892 Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"),
894 break; /* get out of foreach_jcr */
899 * Cancel one of the two jobs (me or dup)
900 * If CancelQueuedDuplicates is set do so only if job is queued.
902 if (job->CancelQueuedDuplicates) {
903 switch (djcr->JobStatus) {
906 case JS_WaitClientRes:
907 case JS_WaitStoreRes:
908 case JS_WaitPriority:
910 case JS_WaitStartTime:
912 cancel_dup = true; /* cancel queued duplicate */
919 if (cancel_dup || job->CancelRunningDuplicates) {
921 * Zap the duplicated job djcr
923 UAContext *ua = new_ua_context(jcr);
924 Jmsg(jcr, M_INFO, 0, _("Cancelling duplicate JobId=%d.\n"), djcr->JobId);
925 cancel_job(ua, djcr);
926 bmicrosleep(0, 500000);
927 djcr->setJobStatus(JS_Canceled);
928 cancel_job(ua, djcr);
930 Dmsg2(800, "Cancel dup %p JobId=%d\n", djcr, djcr->JobId);
935 jcr->setJobStatus(JS_Canceled);
936 Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"),
938 Dmsg2(800, "Cancel me %p JobId=%d\n", jcr, jcr->JobId);
940 Dmsg4(800, "curJobId=%d use_cnt=%d dupJobId=%d use_cnt=%d\n",
941 jcr->JobId, jcr->use_count(), djcr->JobId, djcr->use_count());
942 break; /* did our work, get out of foreach loop */
951 * Apply pool overrides to get the storage properly setup.
953 bool apply_wstorage_overrides(JCR *jcr, POOL *opool)
957 Dmsg1(100, "Original pool=%s\n", opool->name());
958 if (jcr->run_next_pool_override) {
959 pm_strcpy(jcr->next_pool_source, _("Run NextPool override"));
960 pm_strcpy(jcr->pool_source, _("Run NextPool override"));
961 source = _("Run NextPool override");
962 } else if (jcr->job->next_pool) {
963 /* Use Job Next Pool */
964 jcr->next_pool = jcr->job->next_pool;
965 pm_strcpy(jcr->next_pool_source, _("Job's NextPool resource"));
966 pm_strcpy(jcr->pool_source, _("Job's NextPool resource"));
967 source = _("Job's NextPool resource");
969 /* Default to original pool->NextPool */
970 jcr->next_pool = opool->NextPool;
971 Dmsg1(100, "next_pool=%p\n", jcr->next_pool);
972 if (jcr->next_pool) {
973 Dmsg1(100, "Original pool next Pool = %s\n", NPRT(jcr->next_pool->name()));
975 pm_strcpy(jcr->next_pool_source, _("Job Pool's NextPool resource"));
976 pm_strcpy(jcr->pool_source, _("Job Pool's NextPool resource"));
977 source = _("Pool's NextPool resource");
981 * If the original backup pool has a NextPool, make sure a
982 * record exists in the database.
984 if (jcr->next_pool) {
985 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->next_pool->name());
986 if (jcr->jr.PoolId == 0) {
991 if (!set_mac_wstorage(NULL, jcr, jcr->pool, jcr->next_pool, source)) {
995 /* Set write pool and source. Not read pool is in rpool. */
996 jcr->pool = jcr->next_pool;
997 pm_strcpy(jcr->pool_source, source);
1003 void apply_pool_overrides(JCR *jcr)
1005 bool pool_override = false;
1007 if (jcr->run_pool_override) {
1008 pm_strcpy(jcr->pool_source, _("Run Pool override"));
1011 * Apply any level related Pool selections
1013 switch (jcr->getJobLevel()) {
1015 if (jcr->full_pool) {
1016 jcr->pool = jcr->full_pool;
1017 pool_override = true;
1018 if (jcr->run_full_pool_override) {
1019 pm_strcpy(jcr->pool_source, _("Run FullPool override"));
1021 pm_strcpy(jcr->pool_source, _("Job FullPool override"));
1026 if (jcr->inc_pool) {
1027 jcr->pool = jcr->inc_pool;
1028 pool_override = true;
1029 if (jcr->run_inc_pool_override) {
1030 pm_strcpy(jcr->pool_source, _("Run IncPool override"));
1032 pm_strcpy(jcr->pool_source, _("Job IncPool override"));
1036 case L_DIFFERENTIAL:
1037 if (jcr->diff_pool) {
1038 jcr->pool = jcr->diff_pool;
1039 pool_override = true;
1040 if (jcr->run_diff_pool_override) {
1041 pm_strcpy(jcr->pool_source, _("Run DiffPool override"));
1043 pm_strcpy(jcr->pool_source, _("Job DiffPool override"));
1048 /* Update catalog if pool overridden */
1049 if (pool_override && jcr->pool->catalog) {
1050 jcr->catalog = jcr->pool->catalog;
1051 pm_strcpy(jcr->catalog_source, _("Pool resource"));
1057 * Get or create a Client record for this Job
1059 bool get_or_create_client_record(JCR *jcr)
1063 memset(&cr, 0, sizeof(cr));
1064 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
1065 cr.AutoPrune = jcr->client->AutoPrune;
1066 cr.FileRetention = jcr->client->FileRetention;
1067 cr.JobRetention = jcr->client->JobRetention;
1068 if (!jcr->client_name) {
1069 jcr->client_name = get_pool_memory(PM_NAME);
1071 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
1072 if (!db_create_client_record(jcr, jcr->db, &cr)) {
1073 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"),
1074 db_strerror(jcr->db));
1077 jcr->jr.ClientId = cr.ClientId;
1079 if (!jcr->client_uname) {
1080 jcr->client_uname = get_pool_memory(PM_NAME);
1082 pm_strcpy(jcr->client_uname, cr.Uname);
1084 Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name,
1090 * Get or Create FileSet record
1092 bool get_or_create_fileset_record(JCR *jcr)
1096 memset(&fsr, 0, sizeof(FILESET_DBR));
1097 bstrncpy(fsr.FileSet, jcr->fileset->hdr.name, sizeof(fsr.FileSet));
1098 if (jcr->fileset->have_MD5) {
1099 struct MD5Context md5c;
1100 unsigned char digest[MD5HashSize];
1101 memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
1102 MD5Final(digest, &md5c);
1104 * Keep the flag (last arg) set to false otherwise old FileSets will
1105 * get new MD5 sums and the user will get Full backups on everything
1107 bin_to_base64(fsr.MD5, sizeof(fsr.MD5), (char *)digest, MD5HashSize, false);
1108 bstrncpy(jcr->fileset->MD5, fsr.MD5, sizeof(jcr->fileset->MD5));
1110 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n"));
1112 if (!jcr->fileset->ignore_fs_changes ||
1113 !db_get_fileset_record(jcr, jcr->db, &fsr)) {
1114 if (!db_create_fileset_record(jcr, jcr->db, &fsr)) {
1115 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
1116 fsr.FileSet, db_strerror(jcr->db));
1120 jcr->jr.FileSetId = fsr.FileSetId;
1121 bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime));
1122 Dmsg2(119, "Created FileSet %s record %u\n", jcr->fileset->hdr.name,
1127 void init_jcr_job_record(JCR *jcr)
1129 jcr->jr.SchedTime = jcr->sched_time;
1130 jcr->jr.StartTime = jcr->start_time;
1131 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
1132 jcr->jr.JobType = jcr->getJobType();
1133 jcr->jr.JobLevel = jcr->getJobLevel();
1134 jcr->jr.JobStatus = jcr->JobStatus;
1135 jcr->jr.JobId = jcr->JobId;
1136 bstrncpy(jcr->jr.Name, jcr->job->name(), sizeof(jcr->jr.Name));
1137 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
1141 * Write status and such in DB
1143 void update_job_end_record(JCR *jcr)
1145 jcr->jr.EndTime = time(NULL);
1146 jcr->end_time = jcr->jr.EndTime;
1147 jcr->jr.JobId = jcr->JobId;
1148 jcr->jr.JobStatus = jcr->JobStatus;
1149 jcr->jr.JobFiles = jcr->JobFiles;
1150 jcr->jr.JobBytes = jcr->JobBytes;
1151 jcr->jr.ReadBytes = jcr->ReadBytes;
1152 jcr->jr.VolSessionId = jcr->VolSessionId;
1153 jcr->jr.VolSessionTime = jcr->VolSessionTime;
1154 jcr->jr.JobErrors = jcr->JobErrors;
1155 jcr->jr.HasBase = jcr->HasBase;
1156 if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
1157 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
1158 db_strerror(jcr->db));
1163 * Takes base_name and appends (unique) current
1164 * date and time to form unique job name.
1166 * Note, the seconds are actually a sequence number. This
1167 * permits us to start a maximum fo 59 unique jobs a second, which
1168 * should be sufficient.
1170 * Returns: unique job name in jcr->Job
1171 * date/time in jcr->start_time
1173 void create_unique_job_name(JCR *jcr, const char *base_name)
1175 /* Job start mutex */
1176 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1177 static time_t last_start_time = 0;
1179 time_t now = time(NULL);
1181 char dt[MAX_TIME_LENGTH];
1182 char name[MAX_NAME_LENGTH];
1186 /* Guarantee unique start time -- maximum one per second, and
1187 * thus unique Job Name
1189 P(mutex); /* lock creation of jobs */
1191 if (seq > 59) { /* wrap as if it is seconds */
1193 while (now == last_start_time) {
1194 bmicrosleep(0, 500000);
1198 last_start_time = now;
1199 V(mutex); /* allow creation of jobs */
1200 jcr->start_time = now;
1201 /* Form Unique JobName */
1202 (void)localtime_r(&now, &tm);
1203 /* Use only characters that are permitted in Windows filenames */
1204 strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
1205 len = strlen(dt) + 5; /* dt + .%02d EOS */
1206 bstrncpy(name, base_name, sizeof(name));
1207 name[sizeof(name)-len] = 0; /* truncate if too long */
1208 bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s_%02d", name, dt, seq); /* add date & time */
1209 /* Convert spaces into underscores */
1210 for (p=jcr->Job; *p; p++) {
1215 Dmsg2(100, "JobId=%u created Job=%s\n", jcr->JobId, jcr->Job);
1218 /* Called directly from job rescheduling */
1219 void dird_free_jcr_pointers(JCR *jcr)
1221 /* Close but do not free bsock packets */
1222 if (jcr->file_bsock) {
1223 Dmsg0(200, "Close File bsock\n");
1224 jcr->file_bsock->close();
1226 if (jcr->store_bsock) {
1227 Dmsg0(200, "Close Store bsock\n");
1228 jcr->store_bsock->close();
1231 bfree_and_null(jcr->sd_auth_key);
1232 bfree_and_null(jcr->where);
1233 bfree_and_null(jcr->RestoreBootstrap);
1234 jcr->cached_attribute = false;
1235 bfree_and_null(jcr->ar);
1237 free_and_null_pool_memory(jcr->JobIds);
1238 free_and_null_pool_memory(jcr->client_uname);
1239 free_and_null_pool_memory(jcr->attr);
1240 free_and_null_pool_memory(jcr->fname);
1241 free_and_null_pool_memory(jcr->media_type);
1245 * Free the Job Control Record if no one is still using it.
1246 * Called from main free_jcr() routine in src/lib/jcr.c so
1247 * that we can do our Director specific cleanup of the jcr.
1249 void dird_free_jcr(JCR *jcr)
1251 Dmsg0(200, "Start dird free_jcr\n");
1253 dird_free_jcr_pointers(jcr);
1254 /* Free bsock packets */
1255 free_bsock(jcr->file_bsock);
1256 free_bsock(jcr->store_bsock);
1257 if (jcr->term_wait_inited) {
1258 pthread_cond_destroy(&jcr->term_wait);
1259 jcr->term_wait_inited = false;
1261 if (jcr->db_batch) {
1262 db_close_database(jcr, jcr->db_batch);
1263 jcr->db_batch = NULL;
1264 jcr->batch_started = false;
1267 db_close_database(jcr, jcr->db);
1271 free_and_null_pool_memory(jcr->stime);
1272 free_and_null_pool_memory(jcr->fname);
1273 free_and_null_pool_memory(jcr->pool_source);
1274 free_and_null_pool_memory(jcr->next_pool_source);
1275 free_and_null_pool_memory(jcr->catalog_source);
1276 free_and_null_pool_memory(jcr->rpool_source);
1277 free_and_null_pool_memory(jcr->wstore_source);
1278 free_and_null_pool_memory(jcr->rstore_source);
1280 /* Delete lists setup to hold storage pointers */
1281 free_rwstorage(jcr);
1283 jcr->job_end_push.destroy();
1285 if (jcr->JobId != 0) {
1286 write_state_file(director->working_directory, "bacula-dir", get_first_port_host_order(director->DIRaddrs));
1289 free_plugins(jcr); /* release instantiated plugins */
1291 Dmsg0(200, "End dird free_jcr\n");
1295 * The Job storage definition must be either in the Job record
1296 * or in the Pool record. The Pool record overrides the Job
1299 void get_job_storage(USTORE *store, JOB *job, RUN *run)
1301 if (run && run->pool && run->pool->storage) {
1302 store->store = (STORE *)run->pool->storage->first();
1303 pm_strcpy(store->store_source, _("Run pool override"));
1306 if (run && run->storage) {
1307 store->store = run->storage;
1308 pm_strcpy(store->store_source, _("Run storage override"));
1311 if (job->pool->storage) {
1312 store->store = (STORE *)job->pool->storage->first();
1313 pm_strcpy(store->store_source, _("Pool resource"));
1315 store->store = (STORE *)job->storage->first();
1316 pm_strcpy(store->store_source, _("Job resource"));
1321 * Set some defaults in the JCR necessary to
1322 * run. These items are pulled from the job
1323 * definition as defaults, but can be overridden
1324 * later either by the Run record in the Schedule resource,
1325 * or by the Console program.
1327 void set_jcr_defaults(JCR *jcr, JOB *job)
1330 jcr->setJobType(job->JobType);
1331 jcr->JobStatus = JS_Created;
1333 switch (jcr->getJobType()) {
1335 jcr->setJobLevel(L_NONE);
1338 jcr->setJobLevel(job->JobLevel);
1343 jcr->fname = get_pool_memory(PM_FNAME);
1345 if (!jcr->pool_source) {
1346 jcr->pool_source = get_pool_memory(PM_MESSAGE);
1348 if (!jcr->next_pool_source) {
1349 jcr->next_pool_source = get_pool_memory(PM_MESSAGE);
1351 if (!jcr->catalog_source) {
1352 jcr->catalog_source = get_pool_memory(PM_MESSAGE);
1355 jcr->JobPriority = job->Priority;
1356 /* Copy storage definitions -- deleted in dir_free_jcr above */
1358 copy_rwstorage(jcr, job->storage, _("Job resource"));
1360 copy_rwstorage(jcr, job->pool->storage, _("Pool resource"));
1362 jcr->client = job->client;
1363 ASSERT2(jcr->client, "jcr->client==NULL!!!");
1364 if (!jcr->client_name) {
1365 jcr->client_name = get_pool_memory(PM_NAME);
1367 pm_strcpy(jcr->client_name, jcr->client->name());
1368 jcr->pool = job->pool;
1369 pm_strcpy(jcr->pool_source, _("Job resource"));
1370 if (job->next_pool) {
1371 /* Use Job's Next Pool */
1372 jcr->next_pool = job->next_pool;
1373 pm_strcpy(jcr->next_pool_source, _("Job's NextPool resource"));
1375 /* Default to original pool->NextPool */
1376 jcr->next_pool = job->pool->NextPool;
1377 pm_strcpy(jcr->next_pool_source, _("Job Pool's NextPool resource"));
1379 jcr->full_pool = job->full_pool;
1380 jcr->inc_pool = job->inc_pool;
1381 jcr->diff_pool = job->diff_pool;
1382 if (job->pool->catalog) {
1383 jcr->catalog = job->pool->catalog;
1384 pm_strcpy(jcr->catalog_source, _("Pool resource"));
1386 jcr->catalog = job->client->catalog;
1387 pm_strcpy(jcr->catalog_source, _("Client resource"));
1389 jcr->fileset = job->fileset;
1390 jcr->accurate = job->accurate;
1391 jcr->messages = job->messages;
1392 jcr->spool_data = job->spool_data;
1393 jcr->spool_size = job->spool_size;
1394 jcr->write_part_after_job = job->write_part_after_job;
1395 jcr->IgnoreDuplicateJobChecking = job->IgnoreDuplicateJobChecking;
1396 jcr->MaxRunSchedTime = job->MaxRunSchedTime;
1397 if (jcr->RestoreBootstrap) {
1398 free(jcr->RestoreBootstrap);
1399 jcr->RestoreBootstrap = NULL;
1401 /* This can be overridden by Console program */
1402 if (job->RestoreBootstrap) {
1403 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
1405 /* This can be overridden by Console program */
1406 jcr->verify_job = job->verify_job;
1407 /* If no default level given, set one */
1408 if (jcr->getJobLevel() == 0) {
1409 switch (jcr->getJobType()) {
1411 jcr->setJobLevel(L_VERIFY_CATALOG);
1414 jcr->setJobLevel(L_INCREMENTAL);
1418 jcr->setJobLevel(L_NONE);
1421 jcr->setJobLevel(L_FULL);
1428 * Copy the storage definitions from an alist to the JCR
1430 void copy_rwstorage(JCR *jcr, alist *storage, const char *where)
1432 if (jcr->JobReads()) {
1433 copy_rstorage(jcr, storage, where);
1435 copy_wstorage(jcr, storage, where);
1439 /* Set storage override. Releases any previous storage definition */
1440 void set_rwstorage(JCR *jcr, USTORE *store)
1443 Jmsg(jcr, M_FATAL, 0, _("No storage specified.\n"));
1446 if (jcr->JobReads()) {
1447 set_rstorage(jcr, store);
1449 set_wstorage(jcr, store);
1452 void free_rwstorage(JCR *jcr)
1459 * Copy the storage definitions from an alist to the JCR
1461 void copy_rstorage(JCR *jcr, alist *storage, const char *where)
1465 if (jcr->rstorage) {
1466 delete jcr->rstorage;
1468 jcr->rstorage = New(alist(10, not_owned_by_alist));
1469 foreach_alist(st, storage) {
1470 jcr->rstorage->append(st);
1472 if (!jcr->rstore_source) {
1473 jcr->rstore_source = get_pool_memory(PM_MESSAGE);
1475 pm_strcpy(jcr->rstore_source, where);
1476 if (jcr->rstorage) {
1477 jcr->rstore = (STORE *)jcr->rstorage->first();
1483 /* Set storage override. Remove all previous storage */
1484 void set_rstorage(JCR *jcr, USTORE *store)
1488 if (!store->store) {
1491 if (jcr->rstorage) {
1494 if (!jcr->rstorage) {
1495 jcr->rstorage = New(alist(10, not_owned_by_alist));
1497 jcr->rstore = store->store;
1498 if (!jcr->rstore_source) {
1499 jcr->rstore_source = get_pool_memory(PM_MESSAGE);
1501 pm_strcpy(jcr->rstore_source, store->store_source);
1502 foreach_alist(storage, jcr->rstorage) {
1503 if (store->store == storage) {
1507 /* Store not in list, so add it */
1508 jcr->rstorage->prepend(store->store);
1511 void free_rstorage(JCR *jcr)
1513 if (jcr->rstorage) {
1514 delete jcr->rstorage;
1515 jcr->rstorage = NULL;
1521 * Copy the storage definitions from an alist to the JCR
1523 void copy_wstorage(JCR *jcr, alist *storage, const char *where)
1527 if (jcr->wstorage) {
1528 delete jcr->wstorage;
1530 jcr->wstorage = New(alist(10, not_owned_by_alist));
1531 foreach_alist(st, storage) {
1532 Dmsg1(100, "wstorage=%s\n", st->name());
1533 jcr->wstorage->append(st);
1535 if (!jcr->wstore_source) {
1536 jcr->wstore_source = get_pool_memory(PM_MESSAGE);
1538 pm_strcpy(jcr->wstore_source, where);
1539 if (jcr->wstorage) {
1540 jcr->wstore = (STORE *)jcr->wstorage->first();
1541 Dmsg2(100, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
1547 /* Set storage override. Remove all previous storage */
1548 void set_wstorage(JCR *jcr, USTORE *store)
1552 if (!store->store) {
1555 if (jcr->wstorage) {
1558 if (!jcr->wstorage) {
1559 jcr->wstorage = New(alist(10, not_owned_by_alist));
1561 jcr->wstore = store->store;
1562 if (!jcr->wstore_source) {
1563 jcr->wstore_source = get_pool_memory(PM_MESSAGE);
1565 pm_strcpy(jcr->wstore_source, store->store_source);
1566 Dmsg2(50, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
1567 foreach_alist(storage, jcr->wstorage) {
1568 if (store->store == storage) {
1572 /* Store not in list, so add it */
1573 jcr->wstorage->prepend(store->store);
1576 void free_wstorage(JCR *jcr)
1578 if (jcr->wstorage) {
1579 delete jcr->wstorage;
1580 jcr->wstorage = NULL;
1585 void create_clones(JCR *jcr)
1588 * Fire off any clone jobs (run directives)
1590 Dmsg2(900, "cloned=%d run_cmds=%p\n", jcr->cloned, jcr->job->run_cmds);
1591 if (!jcr->cloned && jcr->job->run_cmds) {
1593 JOB *job = jcr->job;
1594 POOLMEM *cmd = get_pool_memory(PM_FNAME);
1595 UAContext *ua = new_ua_context(jcr);
1597 foreach_alist(runcmd, job->run_cmds) {
1598 cmd = edit_job_codes(jcr, cmd, runcmd, "", job_code_callback_director);
1599 Mmsg(ua->cmd, "run %s cloned=yes", cmd);
1600 Dmsg1(900, "=============== Clone cmd=%s\n", ua->cmd);
1601 parse_ua_args(ua); /* parse command */
1602 int stat = run_cmd(ua, ua->cmd);
1604 Jmsg(jcr, M_ERROR, 0, _("Could not start clone job: \"%s\".\n"),
1607 Jmsg(jcr, M_INFO, 0, _("Clone JobId %d started.\n"), stat);
1610 free_ua_context(ua);
1611 free_pool_memory(cmd);
1616 * Given: a JobId in jcr->previous_jr.JobId,
1617 * this subroutine writes a bsr file to restore that job.
1618 * Returns: -1 on error
1619 * number of files if OK
1621 int create_restore_bootstrap_file(JCR *jcr)
1627 memset(&rx, 0, sizeof(rx));
1629 rx.JobIds = (char *)"";
1630 rx.bsr->JobId = jcr->previous_jr.JobId;
1631 ua = new_ua_context(jcr);
1632 if (!complete_bsr(ua, rx.bsr)) {
1636 rx.bsr->fi = new_findex();
1637 rx.bsr->fi->findex = 1;
1638 rx.bsr->fi->findex2 = jcr->previous_jr.JobFiles;
1639 jcr->ExpectedFiles = write_bsr_file(ua, rx);
1640 if (jcr->ExpectedFiles == 0) {
1644 free_ua_context(ua);
1646 jcr->needs_sd = true;
1647 return jcr->ExpectedFiles;
1650 free_ua_context(ua);
1655 /* TODO: redirect command ouput to job log */
1656 bool run_console_command(JCR *jcr, const char *cmd)
1660 JCR *ljcr = new_control_jcr("-RunScript-", JT_CONSOLE);
1661 ua = new_ua_context(ljcr);
1662 /* run from runscript and check if commands are authorized */
1663 ua->runscript = true;
1664 Mmsg(ua->cmd, "%s", cmd);
1665 Dmsg1(100, "Console command: %s\n", ua->cmd);
1667 if (ua->argc > 0 && ua->argk[0][0] == '.') {
1668 ok = do_a_dot_command(ua);
1670 ok = do_a_command(ua);
1672 free_ua_context(ua);