]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/watchdog.c
Home page change. Tweak to Win32 Backup API
[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-2003 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    quit = TRUE;
105    P(mutex);
106    if ((stat = pthread_cond_signal(&timer)) != 0) {
107       V(mutex);
108       return stat;
109    }
110    V(mutex);
111    return 0;
112 }
113
114
115 /* 
116  * This is the actual watchdog thread.
117  */
118 static void *btimer_thread(void *arg)
119 {
120    struct timespec timeout;
121    int stat;
122    JCR *jcr;
123    BSOCK *fd;
124    btimer_t *wid;
125
126    Dmsg0(200, "Start watchdog thread\n");
127    pthread_detach(pthread_self());
128
129    P(mutex);
130    for ( ;!quit; ) {
131       struct timeval tv;
132       struct timezone tz;
133       time_t timer_start, now;
134
135       Dmsg0(200, "Top of for loop\n");
136
137       watchdog_time = time(NULL);     /* update timer */
138
139       /* Walk through all JCRs checking if any one is 
140        * blocked for more than specified max time.
141        */
142       lock_jcr_chain();
143       for (jcr=NULL; (jcr=get_next_jcr(jcr)); ) {
144          free_locked_jcr(jcr);
145          if (jcr->JobId == 0) {
146             continue;
147          }
148          fd = jcr->store_bsock;
149          if (fd) {
150             timer_start = fd->timer_start;
151             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
152                fd->timed_out = TRUE;
153                Jmsg(jcr, M_ERROR, 0, _(
154 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
155                     watchdog_time - timer_start);
156                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
157             }
158          }
159          fd = jcr->file_bsock;
160          if (fd) {
161             timer_start = fd->timer_start;
162             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
163                fd->timed_out = TRUE;
164                Jmsg(jcr, M_ERROR, 0, _(
165 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
166                     watchdog_time - timer_start);
167                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
168             }
169          }
170          fd = jcr->dir_bsock;
171          if (fd) {
172             timer_start = fd->timer_start;
173             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
174                fd->timed_out = TRUE;
175                Jmsg(jcr, M_ERROR, 0, _(
176 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
177                     watchdog_time - timer_start);
178                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
179             }
180          }
181
182       }
183       unlock_jcr_chain();
184
185       gettimeofday(&tv, &tz);
186       timeout.tv_nsec = 0;
187       timeout.tv_sec = tv.tv_sec + SLEEP_TIME;
188
189       Dmsg1(200, "pthread_cond_timedwait sec=%d\n", timeout.tv_sec);
190       /* Note, this unlocks mutex during the sleep */
191       stat = pthread_cond_timedwait(&timer, &mutex, &timeout);
192       Dmsg1(200, "pthread_cond_timedwait stat=%d\n", stat);
193
194       now = time(NULL);
195
196       /* Walk child chain killing off any process overdue */
197       for (wid = timer_chain; wid; wid=wid->next) {
198          int killed = FALSE;
199          /* First ask him politely to go away */
200          if (!wid->killed && now > (wid->start_time + wid->wait)) {
201 //          Dmsg1(000, "Watchdog sigterm pid=%d\n", wid->pid);
202             if (wid->type == TYPE_CHILD) {
203                kill(wid->pid, SIGTERM);
204                killed = TRUE;
205             } else {
206                Dmsg1(200, "watchdog kill thread %d\n", wid->tid);
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    Dmsg2(200, "Start child timer 0x%x for %d secs.\n", wid, wait);
247    return wid;
248 }
249
250 /* 
251  * Start a timer on a thread. kill it after wait seconds.
252  *   NOTE!  Granularity is SLEEP_TIME (i.e. 30 seconds)
253  *
254  *  Returns: btimer_id (pointer to btimer_t struct) on success
255  *           NULL on failure
256  */
257 btimer_id start_thread_timer(pthread_t tid, uint32_t wait)
258 {
259    btimer_t *wid;
260    wid = btimer_start_common(wait);
261    wid->tid = tid;
262    wid->type = TYPE_PTHREAD;
263    Dmsg2(200, "Start thread timer 0x%x for %d secs.\n", wid, wait);
264    return wid;
265 }
266
267 static btimer_id btimer_start_common(uint32_t wait)
268 {
269    btimer_id wid = (btimer_id)malloc(sizeof(btimer_t));
270
271    P(mutex);
272    /* Chain it into timer_chain as the first item */
273    wid->prev = NULL;
274    wid->next = timer_chain;
275    if (timer_chain) {
276       timer_chain->prev = wid;
277    }
278    timer_chain = wid;
279    wid->start_time = time(NULL);
280    wid->wait = wait;
281    wid->killed = FALSE;
282    V(mutex);
283    return wid;
284 }
285
286 /*
287  * Stop child timer
288  */
289 void stop_child_timer(btimer_id wid)
290 {
291    Dmsg2(200, "Stop child timer 0x%x for %d secs.\n", wid, wid->wait);
292    stop_btimer(wid);         
293 }
294
295 /*
296  * Stop thread timer
297  */
298 void stop_thread_timer(btimer_id wid)
299 {
300    if (!wid) {
301       return;
302    }
303    Dmsg2(200, "Stop thread timer 0x%x for %d secs.\n", wid, wid->wait);
304    stop_btimer(wid);         
305 }
306
307
308 /*
309  * Stop btimer
310  */
311 static void stop_btimer(btimer_id wid)
312 {
313    if (wid == NULL) {
314       Emsg0(M_ABORT, 0, _("NULL btimer_id.\n"));
315    }
316    P(mutex);
317    /* Remove wid from timer_chain */
318    if (!wid->prev) {                  /* if no prev */
319       timer_chain = wid->next;        /* set new head */
320    } else {
321       wid->prev->next = wid->next;    /* update prev */
322    }
323    if (wid->next) {
324       wid->next->prev = wid->prev;    /* unlink it */
325    }
326    V(mutex);
327    free(wid);
328 }