summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/standard/proc_open.c329
-rw-r--r--ext/standard/proc_open.h15
-rw-r--r--ext/standard/tests/general_functions/proc_open_pipes3.phpt2
3 files changed, 189 insertions, 157 deletions
diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c
index 5a574cb2ff..f1ecfd932e 100644
--- a/ext/standard/proc_open.c
+++ b/ext/standard/proc_open.c
@@ -47,27 +47,30 @@
# if HAVE_PTY_H
# include <pty.h>
# else
-/* Mac OS X defines openpty() in <util.h> */
+/* Mac OS X defines `openpty` in <util.h> */
# include <util.h>
# endif
#endif
#include "proc_open.h"
-static int le_proc_open;
+static int le_proc_open; /* Resource number for `proc` resources */
-/* {{{ _php_array_to_envp */
-static php_process_env_t _php_array_to_envp(zval *environment)
+/* {{{ _php_array_to_envp
+ * Process the `environment` argument to `proc_open`
+ * Convert into data structures which can be passed to underlying OS APIs like `exec` on POSIX or
+ * `CreateProcessW` on Win32 */
+static php_process_env _php_array_to_envp(zval *environment)
{
zval *element;
- php_process_env_t env;
+ php_process_env env;
zend_string *key, *str;
#ifndef PHP_WIN32
char **ep;
#endif
char *p;
size_t cnt, sizeenv = 0;
- HashTable *env_hash;
+ HashTable *env_hash; /* temporary PHP array used as helper */
memset(&env, 0, sizeof(env));
@@ -139,8 +142,9 @@ static php_process_env_t _php_array_to_envp(zval *environment)
}
/* }}} */
-/* {{{ _php_free_envp */
-static void _php_free_envp(php_process_env_t env)
+/* {{{ _php_free_envp
+ * Free the structures allocated by `_php_array_to_envp` */
+static void _php_free_envp(php_process_env env)
{
#ifndef PHP_WIN32
if (env.envarray) {
@@ -153,10 +157,12 @@ static void _php_free_envp(php_process_env_t env)
}
/* }}} */
-/* {{{ proc_open_rsrc_dtor */
+/* {{{ proc_open_rsrc_dtor
+ * Free `proc` resource, either because all references to it were dropped or because `pclose` or
+ * `proc_close` were called */
static void proc_open_rsrc_dtor(zend_resource *rsrc)
{
- struct php_process_handle *proc = (struct php_process_handle*)rsrc->ptr;
+ php_process_handle *proc = (php_process_handle*)rsrc->ptr;
#ifdef PHP_WIN32
DWORD wstatus;
#elif HAVE_SYS_WAIT_H
@@ -174,6 +180,10 @@ static void proc_open_rsrc_dtor(zend_resource *rsrc)
}
}
+ /* `pclose_wait` tells us: Are we freeing this resource because `pclose` or `proc_close` were
+ * called? If so, we need to wait until the child process exits, because its exit code is
+ * needed as the return value of those functions.
+ * But if we're freeing the resource because of GC, don't wait. */
#ifdef PHP_WIN32
if (FG(pclose_wait)) {
WaitForSingleObject(proc->childHandle, INFINITE);
@@ -187,7 +197,6 @@ static void proc_open_rsrc_dtor(zend_resource *rsrc)
CloseHandle(proc->childHandle);
#elif HAVE_SYS_WAIT_H
-
if (!FG(pclose_wait)) {
waitpid_options = WNOHANG;
}
@@ -198,36 +207,38 @@ static void proc_open_rsrc_dtor(zend_resource *rsrc)
if (wait_pid <= 0) {
FG(pclose_ret) = -1;
} else {
- if (WIFEXITED(wstatus))
+ if (WIFEXITED(wstatus)) {
wstatus = WEXITSTATUS(wstatus);
+ }
FG(pclose_ret) = wstatus;
}
#else
FG(pclose_ret) = -1;
#endif
+
_php_free_envp(proc->env);
efree(proc->pipes);
efree(proc->command);
efree(proc);
-
}
/* }}} */
/* {{{ PHP_MINIT_FUNCTION(proc_open) */
PHP_MINIT_FUNCTION(proc_open)
{
- le_proc_open = zend_register_list_destructors_ex(proc_open_rsrc_dtor, NULL, "process", module_number);
+ le_proc_open = zend_register_list_destructors_ex(proc_open_rsrc_dtor, NULL, "process",
+ module_number);
return SUCCESS;
}
/* }}} */
/* {{{ proto bool proc_terminate(resource process [, int signal])
- kill a process opened by proc_open */
+ Kill a process opened by `proc_open` */
PHP_FUNCTION(proc_terminate)
{
zval *zproc;
- struct php_process_handle *proc;
+ php_process_handle *proc;
zend_long sig_no = SIGTERM;
ZEND_PARSE_PARAMETERS_START(1, 2)
@@ -236,42 +247,36 @@ PHP_FUNCTION(proc_terminate)
Z_PARAM_LONG(sig_no)
ZEND_PARSE_PARAMETERS_END();
- if ((proc = (struct php_process_handle *)zend_fetch_resource(Z_RES_P(zproc), "process", le_proc_open)) == NULL) {
+ proc = (php_process_handle*)zend_fetch_resource(Z_RES_P(zproc), "process", le_proc_open);
+ if (proc == NULL) {
RETURN_THROWS();
}
#ifdef PHP_WIN32
- if (TerminateProcess(proc->childHandle, 255)) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(TerminateProcess(proc->childHandle, 255));
#else
- if (kill(proc->child, sig_no) == 0) {
- RETURN_TRUE;
- } else {
- RETURN_FALSE;
- }
+ RETURN_BOOL(kill(proc->child, sig_no) == 0);
#endif
}
/* }}} */
/* {{{ proto int|false proc_close(resource process)
- close a process opened by proc_open */
+ Close a process opened by `proc_open` */
PHP_FUNCTION(proc_close)
{
zval *zproc;
- struct php_process_handle *proc;
+ php_process_handle *proc;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_RESOURCE(zproc)
ZEND_PARSE_PARAMETERS_END();
- if ((proc = (struct php_process_handle *)zend_fetch_resource(Z_RES_P(zproc), "process", le_proc_open)) == NULL) {
+ proc = (php_process_handle*)zend_fetch_resource(Z_RES_P(zproc), "process", le_proc_open);
+ if (proc == NULL) {
RETURN_THROWS();
}
- FG(pclose_wait) = 1;
+ FG(pclose_wait) = 1; /* See comment in `proc_open_rsrc_dtor` */
zend_list_close(Z_RES_P(zproc));
FG(pclose_wait) = 0;
RETURN_LONG(FG(pclose_ret));
@@ -279,11 +284,11 @@ PHP_FUNCTION(proc_close)
/* }}} */
/* {{{ proto array|false proc_get_status(resource process)
- get information about a process opened by proc_open */
+ Get information about a process opened by `proc_open` */
PHP_FUNCTION(proc_get_status)
{
zval *zproc;
- struct php_process_handle *proc;
+ php_process_handle *proc;
#ifdef PHP_WIN32
DWORD wstatus;
#elif HAVE_SYS_WAIT_H
@@ -297,25 +302,21 @@ PHP_FUNCTION(proc_get_status)
Z_PARAM_RESOURCE(zproc)
ZEND_PARSE_PARAMETERS_END();
- if ((proc = (struct php_process_handle *)zend_fetch_resource(Z_RES_P(zproc), "process", le_proc_open)) == NULL) {
+ proc = (php_process_handle*)zend_fetch_resource(Z_RES_P(zproc), "process", le_proc_open);
+ if (proc == NULL) {
RETURN_THROWS();
}
array_init(return_value);
-
add_assoc_string(return_value, "command", proc->command);
- add_assoc_long(return_value, "pid", (zend_long) proc->child);
+ add_assoc_long(return_value, "pid", (zend_long)proc->child);
#ifdef PHP_WIN32
-
GetExitCodeProcess(proc->childHandle, &wstatus);
-
running = wstatus == STILL_ACTIVE;
exitcode = running ? -1 : wstatus;
#elif HAVE_SYS_WAIT_H
-
- errno = 0;
wait_pid = waitpid(proc->child, &wstatus, WNOHANG|WUNTRACED);
if (wait_pid == proc->child) {
@@ -326,7 +327,6 @@ PHP_FUNCTION(proc_get_status)
if (WIFSIGNALED(wstatus)) {
running = 0;
signaled = 1;
-
termsig = WTERMSIG(wstatus);
}
if (WIFSTOPPED(wstatus)) {
@@ -334,6 +334,8 @@ PHP_FUNCTION(proc_get_status)
stopsig = WSTOPSIG(wstatus);
}
} else if (wait_pid == -1) {
+ /* The only error which could occur here is ECHILD, which means that the PID we were
+ * looking for either does not exist or is not a child of this process */
running = 0;
}
#endif
@@ -347,10 +349,11 @@ PHP_FUNCTION(proc_get_status)
}
/* }}} */
-/* {{{ handy definitions for portability/readability */
#ifdef PHP_WIN32
-/* we use this to allow child processes to inherit handles */
+/* We use this to allow child processes to inherit handles
+ * One static instance can be shared and used for all calls to `proc_open`, since the values are
+ * never changed */
SECURITY_ATTRIBUTES php_proc_open_security = {
.nLength = sizeof(SECURITY_ATTRIBUTES),
.lpSecurityDescriptor = NULL,
@@ -377,17 +380,21 @@ static inline HANDLE dup_fd_as_handle(int fd)
}
# define close_descriptor(fd) CloseHandle(fd)
-#else
+#else /* !PHP_WIN32 */
# define close_descriptor(fd) close(fd)
#endif
-struct php_proc_open_descriptor_item {
- int index; /* desired FD number in child process */
+/* One instance of this struct is created for each item in `$descriptorspec` argument to `proc_open`
+ * They are used within `proc_open` and freed before it returns */
+typedef struct _descriptorspec_item {
+ int index; /* desired FD # in child process */
int is_pipe;
- php_file_descriptor_t parentend, childend; /* FDs for pipes in parent/child */
- int mode_flags; /* mode flags for opening FDs */
-};
-/* }}} */
+ php_file_descriptor_t childend; /* FD # opened for use in child
+ * (will be copied to `index` in child) */
+ php_file_descriptor_t parentend; /* FD # opened for use in parent
+ * (for pipes only; will be 0 otherwise) */
+ int mode_flags; /* mode for opening FDs: r/o, r/w, binary (on Win32), etc */
+} descriptorspec_item;
static zend_string *get_valid_arg_string(zval *zv, int elem_num) {
zend_string *str = zval_get_string(zv);
@@ -463,18 +470,20 @@ static char *create_win_command_from_args(HashTable *args)
return str.c;
}
-static int get_option(zval *other_options, char *option_name)
+/* Get a boolean option from the `other_options` array which can be passed to `proc_open`.
+ * (Currently, all options apply on Windows only.) */
+static int get_option(zval *other_options, char *opt_name)
{
- zval *item = zend_hash_str_find_deref(Z_ARRVAL_P(other_options), option_name, strlen(option_name));
- if (item != NULL) {
- if (Z_TYPE_P(item) == IS_TRUE || ((Z_TYPE_P(item) == IS_LONG) && Z_LVAL_P(item))) {
- return 1;
- }
- }
- return 0;
+ HashTable *opt_ary = Z_ARRVAL_P(other_options);
+ zval *item = zend_hash_str_find_deref(opt_ary, opt_name, strlen(opt_name));
+ return item != NULL &&
+ (Z_TYPE_P(item) == IS_TRUE ||
+ ((Z_TYPE_P(item) == IS_LONG) && Z_LVAL_P(item)));
}
-static void init_startup_info(STARTUPINFOW *si, struct php_proc_open_descriptor_item *descriptors, int ndesc)
+/* Initialize STARTUPINFOW struct, used on Windows when spawning a process.
+ * Ref: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow */
+static void init_startup_info(STARTUPINFOW *si, descriptorspec_item *descriptors, int ndesc)
{
memset(si, 0, sizeof(STARTUPINFOW));
si->cb = sizeof(STARTUPINFOW);
@@ -528,6 +537,7 @@ static int convert_command_to_use_shell(wchar_t **cmdw, size_t cmdw_len)
}
#endif
+/* Convert command parameter array passed as first argument to `proc_open` into command string */
static char* get_command_from_array(zval *array, char ***argv, int num_elems)
{
zval *arg_zv;
@@ -539,7 +549,7 @@ static char* get_command_from_array(zval *array, char ***argv, int num_elems)
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), arg_zv) {
zend_string *arg_str = get_valid_arg_string(arg_zv, i + 1);
if (!arg_str) {
- /* terminate argv with NULL so exit_fail code knows how many entries to free */
+ /* Terminate with NULL so exit_fail code knows how many entries to free */
(*argv)[i] = NULL;
if (command != NULL) {
efree(command);
@@ -559,10 +569,10 @@ static char* get_command_from_array(zval *array, char ***argv, int num_elems)
return command;
}
-static struct php_proc_open_descriptor_item* alloc_descriptor_array(zval *descriptorspec)
+static descriptorspec_item* alloc_descriptor_array(zval *descriptorspec)
{
int ndescriptors = zend_hash_num_elements(Z_ARRVAL_P(descriptorspec));
- return ecalloc(sizeof(struct php_proc_open_descriptor_item), ndescriptors);
+ return ecalloc(sizeof(descriptorspec_item), ndescriptors);
}
static zend_string* get_string_parameter(zval *array, int index, char *param_name)
@@ -575,12 +585,11 @@ static zend_string* get_string_parameter(zval *array, int index, char *param_nam
return zval_try_get_string(array_item);
}
-static int set_proc_descriptor_to_blackhole(struct php_proc_open_descriptor_item *desc)
+static int set_proc_descriptor_to_blackhole(descriptorspec_item *desc)
{
#ifdef PHP_WIN32
- desc->childend = CreateFileA(
- "nul", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
- NULL, OPEN_EXISTING, 0, NULL);
+ desc->childend = CreateFileA("nul", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (desc->childend == NULL) {
php_error_docref(NULL, E_WARNING, "Failed to open nul");
return FAILURE;
@@ -588,19 +597,24 @@ static int set_proc_descriptor_to_blackhole(struct php_proc_open_descriptor_item
#else
desc->childend = open("/dev/null", O_RDWR);
if (desc->childend < 0) {
- php_error_docref(NULL, E_WARNING, "Failed to open /dev/null - %s", strerror(errno));
+ php_error_docref(NULL, E_WARNING, "Failed to open /dev/null: %s", strerror(errno));
return FAILURE;
}
#endif
return SUCCESS;
}
-static int set_proc_descriptor_to_pty(struct php_proc_open_descriptor_item *desc, int *master_fd, int *slave_fd)
+static int set_proc_descriptor_to_pty(descriptorspec_item *desc, int *master_fd, int *slave_fd)
{
#if HAVE_OPENPTY
+ /* All FDs set to PTY in the child process will go to the slave end of the same PTY.
+ * Likewise, all the corresponding entries in `$pipes` in the parent will all go to the master
+ * end of the same PTY.
+ * If this is the first descriptorspec set to 'pty', find an available PTY and get master and
+ * slave FDs. */
if (*master_fd == -1) {
if (openpty(master_fd, slave_fd, NULL, NULL, NULL)) {
- php_error_docref(NULL, E_WARNING, "Could not open PTY (pseudoterminal) - %s", strerror(errno));
+ php_error_docref(NULL, E_WARNING, "Could not open PTY (pseudoterminal): %s", strerror(errno));
return FAILURE;
}
}
@@ -616,7 +630,7 @@ static int set_proc_descriptor_to_pty(struct php_proc_open_descriptor_item *desc
#endif
}
-static int set_proc_descriptor_to_pipe(struct php_proc_open_descriptor_item *desc, zend_string *zmode)
+static int set_proc_descriptor_to_pipe(descriptorspec_item *desc, zend_string *zmode)
{
php_file_descriptor_t newpipe[2];
@@ -648,19 +662,21 @@ static int set_proc_descriptor_to_pipe(struct php_proc_open_descriptor_item *des
return SUCCESS;
}
-static int set_proc_descriptor_to_file(struct php_proc_open_descriptor_item *desc, zend_string *zfile, zend_string *zmode)
+static int set_proc_descriptor_to_file(descriptorspec_item *desc, zend_string *file_path,
+ zend_string *file_mode)
{
php_socket_t fd;
/* try a wrapper */
- php_stream *stream = php_stream_open_wrapper(ZSTR_VAL(zfile), ZSTR_VAL(zmode),
- REPORT_ERRORS|STREAM_WILL_CAST, NULL);
+ php_stream *stream = php_stream_open_wrapper(ZSTR_VAL(file_path), ZSTR_VAL(file_mode),
+ REPORT_ERRORS|STREAM_WILL_CAST, NULL);
if (stream == NULL) {
return FAILURE;
}
/* force into an fd */
- if (php_stream_cast(stream, PHP_STREAM_CAST_RELEASE|PHP_STREAM_AS_FD, (void **)&fd, REPORT_ERRORS) == FAILURE) {
+ if (php_stream_cast(stream, PHP_STREAM_CAST_RELEASE|PHP_STREAM_AS_FD, (void **)&fd,
+ REPORT_ERRORS) == FAILURE) {
return FAILURE;
}
@@ -668,9 +684,9 @@ static int set_proc_descriptor_to_file(struct php_proc_open_descriptor_item *des
desc->childend = dup_fd_as_handle((int)fd);
_close((int)fd);
- /* simulate the append mode by fseeking to the end of the file
- this introduces a potential race-condition, but it is the best we can do, though */
- if (strchr(ZSTR_VAL(zmode), 'a')) {
+ /* Simulate the append mode by fseeking to the end of the file
+ * This introduces a potential race condition, but it is the best we can do */
+ if (strchr(ZSTR_VAL(file_mode), 'a')) {
SetFilePointer(desc->childend, 0, NULL, FILE_END);
}
#else
@@ -679,7 +695,8 @@ static int set_proc_descriptor_to_file(struct php_proc_open_descriptor_item *des
return SUCCESS;
}
-static int dup_proc_descriptor(php_file_descriptor_t from, php_file_descriptor_t *to, zend_ulong nindex)
+static int dup_proc_descriptor(php_file_descriptor_t from, php_file_descriptor_t *to,
+ zend_ulong nindex)
{
#ifdef PHP_WIN32
*to = dup_handle(from, TRUE, FALSE);
@@ -690,15 +707,16 @@ static int dup_proc_descriptor(php_file_descriptor_t from, php_file_descriptor_t
#else
*to = dup(from);
if (*to < 0) {
- php_error_docref(NULL, E_WARNING,
- "Failed to dup() for descriptor " ZEND_LONG_FMT " - %s", nindex, strerror(errno));
+ php_error_docref(NULL, E_WARNING, "Failed to dup() for descriptor " ZEND_LONG_FMT ": %s",
+ nindex, strerror(errno));
return FAILURE;
}
#endif
return SUCCESS;
}
-static int redirect_proc_descriptor(struct php_proc_open_descriptor_item *desc, int target, struct php_proc_open_descriptor_item *descriptors, int ndesc, int nindex)
+static int redirect_proc_descriptor(descriptorspec_item *desc, int target,
+ descriptorspec_item *descriptors, int ndesc, int nindex)
{
php_file_descriptor_t redirect_to = PHP_INVALID_FD;
@@ -709,7 +727,7 @@ static int redirect_proc_descriptor(struct php_proc_open_descriptor_item *desc,
}
}
- if (redirect_to == PHP_INVALID_FD) { /* didn't find the index we wanted */
+ if (redirect_to == PHP_INVALID_FD) { /* Didn't find the index we wanted */
if (target < 0 || target > 2) {
php_error_docref(NULL, E_WARNING, "Redirection target %d not found", target);
return FAILURE;
@@ -732,9 +750,9 @@ static int redirect_proc_descriptor(struct php_proc_open_descriptor_item *desc,
return dup_proc_descriptor(redirect_to, &desc->childend, nindex);
}
-int set_proc_descriptor_from_array(
- zval *descitem, struct php_proc_open_descriptor_item *descriptors, int ndesc,
- int nindex, int *pty_master_fd, int *pty_slave_fd) {
+/* Process one item from `$descriptorspec` argument to `proc_open` */
+static int set_proc_descriptor_from_array(zval *descitem, descriptorspec_item *descriptors,
+ int ndesc, int nindex, int *pty_master_fd, int *pty_slave_fd) {
zend_string *ztype = get_string_parameter(descitem, 0, "handle qualifier");
if (!ztype) {
return FAILURE;
@@ -743,21 +761,23 @@ int set_proc_descriptor_from_array(
zend_string *zmode = NULL, *zfile = NULL;
int retval = FAILURE;
if (zend_string_equals_literal(ztype, "pipe")) {
- if ((zmode = get_string_parameter(descitem, 1, "mode parameter for 'pipe'")) == NULL) {
+ /* Set descriptor to pipe */
+ zmode = get_string_parameter(descitem, 1, "mode parameter for 'pipe'");
+ if (zmode == NULL) {
goto finish;
}
-
retval = set_proc_descriptor_to_pipe(&descriptors[ndesc], zmode);
} else if (zend_string_equals_literal(ztype, "file")) {
+ /* Set descriptor to file */
if ((zfile = get_string_parameter(descitem, 1, "file name parameter for 'file'")) == NULL) {
goto finish;
}
if ((zmode = get_string_parameter(descitem, 2, "mode parameter for 'file'")) == NULL) {
goto finish;
}
-
retval = set_proc_descriptor_to_file(&descriptors[ndesc], zfile, zmode);
} else if (zend_string_equals_literal(ztype, "redirect")) {
+ /* Redirect descriptor to whatever another descriptor is set to */
zval *ztarget = zend_hash_index_find_deref(Z_ARRVAL_P(descitem), 1);
if (!ztarget) {
zend_value_error("Missing redirection target");
@@ -771,8 +791,10 @@ int set_proc_descriptor_from_array(
retval = redirect_proc_descriptor(
&descriptors[ndesc], Z_LVAL_P(ztarget), descriptors, ndesc, nindex);
} else if (zend_string_equals_literal(ztype, "null")) {
+ /* Set descriptor to blackhole (discard all data written) */
retval = set_proc_descriptor_to_blackhole(&descriptors[ndesc]);
} else if (zend_string_equals_literal(ztype, "pty")) {
+ /* Set descriptor to slave end of PTY */
retval = set_proc_descriptor_to_pty(&descriptors[ndesc], pty_master_fd, pty_slave_fd);
} else {
php_error_docref(NULL, E_WARNING, "%s is not a valid descriptor spec/mode", ZSTR_VAL(ztype));
@@ -786,20 +808,47 @@ finish:
return retval;
}
-static int close_parent_ends_of_pipes_in_child(struct php_proc_open_descriptor_item *descriptors, int ndesc)
+static int set_proc_descriptor_from_resource(zval *resource, descriptorspec_item *desc, int nindex)
{
- /* we are running in child process
- * close the 'parent end' of all pipes which were opened before forking/spawning
- * also, dup() the child end of all pipes as necessary so they will use the FD number
- * which the user requested */
+ /* Should be a stream - try and dup the descriptor */
+ php_stream *stream = (php_stream*)zend_fetch_resource(Z_RES_P(resource), "stream",
+ php_file_le_stream());
+ if (stream == NULL) {
+ return FAILURE;
+ }
+
+ php_socket_t fd;
+ int status = php_stream_cast(stream, PHP_STREAM_AS_FD, (void **)&fd, REPORT_ERRORS);
+ if (status == FAILURE) {
+ return FAILURE;
+ }
+
+#ifdef PHP_WIN32
+ php_file_descriptor_t fd_t = (HANDLE)_get_osfhandle(fd);
+#else
+ php_file_descriptor_t fd_t = fd;
+#endif
+ if (dup_proc_descriptor(fd_t, &desc->childend, nindex) == FAILURE) {
+ return FAILURE;
+ }
+
+ return SUCCESS;
+}
+
+static int close_parentends_of_pipes(descriptorspec_item *descriptors, int ndesc)
+{
+ /* We are running in child process
+ * Close the 'parent end' of pipes which were opened before forking/spawning
+ * Also, dup() the child end of all pipes as necessary so they will use the FD
+ * number which the user requested */
for (int i = 0; i < ndesc; i++) {
if (descriptors[i].is_pipe) {
close(descriptors[i].parentend);
}
if (descriptors[i].childend != descriptors[i].index) {
if (dup2(descriptors[i].childend, descriptors[i].index) < 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(errno));
+ 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(errno));
return FAILURE;
}
close(descriptors[i].childend);
@@ -809,7 +858,7 @@ static int close_parent_ends_of_pipes_in_child(struct php_proc_open_descriptor_i
return SUCCESS;
}
-static void close_all_descriptors(struct php_proc_open_descriptor_item *descriptors, int ndesc)
+static void close_all_descriptors(descriptorspec_item *descriptors, int ndesc)
{
for (int i = 0; i < ndesc; i++) {
close_descriptor(descriptors[i].childend);
@@ -831,23 +880,22 @@ static void efree_argv(char **argv)
}
/* {{{ proto resource|false proc_open(string|array command, array descriptorspec, array &pipes [, string cwd [, array env [, array other_options]]])
- Run a process with more control over it's file descriptors */
+ Execute a command, with specified files used for input/output */
PHP_FUNCTION(proc_open)
{
- zval *command_zv;
- char *command = NULL, *cwd = NULL;
- size_t cwd_len = 0;
- zval *descriptorspec;
- zval *pipes;
- zval *environment = NULL;
- zval *other_options = NULL;
- php_process_env_t env;
+ zval *command_zv, *descriptorspec, *pipes; /* Mandatory arguments */
+ char *cwd = NULL; /* Optional argument */
+ size_t cwd_len = 0; /* Optional argument */
+ zval *environment = NULL, *other_options = NULL; /* Optional arguments */
+
+ char *command = NULL;
+ php_process_env env;
int ndesc = 0;
int i;
zval *descitem = NULL;
zend_string *str_index;
zend_ulong nindex;
- struct php_proc_open_descriptor_item *descriptors = NULL;
+ descriptorspec_item *descriptors = NULL;
#ifdef PHP_WIN32
PROCESS_INFORMATION pi;
HANDLE childHandle;
@@ -868,7 +916,7 @@ PHP_FUNCTION(proc_open)
#endif
int pty_master_fd = -1, pty_slave_fd = -1;
php_process_id_t child;
- struct php_process_handle *proc;
+ php_process_handle *proc;
ZEND_PARSE_PARAMETERS_START(3, 6)
Z_PARAM_ZVAL(command_zv)
@@ -890,13 +938,15 @@ PHP_FUNCTION(proc_open)
}
#ifdef PHP_WIN32
+ /* Automatically bypass shell if command is given as an array */
bypass_shell = 1;
command = create_win_command_from_args(Z_ARRVAL_P(command_zv));
if (!command) {
RETURN_FALSE;
}
#else
- if ((command = get_command_from_array(command_zv, &argv, num_elems)) == NULL) {
+ command = get_command_from_array(command_zv, &argv, num_elems);
+ if (command == NULL) {
goto exit_fail;
}
#endif
@@ -908,7 +958,7 @@ PHP_FUNCTION(proc_open)
#ifdef PHP_WIN32
if (other_options) {
suppress_errors = get_option(other_options, "suppress_errors");
- /* TODO Deprecate in favor of array command? */
+ /* TODO: Deprecate in favor of array command? */
bypass_shell = bypass_shell || get_option(other_options, "bypass_shell");
blocking_pipes = get_option(other_options, "blocking_pipes");
create_process_group = get_option(other_options, "create_process_group");
@@ -922,7 +972,7 @@ PHP_FUNCTION(proc_open)
descriptors = alloc_descriptor_array(descriptorspec);
- /* walk the descriptor spec and set up files/pipes */
+ /* Walk the descriptor spec and set up files/pipes */
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(descriptorspec), nindex, str_index, descitem) {
if (str_index) {
zend_argument_value_error(2, "must be an integer indexed array");
@@ -932,34 +982,16 @@ PHP_FUNCTION(proc_open)
descriptors[ndesc].index = (int)nindex;
if (Z_TYPE_P(descitem) == IS_RESOURCE) {
- /* should be a stream - try and dup the descriptor */
- php_stream *stream = (php_stream*)zend_fetch_resource(Z_RES_P(descitem), "stream", php_file_le_stream());
- if (stream == NULL) {
- goto exit_fail;
- }
-
- php_socket_t fd;
- php_file_descriptor_t desc;
-
- if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_FD, (void **)&fd, REPORT_ERRORS)) {
- goto exit_fail;
- }
-
-#ifdef PHP_WIN32
- desc = (HANDLE)_get_osfhandle(fd);
-#else
- desc = fd;
-#endif
- if (dup_proc_descriptor(desc, &descriptors[ndesc].childend, nindex) == FAILURE) {
+ if (set_proc_descriptor_from_resource(descitem, &descriptors[ndesc], ndesc) == FAILURE) {
goto exit_fail;
}
} else if (Z_TYPE_P(descitem) == IS_ARRAY) {
- if (set_proc_descriptor_from_array(descitem, descriptors, ndesc, nindex,
- &pty_master_fd, &pty_slave_fd) == FAILURE) {
+ if (set_proc_descriptor_from_array(descitem, descriptors, ndesc, nindex, &pty_master_fd,
+ &pty_slave_fd) == FAILURE) {
goto exit_fail;
}
} else {
- zend_argument_value_error(2, "must only contain arrays and File-Handles");
+ zend_argument_value_error(2, "must only contain arrays and streams");
goto exit_fail;
}
ndesc++;
@@ -1018,17 +1050,17 @@ PHP_FUNCTION(proc_open)
goto exit_fail;
}
}
- newprocok = CreateProcessW(NULL, cmdw, &php_proc_open_security, &php_proc_open_security,
- TRUE, dwCreateFlags, envpw, cwdw, &si, &pi);
+ newprocok = CreateProcessW(NULL, cmdw, &php_proc_open_security,
+ &php_proc_open_security, TRUE, dwCreateFlags, envpw, cwdw, &si, &pi);
if (suppress_errors) {
SetErrorMode(old_error_mode);
}
- if (FALSE == newprocok) {
+ if (newprocok == FALSE) {
DWORD dw = GetLastError();
close_all_descriptors(descriptors, ndesc);
- php_error_docref(NULL, E_WARNING, "CreateProcess failed, error code - %u", dw);
+ php_error_docref(NULL, E_WARNING, "CreateProcess failed, error code: %u", dw);
goto exit_fail;
}
@@ -1036,15 +1068,15 @@ PHP_FUNCTION(proc_open)
child = pi.dwProcessId;
CloseHandle(pi.hThread);
#elif HAVE_FORK
- /* the unix way */
+ /* the Unix way */
child = fork();
if (child == 0) {
- /* this is the child process */
+ /* This is the child process */
- if (close_parent_ends_of_pipes_in_child(descriptors, ndesc) == FAILURE) {
+ if (close_parentends_of_pipes(descriptors, ndesc) == FAILURE) {
/* We are already in child process and can't do anything to make
- * proc_open() return an error in the parent
+ * `proc_open` return an error in the parent
* All we can do is exit with a non-zero (error) exit code */
_exit(127);
}
@@ -1069,26 +1101,26 @@ PHP_FUNCTION(proc_open)
/* If execvp/execle/execl are successful, we will never reach here
* Display error and exit with non-zero (error) status code */
- php_error_docref(NULL, E_WARNING, "Exec failed - %s", strerror(errno));
+ php_error_docref(NULL, E_WARNING, "Exec failed: %s", strerror(errno));
_exit(127);
} else if (child < 0) {
- /* failed to fork() */
+ /* Failed to fork() */
close_all_descriptors(descriptors, ndesc);
- php_error_docref(NULL, E_WARNING, "Fork failed - %s", strerror(errno));
+ php_error_docref(NULL, E_WARNING, "Fork failed: %s", strerror(errno));
goto exit_fail;
}
#else
# error You lose (configure should not have let you get here)
#endif
- /* we forked/spawned and this is the parent */
+ /* We forked/spawned and this is the parent */
pipes = zend_try_array_init(pipes);
if (!pipes) {
goto exit_fail;
}
- proc = (struct php_process_handle*) emalloc(sizeof(struct php_process_handle));
+ proc = (php_process_handle*) emalloc(sizeof(php_process_handle));
proc->command = command;
proc->pipes = emalloc(sizeof(zend_resource *) * ndesc);
proc->npipes = ndesc;
@@ -1098,8 +1130,8 @@ PHP_FUNCTION(proc_open)
#endif
proc->env = env;
- /* clean up all the child ends and then open streams on the parent
- * ends, where appropriate */
+ /* Clean up all the child ends and then open streams on the parent
+ * ends, where appropriate */
for (i = 0; i < ndesc; i++) {
char *mode_string = NULL;
php_stream *stream = NULL;
@@ -1133,7 +1165,8 @@ PHP_FUNCTION(proc_open)
#else
stream = php_stream_fopen_from_fd(descriptors[i].parentend, mode_string, NULL);
# if defined(F_SETFD) && defined(FD_CLOEXEC)
- /* mark the descriptor close-on-exec, so that it won't be inherited by potential other children */
+ /* Mark the descriptor close-on-exec, so it won't be inherited by
+ * potential other children */
fcntl(descriptors[i].parentend, F_SETFD, FD_CLOEXEC);
# endif
#endif
diff --git a/ext/standard/proc_open.h b/ext/standard/proc_open.h
index e6bfd4c742..d10cb0ecf0 100644
--- a/ext/standard/proc_open.h
+++ b/ext/standard/proc_open.h
@@ -24,18 +24,17 @@ typedef pid_t php_process_id_t;
# define PHP_INVALID_FD (-1)
#endif
-/* Environment block under win32 is a NUL terminated sequence of NUL terminated
- * name=value strings.
- * Under unix, it is an argv style array.
- * */
+/* Environment block under Win32 is a NUL terminated sequence of NUL terminated
+ * name=value strings.
+ * Under Unix, it is an argv style array. */
typedef struct _php_process_env {
char *envp;
#ifndef PHP_WIN32
char **envarray;
#endif
-} php_process_env_t;
+} php_process_env;
-struct php_process_handle {
+typedef struct _php_process_handle {
php_process_id_t child;
#ifdef PHP_WIN32
HANDLE childHandle;
@@ -43,5 +42,5 @@ struct php_process_handle {
int npipes;
zend_resource **pipes;
char *command;
- php_process_env_t env;
-};
+ php_process_env env;
+} php_process_handle;
diff --git a/ext/standard/tests/general_functions/proc_open_pipes3.phpt b/ext/standard/tests/general_functions/proc_open_pipes3.phpt
index 307fbbaa1d..96ce75bd0c 100644
--- a/ext/standard/tests/general_functions/proc_open_pipes3.phpt
+++ b/ext/standard/tests/general_functions/proc_open_pipes3.phpt
@@ -32,7 +32,7 @@ echo "END\n";
?>
--EXPECTF--
Warning: proc_open(): pi is not a valid descriptor spec/mode in %s on line %d
-proc_open(): Argument #2 ($descriptorspec) must only contain arrays and File-Handles
+proc_open(): Argument #2 ($descriptorspec) must only contain arrays and streams
array(4) {
[3]=>
resource(%d) of type (Unknown)