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

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