diff --git a/ext/pcntl/config.m4 b/ext/pcntl/config.m4 index 881a0da688c87..7e7a389615a88 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 forkx]) + AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx pidfd_open]) AC_CHECK_TYPE([siginfo_t],[PCNTL_CFLAGS="-DHAVE_STRUCT_SIGINFO_T"],,[#include ]) diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 79b51967f3eaf..b19edb0ddb928 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -46,6 +46,10 @@ #include #endif +#ifdef HAVE_PIDFD_OPEN +#include +#endif + #ifdef HAVE_FORKX #include #endif @@ -1402,6 +1406,76 @@ PHP_FUNCTION(pcntl_forkx) #endif /* }}} */ +#ifdef HAVE_PIDFD_OPEN +// The `pidfd_open` syscall is available since 5.3 +// and `setns` since 3.0. +PHP_FUNCTION(pcntl_setns) +{ + zend_long pid, nstype = CLONE_NEWNET; + bool pid_is_null = 1; + int fd, ret; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(pid, pid_is_null) + Z_PARAM_LONG(nstype) + ZEND_PARSE_PARAMETERS_END(); + + pid = pid_is_null ? getpid() : pid; + fd = syscall(SYS_pidfd_open, pid, 0); + if (errno) { + PCNTL_G(last_error) = errno; + switch (errno) { + case EINVAL: + case ESRCH: + zend_argument_value_error(1, "is not a valid process (%d)", pid); + RETURN_THROWS(); + + case ENFILE: + php_error_docref(NULL, E_WARNING, "Error %d: File descriptors per-process limit reached", errno); + break; + + case ENODEV: + php_error_docref(NULL, E_WARNING, "Error %d: Anonymous inode fs unsupported", errno); + break; + + case ENOMEM: + php_error_docref(NULL, E_WARNING, "Error %d: Insufficient memory for pidfd_open", errno); + break; + + default: + php_error_docref(NULL, E_WARNING, "Error %d", errno); + } + RETURN_FALSE; + } + ret = setns(fd, (int)nstype); + close(fd); + + if (ret == -1) { + PCNTL_G(last_error) = errno; + switch (errno) { + case ESRCH: + zend_argument_value_error(1, "process no longer available (%d)", pid); + RETURN_THROWS(); + + case EINVAL: + zend_argument_value_error(2, "is an invalid nstype (%d)", nstype); + RETURN_THROWS(); + + case EPERM: + php_error_docref(NULL, E_WARNING, "Error %d: No required capability for this process", errno); + break; + + default: + php_error_docref(NULL, E_WARNING, "Error %d", errno); + } + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +#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 7a1aabaacdeeb..09e318307d054 100644 --- a/ext/pcntl/pcntl.stub.php +++ b/ext/pcntl/pcntl.stub.php @@ -990,3 +990,7 @@ function pcntl_rfork(int $flags, int $signal = 0): int{} #ifdef HAVE_FORKX function pcntl_forkx(int $flags): int{} #endif + +#ifdef HAVE_PIDFD_OPEN +function pcntl_setns(int $process_id = null, int $nstype = CLONE_NEWNET): bool {} +#endif diff --git a/ext/pcntl/pcntl_arginfo.h b/ext/pcntl/pcntl_arginfo.h index 8cb96afee8d7f..c86c1d815b5e7 100644 --- a/ext/pcntl/pcntl_arginfo.h +++ b/ext/pcntl/pcntl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3b03373d1bb68de779baaa62db14d98ca9018339 */ + * Stub hash: 614bd67bb4cfcdc68d37141ff9dfad0a49c318b5 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_fork, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -132,6 +132,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_forkx, 0, 1, IS_LONG, 0) ZEND_END_ARG_INFO() #endif +#if defined(HAVE_PIDFD_OPEN) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_setns, 0, 0, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, process_id, IS_LONG, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nstype, IS_LONG, 0, "CLONE_NEWNET") +ZEND_END_ARG_INFO() +#endif + ZEND_FUNCTION(pcntl_fork); ZEND_FUNCTION(pcntl_waitpid); ZEND_FUNCTION(pcntl_wait); @@ -176,6 +183,9 @@ ZEND_FUNCTION(pcntl_rfork); #if defined(HAVE_FORKX) ZEND_FUNCTION(pcntl_forkx); #endif +#if defined(HAVE_PIDFD_OPEN) +ZEND_FUNCTION(pcntl_setns); +#endif static const zend_function_entry ext_functions[] = { ZEND_FE(pcntl_fork, arginfo_pcntl_fork) @@ -222,6 +232,9 @@ static const zend_function_entry ext_functions[] = { #endif #if defined(HAVE_FORKX) ZEND_FE(pcntl_forkx, arginfo_pcntl_forkx) +#endif +#if defined(HAVE_PIDFD_OPEN) + ZEND_FE(pcntl_setns, arginfo_pcntl_setns) #endif ZEND_FE_END }; diff --git a/ext/pcntl/tests/pcntl_setns_basic.phpt b/ext/pcntl/tests/pcntl_setns_basic.phpt new file mode 100644 index 0000000000000..a4f2ea773c57f --- /dev/null +++ b/ext/pcntl/tests/pcntl_setns_basic.phpt @@ -0,0 +1,24 @@ +--TEST-- +pcntl_setns() +--EXTENSIONS-- +pcntl +posix +--SKIPIF-- + +--FILE-- +getMessage(); + } +} +?> +--EXPECTF-- +pcntl_setns(): Argument #2 ($nstype) is an invalid nstype (%d) diff --git a/ext/pcntl/tests/pcntl_setns_newpid.phpt b/ext/pcntl/tests/pcntl_setns_newpid.phpt new file mode 100644 index 0000000000000..79d8a426f041f --- /dev/null +++ b/ext/pcntl/tests/pcntl_setns_newpid.phpt @@ -0,0 +1,21 @@ +--TEST-- +pcntl_setns() +--EXTENSIONS-- +pcntl +posix +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true)