]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/jcr.c
Fix releasing of last_jobs
[bacula/bacula] / bacula / src / lib / jcr.c
1 /*
2  * Manipulation routines for Job Control Records
3  *
4  *  Kern E. Sibbald, December 2000
5  *
6  *  Version $Id$
7  *
8  *  These routines are thread safe.
9  *
10  */
11 /*
12    Copyright (C) 2000-2003 Kern Sibbald and John Walker
13
14    This program is free software; you can redistribute it and/or
15    modify it under the terms of the GNU General Public License as
16    published by the Free Software Foundation; either version 2 of
17    the License, or (at your option) any later version.
18
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22    General Public License for more details.
23
24    You should have received a copy of the GNU General Public
25    License along with this program; if not, write to the Free
26    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27    MA 02111-1307, USA.
28
29  */
30
31 #include "bacula.h"
32 #include "jcr.h"
33
34 /* External variables we reference */
35 extern time_t watchdog_time;
36
37 /* Forward referenced functions */
38 static void timeout_handler(int sig);
39 static void jcr_timeout_check(watchdog_t *self);
40
41 int num_jobs_run;
42 dlist *last_jobs = NULL;
43 static const int max_last_jobs = 10;
44
45 static JCR *jobs = NULL;              /* pointer to JCR chain */
46 static brwlock_t lock;                /* lock for last jobs and JCR chain */
47
48 void init_last_jobs_list()
49 {
50    int errstat;
51    struct s_last_job *job_entry = NULL;
52    if (!last_jobs) {
53       last_jobs = new dlist(job_entry, &job_entry->link);
54       if ((errstat=rwl_init(&lock)) != 0) {
55          Emsg1(M_ABORT, 0, _("Unable to initialize jcr_chain lock. ERR=%s\n"), 
56                strerror(errstat));
57       }
58    }
59
60 }
61
62 void term_last_jobs_list()
63 {
64    if (last_jobs) {
65       while (!last_jobs->empty()) {
66          free(last_jobs->first());     
67       }
68       delete last_jobs;
69       last_jobs = NULL;
70       rwl_destroy(&lock);
71    }
72 }
73
74 void read_last_jobs_list(int fd, uint64_t addr)
75 {
76    struct s_last_job *je, job;
77    uint32_t num;
78
79    Dmsg1(100, "read_last_jobs seek to %d\n", (int)addr);
80    if (addr == 0 || lseek(fd, addr, SEEK_SET) < 0) {
81       return;
82    }
83    if (read(fd, &num, sizeof(num)) != sizeof(num)) {
84       return;
85    }
86    Dmsg1(100, "Read num_items=%d\n", num);
87    if (num > 4 * max_last_jobs) {  /* sanity check */
88       return;
89    }
90    for ( ; num; num--) {
91       if (read(fd, &job, sizeof(job)) != sizeof(job)) {
92          Dmsg1(000, "Read job entry. ERR=%s\n", strerror(errno));
93          return;
94       }
95       if (job.JobId > 0) {
96          je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
97          memcpy((char *)je, (char *)&job, sizeof(job));
98          if (!last_jobs) {
99             init_last_jobs_list();
100          }
101          last_jobs->append(je);
102          if (last_jobs->size() > max_last_jobs) {
103             last_jobs->remove(last_jobs->first());
104          }
105       }
106    }
107 }
108
109 uint64_t write_last_jobs_list(int fd, uint64_t addr)
110 {
111    struct s_last_job *je;
112    uint32_t num;
113
114    Dmsg1(100, "write_last_jobs seek to %d\n", (int)addr);
115    if (lseek(fd, addr, SEEK_SET) < 0) {
116       return 0;
117    }
118    if (last_jobs) {
119       /* First record is number of entires */
120       num = last_jobs->size();
121       if (write(fd, &num, sizeof(num)) != sizeof(num)) {
122          Dmsg1(000, "Error writing num_items: ERR=%s\n", strerror(errno));
123          return 0;
124       }
125       foreach_dlist(je, last_jobs) {
126          if (write(fd, je, sizeof(struct s_last_job)) != sizeof(struct s_last_job)) {
127             Dmsg1(000, "Error writing job: ERR=%s\n", strerror(errno));
128             return 0;
129          }
130       }
131    }
132    /* Return current address */
133    ssize_t stat = lseek(fd, 0, SEEK_CUR);
134    if (stat < 0) {
135       stat = 0;
136    }
137    return stat;
138       
139 }
140
141 void lock_last_jobs_list() 
142 {
143    /* Use jcr chain mutex */
144    lock_jcr_chain();
145 }
146
147 void unlock_last_jobs_list() 
148 {
149    /* Use jcr chain mutex */
150    unlock_jcr_chain();
151 }
152
153 /*
154  * Push a subroutine address into the job end callback stack
155  */
156 void job_end_push(JCR *jcr, void job_end_cb(JCR *jcr))
157 {
158    jcr->job_end_push.prepend((void *)job_end_cb);
159 }
160
161 /* Pop each job_end subroutine and call it */
162 static void job_end_pop(JCR *jcr)
163 {
164    void (*job_end_cb)(JCR *jcr);
165    for (int i=0; i<jcr->job_end_push.size(); i++) {
166       job_end_cb = (void (*)(JCR *))jcr->job_end_push.get(i);
167       job_end_cb(jcr);
168    }
169 }
170
171 /*
172  * Create a Job Control Record and link it into JCR chain
173  * Returns newly allocated JCR
174  * Note, since each daemon has a different JCR, he passes
175  *  us the size.
176  */
177 JCR *new_jcr(int size, JCR_free_HANDLER *daemon_free_jcr)
178 {
179    JCR *jcr;
180    MQUEUE_ITEM *item = NULL;
181    struct sigaction sigtimer;
182
183    Dmsg0(400, "Enter new_jcr\n");
184    jcr = (JCR *)malloc(size);
185    memset(jcr, 0, size);
186    jcr->msg_queue = new dlist(item, &item->link);
187    jcr->job_end_push.init(1, false);
188    jcr->sched_time = time(NULL);
189    jcr->daemon_free_jcr = daemon_free_jcr;    /* plug daemon free routine */
190    jcr->use_count = 1;
191    pthread_mutex_init(&(jcr->mutex), NULL);
192    jcr->JobStatus = JS_Created;       /* ready to run */
193    jcr->VolumeName = get_pool_memory(PM_FNAME);
194    jcr->VolumeName[0] = 0;
195    jcr->errmsg = get_pool_memory(PM_MESSAGE);
196    jcr->errmsg[0] = 0;
197    /* Setup some dummy values */
198    jcr->Job[0] = 0;                   /* no job name by default */
199    jcr->JobId = 0;
200    jcr->JobType = JT_ADMIN;
201    jcr->JobLevel = L_NONE;
202    jcr->JobStatus = JS_Created;
203
204    sigtimer.sa_flags = 0;
205    sigtimer.sa_handler = timeout_handler;
206    sigfillset(&sigtimer.sa_mask);
207    sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL);
208
209    lock_jcr_chain();
210    jcr->prev = NULL;
211    jcr->next = jobs;
212    if (jobs) {
213       jobs->prev = jcr;
214    }
215    jobs = jcr;
216    unlock_jcr_chain();
217    return jcr;
218 }
219
220
221 /*
222  * Remove a JCR from the chain
223  * NOTE! The chain must be locked prior to calling
224  *       this routine.
225  */
226 static void remove_jcr(JCR *jcr)
227 {
228    Dmsg0(400, "Enter remove_jcr\n");
229    if (!jcr) {
230       Emsg0(M_ABORT, 0, "NULL jcr.\n");
231    }
232    if (!jcr->prev) {                  /* if no prev */
233       jobs = jcr->next;               /* set new head */
234    } else {
235       jcr->prev->next = jcr->next;    /* update prev */
236    }
237    if (jcr->next) {
238       jcr->next->prev = jcr->prev;
239    }
240    Dmsg0(400, "Leave remove_jcr\n");
241 }
242
243 /*
244  * Free stuff common to all JCRs.  N.B. Be careful to include only
245  *  generic stuff in the common part of the jcr. 
246  */
247 static void free_common_jcr(JCR *jcr)
248 {
249    struct s_last_job *je, last_job;
250
251    /* Keep some statistics */
252    switch (jcr->JobType) {
253    case JT_BACKUP:
254    case JT_VERIFY:
255    case JT_RESTORE:
256    case JT_ADMIN:
257       num_jobs_run++;
258       last_job.JobType = jcr->JobType;
259       last_job.JobId = jcr->JobId;
260       last_job.VolSessionId = jcr->VolSessionId;
261       last_job.VolSessionTime = jcr->VolSessionTime;
262       bstrncpy(last_job.Job, jcr->Job, sizeof(last_job.Job));
263       last_job.JobFiles = jcr->JobFiles;
264       last_job.JobBytes = jcr->JobBytes;
265       last_job.JobStatus = jcr->JobStatus;
266       last_job.JobLevel = jcr->JobLevel;
267       last_job.start_time = jcr->start_time;
268       last_job.end_time = time(NULL);
269       /* Keep list of last jobs, but not Console where JobId==0 */
270       if (last_job.JobId > 0) {
271          je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
272          memcpy((char *)je, (char *)&last_job, sizeof(last_job));
273          if (!last_jobs) {
274             init_last_jobs_list();
275          }
276          last_jobs->append(je);
277          if (last_jobs->size() > max_last_jobs) {
278             last_jobs->remove(last_jobs->first());
279          }
280       }
281       break;
282    default:
283       break;
284    }
285    pthread_mutex_destroy(&jcr->mutex);
286
287    delete jcr->msg_queue;
288    close_msg(jcr);                    /* close messages for this job */
289
290    /* do this after closing messages */
291    if (jcr->client_name) {
292       free_pool_memory(jcr->client_name);
293       jcr->client_name = NULL;
294    }
295
296    if (jcr->sd_auth_key) {
297       free(jcr->sd_auth_key);
298       jcr->sd_auth_key = NULL;
299    }
300    if (jcr->VolumeName) {
301       free_pool_memory(jcr->VolumeName);
302       jcr->VolumeName = NULL;
303    }
304
305    if (jcr->dir_bsock) {
306       bnet_close(jcr->dir_bsock);
307       jcr->dir_bsock = NULL;
308    }
309    if (jcr->errmsg) {
310       free_pool_memory(jcr->errmsg);
311       jcr->errmsg = NULL;
312    }
313    if (jcr->where) {
314       free(jcr->where);
315       jcr->where = NULL;
316    }
317    if (jcr->cached_path) {
318       free_pool_memory(jcr->cached_path);
319       jcr->cached_path = NULL;
320       jcr->cached_pnl = 0;
321    }
322    free_getuser_cache();
323    free_getgroup_cache();
324    free(jcr);
325 }
326
327 /* 
328  * Global routine to free a jcr
329  */
330 #ifdef DEBUG
331 void b_free_jcr(const char *file, int line, JCR *jcr)
332 {
333    Dmsg3(400, "Enter free_jcr 0x%x from %s:%d\n", jcr, file, line);
334
335 #else
336
337 void free_jcr(JCR *jcr)
338 {
339
340    Dmsg1(400, "Enter free_jcr 0x%x\n", jcr);
341
342 #endif
343
344    lock_jcr_chain();
345    jcr->use_count--;                  /* decrement use count */
346    if (jcr->use_count < 0) {
347       Emsg2(M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"),
348          jcr->use_count, jcr->JobId);
349    }
350    Dmsg3(400, "Dec free_jcr 0x%x use_count=%d jobid=%d\n", jcr, jcr->use_count, jcr->JobId);
351    if (jcr->use_count > 0) {          /* if in use */
352       unlock_jcr_chain();
353       Dmsg2(400, "free_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
354       return;
355    }
356    dequeue_messages(jcr);
357    remove_jcr(jcr);
358    job_end_pop(jcr);                  /* pop and call hooked routines */
359
360    Dmsg1(400, "End job=%d\n", jcr->JobId);
361    if (jcr->daemon_free_jcr) {
362       jcr->daemon_free_jcr(jcr);      /* call daemon free routine */
363    }
364
365    free_common_jcr(jcr);
366
367    close_msg(NULL);                   /* flush any daemon messages */
368    unlock_jcr_chain();
369    Dmsg0(400, "Exit free_jcr\n");
370 }
371
372
373 /* 
374  * Global routine to free a jcr
375  *  JCR chain is already locked
376  */
377 void free_locked_jcr(JCR *jcr)
378 {
379    jcr->use_count--;                  /* decrement use count */
380    Dmsg2(400, "Dec free_locked_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
381    if (jcr->use_count > 0) {          /* if in use */
382       return;
383    }
384    remove_jcr(jcr);
385    jcr->daemon_free_jcr(jcr);         /* call daemon free routine */
386    free_common_jcr(jcr);
387 }
388
389
390
391 /*
392  * Given a JobId, find the JCR      
393  *   Returns: jcr on success
394  *            NULL on failure
395  */
396 JCR *get_jcr_by_id(uint32_t JobId)
397 {
398    JCR *jcr;       
399
400    lock_jcr_chain();                    /* lock chain */
401    for (jcr = jobs; jcr; jcr=jcr->next) {
402       if (jcr->JobId == JobId) {
403          P(jcr->mutex);
404          jcr->use_count++;
405          V(jcr->mutex);
406          Dmsg2(400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
407          break;
408       }
409    }
410    unlock_jcr_chain();
411    return jcr; 
412 }
413
414 /*
415  * Given a SessionId and SessionTime, find the JCR      
416  *   Returns: jcr on success
417  *            NULL on failure
418  */
419 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
420 {
421    JCR *jcr;       
422
423    lock_jcr_chain();
424    for (jcr = jobs; jcr; jcr=jcr->next) {
425       if (jcr->VolSessionId == SessionId && 
426           jcr->VolSessionTime == SessionTime) {
427          P(jcr->mutex);
428          jcr->use_count++;
429          V(jcr->mutex);
430          Dmsg2(400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
431          break;
432       }
433    }
434    unlock_jcr_chain();
435    return jcr; 
436 }
437
438
439 /*
440  * Given a Job, find the JCR      
441  *  compares on the number of characters in Job
442  *  thus allowing partial matches.
443  *   Returns: jcr on success
444  *            NULL on failure
445  */
446 JCR *get_jcr_by_partial_name(char *Job)
447 {
448    JCR *jcr;       
449    int len;
450
451    if (!Job) {
452       return NULL;
453    }
454    lock_jcr_chain();
455    len = strlen(Job);
456    for (jcr = jobs; jcr; jcr=jcr->next) {
457       if (strncmp(Job, jcr->Job, len) == 0) {
458          P(jcr->mutex);
459          jcr->use_count++;
460          V(jcr->mutex);
461          Dmsg2(400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
462          break;
463       }
464    }
465    unlock_jcr_chain();
466    return jcr; 
467 }
468
469
470 /*
471  * Given a Job, find the JCR      
472  *  requires an exact match of names.
473  *   Returns: jcr on success
474  *            NULL on failure
475  */
476 JCR *get_jcr_by_full_name(char *Job)
477 {
478    JCR *jcr;       
479
480    if (!Job) {
481       return NULL;
482    }
483    lock_jcr_chain();
484    for (jcr = jobs; jcr; jcr=jcr->next) {
485       if (strcmp(jcr->Job, Job) == 0) {
486          P(jcr->mutex);
487          jcr->use_count++;
488          V(jcr->mutex);
489          Dmsg2(400, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
490          break;
491       }
492    }
493    unlock_jcr_chain();
494    return jcr; 
495 }
496
497 void set_jcr_job_status(JCR *jcr, int JobStatus)
498 {
499    /*
500     * For a set of errors, ... keep the current status
501     *   so it isn't lost. For all others, set it.
502     */
503    switch (jcr->JobStatus) {
504    case JS_ErrorTerminated:
505    case JS_Error:
506    case JS_FatalError:
507    case JS_Differences:
508    case JS_Canceled:
509       break;
510    default:
511       jcr->JobStatus = JobStatus;
512    }
513 }
514
515 /* 
516  * Lock the chain
517  */
518 void lock_jcr_chain()
519 {
520    int errstat;
521    if ((errstat=rwl_writelock(&lock)) != 0) {
522       Emsg1(M_ABORT, 0, "rwl_writelock failure. ERR=%s\n",
523            strerror(errstat));
524    }
525 }
526
527 /*
528  * Unlock the chain
529  */
530 void unlock_jcr_chain()
531 {
532    int errstat;
533    if ((errstat=rwl_writeunlock(&lock)) != 0) {
534       Emsg1(M_ABORT, 0, "rwl_writeunlock failure. ERR=%s\n",
535            strerror(errstat));
536    }
537 }
538
539
540 JCR *get_next_jcr(JCR *prev_jcr)
541 {
542    JCR *jcr;
543
544    if (prev_jcr == NULL) {
545       jcr = jobs;
546    } else {
547       jcr = prev_jcr->next;
548    }
549    if (jcr) {
550       P(jcr->mutex);
551       jcr->use_count++;
552       V(jcr->mutex);
553       Dmsg2(400, "Inc get_next_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
554    }
555    return jcr;
556 }
557
558 bool init_jcr_subsystem(void)
559 {
560    watchdog_t *wd = new_watchdog();
561
562    wd->one_shot = false;
563    wd->interval = 30;   /* FIXME: should be configurable somewhere, even
564                          if only with a #define */
565    wd->callback = jcr_timeout_check;
566
567    register_watchdog(wd);
568
569    return true;
570 }
571
572 static void jcr_timeout_check(watchdog_t *self)
573 {
574    JCR *jcr;
575    BSOCK *fd;
576    time_t timer_start;
577
578    Dmsg0(400, "Start JCR timeout checks\n");
579
580    /* Walk through all JCRs checking if any one is 
581     * blocked for more than specified max time.
582     */
583    lock_jcr_chain();
584    foreach_jcr(jcr) {
585       free_locked_jcr(jcr);           /* OK to free now cuz chain is locked */
586       if (jcr->JobId == 0) {
587          continue;
588       }
589       fd = jcr->store_bsock;
590       if (fd) {
591          timer_start = fd->timer_start;
592          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
593             fd->timer_start = 0;      /* turn off timer */
594             fd->timed_out = TRUE;
595             Jmsg(jcr, M_ERROR, 0, _(
596 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
597                  watchdog_time - timer_start);
598             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
599          }
600       }
601       fd = jcr->file_bsock;
602       if (fd) {
603          timer_start = fd->timer_start;
604          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
605             fd->timer_start = 0;      /* turn off timer */
606             fd->timed_out = TRUE;
607             Jmsg(jcr, M_ERROR, 0, _(
608 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
609                  watchdog_time - timer_start);
610             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
611          }
612       }
613       fd = jcr->dir_bsock;
614       if (fd) {
615          timer_start = fd->timer_start;
616          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
617             fd->timer_start = 0;      /* turn off timer */
618             fd->timed_out = TRUE;
619             Jmsg(jcr, M_ERROR, 0, _(
620 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
621                  watchdog_time - timer_start);
622             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
623          }
624       }
625
626    }
627    unlock_jcr_chain();
628
629    Dmsg0(400, "Finished JCR timeout checks\n");
630 }
631
632 /*
633  * Timeout signal comes here
634  */
635 static void timeout_handler(int sig)
636 {
637    return;                            /* thus interrupting the function */
638 }