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