MFH: Added pcntl_sigwaitinfo(), pcntl_sigtimedwait() and pcntl_sigprocmask()

[DOC] pcntl_sigprocmask() allows to block signals. pcntl_sigwaitinfo()
allows to fetch blocked signals or signals delivered while pcntl_sigwaitinfo()
is running. pcntl_sigtimedwait() is pcntl_sigwaitinfo() with a timeout.
This commit is contained in:
Arnaud Le Blanc 2008-07-29 16:59:10 +00:00
parent 204fcbe5d3
commit c58e2b9d20
5 changed files with 358 additions and 1 deletions

6
NEWS
View file

@ -210,6 +210,12 @@ PHP NEWS
DateInterval on each iteration, up to an end date or limited by maximum DateInterval on each iteration, up to an end date or limited by maximum
number of occurences. number of occurences.
- Improved pcntl extension: (Arnaud)
. Added pcntl_signal_dispatch()
. Added pcntl_sigprocmask()
. Added pcntl_sigwaitinfo()
. Added pcntl_sigtimedwait()
- Added hash_copy() function. (Tony) - Added hash_copy() function. (Tony)
- Added sha224 hash algorithm to the hash extension. (Scott) - Added sha224 hash algorithm to the hash extension. (Scott)
- Added ReflectionProperty::setAccessible() method that allows non-public - Added ReflectionProperty::setAccessible() method that allows non-public

View file

@ -13,7 +13,7 @@ if test "$PHP_PCNTL" != "no"; then
AC_CHECK_FUNCS(fork, [ AC_DEFINE(HAVE_FORK,1,[ ]) ], [ AC_MSG_ERROR(pcntl: fork() not supported by this platform) ]) AC_CHECK_FUNCS(fork, [ AC_DEFINE(HAVE_FORK,1,[ ]) ], [ AC_MSG_ERROR(pcntl: fork() not supported by this platform) ])
AC_CHECK_FUNCS(waitpid, [ AC_DEFINE(HAVE_WAITPID,1,[ ]) ], [ AC_MSG_ERROR(pcntl: fork() not supported by this platform) ]) AC_CHECK_FUNCS(waitpid, [ AC_DEFINE(HAVE_WAITPID,1,[ ]) ], [ AC_MSG_ERROR(pcntl: fork() not supported by this platform) ])
AC_CHECK_FUNCS(sigaction, [ AC_DEFINE(HAVE_SIGACTION,1,[ ]) ], [ AC_MSG_ERROR(pcntl: sigaction() not supported by this platform) ]) AC_CHECK_FUNCS(sigaction, [ AC_DEFINE(HAVE_SIGACTION,1,[ ]) ], [ AC_MSG_ERROR(pcntl: sigaction() not supported by this platform) ])
AC_CHECK_FUNCS(getpriority setpriority wait3) AC_CHECK_FUNCS(getpriority setpriority wait3 sigprocmask sigwaitinfo sigtimedwait)
PHP_NEW_EXTENSION(pcntl, pcntl.c php_signal.c, $ext_shared, cli) PHP_NEW_EXTENSION(pcntl, pcntl.c php_signal.c, $ext_shared, cli)
fi fi

View file

