1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2016 Planets Communications B.V.
6 Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 02110-1301, USA.
22 */
23 /*
24 * Kern Sibbald, October MM
25 */
26 /**
27 * @file
28 * BAREOS Director Job processing routines
29 */
30
31 #include "include/bareos.h"
32 #include "dird.h"
33 #include "dird/dird_globals.h"
34 #include "dird/admin.h"
35 #include "dird/archive.h"
36 #include "dird/autoprune.h"
37 #include "dird/backup.h"
38 #include "dird/consolidate.h"
39 #include "dird/fd_cmds.h"
40 #include "dird/job.h"
41 #include "dird/migration.h"
42 #include "dird/restore.h"
43 #include "dird/sd_cmds.h"
44 #include "dird/stats.h"
45 #include "dird/storage.h"
46 #include "dird/ua_cmds.h"
47 #include "dird/ua_db.h"
48 #include "dird/ua_input.h"
49 #include "dird/ua_server.h"
50 #include "dird/ua_run.h"
51 #include "dird/vbackup.h"
52 #include "dird/verify.h"
53
54 #include "dird/ndmp_dma_backup_common.h"
55 #include "dird/ndmp_dma_backup.h"
56 #include "dird/ndmp_dma_backup_NATIVE_NDMP.h"
57 #include "dird/ndmp_dma_restore_common.h"
58 #include "dird/ndmp_dma_restore_NDMP_BAREOS.h"
59 #include "dird/ndmp_dma_restore_NDMP_NATIVE.h"
60
61 #include "cats/cats_backends.h"
62 #include "cats/sql_pooling.h"
63 #include "lib/edit.h"
64 #include "lib/parse_bsr.h"
65
66
67 namespace directordaemon {
68
69 /* Forward referenced subroutines */
70 static void *job_thread(void *arg);
71 static void JobMonitorWatchdog(watchdog_t *self);
72 static void JobMonitorDestructor(watchdog_t *self);
73 static bool JobCheckMaxwaittime(JobControlRecord *jcr);
74 static bool JobCheckMaxruntime(JobControlRecord *jcr);
75 static bool JobCheckMaxrunschedtime(JobControlRecord *jcr);
76
77 /* Imported subroutines */
78
79 /* Imported variables */
80
81 jobq_t job_queue;
82
InitJobServer(int max_workers)83 void InitJobServer(int max_workers)
84 {
85 int status;
86 watchdog_t *wd;
87
88 if ((status = JobqInit(&job_queue, max_workers, job_thread)) != 0) {
89 BErrNo be;
90 Emsg1(M_ABORT, 0, _("Could not init job queue: ERR=%s\n"), be.bstrerror(status));
91 }
92 wd = new_watchdog();
93 wd->callback = JobMonitorWatchdog;
94 wd->destructor = JobMonitorDestructor;
95 wd->one_shot = false;
96 wd->interval = 60;
97 wd->data = new_control_jcr("*JobMonitor*", JT_SYSTEM);
98 RegisterWatchdog(wd);
99 }
100
TermJobServer()101 void TermJobServer()
102 {
103 JobqDestroy(&job_queue); /* ignore any errors */
104 }
105
106 /**
107 * Run a job -- typically called by the scheduler, but may also
108 * be called by the UA (Console program).
109 *
110 * Returns: 0 on failure
111 * JobId on success
112 */
RunJob(JobControlRecord * jcr)113 JobId_t RunJob(JobControlRecord *jcr)
114 {
115 int status;
116
117 if (SetupJob(jcr)) {
118 Dmsg0(200, "Add jrc to work queue\n");
119 /*
120 * Queue the job to be run
121 */
122 if ((status = JobqAdd(&job_queue, jcr)) != 0) {
123 BErrNo be;
124 Jmsg(jcr, M_FATAL, 0, _("Could not add job queue: ERR=%s\n"), be.bstrerror(status));
125 return 0;
126 }
127 return jcr->JobId;
128 }
129
130 return 0;
131 }
132
SetupJob(JobControlRecord * jcr,bool suppress_output)133 bool SetupJob(JobControlRecord *jcr, bool suppress_output)
134 {
135 int errstat;
136
137 jcr->lock();
138 Dsm_check(100);
139
140 /*
141 * See if we should suppress all output.
142 */
143 if (!suppress_output) {
144 InitMsg(jcr, jcr->res.messages, job_code_callback_director);
145 } else {
146 jcr->suppress_output = true;
147 }
148
149 /*
150 * Initialize termination condition variable
151 */
152 if ((errstat = pthread_cond_init(&jcr->term_wait, NULL)) != 0) {
153 BErrNo be;
154 Jmsg1(jcr, M_FATAL, 0, _("Unable to init job cond variable: ERR=%s\n"), be.bstrerror(errstat));
155 jcr->unlock();
156 goto bail_out;
157 }
158 jcr->term_wait_inited = true;
159
160 /*
161 * Initialize nextrun ready condition variable
162 */
163 if ((errstat = pthread_cond_init(&jcr->nextrun_ready, NULL)) != 0) {
164 BErrNo be;
165 Jmsg1(jcr, M_FATAL, 0, _("Unable to init job nextrun cond variable: ERR=%s\n"), be.bstrerror(errstat));
166 jcr->unlock();
167 goto bail_out;
168 }
169 jcr->nextrun_ready_inited = true;
170
171 CreateUniqueJobName(jcr, jcr->res.job->name());
172 jcr->setJobStatus(JS_Created);
173 jcr->unlock();
174
175 /*
176 * Open database
177 */
178 Dmsg0(100, "Open database\n");
179 jcr->db = DbSqlGetPooledConnection(jcr,
180 jcr->res.catalog->db_driver,
181 jcr->res.catalog->db_name,
182 jcr->res.catalog->db_user,
183 jcr->res.catalog->db_password.value,
184 jcr->res.catalog->db_address,
185 jcr->res.catalog->db_port,
186 jcr->res.catalog->db_socket,
187 jcr->res.catalog->mult_db_connections,
188 jcr->res.catalog->disable_batch_insert,
189 jcr->res.catalog->try_reconnect,
190 jcr->res.catalog->exit_on_fatal);
191 if (jcr->db == NULL) {
192 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"), jcr->res.catalog->db_name);
193 goto bail_out;
194 }
195 Dmsg0(150, "DB opened\n");
196 if (!jcr->fname) {
197 jcr->fname = GetPoolMemory(PM_FNAME);
198 }
199
200 if (!jcr->res.pool_source) {
201 jcr->res.pool_source = GetPoolMemory(PM_MESSAGE);
202 PmStrcpy(jcr->res.pool_source, _("unknown source"));
203 }
204
205 if (!jcr->res.npool_source) {
206 jcr->res.npool_source = GetPoolMemory(PM_MESSAGE);
207 PmStrcpy(jcr->res.npool_source, _("unknown source"));
208 }
209
210 if (jcr->JobReads()) {
211 if (!jcr->res.rpool_source) {
212 jcr->res.rpool_source = GetPoolMemory(PM_MESSAGE);
213 PmStrcpy(jcr->res.rpool_source, _("unknown source"));
214 }
215 }
216
217 /*
218 * Create Job record
219 */
220 InitJcrJobRecord(jcr);
221
222 if (jcr->res.client) {
223 if (!GetOrCreateClientRecord(jcr)) {
224 goto bail_out;
225 }
226 }
227
228 if (!jcr->db->CreateJobRecord(jcr, &jcr->jr)) {
229 Jmsg(jcr, M_FATAL, 0, "%s", jcr->db->strerror());
230 goto bail_out;
231 }
232
233 jcr->JobId = jcr->jr.JobId;
234 Dmsg4(100, "Created job record JobId=%d Name=%s Type=%c Level=%c\n",
235 jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel);
236
237 NewPlugins(jcr); /* instantiate plugins for this jcr */
238 DispatchNewPluginOptions(jcr);
239 GeneratePluginEvent(jcr, bDirEventJobStart);
240
241 if (JobCanceled(jcr)) {
242 goto bail_out;
243 }
244
245 if (jcr->JobReads() && !jcr->res.read_storage_list) {
246 if (jcr->res.job->storage) {
247 CopyRwstorage(jcr, jcr->res.job->storage, _("Job resource"));
248 } else {
249 CopyRwstorage(jcr, jcr->res.job->pool->storage, _("Pool resource"));
250 }
251 }
252
253 if (!jcr->JobReads()) {
254 FreeRstorage(jcr);
255 }
256
257 /*
258 * Now, do pre-run stuff, like setting job level (Inc/diff, ...)
259 * this allows us to setup a proper job start record for restarting
260 * in case of later errors.
261 */
262 switch (jcr->getJobType()) {
263 case JT_BACKUP:
264 if (!jcr->is_JobLevel(L_VIRTUAL_FULL)) {
265 if (GetOrCreateFilesetRecord(jcr)) {
266 /*
267 * See if we need to upgrade the level. If GetLevelSinceTime returns true
268 * it has updated the level of the backup and we run apply_pool_overrides
269 * with the force flag so the correct pool (full, diff, incr) is selected.
270 * For all others we respect any set ignore flags.
271 */
272 if (GetLevelSinceTime(jcr)) {
273 ApplyPoolOverrides(jcr, true);
274 } else {
275 ApplyPoolOverrides(jcr, false);
276 }
277 } else {
278 goto bail_out;
279 }
280 }
281
282 switch (jcr->getJobProtocol()) {
283 case PT_NDMP_BAREOS:
284 if (!DoNdmpBackupInit(jcr)) {
285 NdmpBackupCleanup(jcr, JS_ErrorTerminated);
286 goto bail_out;
287 }
288 break;
289 case PT_NDMP_NATIVE:
290 if (!DoNdmpBackupInitNdmpNative(jcr)) {
291 NdmpBackupCleanup(jcr, JS_ErrorTerminated);
292 goto bail_out;
293 }
294 break;
295 default:
296 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
297 if (!DoNativeVbackupInit(jcr)) {
298 NativeVbackupCleanup(jcr, JS_ErrorTerminated);
299 goto bail_out;
300 }
301 } else {
302 if (!DoNativeBackupInit(jcr)) {
303 NativeBackupCleanup(jcr, JS_ErrorTerminated);
304 goto bail_out;
305 }
306 }
307 break;
308 }
309 break;
310 case JT_VERIFY:
311 if (!DoVerifyInit(jcr)) {
312 VerifyCleanup(jcr, JS_ErrorTerminated);
313 goto bail_out;
314 }
315 break;
316 case JT_RESTORE:
317 switch (jcr->getJobProtocol()) {
318 case PT_NDMP_BAREOS:
319 case PT_NDMP_NATIVE:
320 if (!DoNdmpRestoreInit(jcr)) {
321 NdmpRestoreCleanup(jcr, JS_ErrorTerminated);
322 goto bail_out;
323 }
324 break;
325 default:
326 /*
327 * Any non NDMP restore is not interested at the items
328 * that were selected for restore so drop them now.
329 */
330 if (jcr->restore_tree_root) {
331 FreeTree(jcr->restore_tree_root);
332 jcr->restore_tree_root = NULL;
333 }
334 if (!DoNativeRestoreInit(jcr)) {
335 NativeRestoreCleanup(jcr, JS_ErrorTerminated);
336 goto bail_out;
337 }
338 break;
339 }
340 break;
341 case JT_ADMIN:
342 if (!DoAdminInit(jcr)) {
343 AdminCleanup(jcr, JS_ErrorTerminated);
344 goto bail_out;
345 }
346 break;
347 case JT_ARCHIVE:
348 if (!DoArchiveInit(jcr)) {
349 ArchiveCleanup(jcr, JS_ErrorTerminated);
350 goto bail_out;
351 }
352 break;
353 case JT_COPY:
354 case JT_MIGRATE:
355 if (!DoMigrationInit(jcr)) {
356 MigrationCleanup(jcr, JS_ErrorTerminated);
357 goto bail_out;
358 }
359
360 /*
361 * If there is nothing to do the DoMigrationInit() function will set
362 * the termination status to JS_Terminated.
363 */
364 if (JobTerminatedSuccessfully(jcr)) {
365 MigrationCleanup(jcr, jcr->getJobStatus());
366 goto bail_out;
367 }
368 break;
369 case JT_CONSOLIDATE:
370 if (!DoConsolidateInit(jcr)) {
371 ConsolidateCleanup(jcr, JS_ErrorTerminated);
372 goto bail_out;
373 }
374
375 /*
376 * If there is nothing to do the do_consolidation_init() function will set
377 * the termination status to JS_Terminated.
378 */
379 if (JobTerminatedSuccessfully(jcr)) {
380 ConsolidateCleanup(jcr, jcr->getJobStatus());
381 goto bail_out;
382 }
383 break;
384 default:
385 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->getJobType());
386 jcr->setJobStatus(JS_ErrorTerminated);
387 goto bail_out;
388 }
389
390 GeneratePluginEvent(jcr, bDirEventJobInit);
391 Dsm_check(100);
392 return true;
393
394 bail_out:
395 return false;
396 }
397
IsConnectingToClientAllowed(ClientResource * res)398 bool IsConnectingToClientAllowed(ClientResource *res)
399 {
400 return res->conn_from_dir_to_fd;
401 }
402
IsConnectingToClientAllowed(JobControlRecord * jcr)403 bool IsConnectingToClientAllowed(JobControlRecord *jcr)
404 {
405 return IsConnectingToClientAllowed(jcr->res.client);
406 }
407
IsConnectFromClientAllowed(ClientResource * res)408 bool IsConnectFromClientAllowed(ClientResource *res)
409 {
410 return res->conn_from_fd_to_dir;
411 }
412
IsConnectFromClientAllowed(JobControlRecord * jcr)413 bool IsConnectFromClientAllowed(JobControlRecord *jcr)
414 {
415 return IsConnectFromClientAllowed(jcr->res.client);
416 }
417
UseWaitingClient(JobControlRecord * jcr,int timeout)418 bool UseWaitingClient(JobControlRecord *jcr, int timeout)
419 {
420 bool result = false;
421 Connection *connection = NULL;
422 ConnectionPool *connections = get_client_connections();
423
424 if (!IsConnectFromClientAllowed(jcr)) {
425 Dmsg1(120, "Client Initiated Connection from \"%s\" is not allowed.\n", jcr->res.client->name());
426 } else {
427 connection = connections->remove(jcr->res.client->name(), timeout);
428 if (connection) {
429 jcr->file_bsock = connection->bsock();
430 jcr->FDVersion = connection->protocol_version();
431 jcr->authenticated = connection->authenticated();
432 delete(connection);
433 Jmsg(jcr, M_INFO, 0, _("Using Client Initiated Connection (%s).\n"), jcr->res.client->name());
434 result = true;
435 }
436 }
437
438 return result;
439 }
440
UpdateJobEnd(JobControlRecord * jcr,int TermCode)441 void UpdateJobEnd(JobControlRecord *jcr, int TermCode)
442 {
443 DequeueMessages(jcr); /* display any queued messages */
444 jcr->setJobStatus(TermCode);
445 UpdateJobEndRecord(jcr);
446 }
447
448 /**
449 * This is the engine called by jobq.c:JobqAdd() when we were pulled from the work queue.
450 *
451 * At this point, we are running in our own thread and all necessary resources are
452 * allocated -- see jobq.c
453 */
job_thread(void * arg)454 static void *job_thread(void *arg)
455 {
456 JobControlRecord *jcr = (JobControlRecord *)arg;
457
458 pthread_detach(pthread_self());
459 Dsm_check(100);
460
461 Dmsg0(200, "=====Start Job=========\n");
462 jcr->setJobStatus(JS_Running); /* this will be set only if no error */
463 jcr->start_time = time(NULL); /* set the real start time */
464 jcr->jr.StartTime = jcr->start_time;
465
466 /*
467 * Let the statistics subsystem know a new Job was started.
468 */
469 stats_job_started();
470
471 if (jcr->res.job->MaxStartDelay != 0 && jcr->res.job->MaxStartDelay <
472 (utime_t)(jcr->start_time - jcr->sched_time)) {
473 jcr->setJobStatus(JS_Canceled);
474 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
475 }
476
477 if (JobCheckMaxrunschedtime(jcr)) {
478 jcr->setJobStatus(JS_Canceled);
479 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max run sched time exceeded.\n"));
480 }
481
482 /*
483 * TODO : check if it is used somewhere
484 */
485 if (jcr->res.job->RunScripts == NULL) {
486 Dmsg0(200, "Warning, job->RunScripts is empty\n");
487 jcr->res.job->RunScripts = New(alist(10, not_owned_by_alist));
488 }
489
490 if (!jcr->db->UpdateJobStartRecord(jcr, &jcr->jr)) {
491 Jmsg(jcr, M_FATAL, 0, "%s", jcr->db->strerror());
492 }
493
494 /*
495 * Run any script BeforeJob on dird
496 */
497 RunScripts(jcr, jcr->res.job->RunScripts, "BeforeJob");
498
499 /*
500 * We re-update the job start record so that the start time is set after the run before job.
501 * This avoids that any files created by the run before job will be saved twice. They will
502 * be backed up in the current job, but not in the next one unless they are changed.
503 *
504 * Without this, they will be backed up in this job and in the next job run because in that
505 * case, their date is after the start of this run.
506 */
507 jcr->start_time = time(NULL);
508 jcr->jr.StartTime = jcr->start_time;
509 if (!jcr->db->UpdateJobStartRecord(jcr, &jcr->jr)) {
510 Jmsg(jcr, M_FATAL, 0, "%s", jcr->db->strerror());
511 }
512
513 GeneratePluginEvent(jcr, bDirEventJobRun);
514
515 switch (jcr->getJobType()) {
516 case JT_BACKUP:
517 switch (jcr->getJobProtocol()) {
518 case PT_NDMP_BAREOS:
519 if (!JobCanceled(jcr)) {
520 if (DoNdmpBackup(jcr)) {
521 DoAutoprune(jcr);
522 } else {
523 NdmpBackupCleanup(jcr, JS_ErrorTerminated);
524 }
525 } else {
526 NdmpBackupCleanup(jcr, JS_Canceled);
527 }
528 break;
529 case PT_NDMP_NATIVE:
530 if (!JobCanceled(jcr)) {
531 if (DoNdmpBackupNdmpNative(jcr)) {
532 DoAutoprune(jcr);
533 } else {
534 NdmpBackupCleanup(jcr, JS_ErrorTerminated);
535 }
536 } else {
537 NdmpBackupCleanup(jcr, JS_Canceled);
538 }
539 break;
540 default:
541 if (!JobCanceled(jcr)) {
542 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
543 if (DoNativeVbackup(jcr)) {
544 DoAutoprune(jcr);
545 } else {
546 NativeVbackupCleanup(jcr, JS_ErrorTerminated);
547 }
548 } else {
549 if (DoNativeBackup(jcr)) {
550 DoAutoprune(jcr);
551 } else {
552 NativeBackupCleanup(jcr, JS_ErrorTerminated);
553 }
554 }
555 } else {
556 if (jcr->is_JobLevel(L_VIRTUAL_FULL)) {
557 NativeVbackupCleanup(jcr, JS_Canceled);
558 } else {
559 NativeBackupCleanup(jcr, JS_Canceled);
560 }
561 }
562 break;
563 }
564 break;
565 case JT_VERIFY:
566 if (!JobCanceled(jcr)) {
567 if (DoVerify(jcr)) {
568 DoAutoprune(jcr);
569 } else {
570 VerifyCleanup(jcr, JS_ErrorTerminated);
571 }
572 } else {
573 VerifyCleanup(jcr, JS_Canceled);
574 }
575 break;
576 case JT_RESTORE:
577 switch (jcr->getJobProtocol()) {
578 case PT_NDMP_BAREOS:
579 if (!JobCanceled(jcr)) {
580 if (DoNdmpRestore(jcr)) {
581 DoAutoprune(jcr);
582 } else {
583 NdmpRestoreCleanup(jcr, JS_ErrorTerminated);
584 }
585 } else {
586 NdmpRestoreCleanup(jcr, JS_Canceled);
587 }
588 break;
589 case PT_NDMP_NATIVE:
590 if (!JobCanceled(jcr)) {
591 if (DoNdmpRestoreNdmpNative(jcr)) {
592 DoAutoprune(jcr);
593 } else {
594 NdmpRestoreCleanup(jcr, JS_ErrorTerminated);
595 }
596 } else {
597 NdmpRestoreCleanup(jcr, JS_Canceled);
598 }
599 break;
600 default:
601 if (!JobCanceled(jcr)) {
602 if (DoNativeRestore(jcr)) {
603 DoAutoprune(jcr);
604 } else {
605 NativeRestoreCleanup(jcr, JS_ErrorTerminated);
606 }
607 } else {
608 NativeRestoreCleanup(jcr, JS_Canceled);
609 }
610 break;
611 }
612 break;
613 case JT_ADMIN:
614 if (!JobCanceled(jcr)) {
615 if (do_admin(jcr)) {
616 DoAutoprune(jcr);
617 } else {
618 AdminCleanup(jcr, JS_ErrorTerminated);
619 }
620 } else {
621 AdminCleanup(jcr, JS_Canceled);
622 }
623 break;
624 case JT_ARCHIVE:
625 if (!JobCanceled(jcr)) {
626 if (DoArchive(jcr)) {
627 DoAutoprune(jcr);
628 } else {
629 ArchiveCleanup(jcr, JS_ErrorTerminated);
630 }
631 } else {
632 ArchiveCleanup(jcr, JS_Canceled);
633 }
634 break;
635 case JT_COPY:
636 case JT_MIGRATE:
637 if (!JobCanceled(jcr)) {
638 if (DoMigration(jcr)) {
639 DoAutoprune(jcr);
640 } else {
641 MigrationCleanup(jcr, JS_ErrorTerminated);
642 }
643 } else {
644 MigrationCleanup(jcr, JS_Canceled);
645 }
646 break;
647 case JT_CONSOLIDATE:
648 if (!JobCanceled(jcr)) {
649 if (DoConsolidate(jcr)) {
650 DoAutoprune(jcr);
651 } else {
652 ConsolidateCleanup(jcr, JS_ErrorTerminated);
653 }
654 } else {
655 ConsolidateCleanup(jcr, JS_Canceled);
656 }
657 break;
658 default:
659 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->getJobType());
660 break;
661 }
662
663 /*
664 * Check for subscriptions and issue a warning when exceeded.
665 */
666 if (me->subscriptions &&
667 me->subscriptions < me->subscriptions_used) {
668 Jmsg(jcr, M_WARNING, 0, _("Subscriptions exceeded: (used/total) (%d/%d)\n"),
669 me->subscriptions_used, me->subscriptions);
670 }
671
672 RunScripts(jcr, jcr->res.job->RunScripts, "AfterJob");
673
674 /*
675 * Send off any queued messages
676 */
677 if (jcr->msg_queue && jcr->msg_queue->size() > 0) {
678 DequeueMessages(jcr);
679 }
680
681 GeneratePluginEvent(jcr, bDirEventJobEnd);
682 Dmsg1(50, "======== End Job stat=%c ==========\n", jcr->JobStatus);
683 Dsm_check(100);
684
685 return NULL;
686 }
687
SdMsgThreadSendSignal(JobControlRecord * jcr,int sig)688 void SdMsgThreadSendSignal(JobControlRecord *jcr, int sig)
689 {
690 jcr->lock();
691 if (!jcr->sd_msg_thread_done &&
692 jcr->SD_msg_chan_started &&
693 !pthread_equal(jcr->SD_msg_chan, pthread_self())) {
694 Dmsg1(800, "Send kill to SD msg chan jid=%d\n", jcr->JobId);
695 pthread_kill(jcr->SD_msg_chan, sig);
696 }
697 jcr->unlock();
698 }
699
700 /**
701 * Cancel a job -- typically called by the UA (Console program), but may also
702 * be called by the job watchdog.
703 *
704 * Returns: true if cancel appears to be successful
705 * false on failure. Message sent to ua->jcr.
706 */
CancelJob(UaContext * ua,JobControlRecord * jcr)707 bool CancelJob(UaContext *ua, JobControlRecord *jcr)
708 {
709 char ed1[50];
710 int32_t old_status = jcr->JobStatus;
711
712 jcr->setJobStatus(JS_Canceled);
713
714 switch (old_status) {
715 case JS_Created:
716 case JS_WaitJobRes:
717 case JS_WaitClientRes:
718 case JS_WaitStoreRes:
719 case JS_WaitPriority:
720 case JS_WaitMaxJobs:
721 case JS_WaitStartTime:
722 ua->InfoMsg(_("JobId %s, Job %s marked to be canceled.\n"),
723 edit_uint64(jcr->JobId, ed1), jcr->Job);
724 JobqRemove(&job_queue, jcr); /* attempt to remove it from queue */
725 break;
726
727 default:
728 /*
729 * Cancel File daemon
730 */
731 if (jcr->file_bsock) {
732 if (!CancelFileDaemonJob(ua, jcr)) {
733 return false;
734 }
735 }
736
737 /*
738 * Cancel Storage daemon
739 */
740 if (jcr->store_bsock) {
741 if (!CancelStorageDaemonJob(ua, jcr)) {
742 return false;
743 }
744 }
745
746 /*
747 * Cancel second Storage daemon for SD-SD replication.
748 */
749 if (jcr->mig_jcr && jcr->mig_jcr->store_bsock) {
750 if (!CancelStorageDaemonJob(ua, jcr->mig_jcr)) {
751 return false;
752 }
753 }
754
755 break;
756 }
757
758 RunScripts(jcr, jcr->res.job->RunScripts, "AfterJob");
759
760 return true;
761 }
762
JobMonitorDestructor(watchdog_t * self)763 static void JobMonitorDestructor(watchdog_t *self)
764 {
765 JobControlRecord *control_jcr = (JobControlRecord *)self->data;
766
767 FreeJcr(control_jcr);
768 }
769
JobMonitorWatchdog(watchdog_t * self)770 static void JobMonitorWatchdog(watchdog_t *self)
771 {
772 JobControlRecord *control_jcr, *jcr;
773
774 control_jcr = (JobControlRecord *)self->data;
775
776 Dsm_check(100);
777 Dmsg1(800, "JobMonitorWatchdog %p called\n", self);
778
779 foreach_jcr(jcr) {
780 bool cancel = false;
781
782 if (jcr->JobId == 0 || JobCanceled(jcr) || jcr->no_maxtime) {
783 Dmsg2(800, "Skipping JobControlRecord=%p Job=%s\n", jcr, jcr->Job);
784 continue;
785 }
786
787 /* check MaxWaitTime */
788 if (JobCheckMaxwaittime(jcr)) {
789 jcr->setJobStatus(JS_Canceled);
790 Qmsg(jcr, M_FATAL, 0, _("Max wait time exceeded. Job canceled.\n"));
791 cancel = true;
792 /* check MaxRunTime */
793 } else if (JobCheckMaxruntime(jcr)) {
794 jcr->setJobStatus(JS_Canceled);
795 Qmsg(jcr, M_FATAL, 0, _("Max run time exceeded. Job canceled.\n"));
796 cancel = true;
797 /* check MaxRunSchedTime */
798 } else if (JobCheckMaxrunschedtime(jcr)) {
799 jcr->setJobStatus(JS_Canceled);
800 Qmsg(jcr, M_FATAL, 0, _("Max run sched time exceeded. Job canceled.\n"));
801 cancel = true;
802 }
803
804 if (cancel) {
805 Dmsg3(800, "Cancelling JobControlRecord %p jobid %d (%s)\n", jcr, jcr->JobId, jcr->Job);
806 UaContext *ua = new_ua_context(jcr);
807 ua->jcr = control_jcr;
808 CancelJob(ua, jcr);
809 FreeUaContext(ua);
810 Dmsg2(800, "Have cancelled JobControlRecord %p Job=%d\n", jcr, jcr->JobId);
811 }
812
813 }
814 /* Keep reference counts correct */
815 endeach_jcr(jcr);
816 }
817
818 /**
819 * Check if the maxwaittime has expired and it is possible
820 * to cancel the job.
821 */
JobCheckMaxwaittime(JobControlRecord * jcr)822 static bool JobCheckMaxwaittime(JobControlRecord *jcr)
823 {
824 bool cancel = false;
825 JobResource *job = jcr->res.job;
826 utime_t current=0;
827
828 if (!JobWaiting(jcr)) {
829 return false;
830 }
831
832 if (jcr->wait_time) {
833 current = watchdog_time - jcr->wait_time;
834 }
835
836 Dmsg2(200, "check maxwaittime %u >= %u\n",
837 current + jcr->wait_time_sum, job->MaxWaitTime);
838 if (job->MaxWaitTime != 0 &&
839 (current + jcr->wait_time_sum) >= job->MaxWaitTime) {
840 cancel = true;
841 }
842
843 return cancel;
844 }
845
846 /**
847 * Check if maxruntime has expired and if the job can be
848 * canceled.
849 */
JobCheckMaxruntime(JobControlRecord * jcr)850 static bool JobCheckMaxruntime(JobControlRecord *jcr)
851 {
852 bool cancel = false;
853 JobResource *job = jcr->res.job;
854 utime_t run_time;
855
856 if (JobCanceled(jcr) || !jcr->job_started) {
857 return false;
858 }
859 if (job->MaxRunTime == 0 && job->FullMaxRunTime == 0 &&
860 job->IncMaxRunTime == 0 && job->DiffMaxRunTime == 0) {
861 return false;
862 }
863 run_time = watchdog_time - jcr->start_time;
864 Dmsg7(200, "check_maxruntime %llu-%u=%llu >= %llu|%llu|%llu|%llu\n",
865 watchdog_time, jcr->start_time, run_time, job->MaxRunTime, job->FullMaxRunTime,
866 job->IncMaxRunTime, job->DiffMaxRunTime);
867
868 if (jcr->getJobLevel() == L_FULL && job->FullMaxRunTime != 0 &&
869 run_time >= job->FullMaxRunTime) {
870 Dmsg0(200, "check_maxwaittime: FullMaxcancel\n");
871 cancel = true;
872 } else if (jcr->getJobLevel() == L_DIFFERENTIAL && job->DiffMaxRunTime != 0 &&
873 run_time >= job->DiffMaxRunTime) {
874 Dmsg0(200, "check_maxwaittime: DiffMaxcancel\n");
875 cancel = true;
876 } else if (jcr->getJobLevel() == L_INCREMENTAL && job->IncMaxRunTime != 0 &&
877 run_time >= job->IncMaxRunTime) {
878 Dmsg0(200, "check_maxwaittime: IncMaxcancel\n");
879 cancel = true;
880 } else if (job->MaxRunTime > 0 && run_time >= job->MaxRunTime) {
881 Dmsg0(200, "check_maxwaittime: Maxcancel\n");
882 cancel = true;
883 }
884
885 return cancel;
886 }
887
888 /**
889 * Check if MaxRunSchedTime has expired and if the job can be
890 * canceled.
891 */
JobCheckMaxrunschedtime(JobControlRecord * jcr)892 static bool JobCheckMaxrunschedtime(JobControlRecord *jcr)
893 {
894 if (jcr->MaxRunSchedTime == 0 || JobCanceled(jcr)) {
895 return false;
896 }
897 if ((watchdog_time - jcr->initial_sched_time) < jcr->MaxRunSchedTime) {
898 Dmsg3(200, "Job %p (%s) with MaxRunSchedTime %d not expired\n",
899 jcr, jcr->Job, jcr->MaxRunSchedTime);
900 return false;
901 }
902
903 return true;
904 }
905
906 /**
907 * Get or create a Pool record with the given name.
908 * Returns: 0 on error
909 * poolid if OK
910 */
GetOrCreatePoolRecord(JobControlRecord * jcr,char * pool_name)911 DBId_t GetOrCreatePoolRecord(JobControlRecord *jcr, char *pool_name)
912 {
913 PoolDbRecord pr;
914
915 memset(&pr, 0, sizeof(pr));
916 bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
917 Dmsg1(110, "get_or_create_pool=%s\n", pool_name);
918
919 while (!jcr->db->GetPoolRecord(jcr, &pr)) { /* get by Name */
920 /* Try to create the pool */
921 if (CreatePool(jcr, jcr->db, jcr->res.pool, POOL_OP_CREATE) < 0) {
922 Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. ERR=%s"), pr.Name, jcr->db->strerror());
923 return 0;
924 } else {
925 Jmsg(jcr, M_INFO, 0, _("Created database record for Pool \"%s\".\n"), pr.Name);
926 }
927 }
928 return pr.PoolId;
929 }
930
931 /**
932 * Check for duplicate jobs.
933 * Returns: true if current job should continue
934 * false if current job should terminate
935 */
AllowDuplicateJob(JobControlRecord * jcr)936 bool AllowDuplicateJob(JobControlRecord *jcr)
937 {
938 JobControlRecord *djcr; /* possible duplicate job */
939 JobResource *job = jcr->res.job;
940 bool cancel_dup = false;
941 bool cancel_me = false;
942
943 /*
944 * See if AllowDuplicateJobs is set or
945 * if duplicate checking is disabled for this job.
946 */
947 if (job->AllowDuplicateJobs || jcr->IgnoreDuplicateJobChecking) {
948 return true;
949 }
950
951 Dmsg0(800, "Enter AllowDuplicateJob\n");
952
953 /*
954 * After this point, we do not want to allow any duplicate
955 * job to run.
956 */
957
958 foreach_jcr(djcr) {
959 if (jcr == djcr || djcr->JobId == 0) {
960 continue; /* do not cancel this job or consoles */
961 }
962
963 /*
964 * See if this Job has the IgnoreDuplicateJobChecking flag set, ignore it
965 * for any checking against other jobs.
966 */
967 if (djcr->IgnoreDuplicateJobChecking) {
968 continue;
969 }
970
971 if (bstrcmp(job->name(), djcr->res.job->name())) {
972 if (job->DuplicateJobProximity > 0) {
973 utime_t now = (utime_t)time(NULL);
974 if ((now - djcr->start_time) > job->DuplicateJobProximity) {
975 continue; /* not really a duplicate */
976 }
977 }
978 if (job->CancelLowerLevelDuplicates &&
979 djcr->is_JobType(JT_BACKUP) && jcr->is_JobType(JT_BACKUP)) {
980 switch (jcr->getJobLevel()) {
981 case L_FULL:
982 if (djcr->getJobLevel() == L_DIFFERENTIAL ||
983 djcr->getJobLevel() == L_INCREMENTAL) {
984 cancel_dup = true;
985 }
986 break;
987 case L_DIFFERENTIAL:
988 if (djcr->getJobLevel() == L_INCREMENTAL) {
989 cancel_dup = true;
990 }
991 if (djcr->getJobLevel() == L_FULL) {
992 cancel_me = true;
993 }
994 break;
995 case L_INCREMENTAL:
996 if (djcr->getJobLevel() == L_FULL ||
997 djcr->getJobLevel() == L_DIFFERENTIAL) {
998 cancel_me = true;
999 }
1000 }
1001 /*
1002 * cancel_dup will be done below
1003 */
1004 if (cancel_me) {
1005 /* Zap current job */
1006 jcr->setJobStatus(JS_Canceled);
1007 Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"),
1008 djcr->JobId);
1009 break; /* get out of foreach_jcr */
1010 }
1011 }
1012
1013 /*
1014 * Cancel one of the two jobs (me or dup)
1015 * If CancelQueuedDuplicates is set do so only if job is queued.
1016 */
1017 if (job->CancelQueuedDuplicates) {
1018 switch (djcr->JobStatus) {
1019 case JS_Created:
1020 case JS_WaitJobRes:
1021 case JS_WaitClientRes:
1022 case JS_WaitStoreRes:
1023 case JS_WaitPriority:
1024 case JS_WaitMaxJobs:
1025 case JS_WaitStartTime:
1026 cancel_dup = true; /* cancel queued duplicate */
1027 break;
1028 default:
1029 break;
1030 }
1031 }
1032
1033 if (cancel_dup || job->CancelRunningDuplicates) {
1034 /*
1035 * Zap the duplicated job djcr
1036 */
1037 UaContext *ua = new_ua_context(jcr);
1038 Jmsg(jcr, M_INFO, 0, _("Cancelling duplicate JobId=%d.\n"), djcr->JobId);
1039 CancelJob(ua, djcr);
1040 Bmicrosleep(0, 500000);
1041 djcr->setJobStatus(JS_Canceled);
1042 CancelJob(ua, djcr);
1043 FreeUaContext(ua);
1044 Dmsg2(800, "Cancel dup %p JobId=%d\n", djcr, djcr->JobId);
1045 } else {
1046 /*
1047 * Zap current job
1048 */
1049 jcr->setJobStatus(JS_Canceled);
1050 Jmsg(jcr, M_FATAL, 0, _("JobId %d already running. Duplicate job not allowed.\n"),
1051 djcr->JobId);
1052 Dmsg2(800, "Cancel me %p JobId=%d\n", jcr, jcr->JobId);
1053 }
1054 Dmsg4(800, "curJobId=%d use_cnt=%d dupJobId=%d use_cnt=%d\n",
1055 jcr->JobId, jcr->UseCount(), djcr->JobId, djcr->UseCount());
1056 break; /* did our work, get out of foreach loop */
1057 }
1058 }
1059 endeach_jcr(djcr);
1060
1061 return true;
1062 }
1063
1064 /**
1065 * This subroutine edits the last job start time into a
1066 * "since=date/time" buffer that is returned in the
1067 * variable since. This is used for display purposes in
1068 * the job report. The time in jcr->stime is later
1069 * passed to tell the File daemon what to do.
1070 */
GetLevelSinceTime(JobControlRecord * jcr)1071 bool GetLevelSinceTime(JobControlRecord *jcr)
1072 {
1073 int JobLevel;
1074 bool have_full;
1075 bool do_full = false;
1076 bool do_vfull = false;
1077 bool do_diff = false;
1078 bool pool_updated = false;
1079 utime_t now;
1080 utime_t last_full_time = 0;
1081 utime_t last_diff_time;
1082 char prev_job[MAX_NAME_LENGTH];
1083
1084 jcr->since[0] = 0;
1085
1086 /*
1087 * If job cloned and a since time already given, use it
1088 */
1089 if (jcr->cloned && jcr->stime && jcr->stime[0]) {
1090 bstrncpy(jcr->since, _(", since="), sizeof(jcr->since));
1091 bstrncat(jcr->since, jcr->stime, sizeof(jcr->since));
1092 return pool_updated;
1093 }
1094
1095 /*
1096 * Make sure stime buffer is allocated
1097 */
1098 if (!jcr->stime) {
1099 jcr->stime = GetPoolMemory(PM_MESSAGE);
1100 }
1101 jcr->PrevJob[0] = 0;
1102 jcr->stime[0] = 0;
1103
1104 /*
1105 * Lookup the last FULL backup job to get the time/date for a
1106 * differential or incremental save.
1107 */
1108 JobLevel = jcr->getJobLevel();
1109 switch (JobLevel) {
1110 case L_DIFFERENTIAL:
1111 case L_INCREMENTAL:
1112 POOLMEM *stime = GetPoolMemory(PM_MESSAGE);
1113
1114 /*
1115 * Look up start time of last Full job
1116 */
1117 now = (utime_t)time(NULL);
1118 jcr->jr.JobId = 0; /* flag to return since time */
1119
1120 /*
1121 * This is probably redundant, but some of the code below
1122 * uses jcr->stime, so don't remove unless you are sure.
1123 */
1124 if (!jcr->db->FindJobStartTime(jcr, &jcr->jr, jcr->stime, jcr->PrevJob)) {
1125 do_full = true;
1126 }
1127
1128 have_full = jcr->db->FindLastJobStartTime(jcr, &jcr->jr, stime, prev_job, L_FULL);
1129 if (have_full) {
1130 last_full_time = StrToUtime(stime);
1131 } else {
1132 do_full = true; /* No full, upgrade to one */
1133 }
1134
1135 Dmsg4(50, "have_full=%d do_full=%d now=%lld full_time=%lld\n", have_full,
1136 do_full, now, last_full_time);
1137
1138 /*
1139 * Make sure the last diff is recent enough
1140 */
1141 if (have_full && JobLevel == L_INCREMENTAL && jcr->res.job->MaxDiffInterval > 0) {
1142 /*
1143 * Lookup last diff job
1144 */
1145 if (jcr->db->FindLastJobStartTime(jcr, &jcr->jr, stime, prev_job, L_DIFFERENTIAL)) {
1146 last_diff_time = StrToUtime(stime);
1147 /*
1148 * If no Diff since Full, use Full time
1149 */
1150 if (last_diff_time < last_full_time) {
1151 last_diff_time = last_full_time;
1152 }
1153 Dmsg2(50, "last_diff_time=%lld last_full_time=%lld\n", last_diff_time,
1154 last_full_time);
1155 } else {
1156 /*
1157 * No last differential, so use last full time
1158 */
1159 last_diff_time = last_full_time;
1160 Dmsg1(50, "No last_diff_time setting to full_time=%lld\n", last_full_time);
1161 }
1162 do_diff = ((now - last_diff_time) >= jcr->res.job->MaxDiffInterval);
1163 Dmsg2(50, "do_diff=%d diffInter=%lld\n", do_diff, jcr->res.job->MaxDiffInterval);
1164 }
1165
1166 /*
1167 * Note, do_full takes precedence over do_vfull and do_diff
1168 */
1169 if (have_full && jcr->res.job->MaxFullInterval > 0) {
1170 do_full = ((now - last_full_time) >= jcr->res.job->MaxFullInterval);
1171 } else if (have_full && jcr->res.job->MaxVFullInterval > 0) {
1172 do_vfull = ((now - last_full_time) >= jcr->res.job->MaxVFullInterval);
1173 }
1174 FreePoolMemory(stime);
1175
1176 if (do_full) {
1177 /*
1178 * No recent Full job found, so upgrade this one to Full
1179 */
1180 Jmsg(jcr, M_INFO, 0, "%s", jcr->db->strerror());
1181 Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full backup found in catalog. Doing FULL backup.\n"));
1182 Bsnprintf(jcr->since, sizeof(jcr->since), _(" (upgraded from %s)"), level_to_str(JobLevel));
1183 jcr->setJobLevel(jcr->jr.JobLevel = L_FULL);
1184 pool_updated = true;
1185 } else if (do_vfull) {
1186 /*
1187 * No recent Full job found, and MaxVirtualFull is set so upgrade this one to Virtual Full
1188 */
1189 Jmsg(jcr, M_INFO, 0, "%s", jcr->db->strerror());
1190 Jmsg(jcr, M_INFO, 0, _("No prior or suitable Full backup found in catalog. Doing Virtual FULL backup.\n"));
1191 Bsnprintf(jcr->since, sizeof(jcr->since), _(" (upgraded from %s)"), level_to_str(jcr->getJobLevel()));
1192 jcr->setJobLevel(jcr->jr.JobLevel = L_VIRTUAL_FULL);
1193 pool_updated = true;
1194
1195 /*
1196 * If we get upgraded to a Virtual Full we will be using a read pool so make sure we have a rpool_source.
1197 */
1198 if (!jcr->res.rpool_source) {
1199 jcr->res.rpool_source = GetPoolMemory(PM_MESSAGE);
1200 PmStrcpy(jcr->res.rpool_source, _("unknown source"));
1201 }
1202 } else if (do_diff) {
1203 /*
1204 * No recent diff job found, so upgrade this one to Diff
1205 */
1206 Jmsg(jcr, M_INFO, 0, _("No prior or suitable Differential backup found in catalog. Doing Differential backup.\n"));
1207 Bsnprintf(jcr->since, sizeof(jcr->since), _(" (upgraded from %s)"), level_to_str(JobLevel));
1208 jcr->setJobLevel(jcr->jr.JobLevel = L_DIFFERENTIAL);
1209 pool_updated = true;
1210 } else {
1211 if (jcr->res.job->rerun_failed_levels) {
1212 if (jcr->db->FindFailedJobSince(jcr, &jcr->jr, jcr->stime, JobLevel)) {
1213 Jmsg(jcr, M_INFO, 0, _("Prior failed job found in catalog. Upgrading to %s.\n"), level_to_str(JobLevel));
1214 Bsnprintf(jcr->since, sizeof(jcr->since), _(" (upgraded from %s)"), level_to_str(JobLevel));
1215 jcr->setJobLevel(jcr->jr.JobLevel = JobLevel);
1216 jcr->jr.JobId = jcr->JobId;
1217 pool_updated = true;
1218 break;
1219 }
1220 }
1221
1222 bstrncpy(jcr->since, _(", since="), sizeof(jcr->since));
1223 bstrncat(jcr->since, jcr->stime, sizeof(jcr->since));
1224 }
1225 jcr->jr.JobId = jcr->JobId;
1226
1227 /*
1228 * Lookup the Job record of the previous Job and store it in jcr->previous_jr.
1229 */
1230 if (jcr->PrevJob[0]) {
1231 bstrncpy(jcr->previous_jr.Job, jcr->PrevJob, sizeof(jcr->previous_jr.Job));
1232 if (!jcr->db->GetJobRecord(jcr, &jcr->previous_jr)) {
1233 Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"), jcr->db->strerror());
1234 }
1235 }
1236
1237 break;
1238 }
1239
1240 Dmsg3(100, "Level=%c last start time=%s job=%s\n", JobLevel, jcr->stime, jcr->PrevJob);
1241
1242 return pool_updated;
1243 }
1244
ApplyPoolOverrides(JobControlRecord * jcr,bool force)1245 void ApplyPoolOverrides(JobControlRecord *jcr, bool force)
1246 {
1247 Dmsg0(100, "entering ApplyPoolOverrides()\n");
1248 bool pool_override = false;
1249
1250 /*
1251 * If a cmdline pool override is given ignore any level pool overrides.
1252 * Unless a force is given then we always apply any overrides.
1253 */
1254 if (!force && jcr->IgnoreLevelPoolOverides) {
1255 return;
1256 }
1257
1258 /*
1259 * If only a pool override and no level overrides are given in run entry choose this pool
1260 */
1261 if (jcr->res.run_pool_override &&
1262 !jcr->res.run_full_pool_override &&
1263 !jcr->res.run_vfull_pool_override &&
1264 !jcr->res.run_inc_pool_override &&
1265 !jcr->res.run_diff_pool_override) {
1266 PmStrcpy(jcr->res.pool_source, _("Run Pool override"));
1267 Dmsg2(100, "Pool set to '%s' because of %s", jcr->res.pool->name(), _("Run Pool override\n"));
1268 } else {
1269 /*
1270 * Apply any level related Pool selections
1271 */
1272 switch (jcr->getJobLevel()) {
1273 case L_FULL:
1274 if (jcr->res.full_pool) {
1275 jcr->res.pool = jcr->res.full_pool;
1276 pool_override = true;
1277 if (jcr->res.run_full_pool_override) {
1278 PmStrcpy(jcr->res.pool_source, _("Run FullPool override"));
1279 Dmsg2(100, "Pool set to '%s' because of %s", jcr->res.full_pool->name(), "Run FullPool override\n");
1280 } else {
1281 PmStrcpy(jcr->res.pool_source, _("Job FullPool override"));
1282 Dmsg2(100, "Pool set to '%s' because of %s", jcr->res.full_pool->name(), "Job FullPool override\n");
1283 }
1284 }
1285 break;
1286 case L_VIRTUAL_FULL:
1287 if (jcr->res.vfull_pool) {
1288 jcr->res.pool = jcr->res.vfull_pool;
1289 pool_override = true;
1290 if (jcr->res.run_vfull_pool_override) {
1291 PmStrcpy(jcr->res.pool_source, _("Run VFullPool override"));
1292 Dmsg2(100, "Pool set to '%s' because of %s", jcr->res.vfull_pool->name(), "Run VFullPool override\n");
1293 } else {
1294 PmStrcpy(jcr->res.pool_source, _("Job VFullPool override"));
1295 Dmsg2(100, "Pool set to '%s' because of %s", jcr->res.vfull_pool->name(), "Job VFullPool override\n");
1296 }
1297 }
1298 break;
1299 case L_INCREMENTAL:
1300 if (jcr->res.inc_pool) {
1301 jcr->res.pool = jcr->res.inc_pool;
1302 pool_override = true;
1303 if (jcr->res.run_inc_pool_override) {
1304 PmStrcpy(jcr->res.pool_source, _("Run IncPool override"));
1305 Dmsg2(100, "Pool set to '%s' because of %s", jcr->res.inc_pool->name(), "Run IncPool override\n");
1306 } else {
1307 PmStrcpy(jcr->res.pool_source, _("Job IncPool override"));
1308 Dmsg2(100, "Pool set to '%s' because of %s", jcr->res.inc_pool->name(), "Job IncPool override\n");
1309 }
1310 }
1311 break;
1312 case L_DIFFERENTIAL:
1313 if (jcr->res.diff_pool) {
1314 jcr->res.pool = jcr->res.diff_pool;
1315 pool_override = true;
1316 if (jcr->res.run_diff_pool_override) {
1317 PmStrcpy(jcr->res.pool_source, _("Run DiffPool override"));
1318 Dmsg2(100, "Pool set to '%s' because of %s", jcr->res.diff_pool->name(), "Run DiffPool override\n");
1319 } else {
1320 PmStrcpy(jcr->res.pool_source, _("Job DiffPool override"));
1321 Dmsg2(100, "Pool set to '%s' because of %s", jcr->res.diff_pool->name(), "Job DiffPool override\n");
1322 }
1323 }
1324 break;
1325 }
1326 }
1327
1328 /*
1329 * Update catalog if pool overridden
1330 */
1331 if (pool_override && jcr->res.pool->catalog) {
1332 jcr->res.catalog = jcr->res.pool->catalog;
1333 PmStrcpy(jcr->res.catalog_source, _("Pool resource"));
1334 }
1335 }
1336
1337 /**
1338 * Get or create a Client record for this Job
1339 */
GetOrCreateClientRecord(JobControlRecord * jcr)1340 bool GetOrCreateClientRecord(JobControlRecord *jcr)
1341 {
1342 ClientDbRecord cr;
1343
1344 memset(&cr, 0, sizeof(cr));
1345 bstrncpy(cr.Name, jcr->res.client->hdr.name, sizeof(cr.Name));
1346 cr.AutoPrune = jcr->res.client->AutoPrune;
1347 cr.FileRetention = jcr->res.client->FileRetention;
1348 cr.JobRetention = jcr->res.client->JobRetention;
1349 if (!jcr->client_name) {
1350 jcr->client_name = GetPoolMemory(PM_NAME);
1351 }
1352 PmStrcpy(jcr->client_name, jcr->res.client->hdr.name);
1353 if (!jcr->db->CreateClientRecord(jcr, &cr)) {
1354 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"), jcr->db->strerror());
1355 return false;
1356 }
1357 /*
1358 * Only initialize quota when a Soft or Hard Limit is set.
1359 */
1360 if (jcr->res.client->HardQuota != 0 ||
1361 jcr->res.client->SoftQuota != 0) {
1362 if (!jcr->db->GetQuotaRecord(jcr, &cr)) {
1363 if (!jcr->db->CreateQuotaRecord(jcr, &cr)) {
1364 Jmsg(jcr, M_FATAL, 0, _("Could not create Quota record. ERR=%s\n"), jcr->db->strerror());
1365 }
1366 jcr->res.client->QuotaLimit = 0;
1367 jcr->res.client->GraceTime = 0;
1368 }
1369 }
1370 jcr->jr.ClientId = cr.ClientId;
1371 jcr->res.client->QuotaLimit = cr.QuotaLimit;
1372 jcr->res.client->GraceTime = cr.GraceTime;
1373 if (cr.Uname[0]) {
1374 if (!jcr->client_uname) {
1375 jcr->client_uname = GetPoolMemory(PM_NAME);
1376 }
1377 PmStrcpy(jcr->client_uname, cr.Uname);
1378 }
1379 Dmsg2(100, "Created Client %s record %d\n", jcr->res.client->hdr.name,
1380 jcr->jr.ClientId);
1381 return true;
1382 }
1383
GetOrCreateFilesetRecord(JobControlRecord * jcr)1384 bool GetOrCreateFilesetRecord(JobControlRecord *jcr)
1385 {
1386 FileSetDbRecord fsr;
1387
1388 /*
1389 * Get or Create FileSet record
1390 */
1391 memset(&fsr, 0, sizeof(fsr));
1392 bstrncpy(fsr.FileSet, jcr->res.fileset->hdr.name, sizeof(fsr.FileSet));
1393 if (jcr->res.fileset->have_MD5) {
1394 MD5_CTX md5c;
1395 unsigned char digest[MD5HashSize];
1396 memcpy(&md5c, &jcr->res.fileset->md5c, sizeof(md5c));
1397 MD5_Final(digest, &md5c);
1398 /*
1399 * Keep the flag (last arg) set to false otherwise old FileSets will
1400 * get new MD5 sums and the user will get Full backups on everything
1401 */
1402 BinToBase64(fsr.MD5, sizeof(fsr.MD5), (char *)digest, MD5HashSize, false);
1403 bstrncpy(jcr->res.fileset->MD5, fsr.MD5, sizeof(jcr->res.fileset->MD5));
1404 } else {
1405 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n"));
1406 }
1407 if (!jcr->res.fileset->ignore_fs_changes ||
1408 !jcr->db->GetFilesetRecord(jcr, &fsr)) {
1409 PoolMem FileSetText(PM_MESSAGE);
1410
1411 jcr->res.fileset->PrintConfig(FileSetText, false, false);
1412 fsr.FileSetText = FileSetText.c_str();
1413
1414 if (!jcr->db->CreateFilesetRecord(jcr, &fsr)) {
1415 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
1416 fsr.FileSet, jcr->db->strerror());
1417 return false;
1418 }
1419 }
1420
1421 jcr->jr.FileSetId = fsr.FileSetId;
1422 bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime));
1423
1424 Dmsg2(119, "Created FileSet %s record %u\n", jcr->res.fileset->hdr.name, jcr->jr.FileSetId);
1425
1426 return true;
1427 }
1428
InitJcrJobRecord(JobControlRecord * jcr)1429 void InitJcrJobRecord(JobControlRecord *jcr)
1430 {
1431 jcr->jr.SchedTime = jcr->sched_time;
1432 jcr->jr.StartTime = jcr->start_time;
1433 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
1434 jcr->jr.JobType = jcr->getJobType();
1435 jcr->jr.JobLevel = jcr->getJobLevel();
1436 jcr->jr.JobStatus = jcr->JobStatus;
1437 jcr->jr.JobId = jcr->JobId;
1438 jcr->jr.JobSumTotalBytes = 18446744073709551615LLU;
1439 bstrncpy(jcr->jr.Name, jcr->res.job->name(), sizeof(jcr->jr.Name));
1440 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
1441 }
1442
1443 /**
1444 * Write status and such in DB
1445 */
UpdateJobEndRecord(JobControlRecord * jcr)1446 void UpdateJobEndRecord(JobControlRecord *jcr)
1447 {
1448 jcr->jr.EndTime = time(NULL);
1449 jcr->end_time = jcr->jr.EndTime;
1450 jcr->jr.JobId = jcr->JobId;
1451 jcr->jr.JobStatus = jcr->JobStatus;
1452 jcr->jr.JobFiles = jcr->JobFiles;
1453 jcr->jr.JobBytes = jcr->JobBytes;
1454 jcr->jr.ReadBytes = jcr->ReadBytes;
1455 jcr->jr.VolSessionId = jcr->VolSessionId;
1456 jcr->jr.VolSessionTime = jcr->VolSessionTime;
1457 jcr->jr.JobErrors = jcr->JobErrors;
1458 jcr->jr.HasBase = jcr->HasBase;
1459 if (!jcr->db->UpdateJobEndRecord(jcr, &jcr->jr)) {
1460 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"), jcr->db->strerror());
1461 }
1462 }
1463
1464 /**
1465 * Takes base_name and appends (unique) current
1466 * date and time to form unique job name.
1467 *
1468 * Note, the seconds are actually a sequence number. This
1469 * permits us to start a maximum fo 59 unique jobs a second, which
1470 * should be sufficient.
1471 *
1472 * Returns: unique job name in jcr->Job
1473 * date/time in jcr->start_time
1474 */
CreateUniqueJobName(JobControlRecord * jcr,const char * base_name)1475 void CreateUniqueJobName(JobControlRecord *jcr, const char *base_name)
1476 {
1477 /* Job start mutex */
1478 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1479 static time_t last_start_time = 0;
1480 static int seq = 0;
1481 int lseq = 0;
1482 time_t now = time(NULL);
1483 char dt[MAX_TIME_LENGTH];
1484 char name[MAX_NAME_LENGTH];
1485 char *p;
1486 int len;
1487
1488 /* Guarantee unique start time -- maximum one per second, and
1489 * thus unique Job Name
1490 */
1491 P(mutex); /* lock creation of jobs */
1492 seq++;
1493 if (seq > 59) { /* wrap as if it is seconds */
1494 seq = 0;
1495 while (now == last_start_time) {
1496 Bmicrosleep(0, 500000);
1497 now = time(NULL);
1498 }
1499 }
1500 lseq = seq;
1501 last_start_time = now;
1502 V(mutex); /* allow creation of jobs */
1503 jcr->start_time = now;
1504
1505 /*
1506 * Form Unique JobName
1507 * Use only characters that are permitted in Windows filenames
1508 */
1509 bstrftime(dt, sizeof(dt), jcr->start_time, "%Y-%m-%d_%H.%M.%S");
1510
1511 len = strlen(dt) + 5; /* dt + .%02d EOS */
1512 bstrncpy(name, base_name, sizeof(name));
1513 name[sizeof(name)-len] = 0; /* truncate if too long */
1514 Bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s_%02d", name, dt, lseq); /* add date & time */
1515 /* Convert spaces into underscores */
1516 for (p=jcr->Job; *p; p++) {
1517 if (*p == ' ') {
1518 *p = '_';
1519 }
1520 }
1521 Dmsg2(100, "JobId=%u created Job=%s\n", jcr->JobId, jcr->Job);
1522 }
1523
1524 /**
1525 * Called directly from job rescheduling
1526 */
DirdFreeJcrPointers(JobControlRecord * jcr)1527 void DirdFreeJcrPointers(JobControlRecord *jcr)
1528 {
1529 if (jcr->file_bsock) {
1530 Dmsg0(200, "Close File bsock\n");
1531 jcr->file_bsock->close();
1532 delete jcr->file_bsock;
1533 jcr->file_bsock = NULL;
1534 }
1535
1536 if (jcr->store_bsock) {
1537 Dmsg0(200, "Close Store bsock\n");
1538 jcr->store_bsock->close();
1539 delete jcr->store_bsock;
1540 jcr->store_bsock = NULL;
1541 }
1542
1543 BfreeAndNull(jcr->sd_auth_key);
1544 BfreeAndNull(jcr->where);
1545 BfreeAndNull(jcr->backup_format);
1546 BfreeAndNull(jcr->RestoreBootstrap);
1547 BfreeAndNull(jcr->ar);
1548
1549 FreeAndNullPoolMemory(jcr->JobIds);
1550 FreeAndNullPoolMemory(jcr->client_uname);
1551 FreeAndNullPoolMemory(jcr->attr);
1552 FreeAndNullPoolMemory(jcr->fname);
1553 }
1554
1555 /**
1556 * Free the Job Control Record if no one is still using it.
1557 * Called from main FreeJcr() routine in src/lib/jcr.c so
1558 * that we can do our Director specific cleanup of the jcr.
1559 */
DirdFreeJcr(JobControlRecord * jcr)1560 void DirdFreeJcr(JobControlRecord *jcr)
1561 {
1562 Dmsg0(200, "Start dird FreeJcr\n");
1563
1564 if (jcr->mig_jcr) {
1565 FreeJcr(jcr->mig_jcr);
1566 jcr->mig_jcr = NULL;
1567 }
1568
1569 DirdFreeJcrPointers(jcr);
1570
1571 if (jcr->term_wait_inited) {
1572 pthread_cond_destroy(&jcr->term_wait);
1573 jcr->term_wait_inited = false;
1574 }
1575
1576 if (jcr->nextrun_ready_inited) {
1577 pthread_cond_destroy(&jcr->nextrun_ready);
1578 jcr->nextrun_ready_inited = false;
1579 }
1580
1581 if (jcr->db_batch) {
1582 DbSqlClosePooledConnection(jcr, jcr->db_batch);
1583 jcr->db_batch = NULL;
1584 jcr->batch_started = false;
1585 }
1586
1587 if (jcr->db) {
1588 DbSqlClosePooledConnection(jcr, jcr->db);
1589 jcr->db = NULL;
1590 }
1591
1592 if (jcr->restore_tree_root) {
1593 FreeTree(jcr->restore_tree_root);
1594 }
1595
1596 if (jcr->bsr) {
1597 libbareos::FreeBsr(jcr->bsr);
1598 jcr->bsr = NULL;
1599 }
1600
1601 FreeAndNullPoolMemory(jcr->stime);
1602 FreeAndNullPoolMemory(jcr->fname);
1603 FreeAndNullPoolMemory(jcr->res.pool_source);
1604 FreeAndNullPoolMemory(jcr->res.npool_source);
1605 FreeAndNullPoolMemory(jcr->res.rpool_source);
1606 FreeAndNullPoolMemory(jcr->res.wstore_source);
1607 FreeAndNullPoolMemory(jcr->res.rstore_source);
1608 FreeAndNullPoolMemory(jcr->res.catalog_source);
1609 FreeAndNullPoolMemory(jcr->FDSecureEraseCmd);
1610 FreeAndNullPoolMemory(jcr->SDSecureEraseCmd);
1611 FreeAndNullPoolMemory(jcr->vf_jobids);
1612
1613 /*
1614 * Delete lists setup to hold storage pointers
1615 */
1616 FreeRwstorage(jcr);
1617
1618 jcr->job_end_callbacks.destroy();
1619
1620 if (jcr->JobId != 0) {
1621 WriteStateFile(me->working_directory, "bareos-dir", GetFirstPortHostOrder(me->DIRaddrs));
1622 }
1623
1624 FreePlugins(jcr); /* release instantiated plugins */
1625
1626 Dmsg0(200, "End dird FreeJcr\n");
1627 }
1628
1629 /**
1630 * The Job storage definition must be either in the Job record
1631 * or in the Pool record. The Pool record overrides the Job record.
1632 */
GetJobStorage(UnifiedStorageResource * store,JobResource * job,RunResource * run)1633 void GetJobStorage(UnifiedStorageResource *store, JobResource *job, RunResource *run)
1634 {
1635 if (run && run->pool && run->pool->storage) {
1636 store->store = (StorageResource *)run->pool->storage->first();
1637 PmStrcpy(store->store_source, _("Run pool override"));
1638 return;
1639 }
1640 if (run && run->storage) {
1641 store->store = run->storage;
1642 PmStrcpy(store->store_source, _("Run storage override"));
1643 return;
1644 }
1645 if (job->pool->storage) {
1646 store->store = (StorageResource *)job->pool->storage->first();
1647 PmStrcpy(store->store_source, _("Pool resource"));
1648 } else {
1649 if (job->storage) {
1650 store->store = (StorageResource *)job->storage->first();
1651 PmStrcpy(store->store_source, _("Job resource"));
1652 }
1653 }
1654 }
1655
1656 /**
1657 * Set some defaults in the JobControlRecord necessary to
1658 * run. These items are pulled from the job
1659 * definition as defaults, but can be overridden
1660 * later either by the Run record in the Schedule resource,
1661 * or by the Console program.
1662 */
SetJcrDefaults(JobControlRecord * jcr,JobResource * job)1663 void SetJcrDefaults(JobControlRecord *jcr, JobResource *job)
1664 {
1665 jcr->res.job = job;
1666 jcr->setJobType(job->JobType);
1667 jcr->setJobProtocol(job->Protocol);
1668 jcr->JobStatus = JS_Created;
1669
1670 switch (jcr->getJobType()) {
1671 case JT_ADMIN:
1672 jcr->setJobLevel(L_NONE);
1673 break;
1674 case JT_ARCHIVE:
1675 jcr->setJobLevel(L_NONE);
1676 break;
1677 default:
1678 jcr->setJobLevel(job->JobLevel);
1679 break;
1680 }
1681
1682 if (!jcr->fname) {
1683 jcr->fname = GetPoolMemory(PM_FNAME);
1684 }
1685 if (!jcr->res.pool_source) {
1686 jcr->res.pool_source = GetPoolMemory(PM_MESSAGE);
1687 PmStrcpy(jcr->res.pool_source, _("unknown source"));
1688 }
1689 if (!jcr->res.npool_source) {
1690 jcr->res.npool_source = GetPoolMemory(PM_MESSAGE);
1691 PmStrcpy(jcr->res.npool_source, _("unknown source"));
1692 }
1693 if (!jcr->res.catalog_source) {
1694 jcr->res.catalog_source = GetPoolMemory(PM_MESSAGE);
1695 PmStrcpy(jcr->res.catalog_source, _("unknown source"));
1696 }
1697
1698 jcr->JobPriority = job->Priority;
1699
1700 /*
1701 * Copy storage definitions -- deleted in dir_free_jcr above
1702 */
1703 if (job->storage) {
1704 CopyRwstorage(jcr, job->storage, _("Job resource"));
1705 } else if (job->pool) {
1706 CopyRwstorage(jcr, job->pool->storage, _("Pool resource"));
1707 }
1708 jcr->res.client = job->client;
1709
1710 if (jcr->res.client) {
1711 if (!jcr->client_name) {
1712 jcr->client_name = GetPoolMemory(PM_NAME);
1713 }
1714 PmStrcpy(jcr->client_name, jcr->res.client->hdr.name);
1715 }
1716
1717 PmStrcpy(jcr->res.pool_source, _("Job resource"));
1718 jcr->res.pool = job->pool;
1719 jcr->res.full_pool = job->full_pool;
1720 jcr->res.inc_pool = job->inc_pool;
1721 jcr->res.diff_pool = job->diff_pool;
1722
1723 if (job->pool && job->pool->catalog) {
1724 jcr->res.catalog = job->pool->catalog;
1725 PmStrcpy(jcr->res.catalog_source, _("Pool resource"));
1726 } else {
1727 if (job->catalog) {
1728 jcr->res.catalog = job->catalog;
1729 PmStrcpy(jcr->res.catalog_source, _("Job resource"));
1730 } else {
1731 if (job->client) {
1732 jcr->res.catalog = job->client->catalog;
1733 PmStrcpy(jcr->res.catalog_source, _("Client resource"));
1734 } else {
1735 jcr->res.catalog = (CatalogResource *)my_config->GetNextRes(R_CATALOG, NULL);
1736 PmStrcpy(jcr->res.catalog_source, _("Default catalog"));
1737 }
1738 }
1739 }
1740
1741 jcr->res.fileset = job->fileset;
1742 jcr->accurate = job->accurate;
1743 jcr->res.messages = job->messages;
1744 jcr->spool_data = job->spool_data;
1745 jcr->spool_size = job->spool_size;
1746 jcr->IgnoreDuplicateJobChecking = job->IgnoreDuplicateJobChecking;
1747 jcr->MaxRunSchedTime = job->MaxRunSchedTime;
1748
1749 if (jcr->backup_format) {
1750 free(jcr->backup_format);
1751 }
1752 jcr->backup_format = bstrdup(job->backup_format);
1753
1754 if (jcr->RestoreBootstrap) {
1755 free(jcr->RestoreBootstrap);
1756 jcr->RestoreBootstrap = NULL;
1757 }
1758
1759 /*
1760 * This can be overridden by Console program
1761 */
1762 if (job->RestoreBootstrap) {
1763 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
1764 }
1765
1766 /*
1767 * This can be overridden by Console program
1768 */
1769 jcr->res.verify_job = job->verify_job;
1770
1771 /*
1772 * If no default level given, set one
1773 */
1774 if (jcr->getJobLevel() == 0) {
1775 switch (jcr->getJobType()) {
1776 case JT_VERIFY:
1777 jcr->setJobLevel(L_VERIFY_CATALOG);
1778 break;
1779 case JT_BACKUP:
1780 jcr->setJobLevel(L_INCREMENTAL);
1781 break;
1782 case JT_RESTORE:
1783 case JT_ADMIN:
1784 jcr->setJobLevel(L_NONE);
1785 break;
1786 default:
1787 jcr->setJobLevel(L_FULL);
1788 break;
1789 }
1790 }
1791 }
1792
CreateClones(JobControlRecord * jcr)1793 void CreateClones(JobControlRecord *jcr)
1794 {
1795 /*
1796 * Fire off any clone jobs (run directives)
1797 */
1798 Dmsg2(900, "cloned=%d run_cmds=%p\n", jcr->cloned, jcr->res.job->run_cmds);
1799 if (!jcr->cloned && jcr->res.job->run_cmds) {
1800 char *runcmd = nullptr;
1801 JobId_t jobid;
1802 JobResource *job = jcr->res.job;
1803 POOLMEM *cmd = GetPoolMemory(PM_FNAME);
1804
1805 UaContext *ua = new_ua_context(jcr);
1806 ua->batch = true;
1807 foreach_alist(runcmd, job->run_cmds) {
1808 cmd = edit_job_codes(jcr, cmd, runcmd, "", job_code_callback_director);
1809 Mmsg(ua->cmd, "run %s cloned=yes", cmd);
1810 Dmsg1(900, "=============== Clone cmd=%s\n", ua->cmd);
1811 ParseUaArgs(ua); /* parse command */
1812
1813 jobid = DoRunCmd(ua, ua->cmd);
1814 if (!jobid) {
1815 Jmsg(jcr, M_ERROR, 0, _("Could not start clone job: \"%s\".\n"), ua->cmd);
1816 } else {
1817 Jmsg(jcr, M_INFO, 0, _("Clone JobId %d started.\n"), jobid);
1818 }
1819 }
1820 FreeUaContext(ua);
1821 FreePoolMemory(cmd);
1822 }
1823 }
1824
1825 /**
1826 * Given: a JobId in jcr->previous_jr.JobId,
1827 * this subroutine writes a bsr file to restore that job.
1828 * Returns: -1 on error
1829 * number of files if OK
1830 */
CreateRestoreBootstrapFile(JobControlRecord * jcr)1831 int CreateRestoreBootstrapFile(JobControlRecord *jcr)
1832 {
1833 RestoreContext rx;
1834 UaContext *ua;
1835 int files;
1836
1837 memset(&rx, 0, sizeof(rx));
1838 rx.bsr = new_bsr();
1839 rx.JobIds = (char *)"";
1840 rx.bsr->JobId = jcr->previous_jr.JobId;
1841 ua = new_ua_context(jcr);
1842 if (!CompleteBsr(ua, rx.bsr)) {
1843 files = -1;
1844 goto bail_out;
1845 }
1846 rx.bsr->fi = new_findex();
1847 rx.bsr->fi->findex = 1;
1848 rx.bsr->fi->findex2 = jcr->previous_jr.JobFiles;
1849 jcr->ExpectedFiles = WriteBsrFile(ua, rx);
1850 if (jcr->ExpectedFiles == 0) {
1851 files = 0;
1852 goto bail_out;
1853 }
1854 FreeUaContext(ua);
1855 directordaemon::FreeBsr(rx.bsr);
1856 jcr->needs_sd = true;
1857 return jcr->ExpectedFiles;
1858
1859 bail_out:
1860 FreeUaContext(ua);
1861 directordaemon::FreeBsr(rx.bsr);
1862 return files;
1863 }
1864
1865 /* TODO: redirect command ouput to job log */
RunConsoleCommand(JobControlRecord * jcr,const char * cmd)1866 bool RunConsoleCommand(JobControlRecord *jcr, const char *cmd)
1867 {
1868 UaContext *ua;
1869 bool ok;
1870 JobControlRecord *ljcr = new_control_jcr("-RunScript-", JT_CONSOLE);
1871 ua = new_ua_context(ljcr);
1872 /* run from runscript and check if commands are authorized */
1873 ua->runscript = true;
1874 Mmsg(ua->cmd, "%s", cmd);
1875 Dmsg1(100, "Console command: %s\n", ua->cmd);
1876 ParseUaArgs(ua);
1877 ok = Do_a_command(ua);
1878 FreeUaContext(ua);
1879 FreeJcr(ljcr);
1880 return ok;
1881 }
1882 } /* namespace directordaemon */
1883