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.8 $
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,2002 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 /*
324 Linux doesn't seem to like getting signals in select(), which isn't
325 technically allowed by POSIX. The workaround in this case is simply
326 to not create a watchdog for Linux. This is probably acceptable
327 because we will be changing the query path to perform queries in small
328 chunks, so if a disconnect occurs it won't consume a lot of database
329 resources in any case, even without a watchdog.
330 */
331 #ifndef __linux__
332 /* NOT DETACHED! */
333 pthread_create(&condat->watchdog, &attr, sk_watchdog, (void *) condat );
334 #endif /* __linux__ */
335
336 dieif(pthread_attr_destroy(&attr) != 0);
337
338 return SK_OK;
339 }
340
341
342 /*++++++++++++++++++++++++++++++++++++++
343
344 stops running sk_watchdog thread.
345 If it is not running ( == not registered in the connection struct),
346 it does nothing.
347
348 er_ret_t SK_watchstop always succeeds (returns SK_OK)
349
350 sk_conn_st *condat pointer to the connection data structure
351 ++++++++++++++++++++++++++++++++++++++*/
352 er_ret_t
353 SK_watchstop(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
354 {
355 void *res;
356
357 if(condat->watchdog > 0) {
358 int ret;
359
360 /* wait until the watchdog is ready for signal */
361 pthread_mutex_lock( & condat->watchmutex );
362
363 #ifdef WATCHDOG_BY_SIGNAL
364 ret = pthread_kill(condat->watchdog, SIGUSR2);
365 #else
366 ret = pthread_cancel(condat->watchdog);
367 #endif
368
369 ret = pthread_join(condat->watchdog, &res);
370
371 pthread_mutex_destroy( & condat->watchmutex );
372 condat->watchdog = 0;
373 }
374 return SK_OK;
375 }
376
377
378 /*++++++++++++++++++++++++++++++++++++++
379
380 void SK_watch_setkill sets the thread id of the thread to be
381 cancelled by the watchdog watching this socket.
382 0 (default) means do not cancel anything.
383
384 sk_conn_st *condat pointer to the connection data structure.
385
386 pthread_t killthis thread id of the thread to be cancelled, or 0.
387 ++++++++++++++++++++++++++++++++++++++*/
388 void
389 SK_watch_setkill(sk_conn_st *condat, pthread_t killthis)
/* [<][>][^][v][top][bottom][index][help] */
390 {
391 condat->killthis = killthis;
392 }
393
394
395 /*++++++++++++++++++++++++++++++++++++++
396
397 void SK_watch_setexec sets the function to be invoked by the watchdog
398 watching this socket. NULL (default) means do
399 not invoke anything.
400
401 sk_conn_st *condat pointer to the connection data structure.
402
403 void *(*function)(void *) function to be invoked
404
405 void *args argument to be passed to the function.
406
407 ++++++++++++++++++++++++++++++++++++++*/
408 void
409 SK_watch_setexec( sk_conn_st *condat, void *(*function)(void *) , void *args)
/* [<][>][^][v][top][bottom][index][help] */
410 {
411 condat->execthis = function;
412 condat->execargs = args;
413 }
414
415
416 /*++++++++++++++++++++++++++++++++++++++
417
418 void SK_watch_setclear clears the function and thread id fields so that
419 nothing gets cancelled or invoked by the
420 watchdog.
421
422 sk_conn_st *condat pointer to the connection data structure.
423
424 ++++++++++++++++++++++++++++++++++++++*/
425 void
426 SK_watch_setclear(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
427 {
428 condat->execthis = NULL;
429 condat->execargs = NULL;
430 condat->killthis = 0;
431 }
432
433 /* call the function to be called if defined */
434
435
436 /*++++++++++++++++++++++++++++++++++++++
437
438 void SK_watchexec invokes the predefined function if defined.
439 (usually called from the watchdog).
440 Also sets the reason-to-close
441 flag on this connection to SK_INTERRUPT.
442
443 sk_conn_st *condat pointer to the connection data structure.
444
445 ++++++++++++++++++++++++++++++++++++++*/
446 void
447 SK_watchexec(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
448 {
449 /* set the reason-to-close flag on this connection */
450 condat->rtc |= SK_INTERRUPT;
451
452 if( condat->execthis != NULL ) {
453 condat->execthis(condat->execargs);
454 }
455 }
456
457 /* cancel the thread to be cancelled if defined */
458
459
460 /*++++++++++++++++++++++++++++++++++++++
461
462 void SK_watchkill cancels the predefined thread if defined.
463 (usually called from the watchdog).
464 Also sets the reason-to-close
465 flag on this connection to SK_INTERRUPT.
466
467 sk_conn_st *condat pointer to the connection data structure.
468
469 ++++++++++++++++++++++++++++++++++++++*/
470 void
471 SK_watchkill(sk_conn_st *condat) {
/* [<][>][^][v][top][bottom][index][help] */
472
473 /* set the reason-to-close flag on this connection */
474 condat->rtc |= SK_INTERRUPT;
475
476 /* cancel thread if defined */
477 if( condat->killthis != 0 ) {
478 pthread_cancel(condat->killthis);
479 /* The only possible error is ESRCH, so we do not care about it*/
480 }
481 }
482
483
484 /*++++++++++++++++++++++++++++++++++++++
485
486 void SK_watchtrigger Wrapper around SK_watchkill and SK_watchexec.
487 First executes the function, then cancels the
488 thread.
489
490 sk_conn_st *condat pointer to the connection data structure.
491
492 ++++++++++++++++++++++++++++++++++++++*/
493 void SK_watchtrigger(sk_conn_st *condat)
/* [<][>][^][v][top][bottom][index][help] */
494 {
495 SK_watchexec(condat);
496 SK_watchkill(condat);
497 }
498
499
500 /*++++++++++++++++++++++++++++++++++++++
501 Initialisation function, should be called exactly once
502 (well, it ignores repeated calls). The actions depend on cancellation
503 mode (signal or pthread_cancel).
504 ++++++++++++++++++++++++++++++++++++++*/
505 void SK_init(void)
/* [<][>][^][v][top][bottom][index][help] */
506 {
507 /* can be called only once */
508 pthread_once( &sk_init_once, sk_real_init);
509 }