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 int cancel_file_daemon_job(UAContext *ua, const char *cmd, JCR *jcr)
373 Dmsg0(100, "No client to cancel\n");
376 ua->jcr->client = jcr->client;
377 if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
378 ua->error_msg(_("Failed to connect to File daemon.\n"));
381 Dmsg0(100, "Connected to file daemon\n");
382 BSOCK *fd = ua->jcr->file_bsock;
383 fd->fsend("%s Job=%s\n", cmd, jcr->Job);
384 while (fd->recv() >= 0) {
385 ua->send_msg("%s", fd->msg);
387 fd->signal(BNET_TERMINATE);
388 free_bsock(ua->jcr->file_bsock);
389 ua->jcr->client = NULL;
393 static bool cancel_sd_job(UAContext *ua, const char *cmd, JCR *jcr)
395 if (jcr->store_bsock) {
397 copy_wstorage(ua->jcr, jcr->rstorage, _("Job resource"));
399 copy_wstorage(ua->jcr, jcr->wstorage, _("Job resource"));
404 store.store = jcr->rstore;
406 store.store = jcr->wstore;
408 set_wstorage(ua->jcr, &store);
411 if (!ua->jcr->wstore) {
412 ua->error_msg(_("Failed to select Storage daemon.\n"));
416 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
417 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
420 Dmsg0(200, "Connected to storage daemon\n");
421 BSOCK *sd = ua->jcr->store_bsock;
422 sd->fsend("%s Job=%s\n", cmd, jcr->Job);
423 while (sd->recv() >= 0) {
424 ua->send_msg("%s", sd->msg);
426 sd->signal(BNET_TERMINATE);
427 free_bsock(ua->jcr->store_bsock);
431 /* The FD is not connected, so we try to complete JCR fields and send
432 * the cancel command.
434 static int cancel_inactive_job(UAContext *ua, JCR *jcr)
443 memset(&cr, 0, sizeof(cr));
445 /* User is kind enough to provide the client name */
446 if ((i = find_arg_with_value(ua, "client")) > 0) {
447 bstrncpy(cr.Name, ua->argv[i], sizeof(cr.Name));
450 memset(&jr, 0, sizeof(jr));
451 bstrncpy(jr.Job, jcr->Job, sizeof(jr.Job));
453 if (!open_client_db(ua)) {
456 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
459 cr.ClientId = jr.ClientId;
460 if (!cr.ClientId || !db_get_client_record(ua->jcr, ua->db, &cr)) {
465 if (acl_access_ok(ua, Client_ACL, cr.Name)) {
466 client = (CLIENT *)GetResWithName(R_CLIENT, cr.Name);
468 jcr->client = client;
470 Jmsg1(jcr, M_FATAL, 0, _("Client resource \"%s\" does not exist.\n"), cr.Name);
478 cancel_file_daemon_job(ua, "cancel", jcr);
480 /* At this time, we can't really guess the storage name from
483 store.store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
488 set_wstorage(ua->jcr, &store);
490 cancel_sd_job(ua, "cancel", jcr);
497 * Cancel a job -- typically called by the UA (Console program), but may also
498 * be called by the job watchdog.
500 * Returns: true if cancel appears to be successful
501 * false on failure. Message sent to ua->jcr.
503 bool cancel_job(UAContext *ua, JCR *jcr, bool cancel)
506 int32_t old_status = jcr->JobStatus;
508 const char *reason, *cmd;
509 bool force = find_arg(ua, "inactive") > 0;
510 JCR *wjcr = jcr->wjcr;
513 /* If the user explicitely ask, we can send the cancel command to
516 if (cancel && force) {
517 return cancel_inactive_job(ua, jcr);
520 status = JS_Canceled;
521 reason = _("canceled");
524 jcr->setJobStatus(status);
526 switch (old_status) {
529 case JS_WaitClientRes:
530 case JS_WaitStoreRes:
531 case JS_WaitPriority:
533 case JS_WaitStartTime:
535 ua->info_msg(_("JobId %s, Job %s marked to be %s.\n"),
536 edit_uint64(jcr->JobId, ed1), jcr->Job,
538 jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
543 /* Cancel File daemon */
544 if (jcr->file_bsock) {
545 /* do not return now, we want to try to cancel the sd */
546 cancel_file_daemon_job(ua, cmd, jcr);
549 /* We test file_bsock because the previous operation can take
552 if (jcr->file_bsock && cancel) {
553 jcr->file_bsock->set_terminated();
554 jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
557 /* Cancel Storage daemon */
558 if (jcr->store_bsock) {
559 /* do not return now, we want to try to cancel the sd socket */
560 cancel_sd_job(ua, cmd, jcr);
563 /* We test file_bsock because the previous operation can take
566 if (jcr->store_bsock && cancel) {
567 jcr->store_bsock->set_timed_out();
568 jcr->store_bsock->set_terminated();
569 sd_msg_thread_send_signal(jcr, TIMEOUT_SIGNAL);
570 jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
573 /* Cancel Copy/Migration Storage daemon */
574 if (wjcr && wjcr->store_bsock) {
575 /* do not return now, we want to try to cancel the sd socket */
576 cancel_sd_job(ua, cmd, wjcr);
578 /* We test file_bsock because the previous operation can take
581 if (wjcr->store_bsock && cancel) {
582 wjcr->store_bsock->set_timed_out();
583 wjcr->store_bsock->set_terminated();
584 sd_msg_thread_send_signal(wjcr, TIMEOUT_SIGNAL);
585 wjcr->my_thread_send_signal(TIMEOUT_SIGNAL);
594 void cancel_storage_daemon_job(JCR *jcr)
596 if (jcr->sd_canceled) {
597 return; /* cancel only once */
600 UAContext *ua = new_ua_context(jcr);
601 JCR *control_jcr = new_control_jcr("*JobCancel*", JT_SYSTEM);
604 ua->jcr = control_jcr;
605 if (jcr->store_bsock) {
606 if (!ua->jcr->wstorage) {
608 copy_wstorage(ua->jcr, jcr->rstorage, _("Job resource"));
610 copy_wstorage(ua->jcr, jcr->wstorage, _("Job resource"));
615 store.store = jcr->rstore;
617 store.store = jcr->wstore;
619 set_wstorage(ua->jcr, &store);
622 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
625 Dmsg0(200, "Connected to storage daemon\n");
626 sd = ua->jcr->store_bsock;
627 sd->fsend("cancel Job=%s\n", jcr->Job);
628 while (sd->recv() >= 0) {
630 sd->signal(BNET_TERMINATE);
631 free_bsock(ua->jcr->store_bsock);
632 jcr->sd_canceled = true;
633 jcr->store_bsock->set_timed_out();
634 jcr->store_bsock->set_terminated();
635 sd_msg_thread_send_signal(jcr, TIMEOUT_SIGNAL);
636 jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
639 free_jcr(control_jcr);
643 static void job_monitor_destructor(watchdog_t *self)
645 JCR *control_jcr = (JCR *)self->data;
647 free_jcr(control_jcr);
650 static void job_monitor_watchdog(watchdog_t *self)
652 JCR *control_jcr, *jcr;
654 control_jcr = (JCR *)self->data;
657 Dmsg1(800, "job_monitor_watchdog %p called\n", self);
662 if (jcr->JobId == 0 || job_canceled(jcr) || jcr->no_maxtime) {
663 Dmsg2(800, "Skipping JCR=%p Job=%s\n", jcr, jcr->Job);
667 /* check MaxWaitTime */
668 if (job_check_maxwaittime(jcr)) {
669 jcr->setJobStatus(JS_Canceled);
670 Qmsg(jcr, M_FATAL, 0, _("Max wait time exceeded. Job canceled.\n"));
672 /* check MaxRunTime */
673 } else if (job_check_maxruntime(jcr)) {
674 jcr->setJobStatus(JS_Canceled);
675 Qmsg(jcr, M_FATAL, 0, _("Max run time exceeded. Job canceled.\n"));
677 /* check MaxRunSchedTime */
678 } else if (job_check_maxrunschedtime(jcr)) {
679 jcr->setJobStatus(JS_Canceled);
680 Qmsg(jcr, M_FATAL, 0, _("Max run sched time exceeded. Job canceled.\n"));
685 Dmsg3(800, "Cancelling JCR %p jobid %d (%s)\n", jcr, jcr->JobId, jcr->Job);
686 UAContext *ua = new_ua_context(jcr);
687 ua->jcr = control_jcr;
690 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", jcr, jcr->JobId);
694 /* Keep reference counts correct */
699 * Check if the maxwaittime has expired and it is possible
702 static bool job_check_maxwaittime(JCR *jcr)
708 if (!job_waiting(jcr)) {
712 if (jcr->wait_time) {
713 current = watchdog_time - jcr->wait_time;
716 Dmsg2(200, "check maxwaittime %u >= %u\n",
717 current + jcr->wait_time_sum, job->MaxWaitTime);
718 if (job->MaxWaitTime != 0 &&
719 (current + jcr->wait_time_sum) >= job->MaxWaitTime) {
727 * Check if maxruntime has expired and if the job can be
730 static bool job_check_maxruntime(JCR *jcr)
736 if (job_canceled(jcr) || !jcr->job_started) {
739 if (jcr->job->MaxRunTime == 0 && job->FullMaxRunTime == 0 &&
740 job->IncMaxRunTime == 0 && job->DiffMaxRunTime == 0) {
743 run_time = watchdog_time - jcr->start_time;
744 Dmsg7(200, "check_maxruntime %llu-%u=%llu >= %llu|%llu|%llu|%llu\n",
745 watchdog_time, jcr->start_time, run_time, job->MaxRunTime, job->FullMaxRunTime,
746 job->IncMaxRunTime, job->DiffMaxRunTime);
748 if (jcr->getJobLevel() == L_FULL && job->FullMaxRunTime != 0 &&
749 run_time >= job->FullMaxRunTime) {
750 Dmsg0(200, "check_maxwaittime: FullMaxcancel\n");
752 } else if (jcr->getJobLevel() == L_DIFFERENTIAL && job->DiffMaxRunTime != 0 &&
753 run_time >= job->DiffMaxRunTime) {
754 Dmsg0(200, "check_maxwaittime: DiffMaxcancel\n");
756 } else if (jcr->getJobLevel() == L_INCREMENTAL && job->IncMaxRunTime != 0 &&
757 run_time >= job->IncMaxRunTime) {
758 Dmsg0(200, "check_maxwaittime: IncMaxcancel\n");
760 } else if (job->MaxRunTime > 0 && run_time >= job->MaxRunTime) {
761 Dmsg0(200, "check_maxwaittime: Maxcancel\n");
769 * Check if MaxRunSchedTime has expired and if the job can be
772 static bool job_check_maxrunschedtime(JCR *jcr)
774 if (jcr->MaxRunSchedTime == 0 || job_canceled(jcr)) {
777 if ((watchdog_time - jcr->initial_sched_time) < jcr->MaxRunSchedTime) {
778 Dmsg3(200, "Job %p (%s) with MaxRunSchedTime %d not expired\n",
779 jcr, jcr->Job, jcr->MaxRunSchedTime);
787 * Get or create a Pool record with the given name.
788 * Returns: 0 on error
791 DBId_t get_or_create_pool_record(JCR *jcr, char *pool_name)
795 memset(&pr, 0, sizeof(pr));
796 bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
797 Dmsg1(110, "get_or_create_pool=%s\n", pool_name);
799 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
800 /* Try to create the pool */
801 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
802 Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. ERR=%s"), pr.Name,
803 db_strerror(jcr->db));
806 Jmsg(jcr, M_INFO, 0, _("Created database record for Pool \"%s\".\n"), pr.Name);
813 * Check for duplicate jobs.
814 * Returns: true if current job should continue
815 * false if current job should terminate
817 bool allow_duplicate_job(JCR *jcr)
820 JCR *djcr; /* possible duplicate job */
821 bool cancel_dup = false;
822 bool cancel_me = false;
825 * See if AllowDuplicateJobs is set or
826 * if duplicate checking is disabled for this job.
828 if (job->AllowDuplicateJobs || jcr->IgnoreDuplicateJobChecking) {
832 Dmsg0(800, "Enter allow_duplicate_job\n");
835 * After this point, we do not want to allow any duplicate
840 if (jcr == djcr || djcr->JobId == 0) {
841 continue; /* do not cancel this job or consoles */
845 * See if this Job has the IgnoreDuplicateJobChecking flag set, ignore it
846 * for any checking against other jobs.
848 if (djcr->IgnoreDuplicateJobChecking) {
852 if (strcmp(job->name(), djcr->job->name()) == 0) {
853 if (job->DuplicateJobProximity > 0) {
854 utime_t now = (utime_t)time(NULL);
855 if ((now - djcr->start_time) > job->DuplicateJobProximity) {
856 continue; /* not really a duplicate */
859 if (job->CancelLowerLevelDuplicates &&
860 djcr->getJobType() == 'B' && jcr->getJobType() == 'B') {
861 switch (jcr->getJobLevel()) {
863 if (djcr->getJobLevel() == L_DIFFERENTIAL ||
864 djcr->getJobLevel() == L_INCREMENTAL) {
869 if (djcr->getJobLevel() == L_INCREMENTAL) {
872 if (djcr->getJobLevel() == L_FULL) {
877 if (djcr->getJobLevel() == L_FULL ||
878 djcr->getJobLevel() == L_DIFFERENTIAL) {
883 * cancel_dup will be done below
886 /* Zap current job */
887 jcr->setJobStatus(JS_Canceled);
888 Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"),
890 break; /* get out of foreach_jcr */
895 * Cancel one of the two jobs (me or dup)
896 * If CancelQueuedDuplicates is set do so only if job is queued.
898 if (job->CancelQueuedDuplicates) {
899 switch (djcr->JobStatus) {
902 case JS_WaitClientRes:
903 case JS_WaitStoreRes:
904 case JS_WaitPriority:
906 case JS_WaitStartTime:
908 cancel_dup = true; /* cancel queued duplicate */
915 if (cancel_dup || job->CancelRunningDuplicates) {
917 * Zap the duplicated job djcr
919 UAContext *ua = new_ua_context(jcr);
920 Jmsg(jcr, M_INFO, 0, _("Cancelling duplicate JobId=%d.\n"), djcr->JobId);
921 cancel_job(ua, djcr);
922 bmicrosleep(0, 500000);
923 djcr->setJobStatus(JS_Canceled);
924 cancel_job(ua, djcr);
926 Dmsg2(800, "Cancel dup %p JobId=%d\n", djcr, djcr->JobId);
931 jcr->setJobStatus(JS_Canceled);
932 Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"),
934 Dmsg2(800, "Cancel me %p JobId=%d\n", jcr, jcr->JobId);
936 Dmsg4(800, "curJobId=%d use_cnt=%d dupJobId=%d use_cnt=%d\n",
937 jcr->JobId, jcr->use_count(), djcr->JobId, djcr->use_count());
938 break; /* did our work, get out of foreach loop */
947 * Apply pool overrides to get the storage properly setup.
949 bool apply_wstorage_overrides(JCR *jcr, POOL *opool)
953 Dmsg1(100, "Original pool=%s\n", opool->name());
954 if (jcr->run_next_pool_override) {
955 pm_strcpy(jcr->next_pool_source, _("Run NextPool override"));
956 pm_strcpy(jcr->pool_source, _("Run NextPool override"));
957 source = _("Run NextPool override");
958 } else if (jcr->job->next_pool) {
959 /* Use Job Next Pool */
960 jcr->next_pool = jcr->job->next_pool;
961 pm_strcpy(jcr->next_pool_source, _("Job's NextPool resource"));
962 pm_strcpy(jcr->pool_source, _("Job's NextPool resource"));
963 source = _("Job's NextPool resource");
965 /* Default to original pool->NextPool */
966 jcr->next_pool = opool->NextPool;
967 Dmsg1(100, "next_pool=%p\n", jcr->next_pool);
968 if (jcr->next_pool) {
969 Dmsg1(100, "Original pool next Pool = %s\n", NPRT(jcr->next_pool->name()));
971 pm_strcpy(jcr->next_pool_source, _("Job Pool's NextPool resource"));
972 pm_strcpy(jcr->pool_source, _("Job Pool's NextPool resource"));
973 source = _("Pool's NextPool resource");
977 * If the original backup pool has a NextPool, make sure a
978 * record exists in the database.
980 if (jcr->next_pool) {
981 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->next_pool->name());
982 if (jcr->jr.PoolId == 0) {
987 if (!set_mac_wstorage(NULL, jcr, jcr->pool, jcr->next_pool, source)) {
991 /* Set write pool and source. Not read pool is in rpool. */
992 jcr->pool = jcr->next_pool;
993 pm_strcpy(jcr->pool_source, source);
999 void apply_pool_overrides(JCR *jcr)
1001 bool pool_override = false;
1003 if (jcr->run_pool_override) {
1004 pm_strcpy(jcr->pool_source, _("Run Pool override"));
1007 * Apply any level related Pool selections
1009 switch (jcr->getJobLevel()) {
1011 if (jcr->full_pool) {
1012 jcr->pool = jcr->full_pool;
1013 pool_override = true;
1014 if (jcr->run_full_pool_override) {
1015 pm_strcpy(jcr->pool_source, _("Run FullPool override"));
1017 pm_strcpy(jcr->pool_source, _("Job FullPool override"));
1022 if (jcr->inc_pool) {
1023 jcr->pool = jcr->inc_pool;
1024 pool_override = true;
1025 if (jcr->run_inc_pool_override) {
1026 pm_strcpy(jcr->pool_source, _("Run IncPool override"));
1028 pm_strcpy(jcr->pool_source, _("Job IncPool override"));
1032 case L_DIFFERENTIAL:
1033 if (jcr->diff_pool) {
1034 jcr->pool = jcr->diff_pool;
1035 pool_override = true;
1036 if (jcr->run_diff_pool_override) {
1037 pm_strcpy(jcr->pool_source, _("Run DiffPool override"));
1039 pm_strcpy(jcr->pool_source, _("Job DiffPool override"));
1044 /* Update catalog if pool overridden */
1045 if (pool_override && jcr->pool->catalog) {
1046 jcr->catalog = jcr->pool->catalog;
1047 pm_strcpy(jcr->catalog_source, _("Pool resource"));
1053 * Get or create a Client record for this Job
1055 bool get_or_create_client_record(JCR *jcr)
1059 memset(&cr, 0, sizeof(cr));
1060 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
1061 cr.AutoPrune = jcr->client->AutoPrune;
1062 cr.FileRetention = jcr->client->FileRetention;
1063 cr.JobRetention = jcr->client->JobRetention;
1064 if (!jcr->client_name) {
1065 jcr->client_name = get_pool_memory(PM_NAME);
1067 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
1068 if (!db_create_client_record(jcr, jcr->db, &cr)) {
1069 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"),
1070 db_strerror(jcr->db));
1073 jcr->jr.ClientId = cr.ClientId;
1075 if (!jcr->client_uname) {
1076 jcr->client_uname = get_pool_memory(PM_NAME);
1078 pm_strcpy(jcr->client_uname, cr.Uname);
1080 Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name,
1086 * Get or Create FileSet record
1088 bool get_or_create_fileset_record(JCR *jcr)
1092 memset(&fsr, 0, sizeof(FILESET_DBR));
1093 bstrncpy(fsr.FileSet, jcr->fileset->hdr.name, sizeof(fsr.FileSet));
1094 if (jcr->fileset->have_MD5) {
1095 struct MD5Context md5c;
1096 unsigned char digest[MD5HashSize];
1097 memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
1098 MD5Final(digest, &md5c);
1100 * Keep the flag (last arg) set to false otherwise old FileSets will
1101 * get new MD5 sums and the user will get Full backups on everything
1103 bin_to_base64(fsr.MD5, sizeof(fsr.MD5), (char *)digest, MD5HashSize, false);
1104 bstrncpy(jcr->fileset->MD5, fsr.MD5, sizeof(jcr->fileset->MD5));
1106 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n"));
1108 if (!jcr->fileset->ignore_fs_changes ||
1109 !db_get_fileset_record(jcr, jcr->db, &fsr)) {
1110 if (!db_create_fileset_record(jcr, jcr->db, &fsr)) {
1111 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
1112 fsr.FileSet, db_strerror(jcr->db));
1116 jcr->jr.FileSetId = fsr.FileSetId;
1117 bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime));
1118 Dmsg2(119, "Created FileSet %s record %u\n", jcr->fileset->hdr.name,
1123 void init_jcr_job_record(JCR *jcr)
1125 jcr->jr.SchedTime = jcr->sched_time;
1126 jcr->jr.StartTime = jcr->start_time;
1127 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
1128 jcr->jr.JobType = jcr->getJobType();
1129 jcr->jr.JobLevel = jcr->getJobLevel();
1130 jcr->jr.JobStatus = jcr->JobStatus;
1131 jcr->jr.JobId = jcr->JobId;
1132 bstrncpy(jcr->jr.Name, jcr->job->name(), sizeof(jcr->jr.Name));
1133 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
1137 * Write status and such in DB
1139 void update_job_end_record(JCR *jcr)
1141 jcr->jr.EndTime = time(NULL);
1142 jcr->end_time = jcr->jr.EndTime;
1143 jcr->jr.JobId = jcr->JobId;
1144 jcr->jr.JobStatus = jcr->JobStatus;
1145 jcr->jr.JobFiles = jcr->JobFiles;
1146 jcr->jr.JobBytes = jcr->JobBytes;
1147 jcr->jr.ReadBytes = jcr->ReadBytes;
1148 jcr->jr.VolSessionId = jcr->VolSessionId;
1149 jcr->jr.VolSessionTime = jcr->VolSessionTime;
1150 jcr->jr.JobErrors = jcr->JobErrors;
1151 jcr->jr.HasBase = jcr->HasBase;
1152 if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
1153 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
1154 db_strerror(jcr->db));
1159 * Takes base_name and appends (unique) current
1160 * date and time to form unique job name.
1162 * Note, the seconds are actually a sequence number. This
1163 * permits us to start a maximum fo 59 unique jobs a second, which
1164 * should be sufficient.
1166 * Returns: unique job name in jcr->Job
1167 * date/time in jcr->start_time
1169 void create_unique_job_name(JCR *jcr, const char *base_name)
1171 /* Job start mutex */
1172 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1173 static time_t last_start_time = 0;
1175 time_t now = time(NULL);
1177 char dt[MAX_TIME_LENGTH];
1178 char name[MAX_NAME_LENGTH];
1182 /* Guarantee unique start time -- maximum one per second, and
1183 * thus unique Job Name
1185 P(mutex); /* lock creation of jobs */
1187 if (seq > 59) { /* wrap as if it is seconds */
1189 while (now == last_start_time) {
1190 bmicrosleep(0, 500000);
1194 last_start_time = now;
1195 V(mutex); /* allow creation of jobs */
1196 jcr->start_time = now;
1197 /* Form Unique JobName */
1198 (void)localtime_r(&now, &tm);
1199 /* Use only characters that are permitted in Windows filenames */
1200 strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
1201 len = strlen(dt) + 5; /* dt + .%02d EOS */
1202 bstrncpy(name, base_name, sizeof(name));
1203 name[sizeof(name)-len] = 0; /* truncate if too long */
1204 bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s_%02d", name, dt, seq); /* add date & time */
1205 /* Convert spaces into underscores */
1206 for (p=jcr->Job; *p; p++) {
1211 Dmsg2(100, "JobId=%u created Job=%s\n", jcr->JobId, jcr->Job);
1214 /* Called directly from job rescheduling */
1215 void dird_free_jcr_pointers(JCR *jcr)
1217 /* Close but do not free bsock packets */
1218 if (jcr->file_bsock) {
1219 Dmsg0(200, "Close File bsock\n");
1220 jcr->file_bsock->close();
1222 if (jcr->store_bsock) {
1223 Dmsg0(200, "Close Store bsock\n");
1224 jcr->store_bsock->close();
1227 bfree_and_null(jcr->sd_auth_key);
1228 bfree_and_null(jcr->where);
1229 bfree_and_null(jcr->RestoreBootstrap);
1230 jcr->cached_attribute = false;
1231 bfree_and_null(jcr->ar);
1233 free_and_null_pool_memory(jcr->JobIds);
1234 free_and_null_pool_memory(jcr->client_uname);
1235 free_and_null_pool_memory(jcr->attr);
1236 free_and_null_pool_memory(jcr->fname);
1237 free_and_null_pool_memory(jcr->media_type);
1241 * Free the Job Control Record if no one is still using it.
1242 * Called from main free_jcr() routine in src/lib/jcr.c so
1243 * that we can do our Director specific cleanup of the jcr.
1245 void dird_free_jcr(JCR *jcr)
1247 Dmsg0(200, "Start dird free_jcr\n");
1249 dird_free_jcr_pointers(jcr);
1250 /* Free bsock packets */
1251 free_bsock(jcr->file_bsock);
1252 free_bsock(jcr->store_bsock);
1253 if (jcr->term_wait_inited) {
1254 pthread_cond_destroy(&jcr->term_wait);
1255 jcr->term_wait_inited = false;
1257 if (jcr->db_batch) {
1258 db_close_database(jcr, jcr->db_batch);
1259 jcr->db_batch = NULL;
1260 jcr->batch_started = false;
1263 db_close_database(jcr, jcr->db);
1267 free_and_null_pool_memory(jcr->stime);
1268 free_and_null_pool_memory(jcr->fname);
1269 free_and_null_pool_memory(jcr->pool_source);
1270 free_and_null_pool_memory(jcr->next_pool_source);
1271 free_and_null_pool_memory(jcr->catalog_source);
1272 free_and_null_pool_memory(jcr->rpool_source);
1273 free_and_null_pool_memory(jcr->wstore_source);
1274 free_and_null_pool_memory(jcr->rstore_source);
1276 /* Delete lists setup to hold storage pointers */
1277 free_rwstorage(jcr);
1279 jcr->job_end_push.destroy();
1281 if (jcr->JobId != 0) {
1282 write_state_file(director->working_directory, "bacula-dir", get_first_port_host_order(director->DIRaddrs));
1285 free_plugins(jcr); /* release instantiated plugins */
1287 Dmsg0(200, "End dird free_jcr\n");
1291 * The Job storage definition must be either in the Job record
1292 * or in the Pool record. The Pool record overrides the Job
1295 void get_job_storage(USTORE *store, JOB *job, RUN *run)
1297 if (run && run->pool && run->pool->storage) {
1298 store->store = (STORE *)run->pool->storage->first();
1299 pm_strcpy(store->store_source, _("Run pool override"));
1302 if (run && run->storage) {
1303 store->store = run->storage;
1304 pm_strcpy(store->store_source, _("Run storage override"));
1307 if (job->pool->storage) {
1308 store->store = (STORE *)job->pool->storage->first();
1309 pm_strcpy(store->store_source, _("Pool resource"));
1311 store->store = (STORE *)job->storage->first();
1312 pm_strcpy(store->store_source, _("Job resource"));
1317 * Set some defaults in the JCR necessary to
1318 * run. These items are pulled from the job
1319 * definition as defaults, but can be overridden
1320 * later either by the Run record in the Schedule resource,
1321 * or by the Console program.
1323 void set_jcr_defaults(JCR *jcr, JOB *job)
1326 jcr->setJobType(job->JobType);
1327 jcr->JobStatus = JS_Created;
1329 switch (jcr->getJobType()) {
1331 jcr->setJobLevel(L_NONE);
1334 jcr->setJobLevel(job->JobLevel);
1339 jcr->fname = get_pool_memory(PM_FNAME);
1341 if (!jcr->pool_source) {
1342 jcr->pool_source = get_pool_memory(PM_MESSAGE);
1344 if (!jcr->next_pool_source) {
1345 jcr->next_pool_source = get_pool_memory(PM_MESSAGE);
1347 if (!jcr->catalog_source) {
1348 jcr->catalog_source = get_pool_memory(PM_MESSAGE);
1351 jcr->JobPriority = job->Priority;
1352 /* Copy storage definitions -- deleted in dir_free_jcr above */
1354 copy_rwstorage(jcr, job->storage, _("Job resource"));
1356 copy_rwstorage(jcr, job->pool->storage, _("Pool resource"));
1358 jcr->client = job->client;
1359 ASSERT2(jcr->client, "jcr->client==NULL!!!");
1360 if (!jcr->client_name) {
1361 jcr->client_name = get_pool_memory(PM_NAME);
1363 pm_strcpy(jcr->client_name, jcr->client->name());
1364 jcr->pool = job->pool;
1365 pm_strcpy(jcr->pool_source, _("Job resource"));
1366 if (job->next_pool) {
1367 /* Use Job's Next Pool */
1368 jcr->next_pool = job->next_pool;
1369 pm_strcpy(jcr->next_pool_source, _("Job's NextPool resource"));
1371 /* Default to original pool->NextPool */
1372 jcr->next_pool = job->pool->NextPool;
1373 pm_strcpy(jcr->next_pool_source, _("Job Pool's NextPool resource"));
1375 jcr->full_pool = job->full_pool;
1376 jcr->inc_pool = job->inc_pool;
1377 jcr->diff_pool = job->diff_pool;
1378 if (job->pool->catalog) {
1379 jcr->catalog = job->pool->catalog;
1380 pm_strcpy(jcr->catalog_source, _("Pool resource"));
1382 jcr->catalog = job->client->catalog;
1383 pm_strcpy(jcr->catalog_source, _("Client resource"));
1385 jcr->fileset = job->fileset;
1386 jcr->accurate = job->accurate;
1387 jcr->messages = job->messages;
1388 jcr->spool_data = job->spool_data;
1389 jcr->spool_size = job->spool_size;
1390 jcr->write_part_after_job = job->write_part_after_job;
1391 jcr->IgnoreDuplicateJobChecking = job->IgnoreDuplicateJobChecking;
1392 jcr->MaxRunSchedTime = job->MaxRunSchedTime;
1393 if (jcr->RestoreBootstrap) {
1394 free(jcr->RestoreBootstrap);
1395 jcr->RestoreBootstrap = NULL;
1397 /* This can be overridden by Console program */
1398 if (job->RestoreBootstrap) {
1399 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
1401 /* This can be overridden by Console program */
1402 jcr->verify_job = job->verify_job;
1403 /* If no default level given, set one */
1404 if (jcr->getJobLevel() == 0) {
1405 switch (jcr->getJobType()) {
1407 jcr->setJobLevel(L_VERIFY_CATALOG);
1410 jcr->setJobLevel(L_INCREMENTAL);
1414 jcr->setJobLevel(L_NONE);
1417 jcr->setJobLevel(L_FULL);
1424 * Copy the storage definitions from an alist to the JCR
1426 void copy_rwstorage(JCR *jcr, alist *storage, const char *where)
1428 if (jcr->JobReads()) {
1429 copy_rstorage(jcr, storage, where);
1431 copy_wstorage(jcr, storage, where);
1435 /* Set storage override. Releases any previous storage definition */
1436 void set_rwstorage(JCR *jcr, USTORE *store)
1439 Jmsg(jcr, M_FATAL, 0, _("No storage specified.\n"));
1442 if (jcr->JobReads()) {
1443 set_rstorage(jcr, store);
1445 set_wstorage(jcr, store);
1448 void free_rwstorage(JCR *jcr)
1455 * Copy the storage definitions from an alist to the JCR
1457 void copy_rstorage(JCR *jcr, alist *storage, const char *where)
1461 if (jcr->rstorage) {
1462 delete jcr->rstorage;
1464 jcr->rstorage = New(alist(10, not_owned_by_alist));
1465 foreach_alist(st, storage) {
1466 jcr->rstorage->append(st);
1468 if (!jcr->rstore_source) {
1469 jcr->rstore_source = get_pool_memory(PM_MESSAGE);
1471 pm_strcpy(jcr->rstore_source, where);
1472 if (jcr->rstorage) {
1473 jcr->rstore = (STORE *)jcr->rstorage->first();
1479 /* Set storage override. Remove all previous storage */
1480 void set_rstorage(JCR *jcr, USTORE *store)
1484 if (!store->store) {
1487 if (jcr->rstorage) {
1490 if (!jcr->rstorage) {
1491 jcr->rstorage = New(alist(10, not_owned_by_alist));
1493 jcr->rstore = store->store;
1494 if (!jcr->rstore_source) {
1495 jcr->rstore_source = get_pool_memory(PM_MESSAGE);
1497 pm_strcpy(jcr->rstore_source, store->store_source);
1498 foreach_alist(storage, jcr->rstorage) {
1499 if (store->store == storage) {
1503 /* Store not in list, so add it */
1504 jcr->rstorage->prepend(store->store);
1507 void free_rstorage(JCR *jcr)
1509 if (jcr->rstorage) {
1510 delete jcr->rstorage;
1511 jcr->rstorage = NULL;
1517 * Copy the storage definitions from an alist to the JCR
1519 void copy_wstorage(JCR *jcr, alist *storage, const char *where)
1523 if (jcr->wstorage) {
1524 delete jcr->wstorage;
1526 jcr->wstorage = New(alist(10, not_owned_by_alist));
1527 foreach_alist(st, storage) {
1528 Dmsg1(100, "wstorage=%s\n", st->name());
1529 jcr->wstorage->append(st);
1531 if (!jcr->wstore_source) {
1532 jcr->wstore_source = get_pool_memory(PM_MESSAGE);
1534 pm_strcpy(jcr->wstore_source, where);
1535 if (jcr->wstorage) {
1536 jcr->wstore = (STORE *)jcr->wstorage->first();
1537 Dmsg2(100, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
1543 /* Set storage override. Remove all previous storage */
1544 void set_wstorage(JCR *jcr, USTORE *store)
1548 if (!store->store) {
1551 if (jcr->wstorage) {
1554 if (!jcr->wstorage) {
1555 jcr->wstorage = New(alist(10, not_owned_by_alist));
1557 jcr->wstore = store->store;
1558 if (!jcr->wstore_source) {
1559 jcr->wstore_source = get_pool_memory(PM_MESSAGE);
1561 pm_strcpy(jcr->wstore_source, store->store_source);
1562 Dmsg2(50, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
1563 foreach_alist(storage, jcr->wstorage) {
1564 if (store->store == storage) {
1568 /* Store not in list, so add it */
1569 jcr->wstorage->prepend(store->store);
1572 void free_wstorage(JCR *jcr)
1574 if (jcr->wstorage) {
1575 delete jcr->wstorage;
1576 jcr->wstorage = NULL;
1581 void create_clones(JCR *jcr)
1584 * Fire off any clone jobs (run directives)
1586 Dmsg2(900, "cloned=%d run_cmds=%p\n", jcr->cloned, jcr->job->run_cmds);
1587 if (!jcr->cloned && jcr->job->run_cmds) {
1589 JOB *job = jcr->job;
1590 POOLMEM *cmd = get_pool_memory(PM_FNAME);
1591 UAContext *ua = new_ua_context(jcr);
1593 foreach_alist(runcmd, job->run_cmds) {
1594 cmd = edit_job_codes(jcr, cmd, runcmd, "", job_code_callback_director);
1595 Mmsg(ua->cmd, "run %s cloned=yes", cmd);
1596 Dmsg1(900, "=============== Clone cmd=%s\n", ua->cmd);
1597 parse_ua_args(ua); /* parse command */
1598 int stat = run_cmd(ua, ua->cmd);
1600 Jmsg(jcr, M_ERROR, 0, _("Could not start clone job: \"%s\".\n"),
1603 Jmsg(jcr, M_INFO, 0, _("Clone JobId %d started.\n"), stat);
1606 free_ua_context(ua);
1607 free_pool_memory(cmd);
1612 * Given: a JobId in jcr->previous_jr.JobId,
1613 * this subroutine writes a bsr file to restore that job.
1614 * Returns: -1 on error
1615 * number of files if OK
1617 int create_restore_bootstrap_file(JCR *jcr)
1623 memset(&rx, 0, sizeof(rx));
1625 rx.JobIds = (char *)"";
1626 rx.bsr->JobId = jcr->previous_jr.JobId;
1627 ua = new_ua_context(jcr);
1628 if (!complete_bsr(ua, rx.bsr)) {
1632 rx.bsr->fi = new_findex();
1633 rx.bsr->fi->findex = 1;
1634 rx.bsr->fi->findex2 = jcr->previous_jr.JobFiles;
1635 jcr->ExpectedFiles = write_bsr_file(ua, rx);
1636 if (jcr->ExpectedFiles == 0) {
1640 free_ua_context(ua);
1642 jcr->needs_sd = true;
1643 return jcr->ExpectedFiles;
1646 free_ua_context(ua);
1651 /* TODO: redirect command ouput to job log */
1652 bool run_console_command(JCR *jcr, const char *cmd)
1656 JCR *ljcr = new_control_jcr("-RunScript-", JT_CONSOLE);
1657 ua = new_ua_context(ljcr);
1658 /* run from runscript and check if commands are authorized */
1659 ua->runscript = true;
1660 Mmsg(ua->cmd, "%s", cmd);
1661 Dmsg1(100, "Console command: %s\n", ua->cmd);
1663 if (ua->argc > 0 && ua->argk[0][0] == '.') {
1664 ok = do_a_dot_command(ua);
1666 ok = do_a_command(ua);
1668 free_ua_context(ua);