Zend Signal Handling

This commit is contained in:
Ilia Alshanetsky 2011-06-22 14:23:21 +00:00
parent 512be854e5
commit 34d93f0c06
22 changed files with 721 additions and 41 deletions

1
NEWS
View file

@ -33,6 +33,7 @@ PHP NEWS
- <?= is now always available regardless of the short_tags setting (Rasmus)
- General improvements:
. Zend Signal Handling. (Lucas Nealan,Arnaud Le Blanc,Brian Shire, Ilia)
. Added multibyte support by default. Previously php had to be compiled
with --enable-zend-multibyte. Now it can be enabled or disabled through
zend.multibyte directive in php.ini. (Dmitry)

View file

@ -710,6 +710,22 @@ TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp)
#endif
}
/*
Changes the signal mask of the calling thread
*/
#ifdef HAVE_SIGPROCMASK
TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset)
{
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Changed sigmask in thread: %ld", tsrm_thread_id()));
/* TODO: add support for other APIs */
#ifdef PTHREADS
return pthread_sigmask(how, set, oldset);
#else
return sigprocmask(how, set, oldset);
#endif
}
#endif
TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler)
{

View file

@ -90,6 +90,10 @@ typedef struct {
# define MUTEX_T beos_ben *
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
typedef void (*ts_allocate_ctor)(void *, void ***);
typedef void (*ts_allocate_dtor)(void *, void ***);
@ -138,6 +142,9 @@ TSRM_API MUTEX_T tsrm_mutex_alloc(void);
TSRM_API void tsrm_mutex_free(MUTEX_T mutexp);
TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp);
TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp);
#ifdef HAVE_SIGPROCMASK
TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset);
#endif
TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler);
TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler);

View file

@ -30,6 +30,8 @@ AC_REQUIRE([AC_PROG_RANLIB])dnl
AC_CHECK_HEADERS(stdarg.h)
AC_CHECK_FUNCS(sigprocmask)
])

View file

@ -17,7 +17,7 @@ libZend_la_SOURCES=\
zend_objects_API.c zend_ts_hash.c zend_stream.c \
zend_default_classes.c \
zend_iterators.c zend_interfaces.c zend_exceptions.c \
zend_strtod.c zend_closures.c zend_float.c zend_string.c
zend_strtod.c zend_closures.c zend_float.c zend_string.c zend_signal.c
libZend_la_LDFLAGS =
libZend_la_LIBADD = @ZEND_EXTRA_LIBS@

View file

