]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/jcr.c
Fix screw up with setting JobLevel and JobType
[bacula/bacula] / bacula / src / lib / jcr.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 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
60 /* External referenced functions */
61 void free_bregexps(alist *bregexps);
62
63 /* Forward referenced functions */
64 extern "C" void timeout_handler(int sig);
65 static void jcr_timeout_check(watchdog_t *self);
66 #ifdef TRACE_JCR_CHAIN
67 static void b_lock_jcr_chain(const char *filen, int line);
68 static void b_unlock_jcr_chain(const char *filen, int line);
69 #define lock_jcr_chain() b_lock_jcr_chain(__FILE__, __LINE__);
70 #define unlock_jcr_chain() b_unlock_jcr_chain(__FILE__, __LINE__);
71 #else
72 static void lock_jcr_chain();
73 static void unlock_jcr_chain();
74 #endif
75
76
77 int num_jobs_run;
78 dlist *last_jobs = NULL;
79 const int max_last_jobs = 10;
80  
81 static dlist *jcrs = NULL;            /* JCR chain */
82 static pthread_mutex_t jcr_lock = PTHREAD_MUTEX_INITIALIZER;
83
84 static pthread_mutex_t job_start_mutex = PTHREAD_MUTEX_INITIALIZER;
85
86 static pthread_mutex_t last_jobs_mutex = PTHREAD_MUTEX_INITIALIZER;
87
88 static pthread_key_t jcr_key;         /* Pointer to jcr for each thread */
89
90 pthread_once_t key_once = PTHREAD_ONCE_INIT; 
91
92
93 void lock_jobs()
94 {
95    P(job_start_mutex);
96 }
97
98 void unlock_jobs()
99 {
100    V(job_start_mutex);
101 }
102
103 void init_last_jobs_list()
104 {
105    JCR *jcr = NULL;
106    struct s_last_job *job_entry = NULL;
107    if (!last_jobs) {
108       last_jobs = New(dlist(job_entry, &job_entry->link));
109    }
110    if (!jcrs) {
111       jcrs = New(dlist(jcr, &jcr->link));
112    }
113 }
114
115 void term_last_jobs_list()
116 {
117    if (last_jobs) {
118       lock_last_jobs_list();
119       while (!last_jobs->empty()) {
120          void *je = last_jobs->first();
121          last_jobs->remove(je);
122          free(je);
123       }
124       delete last_jobs;
125       last_jobs = NULL;
126       unlock_last_jobs_list();
127    }
128    if (jcrs) {
129       delete jcrs;
130       jcrs = NULL;
131    }
132 }
133
134 bool read_last_jobs_list(int fd, uint64_t addr)
135 {
136    struct s_last_job *je, job;
137    uint32_t num;
138    bool ok = true;
139
140    Dmsg1(100, "read_last_jobs seek to %d\n", (int)addr);
141    if (addr == 0 || lseek(fd, (boffset_t)addr, SEEK_SET) < 0) {
142       return false;
143    }
144    if (read(fd, &num, sizeof(num)) != sizeof(num)) {
145       return false;
146    }
147    Dmsg1(100, "Read num_items=%d\n", num);
148    if (num > 4 * max_last_jobs) {  /* sanity check */
149       return false;
150    }
151    lock_last_jobs_list();
152    for ( ; num; num--) {
153       if (read(fd, &job, sizeof(job)) != sizeof(job)) {
154          berrno be;
155          Pmsg1(000, "Read job entry. ERR=%s\n", be.bstrerror());
156          ok = false;
157          break;
158       }
159       if (job.JobId > 0) {
160          je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
161          memcpy((char *)je, (char *)&job, sizeof(job));
162          if (!last_jobs) {
163             init_last_jobs_list();
164          }
165          last_jobs->append(je);
166          if (last_jobs->size() > max_last_jobs) {
167             je = (struct s_last_job *)last_jobs->first();
168             last_jobs->remove(je);
169             free(je);
170          }
171       }
172    }
173    unlock_last_jobs_list();
174    return ok;
175 }
176
177 uint64_t write_last_jobs_list(int fd, uint64_t addr)
178 {
179    struct s_last_job *je;
180    uint32_t num;
181    ssize_t stat;
182
183    Dmsg1(100, "write_last_jobs seek to %d\n", (int)addr);
184    if (lseek(fd, (boffset_t)addr, SEEK_SET) < 0) {
185       return 0;
186    }
187    if (last_jobs) {
188       lock_last_jobs_list();
189       /* First record is number of entires */
190       num = last_jobs->size();
191       if (write(fd, &num, sizeof(num)) != sizeof(num)) {
192          berrno be;
193          Pmsg1(000, "Error writing num_items: ERR=%s\n", be.bstrerror());
194          goto bail_out;
195       }
196       foreach_dlist(je, last_jobs) {
197          if (write(fd, je, sizeof(struct s_last_job)) != sizeof(struct s_last_job)) {
198             berrno be;
199             Pmsg1(000, "Error writing job: ERR=%s\n", be.bstrerror());
200             goto bail_out;
201          }
202       }
203       unlock_last_jobs_list();
204    }
205    /* Return current address */
206    stat = lseek(fd, 0, SEEK_CUR);
207    if (stat < 0) {
208       stat = 0;
209    }
210    return stat;
211
212 bail_out:
213    unlock_last_jobs_list();
214    return 0;
215 }
216
217 void lock_last_jobs_list()
218 {
219    P(last_jobs_mutex);
220 }
221
222 void unlock_last_jobs_list()
223 {
224    V(last_jobs_mutex);
225 }
226
227 /* Get an ASCII representation of the Operation being performed as an english Noun */
228 const char *JCR::get_OperationName()
229 {
230    switch(m_JobType) {
231    case JT_BACKUP:
232       return _("Backup");
233    case JT_VERIFY:
234       return _("Verifying");
235    case JT_RESTORE:
236       return _("Restoring");
237    case JT_ARCHIVE:
238       return _("Archiving");
239    case JT_COPY:
240       return _("Copying");
241    case JT_MIGRATE:
242       return _("Migration");
243    case JT_SCAN:
244       return _("Scanning");
245    default:
246       return _("Unknown operation");
247    }
248 }
249
250 /* Get an ASCII representation of the Action being performed either an english Verb or Adjective */
251 const char *JCR::get_ActionName(bool past)
252 {
253    switch(m_JobType) {
254    case JT_BACKUP:
255       return _("backup");
256    case JT_VERIFY:
257       return (past == true) ? _("verified") : _("verify");
258    case JT_RESTORE:
259       return (past == true) ? _("restored") : _("restore");
260    case JT_ARCHIVE:
261       return (past == true) ? _("archived") : _("archive");
262    case JT_COPY:
263       return (past == true) ? _("copied") : _("copy");
264    case JT_MIGRATE:
265       return (past == true) ? _("migrated") : _("migrate");
266    case JT_SCAN:
267       return (past == true) ? _("scanned") : _("scan");
268    default:
269       return _("unknown action");
270    }
271 }
272
273 bool JCR::JobReads()
274 {
275    switch (m_JobType) {
276    case JT_VERIFY:
277    case JT_RESTORE:
278    case JT_COPY:
279    case JT_MIGRATE:
280       return true;
281    case JT_BACKUP:
282       if (m_JobLevel == L_VIRTUAL_FULL) {
283          return true;
284       }
285       break;
286    default:
287       break;
288    }
289    return false;
290 }
291
292 /*
293  * Push a subroutine address into the job end callback stack
294  */
295 void job_end_push(JCR *jcr, void job_end_cb(JCR *jcr,void *), void *ctx)
296 {
297    jcr->job_end_push.append((void *)job_end_cb);
298    jcr->job_end_push.append(ctx);
299 }
300
301 /* Pop each job_end subroutine and call it */
302 static void job_end_pop(JCR *jcr)
303 {
304    void (*job_end_cb)(JCR *jcr, void *ctx);
305    void *ctx;
306    for (int i=jcr->job_end_push.size()-1; i > 0; ) {
307       ctx = jcr->job_end_push.get(i--);
308       job_end_cb = (void (*)(JCR *,void *))jcr->job_end_push.get(i--);
309       job_end_cb(jcr, ctx);
310    }
311 }
312
313 void create_jcr_key()
314 {
315    int status = pthread_key_create(&jcr_key, NULL);
316    if (status != 0) {
317       berrno be;
318       Jmsg1(NULL, M_ABORT, 0, _("pthread key create failed: ERR=%s\n"),
319             be.bstrerror(status));
320    }
321 }
322
323 /*
324  * Create a Job Control Record and link it into JCR chain
325  * Returns newly allocated JCR
326  * Note, since each daemon has a different JCR, he passes
327  *  us the size.
328  */
329 JCR *new_jcr(int size, JCR_free_HANDLER *daemon_free_jcr)
330 {
331    JCR *jcr;
332    MQUEUE_ITEM *item = NULL;
333    struct sigaction sigtimer;
334    int status;
335
336    Dmsg0(dbglvl, "Enter new_jcr\n");
337    status = pthread_once(&key_once, create_jcr_key);
338    if (status != 0) {
339       berrno be;
340       Jmsg1(NULL, M_ABORT, 0, _("pthread_once failed. ERR=%s\n"), be.bstrerror(status));
341    }
342    jcr = (JCR *)malloc(size);
343    memset(jcr, 0, size);
344    jcr->my_thread_id = pthread_self();
345    jcr->msg_queue = New(dlist(item, &item->link));
346    jcr->job_end_push.init(1, false);
347    jcr->sched_time = time(NULL);
348    jcr->daemon_free_jcr = daemon_free_jcr;    /* plug daemon free routine */
349    jcr->init_mutex();
350    jcr->inc_use_count();   
351    jcr->VolumeName = get_pool_memory(PM_FNAME);
352    jcr->VolumeName[0] = 0;
353    jcr->errmsg = get_pool_memory(PM_MESSAGE);
354    jcr->errmsg[0] = 0;
355    /* Setup some dummy values */
356    bstrncpy(jcr->Job, "*System*", sizeof(jcr->Job));
357    jcr->JobId = 0;
358    jcr->setJobType(JT_SYSTEM);           /* internal job until defined */
359    jcr->setJobLevel(L_NONE);
360    jcr->setJobStatus(JS_Created);        /* ready to run */
361    set_jcr_in_tsd(jcr);
362    sigtimer.sa_flags = 0;
363    sigtimer.sa_handler = timeout_handler;
364    sigfillset(&sigtimer.sa_mask);
365    sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL);
366
367    /*
368     * Locking jobs is a global lock that is needed
369     * so that the Director can stop new jobs from being
370     * added to the jcr chain while it processes a new
371     * conf file and does the job_end_push().
372     */
373    lock_jobs();
374    lock_jcr_chain();
375    if (!jcrs) {
376       jcrs = New(dlist(jcr, &jcr->link));
377    }
378    jcrs->append(jcr);
379    unlock_jcr_chain();
380    unlock_jobs();
381
382    return jcr;
383 }
384
385
386 /*
387  * Remove a JCR from the chain
388  * NOTE! The chain must be locked prior to calling
389  *       this routine.
390  */
391 static void remove_jcr(JCR *jcr)
392 {
393    Dmsg0(dbglvl, "Enter remove_jcr\n");
394    if (!jcr) {
395       Emsg0(M_ABORT, 0, _("NULL jcr.\n"));
396    }
397    jcrs->remove(jcr);
398    Dmsg0(dbglvl, "Leave remove_jcr\n");
399 }
400
401 /*
402  * Free stuff common to all JCRs.  N.B. Be careful to include only
403  *  generic stuff in the common part of the jcr.
404  */
405 static void free_common_jcr(JCR *jcr)
406 {
407    jcr->destroy_mutex();
408
409    if (jcr->msg_queue) {
410       delete jcr->msg_queue;
411       jcr->msg_queue = NULL;
412    }
413    close_msg(jcr);                    /* close messages for this job */
414
415    /* do this after closing messages */
416    if (jcr->client_name) {
417       free_pool_memory(jcr->client_name);
418       jcr->client_name = NULL;
419    }
420
421    if (jcr->attr) {
422       free_pool_memory(jcr->attr);
423       jcr->attr = NULL;
424    }
425
426    if (jcr->sd_auth_key) {
427       free(jcr->sd_auth_key);
428       jcr->sd_auth_key = NULL;
429    }
430    if (jcr->VolumeName) {
431       free_pool_memory(jcr->VolumeName);
432       jcr->VolumeName = NULL;
433    }
434
435    if (jcr->dir_bsock) {
436       bnet_close(jcr->dir_bsock);
437       jcr->dir_bsock = NULL;
438    }
439    if (jcr->errmsg) {
440       free_pool_memory(jcr->errmsg);
441       jcr->errmsg = NULL;
442    }
443    if (jcr->where) {
444       free(jcr->where);
445       jcr->where = NULL;
446    }
447    if (jcr->RegexWhere) {
448       free(jcr->RegexWhere);
449       jcr->RegexWhere = NULL;
450    }
451    if (jcr->where_bregexp) {
452       free_bregexps(jcr->where_bregexp);
453       delete jcr->where_bregexp;
454       jcr->where_bregexp = NULL;
455    }
456    if (jcr->cached_path) {
457       free_pool_memory(jcr->cached_path);
458       jcr->cached_path = NULL;
459       jcr->cached_pnl = 0;
460    }
461    if (jcr->id_list) {
462       free_guid_list(jcr->id_list);
463       jcr->id_list = NULL;
464    }
465    remove_jcr_from_tsd(jcr);
466    free(jcr);
467 }
468
469 /*
470  * Global routine to free a jcr
471  */
472 #ifdef DEBUG
473 void b_free_jcr(const char *file, int line, JCR *jcr)
474 {
475    struct s_last_job *je;
476
477    Dmsg3(dbglvl, "Enter free_jcr jid=%u from %s:%d\n", jcr->JobId, file, line);
478
479 #else
480
481 void free_jcr(JCR *jcr)
482 {
483    struct s_last_job *je;
484
485    Dmsg3(dbglvl, "Enter free_jcr jid=%u use_count=%d Job=%s\n", 
486          jcr->JobId, jcr->use_count(), jcr->Job);
487
488 #endif
489
490    lock_jcr_chain();
491    jcr->dec_use_count();              /* decrement use count */
492    if (jcr->use_count() < 0) {
493       Jmsg2(jcr, M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
494          jcr->use_count(), jcr->JobId);
495    }
496    if (jcr->JobId > 0) {
497       Dmsg3(dbglvl, "Dec free_jcr jid=%u use_count=%d Job=%s\n", 
498          jcr->JobId, jcr->use_count(), jcr->Job);
499    }
500    if (jcr->use_count() > 0) {          /* if in use */
501       unlock_jcr_chain();
502       return;
503    }
504    if (jcr->JobId > 0) {
505       Dmsg3(dbglvl, "remove jcr jid=%u use_count=%d Job=%s\n", 
506             jcr->JobId, jcr->use_count(), jcr->Job);
507    }
508    remove_jcr(jcr);                   /* remove Jcr from chain */
509    unlock_jcr_chain();
510
511    dequeue_messages(jcr);
512    job_end_pop(jcr);                  /* pop and call hooked routines */
513
514    Dmsg1(dbglvl, "End job=%d\n", jcr->JobId);
515
516    /* Keep some statistics */
517    switch (jcr->get_JobType()) {
518    case JT_BACKUP:
519    case JT_VERIFY:
520    case JT_RESTORE:
521    case JT_MIGRATE:
522    case JT_COPY:
523    case JT_ADMIN:
524       /* Keep list of last jobs, but not Console where JobId==0 */
525       if (jcr->JobId > 0) {
526          lock_last_jobs_list();
527          num_jobs_run++;
528          je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
529          memset(je, 0, sizeof(struct s_last_job));  /* zero in case unset fields */
530          je->Errors = jcr->JobErrors;
531          je->JobType = jcr->get_JobType();
532          je->JobId = jcr->JobId;
533          je->VolSessionId = jcr->VolSessionId;
534          je->VolSessionTime = jcr->VolSessionTime;
535          bstrncpy(je->Job, jcr->Job, sizeof(je->Job));
536          je->JobFiles = jcr->JobFiles;
537          je->JobBytes = jcr->JobBytes;
538          je->JobStatus = jcr->JobStatus;
539          je->JobLevel = jcr->get_JobLevel();
540          je->start_time = jcr->start_time;
541          je->end_time = time(NULL);
542
543          if (!last_jobs) {
544             init_last_jobs_list();
545          }
546          last_jobs->append(je);
547          if (last_jobs->size() > max_last_jobs) {
548             je = (struct s_last_job *)last_jobs->first();
549             last_jobs->remove(je);
550             free(je);
551          }
552          unlock_last_jobs_list();
553       }
554       break;
555    default:
556       break;
557    }
558
559    if (jcr->daemon_free_jcr) {
560       jcr->daemon_free_jcr(jcr);      /* call daemon free routine */
561    }
562
563    free_common_jcr(jcr);
564    close_msg(NULL);                   /* flush any daemon messages */
565    garbage_collect_memory_pool();
566    Dmsg0(dbglvl, "Exit free_jcr\n");
567 }
568
569 /*
570  * Remove jcr from thread specific data, but
571  *   but make sure it is us who are attached.
572  */
573 void remove_jcr_from_tsd(JCR *jcr)
574 {
575    JCR *tjcr = get_jcr_from_tsd();
576    if (tjcr == jcr) { 
577       set_jcr_in_tsd(INVALID_JCR);
578    }
579 }
580
581 /*
582  * Put this jcr in the thread specifc data 
583  */
584 void set_jcr_in_tsd(JCR *jcr)
585 {
586    int status = pthread_setspecific(jcr_key, (void *)jcr);
587    if (status != 0) {
588       berrno be;
589       Jmsg1(jcr, M_ABORT, 0, _("pthread_setspecific failed: ERR=%s\n"), be.bstrerror(status));
590    }
591 }
592
593 /*
594  * Give me the jcr that is attached to this thread
595  */
596 JCR *get_jcr_from_tsd()
597 {
598    JCR *jcr = (JCR *)pthread_getspecific(jcr_key);
599 // printf("get_jcr_from_tsd: jcr=%p\n", jcr);
600    /* set any INVALID_JCR to NULL which the rest of Bacula understands */
601    if (jcr == INVALID_JCR) {
602       jcr = NULL;
603    }
604    return jcr;
605 }
606
607  
608 /*
609  * Find which JobId corresponds to the current thread
610  */
611 uint32_t get_jobid_from_tsd()
612 {
613    JCR *jcr;
614    uint32_t JobId = 0;
615    jcr = get_jcr_from_tsd();
616 // printf("get_jobid_from_tsr: jcr=%p\n", jcr);
617    if (jcr) {
618       JobId = (uint32_t)jcr->JobId;
619    }
620    return JobId;
621 }
622
623 /*
624  * Given a JobId, find the JCR
625  *   Returns: jcr on success
626  *            NULL on failure
627  */
628 JCR *get_jcr_by_id(uint32_t JobId)
629 {
630    JCR *jcr;
631
632    foreach_jcr(jcr) {
633       if (jcr->JobId == JobId) {
634          jcr->inc_use_count();
635          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
636             jcr->JobId, jcr->use_count(), jcr->Job);
637          break;
638       }
639    }
640    endeach_jcr(jcr);
641    return jcr;
642 }
643
644 /*
645  * Given a SessionId and SessionTime, find the JCR
646  *   Returns: jcr on success
647  *            NULL on failure
648  */
649 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
650 {
651    JCR *jcr;
652
653    foreach_jcr(jcr) {
654       if (jcr->VolSessionId == SessionId &&
655           jcr->VolSessionTime == SessionTime) {
656          jcr->inc_use_count();
657          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
658             jcr->JobId, jcr->use_count(), jcr->Job);
659          break;
660       }
661    }
662    endeach_jcr(jcr);
663    return jcr;
664 }
665
666
667 /*
668  * Given a Job, find the JCR
669  *  compares on the number of characters in Job
670  *  thus allowing partial matches.
671  *   Returns: jcr on success
672  *            NULL on failure
673  */
674 JCR *get_jcr_by_partial_name(char *Job)
675 {
676    JCR *jcr;
677    int len;
678
679    if (!Job) {
680       return NULL;
681    }
682    len = strlen(Job);
683    foreach_jcr(jcr) {
684       if (strncmp(Job, jcr->Job, len) == 0) {
685          jcr->inc_use_count();
686          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
687             jcr->JobId, jcr->use_count(), jcr->Job);
688          break;
689       }
690    }
691    endeach_jcr(jcr);
692    return jcr;
693 }
694
695
696 /*
697  * Given a Job, find the JCR
698  *  requires an exact match of names.
699  *   Returns: jcr on success
700  *            NULL on failure
701  */
702 JCR *get_jcr_by_full_name(char *Job)
703 {
704    JCR *jcr;
705
706    if (!Job) {
707       return NULL;
708    }
709    foreach_jcr(jcr) {
710       if (strcmp(jcr->Job, Job) == 0) {
711          jcr->inc_use_count();
712          Dmsg3(dbglvl, "Inc get_jcr jid=%u use_count=%d Job=%s\n", 
713             jcr->JobId, jcr->use_count(), jcr->Job);
714          break;
715       }
716    }
717    endeach_jcr(jcr);
718    return jcr;
719 }
720
721 /* 
722  * Priority runs from 0 (lowest) to 10 (highest)
723  */
724 static int get_status_priority(int JobStatus)
725 {
726    int priority = 0;
727    switch (JobStatus) {
728    case JS_ErrorTerminated:
729    case JS_FatalError:
730    case JS_Canceled:
731    case JS_Incomplete:
732       priority = 10;
733       break;
734    case JS_Error:
735       priority = 8;
736       break;
737    case JS_Differences:
738       priority = 7;
739       break;
740    }
741    return priority;
742 }
743
744
745 static void update_wait_time(JCR *jcr, int newJobStatus)
746 {
747    bool enter_in_waittime;
748    int oldJobStatus = jcr->JobStatus;
749
750    switch (newJobStatus) {
751    case JS_WaitFD:
752    case JS_WaitSD:
753    case JS_WaitMedia:
754    case JS_WaitMount:
755    case JS_WaitStoreRes:
756    case JS_WaitJobRes:
757    case JS_WaitClientRes:
758    case JS_WaitMaxJobs:
759    case JS_WaitPriority:
760       enter_in_waittime = true;
761       break;
762    default:
763       enter_in_waittime = false; /* not a Wait situation */
764       break;
765    }
766    
767    /*
768     * If we were previously waiting and are not any more
769     *   we want to update the wait_time variable, which is
770     *   the start of waiting.
771     */
772    switch (oldJobStatus) {
773    case JS_WaitFD:
774    case JS_WaitSD:
775    case JS_WaitMedia:
776    case JS_WaitMount:
777    case JS_WaitStoreRes:
778    case JS_WaitJobRes:
779    case JS_WaitClientRes:
780    case JS_WaitMaxJobs:
781    case JS_WaitPriority:
782       if (!enter_in_waittime) { /* we get out the wait time */
783          jcr->wait_time_sum += (time(NULL) - jcr->wait_time);
784          jcr->wait_time = 0;
785       }
786       break;
787
788    /* if wait state is new, we keep current time for watchdog MaxWaitTime */
789    default:
790       if (enter_in_waittime) {
791          jcr->wait_time = time(NULL);
792       }
793       break;
794    }
795 }
796
797 void set_jcr_job_status(JCR *jcr, int JobStatus)
798 {
799    jcr->setJobStatus(JobStatus);
800 }
801
802 void JCR::setJobStatus(int JobStatus)
803 {
804    JCR *jcr = this;
805    int priority, old_priority;
806    int oldJobStatus = JobStatus;
807    priority = get_status_priority(JobStatus);
808    old_priority = get_status_priority(oldJobStatus);
809    
810    Dmsg2(800, "set_jcr_job_status(%s, %c)\n", Job, JobStatus);
811
812    /* Update wait_time depending on newJobStatus and oldJobStatus */
813    update_wait_time(this, JobStatus);
814
815    /*
816     * For a set of errors, ... keep the current status
817     *   so it isn't lost. For all others, set it.
818     */
819    Dmsg3(300, "jid=%u OnEntry JobStatus=%c set=%c\n", (uint32_t)JobId,
820          JobStatus, JobStatus);
821    if (priority >= old_priority) {
822       jcr->JobStatus = JobStatus;     /* replace with new priority */
823    }
824
825    if (oldJobStatus != jcr->JobStatus) {
826       Dmsg3(200, "jid=%u leave set_old_job_status=%c new_set=%c\n", (uint32_t)jcr->JobId,
827          oldJobStatus, JobStatus);
828 //    generate_plugin_event(jcr, bEventStatusChange, NULL);
829    }
830 }
831
832 #ifdef TRACE_JCR_CHAIN
833 static int lock_count = 0;
834 #endif
835
836 /*
837  * Lock the chain
838  */
839 #ifdef TRACE_JCR_CHAIN
840 static void b_lock_jcr_chain(const char *fname, int line)
841 #else
842 static void lock_jcr_chain()
843 #endif
844 {
845 #ifdef TRACE_JCR_CHAIN
846    Dmsg3(dbglvl, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
847 #endif
848    P(jcr_lock);
849 }
850
851 /*
852  * Unlock the chain
853  */
854 #ifdef TRACE_JCR_CHAIN
855 static void b_unlock_jcr_chain(const char *fname, int line)
856 #else
857 static void unlock_jcr_chain()
858 #endif
859 {
860 #ifdef TRACE_JCR_CHAIN
861    Dmsg3(dbglvl, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
862 #endif
863    V(jcr_lock);
864 }
865
866 /*
867  * Start walk of jcr chain
868  * The proper way to walk the jcr chain is:
869  *    JCR *jcr;
870  *    foreach_jcr(jcr) {
871  *      ...
872  *    }
873  *    endeach_jcr(jcr);
874  *
875  *  It is possible to leave out the endeach_jcr(jcr), but
876  *   in that case, the last jcr referenced must be explicitly
877  *   released with:
878  *
879  *    free_jcr(jcr);
880  *  
881  */
882 JCR *jcr_walk_start() 
883 {
884    JCR *jcr;
885    lock_jcr_chain();
886    jcr = (JCR *)jcrs->first();
887    if (jcr) {
888       jcr->inc_use_count();
889       if (jcr->JobId > 0) {
890          Dmsg3(dbglvl, "Inc walk_start jid=%u use_count=%d Job=%s\n", 
891             jcr->JobId, jcr->use_count(), jcr->Job);
892       }
893    }
894    unlock_jcr_chain();
895    return jcr;
896 }
897
898 /*
899  * Get next jcr from chain, and release current one
900  */
901 JCR *jcr_walk_next(JCR *prev_jcr)
902 {
903    JCR *jcr;
904
905    lock_jcr_chain();
906    jcr = (JCR *)jcrs->next(prev_jcr);
907    if (jcr) {
908       jcr->inc_use_count();
909       if (jcr->JobId > 0) {
910          Dmsg3(dbglvl, "Inc walk_next jid=%u use_count=%d Job=%s\n", 
911             jcr->JobId, jcr->use_count(), jcr->Job);
912       }
913    }
914    unlock_jcr_chain();
915    if (prev_jcr) {
916       free_jcr(prev_jcr);
917    }
918    return jcr;
919 }
920
921 /*
922  * Release last jcr referenced
923  */
924 void jcr_walk_end(JCR *jcr)
925 {
926    if (jcr) {
927       if (jcr->JobId > 0) {
928          Dmsg3(dbglvl, "Free walk_end jid=%u use_count=%d Job=%s\n", 
929             jcr->JobId, jcr->use_count(), jcr->Job);
930       }
931       free_jcr(jcr);
932    }
933 }
934
935
936 /*
937  * Setup to call the timeout check routine every 30 seconds
938  *  This routine will check any timers that have been enabled.
939  */
940 bool init_jcr_subsystem(void)
941 {
942    watchdog_t *wd = new_watchdog();
943
944    wd->one_shot = false;
945    wd->interval = 30;   /* FIXME: should be configurable somewhere, even
946                          if only with a #define */
947    wd->callback = jcr_timeout_check;
948
949    register_watchdog(wd);
950
951    return true;
952 }
953
954 static void jcr_timeout_check(watchdog_t *self)
955 {
956    JCR *jcr;
957    BSOCK *bs;
958    time_t timer_start;
959
960    Dmsg0(dbglvl, "Start JCR timeout checks\n");
961
962    /* Walk through all JCRs checking if any one is
963     * blocked for more than specified max time.
964     */
965    foreach_jcr(jcr) {
966       Dmsg2(dbglvl, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
967       if (jcr->JobId == 0) {
968          continue;
969       }
970       bs = jcr->store_bsock;
971       if (bs) {
972          timer_start = bs->timer_start;
973          if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
974             bs->timer_start = 0;      /* turn off timer */
975             bs->set_timed_out();
976             Qmsg(jcr, M_ERROR, 0, _(
977 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
978                  watchdog_time - timer_start);
979             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
980          }
981       }
982       bs = jcr->file_bsock;
983       if (bs) {
984          timer_start = bs->timer_start;
985          if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
986             bs->timer_start = 0;      /* turn off timer */
987             bs->set_timed_out();
988             Qmsg(jcr, M_ERROR, 0, _(
989 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
990                  watchdog_time - timer_start);
991             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
992          }
993       }
994       bs = jcr->dir_bsock;
995       if (bs) {
996          timer_start = bs->timer_start;
997          if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
998             bs->timer_start = 0;      /* turn off timer */
999             bs->set_timed_out();
1000             Qmsg(jcr, M_ERROR, 0, _(
1001 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
1002                  watchdog_time - timer_start);
1003             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1004          }
1005       }
1006    }
1007    endeach_jcr(jcr);
1008
1009    Dmsg0(dbglvl, "Finished JCR timeout checks\n");
1010 }
1011
1012 /* 
1013  * Return next JobId from comma separated list   
1014  *
1015  * Returns:
1016  *   1 if next JobId returned
1017  *   0 if no more JobIds are in list
1018  *  -1 there is an error
1019  */
1020 int get_next_jobid_from_list(char **p, uint32_t *JobId)
1021 {
1022    const int maxlen = 30;
1023    char jobid[maxlen+1];
1024    char *q = *p;
1025
1026    jobid[0] = 0;
1027    for (int i=0; i<maxlen; i++) {
1028       if (*q == 0) {
1029          break;
1030       } else if (*q == ',') {
1031          q++;
1032          break;
1033       }
1034       jobid[i] = *q++;
1035       jobid[i+1] = 0;
1036    }
1037    if (jobid[0] == 0) {
1038       return 0;
1039    } else if (!is_a_number(jobid)) {
1040       return -1;                      /* error */
1041    }
1042    *p = q;
1043    *JobId = str_to_int64(jobid);
1044    return 1;
1045 }
1046
1047 /*
1048  * Timeout signal comes here
1049  */
1050 extern "C" void timeout_handler(int sig)
1051 {
1052    return;                            /* thus interrupting the function */
1053 }
1054
1055 /* Used to display specific daemon information after a fatal signal 
1056  * (like B_DB in the director)
1057  */
1058 #define MAX_DBG_HOOK 10
1059 static dbg_jcr_hook_t *dbg_jcr_hooks[MAX_DBG_HOOK];
1060 static int dbg_jcr_handler_count;
1061
1062 void dbg_jcr_add_hook(dbg_jcr_hook_t *fct)
1063 {
1064    ASSERT(dbg_jcr_handler_count < MAX_DBG_HOOK);
1065    dbg_jcr_hooks[dbg_jcr_handler_count++] = fct;
1066 }
1067
1068 /*
1069  * !!! WARNING !!! 
1070  *
1071  * This function should be used ONLY after a fatal signal. We walk through the
1072  * JCR chain without doing any lock, bacula should not be running.
1073  */
1074 void _dbg_print_jcr(FILE *fp)
1075 {
1076    char buf1[128], buf2[128], buf3[128], buf4[128];
1077    if (!jcrs) {
1078       return;
1079    }
1080
1081    fprintf(fp, "Attempt to dump current JCRs\n");
1082
1083    for (JCR *jcr = (JCR *)jcrs->first(); jcr ; jcr = (JCR *)jcrs->next(jcr)) {
1084       if (!jcr) {               /* protect us against something ? */
1085          continue;
1086       }
1087       
1088       fprintf(fp, "JCR=%p JobId=%i name=%s JobStatus=%c\n", 
1089               jcr, jcr->JobId, jcr->Job, jcr->JobStatus);
1090 #ifdef HAVE_WIN32
1091       fprintf(fp, "\tuse_count=%i\n",
1092               jcr->use_count());
1093 #else
1094       /* KES -- removed non-portable code referencing pthread_t */
1095       fprintf(fp, "\tuse_count=%d\n", jcr->use_count());
1096 #endif
1097       fprintf(fp, "\tJobType=%c JobLevel=%c\n",
1098               jcr->get_JobType(), jcr->get_JobLevel());
1099       bstrftime(buf1, sizeof(buf1), jcr->sched_time);
1100       bstrftime(buf2, sizeof(buf2), jcr->start_time);
1101       bstrftime(buf3, sizeof(buf3), jcr->end_time);
1102       bstrftime(buf4, sizeof(buf4), jcr->wait_time);
1103       fprintf(fp, "\tsched_time=%s start_time=%s\n\tend_time=%s wait_time=%s\n",
1104               buf1, buf2, buf3, buf4);
1105       fprintf(fp, "\tdequeing=%i\n", jcr->dequeuing);
1106       fprintf(fp, "\tdb=%p db_batch=%p batch_started=%i\n", 
1107               jcr->db, jcr->db_batch, jcr->batch_started);
1108       
1109       for(int i=0; i < dbg_jcr_handler_count; i++) {
1110          dbg_jcr_hook_t *fct = dbg_jcr_hooks[i];
1111          fct(jcr, fp);
1112       }
1113    }
1114 }