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