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