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