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 jcr->job_end_push.init(1, false);
350 jcr->sched_time = time(NULL);
351 jcr->daemon_free_jcr = daemon_free_jcr; /* plug daemon free routine */
353 jcr->inc_use_count();
354 jcr->VolumeName = get_pool_memory(PM_FNAME);
355 jcr->VolumeName[0] = 0;
356 jcr->errmsg = get_pool_memory(PM_MESSAGE);
358 /* Setup some dummy values */
359 bstrncpy(jcr->Job, "*System*", sizeof(jcr->Job));
361 jcr->setJobType(JT_SYSTEM); /* internal job until defined */
362 jcr->setJobLevel(L_NONE);
363 jcr->setJobStatus(JS_Created); /* ready to run */
365 sigtimer.sa_flags = 0;
366 sigtimer.sa_handler = timeout_handler;
367 sigfillset(&sigtimer.sa_mask);
368 sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL);
371 * Locking jobs is a global lock that is needed
372 * so that the Director can stop new jobs from being
373 * added to the jcr chain while it processes a new
374 * conf file and does the job_end_push().
379 jcrs = New(dlist(jcr, &jcr->link));
390 * Remove a JCR from the chain
391 * NOTE! The chain must be locked prior to calling
394 static void remove_jcr(JCR *jcr)
396 Dmsg0(dbglvl, "Enter remove_jcr\n");
398 Emsg0(M_ABORT, 0, _("NULL jcr.\n"));
401 Dmsg0(dbglvl, "Leave remove_jcr\n");
405 * Free stuff common to all JCRs. N.B. Be careful to include only
406 * generic stuff in the common part of the jcr.
408 static void free_common_jcr(JCR *jcr)
410 jcr->destroy_mutex();
412 if (jcr->msg_queue) {
413 delete jcr->msg_queue;
414 jcr->msg_queue = NULL;
416 close_msg(jcr); /* close messages for this job */
418 /* do this after closing messages */
419 if (jcr->client_name) {
420 free_pool_memory(jcr->client_name);
421 jcr->client_name = NULL;
425 free_pool_memory(jcr->attr);
429 if (jcr->sd_auth_key) {
430 free(jcr->sd_auth_key);
431 jcr->sd_auth_key = NULL;
433 if (jcr->VolumeName) {
434 free_pool_memory(jcr->VolumeName);
435 jcr->VolumeName = NULL;
438 if (jcr->dir_bsock) {
439 bnet_close(jcr->dir_bsock);
440 jcr->dir_bsock = NULL;
443 free_pool_memory(jcr->errmsg);
450 if (jcr->RegexWhere) {
451 free(jcr->RegexWhere);
452 jcr->RegexWhere = NULL;
454 if (jcr->where_bregexp) {
455 free_bregexps(jcr->where_bregexp);
456 delete jcr->where_bregexp;
457 jcr->where_bregexp = NULL;
459 if (jcr->cached_path) {
460 free_pool_memory(jcr->cached_path);
461 jcr->cached_path = NULL;
465 free_guid_list(jcr->id_list);
468 remove_jcr_from_tsd(jcr);
473 * Global routine to free a jcr
476 void b_free_jcr(const char *file, int line, JCR *jcr)
478 struct s_last_job *je;
480 Dmsg3(dbglvl, "Enter free_jcr jid=%u from %s:%d\n", jcr->JobId, file, line);
484 void free_jcr(JCR *jcr)
486 struct s_last_job *je;
488 Dmsg3(dbglvl, "Enter free_jcr jid=%u use_count=%d Job=%s\n",
489 jcr->JobId, jcr->use_count(), jcr->Job);
494 jcr->dec_use_count(); /* decrement use count */
495 if (jcr->use_count() < 0) {
496 Jmsg2(jcr, M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
497 jcr->use_count(), jcr->JobId);
499 if (jcr->JobId > 0) {
500 Dmsg3(dbglvl, "Dec free_jcr jid=%u use_count=%d Job=%s\n",
501 jcr->JobId, jcr->use_count(), jcr->Job);
503 if (jcr->use_count() > 0) { /* if in use */
507 if (jcr->JobId > 0) {
508 Dmsg3(dbglvl, "remove jcr jid=%u use_count=%d Job=%s\n",
509 jcr->JobId, jcr->use_count(), jcr->Job);
511 remove_jcr(jcr); /* remove Jcr from chain */
514 dequeue_messages(jcr);
515 job_end_pop(jcr); /* pop and call hooked routines */
517 Dmsg1(dbglvl, "End job=%d\n", jcr->JobId);
519 /* Keep some statistics */
520 switch (jcr->get_JobType()) {
527 /* Keep list of last jobs, but not Console where JobId==0 */
528 if (jcr->JobId > 0) {
529 lock_last_jobs_list();
531 je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
532 memset(je, 0, sizeof(struct s_last_job)); /* zero in case unset fields */
533 je->Errors = jcr->JobErrors;
534 je->JobType = jcr->get_JobType();
535 je->JobId = jcr->JobId;
536 je->VolSessionId = jcr->VolSessionId;
537 je->VolSessionTime = jcr->VolSessionTime;
538 bstrncpy(je->Job, jcr->Job, sizeof(je->Job));
539 je->JobFiles = jcr->JobFiles;
540 je->JobBytes = jcr->JobBytes;
541 je->JobStatus = jcr->JobStatus;
542 je->JobLevel = jcr->get_JobLevel();
543 je->start_time = jcr->start_time;
544 je->end_time = time(NULL);
547 init_last_jobs_list();
549 last_jobs->append(je);
550 if (last_jobs->size() > max_last_jobs) {
551 je = (struct s_last_job *)last_jobs->first();
552 last_jobs->remove(je);
555 unlock_last_jobs_list();
562 if (jcr->daemon_free_jcr) {
563 jcr->daemon_free_jcr(jcr); /* call daemon free routine */
566 free_common_jcr(jcr);
567 close_msg(NULL); /* flush any daemon messages */
568 garbage_collect_memory_pool();
569 Dmsg0(dbglvl, "Exit free_jcr\n");
573 * Remove jcr from thread specific data, but
574 * but make sure it is us who are attached.
576 void remove_jcr_from_tsd(JCR *jcr)
578 JCR *tjcr = get_jcr_from_tsd();
580 set_jcr_in_tsd(INVALID_JCR);
585 * Put this jcr in the thread specifc data
587 void set_jcr_in_tsd(JCR *jcr)
589 int status = pthread_setspecific(jcr_key, (void *)jcr);
592 Jmsg1(jcr, M_ABORT, 0, _("pthread_setspecific failed: ERR=%s\n"), be.bstrerror(status));
597 * Give me the jcr that is attached to this thread
599 JCR *get_jcr_from_tsd()
601 JCR *jcr = (JCR *)pthread_getspecific(jcr_key);
602 // printf("get_jcr_from_tsd: jcr=%p\n", jcr);
603 /* set any INVALID_JCR to NULL which the rest of Bacula understands */
604 if (jcr == INVALID_JCR) {
612 * Find which JobId corresponds to the current thread
614 uint32_t get_jobid_from_tsd()
618 jcr = get_jcr_from_tsd();
619 // printf("get_jobid_from_tsr: jcr=%p\n", jcr);
621 JobId = (uint32_t)jcr->JobId;
627 * Given a JobId, find the JCR
628 * Returns: jcr on success
631 JCR *get_jcr_by_id(uint32_t JobId)
636 if (jcr->JobId == JobId) {
637 jcr->inc_use_count();
638 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
639 jcr->JobId, jcr->use_count(), jcr->Job);
648 * Given a thread id, find the JobId
649 * Returns: JobId on success
652 uint32_t get_jobid_from_tid(pthread_t tid)
658 if (pthread_equal(jcr->my_thread_id, tid)) {
672 * Given a SessionId and SessionTime, find the JCR
673 * Returns: jcr on success
676 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
681 if (jcr->VolSessionId == SessionId &&
682 jcr->VolSessionTime == SessionTime) {
683 jcr->inc_use_count();
684 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
685 jcr->JobId, jcr->use_count(), jcr->Job);
695 * Given a Job, find the JCR
696 * compares on the number of characters in Job
697 * thus allowing partial matches.
698 * Returns: jcr on success
701 JCR *get_jcr_by_partial_name(char *Job)
711 if (strncmp(Job, jcr->Job, len) == 0) {
712 jcr->inc_use_count();
713 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
714 jcr->JobId, jcr->use_count(), jcr->Job);
724 * Given a Job, find the JCR
725 * requires an exact match of names.
726 * Returns: jcr on success
729 JCR *get_jcr_by_full_name(char *Job)
737 if (strcmp(jcr->Job, Job) == 0) {
738 jcr->inc_use_count();
739 Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n",
740 jcr->JobId, jcr->use_count(), jcr->Job);
749 * Priority runs from 0 (lowest) to 10 (highest)
751 static int get_status_priority(int JobStatus)
755 case JS_ErrorTerminated:
772 static void update_wait_time(JCR *jcr, int newJobStatus)
774 bool enter_in_waittime;
775 int oldJobStatus = jcr->JobStatus;
777 switch (newJobStatus) {
782 case JS_WaitStoreRes:
784 case JS_WaitClientRes:
786 case JS_WaitPriority:
787 enter_in_waittime = true;
790 enter_in_waittime = false; /* not a Wait situation */
795 * If we were previously waiting and are not any more
796 * we want to update the wait_time variable, which is
797 * the start of waiting.
799 switch (oldJobStatus) {
804 case JS_WaitStoreRes:
806 case JS_WaitClientRes:
808 case JS_WaitPriority:
809 if (!enter_in_waittime) { /* we get out the wait time */
810 jcr->wait_time_sum += (time(NULL) - jcr->wait_time);
815 /* if wait state is new, we keep current time for watchdog MaxWaitTime */
817 if (enter_in_waittime) {
818 jcr->wait_time = time(NULL);
824 void set_jcr_job_status(JCR *jcr, int JobStatus)
826 jcr->setJobStatus(JobStatus);
829 void JCR::setJobStatus(int JobStatus)
832 int priority, old_priority;
833 int oldJobStatus = JobStatus;
834 priority = get_status_priority(JobStatus);
835 old_priority = get_status_priority(oldJobStatus);
837 Dmsg2(800, "set_jcr_job_status(%s, %c)\n", Job, JobStatus);
839 /* Update wait_time depending on newJobStatus and oldJobStatus */
840 update_wait_time(this, JobStatus);
843 * For a set of errors, ... keep the current status
844 * so it isn't lost. For all others, set it.
846 Dmsg3(300, "jid=%u OnEntry JobStatus=%c set=%c\n", (uint32_t)JobId,
847 JobStatus, JobStatus);
848 if (priority >= old_priority) {
849 jcr->JobStatus = JobStatus; /* replace with new priority */
852 if (oldJobStatus != jcr->JobStatus) {
853 Dmsg3(200, "jid=%u leave set_old_job_status=%c new_set=%c\n", (uint32_t)jcr->JobId,
854 oldJobStatus, JobStatus);
855 // generate_plugin_event(jcr, bEventStatusChange, NULL);
859 #ifdef TRACE_JCR_CHAIN
860 static int lock_count = 0;
866 #ifdef TRACE_JCR_CHAIN
867 static void b_lock_jcr_chain(const char *fname, int line)
869 static void lock_jcr_chain()
872 #ifdef TRACE_JCR_CHAIN
873 Dmsg3(dbglvl, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
881 #ifdef TRACE_JCR_CHAIN
882 static void b_unlock_jcr_chain(const char *fname, int line)
884 static void unlock_jcr_chain()
887 #ifdef TRACE_JCR_CHAIN
888 Dmsg3(dbglvl, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
894 * Start walk of jcr chain
895 * The proper way to walk the jcr chain is:
902 * It is possible to leave out the endeach_jcr(jcr), but
903 * in that case, the last jcr referenced must be explicitly
909 JCR *jcr_walk_start()
913 jcr = (JCR *)jcrs->first();
915 jcr->inc_use_count();
916 if (jcr->JobId > 0) {
917 Dmsg3(dbglvl, "Inc walk_start jid=%u use_count=%d Job=%s\n",
918 jcr->JobId, jcr->use_count(), jcr->Job);
926 * Get next jcr from chain, and release current one
928 JCR *jcr_walk_next(JCR *prev_jcr)
933 jcr = (JCR *)jcrs->next(prev_jcr);
935 jcr->inc_use_count();
936 if (jcr->JobId > 0) {
937 Dmsg3(dbglvl, "Inc walk_next jid=%u use_count=%d Job=%s\n",
938 jcr->JobId, jcr->use_count(), jcr->Job);
949 * Release last jcr referenced
951 void jcr_walk_end(JCR *jcr)
954 if (jcr->JobId > 0) {
955 Dmsg3(dbglvl, "Free walk_end jid=%u use_count=%d Job=%s\n",
956 jcr->JobId, jcr->use_count(), jcr->Job);
964 * Setup to call the timeout check routine every 30 seconds
965 * This routine will check any timers that have been enabled.
967 bool init_jcr_subsystem(void)
969 watchdog_t *wd = new_watchdog();
971 wd->one_shot = false;
972 wd->interval = 30; /* FIXME: should be configurable somewhere, even
973 if only with a #define */
974 wd->callback = jcr_timeout_check;
976 register_watchdog(wd);
981 static void jcr_timeout_check(watchdog_t *self)
987 Dmsg0(dbglvl, "Start JCR timeout checks\n");
989 /* Walk through all JCRs checking if any one is
990 * blocked for more than specified max time.
993 Dmsg2(dbglvl, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
994 if (jcr->JobId == 0) {
997 bs = jcr->store_bsock;
999 timer_start = bs->timer_start;
1000 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1001 bs->timer_start = 0; /* turn off timer */
1002 bs->set_timed_out();
1003 Qmsg(jcr, M_ERROR, 0, _(
1004 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
1005 watchdog_time - timer_start);
1006 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1009 bs = jcr->file_bsock;
1011 timer_start = bs->timer_start;
1012 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1013 bs->timer_start = 0; /* turn off timer */
1014 bs->set_timed_out();
1015 Qmsg(jcr, M_ERROR, 0, _(
1016 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
1017 watchdog_time - timer_start);
1018 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1021 bs = jcr->dir_bsock;
1023 timer_start = bs->timer_start;
1024 if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1025 bs->timer_start = 0; /* turn off timer */
1026 bs->set_timed_out();
1027 Qmsg(jcr, M_ERROR, 0, _(
1028 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
1029 watchdog_time - timer_start);
1030 pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1036 Dmsg0(dbglvl, "Finished JCR timeout checks\n");
1040 * Return next JobId from comma separated list
1043 * 1 if next JobId returned
1044 * 0 if no more JobIds are in list
1045 * -1 there is an error
1047 int get_next_jobid_from_list(char **p, uint32_t *JobId)
1049 const int maxlen = 30;
1050 char jobid[maxlen+1];
1054 for (int i=0; i<maxlen; i++) {
1057 } else if (*q == ',') {
1064 if (jobid[0] == 0) {
1066 } else if (!is_a_number(jobid)) {
1067 return -1; /* error */
1070 *JobId = str_to_int64(jobid);
1075 * Timeout signal comes here
1077 extern "C" void timeout_handler(int sig)
1079 return; /* thus interrupting the function */
1082 /* Used to display specific daemon information after a fatal signal
1083 * (like B_DB in the director)
1085 #define MAX_DBG_HOOK 10
1086 static dbg_jcr_hook_t *dbg_jcr_hooks[MAX_DBG_HOOK];
1087 static int dbg_jcr_handler_count;
1089 void dbg_jcr_add_hook(dbg_jcr_hook_t *fct)
1091 ASSERT(dbg_jcr_handler_count < MAX_DBG_HOOK);
1092 dbg_jcr_hooks[dbg_jcr_handler_count++] = fct;
1098 * This function should be used ONLY after a fatal signal. We walk through the
1099 * JCR chain without doing any lock, bacula should not be running.
1101 void _dbg_print_jcr(FILE *fp)
1103 char buf1[128], buf2[128], buf3[128], buf4[128];
1108 fprintf(fp, "Attempt to dump current JCRs\n");
1110 for (JCR *jcr = (JCR *)jcrs->first(); jcr ; jcr = (JCR *)jcrs->next(jcr)) {
1111 if (!jcr) { /* protect us against something ? */
1115 fprintf(fp, "JCR=%p JobId=%i name=%s JobStatus=%c\n",
1116 jcr, jcr->JobId, jcr->Job, jcr->JobStatus);
1118 fprintf(fp, "\tuse_count=%i\n",
1121 /* KES -- removed non-portable code referencing pthread_t */
1122 fprintf(fp, "\tuse_count=%d\n", jcr->use_count());
1124 fprintf(fp, "\tJobType=%c JobLevel=%c\n",
1125 jcr->get_JobType(), jcr->get_JobLevel());
1126 bstrftime(buf1, sizeof(buf1), jcr->sched_time);
1127 bstrftime(buf2, sizeof(buf2), jcr->start_time);
1128 bstrftime(buf3, sizeof(buf3), jcr->end_time);
1129 bstrftime(buf4, sizeof(buf4), jcr->wait_time);
1130 fprintf(fp, "\tsched_time=%s start_time=%s\n\tend_time=%s wait_time=%s\n",
1131 buf1, buf2, buf3, buf4);
1132 fprintf(fp, "\tdequeing=%i\n", jcr->dequeuing);
1133 fprintf(fp, "\tdb=%p db_batch=%p batch_started=%i\n",
1134 jcr->db, jcr->db_batch, jcr->batch_started);
1136 for(int i=0; i < dbg_jcr_handler_count; i++) {
1137 dbg_jcr_hook_t *fct = dbg_jcr_hooks[i];