modules/sk/cd_watchdog.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following functions.
  1. sk_real_init
  2. func_sigusr
  3. sk_watchdog
  4. sk_watchdog
  5. sk_real_init
  6. SK_watchstart
  7. SK_watchstop
  8. SK_watch_setkill
  9. SK_watch_setexec
  10. SK_watch_setclear
  11. SK_watchexec
  12. SK_watchkill
  13. SK_watchtrigger
  14. 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 }

/* [<][>][^][v][top][bottom][index][help] */