]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/jcr.c
kes Create plugin instance only when FD job starts.
[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     int oldJobStatus = jcr->JobStatus;
675
676     Dmsg2(800, "set_jcr_job_status(%s, %c)\n", jcr->Job, JobStatus);
677     /* if wait state is new, we keep current time for watchdog MaxWaitTime */
678     switch (JobStatus) {
679        case JS_WaitFD:
680        case JS_WaitSD:
681        case JS_WaitMedia:
682        case JS_WaitMount:
683        case JS_WaitStoreRes:
684        case JS_WaitJobRes:
685        case JS_WaitClientRes:
686        case JS_WaitMaxJobs:
687        case JS_WaitPriority:
688          set_waittime = true;
689        default:
690          break;
691     }
692  
693    /*
694     * For a set of errors, ... keep the current status
695     *   so it isn't lost. For all others, set it.
696     */
697    Dmsg3(300, "jid=%u OnEntry JobStatus=%c set=%c\n", (uint32_t)jcr->JobId,
698          jcr->JobStatus, JobStatus);
699    switch (jcr->JobStatus) {
700    case JS_ErrorTerminated:
701    case JS_FatalError:
702    case JS_Canceled:
703       break;
704    case JS_Error:
705    case JS_Differences:
706       switch (JobStatus) {
707       case JS_ErrorTerminated:
708       case JS_FatalError:
709       case JS_Canceled:
710          /* Override more minor status */
711          jcr->JobStatus = JobStatus;
712          break;
713       default:
714          break;
715       }
716    /*
717     * For a set of Wait situation, keep old time.
718     */
719    case JS_WaitFD:
720    case JS_WaitSD:
721    case JS_WaitMedia:
722    case JS_WaitMount:
723    case JS_WaitStoreRes:
724    case JS_WaitJobRes:
725    case JS_WaitClientRes:
726    case JS_WaitMaxJobs:
727    case JS_WaitPriority:
728        set_waittime = false;    /* keep old time */
729    default:
730       jcr->JobStatus = JobStatus;
731       if (set_waittime) {
732          /* set it before JobStatus */
733          Dmsg0(800, "Setting wait_time\n");
734          jcr->wait_time = time(NULL);
735       }
736    }
737    if (oldJobStatus != jcr->JobStatus) {
738       Dmsg3(200, "jid=%u leave set_old_job_status=%c new_set=%c\n", (uint32_t)jcr->JobId,
739          oldJobStatus, JobStatus);
740 //    generate_plugin_event(jcr, bEventStatusChange, NULL);
741    }
742 }
743
744 #ifdef TRACE_JCR_CHAIN
745 static int lock_count = 0;
746 #endif
747
748 /*
749  * Lock the chain
750  */
751 #ifdef TRACE_JCR_CHAIN
752 static void b_lock_jcr_chain(const char *fname, int line)
753 #else
754 static void lock_jcr_chain()
755 #endif
756 {
757 #ifdef TRACE_JCR_CHAIN
758    Dmsg3(dbglvl, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
759 #endif
760    P(jcr_lock);
761 }
762
763 /*
764  * Unlock the chain
765  */
766 #ifdef TRACE_JCR_CHAIN
767 static void b_unlock_jcr_chain(const char *fname, int line)
768 #else
769 static void unlock_jcr_chain()
770 #endif
771 {
772 #ifdef TRACE_JCR_CHAIN
773    Dmsg3(dbglvl, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
774 #endif
775    V(jcr_lock);
776 }
777
778
779 /*
780  * Start walk of jcr chain
781  * The proper way to walk the jcr chain is:
782  *    JCR *jcr;
783  *    foreach_jcr(jcr) {
784  *      ...
785  *    }
786  *    endeach_jcr(jcr);
787  *
788  *  It is possible to leave out the endeach_jcr(jcr), but
789  *   in that case, the last jcr referenced must be explicitly
790  *   released with:
791  *
792  *    free_jcr(jcr);
793  *  
794  */
795 JCR *jcr_walk_start() 
796 {
797    JCR *jcr;
798    lock_jcr_chain();
799    jcr = (JCR *)jcrs->first();
800    if (jcr) {
801       jcr->inc_use_count();
802       if (jcr->JobId > 0) {
803          Dmsg3(dbglvl, "Inc walk_start jid=%u use_count=%d Job=%s\n", 
804             jcr->JobId, jcr->use_count(), jcr->Job);
805       }
806    }
807    unlock_jcr_chain();
808    return jcr;
809 }
810
811 /*
812  * Get next jcr from chain, and release current one
813  */
814 JCR *jcr_walk_next(JCR *prev_jcr)
815 {
816    JCR *jcr;
817
818    lock_jcr_chain();
819    jcr = (JCR *)jcrs->next(prev_jcr);
820    if (jcr) {
821       jcr->inc_use_count();
822       if (jcr->JobId > 0) {
823          Dmsg3(dbglvl, "Inc walk_next jid=%u use_count=%d Job=%s\n", 
824             jcr->JobId, jcr->use_count(), jcr->Job);
825       }
826    }
827    unlock_jcr_chain();
828    if (prev_jcr) {
829       free_jcr(prev_jcr);
830    }
831    return jcr;
832 }
833
834 /*
835  * Release last jcr referenced
836  */
837 void jcr_walk_end(JCR *jcr)
838 {
839    if (jcr) {
840       if (jcr->JobId > 0) {
841          Dmsg3(dbglvl, "Free walk_end jid=%u use_count=%d Job=%s\n", 
842             jcr->JobId, jcr->use_count(), jcr->Job);
843       }
844       free_jcr(jcr);
845    }
846 }
847
848
849 /*
850  * Setup to call the timeout check routine every 30 seconds
851  *  This routine will check any timers that have been enabled.
852  */
853 bool init_jcr_subsystem(void)
854 {
855    watchdog_t *wd = new_watchdog();
856
857    wd->one_shot = false;
858    wd->interval = 30;   /* FIXME: should be configurable somewhere, even
859                          if only with a #define */
860    wd->callback = jcr_timeout_check;
861
862    register_watchdog(wd);
863
864    return true;
865 }
866
867 static void jcr_timeout_check(watchdog_t *self)
868 {
869    JCR *jcr;
870    BSOCK *bs;
871    time_t timer_start;
872
873    Dmsg0(dbglvl, "Start JCR timeout checks\n");
874
875    /* Walk through all JCRs checking if any one is
876     * blocked for more than specified max time.
877     */
878    foreach_jcr(jcr) {
879       Dmsg2(dbglvl, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
880       if (jcr->JobId == 0) {
881          continue;
882       }
883       bs = jcr->store_bsock;
884       if (bs) {
885          timer_start = bs->timer_start;
886          if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
887             bs->timer_start = 0;      /* turn off timer */
888             bs->set_timed_out();
889             Qmsg(jcr, M_ERROR, 0, _(
890 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
891                  watchdog_time - timer_start);
892             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
893          }
894       }
895       bs = jcr->file_bsock;
896       if (bs) {
897          timer_start = bs->timer_start;
898          if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
899             bs->timer_start = 0;      /* turn off timer */
900             bs->set_timed_out();
901             Qmsg(jcr, M_ERROR, 0, _(
902 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
903                  watchdog_time - timer_start);
904             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
905          }
906       }
907       bs = jcr->dir_bsock;
908       if (bs) {
909          timer_start = bs->timer_start;
910          if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
911             bs->timer_start = 0;      /* turn off timer */
912             bs->set_timed_out();
913             Qmsg(jcr, M_ERROR, 0, _(
914 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
915                  watchdog_time - timer_start);
916             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
917          }
918       }
919    }
920    endeach_jcr(jcr);
921
922    Dmsg0(dbglvl, "Finished JCR timeout checks\n");
923 }
924
925 /*
926  * Timeout signal comes here
927  */
928 extern "C" void timeout_handler(int sig)
929 {
930    return;                            /* thus interrupting the function */
931 }