]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/jcr.c
kes Remove some duplicated code in tls.c
[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    free_getuser_cache();
405    free_getgroup_cache();
406    free(jcr);
407 }
408
409 /*
410  * Global routine to free a jcr
411  */
412 #ifdef DEBUG
413 void b_free_jcr(const char *file, int line, JCR *jcr)
414 {
415    Dmsg3(3400, "Enter free_jcr 0x%x from %s:%d\n", jcr, file, line);
416
417 #else
418
419 void free_jcr(JCR *jcr)
420 {
421
422    Dmsg2(3400, "Enter free_jcr 0x%x job=%d\n", jcr, jcr->JobId);
423
424 #endif
425
426    dequeue_messages(jcr);
427    lock_jcr_chain();
428    jcr->dec_use_count();              /* decrement use count */
429    if (jcr->use_count() < 0) {
430       Emsg2(M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
431          jcr->use_count(), jcr->JobId);
432    }
433    Dmsg3(3400, "Dec free_jcr 0x%x use_count=%d jobid=%d\n", jcr, jcr->use_count(), jcr->JobId);
434    if (jcr->use_count() > 0) {          /* if in use */
435       unlock_jcr_chain();
436       Dmsg3(3400, "free_jcr 0x%x job=%d use_count=%d\n", jcr, jcr->JobId, jcr->use_count());
437       return;
438    }
439
440    remove_jcr(jcr);                   /* remove Jcr from chain */
441    unlock_jcr_chain();
442
443    job_end_pop(jcr);                  /* pop and call hooked routines */
444
445    Dmsg1(3400, "End job=%d\n", jcr->JobId);
446    if (jcr->daemon_free_jcr) {
447       jcr->daemon_free_jcr(jcr);      /* call daemon free routine */
448    }
449    free_common_jcr(jcr);
450    close_msg(NULL);                   /* flush any daemon messages */
451    garbage_collect_memory_pool();
452    Dmsg0(3400, "Exit free_jcr\n");
453 }
454  
455 /*
456  * Find which JobId corresponds to the current thread
457  */
458 uint32_t get_jobid_from_tid()                              
459 {
460    return get_jobid_from_tid(pthread_self());
461 }
462
463 uint32_t get_jobid_from_tid(pthread_t tid)
464 {
465    JCR *jcr;
466    uint32_t JobId = 0;
467    foreach_jcr(jcr) {
468       if (pthread_equal(jcr->my_thread_id, tid)) {
469          JobId = (uint32_t)jcr->JobId;
470          break;
471       }
472    }
473    endeach_jcr(jcr);
474    return JobId;
475 }
476
477 /*
478  * Find the jcr that corresponds to the current thread
479  */
480 JCR *get_jcr_from_tid()                              
481 {
482    return get_jcr_from_tid(pthread_self());
483 }
484
485 JCR *get_jcr_from_tid(pthread_t tid)
486 {
487    JCR *jcr;
488    JCR *rtn_jcr = NULL;
489
490    foreach_jcr(jcr) {
491       if (pthread_equal(jcr->my_thread_id, tid)) {
492          rtn_jcr = jcr;
493          break;
494       }
495    }
496    endeach_jcr(jcr);
497    return rtn_jcr;
498 }
499
500
501
502 /*
503  * Given a JobId, find the JCR
504  *   Returns: jcr on success
505  *            NULL on failure
506  */
507 JCR *get_jcr_by_id(uint32_t JobId)
508 {
509    JCR *jcr;
510
511    foreach_jcr(jcr) {
512       if (jcr->JobId == JobId) {
513          jcr->inc_use_count();
514          Dmsg2(3400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
515          break;
516       }
517    }
518    endeach_jcr(jcr);
519    return jcr;
520 }
521
522 /*
523  * Given a SessionId and SessionTime, find the JCR
524  *   Returns: jcr on success
525  *            NULL on failure
526  */
527 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
528 {
529    JCR *jcr;
530
531    foreach_jcr(jcr) {
532       if (jcr->VolSessionId == SessionId &&
533           jcr->VolSessionTime == SessionTime) {
534          jcr->inc_use_count();
535          Dmsg2(3400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
536          break;
537       }
538    }
539    endeach_jcr(jcr);
540    return jcr;
541 }
542
543
544 /*
545  * Given a Job, find the JCR
546  *  compares on the number of characters in Job
547  *  thus allowing partial matches.
548  *   Returns: jcr on success
549  *            NULL on failure
550  */
551 JCR *get_jcr_by_partial_name(char *Job)
552 {
553    JCR *jcr;
554    int len;
555
556    if (!Job) {
557       return NULL;
558    }
559    len = strlen(Job);
560    foreach_jcr(jcr) {
561       if (strncmp(Job, jcr->Job, len) == 0) {
562          jcr->inc_use_count();
563          Dmsg2(3400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
564          break;
565       }
566    }
567    endeach_jcr(jcr);
568    return jcr;
569 }
570
571
572 /*
573  * Given a Job, find the JCR
574  *  requires an exact match of names.
575  *   Returns: jcr on success
576  *            NULL on failure
577  */
578 JCR *get_jcr_by_full_name(char *Job)
579 {
580    JCR *jcr;
581
582    if (!Job) {
583       return NULL;
584    }
585    foreach_jcr(jcr) {
586       if (strcmp(jcr->Job, Job) == 0) {
587          jcr->inc_use_count();
588          Dmsg2(3400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count());
589          break;
590       }
591    }
592    endeach_jcr(jcr);
593    return jcr;
594 }
595
596 void set_jcr_job_status(JCR *jcr, int JobStatus)
597 {
598    /*
599     * For a set of errors, ... keep the current status
600     *   so it isn't lost. For all others, set it.
601     */
602    switch (jcr->JobStatus) {
603    case JS_ErrorTerminated:
604    case JS_Error:
605    case JS_FatalError:
606    case JS_Differences:
607    case JS_Canceled:
608       break;
609    default:
610       jcr->JobStatus = JobStatus;
611    }
612 }
613
614 #ifdef TRACE_JCR_CHAIN
615 static int lock_count = 0;
616 #endif
617
618 /*
619  * Lock the chain
620  */
621 #ifdef TRACE_JCR_CHAIN
622 static void b_lock_jcr_chain(const char *fname, int line)
623 #else
624 static void lock_jcr_chain()
625 #endif
626 {
627 #ifdef TRACE_JCR_CHAIN
628    Dmsg3(3400, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
629 #endif
630    P(jcr_lock);
631 }
632
633 /*
634  * Unlock the chain
635  */
636 #ifdef TRACE_JCR_CHAIN
637 static void b_unlock_jcr_chain(const char *fname, int line)
638 #else
639 static void unlock_jcr_chain()
640 #endif
641 {
642 #ifdef TRACE_JCR_CHAIN
643    Dmsg3(3400, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
644 #endif
645    V(jcr_lock);
646 }
647
648
649 /*
650  * Start walk of jcr chain
651  * The proper way to walk the jcr chain is:
652  *    JCR *jcr;
653  *    foreach_jcr(jcr) {
654  *      ...
655  *    }
656  *    endeach_jcr(jcr);
657  *
658  *  It is possible to leave out the endeach_jcr(jcr), but
659  *   in that case, the last jcr referenced must be explicitly
660  *   released with:
661  *
662  *    free_jcr(jcr);
663  *  
664  */
665 JCR *jcr_walk_start() 
666 {
667    JCR *jcr;
668    lock_jcr_chain();
669    jcr = (JCR *)jcrs->first();
670    if (jcr) {
671       jcr->inc_use_count();
672       Dmsg3(3400, "Inc jcr_walk_start 0x%x job=%d use_count=%d\n", jcr, jcr->JobId, jcr->use_count());
673    }
674    unlock_jcr_chain();
675    return jcr;
676 }
677
678 /*
679  * Get next jcr from chain, and release current one
680  */
681 JCR *jcr_walk_next(JCR *prev_jcr)
682 {
683    JCR *jcr;
684
685    lock_jcr_chain();
686    jcr = (JCR *)jcrs->next(prev_jcr);
687    if (jcr) {
688       jcr->inc_use_count();
689       Dmsg3(3400, "Inc jcr_walk_next 0x%x job=%d use_count=%d\n", jcr, jcr->JobId, jcr->use_count());
690    }
691    unlock_jcr_chain();
692    if (prev_jcr) {
693       free_jcr(prev_jcr);
694    }
695    return jcr;
696 }
697
698 /*
699  * Release last jcr referenced
700  */
701 void jcr_walk_end(JCR *jcr)
702 {
703    if (jcr) {
704       free_jcr(jcr);
705    }
706 }
707
708
709 /*
710  * Setup to call the timeout check routine every 30 seconds
711  *  This routine will check any timers that have been enabled.
712  */
713 bool init_jcr_subsystem(void)
714 {
715    watchdog_t *wd = new_watchdog();
716
717    wd->one_shot = false;
718    wd->interval = 30;   /* FIXME: should be configurable somewhere, even
719                          if only with a #define */
720    wd->callback = jcr_timeout_check;
721
722    register_watchdog(wd);
723
724    return true;
725 }
726
727 static void jcr_timeout_check(watchdog_t *self)
728 {
729    JCR *jcr;
730    BSOCK *fd;
731    time_t timer_start;
732
733    Dmsg0(3400, "Start JCR timeout checks\n");
734
735    /* Walk through all JCRs checking if any one is
736     * blocked for more than specified max time.
737     */
738    foreach_jcr(jcr) {
739       Dmsg2(3400, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
740       if (jcr->JobId == 0) {
741          continue;
742       }
743       fd = jcr->store_bsock;
744       if (fd) {
745          timer_start = fd->timer_start;
746          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
747             fd->timer_start = 0;      /* turn off timer */
748             fd->set_timed_out();
749             Jmsg(jcr, M_ERROR, 0, _(
750 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
751                  watchdog_time - timer_start);
752             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
753          }
754       }
755       fd = jcr->file_bsock;
756       if (fd) {
757          timer_start = fd->timer_start;
758          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
759             fd->timer_start = 0;      /* turn off timer */
760             fd->set_timed_out();
761             Jmsg(jcr, M_ERROR, 0, _(
762 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
763                  watchdog_time - timer_start);
764             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
765          }
766       }
767       fd = jcr->dir_bsock;
768       if (fd) {
769          timer_start = fd->timer_start;
770          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
771             fd->timer_start = 0;      /* turn off timer */
772             fd->set_timed_out();
773             Jmsg(jcr, M_ERROR, 0, _(
774 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
775                  watchdog_time - timer_start);
776             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
777          }
778       }
779    }
780    endeach_jcr(jcr);
781
782    Dmsg0(3400, "Finished JCR timeout checks\n");
783 }
784
785 /*
786  * Timeout signal comes here
787  */
788 extern "C" void timeout_handler(int sig)
789 {
790    return;                            /* thus interrupting the function */
791 }