]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/jcr.c
ebl update
[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 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    jcr->destroy_mutex();
340
341    if (jcr->msg_queue) {
342       delete jcr->msg_queue;
343       jcr->msg_queue = NULL;
344    }
345    close_msg(jcr);                    /* close messages for this job */
346
347    /* do this after closing messages */
348    if (jcr->client_name) {
349       free_pool_memory(jcr->client_name);
350       jcr->client_name = NULL;
351    }
352
353    if (jcr->attr) {
354       free_pool_memory(jcr->attr);
355       jcr->attr = NULL;
356    }
357
358    if (jcr->sd_auth_key) {
359       free(jcr->sd_auth_key);
360       jcr->sd_auth_key = NULL;
361    }
362    if (jcr->VolumeName) {
363       free_pool_memory(jcr->VolumeName);
364       jcr->VolumeName = NULL;
365    }
366
367    if (jcr->dir_bsock) {
368       bnet_close(jcr->dir_bsock);
369       jcr->dir_bsock = NULL;
370    }
371    if (jcr->errmsg) {
372       free_pool_memory(jcr->errmsg);
373       jcr->errmsg = NULL;
374    }
375    if (jcr->where) {
376       free(jcr->where);
377       jcr->where = NULL;
378    }
379    if (jcr->RegexWhere) {
380       free(jcr->RegexWhere);
381       jcr->RegexWhere = NULL;
382    }
383    if (jcr->where_bregexp) {
384       free_bregexps(jcr->where_bregexp);
385       delete jcr->where_bregexp;
386       jcr->where_bregexp = NULL;
387    }
388    if (jcr->cached_path) {
389       free_pool_memory(jcr->cached_path);
390       jcr->cached_path = NULL;
391       jcr->cached_pnl = 0;
392    }
393    if (jcr->id_list) {
394       free_guid_list(jcr->id_list);
395       jcr->id_list = NULL;
396    }
397    /* Invalidate the tsd jcr data */
398    set_jcr_in_tsd(INVALID_JCR);
399    free(jcr);
400 }
401
402 /*
403  * Global routine to free a jcr
404  */
405 #ifdef DEBUG
406 void b_free_jcr(const char *file, int line, JCR *jcr)
407 {
408    struct s_last_job *je;
409
410    Dmsg3(dbglvl, "Enter free_jcr jid=%u from %s:%d\n", jcr->JobId, file, line);
411
412 #else
413
414 void free_jcr(JCR *jcr)
415 {
416    struct s_last_job *je;
417
418    Dmsg3(dbglvl, "Enter free_jcr jid=%u use_count=%d Job=%s\n", 
419          jcr->JobId, jcr->use_count(), jcr->Job);
420
421 #endif
422
423    dequeue_messages(jcr);
424    lock_jcr_chain();
425    jcr->dec_use_count();              /* decrement use count */
426    if (jcr->use_count() < 0) {
427       Emsg2(M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
428          jcr->use_count(), jcr->JobId);
429    }
430    if (jcr->JobId > 0) {
431       Dmsg3(dbglvl, "Dec free_jcr jid=%u use_count=%d Job=%s\n", 
432          jcr->JobId, jcr->use_count(), jcr->Job);
433    }
434    if (jcr->use_count() > 0) {          /* if in use */
435       unlock_jcr_chain();
436       return;
437    }
438    if (jcr->JobId > 0) {
439       Dmsg3(dbglvl, "remove jcr jid=%u use_count=%d Job=%s\n", 
440             jcr->JobId, jcr->use_count(), jcr->Job);
441    }
442    remove_jcr(jcr);                   /* remove Jcr from chain */
443
444    job_end_pop(jcr);                  /* pop and call hooked routines */
445
446    Dmsg1(dbglvl, "End job=%d\n", jcr->JobId);
447
448    /* Keep some statistics */
449    switch (jcr->JobType) {
450    case JT_BACKUP:
451    case JT_VERIFY:
452    case JT_RESTORE:
453    case JT_MIGRATE:
454    case JT_COPY:
455    case JT_ADMIN:
456       /* Keep list of last jobs, but not Console where JobId==0 */
457       if (jcr->JobId > 0) {
458          num_jobs_run++;
459          je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
460          memset(je, 0, sizeof(struct s_last_job));  /* zero in case unset fields */
461          je->Errors = jcr->Errors;
462          je->JobType = jcr->JobType;
463          je->JobId = jcr->JobId;
464          je->VolSessionId = jcr->VolSessionId;
465          je->VolSessionTime = jcr->VolSessionTime;
466          bstrncpy(je->Job, jcr->Job, sizeof(je->Job));
467          je->JobFiles = jcr->JobFiles;
468          je->JobBytes = jcr->JobBytes;
469          je->JobStatus = jcr->JobStatus;
470          je->JobLevel = jcr->JobLevel;
471          je->start_time = jcr->start_time;
472          je->end_time = time(NULL);
473
474          if (!last_jobs) {
475             init_last_jobs_list();
476          }
477          last_jobs->append(je);
478          if (last_jobs->size() > max_last_jobs) {
479             je = (struct s_last_job *)last_jobs->first();
480             last_jobs->remove(je);
481             free(je);
482          }
483       }
484       break;
485    default:
486       break;
487
488    }
489    if (jcr->daemon_free_jcr) {
490       jcr->daemon_free_jcr(jcr);      /* call daemon free routine */
491    }
492
493    unlock_jcr_chain();
494    free_common_jcr(jcr);
495    close_msg(NULL);                   /* flush any daemon messages */
496    garbage_collect_memory_pool();
497    Dmsg0(dbglvl, "Exit free_jcr\n");
498 }
499
500 void set_jcr_in_tsd(JCR *jcr)
501 {
502    int status = pthread_setspecific(jcr_key, (void *)jcr);
503    if (status != 0) {
504       berrno be;
505       Jmsg1(jcr, M_ABORT, 0, _("pthread_setspecific failed: ERR=%s\n"), be.bstrerror(status));
506    }
507 }
508
509 JCR *get_jcr_from_tsd()
510 {
511    JCR *jcr = (JCR *)pthread_getspecific(jcr_key);
512 // printf("get_jcr_from_tsd: jcr=%p\n", jcr);
513    /* set any INVALID_JCR to NULL which the rest of Bacula understands */
514    if (jcr == INVALID_JCR) {
515       jcr = NULL;
516    }
517    return jcr;
518 }
519
520  
521 /*
522  * Find which JobId corresponds to the current thread
523  */
524 uint32_t get_jobid_from_tsd()
525 {
526    JCR *jcr;
527    uint32_t JobId = 0;
528    jcr = get_jcr_from_tsd();
529 // printf("get_jobid_from_tsr: jcr=%p\n", jcr);
530    if (jcr) {
531       JobId = (uint32_t)jcr->JobId;
532    }
533    return JobId;
534 }
535
536 /*
537  * Given a JobId, find the JCR
538  *   Returns: jcr on success
539  *            NULL on failure
540  */
541 JCR *get_jcr_by_id(uint32_t JobId)
542 {
543    JCR *jcr;
544
545    foreach_jcr(jcr) {
546       if (jcr->JobId == JobId) {
547          jcr->inc_use_count();
548          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
549             jcr->JobId, jcr->use_count(), jcr->Job);
550          break;
551       }
552    }
553    endeach_jcr(jcr);
554    return jcr;
555 }
556
557 /*
558  * Given a SessionId and SessionTime, find the JCR
559  *   Returns: jcr on success
560  *            NULL on failure
561  */
562 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
563 {
564    JCR *jcr;
565
566    foreach_jcr(jcr) {
567       if (jcr->VolSessionId == SessionId &&
568           jcr->VolSessionTime == SessionTime) {
569          jcr->inc_use_count();
570          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
571             jcr->JobId, jcr->use_count(), jcr->Job);
572          break;
573       }
574    }
575    endeach_jcr(jcr);
576    return jcr;
577 }
578
579
580 /*
581  * Given a Job, find the JCR
582  *  compares on the number of characters in Job
583  *  thus allowing partial matches.
584  *   Returns: jcr on success
585  *            NULL on failure
586  */
587 JCR *get_jcr_by_partial_name(char *Job)
588 {
589    JCR *jcr;
590    int len;
591
592    if (!Job) {
593       return NULL;
594    }
595    len = strlen(Job);
596    foreach_jcr(jcr) {
597       if (strncmp(Job, jcr->Job, len) == 0) {
598          jcr->inc_use_count();
599          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
600             jcr->JobId, jcr->use_count(), jcr->Job);
601          break;
602       }
603    }
604    endeach_jcr(jcr);
605    return jcr;
606 }
607
608
609 /*
610  * Given a Job, find the JCR
611  *  requires an exact match of names.
612  *   Returns: jcr on success
613  *            NULL on failure
614  */
615 JCR *get_jcr_by_full_name(char *Job)
616 {
617    JCR *jcr;
618
619    if (!Job) {
620       return NULL;
621    }
622    foreach_jcr(jcr) {
623       if (strcmp(jcr->Job, Job) == 0) {
624          jcr->inc_use_count();
625          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
626             jcr->JobId, jcr->use_count(), jcr->Job);
627          break;
628       }
629    }
630    endeach_jcr(jcr);
631    return jcr;
632 }
633
634 void set_jcr_job_status(JCR *jcr, int JobStatus)
635 {
636     bool set_waittime=false;
637     Dmsg2(800, "set_jcr_job_status(%s, %c)\n", jcr->Job, JobStatus);
638     /* if wait state is new, we keep current time for watchdog MaxWaitTime */
639     switch (JobStatus) {
640        case JS_WaitFD:
641        case JS_WaitSD:
642        case JS_WaitMedia:
643        case JS_WaitMount:
644        case JS_WaitStoreRes:
645        case JS_WaitJobRes:
646        case JS_WaitClientRes:
647        case JS_WaitMaxJobs:
648        case JS_WaitPriority:
649          set_waittime = true;
650        default:
651          break;
652     }
653  
654    /*
655     * For a set of errors, ... keep the current status
656     *   so it isn't lost. For all others, set it.
657     */
658    Dmsg3(300, "jid=%u OnEntry JobStatus=%c set=%c\n", (uint32_t)jcr->JobId,
659          jcr->JobStatus, JobStatus);
660    switch (jcr->JobStatus) {
661    case JS_ErrorTerminated:
662    case JS_FatalError:
663    case JS_Canceled:
664       break;
665    case JS_Error:
666    case JS_Differences:
667       switch (JobStatus) {
668       case JS_ErrorTerminated:
669       case JS_FatalError:
670       case JS_Canceled:
671          /* Override more minor status */
672          jcr->JobStatus = JobStatus;
673          break;
674       default:
675          break;
676       }
677    /*
678     * For a set of Wait situation, keep old time.
679     */
680    case JS_WaitFD:
681    case JS_WaitSD:
682    case JS_WaitMedia:
683    case JS_WaitMount:
684    case JS_WaitStoreRes:
685    case JS_WaitJobRes:
686    case JS_WaitClientRes:
687    case JS_WaitMaxJobs:
688    case JS_WaitPriority:
689        set_waittime = false;    /* keep old time */
690    default:
691       jcr->JobStatus = JobStatus;
692       if (set_waittime) {
693          /* set it before JobStatus */
694          Dmsg0(800, "Setting wait_time\n");
695          jcr->wait_time = time(NULL);
696       }
697    }
698    Dmsg3(100, "jid=%u leave set_jcr_job_status=%c set=%c\n", (uint32_t)jcr->JobId,
699          jcr->JobStatus, JobStatus);
700 }
701
702 #ifdef TRACE_JCR_CHAIN
703 static int lock_count = 0;
704 #endif
705
706 /*
707  * Lock the chain
708  */
709 #ifdef TRACE_JCR_CHAIN
710 static void b_lock_jcr_chain(const char *fname, int line)
711 #else
712 static void lock_jcr_chain()
713 #endif
714 {
715 #ifdef TRACE_JCR_CHAIN
716    Dmsg3(dbglvl, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
717 #endif
718    P(jcr_lock);
719 }
720
721 /*
722  * Unlock the chain
723  */
724 #ifdef TRACE_JCR_CHAIN
725 static void b_unlock_jcr_chain(const char *fname, int line)
726 #else
727 static void unlock_jcr_chain()
728 #endif
729 {
730 #ifdef TRACE_JCR_CHAIN
731    Dmsg3(dbglvl, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
732 #endif
733    V(jcr_lock);
734 }
735
736
737 /*
738  * Start walk of jcr chain
739  * The proper way to walk the jcr chain is:
740  *    JCR *jcr;
741  *    foreach_jcr(jcr) {
742  *      ...
743  *    }
744  *    endeach_jcr(jcr);
745  *
746  *  It is possible to leave out the endeach_jcr(jcr), but
747  *   in that case, the last jcr referenced must be explicitly
748  *   released with:
749  *
750  *    free_jcr(jcr);
751  *  
752  */
753 JCR *jcr_walk_start() 
754 {
755    JCR *jcr;
756    lock_jcr_chain();
757    jcr = (JCR *)jcrs->first();
758    if (jcr) {
759       jcr->inc_use_count();
760       if (jcr->JobId > 0) {
761          Dmsg3(dbglvl, "Inc walk_start jid=%u use_count=%d Job=%s\n", 
762             jcr->JobId, jcr->use_count(), jcr->Job);
763       }
764    }
765    unlock_jcr_chain();
766    return jcr;
767 }
768
769 /*
770  * Get next jcr from chain, and release current one
771  */
772 JCR *jcr_walk_next(JCR *prev_jcr)
773 {
774    JCR *jcr;
775
776    lock_jcr_chain();
777    jcr = (JCR *)jcrs->next(prev_jcr);
778    if (jcr) {
779       jcr->inc_use_count();
780       if (jcr->JobId > 0) {
781          Dmsg3(dbglvl, "Inc walk_next jid=%u use_count=%d Job=%s\n", 
782             jcr->JobId, jcr->use_count(), jcr->Job);
783       }
784    }
785    unlock_jcr_chain();
786    if (prev_jcr) {
787       free_jcr(prev_jcr);
788    }
789    return jcr;
790 }
791
792 /*
793  * Release last jcr referenced
794  */
795 void jcr_walk_end(JCR *jcr)
796 {
797    if (jcr) {
798       if (jcr->JobId > 0) {
799          Dmsg3(dbglvl, "Free walk_end jid=%u use_count=%d Job=%s\n", 
800             jcr->JobId, jcr->use_count(), jcr->Job);
801       }
802       free_jcr(jcr);
803    }
804 }
805
806
807 /*
808  * Setup to call the timeout check routine every 30 seconds
809  *  This routine will check any timers that have been enabled.
810  */
811 bool init_jcr_subsystem(void)
812 {
813    watchdog_t *wd = new_watchdog();
814
815    wd->one_shot = false;
816    wd->interval = 30;   /* FIXME: should be configurable somewhere, even
817                          if only with a #define */
818    wd->callback = jcr_timeout_check;
819
820    register_watchdog(wd);
821
822    return true;
823 }
824
825 static void jcr_timeout_check(watchdog_t *self)
826 {
827    JCR *jcr;
828    BSOCK *fd;
829    time_t timer_start;
830
831    Dmsg0(dbglvl, "Start JCR timeout checks\n");
832
833    /* Walk through all JCRs checking if any one is
834     * blocked for more than specified max time.
835     */
836    foreach_jcr(jcr) {
837       Dmsg2(dbglvl, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
838       if (jcr->JobId == 0) {
839          continue;
840       }
841       fd = jcr->store_bsock;
842       if (fd) {
843          timer_start = fd->timer_start;
844          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
845             fd->timer_start = 0;      /* turn off timer */
846             fd->set_timed_out();
847             Jmsg(jcr, M_ERROR, 0, _(
848 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
849                  watchdog_time - timer_start);
850             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
851          }
852       }
853       fd = jcr->file_bsock;
854       if (fd) {
855          timer_start = fd->timer_start;
856          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
857             fd->timer_start = 0;      /* turn off timer */
858             fd->set_timed_out();
859             Jmsg(jcr, M_ERROR, 0, _(
860 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
861                  watchdog_time - timer_start);
862             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
863          }
864       }
865       fd = jcr->dir_bsock;
866       if (fd) {
867          timer_start = fd->timer_start;
868          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
869             fd->timer_start = 0;      /* turn off timer */
870             fd->set_timed_out();
871             Jmsg(jcr, M_ERROR, 0, _(
872 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
873                  watchdog_time - timer_start);
874             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
875          }
876       }
877    }
878    endeach_jcr(jcr);
879
880    Dmsg0(dbglvl, "Finished JCR timeout checks\n");
881 }
882
883 /*
884  * Timeout signal comes here
885  */
886 extern "C" void timeout_handler(int sig)
887 {
888    return;                            /* thus interrupting the function */
889 }