]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/jcr.c
Put jcr.c on dbglvl
[bacula/bacula] / bacula / src / lib / jcr.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
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.
27 */
28 /*
29  * Manipulation routines for Job Control Records and
30  *  handling of last_jobs_list.
31  *
32  *  Kern E. Sibbald, December 2000
33  *
34  *  Version $Id$
35  *
36  *  These routines are thread safe.
37  *
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.
50  *
51  */
52
53 #include "bacula.h"
54 #include "jcr.h"
55
56 const int dbglvl = 3400;
57
58 /* External variables we reference */
59 extern time_t watchdog_time;
60
61 /* External referenced functions */
62 void free_bregexps(alist *bregexps);
63
64 /* Forward referenced functions */
65 extern "C" void timeout_handler(int sig);
66 static void jcr_timeout_check(watchdog_t *self);
67 #ifdef TRACE_JCR_CHAIN
68 static void b_lock_jcr_chain(const char *filen, int line);
69 static void b_unlock_jcr_chain(const char *filen, int line);
70 #define lock_jcr_chain() b_lock_jcr_chain(__FILE__, __LINE__);
71 #define unlock_jcr_chain() b_unlock_jcr_chain(__FILE__, __LINE__);
72 #else
73 static void lock_jcr_chain();
74 static void unlock_jcr_chain();
75 #endif
76
77
78 int num_jobs_run;
79 dlist *last_jobs = NULL;
80 const int max_last_jobs = 10;
81  
82 static dlist *jcrs = NULL;            /* JCR chain */
83 static pthread_mutex_t jcr_lock = PTHREAD_MUTEX_INITIALIZER;
84
85 static pthread_mutex_t job_start_mutex = PTHREAD_MUTEX_INITIALIZER;
86
87 static pthread_mutex_t last_jobs_mutex = PTHREAD_MUTEX_INITIALIZER;
88
89
90 void lock_jobs()
91 {
92    P(job_start_mutex);
93 }
94
95 void unlock_jobs()
96 {
97    V(job_start_mutex);
98 }
99
100 void init_last_jobs_list()
101 {
102    JCR *jcr = NULL;
103    struct s_last_job *job_entry = NULL;
104    if (!last_jobs) {
105       last_jobs = New(dlist(job_entry, &job_entry->link));
106    }
107    if (!jcrs) {
108       jcrs = New(dlist(jcr, &jcr->link));
109    }
110 }
111
112 void term_last_jobs_list()
113 {
114    if (last_jobs) {
115       while (!last_jobs->empty()) {
116          void *je = last_jobs->first();
117          last_jobs->remove(je);
118          free(je);
119       }
120       delete last_jobs;
121       last_jobs = NULL;
122    }
123    if (jcrs) {
124       delete jcrs;
125       jcrs = NULL;
126    }
127 }
128
129 bool read_last_jobs_list(int fd, uint64_t addr)
130 {
131    struct s_last_job *je, job;
132    uint32_t num;
133
134    Dmsg1(100, "read_last_jobs seek to %d\n", (int)addr);
135    if (addr == 0 || lseek(fd, (off_t)addr, SEEK_SET) < 0) {
136       return false;
137    }
138    if (read(fd, &num, sizeof(num)) != sizeof(num)) {
139       return false;
140    }
141    Dmsg1(100, "Read num_items=%d\n", num);
142    if (num > 4 * max_last_jobs) {  /* sanity check */
143       return false;
144    }
145    for ( ; num; num--) {
146       if (read(fd, &job, sizeof(job)) != sizeof(job)) {
147          berrno be;
148          Pmsg1(000, "Read job entry. ERR=%s\n", be.bstrerror());
149          return false;
150       }
151       if (job.JobId > 0) {
152          je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
153          memcpy((char *)je, (char *)&job, sizeof(job));
154          if (!last_jobs) {
155             init_last_jobs_list();
156          }
157          last_jobs->append(je);
158          if (last_jobs->size() > max_last_jobs) {
159             je = (struct s_last_job *)last_jobs->first();
160             last_jobs->remove(je);
161             free(je);
162          }
163       }
164    }
165    return true;
166 }
167
168 uint64_t write_last_jobs_list(int fd, uint64_t addr)
169 {
170    struct s_last_job *je;
171    uint32_t num;
172
173    Dmsg1(100, "write_last_jobs seek to %d\n", (int)addr);
174    if (lseek(fd, (off_t)addr, SEEK_SET) < 0) {
175       return 0;
176    }
177    if (last_jobs) {
178       /* First record is number of entires */
179       num = last_jobs->size();
180       if (write(fd, &num, sizeof(num)) != sizeof(num)) {
181          berrno be;
182          Pmsg1(000, "Error writing num_items: ERR=%s\n", be.bstrerror());
183          return 0;
184       }
185       foreach_dlist(je, last_jobs) {
186          if (write(fd, je, sizeof(struct s_last_job)) != sizeof(struct s_last_job)) {
187             berrno be;
188             Pmsg1(000, "Error writing job: ERR=%s\n", be.bstrerror());
189             return 0;
190          }
191       }
192    }
193    /* Return current address */
194    ssize_t stat = lseek(fd, 0, SEEK_CUR);
195    if (stat < 0) {
196       stat = 0;
197    }
198    return stat;
199
200 }
201
202 void lock_last_jobs_list()
203 {
204    P(last_jobs_mutex);
205 }
206
207 void unlock_last_jobs_list()
208 {
209    V(last_jobs_mutex);
210 }
211
212 /*
213  * Push a subroutine address into the job end callback stack
214  */
215 void job_end_push(JCR *jcr, void job_end_cb(JCR *jcr,void *), void *ctx)
216 {
217    jcr->job_end_push.append((void *)job_end_cb);
218    jcr->job_end_push.append(ctx);
219 }
220
221 /* Pop each job_end subroutine and call it */
222 static void job_end_pop(JCR *jcr)
223 {
224    void (*job_end_cb)(JCR *jcr, void *ctx);
225    void *ctx;
226    for (int i=jcr->job_end_push.size()-1; i > 0; ) {
227       ctx = jcr->job_end_push.get(i--);
228       job_end_cb = (void (*)(JCR *,void *))jcr->job_end_push.get(i--);
229       job_end_cb(jcr, ctx);
230    }
231 }
232
233 /*
234  * Create a Job Control Record and link it into JCR chain
235  * Returns newly allocated JCR
236  * Note, since each daemon has a different JCR, he passes
237  *  us the size.
238  */
239 JCR *new_jcr(int size, JCR_free_HANDLER *daemon_free_jcr)
240 {
241    JCR *jcr;
242    MQUEUE_ITEM *item = NULL;
243    struct sigaction sigtimer;
244
245    Dmsg0(dbglvl, "Enter new_jcr\n");
246    jcr = (JCR *)malloc(size);
247    memset(jcr, 0, size);
248    jcr->my_thread_id = pthread_self();
249    jcr->msg_queue = New(dlist(item, &item->link));
250    jcr->job_end_push.init(1, false);
251    jcr->sched_time = time(NULL);
252    jcr->daemon_free_jcr = daemon_free_jcr;    /* plug daemon free routine */
253    jcr->init_mutex();
254    jcr->inc_use_count();   
255    jcr->VolumeName = get_pool_memory(PM_FNAME);
256    jcr->VolumeName[0] = 0;
257    jcr->errmsg = get_pool_memory(PM_MESSAGE);
258    jcr->errmsg[0] = 0;
259    /* Setup some dummy values */
260    bstrncpy(jcr->Job, "*System*", sizeof(jcr->Job));
261    jcr->JobId = 0;
262    jcr->JobType = JT_SYSTEM;          /* internal job until defined */
263    jcr->JobLevel = L_NONE;
264    set_jcr_job_status(jcr, JS_Created);       /* ready to run */
265
266    sigtimer.sa_flags = 0;
267    sigtimer.sa_handler = timeout_handler;
268    sigfillset(&sigtimer.sa_mask);
269    sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL);
270
271    /*
272     * Locking jobs is a global lock that is needed
273     * so that the Director can stop new jobs from being
274     * added to the jcr chain while it processes a new
275     * conf file and does the job_end_push().
276     */
277    lock_jobs();
278    lock_jcr_chain();
279    if (!jcrs) {
280       jcrs = New(dlist(jcr, &jcr->link));
281    }
282    jcrs->append(jcr);
283    unlock_jcr_chain();
284    unlock_jobs();
285
286    return jcr;
287 }
288
289
290 /*
291  * Remove a JCR from the chain
292  * NOTE! The chain must be locked prior to calling
293  *       this routine.
294  */
295 static void remove_jcr(JCR *jcr)
296 {
297    Dmsg0(dbglvl, "Enter remove_jcr\n");
298    if (!jcr) {
299       Emsg0(M_ABORT, 0, _("NULL jcr.\n"));
300    }
301    jcrs->remove(jcr);
302    Dmsg0(dbglvl, "Leave remove_jcr\n");
303 }
304
305 /*
306  * Free stuff common to all JCRs.  N.B. Be careful to include only
307  *  generic stuff in the common part of the jcr.
308  */
309 static void free_common_jcr(JCR *jcr)
310 {
311    struct s_last_job *je, last_job;
312
313    /* Keep some statistics */
314    switch (jcr->JobType) {
315    case JT_BACKUP:
316    case JT_VERIFY:
317    case JT_RESTORE:
318    case JT_MIGRATE:
319    case JT_COPY:
320    case JT_ADMIN:
321       num_jobs_run++;
322       last_job.Errors = jcr->Errors;
323       last_job.JobType = jcr->JobType;
324       last_job.JobId = jcr->JobId;
325       last_job.VolSessionId = jcr->VolSessionId;
326       last_job.VolSessionTime = jcr->VolSessionTime;
327       bstrncpy(last_job.Job, jcr->Job, sizeof(last_job.Job));
328       last_job.JobFiles = jcr->JobFiles;
329       last_job.JobBytes = jcr->JobBytes;
330       last_job.JobStatus = jcr->JobStatus;
331       last_job.JobLevel = jcr->JobLevel;
332       last_job.start_time = jcr->start_time;
333       last_job.end_time = time(NULL);
334       /* Keep list of last jobs, but not Console where JobId==0 */
335       if (last_job.JobId > 0) {
336          je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
337          memcpy((char *)je, (char *)&last_job, sizeof(last_job));
338          if (!last_jobs) {
339             init_last_jobs_list();
340          }
341          last_jobs->append(je);
342          if (last_jobs->size() > max_last_jobs) {
343             je = (struct s_last_job *)last_jobs->first();
344             last_jobs->remove(je);
345             free(je);
346          }
347       }
348       break;
349    default:
350       break;
351    }
352    jcr->destroy_mutex();
353
354    if (jcr->msg_queue) {
355       delete jcr->msg_queue;
356       jcr->msg_queue = NULL;
357    }
358    close_msg(jcr);                    /* close messages for this job */
359
360    /* do this after closing messages */
361    if (jcr->client_name) {
362       free_pool_memory(jcr->client_name);
363       jcr->client_name = NULL;
364    }
365
366    if (jcr->attr) {
367       free_pool_memory(jcr->attr);
368       jcr->attr = NULL;
369    }
370
371    if (jcr->sd_auth_key) {
372       free(jcr->sd_auth_key);
373       jcr->sd_auth_key = NULL;
374    }
375    if (jcr->VolumeName) {
376       free_pool_memory(jcr->VolumeName);
377       jcr->VolumeName = NULL;
378    }
379
380    if (jcr->dir_bsock) {
381       bnet_close(jcr->dir_bsock);
382       jcr->dir_bsock = NULL;
383    }
384    if (jcr->errmsg) {
385       free_pool_memory(jcr->errmsg);
386       jcr->errmsg = NULL;
387    }
388    if (jcr->where) {
389       free(jcr->where);
390       jcr->where = NULL;
391    }
392    if (jcr->RegexWhere) {
393       free(jcr->RegexWhere);
394       jcr->RegexWhere = NULL;
395    }
396    if (jcr->where_bregexp) {
397       free_bregexps(jcr->where_bregexp);
398       delete jcr->where_bregexp;
399       jcr->where_bregexp = NULL;
400    }
401    if (jcr->cached_path) {
402       free_pool_memory(jcr->cached_path);
403       jcr->cached_path = NULL;
404       jcr->cached_pnl = 0;
405    }
406    if (jcr->id_list) {
407       free_guid_list(jcr->id_list);
408       jcr->id_list = NULL;
409    }
410    free(jcr);
411 }
412
413 /*
414  * Global routine to free a jcr
415  */
416 #ifdef DEBUG
417 void b_free_jcr(const char *file, int line, JCR *jcr)
418 {
419    Dmsg3(dbglvl, "Enter free_jcr 0x%x from %s:%d\n", jcr, file, line);
420
421 #else
422
423 void free_jcr(JCR *jcr)
424 {
425
426    Dmsg2(dbglvl, "Enter free_jcr 0x%x job=%d\n", jcr, jcr->JobId);
427
428 #endif
429
430    dequeue_messages(jcr);
431    lock_jcr_chain();
432    jcr->dec_use_count();              /* decrement use count */
433    if (jcr->use_count() < 0) {
434       Emsg2(M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
435          jcr->use_count(), jcr->JobId);
436    }
437    Dmsg3(dbglvl, "Dec free_jcr 0x%x use_count=%d jobid=%d\n", jcr, jcr->use_count(), jcr->JobId);
438    if (jcr->use_count() > 0) {          /* if in use */
439       unlock_jcr_chain();
440       Dmsg3(dbglvl, "free_jcr 0x%x job=%d use_count=%d\n", jcr, jcr->JobId, jcr->use_count());
441       return;
442    }
443
444    remove_jcr(jcr);                   /* remove Jcr from chain */
445    unlock_jcr_chain();
446
447    job_end_pop(jcr);                  /* pop and call hooked routines */
448
449    Dmsg1(dbglvl, "End job=%d\n", jcr->JobId);
450    if (jcr->daemon_free_jcr) {
451       jcr->daemon_free_jcr(jcr);      /* call daemon free routine */
452    }
453    free_common_jcr(jcr);
454    close_msg(NULL);                   /* flush any daemon messages */
455    garbage_collect_memory_pool();
456    Dmsg0(dbglvl, "Exit free_jcr\n");
457 }
458  
459 /*
460  * Find which JobId corresponds to the current thread
461  */
462 uint32_t get_jobid_from_tid()                              
463 {
464    return get_jobid_from_tid(pthread_self());
465 }
466
467 uint32_t get_jobid_from_tid(pthread_t tid)
468 {
469    JCR *jcr;
470    uint32_t JobId = 0;
471    foreach_jcr(jcr) {
472       if (pthread_equal(jcr->my_thread_id, tid)) {
473          JobId = (uint32_t)jcr->JobId;
474          break;
475       }
476    }
477    endeach_jcr(jcr);
478    return JobId;
479 }
480
481 /*
482  * Find the jcr that corresponds to the current thread
483  */
484 JCR *get_jcr_from_tid()                              
485 {
486    return get_jcr_from_tid(pthread_self());
487 }
488
489 JCR *get_jcr_from_tid(pthread_t tid)
490 {
491    JCR *jcr;
492    JCR *rtn_jcr = NULL;
493
494    foreach_jcr(jcr) {
495       if (pthread_equal(jcr->my_thread_id, tid)) {
496          rtn_jcr = jcr;
497          break;
498       }
499    }
500    endeach_jcr(jcr);
501    return rtn_jcr;
502 }
503
504
505
506 /*
507  * Given a JobId, find the JCR
508  *   Returns: jcr on success
509  *            NULL on failure
510  */
511 JCR *get_jcr_by_id(uint32_t JobId)
512 {
513    JCR *jcr;
514
515    foreach_jcr(jcr) {
516       if (jcr->JobId == JobId) {
517          jcr->inc_use_count();
518          Dmsg2(dbglvl, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
519          break;
520       }
521    }
522    endeach_jcr(jcr);
523    return jcr;
524 }
525
526 /*
527  * Given a SessionId and SessionTime, find the JCR
528  *   Returns: jcr on success
529  *            NULL on failure
530  */
531 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
532 {
533    JCR *jcr;
534
535    foreach_jcr(jcr) {
536       if (jcr->VolSessionId == SessionId &&
537           jcr->VolSessionTime == SessionTime) {
538          jcr->inc_use_count();
539          Dmsg2(dbglvl, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
540          break;
541       }
542    }
543    endeach_jcr(jcr);
544    return jcr;
545 }
546
547
548 /*
549  * Given a Job, find the JCR
550  *  compares on the number of characters in Job
551  *  thus allowing partial matches.
552  *   Returns: jcr on success
553  *            NULL on failure
554  */
555 JCR *get_jcr_by_partial_name(char *Job)
556 {
557    JCR *jcr;
558    int len;
559
560    if (!Job) {
561       return NULL;
562    }
563    len = strlen(Job);
564    foreach_jcr(jcr) {
565       if (strncmp(Job, jcr->Job, len) == 0) {
566          jcr->inc_use_count();
567          Dmsg2(dbglvl, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
568          break;
569       }
570    }
571    endeach_jcr(jcr);
572    return jcr;
573 }
574
575
576 /*
577  * Given a Job, find the JCR
578  *  requires an exact match of names.
579  *   Returns: jcr on success
580  *            NULL on failure
581  */
582 JCR *get_jcr_by_full_name(char *Job)
583 {
584    JCR *jcr;
585
586    if (!Job) {
587       return NULL;
588    }
589    foreach_jcr(jcr) {
590       if (strcmp(jcr->Job, Job) == 0) {
591          jcr->inc_use_count();
592          Dmsg2(dbglvl, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
593          break;
594       }
595    }
596    endeach_jcr(jcr);
597    return jcr;
598 }
599
600 void set_jcr_job_status(JCR *jcr, int JobStatus)
601 {
602    /*
603     * For a set of errors, ... keep the current status
604     *   so it isn't lost. For all others, set it.
605     */
606    Dmsg3(300, "jid=%u OnEntry JobStatus=%c set=%c\n", (uint32_t)jcr->JobId,
607          jcr->JobStatus, JobStatus);
608    switch (jcr->JobStatus) {
609    case JS_ErrorTerminated:
610    case JS_FatalError:
611    case JS_Canceled:
612       break;
613    case JS_Error:
614    case JS_Differences:
615       switch (JobStatus) {
616       case JS_ErrorTerminated:
617       case JS_FatalError:
618       case JS_Canceled:
619          /* Override more minor status */
620          jcr->JobStatus = JobStatus;
621          break;
622       }
623       break;
624    default:
625       jcr->JobStatus = JobStatus;
626    }
627    Dmsg3(100, "jid=%u OnExit JobStatus=%c set=%c\n", (uint32_t)jcr->JobId,
628          jcr->JobStatus, JobStatus);
629 }
630
631 #ifdef TRACE_JCR_CHAIN
632 static int lock_count = 0;
633 #endif
634
635 /*
636  * Lock the chain
637  */
638 #ifdef TRACE_JCR_CHAIN
639 static void b_lock_jcr_chain(const char *fname, int line)
640 #else
641 static void lock_jcr_chain()
642 #endif
643 {
644 #ifdef TRACE_JCR_CHAIN
645    Dmsg3(dbglvl, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
646 #endif
647    P(jcr_lock);
648 }
649
650 /*
651  * Unlock the chain
652  */
653 #ifdef TRACE_JCR_CHAIN
654 static void b_unlock_jcr_chain(const char *fname, int line)
655 #else
656 static void unlock_jcr_chain()
657 #endif
658 {
659 #ifdef TRACE_JCR_CHAIN
660    Dmsg3(dbglvl, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
661 #endif
662    V(jcr_lock);
663 }
664
665
666 /*
667  * Start walk of jcr chain
668  * The proper way to walk the jcr chain is:
669  *    JCR *jcr;
670  *    foreach_jcr(jcr) {
671  *      ...
672  *    }
673  *    endeach_jcr(jcr);
674  *
675  *  It is possible to leave out the endeach_jcr(jcr), but
676  *   in that case, the last jcr referenced must be explicitly
677  *   released with:
678  *
679  *    free_jcr(jcr);
680  *  
681  */
682 JCR *jcr_walk_start() 
683 {
684    JCR *jcr;
685    lock_jcr_chain();
686    jcr = (JCR *)jcrs->first();
687    if (jcr) {
688       jcr->inc_use_count();
689       Dmsg3(dbglvl, "Inc jcr_walk_start 0x%x job=%d use_count=%d\n", jcr, jcr->JobId, jcr->use_count());
690    }
691    unlock_jcr_chain();
692    return jcr;
693 }
694
695 /*
696  * Get next jcr from chain, and release current one
697  */
698 JCR *jcr_walk_next(JCR *prev_jcr)
699 {
700    JCR *jcr;
701
702    lock_jcr_chain();
703    jcr = (JCR *)jcrs->next(prev_jcr);
704    if (jcr) {
705       jcr->inc_use_count();
706       Dmsg3(dbglvl, "Inc jcr_walk_next 0x%x job=%d use_count=%d\n", jcr, jcr->JobId, jcr->use_count());
707    }
708    unlock_jcr_chain();
709    if (prev_jcr) {
710       free_jcr(prev_jcr);
711    }
712    return jcr;
713 }
714
715 /*
716  * Release last jcr referenced
717  */
718 void jcr_walk_end(JCR *jcr)
719 {
720    if (jcr) {
721       free_jcr(jcr);
722    }
723 }
724
725
726 /*
727  * Setup to call the timeout check routine every 30 seconds
728  *  This routine will check any timers that have been enabled.
729  */
730 bool init_jcr_subsystem(void)
731 {
732    watchdog_t *wd = new_watchdog();
733
734    wd->one_shot = false;
735    wd->interval = 30;   /* FIXME: should be configurable somewhere, even
736                          if only with a #define */
737    wd->callback = jcr_timeout_check;
738
739    register_watchdog(wd);
740
741    return true;
742 }
743
744 static void jcr_timeout_check(watchdog_t *self)
745 {
746    JCR *jcr;
747    BSOCK *fd;
748    time_t timer_start;
749
750    Dmsg0(dbglvl, "Start JCR timeout checks\n");
751
752    /* Walk through all JCRs checking if any one is
753     * blocked for more than specified max time.
754     */
755    foreach_jcr(jcr) {
756       Dmsg2(dbglvl, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
757       if (jcr->JobId == 0) {
758          continue;
759       }
760       fd = jcr->store_bsock;
761       if (fd) {
762          timer_start = fd->timer_start;
763          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
764             fd->timer_start = 0;      /* turn off timer */
765             fd->set_timed_out();
766             Jmsg(jcr, M_ERROR, 0, _(
767 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
768                  watchdog_time - timer_start);
769             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
770          }
771       }
772       fd = jcr->file_bsock;
773       if (fd) {
774          timer_start = fd->timer_start;
775          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
776             fd->timer_start = 0;      /* turn off timer */
777             fd->set_timed_out();
778             Jmsg(jcr, M_ERROR, 0, _(
779 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
780                  watchdog_time - timer_start);
781             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
782          }
783       }
784       fd = jcr->dir_bsock;
785       if (fd) {
786          timer_start = fd->timer_start;
787          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
788             fd->timer_start = 0;      /* turn off timer */
789             fd->set_timed_out();
790             Jmsg(jcr, M_ERROR, 0, _(
791 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
792                  watchdog_time - timer_start);
793             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
794          }
795       }
796    }
797    endeach_jcr(jcr);
798
799    Dmsg0(dbglvl, "Finished JCR timeout checks\n");
800 }
801
802 /*
803  * Timeout signal comes here
804  */
805 extern "C" void timeout_handler(int sig)
806 {
807    return;                            /* thus interrupting the function */
808 }