]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/watchdog.c
Massive cleanup of recycling code
[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 #define SLEEP_TIME 30                 /* examine things every 30 seconds */
37
38 /* Forward referenced functions */
39 static void *btimer_thread(void *arg);
40 static void stop_btimer(btimer_id wid);
41 static btimer_id btimer_start_common(uint32_t wait);
42
43 /* Static globals */
44 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
45 static pthread_cond_t  timer = PTHREAD_COND_INITIALIZER;
46 static int quit;
47 static btimer_t *timer_chain = NULL;
48
49
50 /*
51  * Timeout signal comes here
52  */
53 void timeout_handler(int sig)
54 {
55    return;                            /* thus interrupting the function */
56 }
57
58
59 /*   
60  * Start watchdog thread
61  *
62  *  Returns: 0 on success
63  *           errno on failure
64  */
65 int start_watchdog(void)
66 {
67    int stat;
68    pthread_t wdid;
69    struct sigaction sigtimer;
70                         
71    sigtimer.sa_flags = 0;
72    sigtimer.sa_handler = timeout_handler;
73    sigfillset(&sigtimer.sa_mask);
74    sigaction(TIMEOUT_SIGNAL, &sigtimer, NULL);
75    watchdog_time = time(NULL);
76    quit = FALSE;
77    if ((stat = pthread_create(&wdid, NULL, btimer_thread, (void *)NULL)) != 0) {
78       return stat;
79    }
80    return 0;
81 }
82
83 /*
84  * Terminate the watchdog thread
85  *
86  * Returns: 0 on success
87  *          errno on failure
88  */
89 int stop_watchdog(void)
90 {
91    int stat;
92
93    quit = TRUE;
94    P(mutex);
95    if ((stat = pthread_cond_signal(&timer)) != 0) {
96       V(mutex);
97       return stat;
98    }
99    V(mutex);
100    return 0;
101 }
102
103
104 /* 
105  * This is the actual watchdog thread.
106  */
107 static void *btimer_thread(void *arg)
108 {
109    JCR *jcr;
110    BSOCK *fd;
111    btimer_t *wid;
112
113    Dmsg0(200, "Start watchdog thread\n");
114    pthread_detach(pthread_self());
115
116    for ( ;!quit; ) {
117       time_t timer_start, now;
118
119       Dmsg0(200, "Top of watchdog loop\n");
120
121       watchdog_time = time(NULL);     /* update timer */
122
123       /* Walk through all JCRs checking if any one is 
124        * blocked for more than specified max time.
125        */
126       lock_jcr_chain();
127       for (jcr=NULL; (jcr=get_next_jcr(jcr)); ) {
128          free_locked_jcr(jcr);
129          if (jcr->JobId == 0) {
130             continue;
131          }
132          fd = jcr->store_bsock;
133          if (fd) {
134             timer_start = fd->timer_start;
135             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
136                fd->timed_out = TRUE;
137                Jmsg(jcr, M_ERROR, 0, _(
138 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
139                     watchdog_time - timer_start);
140                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
141             }
142          }
143          fd = jcr->file_bsock;
144          if (fd) {
145             timer_start = fd->timer_start;
146             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
147                fd->timed_out = TRUE;
148                Jmsg(jcr, M_ERROR, 0, _(
149 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
150                     watchdog_time - timer_start);
151                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
152             }
153          }
154          fd = jcr->dir_bsock;
155          if (fd) {
156             timer_start = fd->timer_start;
157             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
158                fd->timed_out = TRUE;
159                Jmsg(jcr, M_ERROR, 0, _(
160 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
161                     watchdog_time - timer_start);
162                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
163             }
164          }
165
166       }
167       unlock_jcr_chain();
168
169       Dmsg0(200, "Watchdog sleep.\n");
170       bmicrosleep(SLEEP_TIME, 0);
171       now = time(NULL);
172
173       /* 
174        * Now handle child and thread timers set by the code.
175        */
176       /* Walk child chain killing off any process overdue */
177       P(mutex);
178       for (wid = timer_chain; wid; wid=wid->next) {
179          int killed = FALSE;
180          /* First ask him politely to go away */
181          if (!wid->killed && now > (wid->start_time + wid->wait)) {
182 //          Dmsg1(000, "Watchdog sigterm pid=%d\n", wid->pid);
183             if (wid->type == TYPE_CHILD) {
184                kill(wid->pid, SIGTERM);
185                killed = TRUE;
186             } else {
187                Dmsg1(200, "watchdog kill thread %d\n", wid->tid);
188                pthread_kill(wid->tid, TIMEOUT_SIGNAL);
189                wid->killed = TRUE;
190             }
191          }
192          /* If we asked a child to die, wait 3 seconds and slam him */
193          if (killed) {
194             btimer_t *wid1;
195             bmicrosleep(3, 0);
196             for (wid1 = timer_chain; wid1; wid1=wid1->next) {
197                if (wid->type == TYPE_CHILD &&
198                    !wid1->killed && now > (wid1->start_time + wid1->wait)) {
199                   kill(wid1->pid, SIGKILL);
200 //                Dmsg1(000, "Watchdog killed pid=%d\n", wid->pid);
201                   wid1->killed = TRUE;
202                }
203             }
204          }
205       }
206       V(mutex);
207    } /* end of big for loop */
208
209    Dmsg0(200, "End watchdog\n");
210    return NULL;
211 }
212
213 /* 
214  * Start a timer on a child process of pid, kill it after wait seconds.
215  *   NOTE!  Granularity is SLEEP_TIME (i.e. 30 seconds)
216  *
217  *  Returns: btimer_id (pointer to btimer_t struct) on success
218  *           NULL on failure
219  */
220 btimer_id start_child_timer(pid_t pid, uint32_t wait)
221 {
222    btimer_t *wid;
223    wid = btimer_start_common(wait);
224    wid->pid = pid;
225    wid->type = TYPE_CHILD;
226    Dmsg2(200, "Start child timer 0x%x for %d secs.\n", wid, wait);
227    return wid;
228 }
229
230 /* 
231  * Start a timer on a thread. kill it after wait seconds.
232  *   NOTE!  Granularity is SLEEP_TIME (i.e. 30 seconds)
233  *
234  *  Returns: btimer_id (pointer to btimer_t struct) on success
235  *           NULL on failure
236  */
237 btimer_id start_thread_timer(pthread_t tid, uint32_t wait)
238 {
239    btimer_t *wid;
240    wid = btimer_start_common(wait);
241    wid->tid = tid;
242    wid->type = TYPE_PTHREAD;
243    Dmsg2(200, "Start thread timer 0x%x for %d secs.\n", wid, wait);
244    return wid;
245 }
246
247 static btimer_id btimer_start_common(uint32_t wait)
248 {
249    btimer_id wid = (btimer_id)malloc(sizeof(btimer_t));
250
251    P(mutex);
252    /* Chain it into timer_chain as the first item */
253    wid->prev = NULL;
254    wid->next = timer_chain;
255    if (timer_chain) {
256       timer_chain->prev = wid;
257    }
258    timer_chain = wid;
259    wid->start_time = time(NULL);
260    wid->wait = wait;
261    wid->killed = FALSE;
262    V(mutex);
263    return wid;
264 }
265
266 /*
267  * Stop child timer
268  */
269 void stop_child_timer(btimer_id wid)
270 {
271    Dmsg2(200, "Stop child timer 0x%x for %d secs.\n", wid, wid->wait);
272    stop_btimer(wid);         
273 }
274
275 /*
276  * Stop thread timer
277  */
278 void stop_thread_timer(btimer_id wid)
279 {
280    if (!wid) {
281       return;
282    }
283    Dmsg2(200, "Stop thread timer 0x%x for %d secs.\n", wid, wid->wait);
284    stop_btimer(wid);         
285 }
286
287
288 /*
289  * Stop btimer
290  */
291 static void stop_btimer(btimer_id wid)
292 {
293    if (wid == NULL) {
294       Emsg0(M_ABORT, 0, _("NULL btimer_id.\n"));
295    }
296    P(mutex);
297    /* Remove wid from timer_chain */
298    if (!wid->prev) {                  /* if no prev */
299       timer_chain = wid->next;        /* set new head */
300    } else {
301       wid->prev->next = wid->next;    /* update prev */
302    }
303    if (wid->next) {
304       wid->next->prev = wid->prev;    /* unlink it */
305    }
306    V(mutex);
307    free(wid);
308 }