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

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