]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/watchdog.c
Watchdog updates
[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
43 /* Static globals */
44 static pthread_mutex_t mutex;
45 static pthread_cond_t  timer;
46 static int quit;
47 static btimer_t *child_chain = NULL;
48
49
50 /*
51  * Timeout signal comes here
52  */
53 static 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    if ((stat = pthread_mutex_init(&mutex, NULL)) != 0) {
77       return stat;
78    }
79    if ((stat = pthread_cond_init(&timer, NULL)) != 0) {
80       pthread_mutex_destroy(&mutex);
81       return stat;
82    }
83    quit = FALSE;
84    if ((stat = pthread_create(&wdid, NULL, btimer_thread, (void *)NULL)) != 0) {
85       pthread_mutex_destroy(&mutex);
86       pthread_cond_destroy(&timer);
87       return stat;
88    }
89    return 0;
90 }
91
92 /*
93  * Terminate the watchdog thread
94  *
95  * Returns: 0 on success
96  *          errno on failure
97  */
98 int stop_watchdog(void)
99 {
100    int stat;
101
102    P(mutex);
103    quit = TRUE;
104
105    if ((stat = pthread_cond_signal(&timer)) != 0) {
106       V(mutex);
107       return stat;
108    }
109    V(mutex);
110    return 0;
111 }
112
113
114 /* 
115  * This is the actual watchdog thread.
116  */
117 static void *btimer_thread(void *arg)
118 {
119    struct timespec timeout;
120    int stat;
121    JCR *jcr;
122    BSOCK *fd;
123    btimer_t *wid;
124
125    Dmsg0(200, "Start watchdog thread\n");
126    pthread_detach(pthread_self());
127
128    P(mutex);
129    for ( ;!quit; ) {
130       struct timeval tv;
131       struct timezone tz;
132       time_t timer_start, now;
133
134       Dmsg0(200, "Top of for loop\n");
135
136       watchdog_time = time(NULL);     /* update timer */
137
138       /* Walk through all JCRs checking if any one is 
139        * blocked for more than specified max time.
140        */
141       lock_jcr_chain();
142       for (jcr=NULL; (jcr=get_next_jcr(jcr)); ) {
143          free_locked_jcr(jcr);
144          if (jcr->JobId == 0) {
145             continue;
146          }
147          fd = jcr->store_bsock;
148          if (fd) {
149             timer_start = fd->timer_start;
150             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
151                fd->timed_out = TRUE;
152                Jmsg(jcr, M_ERROR, 0, _(
153 "Watchdog sending kill after %d secs to thread stalled reading Storage daemon.\n"),
154                     watchdog_time - timer_start);
155                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
156             }
157          }
158          fd = jcr->file_bsock;
159          if (fd) {
160             timer_start = fd->timer_start;
161             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
162                fd->timed_out = TRUE;
163                Jmsg(jcr, M_ERROR, 0, _(
164 "Watchdog sending kill after %d secs to thread stalled reading File daemon.\n"),
165                     watchdog_time - timer_start);
166                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
167             }
168          }
169          fd = jcr->dir_bsock;
170          if (fd) {
171             timer_start = fd->timer_start;
172             if (timer_start && (watchdog_time - timer_start) > fd->timeout) {
173                fd->timed_out = TRUE;
174                Jmsg(jcr, M_ERROR, 0, _(
175 "Watchdog sending kill after %d secs to thread stalled reading Director.\n"),
176                     watchdog_time - timer_start);
177                pthread_kill(jcr->my_thread_id, TIMEOUT_SIGNAL);
178             }
179          }
180
181       }
182       unlock_jcr_chain();
183
184       gettimeofday(&tv, &tz);
185       timeout.tv_nsec = 0;
186       timeout.tv_sec = tv.tv_sec + SLEEP_TIME;
187
188       Dmsg1(200, "pthread_cond_timedwait sec=%d\n", timeout.tv_sec);
189       /* Note, this unlocks mutex during the sleep */
190       stat = pthread_cond_timedwait(&timer, &mutex, &timeout);
191       Dmsg1(200, "pthread_cond_timedwait stat=%d\n", stat);
192
193       now = time(NULL);
194
195       /* Walk child chain killing off any process overdue */
196       for (wid = child_chain; wid; wid=wid->next) {
197          int killed = FALSE;
198          /* First ask him politely to go away */
199          if (!wid->killed && now > (wid->start_time + wid->wait)) {
200 //          Dmsg1(000, "Watchdog sigterm pid=%d\n", wid->pid);
201             kill(wid->pid, SIGTERM);
202             killed = TRUE;
203          }
204          /* If we asked somone to die, wait 3 seconds and slam him */
205          if (killed) {
206             btimer_t *wid1;
207             sleep(3);
208             for (wid1 = child_chain; wid1; wid1=wid1->next) {
209                if (!wid1->killed && now > (wid1->start_time + wid1->wait)) {
210                   kill(wid1->pid, SIGKILL);
211 //                Dmsg1(000, "Watchdog killed pid=%d\n", wid->pid);
212                   wid1->killed = TRUE;
213                }
214             }
215          }
216       }
217       
218    } /* end of big for loop */
219
220    V(mutex);
221    Dmsg0(200, "End watchdog\n");
222    return NULL;
223 }
224
225 /* 
226  * Start a timer on a child process of pid, kill it after wait seconds.
227  *   NOTE!  Granularity is SLEEP_TIME (i.e. 30 seconds)
228  *
229  *  Returns: btimer_id (pointer to btimer_t struct) on success
230  *           NULL on failure
231  */
232 btimer_id start_child_timer(pid_t pid, uint32_t wait)
233 {
234    btimer_id wid = (btimer_id)malloc(sizeof(btimer_t));
235
236    P(mutex);
237    /* Chain it into child_chain as the first item */
238    wid->prev = NULL;
239    wid->next = child_chain;
240    if (child_chain) {
241       child_chain->prev = wid;
242    }
243    child_chain = wid;
244    wid->start_time = time(NULL);
245    wid->wait = wait;
246    wid->pid = pid;
247    wid->killed = FALSE;
248    Dmsg2(200, "Start child timer 0x%x for %d secs.\n", wid, wait);
249    V(mutex);
250    return wid;
251 }
252
253 /*
254  * Stop child timer
255  */
256 void stop_child_timer(btimer_id wid)
257 {
258    if (wid == NULL) {
259       Emsg0(M_ABORT, 0, _("NULL btimer_id.\n"));
260    }
261    P(mutex);
262    /* Remove wid from child_chain */
263    if (!wid->prev) {                  /* if no prev */
264       child_chain = wid->next;        /* set new head */
265    } else {
266       wid->prev->next = wid->next;    /* update prev */
267    }
268    if (wid->next) {
269       wid->next->prev = wid->prev;    /* unlink it */
270    }
271    V(mutex);
272    Dmsg2(200, "Stop child timer 0x%x for %d secs.\n", wid, wid->wait);
273    free(wid);
274 }