## 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] */