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.
29 * Manipulation routines for Job Control Records and
30 * handling of last_jobs_list.
32 * Kern E. Sibbald, December 2000
36 * These routines are thread safe.
38 * The job list routines were re-written in May 2005 to
39 * eliminate the global lock while traversing the list, and
40 * to use the dlist subroutines. The locking is now done
41 * on the list each time the list is modified or traversed.
42 * That is it is "micro-locked" rather than globally locked.
43 * The result is that there is one lock/unlock for each entry
44 * in the list while traversing it rather than a single lock
45 * at the beginning of a traversal and one at the end. This
46 * incurs slightly more overhead, but effectively eliminates
47 * the possibilty of race conditions. In addition, with the
48 * exception of the global locking of the list during the
49 * re-reading of the config file, no recursion is needed.
56 const int dbglvl = 3400;
58 /* External variables we reference */
60 /* External referenced functions */
61 void free_bregexps(alist *bregexps);
63 /* Forward referenced functions */
64 extern "C" void timeout_handler(int sig);
65 static void jcr_timeout_check(watchdog_t *self);
66 #ifdef TRACE_JCR_CHAIN
67 static void b_lock_jcr_chain(const char *filen, int line);
68 static void b_unlock_jcr_chain(const char *filen, int line);
69 #define lock_jcr_chain() b_lock_jcr_chain(__FILE__, __LINE__);
70 #define unlock_jcr_chain() b_unlock_jcr_chain(__FILE__, __LINE__);
72 static void lock_jcr_chain();
73 static void unlock_jcr_chain();
78 dlist *last_jobs = NULL;
79 const int max_last_jobs = 10;
81 static dlist *jcrs = NULL; /* JCR chain */
82 static pthread_mutex_t jcr_lock = PTHREAD_MUTEX_INITIALIZER;
84 static pthread_mutex_t job_start_mutex = PTHREAD_MUTEX_INITIALIZER;
86 static pthread_mutex_t last_jobs_mutex = PTHREAD_MUTEX_INITIALIZER;
88 static pthread_key_t jcr_key; /* Pointer to jcr for each thread */
90 pthread_once_t key_once = PTHREAD_ONCE_INIT;
103 void init_last_jobs_list()
106 struct s_last_job *job_entry = NULL;
108 last_jobs = New(dlist(job_entry, &job_entry->link));
111 jcrs = New(dlist(jcr, &jcr->link));
115 void term_last_jobs_list()
118 lock_last_jobs_list();
119 while (!last_jobs->empty()) {
120 void *je = last_jobs->first();
121 last_jobs->remove(je);
126 unlock_last_jobs_list();
134 bool read_last_jobs_list(int fd, uint64_t addr)
136 struct s_last_job *je, job;
140 Dmsg1(100, "read_last_jobs seek to %d\n", (int)addr);
141 if (addr == 0 || lseek(fd, (boffset_t)addr, SEEK_SET) < 0) {
144 if (read(fd, &num, sizeof(num)) != sizeof(num)) {
147 Dmsg1(100, "Read num_items=%d\n", num);
148 if (num > 4 * max_last_jobs) { /* sanity check */
151 lock_last_jobs_list();
152 for ( ; num; num--) {
153 if (read(fd, &job, sizeof(job)) != sizeof(job)) {
155 Pmsg1(000, "Read job entry. ERR=%s\n", be.bstrerror());
160 je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
161 memcpy((char *)je, (char *)&job, sizeof(job));
163 init_last_jobs_list();
165 last_jobs->append(je);
166 if (last_jobs->size() > max_last_jobs) {
167 je = (struct s_last_job *)last_jobs->first();
168 last_jobs->remove(je);
173 unlock_last_jobs_list();
177 uint64_t write_last_jobs_list(int fd, uint64_t addr)
179 struct s_last_job *je;
183 Dmsg1(100, "write_last_jobs seek to %d\n", (int)addr);
184 if (lseek(fd, (boffset_t)addr, SEEK_SET) < 0) {
188 lock_last_jobs_list();
189 /* First record is number of entires */
190 num = last_jobs->size();
191 if (write(fd, &num, sizeof(num)) != sizeof(num)) {
193 Pmsg1(000, "Error writing num_items: ERR=%s\n", be.bstrerror());
196 foreach_dlist(je, last_jobs) {
197 if (write(fd, je, sizeof(struct s_last_job)) != sizeof(struct s_last_job)) {
199 Pmsg1(000, "Error writing job: ERR=%s\n", be.bstrerror());
203 unlock_last_jobs_list();
205 /* Return current address */
206 stat = lseek(fd, 0, SEEK_CUR);
213 unlock_last_jobs_list();
217 void lock_last_jobs_list()
222 void unlock_last_jobs_list()
227 /* Get an ASCII representation of the Operation being performed as an english Noun */
228 const char *JCR::get_OperationName()
234 return _("Verifying");
236 return _("Restoring");
238 return _("Archiving");
242 return _("Migration");
244 return _("Scanning");
246 return _("Unknown operation");
250 /* Get an ASCII representation of the Action being performed either an english Verb or Adjective */
251 const char *JCR::get_ActionName(bool past)
257 return (past == true) ? _("verified") : _("verify");
259 return (past == true) ? _("restored") : _("restore");
261 return (past == true) ? _("archived") : _("archive");
263 return (past == true) ? _("copied") : _("copy");
265 return (past == true) ? _("migrated") : _("migrate");
267 return (past == true) ? _("scanned") : _("scan");
269 return _("unknown action");
282 if (m_JobLevel == L_VIRTUAL_FULL) {
293 * Push a subroutine address into the job end callback stack
295 void job_end_push(JCR *jcr, void job_end_cb(JCR *jcr,void *), void *ctx)
297 jcr->job_end_push.append((void *)job_end_cb);
298 jcr->job_end_push.append(ctx);
301 /* Pop each job_end subroutine and call it */
302 static void job_end_pop(JCR *jcr)
304 void (*job_end_cb)(JCR *jcr, void *ctx);
306 for (int i=jcr->job_end_push.size()-1; i > 0; ) {
307 ctx = jcr->job_end_push.get(i--);
308 job_end_cb = (void (*)(JCR *,void *))jcr->job_end_push.get(i--);
309 job_end_cb(jcr, ctx);
314 * Create thread key for thread specific data
316 void create_jcr_key()
318 int status = pthread_key_create(&jcr_key, NULL);
321 Jmsg1(NULL, M_ABORT, 0, _("pthread key create failed: ERR=%s\n"),
322 be.bstrerror(status));
327 * Create a Job Control Record and link it into JCR chain
328 * Returns newly allocated JCR
329 * Note, since each daemon has a different JCR, he passes
332 JCR *new_jcr(int size, JCR_free_HANDLER *daemon_free_jcr)
335 MQUEUE_ITEM *item = NULL;
336 struct sigaction sigtimer;
339 Dmsg0(dbglvl, "Enter new_jcr\n");
340 status = pthread_once(&key_once, create_jcr_key);
343 Jmsg1(NULL, M_ABORT, 0, _("pthread_once failed. ERR=%s\n"), be.bstrerror(status));
345 jcr = (JCR *)malloc(size);
346 memset(jcr, 0, size);
347 jcr->my_thread_id = pthread_self();
348 jcr->msg_queue = New(dlist(item, &item->link));
349 if ((status = pthread_mutex_init(&jcr->msg_queue_mutex, NULL)) != 0) {
351 Jmsg(NULL, M_ABORT, 0, _("Could not init msg_queue mutex. ERR=%s\n"),
352 be.bstrerror(status));
354 jcr->job_end_push.init(1, false);
355 jcr->sched_time = time(NULL);
356 jcr->daemon_free_jcr = daemon_free_jcr; /* plug daemon free routine */
358 jcr->inc_use_count();
359 jcr->VolumeName = get_pool_memory(PM_FNAME);
360 jcr->VolumeName[0] = 0;
361 jcr->errmsg = get_pool_memory(PM_MESSAGE);
363 /* Setup some dummy values */
364 bstrncpy(jcr->Job, "*System*", sizeof(jcr->Job));
366 jcr->setJobType(JT_SYSTEM); /* internal job until defined */
367 jcr->setJobLevel(L_NONE);
368 jcr->setJobStatus(JS_Created); /* ready to run */
370 sigtimer.sa_flags = 0;
371 sigtimer.sa_handler = timeout_handler;
372 sigfillset(&sigtimer.sa_mask);
373 sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL);
376 * Locking jobs is a global lock that is needed
377 * so that the Director can stop new jobs from being
378 * added to the jcr chain while it processes a new
379 * conf file and does the job_end_push().
384 jcrs = New(dlist(jcr, &jcr->link));
395 * Remove a JCR from the chain
396 * NOTE! The chain must be locked prior to calling
399 static void remove_jcr(JCR *jcr)
401 Dmsg0(dbglvl, "Enter remove_jcr\n");
403 Emsg0(M_ABORT, 0, _("NULL jcr.\n"));
406 Dmsg0(dbglvl, "Leave remove_jcr\n");
410 * Free stuff common to all JCRs. N.B. Be careful to include only
411 * generic stuff in the common part of the jcr.
413 static void free_common_jcr(JCR *jcr)
415 jcr->destroy_mutex();
417 if (jcr->msg_queue) {
418 delete jcr->msg_queue;
419 jcr->msg_queue = NULL;
420 pthread_mutex_destroy(&jcr->msg_queue_mutex);
422 close_msg(jcr); /* close messages for this job */
424 /* do this after closing messages */
425 if (jcr->client_name) {
426 free_pool_memory(jcr->client_name);
427 jcr->client_name = NULL;
431 free_pool_memory(jcr->attr);
435 if (jcr->sd_auth_key) {
436 free(jcr->sd_auth_key);
437 jcr->sd_auth_key = NULL;
439 if (jcr->VolumeName) {
440 free_pool_memory(jcr->VolumeName);
441 jcr->VolumeName = NULL;
444 if (jcr->dir_bsock) {
445 bnet_close(jcr->dir_bsock);
446 jcr->dir_bsock = NULL;
449 free_pool_memory(jcr->errmsg);
456 if (jcr->RegexWhere) {
457 free(jcr->RegexWhere);
458 jcr->RegexWhere = NULL;
460 if (jcr->where_bregexp) {
461 free_bregexps(jcr->where_bregexp);
462 delete jcr->where_bregexp;
463 jcr->where_bregexp = NULL;
465 if (jcr->cached_path) {
466 free_pool_memory(jcr->cached_path);
467 jcr->cached_path = NULL;
471 free_guid_list(jcr->id_list);
474 remove_jcr_from_tsd(jcr);
479 * Global routine to free a jcr
482 void b_free_jcr(const char *file, int line, JCR *jcr)
484 struct s_last_job *je;
486 Dmsg3(dbglvl, "Enter free_jcr jid=%u from %s:%d\n", jcr->JobId, file, line);
490 void free_jcr(JCR *jcr)
492 struct s_last_job *je;
494 Dmsg3(dbglvl, "Enter free_jcr jid=%u use_count=%d Job=%s\n",
495 jcr->JobId, jcr->use_count(), jcr->Job);
500 jcr->dec_use_count(); /* decrement use count */
501 if (jcr->use_count() < 0) {
502 Jmsg2(jcr, M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
503 jcr->use_count(), jcr->JobId);
505 if (jcr->JobId > 0) {
506 Dmsg3(dbglvl, "Dec free_jcr jid=%u use_count=%d Job=%s\n",
507 jcr->JobId, jcr->use_count(), jcr->Job);
509 if (jcr->use_count() > 0) { /* if in use */
513 if (jcr->JobId > 0) {
514 Dmsg3(dbglvl, "remove jcr jid=%u use_count=%d Job=%s\n",
515 jcr->JobId, jcr->use_count(), jcr->Job);
517 remove_jcr(jcr); /* remove Jcr from chain */
520 dequeue_messages(jcr);
521 job_end_pop(jcr); /* pop and call hooked routines */
523 Dmsg1(dbglvl, "End job=%d\n", jcr->JobId);
525 /* Keep some statistics */
526 switch (jcr->get_JobType()) {
533 /* Keep list of last jobs, but not Console where JobId==0 */
534 if (jcr->JobId > 0) {
535 lock_last_jobs_list();
537 je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
538 memset(je, 0, sizeof(struct s_last_job)); /* zero in case unset fields */
539 je->Errors = jcr->JobErrors;
540 je->JobType = jcr->get_JobType();
541 je->JobId = jcr->JobId;
542 je->VolSessionId = jcr->VolSessionId;
543 je->VolSessionTime = jcr->VolSessionTime;
544 bstrncpy(je->Job, jcr->Job, sizeof(je->Job));
545 je->JobFiles = jcr->JobFiles;
546 je->JobBytes = jcr->JobBytes;
547 je->JobStatus = jcr->JobStatus;
548 je->JobLevel = jcr->get_JobLevel();
549 je->start_time = jcr->start_time;
550 je->end_time = time(NULL);
553 init_last_jobs_list();
555 last_jobs->append(je);
556 if (last_jobs->size() > max_last_jobs) {
557 je = (struct s_last_job *)last_jobs->first();
558 last_jobs->remove(je);
561 unlock_last_jobs_list();
568 if (jcr->daemon_free_jcr) {
569 jcr->daemon_free_jcr(jcr); /* call daemon free routine */
572 free_common_jcr(jcr);
573 close_msg(NULL); /* flush any daemon messages */
574 garbage_collect_memory_pool();
575 Dmsg0(dbglvl, "Exit free_jcr\n");
579 * Remove jcr from thread specific data, but
580 * but make sure it is us who are attached.
582 void remove_jcr_from_tsd(JCR *jcr)
584 JCR *tjcr = get_jcr_from_tsd();
586 set_jcr_in_tsd(INVALID_JCR);
591 * Put this jcr in the thread specifc data
593 void set_jcr_in_tsd(JCR *jcr)
595 int status = pthread_setspecific(jcr_key, (void *)jcr);
598 Jmsg1(jcr, M_ABORT, 0, _("pthread_setspecific failed: ERR=%s\n"), be.bstrerror(status));
603 * Give me the jcr that is attached to this thread
605 JCR *get_jcr_from_tsd()
607 JCR *jcr = (JCR *)pthread_getspecific(jcr_key);
608 // printf("get_jcr_from_tsd: jcr=%p\n", jcr);
609 /* set any INVALID_JCR to NULL which the rest of Bacula understands */
610 if (jcr == INVALID_JCR) {
618 * Find which JobId corresponds to the current thread
620 uint32_t get_jobid_from_tsd()
624 jcr = get_jcr_from_tsd();
625 // printf("get_jobid_from_tsr: jcr=%p\n", jcr);
627 JobId = (uint32_t)jcr->JobId;
633 * Given a JobId, find the JCR
634 * Returns: jcr on success
637 JCR *get_jcr_by_id(uint32_t JobId)
642 if (jcr->JobId == JobId) {
643 jcr->inc_use_count();
644 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
645 jcr->JobId, jcr->use_count(), jcr->Job);
654 * Given a thread id, find the JobId
655 * Returns: JobId on success
658 uint32_t get_jobid_from_tid(pthread_t tid)
664 if (pthread_equal(jcr->my_thread_id, tid)) {
678 * Given a SessionId and SessionTime, find the JCR
679 * Returns: jcr on success
682 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
687 if (jcr->VolSessionId == SessionId &&
688 jcr->VolSessionTime == SessionTime) {
689 jcr->inc_use_count();
690 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
691 jcr->JobId, jcr->use_count(), jcr->Job);
701 * Given a Job, find the JCR
702 * compares on the number of characters in Job
703 * thus allowing partial matches.
704 * Returns: jcr on success
707 JCR *get_jcr_by_partial_name(char *Job)
717 if (strncmp(Job, jcr->Job, len) == 0) {
718 jcr->inc_use_count();
719 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
720 jcr->JobId, jcr->use_count(), jcr->Job);
730 * Given a Job, find the JCR
731 * requires an exact match of names.
732 * Returns: jcr on success
735 JCR *get_jcr_by_full_name(char *Job)
743 if (strcmp(jcr->Job, Job) == 0) {
744 jcr->inc_use_count();
745 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
746 jcr->JobId, jcr->use_count(), jcr->Job);
754 static void update_wait_time(JCR *jcr, int newJobStatus)
756 bool enter_in_waittime;
757 int oldJobStatus = jcr->JobStatus;
759 switch (newJobStatus) {
764 case JS_WaitStoreRes:
766 case JS_WaitClientRes:
768 case JS_WaitPriority:
769 enter_in_waittime = true;
772 enter_in_waittime = false; /* not a Wait situation */
777 * If we were previously waiting and are not any more
778 * we want to update the wait_time variable, which is
779 * the start of waiting.
781 switch (oldJobStatus) {
786 case JS_WaitStoreRes:
788 case JS_WaitClientRes:
790 case JS_WaitPriority:
791 if (!enter_in_waittime) { /* we get out the wait time */
792 jcr->wait_time_sum += (time(NULL) - jcr->wait_time);
797 /* if wait state is new, we keep current time for watchdog MaxWaitTime */
799 if (enter_in_waittime) {
800 jcr->wait_time = time(NULL);
807 * Priority runs from 0 (lowest) to 10 (highest)
809 static int get_status_priority(int JobStatus)
813 case JS_ErrorTerminated:
830 void set_jcr_job_status(JCR *jcr, int JobStatus)
832 jcr->setJobStatus(JobStatus);
835 void JCR::setJobStatus(int newJobStatus)
838 int priority, old_priority;
839 int oldJobStatus = jcr->JobStatus;
840 priority = get_status_priority(newJobStatus);
841 old_priority = get_status_priority(oldJobStatus);
843 Dmsg2(800, "set_jcr_job_status(%s, %c)\n", Job, newJobStatus);
845 /* Update wait_time depending on newJobStatus and oldJobStatus */
846 update_wait_time(jcr, newJobStatus);
849 * For a set of errors, ... keep the current status
850 * so it isn't lost. For all others, set it.
852 Dmsg2(800, "OnEntry JobStatus=%c newJobstatus=%c\n", oldJobStatus, newJobStatus);
854 * If status priority is > than proposed new status, change it.
855 * If status priority == new priority and both are zero, take
857 * If it is not zero, then we keep the first non-zero "error" that
860 if (priority > old_priority || (
861 priority == 0 && old_priority == 0)) {
862 Dmsg4(800, "Set new stat. old: %c,%d new: %c,%d\n",
863 jcr->JobStatus, old_priority, newJobStatus, priority);
864 jcr->JobStatus = newJobStatus; /* replace with new status */
867 if (oldJobStatus != jcr->JobStatus) {
868 Dmsg2(800, "leave set_job_status old=%c new=%c\n", oldJobStatus, newJobStatus);
869 // generate_plugin_event(jcr, bEventStatusChange, NULL);
873 #ifdef TRACE_JCR_CHAIN
874 static int lock_count = 0;
880 #ifdef TRACE_JCR_CHAIN
881 static void b_lock_jcr_chain(const char *fname, int line)
883 static void lock_jcr_chain()
886 #ifdef TRACE_JCR_CHAIN
887 Dmsg3(dbglvl, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
895 #ifdef TRACE_JCR_CHAIN
896 static void b_unlock_jcr_chain(const char *fname, int line)
898 static void unlock_jcr_chain()
901 #ifdef TRACE_JCR_CHAIN
902 Dmsg3(dbglvl, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
908 * Start walk of jcr chain
909 * The proper way to walk the jcr chain is:
916 * It is possible to leave out the endeach_jcr(jcr), but
917 * in that case, the last jcr referenced must be explicitly
923 JCR *jcr_walk_start()
927 jcr = (JCR *)jcrs->first();
929 jcr->inc_use_count();
930 if (jcr->JobId > 0) {
931 Dmsg3(dbglvl, "Inc walk_start jid=%u use_count=%d Job=%s\n",
932 jcr->JobId, jcr->use_count(), jcr->Job);
940 * Get next jcr from chain, and release current one
942 JCR *jcr_walk_next(JCR *prev_jcr)
947 jcr = (JCR *)jcrs->next(prev_jcr);
949 jcr->inc_use_count();
950 if (jcr->JobId > 0) {
951 Dmsg3(dbglvl, "Inc walk_next jid=%u use_count=%d Job=%s\n",
952 jcr->JobId, jcr->use_count(), jcr->Job);
963 * Release last jcr referenced
965 void jcr_walk_end(JCR *jcr)
968 if (jcr->JobId > 0) {
969 Dmsg3(dbglvl, "Free walk_end jid=%u use_count=%d Job=%s\n",
970 jcr->JobId, jcr->use_count(), jcr->Job);
978 * Setup to call the timeout check routine every 30 seconds
979 * This routine will check any timers that have been enabled.
981 bool init_jcr_subsystem(void)
983 watchdog_t *wd = new_watchdog();
985 wd->one_shot = false;
986 wd->interval = 30; /* FIXME: should be configurable somewhere, even
987 if only with a #define */
988 wd->callback = jcr_timeout_check;
990 register_watchdog(wd);
995 static void jcr_timeout_check(watchdog_t *self)
1001 Dmsg0(dbglvl, "Start JCR timeout checks\n");
1003 /* Walk through all JCRs checking if any one is
1004 * blocked for more than specified max time.
1007 Dmsg2(dbglvl, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
1008 if (jcr->JobId == 0) {
1011 bs = jcr->store_bsock;
1013 timer_start = bs->timer_start;
1014 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1015 bs->timer_start = 0; /* turn off timer */
1016 bs->set_timed_out();
1017 Qmsg(jcr, M_ERROR, 0, _(
1018 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
1019 watchdog_time - timer_start);
1020 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1023 bs = jcr->file_bsock;
1025 timer_start = bs->timer_start;
1026 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1027 bs->timer_start = 0; /* turn off timer */
1028 bs->set_timed_out();
1029 Qmsg(jcr, M_ERROR, 0, _(
1030 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
1031 watchdog_time - timer_start);
1032 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1035 bs = jcr->dir_bsock;
1037 timer_start = bs->timer_start;
1038 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1039 bs->timer_start = 0; /* turn off timer */
1040 bs->set_timed_out();
1041 Qmsg(jcr, M_ERROR, 0, _(
1042 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
1043 watchdog_time - timer_start);
1044 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1050 Dmsg0(dbglvl, "Finished JCR timeout checks\n");
1054 * Return next JobId from comma separated list
1057 * 1 if next JobId returned
1058 * 0 if no more JobIds are in list
1059 * -1 there is an error
1061 int get_next_jobid_from_list(char **p, uint32_t *JobId)
1063 const int maxlen = 30;
1064 char jobid[maxlen+1];
1068 for (int i=0; i<maxlen; i++) {
1071 } else if (*q == ',') {
1078 if (jobid[0] == 0) {
1080 } else if (!is_a_number(jobid)) {
1081 return -1; /* error */
1084 *JobId = str_to_int64(jobid);
1089 * Timeout signal comes here
1091 extern "C" void timeout_handler(int sig)
1093 return; /* thus interrupting the function */
1096 /* Used to display specific daemon information after a fatal signal
1097 * (like B_DB in the director)
1099 #define MAX_DBG_HOOK 10
1100 static dbg_jcr_hook_t *dbg_jcr_hooks[MAX_DBG_HOOK];
1101 static int dbg_jcr_handler_count;
1103 void dbg_jcr_add_hook(dbg_jcr_hook_t *hook)
1105 ASSERT(dbg_jcr_handler_count < MAX_DBG_HOOK);
1106 dbg_jcr_hooks[dbg_jcr_handler_count++] = hook;
1112 * This function should be used ONLY after a fatal signal. We walk through the
1113 * JCR chain without doing any lock, Bacula should not be running.
1115 void dbg_print_jcr(FILE *fp)
1117 char buf1[128], buf2[128], buf3[128], buf4[128];
1122 fprintf(fp, "Attempt to dump current JCRs\n");
1124 for (JCR *jcr = (JCR *)jcrs->first(); jcr ; jcr = (JCR *)jcrs->next(jcr)) {
1125 fprintf(fp, "JCR=%p JobId=%d name=%s JobStatus=%c\n",
1126 jcr, (int)jcr->JobId, jcr->Job, jcr->JobStatus);
1127 fprintf(fp, "\tuse_count=%i\n", jcr->use_count());
1128 fprintf(fp, "\tJobType=%c JobLevel=%c\n",
1129 jcr->get_JobType(), jcr->get_JobLevel());
1130 bstrftime(buf1, sizeof(buf1), jcr->sched_time);
1131 bstrftime(buf2, sizeof(buf2), jcr->start_time);
1132 bstrftime(buf3, sizeof(buf3), jcr->end_time);
1133 bstrftime(buf4, sizeof(buf4), jcr->wait_time);
1134 fprintf(fp, "\tsched_time=%s start_time=%s\n\tend_time=%s wait_time=%s\n",
1135 buf1, buf2, buf3, buf4);
1136 fprintf(fp, "\tdb=%p db_batch=%p batch_started=%i\n",
1137 jcr->db, jcr->db_batch, jcr->batch_started);
1140 * Call all the jcr debug hooks
1142 for(int i=0; i < dbg_jcr_handler_count; i++) {
1143 dbg_jcr_hook_t *hook = dbg_jcr_hooks[i];