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