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