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