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