]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/jcr.c
20f2fe7a8027958e9a2b81eb878aa6102431ac81
[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.NumJobs++;
220       last_job.JobType = jcr->JobType;
221       last_job.JobId = jcr->JobId;
222       last_job.VolSessionId = jcr->VolSessionId;
223       last_job.VolSessionTime = jcr->VolSessionTime;
224       bstrncpy(last_job.Job, jcr->Job, sizeof(last_job.Job));
225       last_job.JobFiles = jcr->JobFiles;
226       last_job.JobBytes = jcr->JobBytes;
227       last_job.JobStatus = jcr->JobStatus;
228       last_job.JobLevel = jcr->JobLevel;
229       last_job.start_time = jcr->start_time;
230       last_job.end_time = time(NULL);
231       break;
232    default:
233       break;
234    }
235    pthread_mutex_destroy(&jcr->mutex);
236
237    close_msg(jcr);                    /* close messages for this job */
238    delete jcr->msg_queue;
239
240    /* do this after closing messages */
241    if (jcr->client_name) {
242       free_pool_memory(jcr->client_name);
243       jcr->client_name = NULL;
244    }
245
246    if (jcr->sd_auth_key) {
247       free(jcr->sd_auth_key);
248       jcr->sd_auth_key = NULL;
249    }
250    if (jcr->VolumeName) {
251       free_pool_memory(jcr->VolumeName);
252       jcr->VolumeName = NULL;
253    }
254
255    if (jcr->dir_bsock) {
256       bnet_close(jcr->dir_bsock);
257       jcr->dir_bsock = NULL;
258    }
259    if (jcr->errmsg) {
260       free_pool_memory(jcr->errmsg);
261       jcr->errmsg = NULL;
262    }
263    if (jcr->where) {
264       free(jcr->where);
265       jcr->where = NULL;
266    }
267    if (jcr->cached_path) {
268       free_pool_memory(jcr->cached_path);
269       jcr->cached_path = NULL;
270       jcr->cached_pnl = 0;
271    }
272    free_getuser_cache();
273    free_getgroup_cache();
274    free(jcr);
275 }
276
277 /* 
278  * Global routine to free a jcr
279  */
280 #ifdef DEBUG
281 void b_free_jcr(const char *file, int line, JCR *jcr)
282 {
283    Dmsg3(200, "Enter free_jcr 0x%x from %s:%d\n", jcr, file, line);
284
285 #else
286
287 void free_jcr(JCR *jcr)
288 {
289
290    Dmsg1(200, "Enter free_jcr 0x%x\n", jcr);
291
292 #endif
293    struct s_last_job *je;
294
295    lock_jcr_chain();
296    jcr->use_count--;                  /* decrement use count */
297    Dmsg3(200, "Dec free_jcr 0x%x use_count=%d jobid=%d\n", jcr, jcr->use_count, jcr->JobId);
298    if (jcr->use_count > 0) {          /* if in use */
299       unlock_jcr_chain();
300       Dmsg2(200, "free_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
301       return;
302    }
303    remove_jcr(jcr);
304
305    Dmsg1(200, "End job=%d\n", jcr->JobId);
306    if (jcr->daemon_free_jcr) {
307       jcr->daemon_free_jcr(jcr);      /* call daemon free routine */
308    }
309
310    free_common_jcr(jcr);
311
312    /* Keep list of last jobs, but not Console where JobId==0 */
313    if (last_job.JobId > 0) {
314       je = (struct s_last_job *)malloc(sizeof(struct s_last_job));
315       memcpy((char *)je, (char *)&last_job, sizeof(last_job));
316       if (!last_jobs) {
317          init_last_jobs_list();
318       }
319       last_jobs->append(je);
320       if (last_jobs->size() > MAX_LAST_JOBS) {
321          last_jobs->remove(last_jobs->first());
322       }
323       last_job.JobId = 0;             /* zap last job */
324    }
325    close_msg(NULL);                   /* flush any daemon messages */
326    unlock_jcr_chain();
327    Dmsg0(200, "Exit free_jcr\n");
328 }
329
330
331 /* 
332  * Global routine to free a jcr
333  *  JCR chain is already locked
334  */
335 void free_locked_jcr(JCR *jcr)
336 {
337    jcr->use_count--;                  /* decrement use count */
338    Dmsg2(200, "Dec free_locked_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
339    if (jcr->use_count > 0) {          /* if in use */
340       return;
341    }
342    remove_jcr(jcr);
343    jcr->daemon_free_jcr(jcr);         /* call daemon free routine */
344    free_common_jcr(jcr);
345 }
346
347
348
349
350 /*
351  * Given a JobId, find the JCR      
352  *   Returns: jcr on success
353  *            NULL on failure
354  */
355 JCR *get_jcr_by_id(uint32_t JobId)
356 {
357    JCR *jcr;       
358
359    lock_jcr_chain();                    /* lock chain */
360    for (jcr = jobs; jcr; jcr=jcr->next) {
361       if (jcr->JobId == JobId) {
362          P(jcr->mutex);
363          jcr->use_count++;
364          V(jcr->mutex);
365          Dmsg2(200, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
366          break;
367       }
368    }
369    unlock_jcr_chain();
370    return jcr; 
371 }
372
373 /*
374  * Given a SessionId and SessionTime, find the JCR      
375  *   Returns: jcr on success
376  *            NULL on failure
377  */
378 JCR *get_jcr_by_session(uint32_t SessionId, uint32_t SessionTime)
379 {
380    JCR *jcr;       
381
382    lock_jcr_chain();
383    for (jcr = jobs; jcr; jcr=jcr->next) {
384       if (jcr->VolSessionId == SessionId && 
385           jcr->VolSessionTime == SessionTime) {
386          P(jcr->mutex);
387          jcr->use_count++;
388          V(jcr->mutex);
389          Dmsg2(200, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
390          break;
391       }
392    }
393    unlock_jcr_chain();
394    return jcr; 
395 }
396
397
398 /*
399  * Given a Job, find the JCR      
400  *  compares on the number of characters in Job
401  *  thus allowing partial matches.
402  *   Returns: jcr on success
403  *            NULL on failure
404  */
405 JCR *get_jcr_by_partial_name(char *Job)
406 {
407    JCR *jcr;       
408    int len;
409
410    if (!Job) {
411       return NULL;
412    }
413    lock_jcr_chain();
414    len = strlen(Job);
415    for (jcr = jobs; jcr; jcr=jcr->next) {
416       if (strncmp(Job, jcr->Job, len) == 0) {
417          P(jcr->mutex);
418          jcr->use_count++;
419          V(jcr->mutex);
420          Dmsg2(200, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
421          break;
422       }
423    }
424    unlock_jcr_chain();
425    return jcr; 
426 }
427
428
429 /*
430  * Given a Job, find the JCR      
431  *  requires an exact match of names.
432  *   Returns: jcr on success
433  *            NULL on failure
434  */
435 JCR *get_jcr_by_full_name(char *Job)
436 {
437    JCR *jcr;       
438
439    if (!Job) {
440       return NULL;
441    }
442    lock_jcr_chain();
443    for (jcr = jobs; jcr; jcr=jcr->next) {
444       if (strcmp(jcr->Job, Job) == 0) {
445          P(jcr->mutex);
446          jcr->use_count++;
447          V(jcr->mutex);
448          Dmsg2(200, "Inc get_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
449          break;
450       }
451    }
452    unlock_jcr_chain();
453    return jcr; 
454 }
455
456 void set_jcr_job_status(JCR *jcr, int JobStatus)
457 {
458    /*
459     * For a set of errors, ... keep the current status
460     *   so it isn't lost. For all others, set it.
461     */
462    switch (jcr->JobStatus) {
463    case JS_ErrorTerminated:
464    case JS_Error:
465    case JS_FatalError:
466    case JS_Differences:
467    case JS_Canceled:
468       break;
469    default:
470       jcr->JobStatus = JobStatus;
471    }
472 }
473
474 /* 
475  * Lock the chain
476  */
477 void lock_jcr_chain()
478 {
479    int errstat;
480    if ((errstat=rwl_writelock(&lock)) != 0) {
481       Emsg1(M_ABORT, 0, "rwl_writelock failure. ERR=%s\n",
482            strerror(errstat));
483    }
484 }
485
486 /*
487  * Unlock the chain
488  */
489 void unlock_jcr_chain()
490 {
491    int errstat;
492    if ((errstat=rwl_writeunlock(&lock)) != 0) {
493       Emsg1(M_ABORT, 0, "rwl_writeunlock failure. ERR=%s\n",
494            strerror(errstat));
495    }
496 }
497
498
499 JCR *get_next_jcr(JCR *prev_jcr)
500 {
501    JCR *jcr;
502
503    if (prev_jcr == NULL) {
504       jcr = jobs;
505    } else {
506       jcr = prev_jcr->next;
507    }
508    if (jcr) {
509       P(jcr->mutex);
510       jcr->use_count++;
511       V(jcr->mutex);
512       Dmsg2(200, "Inc get_next_jcr 0x%x use_count=%d\n", jcr, jcr->use_count);
513    }
514    return jcr;
515 }
516
517 bool init_jcr_subsystem(void)
518 {
519    watchdog_t *wd = new_watchdog();
520
521    wd->one_shot = false;
522    wd->interval = 30;   /* FIXME: should be configurable somewhere, even
523                          if only with a #define */
524    wd->callback = jcr_timeout_check;
525
526    register_watchdog(wd);
527
528    return true;
529 }
530
531 static void jcr_timeout_check(watchdog_t *self)
532 {
533    JCR *jcr;
534    BSOCK *fd;
535    time_t timer_start;
536
537    Dmsg0(400, "Start JCR timeout checks\n");
538
539    /* Walk through all JCRs checking if any one is 
540     * blocked for more than specified max time.
541     */
542    lock_jcr_chain();
543    foreach_jcr(jcr) {
544       free_locked_jcr(jcr);           /* OK to free now cuz chain is locked */
545       if (jcr->JobId == 0) {
546          continue;
547       }
548       fd = jcr->store_bsock;
549       if (fd) {
550          timer_start = fd->timer_start;
551          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
552             fd->timer_start = 0;      /* turn off timer */
553             fd->timed_out = TRUE;
554             Jmsg(jcr, M_ERROR, 0, _(
555 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
556                  watchdog_time - timer_start);
557             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
558          }
559       }
560       fd = jcr->file_bsock;
561       if (fd) {
562          timer_start = fd->timer_start;
563          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
564             fd->timer_start = 0;      /* turn off timer */
565             fd->timed_out = TRUE;
566             Jmsg(jcr, M_ERROR, 0, _(
567 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
568                  watchdog_time - timer_start);
569             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
570          }
571       }
572       fd = jcr->dir_bsock;
573       if (fd) {
574          timer_start = fd->timer_start;
575          if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
576             fd->timer_start = 0;      /* turn off timer */
577             fd->timed_out = TRUE;
578             Jmsg(jcr, M_ERROR, 0, _(
579 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
580                  watchdog_time - timer_start);
581             pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
582          }
583       }
584
585    }
586    unlock_jcr_chain();
587
588    Dmsg0(200, "Finished JCR timeout checks\n");
589 }
590
591 /*
592  * Timeout signal comes here
593  */
594 static void timeout_handler(int sig)
595 {
596    return;                            /* thus interrupting the function */
597 }