2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
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 General Public License for more details.
18 You should have received a copy of the GNU 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
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director Job processing routines
32 * Kern Sibbald, October MM
40 /* Forward referenced subroutines */
41 static void *job_thread(void *arg);
42 static void job_monitor_watchdog(watchdog_t *self);
43 static void job_monitor_destructor(watchdog_t *self);
44 static bool job_check_maxwaittime(JCR *jcr);
45 static bool job_check_maxruntime(JCR *jcr);
46 static bool job_check_maxschedruntime(JCR *jcr);
48 /* Imported subroutines */
49 extern void term_scheduler();
50 extern void term_ua_server();
52 /* Imported variables */
56 void init_job_server(int max_workers)
61 if ((stat = jobq_init(&job_queue, max_workers, job_thread)) != 0) {
63 Emsg1(M_ABORT, 0, _("Could not init job queue: ERR=%s\n"), be.bstrerror(stat));
66 wd->callback = job_monitor_watchdog;
67 wd->destructor = job_monitor_destructor;
70 wd->data = new_control_jcr("*JobMonitor*", JT_SYSTEM);
71 register_watchdog(wd);
74 void term_job_server()
76 jobq_destroy(&job_queue); /* ignore any errors */
80 * Run a job -- typically called by the scheduler, but may also
81 * be called by the UA (Console program).
83 * Returns: 0 on failure
87 JobId_t run_job(JCR *jcr)
91 Dmsg0(200, "Add jrc to work queue\n");
92 /* Queue the job to be run */
93 if ((stat = jobq_add(&job_queue, jcr)) != 0) {
95 Jmsg(jcr, M_FATAL, 0, _("Could not add job queue: ERR=%s\n"), be.bstrerror(stat));
103 bool setup_job(JCR *jcr)
108 sm_check(__FILE__, __LINE__, true);
109 init_msg(jcr, jcr->messages);
111 /* Initialize termination condition variable */
112 if ((errstat = pthread_cond_init(&jcr->term_wait, NULL)) != 0) {
114 Jmsg1(jcr, M_FATAL, 0, _("Unable to init job cond variable: ERR=%s\n"), be.bstrerror(errstat));
118 jcr->term_wait_inited = true;
120 create_unique_job_name(jcr, jcr->job->name());
121 set_jcr_job_status(jcr, JS_Created);
127 Dmsg0(100, "Open database\n");
128 jcr->db=db_init(jcr, jcr->catalog->db_driver, jcr->catalog->db_name,
129 jcr->catalog->db_user,
130 jcr->catalog->db_password, jcr->catalog->db_address,
131 jcr->catalog->db_port, jcr->catalog->db_socket,
132 jcr->catalog->mult_db_connections);
133 if (!jcr->db || !db_open_database(jcr, jcr->db)) {
134 Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
135 jcr->catalog->db_name);
137 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
138 db_close_database(jcr, jcr->db);
142 Dmsg0(150, "DB opened\n");
145 jcr->fname = get_pool_memory(PM_FNAME);
147 if (!jcr->pool_source) {
148 jcr->pool_source = get_pool_memory(PM_MESSAGE);
149 pm_strcpy(jcr->pool_source, _("unknown source"));
152 if (jcr->JobReads()) {
153 if (!jcr->rpool_source) {
154 jcr->rpool_source = get_pool_memory(PM_MESSAGE);
155 pm_strcpy(jcr->rpool_source, _("unknown source"));
162 init_jcr_job_record(jcr);
163 if (!get_or_create_client_record(jcr)) {
167 if (!db_create_job_record(jcr, jcr->db, &jcr->jr)) {
168 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
171 jcr->JobId = jcr->jr.JobId;
172 Dmsg4(100, "Created job record JobId=%d Name=%s Type=%c Level=%c\n",
173 jcr->JobId, jcr->Job, jcr->jr.JobType, jcr->jr.JobLevel);
175 generate_daemon_event(jcr, "JobStart");
176 new_plugins(jcr); /* instantiate plugins for this jcr */
177 generate_plugin_event(jcr, bEventJobStart);
179 if (job_canceled(jcr)) {
183 if (jcr->JobReads() && !jcr->rstorage) {
184 if (jcr->job->storage) {
185 copy_rwstorage(jcr, jcr->job->storage, _("Job resource"));
187 copy_rwstorage(jcr, jcr->job->pool->storage, _("Pool resource"));
190 if (!jcr->JobReads()) {
195 * Now, do pre-run stuff, like setting job level (Inc/diff, ...)
196 * this allows us to setup a proper job start record for restarting
197 * in case of later errors.
199 switch (jcr->get_JobType()) {
201 if (!do_backup_init(jcr)) {
202 backup_cleanup(jcr, JS_ErrorTerminated);
206 if (!do_verify_init(jcr)) {
207 verify_cleanup(jcr, JS_ErrorTerminated);
211 if (!do_restore_init(jcr)) {
212 restore_cleanup(jcr, JS_ErrorTerminated);
216 if (!do_admin_init(jcr)) {
217 admin_cleanup(jcr, JS_ErrorTerminated);
222 if (!do_migration_init(jcr)) {
223 migration_cleanup(jcr, JS_ErrorTerminated);
227 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->get_JobType());
228 set_jcr_job_status(jcr, JS_ErrorTerminated);
232 generate_job_event(jcr, "JobInit");
233 generate_plugin_event(jcr, bEventJobInit);
241 void update_job_end(JCR *jcr, int TermCode)
243 dequeue_messages(jcr); /* display any queued messages */
244 set_jcr_job_status(jcr, TermCode);
245 update_job_end_record(jcr);
249 * This is the engine called by jobq.c:jobq_add() when we were pulled
250 * from the work queue.
251 * At this point, we are running in our own thread and all
252 * necessary resources are allocated -- see jobq.c
254 static void *job_thread(void *arg)
256 JCR *jcr = (JCR *)arg;
258 pthread_detach(pthread_self());
261 Dmsg0(200, "=====Start Job=========\n");
262 set_jcr_job_status(jcr, JS_Running); /* this will be set only if no error */
263 jcr->start_time = time(NULL); /* set the real start time */
264 jcr->jr.StartTime = jcr->start_time;
266 if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
267 (utime_t)(jcr->start_time - jcr->sched_time)) {
268 set_jcr_job_status(jcr, JS_Canceled);
269 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max start delay time exceeded.\n"));
272 if (job_check_maxschedruntime(jcr)) {
273 set_jcr_job_status(jcr, JS_Canceled);
274 Jmsg(jcr, M_FATAL, 0, _("Job canceled because max sched run time exceeded.\n"));
277 /* TODO : check if it is used somewhere */
278 if (jcr->job->RunScripts == NULL) {
279 Dmsg0(200, "Warning, job->RunScripts is empty\n");
280 jcr->job->RunScripts = New(alist(10, not_owned_by_alist));
283 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
284 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
287 /* Run any script BeforeJob on dird */
288 run_scripts(jcr, jcr->job->RunScripts, "BeforeJob");
291 * We re-update the job start record so that the start
292 * time is set after the run before job. This avoids
293 * that any files created by the run before job will
294 * be saved twice. They will be backed up in the current
295 * job, but not in the next one unless they are changed.
296 * Without this, they will be backed up in this job and
297 * in the next job run because in that case, their date
298 * is after the start of this run.
300 jcr->start_time = time(NULL);
301 jcr->jr.StartTime = jcr->start_time;
302 if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) {
303 Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db));
305 generate_job_event(jcr, "JobRun");
306 generate_plugin_event(jcr, bEventJobRun);
308 switch (jcr->get_JobType()) {
310 if (!job_canceled(jcr) && do_backup(jcr)) {
313 backup_cleanup(jcr, JS_ErrorTerminated);
317 if (!job_canceled(jcr) && do_verify(jcr)) {
320 verify_cleanup(jcr, JS_ErrorTerminated);
324 if (!job_canceled(jcr) && do_restore(jcr)) {
327 restore_cleanup(jcr, JS_ErrorTerminated);
331 if (!job_canceled(jcr) && do_admin(jcr)) {
334 admin_cleanup(jcr, JS_ErrorTerminated);
339 if (!job_canceled(jcr) && do_migration(jcr)) {
342 migration_cleanup(jcr, JS_ErrorTerminated);
346 Pmsg1(0, _("Unimplemented job type: %d\n"), jcr->get_JobType());
350 run_scripts(jcr, jcr->job->RunScripts, "AfterJob");
352 /* Send off any queued messages */
353 if (jcr->msg_queue && jcr->msg_queue->size() > 0) {
354 dequeue_messages(jcr);
357 generate_daemon_event(jcr, "JobEnd");
358 generate_plugin_event(jcr, bEventJobEnd);
359 Dmsg1(50, "======== End Job stat=%c ==========\n", jcr->JobStatus);
360 sm_check(__FILE__, __LINE__, true);
366 * Cancel a job -- typically called by the UA (Console program), but may also
367 * be called by the job watchdog.
369 * Returns: true if cancel appears to be successful
370 * false on failure. Message sent to ua->jcr.
372 bool cancel_job(UAContext *ua, JCR *jcr)
376 int32_t old_status = jcr->JobStatus;
378 set_jcr_job_status(jcr, JS_Canceled);
380 switch (old_status) {
383 case JS_WaitClientRes:
384 case JS_WaitStoreRes:
385 case JS_WaitPriority:
387 case JS_WaitStartTime:
388 ua->info_msg(_("JobId %s, Job %s marked to be canceled.\n"),
389 edit_uint64(jcr->JobId, ed1), jcr->Job);
390 jobq_remove(&job_queue, jcr); /* attempt to remove it from queue */
394 /* Cancel File daemon */
395 if (jcr->file_bsock) {
396 ua->jcr->client = jcr->client;
397 if (!connect_to_file_daemon(ua->jcr, 10, FDConnectTimeout, 1)) {
398 ua->error_msg(_("Failed to connect to File daemon.\n"));
401 Dmsg0(200, "Connected to file daemon\n");
402 fd = ua->jcr->file_bsock;
403 fd->fsend("cancel Job=%s\n", jcr->Job);
404 while (fd->recv() >= 0) {
405 ua->send_msg("%s", fd->msg);
407 fd->signal(BNET_TERMINATE);
409 ua->jcr->file_bsock = NULL;
412 /* Cancel Storage daemon */
413 if (jcr->store_bsock) {
414 if (!ua->jcr->wstorage) {
416 copy_wstorage(ua->jcr, jcr->rstorage, _("Job resource"));
418 copy_wstorage(ua->jcr, jcr->wstorage, _("Job resource"));
423 store.store = jcr->rstore;
425 store.store = jcr->wstore;
427 set_wstorage(ua->jcr, &store);
430 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
431 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
434 Dmsg0(200, "Connected to storage daemon\n");
435 sd = ua->jcr->store_bsock;
436 sd->fsend("cancel Job=%s\n", jcr->Job);
437 while (sd->recv() >= 0) {
438 ua->send_msg("%s", sd->msg);
440 sd->signal(BNET_TERMINATE);
442 ua->jcr->store_bsock = NULL;
449 void cancel_storage_daemon_job(JCR *jcr)
451 UAContext *ua = new_ua_context(jcr);
452 JCR *control_jcr = new_control_jcr("*JobCancel*", JT_SYSTEM);
455 ua->jcr = control_jcr;
456 if (jcr->store_bsock) {
457 if (!ua->jcr->wstorage) {
459 copy_wstorage(ua->jcr, jcr->rstorage, _("Job resource"));
461 copy_wstorage(ua->jcr, jcr->wstorage, _("Job resource"));
466 store.store = jcr->rstore;
468 store.store = jcr->wstore;
470 set_wstorage(ua->jcr, &store);
473 if (!connect_to_storage_daemon(ua->jcr, 10, SDConnectTimeout, 1)) {
476 Dmsg0(200, "Connected to storage daemon\n");
477 sd = ua->jcr->store_bsock;
478 sd->fsend("cancel Job=%s\n", jcr->Job);
479 while (sd->recv() >= 0) {
481 sd->signal(BNET_TERMINATE);
483 ua->jcr->store_bsock = NULL;
486 free_jcr(control_jcr);
490 static void job_monitor_destructor(watchdog_t *self)
492 JCR *control_jcr = (JCR *)self->data;
494 free_jcr(control_jcr);
497 static void job_monitor_watchdog(watchdog_t *self)
499 JCR *control_jcr, *jcr;
501 control_jcr = (JCR *)self->data;
504 Dmsg1(800, "job_monitor_watchdog %p called\n", self);
509 if (jcr->JobId == 0 || job_canceled(jcr)) {
510 Dmsg2(800, "Skipping JCR=%p Job=%s\n", jcr, jcr->Job);
514 /* check MaxWaitTime */
515 if (job_check_maxwaittime(jcr)) {
516 set_jcr_job_status(jcr, JS_Canceled);
517 Qmsg(jcr, M_FATAL, 0, _("Max wait time exceeded. Job canceled.\n"));
519 /* check MaxRunTime */
520 } else if (job_check_maxruntime(jcr)) {
521 set_jcr_job_status(jcr, JS_Canceled);
522 Qmsg(jcr, M_FATAL, 0, _("Max run time exceeded. Job canceled.\n"));
524 /* check MaxRunSchedTime */
525 } else if (job_check_maxschedruntime(jcr)) {
526 set_jcr_job_status(jcr, JS_Canceled);
527 Qmsg(jcr, M_FATAL, 0, _("Max sched run time exceeded. Job canceled.\n"));
532 Dmsg3(800, "Cancelling JCR %p jobid %d (%s)\n", jcr, jcr->JobId, jcr->Job);
533 UAContext *ua = new_ua_context(jcr);
534 ua->jcr = control_jcr;
537 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", jcr, jcr->JobId);
541 /* Keep reference counts correct */
546 * Check if the maxwaittime has expired and it is possible
549 static bool job_check_maxwaittime(JCR *jcr)
555 if (!job_waiting(jcr)) {
559 if (jcr->wait_time) {
560 current = watchdog_time - jcr->wait_time;
563 Dmsg2(200, "check maxwaittime %u >= %u\n",
564 current + jcr->wait_time_sum, job->MaxWaitTime);
565 if (job->MaxWaitTime != 0 &&
566 (current + jcr->wait_time_sum) >= job->MaxWaitTime) {
574 * Check if maxruntime has expired and if the job can be
577 static bool job_check_maxruntime(JCR *jcr)
583 if (job_canceled(jcr) || jcr->JobStatus == JS_Created) {
586 if (jcr->job->MaxRunTime == 0 && job->FullMaxRunTime == 0 &&
587 job->IncMaxRunTime == 0 && job->DiffMaxRunTime == 0) {
590 run_time = watchdog_time - jcr->start_time;
591 Dmsg7(200, "check_maxruntime %llu-%u=%llu >= %llu|%llu|%llu|%llu\n",
592 watchdog_time, jcr->start_time, run_time, job->MaxRunTime, job->FullMaxRunTime,
593 job->IncMaxRunTime, job->DiffMaxRunTime);
595 if (jcr->get_JobLevel() == L_FULL && job->FullMaxRunTime != 0 &&
596 run_time >= job->FullMaxRunTime) {
597 Dmsg0(200, "check_maxwaittime: FullMaxcancel\n");
599 } else if (jcr->get_JobLevel() == L_DIFFERENTIAL && job->DiffMaxRunTime != 0 &&
600 run_time >= job->DiffMaxRunTime) {
601 Dmsg0(200, "check_maxwaittime: DiffMaxcancel\n");
603 } else if (jcr->get_JobLevel() == L_INCREMENTAL && job->IncMaxRunTime != 0 &&
604 run_time >= job->IncMaxRunTime) {
605 Dmsg0(200, "check_maxwaittime: IncMaxcancel\n");
607 } else if (job->MaxRunTime > 0 && run_time >= job->MaxRunTime) {
608 Dmsg0(200, "check_maxwaittime: Maxcancel\n");
616 * Check if MaxRunSchedTime has expired and if the job can be
619 static bool job_check_maxschedruntime(JCR *jcr)
621 if (jcr->job->MaxRunSchedTime == 0 || job_canceled(jcr)) {
624 if ((watchdog_time - jcr->sched_time) < jcr->job->MaxRunSchedTime) {
625 Dmsg3(200, "Job %p (%s) with MaxRunSchedTime %d not expired\n",
626 jcr, jcr->Job, jcr->job->MaxRunSchedTime);
634 * Get or create a Pool record with the given name.
635 * Returns: 0 on error
638 DBId_t get_or_create_pool_record(JCR *jcr, char *pool_name)
642 memset(&pr, 0, sizeof(pr));
643 bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
644 Dmsg1(110, "get_or_create_pool=%s\n", pool_name);
646 while (!db_get_pool_record(jcr, jcr->db, &pr)) { /* get by Name */
647 /* Try to create the pool */
648 if (create_pool(jcr, jcr->db, jcr->pool, POOL_OP_CREATE) < 0) {
649 Jmsg(jcr, M_FATAL, 0, _("Pool \"%s\" not in database. ERR=%s"), pr.Name,
650 db_strerror(jcr->db));
653 Jmsg(jcr, M_INFO, 0, _("Created database record for Pool \"%s\".\n"), pr.Name);
660 * Check for duplicate jobs.
661 * Returns: true if current job should continue
662 * false if current job should terminate
664 bool allow_duplicate_job(JCR *jcr)
667 JCR *djcr; /* possible duplicate */
669 if (job->AllowDuplicateJobs) {
672 if (!job->AllowHigherDuplicates) {
675 if (strcmp(job->name(), djcr->job->name()) == 0) {
676 bool cancel_queued = false;
677 if (job->DuplicateJobProximity > 0) {
678 utime_t now = (utime_t)time(NULL);
679 if ((now - djcr->start_time) > job->DuplicateJobProximity) {
680 continue; /* not really a duplicate */
684 if (!(job->CancelQueuedDuplicates || job->CancelRunningDuplicates)) {
685 /* Zap current job */
686 Jmsg(jcr, M_FATAL, 0, _("Duplicate job not allowed. JobId=%s\n"),
687 edit_uint64(djcr->JobId, ec1));
690 /* If CancelQueuedDuplicates is set do so only if job is queued */
691 if (job->CancelQueuedDuplicates) {
692 switch (djcr->JobStatus) {
695 case JS_WaitClientRes:
696 case JS_WaitStoreRes:
697 case JS_WaitPriority:
699 case JS_WaitStartTime:
700 cancel_queued = true;
706 if (cancel_queued || job->CancelRunningDuplicates) {
707 UAContext *ua = new_ua_context(djcr);
708 Jmsg(jcr, M_INFO, 0, _("Cancelling duplicate JobId=%s.\n"),
709 edit_uint64(djcr->JobId, ec1));
711 cancel_job(ua, djcr);
713 Dmsg2(800, "Have cancelled JCR %p Job=%d\n", djcr, djcr->JobId);
722 void apply_pool_overrides(JCR *jcr)
724 bool pool_override = false;
726 if (jcr->run_pool_override) {
727 pm_strcpy(jcr->pool_source, _("Run pool override"));
730 * Apply any level related Pool selections
732 switch (jcr->get_JobLevel()) {
734 if (jcr->full_pool) {
735 jcr->pool = jcr->full_pool;
736 pool_override = true;
737 if (jcr->run_full_pool_override) {
738 pm_strcpy(jcr->pool_source, _("Run FullPool override"));
740 pm_strcpy(jcr->pool_source, _("Job FullPool override"));
746 jcr->pool = jcr->inc_pool;
747 pool_override = true;
748 if (jcr->run_inc_pool_override) {
749 pm_strcpy(jcr->pool_source, _("Run IncPool override"));
751 pm_strcpy(jcr->pool_source, _("Job IncPool override"));
756 if (jcr->diff_pool) {
757 jcr->pool = jcr->diff_pool;
758 pool_override = true;
759 if (jcr->run_diff_pool_override) {
760 pm_strcpy(jcr->pool_source, _("Run DiffPool override"));
762 pm_strcpy(jcr->pool_source, _("Job DiffPool override"));
767 /* Update catalog if pool overridden */
768 if (pool_override && jcr->pool->catalog) {
769 jcr->catalog = jcr->pool->catalog;
770 pm_strcpy(jcr->catalog_source, _("Pool resource"));
776 * Get or create a Client record for this Job
778 bool get_or_create_client_record(JCR *jcr)
782 memset(&cr, 0, sizeof(cr));
783 bstrncpy(cr.Name, jcr->client->hdr.name, sizeof(cr.Name));
784 cr.AutoPrune = jcr->client->AutoPrune;
785 cr.FileRetention = jcr->client->FileRetention;
786 cr.JobRetention = jcr->client->JobRetention;
787 if (!jcr->client_name) {
788 jcr->client_name = get_pool_memory(PM_NAME);
790 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
791 if (!db_create_client_record(jcr, jcr->db, &cr)) {
792 Jmsg(jcr, M_FATAL, 0, _("Could not create Client record. ERR=%s\n"),
793 db_strerror(jcr->db));
796 jcr->jr.ClientId = cr.ClientId;
798 if (!jcr->client_uname) {
799 jcr->client_uname = get_pool_memory(PM_NAME);
801 pm_strcpy(jcr->client_uname, cr.Uname);
803 Dmsg2(100, "Created Client %s record %d\n", jcr->client->hdr.name,
808 bool get_or_create_fileset_record(JCR *jcr)
812 * Get or Create FileSet record
814 memset(&fsr, 0, sizeof(FILESET_DBR));
815 bstrncpy(fsr.FileSet, jcr->fileset->hdr.name, sizeof(fsr.FileSet));
816 if (jcr->fileset->have_MD5) {
817 struct MD5Context md5c;
818 unsigned char digest[MD5HashSize];
819 memcpy(&md5c, &jcr->fileset->md5c, sizeof(md5c));
820 MD5Final(digest, &md5c);
822 * Keep the flag (last arg) set to false otherwise old FileSets will
823 * get new MD5 sums and the user will get Full backups on everything
825 bin_to_base64(fsr.MD5, sizeof(fsr.MD5), (char *)digest, MD5HashSize, false);
826 bstrncpy(jcr->fileset->MD5, fsr.MD5, sizeof(jcr->fileset->MD5));
828 Jmsg(jcr, M_WARNING, 0, _("FileSet MD5 digest not found.\n"));
830 if (!jcr->fileset->ignore_fs_changes ||
831 !db_get_fileset_record(jcr, jcr->db, &fsr)) {
832 if (!db_create_fileset_record(jcr, jcr->db, &fsr)) {
833 Jmsg(jcr, M_ERROR, 0, _("Could not create FileSet \"%s\" record. ERR=%s\n"),
834 fsr.FileSet, db_strerror(jcr->db));
838 jcr->jr.FileSetId = fsr.FileSetId;
839 bstrncpy(jcr->FSCreateTime, fsr.cCreateTime, sizeof(jcr->FSCreateTime));
840 Dmsg2(119, "Created FileSet %s record %u\n", jcr->fileset->hdr.name,
845 void init_jcr_job_record(JCR *jcr)
847 jcr->jr.SchedTime = jcr->sched_time;
848 jcr->jr.StartTime = jcr->start_time;
849 jcr->jr.EndTime = 0; /* perhaps rescheduled, clear it */
850 jcr->jr.JobType = jcr->get_JobType();
851 jcr->jr.JobLevel = jcr->get_JobLevel();
852 jcr->jr.JobStatus = jcr->JobStatus;
853 jcr->jr.JobId = jcr->JobId;
854 bstrncpy(jcr->jr.Name, jcr->job->name(), sizeof(jcr->jr.Name));
855 bstrncpy(jcr->jr.Job, jcr->Job, sizeof(jcr->jr.Job));
859 * Write status and such in DB
861 void update_job_end_record(JCR *jcr)
863 jcr->jr.EndTime = time(NULL);
864 jcr->end_time = jcr->jr.EndTime;
865 jcr->jr.JobId = jcr->JobId;
866 jcr->jr.JobStatus = jcr->JobStatus;
867 jcr->jr.JobFiles = jcr->JobFiles;
868 jcr->jr.JobBytes = jcr->JobBytes;
869 jcr->jr.ReadBytes = jcr->ReadBytes;
870 jcr->jr.VolSessionId = jcr->VolSessionId;
871 jcr->jr.VolSessionTime = jcr->VolSessionTime;
872 jcr->jr.JobErrors = jcr->JobErrors;
873 if (!db_update_job_end_record(jcr, jcr->db, &jcr->jr)) {
874 Jmsg(jcr, M_WARNING, 0, _("Error updating job record. %s"),
875 db_strerror(jcr->db));
880 * Takes base_name and appends (unique) current
881 * date and time to form unique job name.
883 * Note, the seconds are actually a sequence number. This
884 * permits us to start a maximum fo 59 unique jobs a second, which
885 * should be sufficient.
887 * Returns: unique job name in jcr->Job
888 * date/time in jcr->start_time
890 void create_unique_job_name(JCR *jcr, const char *base_name)
892 /* Job start mutex */
893 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
894 static time_t last_start_time = 0;
896 time_t now = time(NULL);
898 char dt[MAX_TIME_LENGTH];
899 char name[MAX_NAME_LENGTH];
903 /* Guarantee unique start time -- maximum one per second, and
904 * thus unique Job Name
906 P(mutex); /* lock creation of jobs */
908 if (seq > 59) { /* wrap as if it is seconds */
910 while (now == last_start_time) {
911 bmicrosleep(0, 500000);
915 last_start_time = now;
916 V(mutex); /* allow creation of jobs */
917 jcr->start_time = now;
918 /* Form Unique JobName */
919 (void)localtime_r(&now, &tm);
920 /* Use only characters that are permitted in Windows filenames */
921 strftime(dt, sizeof(dt), "%Y-%m-%d_%H.%M.%S", &tm);
922 len = strlen(dt) + 5; /* dt + .%02d EOS */
923 bstrncpy(name, base_name, sizeof(name));
924 name[sizeof(name)-len] = 0; /* truncate if too long */
925 bsnprintf(jcr->Job, sizeof(jcr->Job), "%s.%s_%02d", name, dt, seq); /* add date & time */
926 /* Convert spaces into underscores */
927 for (p=jcr->Job; *p; p++) {
932 Dmsg2(100, "JobId=%u created Job=%s\n", jcr->JobId, jcr->Job);
935 /* Called directly from job rescheduling */
936 void dird_free_jcr_pointers(JCR *jcr)
938 if (jcr->sd_auth_key) {
939 free(jcr->sd_auth_key);
940 jcr->sd_auth_key = NULL;
946 if (jcr->file_bsock) {
947 Dmsg0(200, "Close File bsock\n");
948 bnet_close(jcr->file_bsock);
949 jcr->file_bsock = NULL;
951 if (jcr->store_bsock) {
952 Dmsg0(200, "Close Store bsock\n");
953 bnet_close(jcr->store_bsock);
954 jcr->store_bsock = NULL;
957 Dmsg0(200, "Free JCR fname\n");
958 free_pool_memory(jcr->fname);
961 if (jcr->RestoreBootstrap) {
962 free(jcr->RestoreBootstrap);
963 jcr->RestoreBootstrap = NULL;
965 if (jcr->client_uname) {
966 free_pool_memory(jcr->client_uname);
967 jcr->client_uname = NULL;
970 free_pool_memory(jcr->attr);
980 * Free the Job Control Record if no one is still using it.
981 * Called from main free_jcr() routine in src/lib/jcr.c so
982 * that we can do our Director specific cleanup of the jcr.
984 void dird_free_jcr(JCR *jcr)
986 Dmsg0(200, "Start dird free_jcr\n");
988 dird_free_jcr_pointers(jcr);
989 if (jcr->term_wait_inited) {
990 pthread_cond_destroy(&jcr->term_wait);
991 jcr->term_wait_inited = false;
994 db_close_database(jcr, jcr->db_batch);
995 jcr->db_batch = NULL;
996 jcr->batch_started = false;
999 db_close_database(jcr, jcr->db);
1003 Dmsg0(200, "Free JCR stime\n");
1004 free_pool_memory(jcr->stime);
1008 Dmsg0(200, "Free JCR fname\n");
1009 free_pool_memory(jcr->fname);
1012 if (jcr->pool_source) {
1013 free_pool_memory(jcr->pool_source);
1014 jcr->pool_source = NULL;
1016 if (jcr->catalog_source) {
1017 free_pool_memory(jcr->catalog_source);
1018 jcr->catalog_source = NULL;
1020 if (jcr->rpool_source) {
1021 free_pool_memory(jcr->rpool_source);
1022 jcr->rpool_source = NULL;
1024 if (jcr->wstore_source) {
1025 free_pool_memory(jcr->wstore_source);
1026 jcr->wstore_source = NULL;
1028 if (jcr->rstore_source) {
1029 free_pool_memory(jcr->rstore_source);
1030 jcr->rstore_source = NULL;
1033 /* Delete lists setup to hold storage pointers */
1034 free_rwstorage(jcr);
1036 jcr->job_end_push.destroy();
1038 if (jcr->JobId != 0)
1039 write_state_file(director->working_directory, "bacula-dir", get_first_port_host_order(director->DIRaddrs));
1041 free_plugins(jcr); /* release instantiated plugins */
1043 Dmsg0(200, "End dird free_jcr\n");
1047 * The Job storage definition must be either in the Job record
1048 * or in the Pool record. The Pool record overrides the Job
1051 void get_job_storage(USTORE *store, JOB *job, RUN *run)
1053 if (run && run->pool && run->pool->storage) {
1054 store->store = (STORE *)run->pool->storage->first();
1055 pm_strcpy(store->store_source, _("Run pool override"));
1058 if (run && run->storage) {
1059 store->store = run->storage;
1060 pm_strcpy(store->store_source, _("Run storage override"));
1063 if (job->pool->storage) {
1064 store->store = (STORE *)job->pool->storage->first();
1065 pm_strcpy(store->store_source, _("Pool resource"));
1067 store->store = (STORE *)job->storage->first();
1068 pm_strcpy(store->store_source, _("Job resource"));
1073 * Set some defaults in the JCR necessary to
1074 * run. These items are pulled from the job
1075 * definition as defaults, but can be overridden
1076 * later either by the Run record in the Schedule resource,
1077 * or by the Console program.
1079 void set_jcr_defaults(JCR *jcr, JOB *job)
1082 jcr->set_JobType(job->JobType);
1083 jcr->JobStatus = JS_Created;
1085 switch (jcr->get_JobType()) {
1087 jcr->set_JobLevel(L_NONE);
1090 jcr->set_JobLevel(job->JobLevel);
1095 jcr->fname = get_pool_memory(PM_FNAME);
1097 if (!jcr->pool_source) {
1098 jcr->pool_source = get_pool_memory(PM_MESSAGE);
1099 pm_strcpy(jcr->pool_source, _("unknown source"));
1101 if (!jcr->catalog_source) {
1102 jcr->catalog_source = get_pool_memory(PM_MESSAGE);
1103 pm_strcpy(jcr->catalog_source, _("unknown source"));
1106 jcr->JobPriority = job->Priority;
1107 /* Copy storage definitions -- deleted in dir_free_jcr above */
1109 copy_rwstorage(jcr, job->storage, _("Job resource"));
1111 copy_rwstorage(jcr, job->pool->storage, _("Pool resource"));
1113 jcr->client = job->client;
1114 if (!jcr->client_name) {
1115 jcr->client_name = get_pool_memory(PM_NAME);
1117 pm_strcpy(jcr->client_name, jcr->client->hdr.name);
1118 pm_strcpy(jcr->pool_source, _("Job resource"));
1119 jcr->pool = job->pool;
1120 jcr->full_pool = job->full_pool;
1121 jcr->inc_pool = job->inc_pool;
1122 jcr->diff_pool = job->diff_pool;
1123 if (job->pool->catalog) {
1124 jcr->catalog = job->pool->catalog;
1125 pm_strcpy(jcr->catalog_source, _("Pool resource"));
1127 jcr->catalog = job->client->catalog;
1128 pm_strcpy(jcr->catalog_source, _("Client resource"));
1130 jcr->fileset = job->fileset;
1131 jcr->messages = job->messages;
1132 jcr->spool_data = job->spool_data;
1133 jcr->spool_size = job->spool_size;
1134 jcr->write_part_after_job = job->write_part_after_job;
1135 jcr->accurate = job->accurate;
1136 if (jcr->RestoreBootstrap) {
1137 free(jcr->RestoreBootstrap);
1138 jcr->RestoreBootstrap = NULL;
1140 /* This can be overridden by Console program */
1141 if (job->RestoreBootstrap) {
1142 jcr->RestoreBootstrap = bstrdup(job->RestoreBootstrap);
1144 /* This can be overridden by Console program */
1145 jcr->verify_job = job->verify_job;
1146 /* If no default level given, set one */
1147 if (jcr->get_JobLevel() == 0) {
1148 switch (jcr->get_JobType()) {
1150 jcr->set_JobLevel(L_VERIFY_CATALOG);
1153 jcr->set_JobLevel(L_INCREMENTAL);
1157 jcr->set_JobLevel(L_NONE);
1160 jcr->set_JobLevel(L_FULL);
1167 * Copy the storage definitions from an alist to the JCR
1169 void copy_rwstorage(JCR *jcr, alist *storage, const char *where)
1171 if (jcr->JobReads()) {
1172 copy_rstorage(jcr, storage, where);
1174 copy_wstorage(jcr, storage, where);
1178 /* Set storage override. Releases any previous storage definition */
1179 void set_rwstorage(JCR *jcr, USTORE *store)
1182 Jmsg(jcr, M_FATAL, 0, _("No storage specified.\n"));
1185 if (jcr->JobReads()) {
1186 set_rstorage(jcr, store);
1188 set_wstorage(jcr, store);
1191 void free_rwstorage(JCR *jcr)
1198 * Copy the storage definitions from an alist to the JCR
1200 void copy_rstorage(JCR *jcr, alist *storage, const char *where)
1204 if (jcr->rstorage) {
1205 delete jcr->rstorage;
1207 jcr->rstorage = New(alist(10, not_owned_by_alist));
1208 foreach_alist(st, storage) {
1209 jcr->rstorage->append(st);
1211 if (!jcr->rstore_source) {
1212 jcr->rstore_source = get_pool_memory(PM_MESSAGE);
1214 pm_strcpy(jcr->rstore_source, where);
1215 if (jcr->rstorage) {
1216 jcr->rstore = (STORE *)jcr->rstorage->first();
1222 /* Set storage override. Remove all previous storage */
1223 void set_rstorage(JCR *jcr, USTORE *store)
1227 if (!store->store) {
1230 if (jcr->rstorage) {
1233 if (!jcr->rstorage) {
1234 jcr->rstorage = New(alist(10, not_owned_by_alist));
1236 jcr->rstore = store->store;
1237 if (!jcr->rstore_source) {
1238 jcr->rstore_source = get_pool_memory(PM_MESSAGE);
1240 pm_strcpy(jcr->rstore_source, store->store_source);
1241 foreach_alist(storage, jcr->rstorage) {
1242 if (store->store == storage) {
1246 /* Store not in list, so add it */
1247 jcr->rstorage->prepend(store->store);
1250 void free_rstorage(JCR *jcr)
1252 if (jcr->rstorage) {
1253 delete jcr->rstorage;
1254 jcr->rstorage = NULL;
1260 * Copy the storage definitions from an alist to the JCR
1262 void copy_wstorage(JCR *jcr, alist *storage, const char *where)
1266 if (jcr->wstorage) {
1267 delete jcr->wstorage;
1269 jcr->wstorage = New(alist(10, not_owned_by_alist));
1270 foreach_alist(st, storage) {
1271 Dmsg1(100, "wstorage=%s\n", st->name());
1272 jcr->wstorage->append(st);
1274 if (!jcr->wstore_source) {
1275 jcr->wstore_source = get_pool_memory(PM_MESSAGE);
1277 pm_strcpy(jcr->wstore_source, where);
1278 if (jcr->wstorage) {
1279 jcr->wstore = (STORE *)jcr->wstorage->first();
1280 Dmsg2(100, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
1286 /* Set storage override. Remove all previous storage */
1287 void set_wstorage(JCR *jcr, USTORE *store)
1291 if (!store->store) {
1294 if (jcr->wstorage) {
1297 if (!jcr->wstorage) {
1298 jcr->wstorage = New(alist(10, not_owned_by_alist));
1300 jcr->wstore = store->store;
1301 if (!jcr->wstore_source) {
1302 jcr->wstore_source = get_pool_memory(PM_MESSAGE);
1304 pm_strcpy(jcr->wstore_source, store->store_source);
1305 Dmsg2(50, "wstore=%s where=%s\n", jcr->wstore->name(), jcr->wstore_source);
1306 foreach_alist(storage, jcr->wstorage) {
1307 if (store->store == storage) {
1311 /* Store not in list, so add it */
1312 jcr->wstorage->prepend(store->store);
1315 void free_wstorage(JCR *jcr)
1317 if (jcr->wstorage) {
1318 delete jcr->wstorage;
1319 jcr->wstorage = NULL;
1324 char *job_code_callback_clones(JCR *jcr, const char* param)
1326 if (param[0] == 'p') {
1327 return jcr->pool->name();
1332 void create_clones(JCR *jcr)
1335 * Fire off any clone jobs (run directives)
1337 Dmsg2(900, "cloned=%d run_cmds=%p\n", jcr->cloned, jcr->job->run_cmds);
1338 if (!jcr->cloned && jcr->job->run_cmds) {
1340 JOB *job = jcr->job;
1341 POOLMEM *cmd = get_pool_memory(PM_FNAME);
1342 UAContext *ua = new_ua_context(jcr);
1344 foreach_alist(runcmd, job->run_cmds) {
1345 cmd = edit_job_codes(jcr, cmd, runcmd, "", job_code_callback_clones);
1346 Mmsg(ua->cmd, "run %s cloned=yes", cmd);
1347 Dmsg1(900, "=============== Clone cmd=%s\n", ua->cmd);
1348 parse_ua_args(ua); /* parse command */
1349 int stat = run_cmd(ua, ua->cmd);
1351 Jmsg(jcr, M_ERROR, 0, _("Could not start clone job: \"%s\".\n"),
1354 Jmsg(jcr, M_INFO, 0, _("Clone JobId %d started.\n"), stat);
1357 free_ua_context(ua);
1358 free_pool_memory(cmd);
1363 * Given: a JobId in jcr->previous_jr.JobId,
1364 * this subroutine writes a bsr file to restore that job.
1366 bool create_restore_bootstrap_file(JCR *jcr)
1370 memset(&rx, 0, sizeof(rx));
1372 rx.JobIds = (char *)"";
1373 rx.bsr->JobId = jcr->previous_jr.JobId;
1374 ua = new_ua_context(jcr);
1375 if (!complete_bsr(ua, rx.bsr)) {
1378 rx.bsr->fi = new_findex();
1379 rx.bsr->fi->findex = 1;
1380 rx.bsr->fi->findex2 = jcr->previous_jr.JobFiles;
1381 jcr->ExpectedFiles = write_bsr_file(ua, rx);
1382 if (jcr->ExpectedFiles == 0) {
1385 free_ua_context(ua);
1387 jcr->needs_sd = true;
1391 free_ua_context(ua);
1396 /* TODO: redirect command ouput to job log */
1397 bool run_console_command(JCR *jcr, const char *cmd)
1401 JCR *ljcr = new_control_jcr("-RunScript-", JT_CONSOLE);
1402 ua = new_ua_context(ljcr);
1403 /* run from runscript and check if commands are autorized */
1404 ua->runscript = true;
1405 Mmsg(ua->cmd, "%s", cmd);
1406 Dmsg1(100, "Console command: %s\n", ua->cmd);
1408 ok= do_a_command(ua);
1409 free_ua_context(ua);