diff options
Diffstat (limited to 'Zend/zend_signal.c')
-rw-r--r-- | Zend/zend_signal.c | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/Zend/zend_signal.c b/Zend/zend_signal.c new file mode 100644 index 0000000000..8fe8ebc515 --- /dev/null +++ b/Zend/zend_signal.c @@ -0,0 +1,414 @@ +/* + +----------------------------------------------------------------------+ + | Zend Signal Handling | + +----------------------------------------------------------------------+ + | Copyright (c) 2008 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Lucas Nealan <lucas@php.net> | + | Arnaud Le Blanc <lbarnaud@php.net> | + +----------------------------------------------------------------------+ + + This software was contributed to PHP by Facebook Inc. in 2008. + + Future revisions and derivatives of this source code must acknowledge + Facebook Inc. as the original contributor of this module by leaving + this note intact in the source code. + + All other licensing and usage conditions are those of the PHP Group. +*/ + + /* $Id$ */ + +#define _GNU_SOURCE +#include <string.h> + +#include "zend.h" +#include "zend_globals.h" + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef ZEND_SIGNALS + +#include "zend_signal.h" + +#ifdef ZTS +ZEND_API int zend_signal_globals_id; +#else +zend_signal_globals_t zend_signal_globals; +#endif + +static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context TSRMLS_DC); +static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*) TSRMLS_DC); + +#ifdef __CYGWIN__ +#define TIMEOUT_SIG SIGALRM +#else +#define TIMEOUT_SIG SIGPROF +#endif + +static int zend_sigs[] = { TIMEOUT_SIG, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2 }; + +#define SA_FLAGS_MASK ~(SA_NODEFER | SA_RESETHAND) + +/* True globals, written only at process startup */ +static zend_signal_entry_t global_orig_handlers[NSIG]; +static sigset_t global_sigmask; + +/* {{{ zend_signal_handler_defer + * Blocks signals if in critical section */ +void zend_signal_handler_defer(int signo, siginfo_t *siginfo, void *context) +{ + int errno_save = errno; + zend_signal_queue_t *queue, *qtmp; + TSRMLS_FETCH(); + + if (SIGG(active)) { + if (SIGG(depth) == 0) { /* try to handle signal */ + if (SIGG(blocked) != -1) { /* inverse */ + SIGG(blocked) = -1; /* signal is not blocked */ + } + if (SIGG(running) == 0) { + SIGG(running) = 1; + zend_signal_handler(signo, siginfo, context TSRMLS_CC); + + queue = SIGG(phead); + SIGG(phead) = NULL; + + while (queue) { + zend_signal_handler(queue->zend_signal.signo, queue->zend_signal.siginfo, queue->zend_signal.context TSRMLS_CC); + qtmp = queue->next; + queue->next = SIGG(pavail); + queue->zend_signal.signo = 0; + SIGG(pavail) = queue; + queue = qtmp; + } + SIGG(running) = 0; + } + } else { /* delay signal handling */ + SIGG(blocked) = 0; /* signal is blocked */ + + if ((queue = SIGG(pavail))) { /* if none available it's simply forgotton */ + SIGG(pavail) = queue->next; + queue->zend_signal.signo = signo; + queue->zend_signal.siginfo = siginfo; + queue->zend_signal.context = context; + queue->next = NULL; + + if (SIGG(phead) && SIGG(ptail)) { + SIGG(ptail)->next = queue; + } else { + SIGG(phead) = queue; + } + SIGG(ptail) = queue; + } +#if ZEND_DEBUG + else { /* this may not be safe to do, but could work and be useful */ + zend_output_debug_string(0, "zend_signal: not enough queue storage, lost signal (%d)", signo); + } +#endif + } + } else { + /* need to just run handler if we're inactive and getting a signal */ + zend_signal_handler(signo, siginfo, context TSRMLS_CC); + } + + errno = errno_save; +} /* }}} */ + +/* {{{ zend_signal_handler_unblock + * Handle deferred signal from HANDLE_UNBLOCK_ALARMS */ +void zend_signal_handler_unblock(TSRMLS_D) +{ + zend_signal_queue_t *queue; + zend_signal_t zend_signal; + + if (SIGG(active)) { + SIGNAL_BEGIN_CRITICAL(); /* procmask to protect handler_defer as if it were called by the kernel */ + queue = SIGG(phead); + SIGG(phead) = queue->next; + zend_signal = queue->zend_signal; + queue->next = SIGG(pavail); + queue->zend_signal.signo = 0; + SIGG(pavail) = queue; + + zend_signal_handler_defer(zend_signal.signo, zend_signal.siginfo, zend_signal.context); + SIGNAL_END_CRITICAL(); + } +} +/* }}} */ + +/* {{{ zend_signal_handler + * Call the previously registered handler for a signal + */ +static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context TSRMLS_DC) +{ + int errno_save = errno; + struct sigaction sa = {{0}}; + sigset_t sigset; + zend_signal_entry_t p_sig = SIGG(handlers)[signo-1]; + + if (p_sig.handler == SIG_DFL) { /* raise default handler */ + if (sigaction(signo, NULL, &sa) == 0) { + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + + sigemptyset(&sigset); + sigaddset(&sigset, signo); + + if (sigaction(signo, &sa, NULL) == 0) { + /* throw away any blocked signals */ + sigprocmask(SIG_UNBLOCK, &sigset, NULL); + raise(signo); + } + } + } else if (p_sig.handler != SIG_IGN) { /* ignore SIG_IGN */ + if (p_sig.flags & SA_SIGINFO) { + if (p_sig.flags & SA_RESETHAND) { + SIGG(handlers)[signo-1].flags = 0; + SIGG(handlers)[signo-1].handler = SIG_DFL; + } + (*(void (*)(int, siginfo_t*, void*))p_sig.handler)(signo, siginfo, context); + } else { + (*(void (*)(int))p_sig.handler)(signo); + } + } + + errno = errno_save; +} /* }}} */ + +/* {{{ zend_sigaction + * Register a signal handler that will be deferred in critical sections */ +ZEND_API int zend_sigaction(int signo, const struct sigaction *act, struct sigaction *oldact TSRMLS_DC) +{ + struct sigaction sa = {{0}}; + sigset_t sigset; + + if (oldact != NULL) { + oldact->sa_flags = SIGG(handlers)[signo-1].flags; + oldact->sa_handler = (void *) SIGG(handlers)[signo-1].handler; + oldact->sa_mask = global_sigmask; + } + if (act != NULL) { + SIGG(handlers)[signo-1].flags = act->sa_flags; + if (act->sa_flags & SA_SIGINFO) { + SIGG(handlers)[signo-1].handler = (void *) act->sa_sigaction; + } else { + SIGG(handlers)[signo-1].handler = (void *) act->sa_handler; + } + + sa.sa_flags = SA_SIGINFO | (act->sa_flags & SA_FLAGS_MASK); + sa.sa_sigaction = zend_signal_handler_defer; + sa.sa_mask = global_sigmask; + + if (sigaction(signo, &sa, NULL) < 0) { + zend_error(E_ERROR, "Error installing signal handler for %d", signo); + } + + /* unsure this signal is not blocked */ + sigemptyset(&sigset); + sigaddset(&sigset, signo); + zend_sigprocmask(SIG_UNBLOCK, &sigset, NULL); + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ zend_signal + * Register a signal handler that will be deferred in critical sections */ +ZEND_API int zend_signal(int signo, void (*handler)(int) TSRMLS_DC) +{ + struct sigaction sa = {{0}}; + + sa.sa_flags = 0; + sa.sa_handler = handler; + sa.sa_mask = global_sigmask; + + return zend_sigaction(signo, &sa, NULL TSRMLS_CC); +} +/* }}} */ + +/* {{{ zend_signal_register + * Set a handler for a signal we want to defer. + * Previously set handler must have been saved before. + */ +static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*) TSRMLS_DC) +{ + struct sigaction sa = {{0}}; + + if (sigaction(signo, NULL, &sa) == 0) { + if ((sa.sa_flags & SA_SIGINFO) && sa.sa_sigaction == handler) { + return FAILURE; + } + + SIGG(handlers)[signo-1].flags = sa.sa_flags; + if (sa.sa_flags & SA_SIGINFO) { + SIGG(handlers)[signo-1].handler = (void *)sa.sa_sigaction; + } else { + SIGG(handlers)[signo-1].handler = (void *)sa.sa_handler; + } + + sa.sa_flags = SA_SIGINFO; /* we'll use a siginfo handler */ + sa.sa_sigaction = handler; + sa.sa_mask = global_sigmask; + + if (sigaction(signo, &sa, NULL) < 0) { + zend_error(E_ERROR, "Error installing signal handler for %d", signo); + } + + return SUCCESS; + } + return FAILURE; +} /* }}} */ + +/* {{{ zend_signal_activate + * Install our signal handlers, per request */ +void zend_signal_activate(TSRMLS_D) +{ + int x; + + memcpy(&SIGG(handlers), &global_orig_handlers, sizeof(global_orig_handlers)); + + for (x=0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) { + zend_signal_register(zend_sigs[x], zend_signal_handler_defer TSRMLS_CC); + } + + SIGG(active) = 1; + SIGG(depth) = 0; +} /* }}} */ + +/* {{{ zend_signal_deactivate + * */ +void zend_signal_deactivate(TSRMLS_D) +{ + int x; + struct sigaction sa = {{0}}; + + if (SIGG(check)) { + if (SIGG(depth) != 0) { + zend_error(E_CORE_WARNING, "zend_signal: shutdown with non-zero blocking depth (%d)", SIGG(depth)); + } + /* did anyone steal our installed handler */ + for (x=0; x < sizeof(zend_sigs) / sizeof(*zend_sigs); x++) { + sigaction(zend_sigs[x], NULL, &sa); + if (sa.sa_sigaction != zend_signal_handler_defer) { + zend_error(E_CORE_WARNING, "zend_signal: handler was replaced for signal (%d) after startup", zend_sigs[x]); + } + } + } + + SIGNAL_BEGIN_CRITICAL(); + SIGG(active) = 0; + SIGG(running) = 0; + SIGG(blocked) = -1; + SIGG(depth) = 0; + SIGNAL_END_CRITICAL(); +} +/* }}} */ + +static void zend_signal_globals_ctor(zend_signal_globals_t *zend_signal_globals TSRMLS_DC) +{ + size_t x; + + memset(zend_signal_globals, 0, sizeof(*zend_signal_globals)); + zend_signal_globals->blocked = -1; + + for (x = 0; x < sizeof(zend_signal_globals->pstorage) / sizeof(*zend_signal_globals->pstorage); ++x) { + zend_signal_queue_t *queue = &zend_signal_globals->pstorage[x]; + queue->zend_signal.signo = 0; + queue->next = zend_signal_globals->pavail; + zend_signal_globals->pavail = queue; + } +} + +static void zend_signal_globals_dtor(zend_signal_globals_t *zend_signal_globals TSRMLS_DC) +{ + zend_signal_globals->blocked = -1; +} + +/* {{{ zend_signal_startup + * alloc zend signal globals */ +void zend_signal_startup() +{ + int signo; + struct sigaction sa = {{0}}; + +#ifdef ZTS + ts_allocate_id(&zend_signal_globals_id, sizeof(zend_signal_globals_t), (ts_allocate_ctor) zend_signal_globals_ctor, (ts_allocate_dtor) zend_signal_globals_dtor); +#else + zend_signal_globals_ctor(&zend_signal_globals); +#endif + + /* Used to block signals during execution of signal handlers */ + sigfillset(&global_sigmask); + sigdelset(&global_sigmask, SIGILL); + sigdelset(&global_sigmask, SIGABRT); + sigdelset(&global_sigmask, SIGFPE); + sigdelset(&global_sigmask, SIGKILL); + sigdelset(&global_sigmask, SIGSEGV); + sigdelset(&global_sigmask, SIGCONT); + sigdelset(&global_sigmask, SIGSTOP); + sigdelset(&global_sigmask, SIGTSTP); + sigdelset(&global_sigmask, SIGTTIN); + sigdelset(&global_sigmask, SIGTTOU); +#ifdef SIGBUS + sigdelset(&global_sigmask, SIGBUS); +#endif +#ifdef SIGSYS + sigdelset(&global_sigmask, SIGSYS); +#endif +#ifdef SIGTRAP + sigdelset(&global_sigmask, SIGTRAP); +#endif + + /* Save previously registered signal handlers into orig_handlers */ + memset(&global_orig_handlers, 0, sizeof(global_orig_handlers)); + for (signo = 1; signo < NSIG; ++signo) { + if (sigaction(signo, NULL, &sa) == 0) { + global_orig_handlers[signo-1].flags = sa.sa_flags; + if (sa.sa_flags & SA_SIGINFO) { + global_orig_handlers[signo-1].handler = (void *) sa.sa_sigaction; + } else { + global_orig_handlers[signo-1].handler = (void *) sa.sa_handler; + } + } + } +} +/* }}} */ + +/* {{{ zend_signal_shutdown + * called by zend_shutdown */ +void zend_signal_shutdown(TSRMLS_D) +{ +#ifndef ZTS + zend_signal_globals_dtor(&zend_signal_globals); +#endif +} +/* }}} */ + + +#endif /* ZEND_SIGNALS */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + * vim600: fdm=marker + * vim: noet sw=4 ts=4 + */ |