2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 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");
273 /* Set Job type in JCR and also set appropriate read flag */
274 void JCR::set_JobType(int32_t JobType)
279 /* Set Job level in JCR and also set appropriate read flag */
280 void JCR::set_JobLevel(int32_t JobLevel)
282 m_JobLevel = JobLevel;
294 if (m_JobLevel == L_VIRTUAL_FULL) {
305 * Push a subroutine address into the job end callback stack
307 void job_end_push(JCR *jcr, void job_end_cb(JCR *jcr,void *), void *ctx)
309 jcr->job_end_push.append((void *)job_end_cb);
310 jcr->job_end_push.append(ctx);
313 /* Pop each job_end subroutine and call it */
314 static void job_end_pop(JCR *jcr)
316 void (*job_end_cb)(JCR *jcr, void *ctx);
318 for (int i=jcr->job_end_push.size()-1; i > 0; ) {
319 ctx = jcr->job_end_push.get(i--);
320 job_end_cb = (void (*)(JCR *,void *))jcr->job_end_push.get(i--);
321 job_end_cb(jcr, ctx);
325 void create_jcr_key()
327 int status = pthread_key_create(&jcr_key, NULL);
330 Jmsg1(NULL, M_ABORT, 0, _("pthread key create failed: ERR=%s\n"),
331 be.bstrerror(status));
336 * Create a Job Control Record and link it into JCR chain
337 * Returns newly allocated JCR
338 * Note, since each daemon has a different JCR, he passes
341 JCR *new_jcr(int size, JCR_free_HANDLER *daemon_free_jcr)
344 MQUEUE_ITEM *item = NULL;
345 struct sigaction sigtimer;
348 Dmsg0(dbglvl, "Enter new_jcr\n");
349 status = pthread_once(&key_once, create_jcr_key);
352 Jmsg1(NULL, M_ABORT, 0, _("pthread_once failed. ERR=%s\n"), be.bstrerror(status));
354 jcr = (JCR *)malloc(size);
355 memset(jcr, 0, size);
356 jcr->my_thread_id = pthread_self();
357 jcr->msg_queue = New(dlist(item, &item->link));
358 jcr->job_end_push.init(1, false);
359 jcr->sched_time = time(NULL);
360 jcr->daemon_free_jcr = daemon_free_jcr; /* plug daemon free routine */
362 jcr->inc_use_count();
363 jcr->VolumeName = get_pool_memory(PM_FNAME);
364 jcr->VolumeName[0] = 0;
365 jcr->errmsg = get_pool_memory(PM_MESSAGE);
367 /* Setup some dummy values */
368 bstrncpy(jcr->Job, "*System*", sizeof(jcr->Job));
370 jcr->set_JobType(JT_SYSTEM); /* internal job until defined */
371 jcr->set_JobLevel(L_NONE);
372 set_jcr_job_status(jcr, JS_Created); /* ready to run */
374 sigtimer.sa_flags = 0;
375 sigtimer.sa_handler = timeout_handler;
376 sigfillset(&sigtimer.sa_mask);
377 sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL);
380 * Locking jobs is a global lock that is needed
381 * so that the Director can stop new jobs from being
382 * added to the jcr chain while it processes a new
383 * conf file and does the job_end_push().
388 jcrs = New(dlist(jcr, &jcr->link));
399 * Remove a JCR from the chain
400 * NOTE! The chain must be locked prior to calling
403 static void remove_jcr(JCR *jcr)
405 Dmsg0(dbglvl, "Enter remove_jcr\n");
407 Emsg0(M_ABORT, 0, _("NULL jcr.\n"));
410 Dmsg0(dbglvl, "Leave remove_jcr\n");
414 * Free stuff common to all JCRs. N.B. Be careful to include only
415 * generic stuff in the common part of the jcr.
417 static void free_common_jcr(JCR *jcr)
419 jcr->destroy_mutex();
421 if (jcr->msg_queue) {
422 delete jcr->msg_queue;
423 jcr->msg_queue = NULL;
425 close_msg(jcr); /* close messages for this job */
427 /* do this after closing messages */
428 if (jcr->client_name) {
429 free_pool_memory(jcr->client_name);
430 jcr->client_name = NULL;
434 free_pool_memory(jcr->attr);
438 if (jcr->sd_auth_key) {
439 free(jcr->sd_auth_key);
440 jcr->sd_auth_key = NULL;
442 if (jcr->VolumeName) {
443 free_pool_memory(jcr->VolumeName);
444 jcr->VolumeName = NULL;
447 if (jcr->dir_bsock) {
448 bnet_close(jcr->dir_bsock);
449 jcr->dir_bsock = NULL;
452 free_pool_memory(jcr->errmsg);
459 if (jcr->RegexWhere) {
460 free(jcr->RegexWhere);
461 jcr->RegexWhere = NULL;
463 if (jcr->where_bregexp) {
464 free_bregexps(jcr->where_bregexp);
465 delete jcr->where_bregexp;
466 jcr->where_bregexp = NULL;
468 if (jcr->cached_path) {
469 free_pool_memory(jcr->cached_path);
470 jcr->cached_path = NULL;
474 free_guid_list(jcr->id_list);
477 remove_jcr_from_tsd(jcr);
482 * Global routine to free a jcr
485 void b_free_jcr(const char *file, int line, JCR *jcr)
487 struct s_last_job *je;
489 Dmsg3(dbglvl, "Enter free_jcr jid=%u from %s:%d\n", jcr->JobId, file, line);
493 void free_jcr(JCR *jcr)
495 struct s_last_job *je;
497 Dmsg3(dbglvl, "Enter free_jcr jid=%u use_count=%d Job=%s\n",
498 jcr->JobId, jcr->use_count(), jcr->Job);
503 jcr->dec_use_count(); /* decrement use count */
504 if (jcr->use_count() < 0) {
505 Jmsg2(jcr, M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
506 jcr->use_count(), jcr->JobId);
508 if (jcr->JobId > 0) {
509 Dmsg3(dbglvl, "Dec free_jcr jid=%u use_count=%d Job=%s\n",
510 jcr->JobId, jcr->use_count(), jcr->Job);
512 if (jcr->use_count() > 0) { /* if in use */
516 if (jcr->JobId > 0) {
517 Dmsg3(dbglvl, "remove jcr jid=%u use_count=%d Job=%s\n",
518 jcr->JobId, jcr->use_count(), jcr->Job);
520 remove_jcr(jcr); /* remove Jcr from chain */
523 dequeue_messages(jcr);
524 job_end_pop(jcr); /* pop and call hooked routines */
526 Dmsg1(dbglvl, "End job=%d\n", jcr->JobId);
528 /* Keep some statistics */
529 switch (jcr->get_JobType()) {
536 /* Keep list of last jobs, but not Console where JobId==0 */
537 if (jcr->JobId > 0) {
538 lock_last_jobs_list();
540 je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
541 memset(je, 0, sizeof(struct s_last_job)); /* zero in case unset fields */
542 je->Errors = jcr->JobErrors;
543 je->JobType = jcr->get_JobType();
544 je->JobId = jcr->JobId;
545 je->VolSessionId = jcr->VolSessionId;
546 je->VolSessionTime = jcr->VolSessionTime;
547 bstrncpy(je->Job, jcr->Job, sizeof(je->Job));
548 je->JobFiles = jcr->JobFiles;
549 je->JobBytes = jcr->JobBytes;
550 je->JobStatus = jcr->JobStatus;
551 je->JobLevel = jcr->get_JobLevel();
552 je->start_time = jcr->start_time;
553 je->end_time = time(NULL);
556 init_last_jobs_list();
558 last_jobs->append(je);
559 if (last_jobs->size() > max_last_jobs) {
560 je = (struct s_last_job *)last_jobs->first();
561 last_jobs->remove(je);
564 unlock_last_jobs_list();
571 if (jcr->daemon_free_jcr) {
572 jcr->daemon_free_jcr(jcr); /* call daemon free routine */
575 free_common_jcr(jcr);
576 close_msg(NULL); /* flush any daemon messages */
577 garbage_collect_memory_pool();
578 Dmsg0(dbglvl, "Exit free_jcr\n");
582 * Remove jcr from thread specific data, but
583 * but make sure it is us who are attached.
585 void remove_jcr_from_tsd(JCR *jcr)
587 JCR *tjcr = get_jcr_from_tsd();
589 set_jcr_in_tsd(INVALID_JCR);
594 * Put this jcr in the thread specifc data
596 void set_jcr_in_tsd(JCR *jcr)
598 int status = pthread_setspecific(jcr_key, (void *)jcr);
601 Jmsg1(jcr, M_ABORT, 0, _("pthread_setspecific failed: ERR=%s\n"), be.bstrerror(status));
606 * Give me the jcr that is attached to this thread
608 JCR *get_jcr_from_tsd()
610 JCR *jcr = (JCR *)pthread_getspecific(jcr_key);
611 // printf("get_jcr_from_tsd: jcr=%p\n", jcr);
612 /* set any INVALID_JCR to NULL which the rest of Bacula understands */
613 if (jcr == INVALID_JCR) {
621 * Find which JobId corresponds to the current thread
623 uint32_t get_jobid_from_tsd()
627 jcr = get_jcr_from_tsd();
628 // printf("get_jobid_from_tsr: jcr=%p\n", jcr);
630 JobId = (uint32_t)jcr->JobId;
636 * Given a JobId, find the JCR
637 * Returns: jcr on success
640 JCR *get_jcr_by_id(uint32_t JobId)
645 if (jcr->JobId == JobId) {
646 jcr->inc_use_count();
647 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
648 jcr->JobId, jcr->use_count(), jcr->Job);
657 * Given a SessionId and SessionTime, find the JCR
658 * Returns: jcr on success
661 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
666 if (jcr->VolSessionId == SessionId &&
667 jcr->VolSessionTime == SessionTime) {
668 jcr->inc_use_count();
669 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
670 jcr->JobId, jcr->use_count(), jcr->Job);
680 * Given a Job, find the JCR
681 * compares on the number of characters in Job
682 * thus allowing partial matches.
683 * Returns: jcr on success
686 JCR *get_jcr_by_partial_name(char *Job)
696 if (strncmp(Job, jcr->Job, len) == 0) {
697 jcr->inc_use_count();
698 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
699 jcr->JobId, jcr->use_count(), jcr->Job);
709 * Given a Job, find the JCR
710 * requires an exact match of names.
711 * Returns: jcr on success
714 JCR *get_jcr_by_full_name(char *Job)
722 if (strcmp(jcr->Job, Job) == 0) {
723 jcr->inc_use_count();
724 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
725 jcr->JobId, jcr->use_count(), jcr->Job);
734 * Priority runs from 0 (lowest) to 10 (highest)
736 static int get_status_priority(int JobStatus)
740 case JS_ErrorTerminated:
756 static void update_wait_time(JCR *jcr, int newJobStatus)
758 bool enter_in_waittime;
759 int oldJobStatus = jcr->JobStatus;
761 switch (newJobStatus) {
766 case JS_WaitStoreRes:
768 case JS_WaitClientRes:
770 case JS_WaitPriority:
771 enter_in_waittime = true;
774 enter_in_waittime = false; /* not a Wait situation */
779 * If we were previously waiting and are not any more
780 * we want to update the wait_time variable, which is
781 * the start of waiting.
783 switch (oldJobStatus) {
788 case JS_WaitStoreRes:
790 case JS_WaitClientRes:
792 case JS_WaitPriority:
793 if (!enter_in_waittime) { /* we get out the wait time */
794 jcr->wait_time_sum += (time(NULL) - jcr->wait_time);
799 /* if wait state is new, we keep current time for watchdog MaxWaitTime */
801 if (enter_in_waittime) {
802 jcr->wait_time = time(NULL);
808 void set_jcr_job_status(JCR *jcr, int JobStatus)
810 jcr->setJobStatus(JobStatus);
813 void JCR::setJobStatus(int JobStatus)
816 int priority, old_priority;
817 int oldJobStatus = JobStatus;
818 priority = get_status_priority(JobStatus);
819 old_priority = get_status_priority(oldJobStatus);
821 Dmsg2(800, "set_jcr_job_status(%s, %c)\n", Job, JobStatus);
823 /* Update wait_time depending on newJobStatus and oldJobStatus */
824 update_wait_time(this, JobStatus);
827 * For a set of errors, ... keep the current status
828 * so it isn't lost. For all others, set it.
830 Dmsg3(300, "jid=%u OnEntry JobStatus=%c set=%c\n", (uint32_t)JobId,
831 JobStatus, JobStatus);
832 if (priority >= old_priority) {
833 jcr->JobStatus = JobStatus; /* replace with new priority */
836 if (oldJobStatus != jcr->JobStatus) {
837 Dmsg3(200, "jid=%u leave set_old_job_status=%c new_set=%c\n", (uint32_t)jcr->JobId,
838 oldJobStatus, JobStatus);
839 // generate_plugin_event(jcr, bEventStatusChange, NULL);
843 #ifdef TRACE_JCR_CHAIN
844 static int lock_count = 0;
850 #ifdef TRACE_JCR_CHAIN
851 static void b_lock_jcr_chain(const char *fname, int line)
853 static void lock_jcr_chain()
856 #ifdef TRACE_JCR_CHAIN
857 Dmsg3(dbglvl, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
865 #ifdef TRACE_JCR_CHAIN
866 static void b_unlock_jcr_chain(const char *fname, int line)
868 static void unlock_jcr_chain()
871 #ifdef TRACE_JCR_CHAIN
872 Dmsg3(dbglvl, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
878 * Start walk of jcr chain
879 * The proper way to walk the jcr chain is:
886 * It is possible to leave out the endeach_jcr(jcr), but
887 * in that case, the last jcr referenced must be explicitly
893 JCR *jcr_walk_start()
897 jcr = (JCR *)jcrs->first();
899 jcr->inc_use_count();
900 if (jcr->JobId > 0) {
901 Dmsg3(dbglvl, "Inc walk_start jid=%u use_count=%d Job=%s\n",
902 jcr->JobId, jcr->use_count(), jcr->Job);
910 * Get next jcr from chain, and release current one
912 JCR *jcr_walk_next(JCR *prev_jcr)
917 jcr = (JCR *)jcrs->next(prev_jcr);
919 jcr->inc_use_count();
920 if (jcr->JobId > 0) {
921 Dmsg3(dbglvl, "Inc walk_next jid=%u use_count=%d Job=%s\n",
922 jcr->JobId, jcr->use_count(), jcr->Job);
933 * Release last jcr referenced
935 void jcr_walk_end(JCR *jcr)
938 if (jcr->JobId > 0) {
939 Dmsg3(dbglvl, "Free walk_end jid=%u use_count=%d Job=%s\n",
940 jcr->JobId, jcr->use_count(), jcr->Job);
948 * Setup to call the timeout check routine every 30 seconds
949 * This routine will check any timers that have been enabled.
951 bool init_jcr_subsystem(void)
953 watchdog_t *wd = new_watchdog();
955 wd->one_shot = false;
956 wd->interval = 30; /* FIXME: should be configurable somewhere, even
957 if only with a #define */
958 wd->callback = jcr_timeout_check;
960 register_watchdog(wd);
965 static void jcr_timeout_check(watchdog_t *self)
971 Dmsg0(dbglvl, "Start JCR timeout checks\n");
973 /* Walk through all JCRs checking if any one is
974 * blocked for more than specified max time.
977 Dmsg2(dbglvl, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
978 if (jcr->JobId == 0) {
981 bs = jcr->store_bsock;
983 timer_start = bs->timer_start;
984 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
985 bs->timer_start = 0; /* turn off timer */
987 Qmsg(jcr, M_ERROR, 0, _(
988 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
989 watchdog_time - timer_start);
990 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
993 bs = jcr->file_bsock;
995 timer_start = bs->timer_start;
996 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
997 bs->timer_start = 0; /* turn off timer */
999 Qmsg(jcr, M_ERROR, 0, _(
1000 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
1001 watchdog_time - timer_start);
1002 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1005 bs = jcr->dir_bsock;
1007 timer_start = bs->timer_start;
1008 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1009 bs->timer_start = 0; /* turn off timer */
1010 bs->set_timed_out();
1011 Qmsg(jcr, M_ERROR, 0, _(
1012 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
1013 watchdog_time - timer_start);
1014 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1020 Dmsg0(dbglvl, "Finished JCR timeout checks\n");
1024 * Return next JobId from comma separated list
1027 * 1 if next JobId returned
1028 * 0 if no more JobIds are in list
1029 * -1 there is an error
1031 int get_next_jobid_from_list(char **p, uint32_t *JobId)
1033 const int maxlen = 30;
1034 char jobid[maxlen+1];
1038 for (int i=0; i<maxlen; i++) {
1041 } else if (*q == ',') {
1048 if (jobid[0] == 0) {
1050 } else if (!is_a_number(jobid)) {
1051 return -1; /* error */
1054 *JobId = str_to_int64(jobid);
1059 * Timeout signal comes here
1061 extern "C" void timeout_handler(int sig)
1063 return; /* thus interrupting the function */
1066 /* Used to display specific daemon information after a fatal signal
1067 * (like B_DB in the director)
1069 #define MAX_DBG_HOOK 10
1070 static dbg_jcr_hook_t *dbg_jcr_hooks[MAX_DBG_HOOK];
1071 static int dbg_jcr_handler_count;
1073 void dbg_jcr_add_hook(dbg_jcr_hook_t *fct)
1075 ASSERT(dbg_jcr_handler_count < MAX_DBG_HOOK);
1076 dbg_jcr_hooks[dbg_jcr_handler_count++] = fct;
1082 * This function should be used ONLY after a fatal signal. We walk through the
1083 * JCR chain without doing any lock, bacula should not be running.
1085 void _dbg_print_jcr(FILE *fp)
1087 char buf1[128], buf2[128], buf3[128], buf4[128];
1092 fprintf(fp, "Attempt to dump current JCRs\n");
1094 for (JCR *jcr = (JCR *)jcrs->first(); jcr ; jcr = (JCR *)jcrs->next(jcr)) {
1095 if (!jcr) { /* protect us against something ? */
1099 fprintf(fp, "JCR=%p JobId=%i name=%s JobStatus=%c\n",
1100 jcr, jcr->JobId, jcr->Job, jcr->JobStatus);
1102 fprintf(fp, "\tuse_count=%i\n",
1105 /* KES -- removed non-portable code referencing pthread_t */
1106 fprintf(fp, "\tuse_count=%d\n", jcr->use_count());
1108 fprintf(fp, "\tJobType=%c JobLevel=%c\n",
1109 jcr->get_JobType(), jcr->get_JobLevel());
1110 bstrftime(buf1, sizeof(buf1), jcr->sched_time);
1111 bstrftime(buf2, sizeof(buf2), jcr->start_time);
1112 bstrftime(buf3, sizeof(buf3), jcr->end_time);
1113 bstrftime(buf4, sizeof(buf4), jcr->wait_time);
1114 fprintf(fp, "\tsched_time=%s start_time=%s\n\tend_time=%s wait_time=%s\n",
1115 buf1, buf2, buf3, buf4);
1116 fprintf(fp, "\tdequeing=%i\n", jcr->dequeuing);
1117 fprintf(fp, "\tdb=%p db_batch=%p batch_started=%i\n",
1118 jcr->db, jcr->db_batch, jcr->batch_started);
1120 for(int i=0; i < dbg_jcr_handler_count; i++) {
1121 dbg_jcr_hook_t *fct = dbg_jcr_hooks[i];