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