]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/watchdog.c
Mod bacula-dir.conf; fix VolUseDuration bug
[bacula/bacula] / bacula / src / lib / watchdog.c
1 /*
2  * Bacula thread watchdog routine. General routine that monitors
3  *  the daemon and signals a thread if it is blocked on a BSOCK
4  *  too long. This prevents catastropic long waits -- generally
5  *  due to Windows "hanging" the app.
6  *
7  *  Kern Sibbald, January MMII
8  *
9  */
10 /*
11    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation; either version 2 of
16    the License, or (at your option) any later version.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU General Public
24    License along with this program; if not, write to the Free
25    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26    MA 02111-1307, USA.
27
28  */
29
30 #include "bacula.h"
31 #include "jcr.h"
32
33 /* Exported globals */
34 time_t watchdog_time;                 /* this has granularity of SLEEP_TIME */
35
36
37 #define TIMEOUT_SIGNAL SIGUSR2
38 #define SLEEP_TIME 30                 /* examine things every 30 seconds */
39
40 /* Forward referenced functions */
41 static void *btimer_thread(void *arg);
42 static void stop_btimer(btimer_id wid);
43 static btimer_id btimer_start_common(uint32_t wait);
44
45 /* Static globals */
46 static pthread_mutex_t mutex;
47 static pthread_cond_t  timer;
48 static int quit;
49 static btimer_t *timer_chain = NULL;
50
51
52 /*
53  * Timeout signal comes here
54  */
55 static void timeout_handler(int sig)
56 {
57    return;                            /* thus interrupting the function */
58 }
59
60
61 /*   
62  * Start watchdog thread
63  *
64  *  Returns: 0 on success
65  *           errno on failure
66  */
67 int start_watchdog(void)
68 {
69    int stat;
70    pthread_t wdid;
71    struct sigaction sigtimer;
72                         
73    sigtimer.sa_flags = 0;
74    sigtimer.sa_handler = timeout_handler;
75    sigfillset(&sigtimer.sa_mask);
76    sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL);
77    watchdog_time = time(NULL);
78    if ((stat = pthread_mutex_init(&mutex, NULL)) != 0) {
79       return stat;
80    }
81    if ((stat = pthread_cond_init(&timer, NULL)) != 0) {
82       pthread_mutex_destroy(&mutex);
83       return stat;
84    }
85    quit = FALSE;
86    if ((stat = pthread_create(&wdid, NULL, btimer_thread, (void *)NULL)) != 0) {
87       pthread_mutex_destroy(&mutex);
88       pthread_cond_destroy(&timer);
89       return stat;
90    }
91    return 0;
92 }
93
94 /*
95  * Terminate the watchdog thread
96  *
97  * Returns: 0 on success
98  *          errno on failure
99  */
100 int stop_watchdog(void)
101 {
102    int stat;
103
104    P(mutex);
105    quit = TRUE;
106
107    if ((stat = pthread_cond_signal(&timer)) != 0) {
108       V(mutex);
109       return stat;
110    }
111    V(mutex);
112    return 0;
113 }
114
115
116 /* 
117  * This is the actual watchdog thread.
118  */
119 static void *btimer_thread(void *arg)
120 {
121    struct timespec timeout;
122    int stat;
123    JCR *jcr;
124    BSOCK *fd;
125    btimer_t *wid;
126
127    Dmsg0(200, "Start watchdog thread\n");
128    pthread_detach(pthread_self());
129
130    P(mutex);
131    for ( ;!quit; ) {
132       struct timeval tv;
133       struct timezone tz;
134       time_t timer_start, now;
135
136       Dmsg0(200, "Top of for loop\n");
137
138       watchdog_time = time(NULL);     /* update timer */
139
140       /* Walk through all JCRs checking if any one is 
141        * blocked for more than specified max time.
142        */
143       lock_jcr_chain();
144       for (jcr=NULL; (jcr=get_next_jcr(jcr)); ) {
145          free_locked_jcr(jcr);
146          if (jcr->JobId == 0) {
147             continue;
148          }
149          fd = jcr->store_bsock;
150          if (fd) {
151             timer_start = fd->timer_start;
152             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
153                fd->timed_out = TRUE;
154                Jmsg(jcr, M_ERROR, 0, _(
155 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
156                     watchdog_time - timer_start);
157                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
158             }
159          }
160          fd = jcr->file_bsock;
161          if (fd) {
162             timer_start = fd->timer_start;
163             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
164                fd->timed_out = TRUE;
165                Jmsg(jcr, M_ERROR, 0, _(
166 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
167                     watchdog_time - timer_start);
168                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
169             }
170          }
171          fd = jcr->dir_bsock;
172          if (fd) {
173             timer_start = fd->timer_start;
174             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
175                fd->timed_out = TRUE;
176                Jmsg(jcr, M_ERROR, 0, _(
177 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
178                     watchdog_time - timer_start);
179                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
180             }
181          }
182
183       }
184       unlock_jcr_chain();
185
186       gettimeofday(&tv, &tz);
187       timeout.tv_nsec = 0;
188       timeout.tv_sec = tv.tv_sec + SLEEP_TIME;
189
190       Dmsg1(200, "pthread_cond_timedwait sec=%d\n", timeout.tv_sec);
191       /* Note, this unlocks mutex during the sleep */
192       stat = pthread_cond_timedwait(&timer, &mutex, &timeout);
193       Dmsg1(200, "pthread_cond_timedwait stat=%d\n", stat);
194
195       now = time(NULL);
196
197       /* Walk child chain killing off any process overdue */
198       for (wid = timer_chain; wid; wid=wid->next) {
199          int killed = FALSE;
200          /* First ask him politely to go away */
201          if (!wid->killed && now > (wid->start_time + wid->wait)) {
202 //          Dmsg1(000, "Watchdog sigterm pid=%d\n", wid->pid);
203             if (wid->type == TYPE_CHILD) {
204                kill(wid->pid, SIGTERM);
205                killed = TRUE;
206             } else {
207                pthread_kill(wid->tid, TIMEOUT_SIGNAL);
208                wid->killed = TRUE;
209             }
210          }
211          /* If we asked a child to die, wait 3 seconds and slam him */
212          if (killed) {
213             btimer_t *wid1;
214             sleep(3);
215             for (wid1 = timer_chain; wid1; wid1=wid1->next) {
216                if (wid->type == TYPE_CHILD &&
217                    !wid1->killed && now > (wid1->start_time + wid1->wait)) {
218                   kill(wid1->pid, SIGKILL);
219 //                Dmsg1(000, "Watchdog killed pid=%d\n", wid->pid);
220                   wid1->killed = TRUE;
221                }
222             }
223          }
224       }
225       
226    } /* end of big for loop */
227
228    V(mutex);
229    Dmsg0(200, "End watchdog\n");
230    return NULL;
231 }
232
233 /* 
234  * Start a timer on a child process of pid, kill it after wait seconds.
235  *   NOTE!  Granularity is SLEEP_TIME (i.e. 30 seconds)
236  *
237  *  Returns: btimer_id (pointer to btimer_t struct) on success
238  *           NULL on failure
239  */
240 btimer_id start_child_timer(pid_t pid, uint32_t wait)
241 {
242    btimer_t *wid;
243    wid = btimer_start_common(wait);
244    wid->pid = pid;
245    wid->type = TYPE_CHILD;
246    return wid;
247 }
248
249 /* 
250  * Start a timer on a thread. kill it after wait seconds.
251  *   NOTE!  Granularity is SLEEP_TIME (i.e. 30 seconds)
252  *
253  *  Returns: btimer_id (pointer to btimer_t struct) on success
254  *           NULL on failure
255  */
256 btimer_id start_thread_timer(pthread_t tid, uint32_t wait)
257 {
258    btimer_t *wid;
259    wid = btimer_start_common(wait);
260    wid->tid = tid;
261    wid->type = TYPE_PTHREAD;
262    return wid;
263 }
264
265 static btimer_id btimer_start_common(uint32_t wait)
266 {
267    btimer_id wid = (btimer_id)malloc(sizeof(btimer_t));
268
269    P(mutex);
270    /* Chain it into timer_chain as the first item */
271    wid->prev = NULL;
272    wid->next = timer_chain;
273    if (timer_chain) {
274       timer_chain->prev = wid;
275    }
276    timer_chain = wid;
277    wid->start_time = time(NULL);
278    wid->wait = wait;
279    wid->killed = FALSE;
280    Dmsg2(200, "Start child timer 0x%x for %d secs.\n", wid, wait);
281    V(mutex);
282    return wid;
283 }
284
285 /*
286  * Stop child timer
287  */
288 void stop_child_timer(btimer_id wid)
289 {
290    stop_btimer(wid);         
291 }
292
293 /*
294  * Stop thread timer
295  */
296 void stop_thread_timer(btimer_id wid)
297 {
298    stop_btimer(wid);         
299 }
300
301
302 /*
303  * Stop btimer
304  */
305 static void stop_btimer(btimer_id wid)
306 {
307    if (wid == NULL) {
308       Emsg0(M_ABORT, 0, _("NULL btimer_id.\n"));
309    }
310    P(mutex);
311    /* Remove wid from timer_chain */
312    if (!wid->prev) {                  /* if no prev */
313       timer_chain = wid->next;        /* set new head */
314    } else {
315       wid->prev->next = wid->next;    /* update prev */
316    }
317    if (wid->next) {
318       wid->next->prev = wid->prev;    /* unlink it */
319    }
320    V(mutex);
321    Dmsg2(200, "Stop timer 0x%x for %d secs.\n", wid, wid->wait);
322    free(wid);
323 }