@ -69,6 +69,26 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_signal, 0, 0, 2)
ZEND_ARG_INFO(0, restart_syscalls) ZEND_ARG_INFO(0, restart_syscalls)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
static
ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigprocmask, 0, 0, 2)
ZEND_ARG_INFO(0, how)
ZEND_ARG_INFO(0, set)
ZEND_END_ARG_INFO()
static
ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigwaitinfo, 0, 0, 1)
ZEND_ARG_INFO(0, set)
ZEND_ARG_INFO(1, info)
ZEND_END_ARG_INFO()
static
ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_sigtimedwait, 0, 0, 1)
ZEND_ARG_INFO(0, set)
ZEND_ARG_INFO(1, info)
ZEND_ARG_INFO(0, seconds)
ZEND_ARG_INFO(0, nanoseconds)
ZEND_END_ARG_INFO()
static static
ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifexited, 0, 0, 1) ZEND_BEGIN_ARG_INFO_EX(arginfo_pcntl_wifexited, 0, 0, 1)
ZEND_ARG_INFO(0, status) ZEND_ARG_INFO(0, status)
@ -148,6 +168,13 @@ const zend_function_entry pcntl_functions[] = {
#endif #endif
#ifdef HAVE_SETPRIORITY #ifdef HAVE_SETPRIORITY
PHP_FE(pcntl_setpriority, arginfo_pcntl_setpriority) PHP_FE(pcntl_setpriority, arginfo_pcntl_setpriority)
#endif
#ifdef HAVE_SIGPROCMASK
PHP_FE(pcntl_sigprocmask, arginfo_pcntl_sigprocmask)
#endif
#if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
PHP_FE(pcntl_sigwaitinfo, arginfo_pcntl_sigwaitinfo)
PHP_FE(pcntl_sigtimedwait, arginfo_pcntl_sigtimedwait)
#endif #endif
{NULL, NULL, NULL} {NULL, NULL, NULL}
}; };
@ -246,6 +273,81 @@ void php_register_signal_constants(INIT_FUNC_ARGS)
REGISTER_LONG_CONSTANT("PRIO_USER", PRIO_USER, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PRIO_USER", PRIO_USER, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PRIO_PROCESS", PRIO_PROCESS, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PRIO_PROCESS", PRIO_PROCESS, CONST_CS | CONST_PERSISTENT);
#endif #endif
/* {{{ "how" argument for sigprocmask */
#ifdef HAVE_SIGPROCMASK
REGISTER_LONG_CONSTANT("SIG_BLOCK", SIG_BLOCK, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SIG_UNBLOCK", SIG_BLOCK, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SIG_SETMASK", SIG_BLOCK, CONST_CS | CONST_PERSISTENT);
#endif
/* }}} */
/* {{{ si_code */
#if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
REGISTER_LONG_CONSTANT("SI_USER", SI_USER, CONST_CS | CONST_PERSISTENT);
#ifdef SI_NOINFO
REGISTER_LONG_CONSTANT("SI_NOINFO", SI_NOINFO, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef SI_KERNEL
REGISTER_LONG_CONSTANT("SI_KERNEL", SI_KERNEL, CONST_CS | CONST_PERSISTENT);
#endif
REGISTER_LONG_CONSTANT("SI_QUEUE", SI_QUEUE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SI_TIMER", SI_TIMER, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SI_MESGQ", SI_MESGQ, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SI_ASYNCIO", SI_ASYNCIO, CONST_CS | CONST_PERSISTENT);
#ifdef SI_SIGIO
REGISTER_LONG_CONSTANT("SI_SIGIO", SI_SIGIO, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef SI_TKILL
REGISTER_LONG_CONSTANT("SI_TKILL", SI_TKILL, CONST_CS | CONST_PERSISTENT);
#endif
/* si_code for SIGCHILD */
REGISTER_LONG_CONSTANT("CLD_EXITED", CLD_EXITED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CLD_KILLED", CLD_KILLED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CLD_DUMPED", CLD_DUMPED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CLD_TRAPPED", CLD_TRAPPED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CLD_STOPPED", CLD_STOPPED, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("CLD_CONTINUED", CLD_CONTINUED, CONST_CS | CONST_PERSISTENT);
/* si_code for SIGTRAP */
REGISTER_LONG_CONSTANT("TRAP_BRKPT", TRAP_BRKPT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("TRAP_TRACE", TRAP_TRACE, CONST_CS | CONST_PERSISTENT);
/* si_code for SIGPOLL */
REGISTER_LONG_CONSTANT("POLL_IN", POLL_IN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("POLL_OUT", POLL_OUT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("POLL_MSG", POLL_MSG, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("POLL_ERR", POLL_ERR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("POLL_PRI", POLL_PRI, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("POLL_HUP", POLL_HUP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ILL_ILLOPC", ILL_ILLOPC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ILL_ILLOPN", ILL_ILLOPN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ILL_ILLADR", ILL_ILLADR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ILL_ILLTRP", ILL_ILLTRP, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ILL_PRVOPC", ILL_PRVOPC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ILL_PRVREG", ILL_PRVREG, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ILL_COPROC", ILL_COPROC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ILL_BADSTK", ILL_BADSTK, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("FPE_INTDIV", FPE_INTDIV, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("FPE_INTOVF", FPE_INTOVF, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("FPE_FLTDIV", FPE_FLTDIV, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("FPE_FLTOVF", FPE_FLTOVF, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("FPE_FLTUND", FPE_FLTINV, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("FPE_FLTRES", FPE_FLTRES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("FPE_FLTINV", FPE_FLTINV, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("FPE_FLTSUB", FPE_FLTSUB, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SEGV_MAPERR", SEGV_MAPERR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SEGV_ACCERR", SEGV_ACCERR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("BUS_ADRALN", BUS_ADRALN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("BUS_ADRERR", BUS_ADRERR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("BUS_OBJERR", BUS_OBJERR, CONST_CS | CONST_PERSISTENT);
#endif
/* }}} */
} }
static PHP_GINIT_FUNCTION(pcntl) static PHP_GINIT_FUNCTION(pcntl)
@ -654,6 +756,161 @@ PHP_FUNCTION(pcntl_signal_dispatch)
} }
/* }}} */ /* }}} */
#ifdef HAVE_SIGPROCMASK
/* {{{ proto bool pcntl_sigprocmask(int how, array set)
Examine and change blocked signals */
PHP_FUNCTION(pcntl_sigprocmask)
{
long how, signo;
zval *user_set, **user_signo;
sigset_t set;
HashPosition pos;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "la", &how, &user_set) == FAILURE) {
return;
}
if (sigemptyset(&set) != 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
}
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(user_set), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(user_set), (void **)&user_signo, &pos) == SUCCESS)
{
if (Z_TYPE_PP(user_signo) != IS_LONG) {
SEPARATE_ZVAL(user_signo);
convert_to_long_ex(user_signo);
}
signo = Z_LVAL_PP(user_signo);
if (sigaddset(&set, signo) != 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
}
zend_hash_move_forward_ex(Z_ARRVAL_P(user_set), &pos);
}
if (sigprocmask(how, &set, NULL) != 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
}
RETURN_TRUE;
}
/* }}} */
#endif
#if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
static void pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAMETERS, int timedwait) /* {{{ */
{
zval *user_set, **user_signo, *user_siginfo = NULL;
long tv_sec = 0, tv_nsec = 0;
sigset_t set;
HashPosition pos;
int signo;
siginfo_t siginfo;
struct timespec timeout;
if (timedwait) {
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|zll", &user_set, &user_siginfo, &tv_sec, &tv_nsec) == FAILURE) {
return;
}
} else {
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z", &user_set, &user_siginfo) == FAILURE) {
return;
}
}
if (sigemptyset(&set) != 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
}
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(user_set), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(user_set), (void **)&user_signo, &pos) == SUCCESS)
{
if (Z_TYPE_PP(user_signo) != IS_LONG) {
SEPARATE_ZVAL(user_signo);
convert_to_long_ex(user_signo);
}
signo = Z_LVAL_PP(user_signo);
if (sigaddset(&set, signo) != 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
RETURN_FALSE;
}
zend_hash_move_forward_ex(Z_ARRVAL_P(user_set), &pos);
}
if (timedwait) {
timeout.tv_sec = (time_t) tv_sec;
timeout.tv_nsec = tv_nsec;
signo = sigtimedwait(&set, &siginfo, &timeout);
} else {
signo = sigwaitinfo(&set, &siginfo);
}
if (signo == -1 && errno != EAGAIN) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
}
if (signo > 0 && user_siginfo) {
if (Z_TYPE_P(user_siginfo) != IS_ARRAY) {
zval_dtor(user_siginfo);
array_init(user_siginfo);
}
add_assoc_long_ex(user_siginfo, "signo", sizeof("signo"), siginfo.si_signo);
add_assoc_long_ex(user_siginfo, "errno", sizeof("errno"), siginfo.si_errno);
add_assoc_long_ex(user_siginfo, "code", sizeof("code"), siginfo.si_code);
switch(signo) {
#ifdef SIGCHLD
case SIGCHLD:
add_assoc_long_ex(user_siginfo, "status", sizeof("status"), siginfo.si_status);
#ifdef si_utime
add_assoc_double_ex(user_siginfo, "utime", sizeof("utime"), siginfo.si_utime);
#endif
#ifdef si_stime
add_assoc_double_ex(user_siginfo, "stime", sizeof("stime"), siginfo.si_stime);
#endif
add_assoc_long_ex(user_siginfo, "pid", sizeof("pid"), siginfo.si_pid);
add_assoc_long_ex(user_siginfo, "uid", sizeof("uid"), siginfo.si_uid);
break;
#endif
case SIGILL:
case SIGFPE:
case SIGSEGV:
case SIGBUS:
add_assoc_double_ex(user_siginfo, "addr", sizeof("addr"), (long)siginfo.si_addr);
break;
#ifdef SIGPOLL
case SIGPOLL:
add_assoc_long_ex(user_siginfo, "band", sizeof("band"), siginfo.si_band);
add_assoc_long_ex(user_siginfo, "fd", sizeof("fd"), siginfo.si_fd);
break;
#endif
EMPTY_SWITCH_DEFAULT_CASE();
}
}
RETURN_LONG(signo);
}
/* }}} */
/* {{{ proto int sigwaitinfo(array set[, array &siginfo])
Synchronously wait for queued signals */
PHP_FUNCTION(pcntl_sigwaitinfo)
{
pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ proto int sigtimedwait(array set[, array &siginfo[, int seconds[, int nanoseconds]]])
Wait for queued signals */
PHP_FUNCTION(pcntl_sigtimedwait)
{
pcntl_sigwaitinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
#endif
#ifdef HAVE_GETPRIORITY #ifdef HAVE_GETPRIORITY
/* {{{ proto int pcntl_getpriority([int pid [, int process_identifier]]) /* {{{ proto int pcntl_getpriority([int pid [, int process_identifier]])
Get the priority of any process */ Get the priority of any process */

View file

@ -45,6 +45,13 @@ PHP_FUNCTION(pcntl_wtermsig);
PHP_FUNCTION(pcntl_wstopsig); PHP_FUNCTION(pcntl_wstopsig);
PHP_FUNCTION(pcntl_signal); PHP_FUNCTION(pcntl_signal);
PHP_FUNCTION(pcntl_signal_dispatch); PHP_FUNCTION(pcntl_signal_dispatch);
#ifdef HAVE_SIGPROCMASK
PHP_FUNCTION(pcntl_sigprocmask);
#endif
#if HAVE_SIGWAITINFO && HAVE_SIGTIMEDWAIT
PHP_FUNCTION(pcntl_sigwaitinfo);
PHP_FUNCTION(pcntl_sigtimedwait);
#endif
PHP_FUNCTION(pcntl_exec); PHP_FUNCTION(pcntl_exec);
#ifdef HAVE_GETPRIORITY #ifdef HAVE_GETPRIORITY
PHP_FUNCTION(pcntl_getpriority); PHP_FUNCTION(pcntl_getpriority);

87
ext/pcntl/tests/002.phpt Normal file
View file

@ -0,0 +1,87 @@
--TEST--
pcntl: pcntl_sigprocmask(), pcntl_sigwaitinfo(), pcntl_sigtimedwait()
--SKIPIF--
<?php
if (!extension_loaded('pcntl')) die('skip pcntl extension not available');
if (!extension_loaded('posix')) die('skip posix extension not available');
?>
--FILE--
<?php
$pid = pcntl_fork();
if ($pid == -1) {
die('failed');
} else if ($pid) {
pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD,(string)SIGTERM));
posix_kill(posix_getpid(), SIGTERM);
$signo = pcntl_sigwaitinfo(array(SIGTERM), $siginfo);
echo "signo == SIGTERM\n";
var_dump($signo === SIGTERM && $signo === $siginfo['signo']);
echo "code === SI_USER || SI_NOINFO\n";
if (defined('SI_NOINFO')) {
var_dump(($siginfo['code'] === SI_USER) || ($siginfo['code'] === SI_NOINFO));
} else {
var_dump($siginfo['code'] === SI_USER);
}
pcntl_signal(SIGCHLD, function($signo){});
posix_kill($pid, SIGTERM);
$signo = pcntl_sigwaitinfo(array((string)SIGCHLD), $siginfo);
echo "signo == SIGCHLD\n";
var_dump($signo === SIGCHLD && $signo === $siginfo['signo']);
echo "code === CLD_KILLED\n";
var_dump($siginfo['code'] === CLD_KILLED);
echo "signo === SIGCHLD\n";
var_dump($siginfo['signo'] === SIGCHLD);
echo "signo === uid\n";
var_dump($siginfo['uid'] === posix_getuid());
echo "signo === pid\n";
var_dump($siginfo['pid'] === $pid);
pcntl_waitpid($pid, $status);
echo "sigprocmask with invalid arguments\n";
var_dump(pcntl_sigprocmask(PHP_INT_MAX, array(SIGTERM)));
var_dump(pcntl_sigprocmask(SIG_SETMASK, array(0)));
echo "sigwaitinfo with invalid arguments\n";
var_dump(pcntl_sigwaitinfo(array(0)));
echo "sigtimedwait with invalid arguments\n";
var_dump(pcntl_sigtimedwait(array(SIGTERM), $signo, PHP_INT_MAX, PHP_INT_MAX));
} else {
$siginfo = NULL;
pcntl_sigtimedwait(array(SIGTERM), $siginfo, PHP_INT_MAX, 999999999);
exit;
}
?>
--EXPECTF--
signo == SIGTERM
bool(true)
code === SI_USER || SI_NOINFO
bool(true)
signo == SIGCHLD
bool(true)
code === CLD_KILLED
bool(true)
signo === SIGCHLD
bool(true)
signo === uid
bool(true)
signo === pid
bool(true)
sigprocmask with invalid arguments
Warning: pcntl_sigprocmask(): Invalid argument in %s on line %d
bool(false)
Warning: pcntl_sigprocmask(): Invalid argument in %s on line %d
bool(false)
sigwaitinfo with invalid arguments
Warning: pcntl_sigwaitinfo(): Invalid argument in %s on line %d
bool(false)
sigtimedwait with invalid arguments
Warning: pcntl_sigtimedwait(): Invalid argument in %s on line %d
int(-1)