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