]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/jcr.c
Modify setJobStatus so cancel has same priority as fatal errors
[bacula/bacula] / bacula / src / lib / jcr.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Manipulation routines for Job Control Records and
30  *  handling of last_jobs_list.
31  *
32  *  Kern E. Sibbald, December 2000
33  *
34  *  Version $Id$
35  *
36  *  These routines are thread safe.
37  *
38  *  The job list routines were re-written in May 2005 to
39  *  eliminate the global lock while traversing the list, and
40  *  to use the dlist subroutines.  The locking is now done
41  *  on the list each time the list is modified or traversed.
42  *  That is it is "micro-locked" rather than globally locked.
43  *  The result is that there is one lock/unlock for each entry
44  *  in the list while traversing it rather than a single lock
45  *  at the beginning of a traversal and one at the end.  This
46  *  incurs slightly more overhead, but effectively eliminates 
47  *  the possibilty of race conditions.  In addition, with the
48  *  exception of the global locking of the list during the
49  *  re-reading of the config file, no recursion is needed.
50  *
51  */
52
53 #include "bacula.h"
54 #include "jcr.h"
55
56 const int dbglvl = 3400;
57
58 /* 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->set_JobType(JT_SYSTEM);          /* internal job until defined */
371    jcr->set_JobLevel(L_NONE);
372    set_jcr_job_status(jcr, 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       priority = 10;
744       break;
745    case JS_Error:
746       priority = 8;
747       break;
748    case JS_Differences:
749       priority = 7;
750       break;
751    }
752    return priority;
753 }
754
755
756 static void update_wait_time(JCR *jcr, int newJobStatus)
757 {
758    bool enter_in_waittime;
759    int oldJobStatus = jcr->JobStatus;
760
761    switch (newJobStatus) {
762    case JS_WaitFD:
763    case JS_WaitSD:
764    case JS_WaitMedia:
765    case JS_WaitMount:
766    case JS_WaitStoreRes:
767    case JS_WaitJobRes:
768    case JS_WaitClientRes:
769    case JS_WaitMaxJobs:
770    case JS_WaitPriority:
771       enter_in_waittime = true;
772       break;
773    default:
774       enter_in_waittime = false; /* not a Wait situation */
775       break;
776    }
777    
778    /*
779     * If we were previously waiting and are not any more
780     *   we want to update the wait_time variable, which is
781     *   the start of waiting.
782     */
783    switch (oldJobStatus) {
784    case JS_WaitFD:
785    case JS_WaitSD:
786    case JS_WaitMedia:
787    case JS_WaitMount:
788    case JS_WaitStoreRes:
789    case JS_WaitJobRes:
790    case JS_WaitClientRes:
791    case JS_WaitMaxJobs:
792    case JS_WaitPriority:
793       if (!enter_in_waittime) { /* we get out the wait time */
794          jcr->wait_time_sum += (time(NULL) - jcr->wait_time);
795          jcr->wait_time = 0;
796       }
797       break;
798
799    /* if wait state is new, we keep current time for watchdog MaxWaitTime */
800    default:
801       if (enter_in_waittime) {
802          jcr->wait_time = time(NULL);
803       }
804       break;
805    }
806 }
807
808 void set_jcr_job_status(JCR *jcr, int JobStatus)
809 {
810    jcr->setJobStatus(JobStatus);
811 }
812
813 void JCR::setJobStatus(int JobStatus)
814 {
815    JCR *jcr = this;
816    int priority, old_priority;
817    int oldJobStatus = JobStatus;
818    priority = get_status_priority(JobStatus);
819    old_priority = get_status_priority(oldJobStatus);
820    
821    Dmsg2(800, "set_jcr_job_status(%s, %c)\n", Job, JobStatus);
822
823    /* Update wait_time depending on newJobStatus and oldJobStatus */
824    update_wait_time(this, JobStatus);
825
826    /*
827     * For a set of errors, ... keep the current status
828     *   so it isn't lost. For all others, set it.
829     */
830    Dmsg3(300, "jid=%u OnEntry JobStatus=%c set=%c\n", (uint32_t)JobId,
831          JobStatus, JobStatus);
832    if (priority >= old_priority) {
833       jcr->JobStatus = JobStatus;     /* replace with new priority */
834    }
835
836    if (oldJobStatus != jcr->JobStatus) {
837       Dmsg3(200, "jid=%u leave set_old_job_status=%c new_set=%c\n", (uint32_t)jcr->JobId,
838          oldJobStatus, JobStatus);
839 //    generate_plugin_event(jcr, bEventStatusChange, NULL);
840    }
841 }
842
843 #ifdef TRACE_JCR_CHAIN
844 static int lock_count = 0;
845 #endif
846
847 /*
848  * Lock the chain
849  */
850 #ifdef TRACE_JCR_CHAIN
851 static void b_lock_jcr_chain(const char *fname, int line)
852 #else
853 static void lock_jcr_chain()
854 #endif
855 {
856 #ifdef TRACE_JCR_CHAIN
857    Dmsg3(dbglvl, "Lock jcr chain %d from %s:%d\n", ++lock_count, fname, line);
858 #endif
859    P(jcr_lock);
860 }
861
862 /*
863  * Unlock the chain
864  */
865 #ifdef TRACE_JCR_CHAIN
866 static void b_unlock_jcr_chain(const char *fname, int line)
867 #else
868 static void unlock_jcr_chain()
869 #endif
870 {
871 #ifdef TRACE_JCR_CHAIN
872    Dmsg3(dbglvl, "Unlock jcr chain %d from %s:%d\n", lock_count--, fname, line);
873 #endif
874    V(jcr_lock);
875 }
876
877 /*
878  * Start walk of jcr chain
879  * The proper way to walk the jcr chain is:
880  *    JCR *jcr;
881  *    foreach_jcr(jcr) {
882  *      ...
883  *    }
884  *    endeach_jcr(jcr);
885  *
886  *  It is possible to leave out the endeach_jcr(jcr), but
887  *   in that case, the last jcr referenced must be explicitly
888  *   released with:
889  *
890  *    free_jcr(jcr);
891  *  
892  */
893 JCR *jcr_walk_start() 
894 {
895    JCR *jcr;
896    lock_jcr_chain();
897    jcr = (JCR *)jcrs->first();
898    if (jcr) {
899       jcr->inc_use_count();
900       if (jcr->JobId > 0) {
901          Dmsg3(dbglvl, "Inc walk_start jid=%u use_count=%d Job=%s\n", 
902             jcr->JobId, jcr->use_count(), jcr->Job);
903       }
904    }
905    unlock_jcr_chain();
906    return jcr;
907 }
908
909 /*
910  * Get next jcr from chain, and release current one
911  */
912 JCR *jcr_walk_next(JCR *prev_jcr)
913 {
914    JCR *jcr;
915
916    lock_jcr_chain();
917    jcr = (JCR *)jcrs->next(prev_jcr);
918    if (jcr) {
919       jcr->inc_use_count();
920       if (jcr->JobId > 0) {
921          Dmsg3(dbglvl, "Inc walk_next jid=%u use_count=%d Job=%s\n", 
922             jcr->JobId, jcr->use_count(), jcr->Job);
923       }
924    }
925    unlock_jcr_chain();
926    if (prev_jcr) {
927       free_jcr(prev_jcr);
928    }
929    return jcr;
930 }
931
932 /*
933  * Release last jcr referenced
934  */
935 void jcr_walk_end(JCR *jcr)
936 {
937    if (jcr) {
938       if (jcr->JobId > 0) {
939          Dmsg3(dbglvl, "Free walk_end jid=%u use_count=%d Job=%s\n", 
940             jcr->JobId, jcr->use_count(), jcr->Job);
941       }
942       free_jcr(jcr);
943    }
944 }
945
946
947 /*
948  * Setup to call the timeout check routine every 30 seconds
949  *  This routine will check any timers that have been enabled.
950  */
951 bool init_jcr_subsystem(void)
952 {
953    watchdog_t *wd = new_watchdog();
954
955    wd->one_shot = false;
956    wd->interval = 30;   /* FIXME: should be configurable somewhere, even
957                          if only with a #define */
958    wd->callback = jcr_timeout_check;
959
960    register_watchdog(wd);
961
962    return true;
963 }
964
965 static void jcr_timeout_check(watchdog_t *self)
966 {
967    JCR *jcr;
968    BSOCK *bs;
969    time_t timer_start;
970
971    Dmsg0(dbglvl, "Start JCR timeout checks\n");
972
973    /* Walk through all JCRs checking if any one is
974     * blocked for more than specified max time.
975     */
976    foreach_jcr(jcr) {
977       Dmsg2(dbglvl, "jcr_timeout_check JobId=%u jcr=0x%x\n", jcr->JobId, jcr);
978       if (jcr->JobId == 0) {
979          continue;
980       }
981       bs = jcr->store_bsock;
982       if (bs) {
983          timer_start = bs->timer_start;
984          if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
985             bs->timer_start = 0;      /* turn off timer */
986             bs->set_timed_out();
987             Qmsg(jcr, M_ERROR, 0, _(
988 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
989                  watchdog_time - timer_start);
990             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
991          }
992       }
993       bs = jcr->file_bsock;
994       if (bs) {
995          timer_start = bs->timer_start;
996          if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
997             bs->timer_start = 0;      /* turn off timer */
998             bs->set_timed_out();
999             Qmsg(jcr, M_ERROR, 0, _(
1000 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
1001                  watchdog_time - timer_start);
1002             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1003          }
1004       }
1005       bs = jcr->dir_bsock;
1006       if (bs) {
1007          timer_start = bs->timer_start;
1008          if (timer_start && (watchdog_time - timer_start) > bs->timeout) {
1009             bs->timer_start = 0;      /* turn off timer */
1010             bs->set_timed_out();
1011             Qmsg(jcr, M_ERROR, 0, _(
1012 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
1013                  watchdog_time - timer_start);
1014             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
1015          }
1016       }
1017    }
1018    endeach_jcr(jcr);
1019
1020    Dmsg0(dbglvl, "Finished JCR timeout checks\n");
1021 }
1022
1023 /*
1024  * Timeout signal comes here
1025  */
1026 extern "C" void timeout_handler(int sig)
1027 {
1028    return;                            /* thus interrupting the function */
1029 }
1030
1031 /* Used to display specific daemon information after a fatal signal 
1032  * (like B_DB in the director)
1033  */
1034 #define MAX_DBG_HOOK 10
1035 static dbg_jcr_hook_t *dbg_jcr_hooks[MAX_DBG_HOOK];
1036 static int dbg_jcr_handler_count;
1037
1038 void dbg_jcr_add_hook(dbg_jcr_hook_t *fct)
1039 {
1040    ASSERT(dbg_jcr_handler_count < MAX_DBG_HOOK);
1041    dbg_jcr_hooks[dbg_jcr_handler_count++] = fct;
1042 }
1043
1044 /*
1045  * !!! WARNING !!! 
1046  *
1047  * This function should be used ONLY after a fatal signal. We walk through the
1048  * JCR chain without doing any lock, bacula should not be running.
1049  */
1050 void _dbg_print_jcr(FILE *fp)
1051 {
1052    char buf1[128], buf2[128], buf3[128], buf4[128];
1053    if (!jcrs) {
1054       return;
1055    }
1056
1057    fprintf(fp, "Attempt to dump current JCRs\n");
1058
1059    for (JCR *jcr = (JCR *)jcrs->first(); jcr ; jcr = (JCR *)jcrs->next(jcr)) {
1060       if (!jcr) {               /* protect us against something ? */
1061          continue;
1062       }
1063       
1064       fprintf(fp, "JCR=%p JobId=%i name=%s JobStatus=%c\n", 
1065               jcr, jcr->JobId, jcr->Job, jcr->JobStatus);
1066 #ifdef HAVE_WIN32
1067       fprintf(fp, "\tuse_count=%i\n",
1068               jcr->use_count());
1069 #else
1070       /* KES -- removed non-portable code referencing pthread_t */
1071       fprintf(fp, "\tuse_count=%d\n", jcr->use_count());
1072 #endif
1073       fprintf(fp, "\tJobType=%c JobLevel=%c\n",
1074               jcr->get_JobType(), jcr->get_JobLevel());
1075       bstrftime(buf1, sizeof(buf1), jcr->sched_time);
1076       bstrftime(buf2, sizeof(buf2), jcr->start_time);
1077       bstrftime(buf3, sizeof(buf3), jcr->end_time);
1078       bstrftime(buf4, sizeof(buf4), jcr->wait_time);
1079       fprintf(fp, "\tsched_time=%s start_time=%s\n\tend_time=%s wait_time=%s\n",
1080               buf1, buf2, buf3, buf4);
1081       fprintf(fp, "\tdequeing=%i\n", jcr->dequeuing);
1082       fprintf(fp, "\tdb=%p db_batch=%p batch_started=%i\n", 
1083               jcr->db, jcr->db_batch, jcr->batch_started);
1084       
1085       for(int i=0; i < dbg_jcr_handler_count; i++) {
1086          dbg_jcr_hook_t *fct = dbg_jcr_hooks[i];
1087          fct(jcr, fp);
1088       }
1089    }
1090 }