diff --git a/UPGRADING b/UPGRADING index 56125e338db..93437c9aa37 100644 --- a/UPGRADING +++ b/UPGRADING @@ -346,6 +346,9 @@ PHP 8.1 UPGRADE NOTES . Added array_is_list(array $array), which will return true if the array keys are 0 .. count($array)-1 in that order. RFC: https://wiki.php.net/rfc/is_list +- pcntl: + . Added pcntl_rfork for FreeBSD variants + - Reflection: . Added ReflectionFunctionAbstract::getClosureUsedVariables diff --git a/ext/pcntl/config.m4 b/ext/pcntl/config.m4 index 332674df38d..50dfd354e8e 100644 --- a/ext/pcntl/config.m4 +++ b/ext/pcntl/config.m4 @@ -7,7 +7,7 @@ if test "$PHP_PCNTL" != "no"; then AC_CHECK_FUNCS([fork], [], [AC_MSG_ERROR([pcntl: fork() not supported by this platform])]) AC_CHECK_FUNCS([waitpid], [], [AC_MSG_ERROR([pcntl: waitpid() not supported by this platform])]) AC_CHECK_FUNCS([sigaction], [], [AC_MSG_ERROR([pcntl: sigaction() not supported by this platform])]) - AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare]) + AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork]) AC_MSG_CHECKING([for siginfo_t]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 126dfd452da..06c0c1189bc 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -340,6 +340,36 @@ void php_register_signal_constants(INIT_FUNC_ARGS) REGISTER_LONG_CONSTANT("CLONE_NEWCGROUP", CLONE_NEWCGROUP, CONST_CS | CONST_PERSISTENT); #endif #endif + +#ifdef HAVE_RFORK +#ifdef RFPROC + REGISTER_LONG_CONSTANT("RFPROC", RFPROC, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef RFNOWAIT + REGISTER_LONG_CONSTANT("RFNOWAIT", RFNOWAIT, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef RFCFDG + REGISTER_LONG_CONSTANT("RFCFDG", RFCFDG, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef RFFDG + REGISTER_LONG_CONSTANT("RFFDG", RFFDG, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef RFLINUXTHPN + REGISTER_LONG_CONSTANT("RFLINUXTHPN", RFLINUXTHPN, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef RFMEM + REGISTER_LONG_CONSTANT("RFMEM", RFMEM, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef RFSIGSHARE + REGISTER_LONG_CONSTANT("RFSIGSHARE", RFSIGSHARE, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef RFSIGZMB + REGISTER_LONG_CONSTANT("RFSIGSZMB", RFSIGSZMB, CONST_CS | CONST_PERSISTENT); +#endif +#ifdef RFTHREAD + REGISTER_LONG_CONSTANT("RFTHREAD", RFTHREAD, CONST_CS | CONST_PERSISTENT); +#endif +#endif } static void php_pcntl_register_errno_constants(INIT_FUNC_ARGS) @@ -1468,6 +1498,54 @@ PHP_FUNCTION(pcntl_unshare) /* }}} */ #endif +#ifdef HAVE_RFORK +/* {{{ proto bool pcntl_rfork([int flags|custom signal) + More control over the process creation is given over fork/vfork. */ +PHP_FUNCTION(pcntl_rfork) +{ + zend_long flags; + zend_long csignal; + pid_t pid; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(flags) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(csignal) + ZEND_PARSE_PARAMETERS_END(); + + /* This is a flag to use with great caution in general, preferably not within PHP */ + if ((flags & RFMEM) != 0) { + flags &= ~RFMEM; + } + + /* A new pid is required */ + flags |= RFPROC; + + if (ZEND_NUM_ARGS() == 2 && (flags & RFTSIGZMB) != 0) { + flags |= RFTSIGFLAGS(csignal); + } + + pid = rfork(flags); + + if (pid == -1) { + PCNTL_G(last_error) = errno; + switch (errno) { + case EINVAL: + php_error_docref(NULL, E_WARNING, "RFFDG and RFCFDG modes are mutually exclusive"); + break; + case EAGAIN: + php_error_docref(NULL, E_WARNING, "Maximum process creations limit reached\n"); + break; + default: + php_error_docref(NULL, E_WARNING, "Error %d", errno); + } + } + + RETURN_LONG((zend_long) pid); +} +#endif +/* }}} */ + static void pcntl_interrupt_function(zend_execute_data *execute_data) { pcntl_signal_dispatch(); diff --git a/ext/pcntl/pcntl.stub.php b/ext/pcntl/pcntl.stub.php index 53c9b607ad8..770c14a9bcb 100644 --- a/ext/pcntl/pcntl.stub.php +++ b/ext/pcntl/pcntl.stub.php @@ -79,3 +79,7 @@ function pcntl_async_signals(?bool $enable = null): bool {} #ifdef HAVE_UNSHARE function pcntl_unshare(int $flags): bool {} #endif + +#ifdef HAVE_RFORK +function pcntl_rfork(int $flags, int $signal): int{} +#endif diff --git a/ext/pcntl/pcntl_arginfo.h b/ext/pcntl/pcntl_arginfo.h index 38d512631f7..b9863d5c87c 100644 --- a/ext/pcntl/pcntl_arginfo.h +++ b/ext/pcntl/pcntl_arginfo.h @@ -119,6 +119,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_unshare, 0, 1, _IS_BOOL, 0 ZEND_END_ARG_INFO() #endif +#if defined(HAVE_RFORK) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_rfork, 0, 1, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, signal, IS_LONG, 0) +ZEND_END_ARG_INFO() +#endif + ZEND_FUNCTION(pcntl_fork); ZEND_FUNCTION(pcntl_waitpid); @@ -158,6 +165,9 @@ ZEND_FUNCTION(pcntl_async_signals); #if defined(HAVE_UNSHARE) ZEND_FUNCTION(pcntl_unshare); #endif +#if defined(HAVE_RFORK) +ZEND_FUNCTION(pcntl_rfork); +#endif static const zend_function_entry ext_functions[] = { diff --git a/ext/pcntl/tests/pcntl_rfork.phpt b/ext/pcntl/tests/pcntl_rfork.phpt new file mode 100644 index 00000000000..c17d8264d3f --- /dev/null +++ b/ext/pcntl/tests/pcntl_rfork.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test function pcntl_rfork() with no flag. +--SKIPIF-- + +--FILE-- + 0) { + pcntl_wait($status); + var_dump($pid); +} else { + var_dump($pid); +} +?> +--EXPECTF-- +*** Test with no flags *** +int(0) +int(%d) diff --git a/ext/pcntl/tests/pcntl_rfork_nowait.phpt b/ext/pcntl/tests/pcntl_rfork_nowait.phpt new file mode 100644 index 00000000000..bfa3159a5bc --- /dev/null +++ b/ext/pcntl/tests/pcntl_rfork_nowait.phpt @@ -0,0 +1,24 @@ +--TEST-- +Test function pcntl_rfork() with no wait flag. +--SKIPIF-- + +--FILE-- + 0) { + var_dump($pid); +} else { + var_dump($pid); + sleep(1); +} +?> +--EXPECTF-- +*** Test by with child not reporting to the parent *** +int(%d) +int(0)