2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2010 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
34 * These routines are thread safe.
36 * The job list routines were re-written in May 2005 to
37 * eliminate the global lock while traversing the list, and
38 * to use the dlist subroutines. The locking is now done
39 * on the list each time the list is modified or traversed.
40 * That is it is "micro-locked" rather than globally locked.
41 * The result is that there is one lock/unlock for each entry
42 * in the list while traversing it rather than a single lock
43 * at the beginning of a traversal and one at the end. This
44 * incurs slightly more overhead, but effectively eliminates
45 * the possibilty of race conditions. In addition, with the
46 * exception of the global locking of the list during the
47 * re-reading of the config file, no recursion is needed.
54 const int dbglvl = 3400;
56 /* External variables we reference */
58 /* External referenced functions */
59 void free_bregexps(alist *bregexps);
61 /* Forward referenced functions */
62 extern "C" void timeout_handler(int sig);
63 static void jcr_timeout_check(watchdog_t *self);
64 #ifdef TRACE_JCR_CHAIN
65 static void b_lock_jcr_chain(const char *filen, int line);
66 static void b_unlock_jcr_chain(const char *filen, int line);
67 #define lock_jcr_chain() b_lock_jcr_chain(__FILE__, __LINE__);
68 #define unlock_jcr_chain() b_unlock_jcr_chain(__FILE__, __LINE__);
70 static void lock_jcr_chain();
71 static void unlock_jcr_chain();
76 dlist *last_jobs = NULL;
77 const int max_last_jobs = 10;
79 static dlist *jcrs = NULL; /* JCR chain */
80 static pthread_mutex_t jcr_lock = PTHREAD_MUTEX_INITIALIZER;
82 static pthread_mutex_t job_start_mutex = PTHREAD_MUTEX_INITIALIZER;
84 static pthread_mutex_t last_jobs_mutex = PTHREAD_MUTEX_INITIALIZER;
86 static pthread_key_t jcr_key; /* Pointer to jcr for each thread */
88 pthread_once_t key_once = PTHREAD_ONCE_INIT;
101 void init_last_jobs_list()
104 struct s_last_job *job_entry = NULL;
106 last_jobs = New(dlist(job_entry, &job_entry->link));
109 jcrs = New(dlist(jcr, &jcr->link));
113 void term_last_jobs_list()
116 lock_last_jobs_list();
117 while (!last_jobs->empty()) {
118 void *je = last_jobs->first();
119 last_jobs->remove(je);
124 unlock_last_jobs_list();
132 bool read_last_jobs_list(int fd, uint64_t addr)
134 struct s_last_job *je, job;
138 Dmsg1(100, "read_last_jobs seek to %d\n", (int)addr);
139 if (addr == 0 || lseek(fd, (boffset_t)addr, SEEK_SET) < 0) {
142 if (read(fd, &num, sizeof(num)) != sizeof(num)) {
145 Dmsg1(100, "Read num_items=%d\n", num);
146 if (num > 4 * max_last_jobs) { /* sanity check */
149 lock_last_jobs_list();
150 for ( ; num; num--) {
151 if (read(fd, &job, sizeof(job)) != sizeof(job)) {
153 Pmsg1(000, "Read job entry. ERR=%s\n", be.bstrerror());
158 je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
159 memcpy((char *)je, (char *)&job, sizeof(job));
161 init_last_jobs_list();
163 last_jobs->append(je);
164 if (last_jobs->size() > max_last_jobs) {
165 je = (struct s_last_job *)last_jobs->first();
166 last_jobs->remove(je);
171 unlock_last_jobs_list();
175 uint64_t write_last_jobs_list(int fd, uint64_t addr)
177 struct s_last_job *je;
181 Dmsg1(100, "write_last_jobs seek to %d\n", (int)addr);
182 if (lseek(fd, (boffset_t)addr, SEEK_SET) < 0) {
186 lock_last_jobs_list();
187 /* First record is number of entires */
188 num = last_jobs->size();
189 if (write(fd, &num, sizeof(num)) != sizeof(num)) {
191 Pmsg1(000, "Error writing num_items: ERR=%s\n", be.bstrerror());
194 foreach_dlist(je, last_jobs) {
195 if (write(fd, je, sizeof(struct s_last_job)) != sizeof(struct s_last_job)) {
197 Pmsg1(000, "Error writing job: ERR=%s\n", be.bstrerror());
201 unlock_last_jobs_list();
203 /* Return current address */
204 stat = lseek(fd, 0, SEEK_CUR);
211 unlock_last_jobs_list();
215 void lock_last_jobs_list()
220 void unlock_last_jobs_list()
225 /* Get an ASCII representation of the Operation being performed as an english Noun */
226 const char *JCR::get_OperationName()
232 return _("Verifying");
234 return _("Restoring");
236 return _("Archiving");
240 return _("Migration");
242 return _("Scanning");
244 return _("Unknown operation");
248 /* Get an ASCII representation of the Action being performed either an english Verb or Adjective */
249 const char *JCR::get_ActionName(bool past)
255 return (past == true) ? _("verified") : _("verify");
257 return (past == true) ? _("restored") : _("restore");
259 return (past == true) ? _("archived") : _("archive");
261 return (past == true) ? _("copied") : _("copy");
263 return (past == true) ? _("migrated") : _("migrate");
265 return (past == true) ? _("scanned") : _("scan");
267 return _("unknown action");
280 if (m_JobLevel == L_VIRTUAL_FULL) {
291 * Push a subroutine address into the job end callback stack
293 void job_end_push(JCR *jcr, void job_end_cb(JCR *jcr,void *), void *ctx)
295 jcr->job_end_push.append((void *)job_end_cb);
296 jcr->job_end_push.append(ctx);
299 /* Pop each job_end subroutine and call it */
300 static void job_end_pop(JCR *jcr)
302 void (*job_end_cb)(JCR *jcr, void *ctx);
304 for (int i=jcr->job_end_push.size()-1; i > 0; ) {
305 ctx = jcr->job_end_push.get(i--);
306 job_end_cb = (void (*)(JCR *,void *))jcr->job_end_push.get(i--);
307 job_end_cb(jcr, ctx);
312 * Create thread key for thread specific data
314 void create_jcr_key()
316 int status = pthread_key_create(&jcr_key, NULL);
319 Jmsg1(NULL, M_ABORT, 0, _("pthread key create failed: ERR=%s\n"),
320 be.bstrerror(status));
325 * Create a Job Control Record and link it into JCR chain
326 * Returns newly allocated JCR
327 * Note, since each daemon has a different JCR, he passes
330 JCR *new_jcr(int size, JCR_free_HANDLER *daemon_free_jcr)
333 MQUEUE_ITEM *item = NULL;
334 struct sigaction sigtimer;
337 Dmsg0(dbglvl, "Enter new_jcr\n");
338 status = pthread_once(&key_once, create_jcr_key);
341 Jmsg1(NULL, M_ABORT, 0, _("pthread_once failed. ERR=%s\n"), be.bstrerror(status));
343 jcr = (JCR *)malloc(size);
344 memset(jcr, 0, size);
345 jcr->my_thread_id = pthread_self();
346 jcr->msg_queue = New(dlist(item, &item->link));
347 if ((status = pthread_mutex_init(&jcr->msg_queue_mutex, NULL)) != 0) {
349 Jmsg(NULL, M_ABORT, 0, _("Could not init msg_queue mutex. ERR=%s\n"),
350 be.bstrerror(status));
352 jcr->job_end_push.init(1, false);
353 jcr->sched_time = time(NULL);
354 jcr->daemon_free_jcr = daemon_free_jcr; /* plug daemon free routine */
356 jcr->inc_use_count();
357 jcr->VolumeName = get_pool_memory(PM_FNAME);
358 jcr->VolumeName[0] = 0;
359 jcr->errmsg = get_pool_memory(PM_MESSAGE);
361 jcr->comment = get_pool_memory(PM_FNAME);
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);
475 free_pool_memory(jcr->comment);
478 remove_jcr_from_tsd(jcr);
483 * Global routine to free a jcr
486 void b_free_jcr(const char *file, int line, JCR *jcr)
488 struct s_last_job *je;
490 Dmsg3(dbglvl, "Enter free_jcr jid=%u from %s:%d\n", jcr->JobId, file, line);
494 void free_jcr(JCR *jcr)
496 struct s_last_job *je;
498 Dmsg3(dbglvl, "Enter free_jcr jid=%u use_count=%d Job=%s\n",
499 jcr->JobId, jcr->use_count(), jcr->Job);
504 jcr->dec_use_count(); /* decrement use count */
505 if (jcr->use_count() < 0) {
506 Jmsg2(jcr, M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
507 jcr->use_count(), jcr->JobId);
509 if (jcr->JobId > 0) {
510 Dmsg3(dbglvl, "Dec free_jcr jid=%u use_count=%d Job=%s\n",
511 jcr->JobId, jcr->use_count(), jcr->Job);
513 if (jcr->use_count() > 0) { /* if in use */
517 if (jcr->JobId > 0) {
518 Dmsg3(dbglvl, "remove jcr jid=%u use_count=%d Job=%s\n",
519 jcr->JobId, jcr->use_count(), jcr->Job);
521 remove_jcr(jcr); /* remove Jcr from chain */
524 dequeue_messages(jcr);
525 job_end_pop(jcr); /* pop and call hooked routines */
527 Dmsg1(dbglvl, "End job=%d\n", jcr->JobId);
529 /* Keep some statistics */
530 switch (jcr->getJobType()) {
537 /* Keep list of last jobs, but not Console where JobId==0 */
538 if (jcr->JobId > 0) {
539 lock_last_jobs_list();
541 je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
542 memset(je, 0, sizeof(struct s_last_job)); /* zero in case unset fields */
543 je->Errors = jcr->JobErrors;
544 je->JobType = jcr->getJobType();
545 je->JobId = jcr->JobId;
546 je->VolSessionId = jcr->VolSessionId;
547 je->VolSessionTime = jcr->VolSessionTime;
548 bstrncpy(je->Job, jcr->Job, sizeof(je->Job));
549 je->JobFiles = jcr->JobFiles;
550 je->JobBytes = jcr->JobBytes;
551 je->JobStatus = jcr->JobStatus;
552 je->JobLevel = jcr->getJobLevel();
553 je->start_time = jcr->start_time;
554 je->end_time = time(NULL);
557 init_last_jobs_list();
559 last_jobs->append(je);
560 if (last_jobs->size() > max_last_jobs) {
561 je = (struct s_last_job *)last_jobs->first();
562 last_jobs->remove(je);
565 unlock_last_jobs_list();
572 if (jcr->daemon_free_jcr) {
573 jcr->daemon_free_jcr(jcr); /* call daemon free routine */
576 free_common_jcr(jcr);
577 close_msg(NULL); /* flush any daemon messages */
578 garbage_collect_memory_pool();
579 Dmsg0(dbglvl, "Exit free_jcr\n");
583 * Remove jcr from thread specific data, but
584 * but make sure it is us who are attached.
586 void remove_jcr_from_tsd(JCR *jcr)
588 JCR *tjcr = get_jcr_from_tsd();
590 set_jcr_in_tsd(INVALID_JCR);
595 * Put this jcr in the thread specifc data
597 void set_jcr_in_tsd(JCR *jcr)
599 int status = pthread_setspecific(jcr_key, (void *)jcr);
602 Jmsg1(jcr, M_ABORT, 0, _("pthread_setspecific failed: ERR=%s\n"), be.bstrerror(status));
607 * Give me the jcr that is attached to this thread
609 JCR *get_jcr_from_tsd()
611 JCR *jcr = (JCR *)pthread_getspecific(jcr_key);
612 // printf("get_jcr_from_tsd: jcr=%p\n", jcr);
613 /* set any INVALID_JCR to NULL which the rest of Bacula understands */
614 if (jcr == INVALID_JCR) {
622 * Find which JobId corresponds to the current thread
624 uint32_t get_jobid_from_tsd()
628 jcr = get_jcr_from_tsd();
629 // printf("get_jobid_from_tsr: jcr=%p\n", jcr);
631 JobId = (uint32_t)jcr->JobId;
637 * Given a JobId, find the JCR
638 * Returns: jcr on success
641 JCR *get_jcr_by_id(uint32_t JobId)
646 if (jcr->JobId == JobId) {
647 jcr->inc_use_count();
648 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
649 jcr->JobId, jcr->use_count(), jcr->Job);
658 * Given a thread id, find the JobId
659 * Returns: JobId on success
662 uint32_t get_jobid_from_tid(pthread_t tid)
668 if (pthread_equal(jcr->my_thread_id, tid)) {
682 * Given a SessionId and SessionTime, find the JCR
683 * Returns: jcr on success
686 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
691 if (jcr->VolSessionId == SessionId &&
692 jcr->VolSessionTime == SessionTime) {
693 jcr->inc_use_count();
694 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
695 jcr->JobId, jcr->use_count(), jcr->Job);
705 * Given a Job, find the JCR
706 * compares on the number of characters in Job
707 * thus allowing partial matches.
708 * Returns: jcr on success
711 JCR *get_jcr_by_partial_name(char *Job)
721 if (strncmp(Job, jcr->Job, len) == 0) {
722 jcr->inc_use_count();
723 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
724 jcr->JobId, jcr->use_count(), jcr->Job);
734 * Given a Job, find the JCR
735 * requires an exact match of names.
736 * Returns: jcr on success
739 JCR *get_jcr_by_full_name(char *Job)
747 if (strcmp(jcr->Job, Job) == 0) {
748 jcr->inc_use_count();
749 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
750 jcr->JobId, jcr->use_count(), jcr->Job);
758 static void update_wait_time(JCR *jcr, int newJobStatus)
760 bool enter_in_waittime;
761 int oldJobStatus = jcr->JobStatus;
763 switch (newJobStatus) {
768 case JS_WaitStoreRes:
770 case JS_WaitClientRes:
772 case JS_WaitPriority:
773 enter_in_waittime = true;
776 enter_in_waittime = false; /* not a Wait situation */
781 * If we were previously waiting and are not any more
782 * we want to update the wait_time variable, which is
783 * the start of waiting.
785 switch (oldJobStatus) {
790 case JS_WaitStoreRes:
792 case JS_WaitClientRes:
794 case JS_WaitPriority:
795 if (!enter_in_waittime) { /* we get out the wait time */
796 jcr->wait_time_sum += (time(NULL) - jcr->wait_time);
801 /* if wait state is new, we keep current time for watchdog MaxWaitTime */
803 if (enter_in_waittime) {
804 jcr->wait_time = time(NULL);
811 * Priority runs from 0 (lowest) to 10 (highest)
813 static int get_status_priority(int JobStatus)
817 case JS_ErrorTerminated:
834 void set_jcr_job_status(JCR *jcr, int JobStatus)
836 jcr->setJobStatus(JobStatus);
839 void JCR::setJobStatus(int newJobStatus)
842 int priority, old_priority;
843 int oldJobStatus = jcr->JobStatus;
844 priority = get_status_priority(newJobStatus);
845 old_priority = get_status_priority(oldJobStatus);
847 Dmsg2(800, "set_jcr_job_status(%s, %c)\n", Job, newJobStatus);
849 /* Update wait_time depending on newJobStatus and oldJobStatus */
850 update_wait_time(jcr, newJobStatus);
853 * For a set of errors, ... keep the current status
854 * so it isn't lost. For all others, set it.
856 Dmsg2(800, "OnEntry JobStatus=%c newJobstatus=%c\n", oldJobStatus, newJobStatus);
858 * If status priority is > than proposed new status, change it.
859 * If status priority == new priority and both are zero, take
861 * If it is not zero, then we keep the first non-zero "error" that
864 if (priority > old_priority || (
865 priority == 0 && old_priority == 0)) {
866 Dmsg4(800, "Set new stat. old: %c,%d new: %c,%d\n",
867 jcr->JobStatus, old_priority, newJobStatus, priority);
868 jcr->JobStatus = newJobStatus; /* replace with new status */
871 if (oldJobStatus != jcr->JobStatus) {
872 Dmsg2(800, "leave set_job_status old=%c new=%c\n", oldJobStatus, newJobStatus);
873 // generate_plugin_event(jcr, bEventStatusChange, NULL);
877 #ifdef TRACE_JCR_CHAIN
878 static int lock_count = 0;
884 #ifdef TRACE_JCR_CHAIN
885 static void b_lock_jcr_chain(const char *fname, int line)
887 static void lock_jcr_chain()
890 #ifdef TRACE_JCR_CHAIN
891 Dmsg3(dbglvl, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
899 #ifdef TRACE_JCR_CHAIN
900 static void b_unlock_jcr_chain(const char *fname, int line)
902 static void unlock_jcr_chain()
905 #ifdef TRACE_JCR_CHAIN
906 Dmsg3(dbglvl, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
912 * Start walk of jcr chain
913 * The proper way to walk the jcr chain is:
920 * It is possible to leave out the endeach_jcr(jcr), but
921 * in that case, the last jcr referenced must be explicitly
927 JCR *jcr_walk_start()
931 jcr = (JCR *)jcrs->first();
933 jcr->inc_use_count();
934 if (jcr->JobId > 0) {
935 Dmsg3(dbglvl, "Inc walk_start jid=%u use_count=%d Job=%s\n",
936 jcr->JobId, jcr->use_count(), jcr->Job);
944 * Get next jcr from chain, and release current one
946 JCR *jcr_walk_next(JCR *prev_jcr)
951 jcr = (JCR *)jcrs->next(prev_jcr);
953 jcr->inc_use_count();
954 if (jcr->JobId > 0) {
955 Dmsg3(dbglvl, "Inc walk_next jid=%u use_count=%d Job=%s\n",
956 jcr->JobId, jcr->use_count(), jcr->Job);
967 * Release last jcr referenced
969 void jcr_walk_end(JCR *jcr)
972 if (jcr->JobId > 0) {
973 Dmsg3(dbglvl, "Free walk_end jid=%u use_count=%d Job=%s\n",
974 jcr->JobId, jcr->use_count(), jcr->Job);
981 * Return number of Jobs
989 for (jcr = (JCR *)jcrs->first(); (jcr = (JCR *)jcrs->next(jcr)); ) {
990 if (jcr->JobId > 0) {
1000 * Setup to call the timeout check routine every 30 seconds
1001 * This routine will check any timers that have been enabled.
1003 bool init_jcr_subsystem(void)
1005 watchdog_t *wd = new_watchdog();
1007 wd->one_shot = false;
1008 wd->interval = 30; /* FIXME: should be configurable somewhere, even
1009 if only with a #define */
1010 wd->callback = jcr_timeout_check;
1012 register_watchdog(wd);
1017 static void jcr_timeout_check(watchdog_t *self)
1023 Dmsg0(dbglvl, "Start JCR timeout checks\n");
1025 /* Walk through all JCRs checking if any one is
1026 * blocked for more than specified max time.
1029 Dmsg2(dbglvl, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
1030 if (jcr->JobId == 0) {
1033 bs = jcr->store_bsock;
1035 timer_start = bs->timer_start;
1036 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1037 bs->timer_start = 0; /* turn off timer */
1038 bs->set_timed_out();
1039 Qmsg(jcr, M_ERROR, 0, _(
1040 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
1041 watchdog_time - timer_start);
1042 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1045 bs = jcr->file_bsock;
1047 timer_start = bs->timer_start;
1048 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1049 bs->timer_start = 0; /* turn off timer */
1050 bs->set_timed_out();
1051 Qmsg(jcr, M_ERROR, 0, _(
1052 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
1053 watchdog_time - timer_start);
1054 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1057 bs = jcr->dir_bsock;
1059 timer_start = bs->timer_start;
1060 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1061 bs->timer_start = 0; /* turn off timer */
1062 bs->set_timed_out();
1063 Qmsg(jcr, M_ERROR, 0, _(
1064 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
1065 watchdog_time - timer_start);
1066 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1072 Dmsg0(dbglvl, "Finished JCR timeout checks\n");
1076 * Return next JobId from comma separated list
1079 * 1 if next JobId returned
1080 * 0 if no more JobIds are in list
1081 * -1 there is an error
1083 int get_next_jobid_from_list(char **p, uint32_t *JobId)
1085 const int maxlen = 30;
1086 char jobid[maxlen+1];
1090 for (int i=0; i<maxlen; i++) {
1093 } else if (*q == ',') {
1100 if (jobid[0] == 0) {
1102 } else if (!is_a_number(jobid)) {
1103 return -1; /* error */
1106 *JobId = str_to_int64(jobid);
1111 * Timeout signal comes here
1113 extern "C" void timeout_handler(int sig)
1115 return; /* thus interrupting the function */
1118 /* Used to display specific daemon information after a fatal signal
1119 * (like B_DB in the director)
1121 #define MAX_DBG_HOOK 10
1122 static dbg_jcr_hook_t *dbg_jcr_hooks[MAX_DBG_HOOK];
1123 static int dbg_jcr_handler_count;
1125 void dbg_jcr_add_hook(dbg_jcr_hook_t *hook)
1127 ASSERT(dbg_jcr_handler_count < MAX_DBG_HOOK);
1128 dbg_jcr_hooks[dbg_jcr_handler_count++] = hook;
1134 * This function should be used ONLY after a fatal signal. We walk through the
1135 * JCR chain without doing any lock, Bacula should not be running.
1137 void dbg_print_jcr(FILE *fp)
1139 char buf1[128], buf2[128], buf3[128], buf4[128];
1144 fprintf(fp, "Attempt to dump current JCRs\n");
1146 for (JCR *jcr = (JCR *)jcrs->first(); jcr ; jcr = (JCR *)jcrs->next(jcr)) {
1147 fprintf(fp, "JCR=%p JobId=%d name=%s JobStatus=%c\n",
1148 jcr, (int)jcr->JobId, jcr->Job, jcr->JobStatus);
1149 fprintf(fp, "\tuse_count=%i\n", jcr->use_count());
1150 fprintf(fp, "\tJobType=%c JobLevel=%c\n",
1151 jcr->getJobType(), jcr->getJobLevel());
1152 bstrftime(buf1, sizeof(buf1), jcr->sched_time);
1153 bstrftime(buf2, sizeof(buf2), jcr->start_time);
1154 bstrftime(buf3, sizeof(buf3), jcr->end_time);
1155 bstrftime(buf4, sizeof(buf4), jcr->wait_time);
1156 fprintf(fp, "\tsched_time=%s start_time=%s\n\tend_time=%s wait_time=%s\n",
1157 buf1, buf2, buf3, buf4);
1158 fprintf(fp, "\tdb=%p db_batch=%p batch_started=%i\n",
1159 jcr->db, jcr->db_batch, jcr->batch_started);
1162 * Call all the jcr debug hooks
1164 for(int i=0; i < dbg_jcr_handler_count; i++) {
1165 dbg_jcr_hook_t *hook = dbg_jcr_hooks[i];