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