#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_API zend_signal_globals_t zend_signal_globals;
#endif
#define SIGNAL_BEGIN_CRITICAL() \
sigset_t oldmask; \
zend_sigprocmask(SIG_BLOCK, &global_sigmask, &oldmask);
#define SIGNAL_END_CRITICAL() \
zend_sigprocmask(SIG_SETMASK, &oldmask, NULL);
#ifdef ZTS
# define zend_sigprocmask(signo, set, oldset) tsrm_sigmask((signo), (set), (oldset))
#else
# define zend_sigprocmask(signo, set, oldset) sigprocmask((signo), (set), (oldset))
#endif
static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context);
static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*));
#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)
static zend_signal_entry_t global_orig_handlers[NSIG];
static sigset_t global_sigmask;
void zend_signal_handler_defer(int signo, siginfo_t *siginfo, void *context)
{
int errno_save = errno;
zend_signal_queue_t *queue, *qtmp;
zend_bool is_handling_safe = 1;
#ifdef ZTS
ZEND_TSRMLS_CACHE_UPDATE();
if (NULL == TSRMLS_CACHE || NULL == TSRMG_BULK_STATIC(zend_signal_globals_id, zend_signal_globals_t *)) {
is_handling_safe = 0;
}
#endif
if (EXPECTED(is_handling_safe && SIGG(active))) {
if (UNEXPECTED(SIGG(depth) == 0)) {
if (UNEXPECTED(SIGG(blocked))) {
SIGG(blocked) = 0;
}
if (EXPECTED(SIGG(running) == 0)) {
SIGG(running) = 1;
zend_signal_handler(signo, siginfo, context);
queue = SIGG(phead);
SIGG(phead) = NULL;
while (queue) {
zend_signal_handler(queue->zend_signal.signo, queue->zend_signal.siginfo, queue->zend_signal.context);
qtmp = queue->next;
queue->next = SIGG(pavail);
queue->zend_signal.signo = 0;
SIGG(pavail) = queue;
queue = qtmp;
}
SIGG(running) = 0;
}
} else {
SIGG(blocked) = 1;
if ((queue = SIGG(pavail))) {
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 {
zend_output_debug_string(0, "zend_signal: not enough queue storage, lost signal (%d)", signo);
}
#endif
}
} else {
zend_signal_handler(signo, siginfo, context);
}
errno = errno_save;
}
ZEND_API void zend_signal_handler_unblock(void)
{
zend_signal_queue_t *queue;
zend_signal_t zend_signal;
if (EXPECTED(SIGG(active))) {
SIGNAL_BEGIN_CRITICAL();
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();
}
}
static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context)
{
int errno_save = errno;
struct sigaction sa;
sigset_t sigset;
zend_signal_entry_t p_sig;
#ifdef ZTS
if (NULL == TSRMLS_CACHE || NULL == TSRMG_BULK_STATIC(zend_signal_globals_id, zend_signal_globals_t *)) {
p_sig.flags = 0;
p_sig.handler = SIG_DFL;
} else
#endif
p_sig = SIGG(handlers)[signo-1];
if (p_sig.handler == SIG_DFL) {
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) {
zend_sigprocmask(SIG_UNBLOCK, &sigset, NULL);
#ifdef ZTS
# define RAISE_ERROR "raise() failed\n"
if (raise(signo) != 0) {
kill(getpid(), signo);
}
#else
kill(getpid(), signo);
#endif
}
}
} else if (p_sig.handler != 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_API int zend_sigaction(int signo, const struct sigaction *act, struct sigaction *oldact)
{
struct sigaction sa;
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;
}
memset(&sa, 0, sizeof(sa));
if (SIGG(handlers)[signo-1].handler == (void *) SIG_IGN) {
sa.sa_sigaction = (void *) SIG_IGN;
} else {
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_noreturn(E_ERROR, "Error installing signal handler for %d", signo);
}
sigemptyset(&sigset);
sigaddset(&sigset, signo);
zend_sigprocmask(SIG_UNBLOCK, &sigset, NULL);
}
return SUCCESS;
}
ZEND_API int zend_signal(int signo, void (*handler)(int))
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_flags = 0;
sa.sa_handler = handler;
sa.sa_mask = global_sigmask;
return zend_sigaction(signo, &sa, NULL);
}
static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*))
{
struct sigaction sa;
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;
sa.sa_sigaction = handler;
sa.sa_mask = global_sigmask;
if (sigaction(signo, &sa, NULL) < 0) {
zend_error_noreturn(E_ERROR, "Error installing signal handler for %d", signo);
}
return SUCCESS;
}
return FAILURE;
}
void zend_signal_activate(void)
{
size_t 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);
}
SIGG(active) = 1;
SIGG(depth) = 0;
}
void zend_signal_deactivate(void)
{
if (SIGG(check)) {
size_t x;
struct sigaction sa;
if (SIGG(depth) != 0) {
zend_error(E_CORE_WARNING, "zend_signal: shutdown with non-zero blocking depth (%d)", SIGG(depth));
}
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) = 0;
SIGG(depth) = 0;
SIGNAL_END_CRITICAL();
}
static void zend_signal_globals_ctor(zend_signal_globals_t *zend_signal_globals)
{
size_t x;
memset(zend_signal_globals, 0, sizeof(*zend_signal_globals));
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;
}
}
void zend_signal_init(void)
{
int signo;
struct sigaction sa;
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;
}
}
}
}
void zend_signal_startup(void)
{
#ifdef ZTS
ts_allocate_id(&zend_signal_globals_id, sizeof(zend_signal_globals_t), (ts_allocate_ctor) zend_signal_globals_ctor, NULL);
#else
zend_signal_globals_ctor(&zend_signal_globals);
#endif
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
zend_signal_init();
}
#endif