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);
313 void create_jcr_key()
315 int status = pthread_key_create(&jcr_key, NULL);
318 Jmsg1(NULL, M_ABORT, 0, _("pthread key create failed: ERR=%s\n"),
319 be.bstrerror(status));
324 * Create a Job Control Record and link it into JCR chain
325 * Returns newly allocated JCR
326 * Note, since each daemon has a different JCR, he passes
329 JCR *new_jcr(int size, JCR_free_HANDLER *daemon_free_jcr)
332 MQUEUE_ITEM *item = NULL;
333 struct sigaction sigtimer;
336 Dmsg0(dbglvl, "Enter new_jcr\n");
337 status = pthread_once(&key_once, create_jcr_key);
340 Jmsg1(NULL, M_ABORT, 0, _("pthread_once failed. ERR=%s\n"), be.bstrerror(status));
342 jcr = (JCR *)malloc(size);
343 memset(jcr, 0, size);
344 jcr->my_thread_id = pthread_self();
345 jcr->msg_queue = New(dlist(item, &item->link));
346 jcr->job_end_push.init(1, false);
347 jcr->sched_time = time(NULL);
348 jcr->daemon_free_jcr = daemon_free_jcr; /* plug daemon free routine */
350 jcr->inc_use_count();
351 jcr->VolumeName = get_pool_memory(PM_FNAME);
352 jcr->VolumeName[0] = 0;
353 jcr->errmsg = get_pool_memory(PM_MESSAGE);
355 /* Setup some dummy values */
356 bstrncpy(jcr->Job, "*System*", sizeof(jcr->Job));
358 jcr->setJobType(JT_SYSTEM); /* internal job until defined */
359 jcr->setJobLevel(L_NONE);
360 jcr->setJobStatus(JS_Created); /* ready to run */
362 sigtimer.sa_flags = 0;
363 sigtimer.sa_handler = timeout_handler;
364 sigfillset(&sigtimer.sa_mask);
365 sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL);
368 * Locking jobs is a global lock that is needed
369 * so that the Director can stop new jobs from being
370 * added to the jcr chain while it processes a new
371 * conf file and does the job_end_push().
376 jcrs = New(dlist(jcr, &jcr->link));
387 * Remove a JCR from the chain
388 * NOTE! The chain must be locked prior to calling
391 static void remove_jcr(JCR *jcr)
393 Dmsg0(dbglvl, "Enter remove_jcr\n");
395 Emsg0(M_ABORT, 0, _("NULL jcr.\n"));
398 Dmsg0(dbglvl, "Leave remove_jcr\n");
402 * Free stuff common to all JCRs. N.B. Be careful to include only
403 * generic stuff in the common part of the jcr.
405 static void free_common_jcr(JCR *jcr)
407 jcr->destroy_mutex();
409 if (jcr->msg_queue) {
410 delete jcr->msg_queue;
411 jcr->msg_queue = NULL;
413 close_msg(jcr); /* close messages for this job */
415 /* do this after closing messages */
416 if (jcr->client_name) {
417 free_pool_memory(jcr->client_name);
418 jcr->client_name = NULL;
422 free_pool_memory(jcr->attr);
426 if (jcr->sd_auth_key) {
427 free(jcr->sd_auth_key);
428 jcr->sd_auth_key = NULL;
430 if (jcr->VolumeName) {
431 free_pool_memory(jcr->VolumeName);
432 jcr->VolumeName = NULL;
435 if (jcr->dir_bsock) {
436 bnet_close(jcr->dir_bsock);
437 jcr->dir_bsock = NULL;
440 free_pool_memory(jcr->errmsg);
447 if (jcr->RegexWhere) {
448 free(jcr->RegexWhere);
449 jcr->RegexWhere = NULL;
451 if (jcr->where_bregexp) {
452 free_bregexps(jcr->where_bregexp);
453 delete jcr->where_bregexp;
454 jcr->where_bregexp = NULL;
456 if (jcr->cached_path) {
457 free_pool_memory(jcr->cached_path);
458 jcr->cached_path = NULL;
462 free_guid_list(jcr->id_list);
465 remove_jcr_from_tsd(jcr);
470 * Global routine to free a jcr
473 void b_free_jcr(const char *file, int line, JCR *jcr)
475 struct s_last_job *je;
477 Dmsg3(dbglvl, "Enter free_jcr jid=%u from %s:%d\n", jcr->JobId, file, line);
481 void free_jcr(JCR *jcr)
483 struct s_last_job *je;
485 Dmsg3(dbglvl, "Enter free_jcr jid=%u use_count=%d Job=%s\n",
486 jcr->JobId, jcr->use_count(), jcr->Job);
491 jcr->dec_use_count(); /* decrement use count */
492 if (jcr->use_count() < 0) {
493 Jmsg2(jcr, M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
494 jcr->use_count(), jcr->JobId);
496 if (jcr->JobId > 0) {
497 Dmsg3(dbglvl, "Dec free_jcr jid=%u use_count=%d Job=%s\n",
498 jcr->JobId, jcr->use_count(), jcr->Job);
500 if (jcr->use_count() > 0) { /* if in use */
504 if (jcr->JobId > 0) {
505 Dmsg3(dbglvl, "remove jcr jid=%u use_count=%d Job=%s\n",
506 jcr->JobId, jcr->use_count(), jcr->Job);
508 remove_jcr(jcr); /* remove Jcr from chain */
511 dequeue_messages(jcr);
512 job_end_pop(jcr); /* pop and call hooked routines */
514 Dmsg1(dbglvl, "End job=%d\n", jcr->JobId);
516 /* Keep some statistics */
517 switch (jcr->get_JobType()) {
524 /* Keep list of last jobs, but not Console where JobId==0 */
525 if (jcr->JobId > 0) {
526 lock_last_jobs_list();
528 je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
529 memset(je, 0, sizeof(struct s_last_job)); /* zero in case unset fields */
530 je->Errors = jcr->JobErrors;
531 je->JobType = jcr->get_JobType();
532 je->JobId = jcr->JobId;
533 je->VolSessionId = jcr->VolSessionId;
534 je->VolSessionTime = jcr->VolSessionTime;
535 bstrncpy(je->Job, jcr->Job, sizeof(je->Job));
536 je->JobFiles = jcr->JobFiles;
537 je->JobBytes = jcr->JobBytes;
538 je->JobStatus = jcr->JobStatus;
539 je->JobLevel = jcr->get_JobLevel();
540 je->start_time = jcr->start_time;
541 je->end_time = time(NULL);
544 init_last_jobs_list();
546 last_jobs->append(je);
547 if (last_jobs->size() > max_last_jobs) {
548 je = (struct s_last_job *)last_jobs->first();
549 last_jobs->remove(je);
552 unlock_last_jobs_list();
559 if (jcr->daemon_free_jcr) {
560 jcr->daemon_free_jcr(jcr); /* call daemon free routine */
563 free_common_jcr(jcr);
564 close_msg(NULL); /* flush any daemon messages */
565 garbage_collect_memory_pool();
566 Dmsg0(dbglvl, "Exit free_jcr\n");
570 * Remove jcr from thread specific data, but
571 * but make sure it is us who are attached.
573 void remove_jcr_from_tsd(JCR *jcr)
575 JCR *tjcr = get_jcr_from_tsd();
577 set_jcr_in_tsd(INVALID_JCR);
582 * Put this jcr in the thread specifc data
584 void set_jcr_in_tsd(JCR *jcr)
586 int status = pthread_setspecific(jcr_key, (void *)jcr);
589 Jmsg1(jcr, M_ABORT, 0, _("pthread_setspecific failed: ERR=%s\n"), be.bstrerror(status));
594 * Give me the jcr that is attached to this thread
596 JCR *get_jcr_from_tsd()
598 JCR *jcr = (JCR *)pthread_getspecific(jcr_key);
599 // printf("get_jcr_from_tsd: jcr=%p\n", jcr);
600 /* set any INVALID_JCR to NULL which the rest of Bacula understands */
601 if (jcr == INVALID_JCR) {
609 * Find which JobId corresponds to the current thread
611 uint32_t get_jobid_from_tsd()
615 jcr = get_jcr_from_tsd();
616 // printf("get_jobid_from_tsr: jcr=%p\n", jcr);
618 JobId = (uint32_t)jcr->JobId;
624 * Given a JobId, find the JCR
625 * Returns: jcr on success
628 JCR *get_jcr_by_id(uint32_t JobId)
633 if (jcr->JobId == JobId) {
634 jcr->inc_use_count();
635 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
636 jcr->JobId, jcr->use_count(), jcr->Job);
645 * Given a thread id, find the JobId
646 * Returns: JobId on success
649 uint32_t get_jobid_from_tid(pthread_t tid)
655 if (pthread_equal(jcr->my_thread_id, tid)) {
669 * Given a SessionId and SessionTime, find the JCR
670 * Returns: jcr on success
673 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
678 if (jcr->VolSessionId == SessionId &&
679 jcr->VolSessionTime == SessionTime) {
680 jcr->inc_use_count();
681 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
682 jcr->JobId, jcr->use_count(), jcr->Job);
692 * Given a Job, find the JCR
693 * compares on the number of characters in Job
694 * thus allowing partial matches.
695 * Returns: jcr on success
698 JCR *get_jcr_by_partial_name(char *Job)
708 if (strncmp(Job, jcr->Job, len) == 0) {
709 jcr->inc_use_count();
710 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
711 jcr->JobId, jcr->use_count(), jcr->Job);
721 * Given a Job, find the JCR
722 * requires an exact match of names.
723 * Returns: jcr on success
726 JCR *get_jcr_by_full_name(char *Job)
734 if (strcmp(jcr->Job, Job) == 0) {
735 jcr->inc_use_count();
736 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
737 jcr->JobId, jcr->use_count(), jcr->Job);
746 * Priority runs from 0 (lowest) to 10 (highest)
748 static int get_status_priority(int JobStatus)
752 case JS_ErrorTerminated:
769 static void update_wait_time(JCR *jcr, int newJobStatus)
771 bool enter_in_waittime;
772 int oldJobStatus = jcr->JobStatus;
774 switch (newJobStatus) {
779 case JS_WaitStoreRes:
781 case JS_WaitClientRes:
783 case JS_WaitPriority:
784 enter_in_waittime = true;
787 enter_in_waittime = false; /* not a Wait situation */
792 * If we were previously waiting and are not any more
793 * we want to update the wait_time variable, which is
794 * the start of waiting.
796 switch (oldJobStatus) {
801 case JS_WaitStoreRes:
803 case JS_WaitClientRes:
805 case JS_WaitPriority:
806 if (!enter_in_waittime) { /* we get out the wait time */
807 jcr->wait_time_sum += (time(NULL) - jcr->wait_time);
812 /* if wait state is new, we keep current time for watchdog MaxWaitTime */
814 if (enter_in_waittime) {
815 jcr->wait_time = time(NULL);
821 void set_jcr_job_status(JCR *jcr, int JobStatus)
823 jcr->setJobStatus(JobStatus);
826 void JCR::setJobStatus(int JobStatus)
829 int priority, old_priority;
830 int oldJobStatus = JobStatus;
831 priority = get_status_priority(JobStatus);
832 old_priority = get_status_priority(oldJobStatus);
834 Dmsg2(800, "set_jcr_job_status(%s, %c)\n", Job, JobStatus);
836 /* Update wait_time depending on newJobStatus and oldJobStatus */
837 update_wait_time(this, JobStatus);
840 * For a set of errors, ... keep the current status
841 * so it isn't lost. For all others, set it.
843 Dmsg3(300, "jid=%u OnEntry JobStatus=%c set=%c\n", (uint32_t)JobId,
844 JobStatus, JobStatus);
845 if (priority >= old_priority) {
846 jcr->JobStatus = JobStatus; /* replace with new priority */
849 if (oldJobStatus != jcr->JobStatus) {
850 Dmsg3(200, "jid=%u leave set_old_job_status=%c new_set=%c\n", (uint32_t)jcr->JobId,
851 oldJobStatus, JobStatus);
852 // generate_plugin_event(jcr, bEventStatusChange, NULL);
856 #ifdef TRACE_JCR_CHAIN
857 static int lock_count = 0;
863 #ifdef TRACE_JCR_CHAIN
864 static void b_lock_jcr_chain(const char *fname, int line)
866 static void lock_jcr_chain()
869 #ifdef TRACE_JCR_CHAIN
870 Dmsg3(dbglvl, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
878 #ifdef TRACE_JCR_CHAIN
879 static void b_unlock_jcr_chain(const char *fname, int line)
881 static void unlock_jcr_chain()
884 #ifdef TRACE_JCR_CHAIN
885 Dmsg3(dbglvl, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
891 * Start walk of jcr chain
892 * The proper way to walk the jcr chain is:
899 * It is possible to leave out the endeach_jcr(jcr), but
900 * in that case, the last jcr referenced must be explicitly
906 JCR *jcr_walk_start()
910 jcr = (JCR *)jcrs->first();
912 jcr->inc_use_count();
913 if (jcr->JobId > 0) {
914 Dmsg3(dbglvl, "Inc walk_start jid=%u use_count=%d Job=%s\n",
915 jcr->JobId, jcr->use_count(), jcr->Job);
923 * Get next jcr from chain, and release current one
925 JCR *jcr_walk_next(JCR *prev_jcr)
930 jcr = (JCR *)jcrs->next(prev_jcr);
932 jcr->inc_use_count();
933 if (jcr->JobId > 0) {
934 Dmsg3(dbglvl, "Inc walk_next jid=%u use_count=%d Job=%s\n",
935 jcr->JobId, jcr->use_count(), jcr->Job);
946 * Release last jcr referenced
948 void jcr_walk_end(JCR *jcr)
951 if (jcr->JobId > 0) {
952 Dmsg3(dbglvl, "Free walk_end jid=%u use_count=%d Job=%s\n",
953 jcr->JobId, jcr->use_count(), jcr->Job);
961 * Setup to call the timeout check routine every 30 seconds
962 * This routine will check any timers that have been enabled.
964 bool init_jcr_subsystem(void)
966 watchdog_t *wd = new_watchdog();
968 wd->one_shot = false;
969 wd->interval = 30; /* FIXME: should be configurable somewhere, even
970 if only with a #define */
971 wd->callback = jcr_timeout_check;
973 register_watchdog(wd);
978 static void jcr_timeout_check(watchdog_t *self)
984 Dmsg0(dbglvl, "Start JCR timeout checks\n");
986 /* Walk through all JCRs checking if any one is
987 * blocked for more than specified max time.
990 Dmsg2(dbglvl, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
991 if (jcr->JobId == 0) {
994 bs = jcr->store_bsock;
996 timer_start = bs->timer_start;
997 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
998 bs->timer_start = 0; /* turn off timer */
1000 Qmsg(jcr, M_ERROR, 0, _(
1001 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
1002 watchdog_time - timer_start);
1003 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1006 bs = jcr->file_bsock;
1008 timer_start = bs->timer_start;
1009 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1010 bs->timer_start = 0; /* turn off timer */
1011 bs->set_timed_out();
1012 Qmsg(jcr, M_ERROR, 0, _(
1013 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
1014 watchdog_time - timer_start);
1015 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1018 bs = jcr->dir_bsock;
1020 timer_start = bs->timer_start;
1021 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1022 bs->timer_start = 0; /* turn off timer */
1023 bs->set_timed_out();
1024 Qmsg(jcr, M_ERROR, 0, _(
1025 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
1026 watchdog_time - timer_start);
1027 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1033 Dmsg0(dbglvl, "Finished JCR timeout checks\n");
1037 * Return next JobId from comma separated list
1040 * 1 if next JobId returned
1041 * 0 if no more JobIds are in list
1042 * -1 there is an error
1044 int get_next_jobid_from_list(char **p, uint32_t *JobId)
1046 const int maxlen = 30;
1047 char jobid[maxlen+1];
1051 for (int i=0; i<maxlen; i++) {
1054 } else if (*q == ',') {
1061 if (jobid[0] == 0) {
1063 } else if (!is_a_number(jobid)) {
1064 return -1; /* error */
1067 *JobId = str_to_int64(jobid);
1072 * Timeout signal comes here
1074 extern "C" void timeout_handler(int sig)
1076 return; /* thus interrupting the function */
1079 /* Used to display specific daemon information after a fatal signal
1080 * (like B_DB in the director)
1082 #define MAX_DBG_HOOK 10
1083 static dbg_jcr_hook_t *dbg_jcr_hooks[MAX_DBG_HOOK];
1084 static int dbg_jcr_handler_count;
1086 void dbg_jcr_add_hook(dbg_jcr_hook_t *fct)
1088 ASSERT(dbg_jcr_handler_count < MAX_DBG_HOOK);
1089 dbg_jcr_hooks[dbg_jcr_handler_count++] = fct;
1095 * This function should be used ONLY after a fatal signal. We walk through the
1096 * JCR chain without doing any lock, bacula should not be running.
1098 void _dbg_print_jcr(FILE *fp)
1100 char buf1[128], buf2[128], buf3[128], buf4[128];
1105 fprintf(fp, "Attempt to dump current JCRs\n");
1107 for (JCR *jcr = (JCR *)jcrs->first(); jcr ; jcr = (JCR *)jcrs->next(jcr)) {
1108 if (!jcr) { /* protect us against something ? */
1112 fprintf(fp, "JCR=%p JobId=%i name=%s JobStatus=%c\n",
1113 jcr, jcr->JobId, jcr->Job, jcr->JobStatus);
1115 fprintf(fp, "\tuse_count=%i\n",
1118 /* KES -- removed non-portable code referencing pthread_t */
1119 fprintf(fp, "\tuse_count=%d\n", jcr->use_count());
1121 fprintf(fp, "\tJobType=%c JobLevel=%c\n",
1122 jcr->get_JobType(), jcr->get_JobLevel());
1123 bstrftime(buf1, sizeof(buf1), jcr->sched_time);
1124 bstrftime(buf2, sizeof(buf2), jcr->start_time);
1125 bstrftime(buf3, sizeof(buf3), jcr->end_time);
1126 bstrftime(buf4, sizeof(buf4), jcr->wait_time);
1127 fprintf(fp, "\tsched_time=%s start_time=%s\n\tend_time=%s wait_time=%s\n",
1128 buf1, buf2, buf3, buf4);
1129 fprintf(fp, "\tdequeing=%i\n", jcr->dequeuing);
1130 fprintf(fp, "\tdb=%p db_batch=%p batch_started=%i\n",
1131 jcr->db, jcr->db_batch, jcr->batch_started);
1133 for(int i=0; i < dbg_jcr_handler_count; i++) {
1134 dbg_jcr_hook_t *fct = dbg_jcr_hooks[i];