diff --git a/ext/pcntl/config.m4 b/ext/pcntl/config.m4 index 50dfd354e8e..148f1f11467 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 rfork]) + AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx]) 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 54ed5c4b90e..645b211952b 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -46,6 +46,10 @@ #include #endif +#ifdef HAVE_FORKX +#include +#endif + #ifndef NSIG # define NSIG 32 #endif @@ -364,6 +368,11 @@ void php_register_signal_constants(INIT_FUNC_ARGS) REGISTER_LONG_CONSTANT("RFTHREAD", RFTHREAD, CONST_CS | CONST_PERSISTENT); #endif #endif + +#ifdef HAVE_FORKX + REGISTER_LONG_CONSTANT("FORK_NOSIGCHLD", FORK_NOSIGCHLD, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("FORK_WAITPID", FORK_WAITPID, CONST_CS | CONST_PERSISTENT); +#endif } static void php_pcntl_register_errno_constants(INIT_FUNC_ARGS) @@ -1551,6 +1560,49 @@ PHP_FUNCTION(pcntl_rfork) #endif /* }}} */ +#ifdef HAVE_FORKX +/* {{{ proto bool pcntl_forkx(int flags) + More elaborated version of fork with the following settings. + FORK_WAITPID: forbid the parent process to wait for multiple pid but one only + FORK_NOSIGCHLD: SIGCHLD signal ignored when the child terminates */ +PHP_FUNCTION(pcntl_forkx) +{ + zend_long flags; + pid_t pid; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + if (flags < FORK_NOSIGCHLD || flags > FORK_WAITPID) { + zend_argument_value_error(1, "must be FORK_NOSIGCHLD or FORK_WAITPID"); + RETURN_THROWS(); + } + + pid = forkx(flags); + + if (pid == -1) { + PCNTL_G(last_error) = errno; + switch (errno) { + case EAGAIN: + php_error_docref(NULL, E_WARNING, "Maximum process creations limit reached\n"); + break; + case EPERM: + php_error_docref(NULL, E_WARNING, "Calling process not having the proper privileges\n"); + break; + case ENOMEM: + php_error_docref(NULL, E_WARNING, "No swap space left\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 a6b7552c935..d668b4d0662 100644 --- a/ext/pcntl/pcntl.stub.php +++ b/ext/pcntl/pcntl.stub.php @@ -83,3 +83,7 @@ function pcntl_unshare(int $flags): bool {} #ifdef HAVE_RFORK function pcntl_rfork(int $flags, int $signal = 0): int{} #endif +# +#ifdef HAVE_RFORK +function pcntl_forkx(int $flags): int{} +#endif diff --git a/ext/pcntl/pcntl_arginfo.h b/ext/pcntl/pcntl_arginfo.h index b513510286d..4caaff2bcff 100644 --- a/ext/pcntl/pcntl_arginfo.h +++ b/ext/pcntl/pcntl_arginfo.h @@ -125,6 +125,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_rfork, 0, 1, IS_LONG, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, signal, IS_LONG, 0, "0") ZEND_END_ARG_INFO() #endif + +#if defined(HAVE_FORKX) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_forkx, 0, 0, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, flags, IS_LONG, 0) +ZEND_END_ARG_INFO() +#endif ZEND_FUNCTION(pcntl_fork);