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