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