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)
442 memset(&cr, 0, sizeof(cr));
444 /* User is kind enough to provide the client name */
445 if ((i = find_arg_with_value(ua, "client")) > 0) {
446 bstrncpy(cr.Name, ua->argv[i], sizeof(cr.Name));
449 memset(&jr, 0, sizeof(jr));
450 bstrncpy(jr.Job, jcr->Job, sizeof(jr.Job));
452 if (!open_client_db(ua)) {
455 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
458 cr.ClientId = jr.ClientId;
459 if (!cr.ClientId || !db_get_client_record(ua->jcr, ua->db, &cr)) {
464 if (acl_access_ok(ua, Client_ACL, cr.Name)) {
465 jcr->client = (CLIENT *)GetResWithName(R_CLIENT, cr.Name);
469 cancel_file_daemon_job(ua, "cancel", jcr);
471 /* At this time, we can't really guess the storage name from
474 store.store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
479 set_wstorage(ua->jcr, &store);
481 cancel_sd_job(ua, "cancel", jcr);
488 * Cancel a job -- typically called by the UA (Console program), but may also
489 * be called by the job watchdog.
491 * Returns: true if cancel appears to be successful
492 * false on failure. Message sent to ua->jcr.
494 bool cancel_job(UAContext *ua, JCR *jcr, bool cancel)
497 int32_t old_status = jcr->JobStatus;
499 const char *reason, *cmd;
500 bool force = find_arg(ua, "inactive") > 0;
501 JCR *wjcr = jcr->wjcr;
504 /* If the user explicitely ask, we can send the cancel command to
507 if (cancel && force) {
508 return cancel_inactive_job(ua, jcr);
511 status = JS_Canceled;
512 reason = _("canceled");
515 jcr->setJobStatus(status);
517 switch (old_status) {
520 case JS_WaitClientRes:
521 case JS_WaitStoreRes:
522 case JS_WaitPriority:
524 case JS_WaitStartTime:
526 ua->info_msg(_("JobId %s, Job %s marked to be %s.\n"),
527 edit_uint64(jcr->JobId, ed1), jcr->Job,
529 jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
534 /* Cancel File daemon */
535 if (jcr->file_bsock) {
536 /* do not return now, we want to try to cancel the sd */
537 cancel_file_daemon_job(ua, cmd, jcr);
540 /* We test file_bsock because the previous operation can take
543 if (jcr->file_bsock && cancel) {
544 jcr->file_bsock->set_terminated();
545 jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
548 /* Cancel Storage daemon */
549 if (jcr->store_bsock) {
550 /* do not return now, we want to try to cancel the sd socket */
551 cancel_sd_job(ua, cmd, jcr);
554 /* We test file_bsock because the previous operation can take
557 if (jcr->store_bsock && cancel) {
558 jcr->store_bsock->set_timed_out();
559 jcr->store_bsock->set_terminated();
560 sd_msg_thread_send_signal(jcr, TIMEOUT_SIGNAL);
561 jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
564 /* Cancel Copy/Migration Storage daemon */
565 if (wjcr && wjcr->store_bsock) {
566 /* do not return now, we want to try to cancel the sd socket */
567 cancel_sd_job(ua, cmd, wjcr);
569 /* We test file_bsock because the previous operation can take
572 if (wjcr->store_bsock && cancel) {
573 wjcr->store_bsock->set_timed_out();
574 wjcr->store_bsock->set_terminated();
575 sd_msg_thread_send_signal(wjcr, TIMEOUT_SIGNAL);
576 wjcr->my_thread_send_signal(TIMEOUT_SIGNAL);
585 void cancel_storage_daemon_job(JCR *jcr)
587 if (jcr->sd_canceled) {
588 return; /* cancel only once */
591 UAContext *ua = new_ua_context(jcr);
592 JCR *control_jcr = new_control_jcr("*JobCancel*", JT_SYSTEM);
595 ua->jcr = control_jcr;
596 if (jcr->store_bsock) {
597 if (!ua->jcr->wstorage) {
599 copy_wstorage(ua->jcr, jcr->rstorage, _("Job resource"));
601 copy_wstorage(ua->jcr, jcr->wstorage, _("Job resource"));
606 store.store = jcr->rstore;
608 store.store = jcr->wstore;
610 set_wstorage(ua->jcr, &store);
613 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
616 Dmsg0(200, "Connected to storage daemon\n");
617 sd = ua->jcr->store_bsock;
618 sd->fsend("cancel Job=%s\n", jcr->Job);
619 while (sd->recv() >= 0) {
621 sd->signal(BNET_TERMINATE);
622 free_bsock(ua->jcr->store_bsock);
623 jcr->sd_canceled = true;
624 jcr->store_bsock->set_timed_out();
625 jcr->store_bsock->set_terminated();
626 sd_msg_thread_send_signal(jcr, TIMEOUT_SIGNAL);
627 jcr->my_thread_send_signal(TIMEOUT_SIGNAL);
630 free_jcr(control_jcr);
634 static void job_monitor_destructor(watchdog_t *self)
636 JCR *control_jcr = (JCR *)self->data;
638 free_jcr(control_jcr);
641 static void job_monitor_watchdog(watchdog_t *self)
643 JCR *control_jcr, *jcr;
645 control_jcr = (JCR *)self->data;
648 Dmsg1(800, "job_monitor_watchdog %p called\n", self);
653 if (jcr->JobId == 0 || job_canceled(jcr) || jcr->no_maxtime) {
654 Dmsg2(800, "Skipping JCR=%p Job=%s\n", jcr, jcr->Job);
658 /* check MaxWaitTime */
659 if (job_check_maxwaittime(jcr)) {
660 jcr->setJobStatus(JS_Canceled);
661 Qmsg(jcr, M_FATAL, 0, _("Max wait time exceeded. Job canceled.\n"));
663 /* check MaxRunTime */
664 } else if (job_check_maxruntime(jcr)) {
665 jcr->setJobStatus(JS_Canceled);
666 Qmsg(jcr, M_FATAL, 0, _("Max run time exceeded. Job canceled.\n"));
668 /* check MaxRunSchedTime */
669 } else if (job_check_maxrunschedtime(jcr)) {
670 jcr->setJobStatus(JS_Canceled);
671 Qmsg(jcr, M_FATAL, 0, _("Max run sched time exceeded. Job canceled.\n"));
676 Dmsg3(800, "Cancelling JCR %p jobid %d (%s)\n", jcr, jcr->JobId, jcr->Job);
677 UAContext *ua = new_ua_context(jcr);
678 ua->jcr = control_jcr;
681 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", jcr, jcr->JobId);
685 /* Keep reference counts correct */
690 * Check if the maxwaittime has expired and it is possible
693 static bool job_check_maxwaittime(JCR *jcr)
699 if (!job_waiting(jcr)) {
703 if (jcr->wait_time) {
704 current = watchdog_time - jcr->wait_time;
707 Dmsg2(200, "check maxwaittime %u >= %u\n",
708 current + jcr->wait_time_sum, job->MaxWaitTime);
709 if (job->MaxWaitTime != 0 &&
710 (current + jcr->wait_time_sum) >= job->MaxWaitTime) {
718 * Check if maxruntime has expired and if the job can be
721 static bool job_check_maxruntime(JCR *jcr)
727 if (job_canceled(jcr) || !jcr->job_started) {
730 if (jcr->job->MaxRunTime == 0 && job->FullMaxRunTime == 0 &&
731 job->IncMaxRunTime == 0 && job->DiffMaxRunTime == 0) {
734 run_time = watchdog_time - jcr->start_time;
735 Dmsg7(200, "check_maxruntime %llu-%u=%llu >= %llu|%llu|%llu|%llu\n",
736 watchdog_time, jcr->start_time, run_time, job->MaxRunTime, job->FullMaxRunTime,
737 job->IncMaxRunTime, job->DiffMaxRunTime);
739 if (jcr->getJobLevel() == L_FULL && job->FullMaxRunTime != 0 &&
740 run_time >= job->FullMaxRunTime) {
741 Dmsg0(200, "check_maxwaittime: FullMaxcancel\n");
743 } else if (jcr->getJobLevel() == L_DIFFERENTIAL && job->DiffMaxRunTime != 0 &&
744 run_time >= job->DiffMaxRunTime) {
745 Dmsg0(200, "check_maxwaittime: DiffMaxcancel\n");
747 } else if (jcr->getJobLevel() == L_INCREMENTAL && job->IncMaxRunTime != 0 &&
748 run_time >= job->IncMaxRunTime) {
749 Dmsg0(200, "check_maxwaittime: IncMaxcancel\n");
751 } else if (job->MaxRunTime > 0 && run_time >= job->MaxRunTime) {
752 Dmsg0(200, "check_maxwaittime: Maxcancel\n");
760 * Check if MaxRunSchedTime has expired and if the job can be
763 static bool job_check_maxrunschedtime(JCR *jcr)
765 if (jcr->MaxRunSchedTime == 0 || job_canceled(jcr)) {
768 if ((watchdog_time - jcr->initial_sched_time) < jcr->MaxRunSchedTime) {
769 Dmsg3(200, "Job %p (%s) with MaxRunSchedTime %d not expired\n",
770 jcr, jcr->Job, jcr->MaxRunSchedTime);
778 * Get or create a Pool record with the given name.
779 * Returns: 0 on error
782 DBId_t get_or_create_pool_record(JCR *jcr, char *pool_name)
786 memset(&pr, 0, sizeof(pr));
787 bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
788 Dmsg1(110, "get_or_create_pool=%s\n", pool_name);
790 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
791 /* Try to create the pool */
792 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
793 Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. ERR=%s"), pr.Name,
794 db_strerror(jcr->db));
797 Jmsg(jcr, M_INFO, 0, _("Created database record for Pool \"%s\".\n"), pr.Name);
804 * Check for duplicate jobs.
805 * Returns: true if current job should continue
806 * false if current job should terminate
808 bool allow_duplicate_job(JCR *jcr)
811 JCR *djcr; /* possible duplicate job */
812 bool cancel_dup = false;
813 bool cancel_me = false;
816 * See if AllowDuplicateJobs is set or
817 * if duplicate checking is disabled for this job.
819 if (job->AllowDuplicateJobs || jcr->IgnoreDuplicateJobChecking) {
823 Dmsg0(800, "Enter allow_duplicate_job\n");
826 * After this point, we do not want to allow any duplicate
831 if (jcr == djcr || djcr->JobId == 0) {
832 continue; /* do not cancel this job or consoles */
836 * See if this Job has the IgnoreDuplicateJobChecking flag set, ignore it
837 * for any checking against other jobs.
839 if (djcr->IgnoreDuplicateJobChecking) {
843 if (strcmp(job->name(), djcr->job->name()) == 0) {
844 if (job->DuplicateJobProximity > 0) {
845 utime_t now = (utime_t)time(NULL);
846 if ((now - djcr->start_time) > job->DuplicateJobProximity) {
847 continue; /* not really a duplicate */
850 if (job->CancelLowerLevelDuplicates &&
851 djcr->getJobType() == 'B' && jcr->getJobType() == 'B') {
852 switch (jcr->getJobLevel()) {
854 if (djcr->getJobLevel() == L_DIFFERENTIAL ||
855 djcr->getJobLevel() == L_INCREMENTAL) {
860 if (djcr->getJobLevel() == L_INCREMENTAL) {
863 if (djcr->getJobLevel() == L_FULL) {
868 if (djcr->getJobLevel() == L_FULL ||
869 djcr->getJobLevel() == L_DIFFERENTIAL) {
874 * cancel_dup will be done below
877 /* Zap current job */
878 jcr->setJobStatus(JS_Canceled);
879 Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"),
881 break; /* get out of foreach_jcr */
886 * Cancel one of the two jobs (me or dup)
887 * If CancelQueuedDuplicates is set do so only if job is queued.
889 if (job->CancelQueuedDuplicates) {
890 switch (djcr->JobStatus) {
893 case JS_WaitClientRes:
894 case JS_WaitStoreRes:
895 case JS_WaitPriority:
897 case JS_WaitStartTime:
899 cancel_dup = true; /* cancel queued duplicate */
906 if (cancel_dup || job->CancelRunningDuplicates) {
908 * Zap the duplicated job djcr
910 UAContext *ua = new_ua_context(jcr);
911 Jmsg(jcr, M_INFO, 0, _("Cancelling duplicate JobId=%d.\n"), djcr->JobId);
912 cancel_job(ua, djcr);
913 bmicrosleep(0, 500000);
914 djcr->setJobStatus(JS_Canceled);
915 cancel_job(ua, djcr);
917 Dmsg2(800, "Cancel dup %p JobId=%d\n", djcr, djcr->JobId);
922 jcr->setJobStatus(JS_Canceled);
923 Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"),
925 Dmsg2(800, "Cancel me %p JobId=%d\n", jcr, jcr->JobId);
927 Dmsg4(800, "curJobId=%d use_cnt=%d dupJobId=%d use_cnt=%d\n",
928 jcr->JobId, jcr->use_count(), djcr->JobId, djcr->use_count());
929 break; /* did our work, get out of foreach loop */
938 * Apply pool overrides to get the storage properly setup.
940 bool apply_wstorage_overrides(JCR *jcr, POOL *opool)
944 Dmsg1(100, "Original pool=%s\n", opool->name());
945 if (jcr->run_next_pool_override) {
946 pm_strcpy(jcr->next_pool_source, _("Run NextPool override"));
947 pm_strcpy(jcr->pool_source, _("Run NextPool override"));
948 source = _("Run NextPool override");
949 } else if (jcr->job->next_pool) {
950 /* Use Job Next Pool */
951 jcr->next_pool = jcr->job->next_pool;
952 pm_strcpy(jcr->next_pool_source, _("Job's NextPool resource"));
953 pm_strcpy(jcr->pool_source, _("Job's NextPool resource"));
954 source = _("Job's NextPool resource");
956 /* Default to original pool->NextPool */
957 jcr->next_pool = opool->NextPool;
958 Dmsg1(100, "next_pool=%p\n", jcr->next_pool);
959 if (jcr->next_pool) {
960 Dmsg1(100, "Original pool next Pool = %s\n", NPRT(jcr->next_pool->name()));
962 pm_strcpy(jcr->next_pool_source, _("Job Pool's NextPool resource"));
963 pm_strcpy(jcr->pool_source, _("Job Pool's NextPool resource"));
964 source = _("Pool's NextPool resource");
968 * If the original backup pool has a NextPool, make sure a
969 * record exists in the database.
971 if (jcr->next_pool) {
972 jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->next_pool->name());
973 if (jcr->jr.PoolId == 0) {
978 if (!set_mac_wstorage(NULL, jcr, jcr->pool, jcr->next_pool, source)) {
982 /* Set write pool and source. Not read pool is in rpool. */
983 jcr->pool = jcr->next_pool;
984 pm_strcpy(jcr->pool_source, source);
990 void apply_pool_overrides(JCR *jcr)
992 bool pool_override = false;
994 if (jcr->run_pool_override) {
995 pm_strcpy(jcr->pool_source, _("Run Pool override"));
998 * Apply any level related Pool selections
1000 switch (jcr->getJobLevel()) {
1002 if (jcr->full_pool) {
1003 jcr->pool = jcr->full_pool;
1004 pool_override = true;
1005 if (jcr->run_full_pool_override) {
1006 pm_strcpy(jcr->pool_source, _("Run FullPool override"));
1008 pm_strcpy(jcr->pool_source, _("Job FullPool override"));
1013 if (jcr->inc_pool) {
1014 jcr->pool = jcr->inc_pool;
1015 pool_override = true;
1016 if (jcr->run_inc_pool_override) {
1017 pm_strcpy(jcr->pool_source, _("Run IncPool override"));
1019 pm_strcpy(jcr->pool_source, _("Job IncPool override"));
1023 case L_DIFFERENTIAL:
1024 if (jcr->diff_pool) {
1025 jcr->pool = jcr->diff_pool;
1026 pool_override = true;
1027 if (jcr->run_diff_pool_override) {
1028 pm_strcpy(jcr->pool_source, _("Run DiffPool override"));
1030 pm_strcpy(jcr->pool_source, _("Job DiffPool override"));
1035 /* Update catalog if pool overridden */
1036 if (pool_override && jcr->pool->catalog) {
1037 jcr->catalog = jcr->pool->catalog;
1038 pm_strcpy(jcr->catalog_source, _("Pool resource"));
1044 * Get or create a Client record for this Job
1046 bool get_or_create_client_record(JCR *jcr)
1050 memset(&cr, 0, sizeof(cr));
1051 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
1052 cr.AutoPrune = jcr->client->AutoPrune;
1053 cr.FileRetention = jcr->client->FileRetention;
1054 cr.JobRetention = jcr->client->JobRetention;
1055 if (!jcr->client_name) {
1056 jcr->client_name = get_pool_memory(PM_NAME);
1058 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
1059 if (!db_create_client_record(jcr, jcr->db, &cr)) {
1060 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"),
1061 db_strerror(jcr->db));
1064 jcr->jr.ClientId = cr.ClientId;
1066 if (!jcr->client_uname) {
1067 jcr->client_uname = get_pool_memory(PM_NAME);
1069 pm_strcpy(jcr->client_uname, cr.Uname);
1071 Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name,
1077 * Get or Create FileSet record
1079 bool get_or_create_fileset_record(JCR *jcr)
1083 memset(&fsr, 0, sizeof(FILESET_DBR));
1084 bstrncpy(fsr.FileSet, jcr->fileset->hdr.name, sizeof(fsr.FileSet));
1085 if (jcr->fileset->have_MD5) {
1086 struct MD5Context md5c;
1087 unsigned char digest[MD5HashSize];
1088 memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
1089 MD5Final(digest, &md5c);
1091 * Keep the flag (last arg) set to false otherwise old FileSets will
1092 * get new MD5 sums and the user will get Full backups on everything
1094 bin_to_base64(fsr.MD5, sizeof(fsr.MD5), (char *)digest, MD5HashSize, false);
1095 bstrncpy(jcr->fileset->MD5, fsr.MD5, sizeof(jcr->fileset->MD5));
1097 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n"));
1099 if (!jcr->fileset->ignore_fs_changes ||
1100 !db_get_fileset_record(jcr, jcr->db, &fsr)) {
1101 if (!db_create_fileset_record(jcr, jcr->db, &fsr)) {
1102 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
1103 fsr.FileSet, db_strerror(jcr->db));
1107 jcr->jr.FileSetId = fsr.FileSetId;
1108 bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime));
1109 Dmsg2(119, "Created FileSet %s record %u\n", jcr->fileset->hdr.name,
1114 void init_jcr_job_record(JCR *jcr)
1116 jcr->jr.SchedTime = jcr->sched_time;
1117 jcr->jr.StartTime = jcr->start_time;
1118 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
1119 jcr->jr.JobType = jcr->getJobType();
1120 jcr->jr.JobLevel = jcr->getJobLevel();
1121 jcr->jr.JobStatus = jcr->JobStatus;
1122 jcr->jr.JobId = jcr->JobId;
1123 bstrncpy(jcr->jr.Name, jcr->job->name(), sizeof(jcr->jr.Name));
1124 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
1128 * Write status and such in DB
1130 void update_job_end_record(JCR *jcr)
1132 jcr->jr.EndTime = time(NULL);
1133 jcr->end_time = jcr->jr.EndTime;
1134 jcr->jr.JobId = jcr->JobId;
1135 jcr->jr.JobStatus = jcr->JobStatus;
1136 jcr->jr.JobFiles = jcr->JobFiles;
1137 jcr->jr.JobBytes = jcr->JobBytes;
1138 jcr->jr.ReadBytes = jcr->ReadBytes;
1139 jcr->jr.VolSessionId = jcr->VolSessionId;
1140 jcr->jr.VolSessionTime = jcr->VolSessionTime;
1141 jcr->jr.JobErrors = jcr->JobErrors;
1142 jcr->jr.HasBase = jcr->HasBase;
1143 if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
1144 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
1145 db_strerror(jcr->db));
1150 * Takes base_name and appends (unique) current
1151 * date and time to form unique job name.
1153 * Note, the seconds are actually a sequence number. This
1154 * permits us to start a maximum fo 59 unique jobs a second, which
1155 * should be sufficient.
1157 * Returns: unique job name in jcr->Job
1158 * date/time in jcr->start_time
1160 void create_unique_job_name(JCR *jcr, const char *base_name)
1162 /* Job start mutex */
1163 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1164 static time_t last_start_time = 0;
1166 time_t now = time(NULL);
1168 char dt[MAX_TIME_LENGTH];
1169 char name[MAX_NAME_LENGTH];
1173 /* Guarantee unique start time -- maximum one per second, and
1174 * thus unique Job Name
1176 P(mutex); /* lock creation of jobs */
1178 if (seq > 59) { /* wrap as if it is seconds */
1180 while (now == last_start_time) {
1181 bmicrosleep(0, 500000);
1185 last_start_time = now;
1186 V(mutex); /* allow creation of jobs */
1187 jcr->start_time = now;
1188 /* Form Unique JobName */
1189 (void)localtime_r(&now, &tm);
1190 /* Use only characters that are permitted in Windows filenames */
1191 strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
1192 len = strlen(dt) + 5; /* dt + .%02d EOS */
1193 bstrncpy(name, base_name, sizeof(name));
1194 name[sizeof(name)-len] = 0; /* truncate if too long */
1195 bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s_%02d", name, dt, seq); /* add date & time */
1196 /* Convert spaces into underscores */
1197 for (p=jcr->Job; *p; p++) {
1202 Dmsg2(100, "JobId=%u created Job=%s\n", jcr->JobId, jcr->Job);
1205 /* Called directly from job rescheduling */
1206 void dird_free_jcr_pointers(JCR *jcr)
1208 /* Close but do not free bsock packets */
1209 if (jcr->file_bsock) {
1210 Dmsg0(200, "Close File bsock\n");
1211 jcr->file_bsock->close();
1213 if (jcr->store_bsock) {
1214 Dmsg0(200, "Close Store bsock\n");
1215 jcr->store_bsock->close();
1218 bfree_and_null(jcr->sd_auth_key);
1219 bfree_and_null(jcr->where);
1220 bfree_and_null(jcr->RestoreBootstrap);
1221 jcr->cached_attribute = false;
1222 bfree_and_null(jcr->ar);
1224 free_and_null_pool_memory(jcr->JobIds);
1225 free_and_null_pool_memory(jcr->client_uname);
1226 free_and_null_pool_memory(jcr->attr);
1227 free_and_null_pool_memory(jcr->fname);
1228 free_and_null_pool_memory(jcr->media_type);
1232 * Free the Job Control Record if no one is still using it.
1233 * Called from main free_jcr() routine in src/lib/jcr.c so
1234 * that we can do our Director specific cleanup of the jcr.
1236 void dird_free_jcr(JCR *jcr)
1238 Dmsg0(200, "Start dird free_jcr\n");
1240 dird_free_jcr_pointers(jcr);
1241 /* Free bsock packets */
1242 free_bsock(jcr->file_bsock);
1243 free_bsock(jcr->store_bsock);
1244 if (jcr->term_wait_inited) {
1245 pthread_cond_destroy(&jcr->term_wait);
1246 jcr->term_wait_inited = false;
1248 if (jcr->db_batch) {
1249 db_close_database(jcr, jcr->db_batch);
1250 jcr->db_batch = NULL;
1251 jcr->batch_started = false;
1254 db_close_database(jcr, jcr->db);
1258 free_and_null_pool_memory(jcr->stime);
1259 free_and_null_pool_memory(jcr->fname);
1260 free_and_null_pool_memory(jcr->pool_source);
1261 free_and_null_pool_memory(jcr->next_pool_source);
1262 free_and_null_pool_memory(jcr->catalog_source);
1263 free_and_null_pool_memory(jcr->rpool_source);
1264 free_and_null_pool_memory(jcr->wstore_source);
1265 free_and_null_pool_memory(jcr->rstore_source);
1267 /* Delete lists setup to hold storage pointers */
1268 free_rwstorage(jcr);
1270 jcr->job_end_push.destroy();
1272 if (jcr->JobId != 0) {
1273 write_state_file(director->working_directory, "bacula-dir", get_first_port_host_order(director->DIRaddrs));
1276 free_plugins(jcr); /* release instantiated plugins */
1278 Dmsg0(200, "End dird free_jcr\n");
1282 * The Job storage definition must be either in the Job record
1283 * or in the Pool record. The Pool record overrides the Job
1286 void get_job_storage(USTORE *store, JOB *job, RUN *run)
1288 if (run && run->pool && run->pool->storage) {
1289 store->store = (STORE *)run->pool->storage->first();
1290 pm_strcpy(store->store_source, _("Run pool override"));
1293 if (run && run->storage) {
1294 store->store = run->storage;
1295 pm_strcpy(store->store_source, _("Run storage override"));
1298 if (job->pool->storage) {
1299 store->store = (STORE *)job->pool->storage->first();
1300 pm_strcpy(store->store_source, _("Pool resource"));
1302 store->store = (STORE *)job->storage->first();
1303 pm_strcpy(store->store_source, _("Job resource"));
1308 * Set some defaults in the JCR necessary to
1309 * run. These items are pulled from the job
1310 * definition as defaults, but can be overridden
1311 * later either by the Run record in the Schedule resource,
1312 * or by the Console program.
1314 void set_jcr_defaults(JCR *jcr, JOB *job)
1317 jcr->setJobType(job->JobType);
1318 jcr->JobStatus = JS_Created;
1320 switch (jcr->getJobType()) {
1322 jcr->setJobLevel(L_NONE);
1325 jcr->setJobLevel(job->JobLevel);
1330 jcr->fname = get_pool_memory(PM_FNAME);
1332 if (!jcr->pool_source) {
1333 jcr->pool_source = get_pool_memory(PM_MESSAGE);
1335 if (!jcr->next_pool_source) {
1336 jcr->next_pool_source = get_pool_memory(PM_MESSAGE);
1338 if (!jcr->catalog_source) {
1339 jcr->catalog_source = get_pool_memory(PM_MESSAGE);
1342 jcr->JobPriority = job->Priority;
1343 /* Copy storage definitions -- deleted in dir_free_jcr above */
1345 copy_rwstorage(jcr, job->storage, _("Job resource"));
1347 copy_rwstorage(jcr, job->pool->storage, _("Pool resource"));
1349 jcr->client = job->client;
1350 if (!jcr->client_name) {
1351 jcr->client_name = get_pool_memory(PM_NAME);
1353 pm_strcpy(jcr->client_name, jcr->client->name());
1354 jcr->pool = job->pool;
1355 pm_strcpy(jcr->pool_source, _("Job resource"));
1356 if (job->next_pool) {
1357 /* Use Job's Next Pool */
1358 jcr->next_pool = job->next_pool;
1359 pm_strcpy(jcr->next_pool_source, _("Job's NextPool resource"));
1361 /* Default to original pool->NextPool */
1362 jcr->next_pool = job->pool->NextPool;
1363 pm_strcpy(jcr->next_pool_source, _("Job Pool's NextPool resource"));
1365 jcr->full_pool = job->full_pool;
1366 jcr->inc_pool = job->inc_pool;
1367 jcr->diff_pool = job->diff_pool;
1368 if (job->pool->catalog) {
1369 jcr->catalog = job->pool->catalog;
1370 pm_strcpy(jcr->catalog_source, _("Pool resource"));
1372 jcr->catalog = job->client->catalog;
1373 pm_strcpy(jcr->catalog_source, _("Client resource"));
1375 jcr->fileset = job->fileset;
1376 jcr->accurate = job->accurate;
1377 jcr->messages = job->messages;
1378 jcr->spool_data = job->spool_data;
1379 jcr->spool_size = job->spool_size;
1380 jcr->write_part_after_job = job->write_part_after_job;
1381 jcr->IgnoreDuplicateJobChecking = job->IgnoreDuplicateJobChecking;
1382 jcr->MaxRunSchedTime = job->MaxRunSchedTime;
1383 if (jcr->RestoreBootstrap) {
1384 free(jcr->RestoreBootstrap);
1385 jcr->RestoreBootstrap = NULL;
1387 /* This can be overridden by Console program */
1388 if (job->RestoreBootstrap) {
1389 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
1391 /* This can be overridden by Console program */
1392 jcr->verify_job = job->verify_job;
1393 /* If no default level given, set one */
1394 if (jcr->getJobLevel() == 0) {
1395 switch (jcr->getJobType()) {
1397 jcr->setJobLevel(L_VERIFY_CATALOG);
1400 jcr->setJobLevel(L_INCREMENTAL);
1404 jcr->setJobLevel(L_NONE);
1407 jcr->setJobLevel(L_FULL);
1414 * Copy the storage definitions from an alist to the JCR
1416 void copy_rwstorage(JCR *jcr, alist *storage, const char *where)
1418 if (jcr->JobReads()) {
1419 copy_rstorage(jcr, storage, where);
1421 copy_wstorage(jcr, storage, where);
1425 /* Set storage override. Releases any previous storage definition */
1426 void set_rwstorage(JCR *jcr, USTORE *store)
1429 Jmsg(jcr, M_FATAL, 0, _("No storage specified.\n"));
1432 if (jcr->JobReads()) {
1433 set_rstorage(jcr, store);
1435 set_wstorage(jcr, store);
1438 void free_rwstorage(JCR *jcr)
1445 * Copy the storage definitions from an alist to the JCR
1447 void copy_rstorage(JCR *jcr, alist *storage, const char *where)
1451 if (jcr->rstorage) {
1452 delete jcr->rstorage;
1454 jcr->rstorage = New(alist(10, not_owned_by_alist));
1455 foreach_alist(st, storage) {
1456 jcr->rstorage->append(st);
1458 if (!jcr->rstore_source) {
1459 jcr->rstore_source = get_pool_memory(PM_MESSAGE);
1461 pm_strcpy(jcr->rstore_source, where);
1462 if (jcr->rstorage) {
1463 jcr->rstore = (STORE *)jcr->rstorage->first();
1469 /* Set storage override. Remove all previous storage */
1470 void set_rstorage(JCR *jcr, USTORE *store)
1474 if (!store->store) {
1477 if (jcr->rstorage) {
1480 if (!jcr->rstorage) {
1481 jcr->rstorage = New(alist(10, not_owned_by_alist));
1483 jcr->rstore = store->store;
1484 if (!jcr->rstore_source) {
1485 jcr->rstore_source = get_pool_memory(PM_MESSAGE);
1487 pm_strcpy(jcr->rstore_source, store->store_source);
1488 foreach_alist(storage, jcr->rstorage) {
1489 if (store->store == storage) {
1493 /* Store not in list, so add it */
1494 jcr->rstorage->prepend(store->store);
1497 void free_rstorage(JCR *jcr)
1499 if (jcr->rstorage) {
1500 delete jcr->rstorage;
1501 jcr->rstorage = NULL;
1507 * Copy the storage definitions from an alist to the JCR
1509 void copy_wstorage(JCR *jcr, alist *storage, const char *where)
1513 if (jcr->wstorage) {
1514 delete jcr->wstorage;
1516 jcr->wstorage = New(alist(10, not_owned_by_alist));
1517 foreach_alist(st, storage) {
1518 Dmsg1(100, "wstorage=%s\n", st->name());
1519 jcr->wstorage->append(st);
1521 if (!jcr->wstore_source) {
1522 jcr->wstore_source = get_pool_memory(PM_MESSAGE);
1524 pm_strcpy(jcr->wstore_source, where);
1525 if (jcr->wstorage) {
1526 jcr->wstore = (STORE *)jcr->wstorage->first();
1527 Dmsg2(100, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
1533 /* Set storage override. Remove all previous storage */
1534 void set_wstorage(JCR *jcr, USTORE *store)
1538 if (!store->store) {
1541 if (jcr->wstorage) {
1544 if (!jcr->wstorage) {
1545 jcr->wstorage = New(alist(10, not_owned_by_alist));
1547 jcr->wstore = store->store;
1548 if (!jcr->wstore_source) {
1549 jcr->wstore_source = get_pool_memory(PM_MESSAGE);
1551 pm_strcpy(jcr->wstore_source, store->store_source);
1552 Dmsg2(50, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
1553 foreach_alist(storage, jcr->wstorage) {
1554 if (store->store == storage) {
1558 /* Store not in list, so add it */
1559 jcr->wstorage->prepend(store->store);
1562 void free_wstorage(JCR *jcr)
1564 if (jcr->wstorage) {
1565 delete jcr->wstorage;
1566 jcr->wstorage = NULL;
1571 void create_clones(JCR *jcr)
1574 * Fire off any clone jobs (run directives)
1576 Dmsg2(900, "cloned=%d run_cmds=%p\n", jcr->cloned, jcr->job->run_cmds);
1577 if (!jcr->cloned && jcr->job->run_cmds) {
1579 JOB *job = jcr->job;
1580 POOLMEM *cmd = get_pool_memory(PM_FNAME);
1581 UAContext *ua = new_ua_context(jcr);
1583 foreach_alist(runcmd, job->run_cmds) {
1584 cmd = edit_job_codes(jcr, cmd, runcmd, "", job_code_callback_director);
1585 Mmsg(ua->cmd, "run %s cloned=yes", cmd);
1586 Dmsg1(900, "=============== Clone cmd=%s\n", ua->cmd);
1587 parse_ua_args(ua); /* parse command */
1588 int stat = run_cmd(ua, ua->cmd);
1590 Jmsg(jcr, M_ERROR, 0, _("Could not start clone job: \"%s\".\n"),
1593 Jmsg(jcr, M_INFO, 0, _("Clone JobId %d started.\n"), stat);
1596 free_ua_context(ua);
1597 free_pool_memory(cmd);
1602 * Given: a JobId in jcr->previous_jr.JobId,
1603 * this subroutine writes a bsr file to restore that job.
1604 * Returns: -1 on error
1605 * number of files if OK
1607 int create_restore_bootstrap_file(JCR *jcr)
1613 memset(&rx, 0, sizeof(rx));
1615 rx.JobIds = (char *)"";
1616 rx.bsr->JobId = jcr->previous_jr.JobId;
1617 ua = new_ua_context(jcr);
1618 if (!complete_bsr(ua, rx.bsr)) {
1622 rx.bsr->fi = new_findex();
1623 rx.bsr->fi->findex = 1;
1624 rx.bsr->fi->findex2 = jcr->previous_jr.JobFiles;
1625 jcr->ExpectedFiles = write_bsr_file(ua, rx);
1626 if (jcr->ExpectedFiles == 0) {
1630 free_ua_context(ua);
1632 jcr->needs_sd = true;
1633 return jcr->ExpectedFiles;
1636 free_ua_context(ua);
1641 /* TODO: redirect command ouput to job log */
1642 bool run_console_command(JCR *jcr, const char *cmd)
1646 JCR *ljcr = new_control_jcr("-RunScript-", JT_CONSOLE);
1647 ua = new_ua_context(ljcr);
1648 /* run from runscript and check if commands are authorized */
1649 ua->runscript = true;
1650 Mmsg(ua->cmd, "%s", cmd);
1651 Dmsg1(100, "Console command: %s\n", ua->cmd);
1653 if (ua->argc > 0 && ua->argk[0][0] == '.') {
1654 ok = do_a_dot_command(ua);
1656 ok = do_a_command(ua);
1658 free_ua_context(ua);