]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/watchdog.c
add conio.c + fix bsendmsg seg fault some pm_strcpy() cleanups
[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->timer_start = 0;   /* turn off timer */
137                fd->timed_out = TRUE;
138                Jmsg(jcr, M_ERROR, 0, _(
139 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
140                     watchdog_time - timer_start);
141                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
142             }
143          }
144          fd = jcr->file_bsock;
145          if (fd) {
146             timer_start = fd->timer_start;
147             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
148                fd->timer_start = 0;   /* turn off timer */
149                fd->timed_out = TRUE;
150                Jmsg(jcr, M_ERROR, 0, _(
151 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
152                     watchdog_time - timer_start);
153                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
154             }
155          }
156          fd = jcr->dir_bsock;
157          if (fd) {
158             timer_start = fd->timer_start;
159             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
160                fd->timer_start = 0;   /* turn off timer */
161                fd->timed_out = TRUE;
162                Jmsg(jcr, M_ERROR, 0, _(
163 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
164                     watchdog_time - timer_start);
165                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
166             }
167          }
168
169       }
170       unlock_jcr_chain();
171
172       Dmsg0(200, "Watchdog sleep.\n");
173       bmicrosleep(SLEEP_TIME, 0);
174       now = time(NULL);
175
176       /* 
177        * Now handle child and thread timers set by the code.
178        */
179       /* Walk child chain killing off any process overdue */
180       P(mutex);
181       for (wid = timer_chain; wid; wid=wid->next) {
182          int killed = FALSE;
183          /* First ask him politely to go away */
184          if (!wid->killed && now > (wid->start_time + wid->wait)) {
185 //          Dmsg1(000, "Watchdog sigterm pid=%d\n", wid->pid);
186             if (wid->type == TYPE_CHILD) {
187                kill(wid->pid, SIGTERM);
188                killed = TRUE;
189             } else {
190                Dmsg1(200, "watchdog kill thread %d\n", wid->tid);
191                pthread_kill(wid->tid, TIMEOUT_SIGNAL);
192                wid->killed = TRUE;
193             }
194          }
195          /* If we asked a child to die, wait 3 seconds and slam him */
196          if (killed) {
197             btimer_t *wid1;
198             bmicrosleep(3, 0);
199             for (wid1 = timer_chain; wid1; wid1=wid1->next) {
200                if (wid->type == TYPE_CHILD &&
201                    !wid1->killed && now > (wid1->start_time + wid1->wait)) {
202                   kill(wid1->pid, SIGKILL);
203 //                Dmsg1(000, "Watchdog killed pid=%d\n", wid->pid);
204                   wid1->killed = TRUE;
205                }
206             }
207          }
208       }
209       V(mutex);
210    } /* end of big for loop */
211
212    Dmsg0(200, "End watchdog\n");
213    return NULL;
214 }
215
216 /* 
217  * Start a timer on a child process of pid, kill it after wait seconds.
218  *   NOTE!  Granularity is SLEEP_TIME (i.e. 30 seconds)
219  *
220  *  Returns: btimer_id (pointer to btimer_t struct) on success
221  *           NULL on failure
222  */
223 btimer_id start_child_timer(pid_t pid, uint32_t wait)
224 {
225    btimer_t *wid;
226    wid = btimer_start_common(wait);
227    wid->pid = pid;
228    wid->type = TYPE_CHILD;
229    Dmsg2(200, "Start child timer 0x%x for %d secs.\n", wid, wait);
230    return wid;
231 }
232
233 /* 
234  * Start a timer on a thread. 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_thread_timer(pthread_t tid, uint32_t wait)
241 {
242    btimer_t *wid;
243    wid = btimer_start_common(wait);
244    wid->tid = tid;
245    wid->type = TYPE_PTHREAD;
246    Dmsg2(200, "Start thread timer 0x%x for %d secs.\n", wid, wait);
247    return wid;
248 }
249
250 static btimer_id btimer_start_common(uint32_t wait)
251 {
252    btimer_id wid = (btimer_id)malloc(sizeof(btimer_t));
253
254    P(mutex);
255    /* Chain it into timer_chain as the first item */
256    wid->prev = NULL;
257    wid->next = timer_chain;
258    if (timer_chain) {
259       timer_chain->prev = wid;
260    }
261    timer_chain = wid;
262    wid->start_time = time(NULL);
263    wid->wait = wait;
264    wid->killed = FALSE;
265    V(mutex);
266    return wid;
267 }
268
269 /*
270  * Stop child timer
271  */
272 void stop_child_timer(btimer_id wid)
273 {
274    Dmsg2(200, "Stop child timer 0x%x for %d secs.\n", wid, wid->wait);
275    stop_btimer(wid);         
276 }
277
278 /*
279  * Stop thread timer
280  */
281 void stop_thread_timer(btimer_id wid)
282 {
283    if (!wid) {
284       return;
285    }
286    Dmsg2(200, "Stop thread timer 0x%x for %d secs.\n", wid, wid->wait);
287    stop_btimer(wid);         
288 }
289
290
291 /*
292  * Stop btimer
293  */
294 static void stop_btimer(btimer_id wid)
295 {
296    if (wid == NULL) {
297       Emsg0(M_ABORT, 0, _("NULL btimer_id.\n"));
298    }
299    P(mutex);
300    /* Remove wid from timer_chain */
301    if (!wid->prev) {                  /* if no prev */
302       timer_chain = wid->next;        /* set new head */
303    } else {
304       wid->prev->next = wid->next;    /* update prev */
305    }
306    if (wid->next) {
307       wid->next->prev = wid->prev;    /* unlink it */
308    }
309    V(mutex);
310    free(wid);
311 }