modules/sk/cd_watchdog.c

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

FUNCTIONS

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.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 }

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