modules/sk/cd_watchdog.c
/* [<][>][^][v][top][bottom][index][help] */
FUNCTIONS
This source file includes following functions.
- sk_real_init
- func_sigusr
- sk_watchdog
- sk_watchdog
- sk_real_init
- SK_watchstart
- SK_watchstop
- SK_watch_setkill
- SK_watch_setexec
- SK_watch_setclear
- SK_watchexec
- SK_watchkill
- SK_watchtrigger
- SK_init
1 /***************************************
2 $Revision: 1.4 $
3
4 Socket module - cd_watchdog.c - Socket watchdog - when activated, checks the
5 socket for new data and discards it. If the
6 socket is closed, it triggers predefined
7 functions - executes a function and/or
8 cancels a thread.
9
10 Status: NOT REVUED, TESTED
11
12 Design and implementation by Marek Bukowy.
13
14 Modification history:
15 marek (August 2000) Created the watchdog part
16 marek (December 2000) Modified watchdog deactivation -
17 replaced signals by pthread cancellation.
18 ******************/ /******************
19 Copyright (c) 1999, 2000 RIPE NCC
20
21 All Rights Reserved
22
23 Permission to use, copy, modify, and distribute this software and its
24 documentation for any purpose and without fee is hereby granted,
25 provided that the above copyright notice appear in all copies and that
26 both that copyright notice and this permission notice appear in
27 supporting documentation, and that the name of the author not be
28 used in advertising or publicity pertaining to distribution of the
29 software without specific, written prior permission.
30
31 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
32 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
33 AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
34 DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
35 AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
36 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
37 ***************************************/
38
39 #include "sk.h"
40 /*+ String sizes +*/
41 #define STR_S 63
42
43 /*+ Uncomment this to use watchdog deactivation by signal (may be risky)
44
45 #define WATCHDOG_BY_SIGNAL
46 +*/
47
48 static pthread_once_t sk_init_once = { PTHREAD_ONCE_INIT };
49
50 #ifdef WATCHDOG_BY_SIGNAL
51
52 /*+ The signal version is complicated to cope with all timing situations.
53 It uses a thread specific flag to see if the signal handler was invoked
54 in case the signal arrives before select(3) is called in watchdog.
55 +*/
56
57 /* thread specific flag */
58 static pthread_key_t sk_watch_tsd;
59
60 /*++++++++++++++++++++++++++++++++++++++
61 initialisation for the SIGNAL cancellation mode
62 - initialises the thread specific flag.
63 ++++++++++++++++++++++++++++++++++++++*/
64 static void sk_real_init(void)
/* [<][>][^][v][top][bottom][index][help] */
65 {
66 dieif( pthread_key_create( &sk_watch_tsd, NULL) != 0 );
67 }
68
69
70 /*++++++++++++++++++++++++++++++++++++++
71 sk_watchdog signal handler - sets the thread-specific flag.
72
73 int n signal received. (not used)
74 ++++++++++++++++++++++++++++++++++++++*/
75 static void func_sigusr(int n) {
/* [<][>][^][v][top][bottom][index][help] */
76 #if 0
77 /* just for debugging - we don't check the value here */
78 int *tsd_flag = (int *) pthread_getspecific(sk_watch_tsd);
79 #endif
80
81 /* 2000/12/18 MB:
82 DEADLOCK has happened - the watchdog was just getting a mutex
83 for the ER rwlock when a signal arrived and the execution of the
84 pthread_mutex_lock function was interrupted AFTER the lock was
85 grabbed. The this handler was invoked and tried to get that mutex
86 again. As a result, everything stopped.
87
88 Cures:
89 1. Not invoke this here:
90 ER_dbg_va(FAC_SK, ASP_SK_GEN,"func_sigusr(%d) called", n);
91
92 2. Not accept any signals during any pthread calls so that this
93 does not happen again. Must be reimplemented with pthread_cancel
94 and all the signal stuff must go away. (Done, 2000/12/19).
95 */
96 /* set a thread-specific flag that the handler was invoked */
97
98 pthread_setspecific(sk_watch_tsd, (void *)1 );
99 }
100
101 /*++++++++++++++++++++++++++++++++++++++
102 watchdog (SIGNAL VERSION) - started as a separate thread.
103
104 Selects on the given socket; discards all input.
105 whenever it sees end of file (socket closed), it
106 * sets a corresponding flag in the condat structure,
107 * triggers the predefined actions (by SK_watchtrigger).
108
109 void *arg - pointer to the connection data structure
110 ++++++++++++++++++++++++++++++++++++++*/
111 static
112 void *sk_watchdog(void *arg)
/* [<][>][^][v][top][bottom][index][help] */
113 {
114 sk_conn_st *condat = (sk_conn_st *) arg;
115 int nready;
116 int n;
117 fd_set rset;
118 char buff[STR_S];
119 int socket = condat->sock;
120 sigset_t sset;
121 struct sigaction act;
122
123 struct timeval timeout = { 1, 0 }; /* it's a timeout of 1 second */
124
125 FD_ZERO(&rset);
126 FD_SET(socket, &rset);
127
128 sigemptyset(&sset);
129 sigaddset(&sset, SIGUSR2);
130
131 act.sa_handler = func_sigusr;
132 act.sa_flags = 0;
133 dieif(sigaction(SIGUSR2, &act, NULL) != 0);
134
135 /* XXX in fact, it's unblocked already. Should be blocked on startup */
136 dieif(pthread_sigmask(SIG_UNBLOCK, &sset, NULL) != 0);
137
138 /* clear the handler's flag */
139 pthread_setspecific(sk_watch_tsd, NULL);
140
141 /* now ready for signal */
142 pthread_mutex_unlock( & condat->watchmutex );
143
144 /* hey, viva threaded signal handling! There is no way for select
145 to unblock a blocked signal, It must be done by "hand" (above).
146
147 Consequently, every once in a while, the signal will be delivered
148 before the select starts :-/. So, we have to introduce a timeout
149 for select and check if the signal was delivered anyway....aARGH!!!
150
151 This adds a <timeout interval> to unlucky queries, about 0.1% of all.
152 */
153
154 while ((nready=select(socket+1, &rset, NULL, NULL, &timeout))!=-1) {
155
156 ER_dbg_va(FAC_SK, ASP_SK_WATCH,"select returned %d", nready);
157
158 /* don't even try to read if we have been killed */
159 if( errno == EINTR || pthread_getspecific(sk_watch_tsd) != NULL ) {
160 break;
161 }
162
163 /* retry if the timeout has triggered */
164 if( nready == 0 ) {
165 continue;
166 }
167
168 /* There was some input or client half of connection was closed */
169 /* Check for the latter */
170 if (( n=read(socket, buff, sizeof(buff))) == 0) {
171 /* Connection was closed by client */
172 /* Now send a cancellation request to the whois thread. */
173 /* mysql thread will be terminated by thread cleanup routine */
174
175 /* call the actions: kill and exec (the SK_ functions called
176 check if the action is defined. Will set the RTC flag on condat
177 */
178 SK_watchtrigger(condat);
179
180 /* quit */
181 break;
182 }
183 /* Otherwise dump input and continue */
184
185 }
186
187 /* Exit the watchdog thread, passing NULL as we don't expect a join */
188 pthread_exit(NULL);
189
190 /* oh yes. Shouldn't compilers _recognize_ library functions ? */
191 return NULL;
192 }
193
194
195 #else /* not WATCHDOG_BY_SIGNAL */
196
197
198 /*++++++++++++++++++++++++++++++++++++++
199 watchdog (CANCEL VERSION) - started as a separate thread.
200
201 Selects on the given socket; discards all input.
202 whenever it sees end of file (socket closed), it
203 * sets a corresponding flag in the condat structure,
204 * triggers the predefined actions (by SK_watchtrigger).
205
206 void *arg - pointer to the connection data structure
207 ++++++++++++++++++++++++++++++++++++++*/
208 static
209 void *sk_watchdog(void *arg)
/* [<][>][^][v][top][bottom][index][help] */
210 {
211 sk_conn_st *condat = (sk_conn_st *) arg;
212 int nready;
213 int n;
214 char buff[STR_S];
215 int socket = condat->sock;
216 struct timeval timeout = { 1, 0 }; /* it's a timeout of 1 second */
217 fd_set rset;
218
219 /* this is to allow cancellation of the select(3) call */
220 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
221
222 /* now ready for the cancellation */
223 pthread_mutex_unlock( & condat->watchmutex );
224
225 FD_ZERO(&rset);
226 FD_SET(socket, &rset);
227 do {
228 /* run the select exposed to cancellation */
229 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
230 nready=select(socket+1, &rset, NULL, NULL, &timeout);
231 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
232
233 ER_dbg_va(FAC_SK, ASP_SK_WATCH,"select returned %d", nready);
234 /* quit on error */
235 if( nready < 0 ) {
236 break;
237 }
238
239 /* retry if the timeout has triggered */
240 if( nready == 0 ) {
241 continue;
242 }
243
244 /* There was some input or client half of connection was closed */
245 /* Check for the latter */
246 if (( n=read(socket, buff, sizeof(buff))) == 0) {
247 /* Connection was closed by client */
248 /* Now send a cancellation request to the whois thread. */
249 /* mysql thread will be terminated by thread cleanup routine */
250
251 /* call the actions: kill and exec (the SK_ functions called
252 check if the action is defined. Will set the RTC flag on condat
253 */
254 SK_watchtrigger(condat);
255
256 /* quit */
257 break;
258 }
259 /* Otherwise dump input and continue */
260
261 } while(nready != -1);
262
263 return NULL; /* quit */
264 }
265
266
267 /*++++++++++++++++++++++++++++++++++++++
268 initialisation for the PTHREAD_CANCEL mode is not needed.
269 ++++++++++++++++++++++++++++++++++++++*/
270 static void sk_real_init(void) {
/* [<][>][^][v][top][bottom][index][help] */
271 /* EMPTY */
272 }
273
274 #endif /* WATCHDOG_BY_SIGNAL */
275
276
277 /*++++++++++++++++++++++++++++++++++++++
278 starts sk_watchdog thread unless already started,
279 and registers its threadid in the condat structure
280
281 dies if watchdog already running
282
283 er_ret_t SK_watchstart Returns SK_OK on success.
284
285 sk_conn_st *condat pointer to the connection data structure
286
287 The structure may (and normally, should) contain the predefined actions
288 set by SK_watch_set... functions.
289 ++++++++++++++++++++++++++++++++++++++*/
290 er_ret_t
291 SK_watchstart(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
292 {
293 pthread_attr_t attr;
294 size_t ssize;
295 int ret;
296
297 dieif( condat->watchdog != 0 );
298
299 dieif(pthread_attr_init(&attr) != 0);
300
301 #if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \
302 defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE)
303 /*********
304 For SCO, we need to increase the stack size, because the default is
305 exceedingly small. This also works on FreeBSD. In Solaris, the
306 stack size is 0, which is interpreted as the default, meaning 1
307 Mbyte for 32-bit processes or 2 Mbyte for 64-bit processes.
308 However, trying to *set* the stack size to 0 results in an error.
309 Therefore, we don't want to set the size to 0. Probably not a good
310 idea in any event. :) Linux doesn't support this function (as of
311 the 2.4.2 kernel).
312
313 Note: see also modules/th/thread.c
314 *********/
315 dieif(pthread_attr_getstacksize(&attr, &ssize) != 0);
316 if (ssize > 0) {
317 dieif(pthread_attr_setstacksize(&attr, ssize * 4) != 0);
318 }
319 #endif
320
321 /* init the mutex in locked state, watchdog will unlock it when
322 it's ready for signal/cancellation */
323 pthread_mutex_init( & condat->watchmutex, NULL );
324 pthread_mutex_lock( & condat->watchmutex );
325
326 /* NOT DETACHED! */
327 pthread_create(&condat->watchdog, &attr, sk_watchdog, (void *) condat );
328
329 dieif(pthread_attr_destroy(&attr) != 0);
330
331 return SK_OK;
332 }
333
334
335 /*++++++++++++++++++++++++++++++++++++++
336
337 stops running sk_watchdog thread.
338 If it is not running ( == not registered in the connection struct),
339 it does nothing.
340
341 er_ret_t SK_watchstop always succeeds (returns SK_OK)
342
343 sk_conn_st *condat pointer to the connection data structure
344 ++++++++++++++++++++++++++++++++++++++*/
345 er_ret_t
346 SK_watchstop(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
347 {
348 void *res;
349
350 if(condat->watchdog > 0) {
351 int ret;
352
353 /* wait until the watchdog is ready for signal */
354 pthread_mutex_lock( & condat->watchmutex );
355
356 #ifdef WATCHDOG_BY_SIGNAL
357 ret = pthread_kill(condat->watchdog, SIGUSR2);
358 #else
359 ret = pthread_cancel(condat->watchdog);
360 #endif
361
362 ret = pthread_join(condat->watchdog, &res);
363
364 pthread_mutex_destroy( & condat->watchmutex );
365 condat->watchdog = 0;
366 }
367 return SK_OK;
368 }
369
370
371 /*++++++++++++++++++++++++++++++++++++++
372
373 void SK_watch_setkill sets the thread id of the thread to be
374 cancelled by the watchdog watching this socket.
375 0 (default) means do not cancel anything.
376
377 sk_conn_st *condat pointer to the connection data structure.
378
379 pthread_t killthis thread id of the thread to be cancelled, or 0.
380 ++++++++++++++++++++++++++++++++++++++*/
381 void
382 SK_watch_setkill(sk_conn_st *condat, pthread_t killthis)
/* [<][>][^][v][top][bottom][index][help] */
383 {
384 condat->killthis = killthis;
385 }
386
387
388 /*++++++++++++++++++++++++++++++++++++++
389
390 void SK_watch_setexec sets the function to be invoked by the watchdog
391 watching this socket. NULL (default) means do
392 not invoke anything.
393
394 sk_conn_st *condat pointer to the connection data structure.
395
396 void *(*function)(void *) function to be invoked
397
398 void *args argument to be passed to the function.
399
400 ++++++++++++++++++++++++++++++++++++++*/
401 void
402 SK_watch_setexec( sk_conn_st *condat, void *(*function)(void *) , void *args)
/* [<][>][^][v][top][bottom][index][help] */
403 {
404 condat->execthis = function;
405 condat->execargs = args;
406 }
407
408
409 /*++++++++++++++++++++++++++++++++++++++
410
411 void SK_watch_setclear clears the function and thread id fields so that
412 nothing gets cancelled or invoked by the
413 watchdog.
414
415 sk_conn_st *condat pointer to the connection data structure.
416
417 ++++++++++++++++++++++++++++++++++++++*/
418 void
419 SK_watch_setclear(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
420 {
421 condat->execthis = NULL;
422 condat->execargs = NULL;
423 condat->killthis = 0;
424 }
425
426 /* call the function to be called if defined */
427
428
429 /*++++++++++++++++++++++++++++++++++++++
430
431 void SK_watchexec invokes the predefined function if defined.
432 (usually called from the watchdog).
433 Also sets the reason-to-close
434 flag on this connection to SK_INTERRUPT.
435
436 sk_conn_st *condat pointer to the connection data structure.
437
438 ++++++++++++++++++++++++++++++++++++++*/
439 void
440 SK_watchexec(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
441 {
442 /* set the reason-to-close flag on this connection */
443 condat->rtc |= SK_INTERRUPT;
444
445 if( condat->execthis != NULL ) {
446 condat->execthis(condat->execargs);
447 }
448 }
449
450 /* cancel the thread to be cancelled if defined */
451
452
453 /*++++++++++++++++++++++++++++++++++++++
454
455 void SK_watchkill cancels the predefined thread if defined.
456 (usually called from the watchdog).
457 Also sets the reason-to-close
458 flag on this connection to SK_INTERRUPT.
459
460 sk_conn_st *condat pointer to the connection data structure.
461
462 ++++++++++++++++++++++++++++++++++++++*/
463 void
464 SK_watchkill(sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
465
466 /* set the reason-to-close flag on this connection */
467 condat->rtc |= SK_INTERRUPT;
468
469 /* cancel thread if defined */
470 if( condat->killthis != 0 ) {
471 pthread_cancel(condat->killthis);
472 /* The only possible error is ESRCH, so we do not care about it*/
473 }
474 }
475
476
477 /*++++++++++++++++++++++++++++++++++++++
478
479 void SK_watchtrigger Wrapper around SK_watchkill and SK_watchexec.
480 First executes the function, then cancels the
481 thread.
482
483 sk_conn_st *condat pointer to the connection data structure.
484
485 ++++++++++++++++++++++++++++++++++++++*/
486 void SK_watchtrigger(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
487 {
488 SK_watchexec(condat);
489 SK_watchkill(condat);
490 }
491
492
493 /*++++++++++++++++++++++++++++++++++++++
494 Initialisation function, should be called exactly once
495 (well, it ignores repeated calls). The actions depend on cancellation
496 mode (signal or pthread_cancel).
497 ++++++++++++++++++++++++++++++++++++++*/
498 void SK_init(void)
/* [<][>][^][v][top][bottom][index][help] */
499 {
500 /* can be called only once */
501 pthread_once( &sk_init_once, sk_real_init);
502 }