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