@ -392,8 +392,22 @@ int main()
AC_CHECK_FUNCS(mremap)
])
AC_CHECK_FUNC(sigaction, [
ZEND_SIGNALS=yes
AC_DEFINE(ZEND_SIGNALS, 1, [Use zend signal handling])
AC_DEFINE(HAVE_SIGACTION, 1, [Whether sigaction() is available])
], [
ZEND_SIGNALS=no
])
if test "$ZEND_SIGNALS" = "yes"; then
CFLAGS="$CFLAGS -DZEND_SIGNALS"
fi
AC_MSG_CHECKING(whether to enable zend signal handling)
AC_MSG_RESULT($ZEND_SIGNALS)
])
AC_DEFUN([LIBZEND_CPLUSPLUS_CHECKS],[

View file

@ -108,6 +108,9 @@ ZEND_INI_BEGIN()
STD_ZEND_INI_BOOLEAN("zend.multibyte", "0", ZEND_INI_PERDIR, OnUpdateBool, multibyte, zend_compiler_globals, compiler_globals)
ZEND_INI_ENTRY("zend.script_encoding", NULL, ZEND_INI_ALL, OnUpdateScriptEncoding)
STD_ZEND_INI_BOOLEAN("zend.detect_unicode", "1", ZEND_INI_ALL, OnUpdateBool, detect_unicode, zend_compiler_globals, compiler_globals)
#ifdef ZEND_SIGNALS
STD_ZEND_INI_BOOLEAN("zend.signal_check", "0", ZEND_INI_SYSTEM, OnUpdateBool, check, zend_signal_globals_t, zend_signal_globals)
#endif
ZEND_INI_END()
@ -659,8 +662,10 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions TS
}
zend_stream_open_function = utility_functions->stream_open_function;
zend_message_dispatcher_p = utility_functions->message_handler;
#ifndef ZEND_SIGNALS
zend_block_interruptions = utility_functions->block_interruptions;
zend_unblock_interruptions = utility_functions->unblock_interruptions;
#endif
zend_get_configuration_directive_p = utility_functions->get_configuration_directive;
zend_ticks_function = utility_functions->ticks_function;
zend_on_timeout = utility_functions->on_timeout;
@ -791,6 +796,9 @@ void zend_post_startup(TSRMLS_D) /* {{{ */
void zend_shutdown(TSRMLS_D) /* {{{ */
{
#ifdef ZEND_SIGNALS
zend_signal_shutdown(TSRMLS_C);
#endif
#ifdef ZEND_WIN32
zend_shutdown_timeout_thread();
#endif

View file

@ -531,8 +531,10 @@ typedef struct _zend_utility_functions {
int (*write_function)(const char *str, uint str_length);
FILE *(*fopen_function)(const char *filename, char **opened_path TSRMLS_DC);
void (*message_handler)(long message, void *data TSRMLS_DC);
#ifndef ZEND_SIGNALS
void (*block_interruptions)(void);
void (*unblock_interruptions)(void);
#endif
int (*get_configuration_directive)(const char *name, uint name_length, zval *contents);
void (*ticks_function)(int ticks);
void (*on_timeout)(int seconds TSRMLS_DC);
@ -674,8 +676,10 @@ BEGIN_EXTERN_C()
extern ZEND_API int (*zend_printf)(const char *format, ...) ZEND_ATTRIBUTE_PTR_FORMAT(printf, 1, 2);
extern ZEND_API zend_write_func_t zend_write;
extern ZEND_API FILE *(*zend_fopen)(const char *filename, char **opened_path TSRMLS_DC);
#ifndef ZEND_SIGNALS
extern ZEND_API void (*zend_block_interruptions)(void);
extern ZEND_API void (*zend_unblock_interruptions)(void);
#endif
extern ZEND_API void (*zend_ticks_function)(int ticks);
extern ZEND_API void (*zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) ZEND_ATTRIBUTE_PTR_FORMAT(printf, 4, 0);
extern void (*zend_on_timeout)(int seconds TSRMLS_DC);
@ -698,8 +702,15 @@ END_EXTERN_C()
#define ZEND_UV(name) (zend_uv.name)
#ifndef ZEND_SIGNALS
#define HANDLE_BLOCK_INTERRUPTIONS() if (zend_block_interruptions) { zend_block_interruptions(); }
#define HANDLE_UNBLOCK_INTERRUPTIONS() if (zend_unblock_interruptions) { zend_unblock_interruptions(); }
#else
#include "zend_signal.h"
#define HANDLE_BLOCK_INTERRUPTIONS() SIGG(depth)++;
#define HANDLE_UNBLOCK_INTERRUPTIONS() if (UNEXPECTED((--SIGG(depth))==SIGG(blocked))) { zend_signal_handler_unblock(TSRMLS_C); }
#endif
BEGIN_EXTERN_C()
ZEND_API void zend_message_dispatcher(long message, void *data TSRMLS_DC);

View file

@ -1882,6 +1882,11 @@ static void *_zend_mm_alloc_int(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_D
size_t segment_size;
zend_mm_segment *segment;
int keep_rest = 0;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
HANDLE_BLOCK_INTERRUPTIONS();
if (EXPECTED(ZEND_MM_SMALL_SIZE(true_size))) {
size_t index = ZEND_MM_BUCKET_INDEX(true_size);
@ -1902,6 +1907,7 @@ static void *_zend_mm_alloc_int(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_D
heap->cached -= true_size;
ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_CACHED);
ZEND_MM_SET_DEBUG_INFO(best_fit, size, 1, 0);
HANDLE_UNBLOCK_INTERRUPTIONS();
return ZEND_MM_DATA_OF(best_fit);
}
#if ZEND_MM_CACHE_STAT
@ -1955,8 +1961,6 @@ static void *_zend_mm_alloc_int(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_D
segment_size = heap->block_size;
}
HANDLE_BLOCK_INTERRUPTIONS();
if (segment_size < true_size ||
heap->real_size + segment_size > heap->limit) {
/* Memory limit overflow */
@ -1978,8 +1982,8 @@ static void *_zend_mm_alloc_int(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_D
#if ZEND_MM_CACHE
zend_mm_free_cache(heap);
#endif
HANDLE_UNBLOCK_INTERRUPTIONS();
out_of_memory:
HANDLE_UNBLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
zend_mm_safe_error(heap, "Out of memory (allocated %ld) at %s:%d (tried to allocate %lu bytes)", heap->real_size, __zend_filename, __zend_lineno, size);
#else
@ -2007,7 +2011,6 @@ out_of_memory:
} else {
zend_mm_finished_searching_for_block:
/* remove from free list */
HANDLE_BLOCK_INTERRUPTIONS();
ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_FREED);
ZEND_MM_CHECK_COOKIE(best_fit);
ZEND_MM_CHECK_BLOCK_LINKAGE(best_fit);
@ -2055,11 +2058,15 @@ static void _zend_mm_free_int(zend_mm_heap *heap, void *p ZEND_FILE_LINE_DC ZEND
zend_mm_block *mm_block;
zend_mm_block *next_block;
size_t size;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
if (!ZEND_MM_VALID_PTR(p)) {
return;
}
HANDLE_BLOCK_INTERRUPTIONS();
mm_block = ZEND_MM_HEADER_OF(p);
size = ZEND_MM_BLOCK_SIZE(mm_block);
ZEND_MM_CHECK_PROTECTION(mm_block);
@ -2082,12 +2089,11 @@ static void _zend_mm_free_int(zend_mm_heap *heap, void *p ZEND_FILE_LINE_DC ZEND
heap->cache_stat[index].max_count = heap->cache_stat[index].count;
}
#endif
HANDLE_UNBLOCK_INTERRUPTIONS();
return;
}
#endif
HANDLE_BLOCK_INTERRUPTIONS();
heap->size -= size;
next_block = ZEND_MM_BLOCK_AT(mm_block, size);
@ -2117,10 +2123,15 @@ static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_
size_t true_size;
size_t orig_size;
void *ptr;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
if (UNEXPECTED(!p) || !ZEND_MM_VALID_PTR(p)) {
return _zend_mm_alloc_int(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
}
HANDLE_BLOCK_INTERRUPTIONS();
mm_block = ZEND_MM_HEADER_OF(p);
true_size = ZEND_MM_TRUE_SIZE(size);
orig_size = ZEND_MM_BLOCK_SIZE(mm_block);
@ -2136,7 +2147,6 @@ static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_
if (remaining_size >= ZEND_MM_ALIGNED_MIN_HEADER_SIZE) {
zend_mm_free_block *new_free_block;
HANDLE_BLOCK_INTERRUPTIONS();
next_block = ZEND_MM_BLOCK_AT(mm_block, orig_size);
if (ZEND_MM_IS_FREE_BLOCK(next_block)) {
remaining_size += ZEND_MM_FREE_BLOCK_SIZE(next_block);
@ -2152,9 +2162,9 @@ static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_
/* add the new free block to the free list */
zend_mm_add_to_free_list(heap, new_free_block);
heap->size += (true_size - orig_size);
HANDLE_UNBLOCK_INTERRUPTIONS();
}
ZEND_MM_SET_DEBUG_INFO(mm_block, size, 0, 0);
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
@ -2197,6 +2207,7 @@ static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_
}
#endif
HANDLE_UNBLOCK_INTERRUPTIONS();
return ptr;
}
}
@ -2211,7 +2222,6 @@ static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_
size_t block_size = orig_size + ZEND_MM_FREE_BLOCK_SIZE(next_block);
size_t remaining_size = block_size - true_size;
HANDLE_BLOCK_INTERRUPTIONS();
zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block);
if (remaining_size < ZEND_MM_ALIGNED_MIN_HEADER_SIZE) {
@ -2242,7 +2252,6 @@ static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_
return p;
} else if (ZEND_MM_IS_FIRST_BLOCK(mm_block) &&
ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(next_block, ZEND_MM_FREE_BLOCK_SIZE(next_block)))) {
HANDLE_BLOCK_INTERRUPTIONS();
zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block);
goto realloc_segment;
}
@ -2253,7 +2262,6 @@ static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_
size_t block_size;
size_t remaining_size;
HANDLE_BLOCK_INTERRUPTIONS();
realloc_segment:
/* segment size, size of block and size of guard block */
if (true_size > heap->block_size - (ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE)) {
@ -2286,8 +2294,8 @@ realloc_segment:
#if ZEND_MM_CACHE
zend_mm_free_cache(heap);
#endif
HANDLE_UNBLOCK_INTERRUPTIONS();
out_of_memory:
HANDLE_UNBLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
zend_mm_safe_error(heap, "Out of memory (allocated %ld) at %s:%d (tried to allocate %ld bytes)", heap->real_size, __zend_filename, __zend_lineno, size);
#else
@ -2351,6 +2359,7 @@ out_of_memory:
memcpy(ptr, p, orig_size - ZEND_MM_ALIGNED_HEADER_SIZE);
#endif
_zend_mm_free_int(heap, p ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
HANDLE_UNBLOCK_INTERRUPTIONS();
return ptr;
}
@ -2539,12 +2548,18 @@ ZEND_API void *_safe_realloc(void *ptr, size_t nmemb, size_t size, size_t offset
ZEND_API void *_ecalloc(size_t nmemb, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
void *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
HANDLE_BLOCK_INTERRUPTIONS();
p = _safe_emalloc(nmemb, size, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
if (UNEXPECTED(p == NULL)) {
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
memset(p, 0, size * nmemb);
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
@ -2552,26 +2567,40 @@ ZEND_API char *_estrdup(const char *s ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
int length;
char *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
HANDLE_BLOCK_INTERRUPTIONS();
length = strlen(s)+1;
p = (char *) _emalloc(length ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
if (UNEXPECTED(p == NULL)) {
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
memcpy(p, s, length);
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
char *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
HANDLE_BLOCK_INTERRUPTIONS();
p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
if (UNEXPECTED(p == NULL)) {
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
memcpy(p, s, length);
p[length] = 0;
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
@ -2579,15 +2608,22 @@ ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_
ZEND_API char *zend_strndup(const char *s, uint length)
{
char *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
HANDLE_BLOCK_INTERRUPTIONS();
p = (char *) malloc(length+1);
if (UNEXPECTED(p == NULL)) {
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}
if (length) {
memcpy(p, s, length);
}
p[length] = 0;
HANDLE_UNBLOCK_INTERRUPTIONS();
return p;
}

View file

@ -1469,7 +1469,7 @@ void zend_set_timeout(long seconds, int reset_signals) /* {{{ */
# ifdef HAVE_SETITIMER
{
struct itimerval t_r; /* timeout requested */
sigset_t sigset;
int signo;
if(seconds) {
t_r.it_value.tv_sec = seconds;
@ -1478,25 +1478,27 @@ void zend_set_timeout(long seconds, int reset_signals) /* {{{ */
# ifdef __CYGWIN__
setitimer(ITIMER_REAL, &t_r, NULL);
}
if(reset_signals) {
signal(SIGALRM, zend_timeout);
sigemptyset(&sigset);
sigaddset(&sigset, SIGALRM);
}
signo = SIGALRM;
# else
setitimer(ITIMER_PROF, &t_r, NULL);
}
if(reset_signals) {
signal(SIGPROF, zend_timeout);
sigemptyset(&sigset);
sigaddset(&sigset, SIGPROF);
}
signo = SIGPROF;
# endif
if(reset_signals) {
if (reset_signals) {
# ifdef ZEND_SIGNALS
zend_signal(signo, zend_timeout TSRMLS_CC);
# else
sigset_t sigset;
signal(signo, zend_timeout);
sigemptyset(&sigset);
sigaddset(&sigset, signo);
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
# endif
}
}
# endif
# endif /* HAVE_SETITIMER */
#endif
}
/* }}} */

View file

@ -197,6 +197,9 @@ ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKe
ulong h;
uint nIndex;
Bucket *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
IS_CONSISTENT(ht);
@ -276,6 +279,9 @@ ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, ui
{
uint nIndex;
Bucket *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
IS_CONSISTENT(ht);
@ -431,6 +437,9 @@ ZEND_API int _zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void
static int zend_hash_do_resize(HashTable *ht)
{
Bucket **t;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
IS_CONSISTENT(ht);
@ -475,6 +484,9 @@ ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint n
{
uint nIndex;
Bucket *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
IS_CONSISTENT(ht);
@ -595,6 +607,9 @@ ZEND_API void zend_hash_clean(HashTable *ht)
static Bucket *zend_hash_apply_deleter(HashTable *ht, Bucket *p)
{
Bucket *retval;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
HANDLE_BLOCK_INTERRUPTIONS();
if (p->pLast) {
@ -1194,6 +1209,9 @@ ZEND_API int zend_hash_get_current_data_ex(HashTable *ht, void **pData, HashPosi
ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const char *str_index, uint str_length, ulong num_index, int mode, HashPosition *pos)
{
Bucket *p;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
p = pos ? (*pos) : ht->pInternalPointer;

414
Zend/zend_signal.c Normal file
View file

@ -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
*/

104
Zend/zend_signal.h Normal file
View file

@ -0,0 +1,104 @@
/*
+----------------------------------------------------------------------+
| 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> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#ifndef ZEND_SIGNAL_H
#define ZEND_SIGNAL_H
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifndef NSIG
#define NSIG 65
#endif
#ifndef ZEND_SIGNAL_QUEUE_SIZE
#define ZEND_SIGNAL_QUEUE_SIZE 32
#endif
/* Signal structs */
typedef struct _zend_signal_entry_t {
int flags; /* sigaction style flags */
void* handler; /* signal handler or context */
} zend_signal_entry_t;
typedef struct _zend_signal_t {
int signo;
siginfo_t *siginfo;
void* context;
} zend_signal_t;
typedef struct _zend_signal_queue_t {
zend_signal_t zend_signal;
struct _zend_signal_queue_t *next;
} zend_signal_queue_t;
/* Signal Globals */
typedef struct _zend_signal_globals_t {
int depth;
int blocked; /* 0==TRUE, -1==FALSE */
int running; /* in signal handler execution */
int active; /* internal signal handling is enabled */
int initialized; /* memory initialized */
zend_bool check; /* check for replaced handlers on shutdown */
zend_signal_entry_t handlers[NSIG];
zend_signal_queue_t pstorage[ZEND_SIGNAL_QUEUE_SIZE], *phead, *ptail, *pavail; /* pending queue */
} zend_signal_globals_t;
#ifdef ZTS
# define SIGG(v) TSRMG(zend_signal_globals_id, zend_signal_globals_t *, v)
BEGIN_EXTERN_C()
ZEND_API extern int zend_signal_globals_id;
END_EXTERN_C()
#else /* ZTS */
# define SIGG(v) (zend_signal_globals.v)
extern ZEND_API zend_signal_globals_t zend_signal_globals;
#endif /* not ZTS */
# define SIGNAL_BEGIN_CRITICAL() sigset_t oldmask; \
zend_sigprocmask(SIG_BLOCK, &global_sigmask, &oldmask);
# define SIGNAL_END_CRITICAL() zend_sigprocmask(SIG_SETMASK, &oldmask, NULL);
void zend_signal_handler_defer(int signo, siginfo_t *siginfo, void *context);
void zend_signal_handler_unblock();
void zend_signal_activate(TSRMLS_D);
void zend_signal_deactivate(TSRMLS_D);
void zend_signal_startup();
void zend_signal_shutdown(TSRMLS_D);
ZEND_API int zend_signal(int signo, void (*handler)(int) TSRMLS_DC);
ZEND_API int zend_sigaction(int signo, const struct sigaction *act, struct sigaction *oldact TSRMLS_DC);
#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
#endif /* ZEND_SIGNAL_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/

View file

@ -1473,7 +1473,7 @@ PHP_ADD_SOURCES(Zend, \
zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \
zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \
zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_gc.c \
zend_closures.c zend_float.c zend_string.c)
zend_closures.c zend_float.c zend_string.c zend_signal.c)
if test -r "$abs_srcdir/Zend/zend_objects.c"; then
PHP_ADD_SOURCES(Zend, zend_objects.c zend_object_handlers.c zend_objects_API.c zend_default_classes.c)

View file

@ -849,6 +849,11 @@ PHP_FUNCTION(pcntl_signal)
return;
}
if (signo < 1 || signo > 32) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid signal");
RETURN_FALSE;
}
if (!PCNTL_G(spares)) {
/* since calling malloc() from within a signal handler is not portable,
* pre-allocate a few records for recording signals */
@ -864,8 +869,9 @@ PHP_FUNCTION(pcntl_signal)
/* Special long value case for SIG_DFL and SIG_IGN */
if (Z_TYPE_P(handle)==IS_LONG) {
if (Z_LVAL_P(handle)!= (long) SIG_DFL && Z_LVAL_P(handle) != (long) SIG_IGN) {
if (Z_LVAL_P(handle) != (long) SIG_DFL && Z_LVAL_P(handle) != (long) SIG_IGN) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid value for handle argument specified");
RETURN_FALSE;
}
if (php_signal(signo, (Sigfunc *) Z_LVAL_P(handle), (int) restart_syscalls) == SIG_ERR) {
PCNTL_G(last_error) = errno;

View file

@ -18,6 +18,7 @@
/* $Id$ */
#include "TSRM.h"
#include "php_signal.h"
/* php_signal using sigaction is derrived from Advanced Programing
@ -26,6 +27,10 @@ Sigfunc *php_signal4(int signo, Sigfunc *func, int restart, int mask_all)
{
struct sigaction act,oact;
act.sa_handler = func;
#ifdef ZEND_SIGNALS
TSRMLS_FETCH();
#endif
if (mask_all) {
sigfillset(&act.sa_mask);
} else {
@ -41,9 +46,15 @@ Sigfunc *php_signal4(int signo, Sigfunc *func, int restart, int mask_all)
act.sa_flags |= SA_RESTART; /* SVR4, 4.3+BSD */
#endif
}
#ifdef ZEND_SIGNALS
if (zend_sigaction(signo, &act, &oact TSRMLS_CC) < 0)
#else
if (sigaction(signo, &act, &oact) < 0)
#endif
{
return SIG_ERR;
}
return oact.sa_handler;
}

View file

@ -28,12 +28,10 @@ Warning: pcntl_signal() expects at least 2 parameters, 0 given in %s
NULL
bool(true)
Warning: pcntl_signal(): Invalid value for handle argument specified in %s
Warning: pcntl_signal(): Error assigning signal %s
Warning: pcntl_signal(): Invalid signal %s
bool(false)
Warning: pcntl_signal(): Error assigning signal %s
Warning: pcntl_signal(): Invalid signal %s
bool(false)
Warning: pcntl_signal(): not callable is not a callable function name error in %s

View file

@ -747,6 +747,12 @@ PHPAPI void php_print_info(int flag TSRMLS_DC)
php_info_print_table_row(2, "Thread Safety", "disabled" );
#endif
#ifdef ZEND_SIGNALS
php_info_print_table_row(2, "Zend Signal Handling", "enabled" );
#else
php_info_print_table_row(2, "Zend Signal Handling", "disabled" );
#endif
php_info_print_table_row(2, "Zend Memory Manager", is_zend_mm(TSRMLS_C) ? "enabled" : "disabled" );
{

View file

@ -7,7 +7,7 @@ output_handler=
$php = getenv('TEST_PHP_EXECUTABLE');
$tmpfile = tempnam(__DIR__, 'phpt');
$args = ' -n -dsafe_mode=off ';
$args = ' -n ';
/* Regular Data Test */
passthru($php . $args . ' -r " echo \"HELLO\"; "');

View file

@ -35,6 +35,7 @@ PHP Extension Build => API%s
Debug Build => %s
Thread Safety => %s
Zend Memory Manager => %s
Zend Signal Handling => %s
Zend Multibyte Support => %s
IPv6 Support => %s
DTrace Support => %s

View file

@ -75,6 +75,10 @@ SAPI_API sapi_module_struct sapi_module;
SAPI_API void sapi_startup(sapi_module_struct *sf)
{
#ifdef ZEND_SIGNALS
zend_signal_startup();
#endif
sf->ini_entries = NULL;
sapi_module = *sf;

View file

@ -1372,8 +1372,12 @@ void php_on_timeout(int seconds TSRMLS_DC)
*/
static void sigchld_handler(int apar)
{
int errno_save = errno;
while (waitpid(-1, NULL, WNOHANG) > 0);
signal(SIGCHLD, sigchld_handler);
errno = errno_save;
}
/* }}} */
#endif
@ -1442,6 +1446,10 @@ int php_request_startup(TSRMLS_D)
zend_activate(TSRMLS_C);
sapi_activate(TSRMLS_C);
#ifdef ZEND_SIGNALS
zend_signal_activate(TSRMLS_C);
#endif
if (PG(max_input_time) == -1) {
zend_set_timeout(EG(timeout_seconds), 1);
} else {
@ -1565,6 +1573,10 @@ void php_request_shutdown_for_hook(void *dummy)
php_free_shutdown_functions(TSRMLS_C);
}
zend_try {
zend_unset_timeout(TSRMLS_C);
} zend_end_try();
zend_try {
int i;
@ -1591,9 +1603,11 @@ void php_request_shutdown_for_hook(void *dummy)
zend_interned_strings_restore(TSRMLS_C);
#ifdef ZEND_SIGNALS
zend_try {
zend_unset_timeout(TSRMLS_C);
zend_signal_deactivate(TSRMLS_C);
} zend_end_try();
#endif
}
/* }}} */
@ -1648,13 +1662,18 @@ void php_request_shutdown(void *dummy)
sapi_send_headers(TSRMLS_C);
} zend_end_try();
/* 5. Call all extensions RSHUTDOWN functions */
/* 5. Reset max_execution_time (no longer executing php code after response sent) */
zend_try {
zend_unset_timeout(TSRMLS_C);
} zend_end_try();
/* 6. Call all extensions RSHUTDOWN functions */
if (PG(modules_activated)) {
zend_deactivate_modules(TSRMLS_C);
php_free_shutdown_functions(TSRMLS_C);
}
/* 6. Destroy super-globals */
/* 7. Destroy super-globals */
zend_try {
int i;
@ -1665,7 +1684,7 @@ void php_request_shutdown(void *dummy)
}
} zend_end_try();
/* 6.5 free last error information */
/* 7.5 free last error information */
if (PG(last_error_message)) {
free(PG(last_error_message));
PG(last_error_message) = NULL;
@ -1894,8 +1913,10 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod
zuf.write_function = php_output_wrapper;
zuf.fopen_function = php_fopen_wrapper_for_zend;
zuf.message_handler = php_message_handler_for_zend;
#ifndef ZEND_SIGNALS
zuf.block_interruptions = sapi_module.block_interruptions;
zuf.unblock_interruptions = sapi_module.unblock_interruptions;
#endif
zuf.get_configuration_directive = php_get_configuration_directive_for_zend;
zuf.ticks_function = php_run_ticks;
zuf.on_timeout = php_on_timeout;