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