diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index 984dfcd8a256e..f647a22a3f5a4 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -376,6 +376,8 @@ dnl PHP_CHECK_FUNC(res_search, resolv, bind, socket) +PHP_CHECK_FUNC(posix_spawn_file_actions_addchdir_np) + dnl dnl Check for strptime() dnl diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index 182860720c608..264a7c6250cb0 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -36,6 +36,18 @@ #include #endif +#ifdef HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP +/* Only defined on glibc >= 2.29, FreeBSD CURRENT, musl >= 1.1.24, + * MacOS Catalina or later.. + * It should be posible to modify this so it is also + * used in older systems when $cwd == NULL but care must be taken + * as at least glibc < 2.24 has a legacy implementation known + * to be really buggy. + */ +#include +#define USE_POSIX_SPAWN +#endif + /* This symbol is defined in ext/standard/config.m4. * Essentially, it is set if you HAVE_FORK || PHP_WIN32 * Other platforms may modify that configure check and add suitable #ifdefs @@ -982,6 +994,36 @@ static zend_result set_proc_descriptor_from_resource(zval *resource, descriptors } #ifndef PHP_WIN32 +#if defined(USE_POSIX_SPAWN) +static zend_result close_parentends_of_pipes(posix_spawn_file_actions_t * actions, descriptorspec_item *descriptors, int ndesc) +{ + int r; + for (int i = 0; i < ndesc; i++) { + if (descriptors[i].type != DESCRIPTOR_TYPE_STD) { + r = posix_spawn_file_actions_addclose(actions, descriptors[i].parentend); + if (r != 0) { + php_error_docref(NULL, E_WARNING, "Cannot close file descriptor %d: %s", descriptors[i].parentend, strerror(r)); + return FAILURE; + } + } + if (descriptors[i].childend != descriptors[i].index) { + r = posix_spawn_file_actions_adddup2(actions, descriptors[i].childend, descriptors[i].index); + if (r != 0) { + php_error_docref(NULL, E_WARNING, "Unable to copy file descriptor %d (for pipe) into " + "file descriptor %d: %s", descriptors[i].childend, descriptors[i].index, strerror(r)); + return FAILURE; + } + r = posix_spawn_file_actions_addclose(actions, descriptors[i].childend); + if (r != 0) { + php_error_docref(NULL, E_WARNING, "Cannot close file descriptor %d: %s", descriptors[i].childend, strerror(r)); + return FAILURE; + } + } + } + + return SUCCESS; +} +#else static zend_result close_parentends_of_pipes(descriptorspec_item *descriptors, int ndesc) { /* We are running in child process @@ -1005,6 +1047,7 @@ static zend_result close_parentends_of_pipes(descriptorspec_item *descriptors, i return SUCCESS; } #endif +#endif static void close_all_descriptors(descriptorspec_item *descriptors, int ndesc) { @@ -1216,6 +1259,37 @@ PHP_FUNCTION(proc_open) childHandle = pi.hProcess; child = pi.dwProcessId; CloseHandle(pi.hThread); +#elif defined(USE_POSIX_SPAWN) + posix_spawn_file_actions_t factions; + int r; + posix_spawn_file_actions_init(&factions); + + if (close_parentends_of_pipes(&factions, descriptors, ndesc) == FAILURE) { + posix_spawn_file_actions_destroy(&factions); + close_all_descriptors(descriptors, ndesc); + goto exit_fail; + } + + if (cwd) { + r = posix_spawn_file_actions_addchdir_np(&factions, cwd); + if (r != 0) { + php_error_docref(NULL, E_WARNING, "posix_spawn_file_actions_addchdir_np() failed: %s", strerror(r)); + } + } + + if (argv) { + r = posix_spawnp(&child, ZSTR_VAL(command_str), &factions, NULL, argv, (env.envarray ? env.envarray : environ)); + } else { + r = posix_spawn(&child, "/bin/sh" , &factions, NULL, + (char * const[]) {"sh", "-c", ZSTR_VAL(command_str), NULL}, + env.envarray ? env.envarray : environ); + } + posix_spawn_file_actions_destroy(&factions); + if (r != 0) { + close_all_descriptors(descriptors, ndesc); + php_error_docref(NULL, E_WARNING, "posix_spawn() failed: %s", strerror(r)); + goto exit_fail; + } #elif HAVE_FORK /* the Unix way */ child = fork();