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