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