]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/jcr.c
Implement first cut of Copy Job
[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, last_job;
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, last_job;
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       num_jobs_run++;
457       last_job.Errors = jcr->Errors;
458       last_job.JobType = jcr->JobType;
459       last_job.JobId = jcr->JobId;
460       last_job.VolSessionId = jcr->VolSessionId;
461       last_job.VolSessionTime = jcr->VolSessionTime;
462       bstrncpy(last_job.Job, jcr->Job, sizeof(last_job.Job));
463       last_job.JobFiles = jcr->JobFiles;
464       last_job.JobBytes = jcr->JobBytes;
465       last_job.JobStatus = jcr->JobStatus;
466       last_job.JobLevel = jcr->JobLevel;
467       last_job.start_time = jcr->start_time;
468       last_job.end_time = time(NULL);
469       /* Keep list of last jobs, but not Console where JobId==0 */
470       if (last_job.JobId > 0) {
471          je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
472          memcpy((char *)je, (char *)&last_job, sizeof(last_job));
473          if (!last_jobs) {
474             init_last_jobs_list();
475          }
476          last_jobs->append(je);
477          if (last_jobs->size() > max_last_jobs) {
478             je = (struct s_last_job *)last_jobs->first();
479             last_jobs->remove(je);
480             free(je);
481          }
482       }
483       break;
484    default:
485       break;
486
487    }
488    if (jcr->daemon_free_jcr) {
489       jcr->daemon_free_jcr(jcr);      /* call daemon free routine */
490    }
491
492    unlock_jcr_chain();
493    free_common_jcr(jcr);
494    close_msg(NULL);                   /* flush any daemon messages */
495    garbage_collect_memory_pool();
496    Dmsg0(dbglvl, "Exit free_jcr\n");
497 }
498
499 void set_jcr_in_tsd(JCR *jcr)
500 {
501    int status = pthread_setspecific(jcr_key, (void *)jcr);
502    if (status != 0) {
503       berrno be;
504       Jmsg1(jcr, M_ABORT, 0, _("pthread_setspecific failed: ERR=%s\n"), be.bstrerror(status));
505    }
506 }
507
508 JCR *get_jcr_from_tsd()
509 {
510    JCR *jcr = (JCR *)pthread_getspecific(jcr_key);
511 // printf("get_jcr_from_tsd: jcr=%p\n", jcr);
512    /* set any INVALID_JCR to NULL which the rest of Bacula understands */
513    if (jcr == INVALID_JCR) {
514       jcr = NULL;
515    }
516    return jcr;
517 }
518
519  
520 /*
521  * Find which JobId corresponds to the current thread
522  */
523 uint32_t get_jobid_from_tsd()
524 {
525    JCR *jcr;
526    uint32_t JobId = 0;
527    jcr = get_jcr_from_tsd();
528 // printf("get_jobid_from_tsr: jcr=%p\n", jcr);
529    if (jcr) {
530       JobId = (uint32_t)jcr->JobId;
531    }
532    return JobId;
533 }
534
535 /*
536  * Given a JobId, find the JCR
537  *   Returns: jcr on success
538  *            NULL on failure
539  */
540 JCR *get_jcr_by_id(uint32_t JobId)
541 {
542    JCR *jcr;
543
544    foreach_jcr(jcr) {
545       if (jcr->JobId == JobId) {
546          jcr->inc_use_count();
547          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
548             jcr->JobId, jcr->use_count(), jcr->Job);
549          break;
550       }
551    }
552    endeach_jcr(jcr);
553    return jcr;
554 }
555
556 /*
557  * Given a SessionId and SessionTime, find the JCR
558  *   Returns: jcr on success
559  *            NULL on failure
560  */
561 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
562 {
563    JCR *jcr;
564
565    foreach_jcr(jcr) {
566       if (jcr->VolSessionId == SessionId &&
567           jcr->VolSessionTime == SessionTime) {
568          jcr->inc_use_count();
569          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
570             jcr->JobId, jcr->use_count(), jcr->Job);
571          break;
572       }
573    }
574    endeach_jcr(jcr);
575    return jcr;
576 }
577
578
579 /*
580  * Given a Job, find the JCR
581  *  compares on the number of characters in Job
582  *  thus allowing partial matches.
583  *   Returns: jcr on success
584  *            NULL on failure
585  */
586 JCR *get_jcr_by_partial_name(char *Job)
587 {
588    JCR *jcr;
589    int len;
590
591    if (!Job) {
592       return NULL;
593    }
594    len = strlen(Job);
595    foreach_jcr(jcr) {
596       if (strncmp(Job, jcr->Job, len) == 0) {
597          jcr->inc_use_count();
598          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
599             jcr->JobId, jcr->use_count(), jcr->Job);
600          break;
601       }
602    }
603    endeach_jcr(jcr);
604    return jcr;
605 }
606
607
608 /*
609  * Given a Job, find the JCR
610  *  requires an exact match of names.
611  *   Returns: jcr on success
612  *            NULL on failure
613  */
614 JCR *get_jcr_by_full_name(char *Job)
615 {
616    JCR *jcr;
617
618    if (!Job) {
619       return NULL;
620    }
621    foreach_jcr(jcr) {
622       if (strcmp(jcr->Job, Job) == 0) {
623          jcr->inc_use_count();
624          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
625             jcr->JobId, jcr->use_count(), jcr->Job);
626          break;
627       }
628    }
629    endeach_jcr(jcr);
630    return jcr;
631 }
632
633 void set_jcr_job_status(JCR *jcr, int JobStatus)
634 {
635    /*
636     * For a set of errors, ... keep the current status
637     *   so it isn't lost. For all others, set it.
638     */
639    Dmsg3(300, "jid=%u OnEntry JobStatus=%c set=%c\n", (uint32_t)jcr->JobId,
640          jcr->JobStatus, JobStatus);
641    switch (jcr->JobStatus) {
642    case JS_ErrorTerminated:
643    case JS_FatalError:
644    case JS_Canceled:
645       break;
646    case JS_Error:
647    case JS_Differences:
648       switch (JobStatus) {
649       case JS_ErrorTerminated:
650       case JS_FatalError:
651       case JS_Canceled:
652          /* Override more minor status */
653          jcr->JobStatus = JobStatus;
654          break;
655       }
656       break;
657    default:
658       jcr->JobStatus = JobStatus;
659    }
660    Dmsg3(100, "jid=%u OnExit JobStatus=%c set=%c\n", (uint32_t)jcr->JobId,
661          jcr->JobStatus, JobStatus);
662 }
663
664 #ifdef TRACE_JCR_CHAIN
665 static int lock_count = 0;
666 #endif
667
668 /*
669  * Lock the chain
670  */
671 #ifdef TRACE_JCR_CHAIN
672 static void b_lock_jcr_chain(const char *fname, int line)
673 #else
674 static void lock_jcr_chain()
675 #endif
676 {
677 #ifdef TRACE_JCR_CHAIN
678    Dmsg3(dbglvl, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
679 #endif
680    P(jcr_lock);
681 }
682
683 /*
684  * Unlock the chain
685  */
686 #ifdef TRACE_JCR_CHAIN
687 static void b_unlock_jcr_chain(const char *fname, int line)
688 #else
689 static void unlock_jcr_chain()
690 #endif
691 {
692 #ifdef TRACE_JCR_CHAIN
693    Dmsg3(dbglvl, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
694 #endif
695    V(jcr_lock);
696 }
697
698
699 /*
700  * Start walk of jcr chain
701  * The proper way to walk the jcr chain is:
702  *    JCR *jcr;
703  *    foreach_jcr(jcr) {
704  *      ...
705  *    }
706  *    endeach_jcr(jcr);
707  *
708  *  It is possible to leave out the endeach_jcr(jcr), but
709  *   in that case, the last jcr referenced must be explicitly
710  *   released with:
711  *
712  *    free_jcr(jcr);
713  *  
714  */
715 JCR *jcr_walk_start() 
716 {
717    JCR *jcr;
718    lock_jcr_chain();
719    jcr = (JCR *)jcrs->first();
720    if (jcr) {
721       jcr->inc_use_count();
722       if (jcr->JobId > 0) {
723          Dmsg3(dbglvl, "Inc walk_start jid=%u use_count=%d Job=%s\n", 
724             jcr->JobId, jcr->use_count(), jcr->Job);
725       }
726    }
727    unlock_jcr_chain();
728    return jcr;
729 }
730
731 /*
732  * Get next jcr from chain, and release current one
733  */
734 JCR *jcr_walk_next(JCR *prev_jcr)
735 {
736    JCR *jcr;
737
738    lock_jcr_chain();
739    jcr = (JCR *)jcrs->next(prev_jcr);
740    if (jcr) {
741       jcr->inc_use_count();
742       if (jcr->JobId > 0) {
743          Dmsg3(dbglvl, "Inc walk_next jid=%u use_count=%d Job=%s\n", 
744             jcr->JobId, jcr->use_count(), jcr->Job);
745       }
746    }
747    unlock_jcr_chain();
748    if (prev_jcr) {
749       free_jcr(prev_jcr);
750    }
751    return jcr;
752 }
753
754 /*
755  * Release last jcr referenced
756  */
757 void jcr_walk_end(JCR *jcr)
758 {
759    if (jcr) {
760       if (jcr->JobId > 0) {
761          Dmsg3(dbglvl, "Free walk_end jid=%u use_count=%d Job=%s\n", 
762             jcr->JobId, jcr->use_count(), jcr->Job);
763       }
764       free_jcr(jcr);
765    }
766 }
767
768
769 /*
770  * Setup to call the timeout check routine every 30 seconds
771  *  This routine will check any timers that have been enabled.
772  */
773 bool init_jcr_subsystem(void)
774 {
775    watchdog_t *wd = new_watchdog();
776
777    wd->one_shot = false;
778    wd->interval = 30;   /* FIXME: should be configurable somewhere, even
779                          if only with a #define */
780    wd->callback = jcr_timeout_check;
781
782    register_watchdog(wd);
783
784    return true;
785 }
786
787 static void jcr_timeout_check(watchdog_t *self)
788 {
789    JCR *jcr;
790    BSOCK *fd;
791    time_t timer_start;
792
793    Dmsg0(dbglvl, "Start JCR timeout checks\n");
794
795    /* Walk through all JCRs checking if any one is
796     * blocked for more than specified max time.
797     */
798    foreach_jcr(jcr) {
799       Dmsg2(dbglvl, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
800       if (jcr->JobId == 0) {
801          continue;
802       }
803       fd = jcr->store_bsock;
804       if (fd) {
805          timer_start = fd->timer_start;
806          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
807             fd->timer_start = 0;      /* turn off timer */
808             fd->set_timed_out();
809             Jmsg(jcr, M_ERROR, 0, _(
810 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
811                  watchdog_time - timer_start);
812             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
813          }
814       }
815       fd = jcr->file_bsock;
816       if (fd) {
817          timer_start = fd->timer_start;
818          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
819             fd->timer_start = 0;      /* turn off timer */
820             fd->set_timed_out();
821             Jmsg(jcr, M_ERROR, 0, _(
822 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
823                  watchdog_time - timer_start);
824             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
825          }
826       }
827       fd = jcr->dir_bsock;
828       if (fd) {
829          timer_start = fd->timer_start;
830          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
831             fd->timer_start = 0;      /* turn off timer */
832             fd->set_timed_out();
833             Jmsg(jcr, M_ERROR, 0, _(
834 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
835                  watchdog_time - timer_start);
836             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
837          }
838       }
839    }
840    endeach_jcr(jcr);
841
842    Dmsg0(dbglvl, "Finished JCR timeout checks\n");
843 }
844
845 /*
846  * Timeout signal comes here
847  */
848 extern "C" void timeout_handler(int sig)
849 {
850    return;                            /* thus interrupting the function */
851 }