root/Zend/zend_signal.c

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

DEFINITIONS

This source file includes following definitions.
  1. zend_signal_handler_defer
  2. zend_signal_handler_unblock
  3. zend_signal_handler
  4. zend_sigaction
  5. zend_signal
  6. zend_signal_register
  7. zend_signal_activate
  8. zend_signal_deactivate
  9. zend_signal_globals_ctor
  10. zend_signal_init
  11. zend_signal_startup

   1 /*
   2   +----------------------------------------------------------------------+
   3   | Zend Signal Handling                                                 |
   4   +----------------------------------------------------------------------+
   5   | Copyright (c) 2008 The PHP Group                                     |
   6   +----------------------------------------------------------------------+
   7   | This source file is subject to version 3.01 of the PHP license,      |
   8   | that is bundled with this package in the file LICENSE, and is        |
   9   | available through the world-wide-web at the following url:           |
  10   | http://www.php.net/license/3_01.txt                                  |
  11   | If you did not receive a copy of the PHP license and are unable to   |
  12   | obtain it through the world-wide-web, please send a note to          |
  13   | license@php.net so we can mail you a copy immediately.               |
  14   +----------------------------------------------------------------------+
  15   | Authors: Lucas Nealan <lucas@php.net>                                |
  16   |          Arnaud Le Blanc <lbarnaud@php.net>                          |
  17   +----------------------------------------------------------------------+
  18 
  19    This software was contributed to PHP by Facebook Inc. in 2008.
  20 
  21    Future revisions and derivatives of this source code must acknowledge
  22    Facebook Inc. as the original contributor of this module by leaving
  23    this note intact in the source code.
  24 
  25    All other licensing and usage conditions are those of the PHP Group.
  26 */
  27 
  28  /* $Id$ */
  29 
  30 #define _GNU_SOURCE
  31 #include <string.h>
  32 
  33 #include "zend.h"
  34 #include "zend_globals.h"
  35 
  36 #ifdef HAVE_SIGNAL_H
  37 #include <signal.h>
  38 #endif
  39 
  40 #ifdef HAVE_UNISTD_H
  41 #include <unistd.h>
  42 #endif
  43 
  44 #ifdef ZEND_SIGNALS
  45 
  46 #include "zend_signal.h"
  47 
  48 #ifdef ZTS
  49 ZEND_API int zend_signal_globals_id;
  50 #else
  51 zend_signal_globals_t zend_signal_globals;
  52 #endif
  53 
  54 static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context);
  55 static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*));
  56 
  57 #ifdef __CYGWIN__
  58 #define TIMEOUT_SIG SIGALRM
  59 #else
  60 #define TIMEOUT_SIG SIGPROF
  61 #endif
  62 
  63 static int zend_sigs[] = { TIMEOUT_SIG, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2 };
  64 
  65 #define SA_FLAGS_MASK ~(SA_NODEFER | SA_RESETHAND)
  66 
  67 /* True globals, written only at process startup */
  68 static zend_signal_entry_t global_orig_handlers[NSIG];
  69 static sigset_t            global_sigmask;
  70 
  71 /* {{{ zend_signal_handler_defer
  72  *  Blocks signals if in critical section */
  73 void zend_signal_handler_defer(int signo, siginfo_t *siginfo, void *context)
  74 {
  75         int errno_save = errno;
  76         zend_signal_queue_t *queue, *qtmp;
  77 
  78 #ifdef ZTS
  79         ZEND_TSRMLS_CACHE_UPDATE();
  80 #endif
  81 
  82         if (EXPECTED(SIGG(active))) {
  83                 if (UNEXPECTED(SIGG(depth) == 0)) { /* try to handle signal */
  84                         if (UNEXPECTED(SIGG(blocked))) {
  85                                 SIGG(blocked) = 0;
  86                         }
  87                         if (EXPECTED(SIGG(running) == 0)) {
  88                                 SIGG(running) = 1;
  89                                 zend_signal_handler(signo, siginfo, context);
  90 
  91                                 queue = SIGG(phead);
  92                                 SIGG(phead) = NULL;
  93 
  94                                 while (queue) {
  95                                         zend_signal_handler(queue->zend_signal.signo, queue->zend_signal.siginfo, queue->zend_signal.context);
  96                                         qtmp = queue->next;
  97                                         queue->next = SIGG(pavail);
  98                                         queue->zend_signal.signo = 0;
  99                                         SIGG(pavail) = queue;
 100                                         queue = qtmp;
 101                                 }
 102                                 SIGG(running) = 0;
 103                         }
 104                 } else { /* delay signal handling */
 105                         SIGG(blocked) = 1; /* signal is blocked */
 106 
 107                         if ((queue = SIGG(pavail))) { /* if none available it's simply forgotton */
 108                                 SIGG(pavail) = queue->next;
 109                                 queue->zend_signal.signo = signo;
 110                                 queue->zend_signal.siginfo = siginfo;
 111                                 queue->zend_signal.context = context;
 112                                 queue->next = NULL;
 113 
 114                                 if (SIGG(phead) && SIGG(ptail)) {
 115                                         SIGG(ptail)->next = queue;
 116                                 } else {
 117                                         SIGG(phead) = queue;
 118                                 }
 119                                 SIGG(ptail) = queue;
 120                         }
 121 #if ZEND_DEBUG
 122                         else { /* this may not be safe to do, but could work and be useful */
 123                                 zend_output_debug_string(0, "zend_signal: not enough queue storage, lost signal (%d)", signo);
 124                         }
 125 #endif
 126                 }
 127         } else {
 128                 /* need to just run handler if we're inactive and getting a signal */
 129                 zend_signal_handler(signo, siginfo, context);
 130         }
 131 
 132         errno = errno_save;
 133 } /* }}} */
 134 
 135 /* {{{ zend_signal_handler_unblock
 136  * Handle deferred signal from HANDLE_UNBLOCK_ALARMS */
 137 ZEND_API void zend_signal_handler_unblock(void)
 138 {
 139         zend_signal_queue_t *queue;
 140         zend_signal_t zend_signal;
 141 
 142         if (EXPECTED(SIGG(active))) {
 143                 SIGNAL_BEGIN_CRITICAL(); /* procmask to protect handler_defer as if it were called by the kernel */
 144                 queue = SIGG(phead);
 145                 SIGG(phead) = queue->next;
 146                 zend_signal = queue->zend_signal;
 147                 queue->next = SIGG(pavail);
 148                 queue->zend_signal.signo = 0;
 149                 SIGG(pavail) = queue;
 150 
 151                 zend_signal_handler_defer(zend_signal.signo, zend_signal.siginfo, zend_signal.context);
 152                 SIGNAL_END_CRITICAL();
 153         }
 154 }
 155 /* }}} */
 156 
 157 /* {{{ zend_signal_handler
 158  *  Call the previously registered handler for a signal
 159  */
 160 static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context)
 161 {
 162         int errno_save = errno;
 163         struct sigaction sa = {{0}};
 164         sigset_t sigset;
 165         zend_signal_entry_t p_sig = SIGG(handlers)[signo-1];
 166 
 167         if (p_sig.handler == SIG_DFL) { /* raise default handler */
 168                 if (sigaction(signo, NULL, &sa) == 0) {
 169                         sa.sa_handler = SIG_DFL;
 170                         sigemptyset(&sa.sa_mask);
 171 
 172                         sigemptyset(&sigset);
 173                         sigaddset(&sigset, signo);
 174 
 175                         if (sigaction(signo, &sa, NULL) == 0) {
 176                                 /* throw away any blocked signals */
 177                                 sigprocmask(SIG_UNBLOCK, &sigset, NULL);
 178                                 raise(signo);
 179                         }
 180                 }
 181         } else if (p_sig.handler != SIG_IGN) { /* ignore SIG_IGN */
 182                 if (p_sig.flags & SA_SIGINFO) {
 183                         if (p_sig.flags & SA_RESETHAND) {
 184                                 SIGG(handlers)[signo-1].flags   = 0;
 185                                 SIGG(handlers)[signo-1].handler = SIG_DFL;
 186                         }
 187                         (*(void (*)(int, siginfo_t*, void*))p_sig.handler)(signo, siginfo, context);
 188                 } else {
 189                         (*(void (*)(int))p_sig.handler)(signo);
 190                 }
 191         }
 192 
 193         errno = errno_save;
 194 } /* }}} */
 195 
 196 /* {{{ zend_sigaction
 197  *  Register a signal handler that will be deferred in critical sections */
 198 ZEND_API int zend_sigaction(int signo, const struct sigaction *act, struct sigaction *oldact)
 199 {
 200         struct sigaction sa = {{0}};
 201         sigset_t sigset;
 202 
 203         if (oldact != NULL) {
 204                 oldact->sa_flags   = SIGG(handlers)[signo-1].flags;
 205                 oldact->sa_handler = (void *) SIGG(handlers)[signo-1].handler;
 206                 oldact->sa_mask    = global_sigmask;
 207         }
 208         if (act != NULL) {
 209                 SIGG(handlers)[signo-1].flags = act->sa_flags;
 210                 if (act->sa_flags & SA_SIGINFO) {
 211                         SIGG(handlers)[signo-1].handler = (void *) act->sa_sigaction;
 212                 } else {
 213                         SIGG(handlers)[signo-1].handler = (void *) act->sa_handler;
 214                 }
 215 
 216                 sa.sa_flags     = SA_SIGINFO | (act->sa_flags & SA_FLAGS_MASK);
 217                 sa.sa_sigaction = zend_signal_handler_defer;
 218                 sa.sa_mask      = global_sigmask;
 219 
 220                 if (sigaction(signo, &sa, NULL) < 0) {
 221                         zend_error_noreturn(E_ERROR, "Error installing signal handler for %d", signo);
 222                 }
 223 
 224                 /* unsure this signal is not blocked */
 225                 sigemptyset(&sigset);
 226                 sigaddset(&sigset, signo);
 227                 zend_sigprocmask(SIG_UNBLOCK, &sigset, NULL);
 228         }
 229 
 230         return SUCCESS;
 231 }
 232 /* }}} */
 233 
 234 /* {{{ zend_signal
 235  *  Register a signal handler that will be deferred in critical sections */
 236 ZEND_API int zend_signal(int signo, void (*handler)(int))
 237 {
 238         struct sigaction sa = {{0}};
 239 
 240         sa.sa_flags   = 0;
 241         sa.sa_handler = handler;
 242         sa.sa_mask    = global_sigmask;
 243 
 244         return zend_sigaction(signo, &sa, NULL);
 245 }
 246 /* }}} */
 247 
 248 /* {{{ zend_signal_register
 249  *  Set a handler for a signal we want to defer.
 250  *  Previously set handler must have been saved before.
 251  */
 252 static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*))
 253 {
 254         struct sigaction sa = {{0}};
 255 
 256         if (sigaction(signo, NULL, &sa) == 0) {
 257                 if ((sa.sa_flags & SA_SIGINFO) && sa.sa_sigaction == handler) {
 258                         return FAILURE;
 259                 }
 260 
 261                 SIGG(handlers)[signo-1].flags = sa.sa_flags;
 262                 if (sa.sa_flags & SA_SIGINFO) {
 263                         SIGG(handlers)[signo-1].handler = (void *)sa.sa_sigaction;
 264                 } else {
 265                         SIGG(handlers)[signo-1].handler = (void *)sa.sa_handler;
 266                 }
 267 
 268                 sa.sa_flags     = SA_SIGINFO; /* we'll use a siginfo handler */
 269                 sa.sa_sigaction = handler;
 270                 sa.sa_mask      = global_sigmask;
 271 
 272                 if (sigaction(signo, &sa, NULL) < 0) {
 273                         zend_error_noreturn(E_ERROR, "Error installing signal handler for %d", signo);
 274                 }
 275 
 276                 return SUCCESS;
 277         }
 278         return FAILURE;
 279 } /* }}} */
 280 
 281 /* {{{ zend_signal_activate
 282  *  Install our signal handlers, per request */
 283 void zend_signal_activate(void)
 284 {
 285         int x;
 286 
 287         memcpy(&SIGG(handlers), &global_orig_handlers, sizeof(global_orig_handlers));
 288 
 289         for (x = 0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) {
 290                 zend_signal_register(zend_sigs[x], zend_signal_handler_defer);
 291         }
 292 
 293         SIGG(active) = 1;
 294         SIGG(depth)  = 0;
 295 } /* }}} */
 296 
 297 /* {{{ zend_signal_deactivate
 298  * */
 299 void zend_signal_deactivate(void)
 300 {
 301 
 302         if (SIGG(check)) {
 303                 int x;
 304                 struct sigaction sa = {{0}};
 305                 if (SIGG(depth) != 0) {
 306                         zend_error(E_CORE_WARNING, "zend_signal: shutdown with non-zero blocking depth (%d)", SIGG(depth));
 307                 }
 308                 /* did anyone steal our installed handler */
 309                 for (x = 0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) {
 310                         sigaction(zend_sigs[x], NULL, &sa);
 311                         if (sa.sa_sigaction != zend_signal_handler_defer) {
 312                                 zend_error(E_CORE_WARNING, "zend_signal: handler was replaced for signal (%d) after startup", zend_sigs[x]);
 313                         }
 314                 }
 315         }
 316 
 317         SIGNAL_BEGIN_CRITICAL();
 318         SIGG(active) = 0;
 319         SIGG(running) = 0;
 320         SIGG(blocked) = 0;
 321         SIGG(depth) = 0;
 322         SIGNAL_END_CRITICAL();
 323 }
 324 /* }}} */
 325 
 326 static void zend_signal_globals_ctor(zend_signal_globals_t *zend_signal_globals) /* {{{ */
 327 {
 328         size_t x;
 329 
 330         memset(zend_signal_globals, 0, sizeof(*zend_signal_globals));
 331 
 332         for (x = 0; x < sizeof(zend_signal_globals->pstorage) / sizeof(*zend_signal_globals->pstorage); ++x) {
 333                 zend_signal_queue_t *queue = &zend_signal_globals->pstorage[x];
 334                 queue->zend_signal.signo = 0;
 335                 queue->next = zend_signal_globals->pavail;
 336                 zend_signal_globals->pavail = queue;
 337         }
 338 }
 339 /* }}} */
 340 
 341 void zend_signal_init() /* {{{ */
 342 {
 343         int signo;
 344         struct sigaction sa = {{0}};
 345 
 346         /* Save previously registered signal handlers into orig_handlers */
 347         memset(&global_orig_handlers, 0, sizeof(global_orig_handlers));
 348         for (signo = 1; signo < NSIG; ++signo) {
 349                 if (sigaction(signo, NULL, &sa) == 0) {
 350                         global_orig_handlers[signo-1].flags = sa.sa_flags;
 351                         if (sa.sa_flags & SA_SIGINFO) {
 352                                 global_orig_handlers[signo-1].handler = (void *) sa.sa_sigaction;
 353                         } else {
 354                                 global_orig_handlers[signo-1].handler = (void *) sa.sa_handler;
 355                         }
 356                 }
 357         }
 358 }
 359 /* }}} */
 360 
 361 /* {{{ zend_signal_startup
 362  * alloc zend signal globals */
 363 void zend_signal_startup()
 364 {
 365 
 366 #ifdef ZTS
 367         ts_allocate_id(&zend_signal_globals_id, sizeof(zend_signal_globals_t), (ts_allocate_ctor) zend_signal_globals_ctor, NULL);
 368 #else
 369         zend_signal_globals_ctor(&zend_signal_globals);
 370 #endif
 371 
 372         /* Used to block signals during execution of signal handlers */
 373         sigfillset(&global_sigmask);
 374         sigdelset(&global_sigmask, SIGILL);
 375         sigdelset(&global_sigmask, SIGABRT);
 376         sigdelset(&global_sigmask, SIGFPE);
 377         sigdelset(&global_sigmask, SIGKILL);
 378         sigdelset(&global_sigmask, SIGSEGV);
 379         sigdelset(&global_sigmask, SIGCONT);
 380         sigdelset(&global_sigmask, SIGSTOP);
 381         sigdelset(&global_sigmask, SIGTSTP);
 382         sigdelset(&global_sigmask, SIGTTIN);
 383         sigdelset(&global_sigmask, SIGTTOU);
 384 #ifdef SIGBUS
 385         sigdelset(&global_sigmask, SIGBUS);
 386 #endif
 387 #ifdef SIGSYS
 388         sigdelset(&global_sigmask, SIGSYS);
 389 #endif
 390 #ifdef SIGTRAP
 391         sigdelset(&global_sigmask, SIGTRAP);
 392 #endif
 393 
 394         zend_signal_init();
 395 }
 396 /* }}} */
 397 
 398 
 399 #endif /* ZEND_SIGNALS */
 400 
 401 /*
 402  * Local variables:
 403  * tab-width: 4
 404  * c-basic-offset: 4
 405  * indent-tabs-mode: t
 406  * End:
 407  * vim600: fdm=marker
 408  * vim: noet sw=4 ts=4
 409  */

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