static int le_proc_open;
+#if !defined(PHP_WIN32) && !defined(NETWARE)
+/* {{{ _php_array_to_argv */
+static char **_php_array_to_argv(zval *arg_array, int is_persistent)
+{
+ zval **element, temp;
+ char **c_argv, **ap;
+ HashTable *target_hash;
+ HashPosition pos;
+
+ target_hash = Z_ARRVAL_P(arg_array);
+ ap = c_argv = (char **)pecalloc(zend_hash_num_elements(target_hash) + 1, sizeof(char *), is_persistent);
+
+ /* skip first element */
+ zend_hash_internal_pointer_reset_ex(target_hash, &pos);
+ zend_hash_move_forward_ex(target_hash, &pos);
+ for ( ;
+ zend_hash_get_current_data_ex(target_hash, (void **) &element, &pos) == SUCCESS;
+ zend_hash_move_forward_ex(target_hash, &pos)) {
+
+ temp = **element;
+ if (Z_TYPE_PP(element) != IS_STRING) {
+ zval_copy_ctor(&temp);
+ convert_to_string(&temp);
+ }
+ *ap++ = pestrndup(Z_STRVAL(temp), Z_STRLEN(temp), is_persistent);
+ if (Z_TYPE_PP(element) != IS_STRING) {
+ zval_dtor(&temp);
+ }
+ }
+
+ return c_argv;
+}
+/* }}} */
+
+/* {{{ _php_free_argv */
+static void _php_free_argv(char **argv, int is_persistent)
+{
+ if (argv) {
+ char **ap = NULL;
+
+ for (ap = argv; *ap; ap++) {
+ pefree(*ap, is_persistent);
+ }
+ pefree(argv, is_persistent);
+ }
+}
+/* }}} */
+
+#endif
+
/* {{{ _php_array_to_envp */
static php_process_env_t _php_array_to_envp(zval *environment, int is_persistent TSRMLS_DC)
{
}
assert(p - env.envp <= sizeenv);
-
- zend_hash_internal_pointer_reset_ex(target_hash, &pos);
return env;
}
FG(pclose_ret) = -1;
#endif
_php_free_envp(proc->env, proc->is_persistent);
+ _php_free_argv(proc->argv, proc->is_persistent);
pefree(proc->command, proc->is_persistent);
pefree(proc, proc->is_persistent);
PHP_FUNCTION(proc_open)
{
zval **ppcommand, **ppcwd = NULL;
+ zval *command_with_args;
char *command, *cwd=NULL;
int command_len, cwd_len = 0;
zval *descriptorspec;
zval **descitem = NULL;
HashPosition pos;
struct php_proc_open_descriptor_item descriptors[PHP_PROC_OPEN_MAX_DESCRIPTORS];
+ char** child_argv = NULL;
#ifdef PHP_WIN32
PROCESS_INFORMATION pi;
HANDLE childHandle;
UINT old_error_mode;
#endif
#ifdef NETWARE
- char** child_argv = NULL;
char* command_dup = NULL;
char* orig_cwd = NULL;
int command_num_args = 0;
int is_persistent = 0; /* TODO: ensure that persistent procs will work */
#ifdef PHP_WIN32
int suppress_errors = 0;
- int bypass_shell = 0;
#endif
+ int bypass_shell = 0;
#if PHP_CAN_DO_PTS
php_file_descriptor_t dev_ptmx = -1; /* master */
php_file_descriptor_t slave_pty = -1;
php_stream_context *context = FG(default_context);
zend_uchar binary_pipes = 0;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zaz|Z!a!a!", &ppcommand, &descriptorspec, &pipes, &ppcwd, &environment, &other_options) == FAILURE ||
- php_stream_path_param_encode(ppcommand, &command, &command_len, REPORT_ERRORS, FG(default_context)) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zaz|Z!a!a!", &command_with_args, &descriptorspec, &pipes, &ppcwd, &environment, &other_options) == FAILURE) {
RETURN_FALSE;
}
Z_LVAL_PP(item)) {
suppress_errors = 1;
}
- }
+ }
+#endif
if (SUCCESS == zend_ascii_hash_find(Z_ARRVAL_P(other_options), "bypass_shell", sizeof("bypass_shell"), (void**)&item)) {
if ((Z_TYPE_PP(item) == IS_BOOL || Z_TYPE_PP(item) == IS_LONG) &&
Z_LVAL_PP(item)) {
bypass_shell = 1;
}
- }
-#endif
+ }
/* Suppresses automatic application of unicode filters when unicode.semantics=on */
if (SUCCESS == zend_ascii_hash_find(Z_ARRVAL_P(other_options), "binary_pipes", sizeof("binary_pipes"), (void**)&item)) {
if (Z_TYPE_PP(item) == IS_BOOL && Z_BVAL_PP(item)) {
/* Override FG(default_context) */
if (SUCCESS == zend_ascii_hash_find(Z_ARRVAL_P(other_options), "context", sizeof("context"), (void**)&item)) {
context = php_stream_context_from_zval(*item, 0);
- }
+ }
}
-
+
+#if !defined(PHP_WIN32) && !defined(NETWARE)
+ if (bypass_shell) {
+ zval **item;
+
+ if (Z_TYPE_P(command_with_args) != IS_ARRAY) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "first parameter must be array when bypass_shell is on");
+ RETURN_FALSE;
+ }
+ if (zend_hash_num_elements(Z_ARRVAL_P(command_with_args)) < 1) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "arguments array must have at least one element");
+ RETURN_FALSE;
+ }
+
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(command_with_args), &pos);
+ if (zend_hash_get_current_data_ex(Z_ARRVAL_P(command_with_args), (void **)&item, &pos) == SUCCESS) {
+ if (Z_TYPE_PP(item) == IS_STRING || Z_TYPE_PP(item) == IS_UNICODE) {
+ ppcommand = item;
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "first argument must be a nonempty string");
+ RETURN_FALSE;
+ }
+ } else {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "first argument must be at index 0");
+ RETURN_FALSE;
+ }
+ } else {
+#endif
+ if (Z_TYPE_P(command_with_args) != IS_STRING && Z_TYPE_P(command_with_args) != IS_UNICODE) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s() expects parameter 1 to be string, %s given", get_active_function_name(TSRMLS_C),
+ zend_zval_type_name(command_with_args));
+ RETURN_FALSE;
+ }
+ ppcommand = &command_with_args;
+ /* command_len will be set below */
+#if !defined(PHP_WIN32) && !defined(NETWARE)
+ }
+#endif
+
+ if (php_stream_path_param_encode(ppcommand, &command, &command_len, REPORT_ERRORS, FG(default_context)) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+#if !defined(PHP_WIN32) && !defined(NETWARE)
+ if (bypass_shell) {
+ child_argv = _php_array_to_argv(command_with_args, is_persistent);
+ }
+#endif
+
if (environment) {
env = _php_array_to_envp(environment, is_persistent TSRMLS_CC);
} else {
chdir(cwd);
}
- if (env.envarray) {
+ if (bypass_shell && env.envarray) {
+ execve(command, child_argv, env.envarray);
+ } else if (bypass_shell) {
+ execv(command, child_argv);
+ } else if (env.envarray) {
execle("/bin/sh", "sh", "-c", command, NULL, env.envarray);
} else {
execl("/bin/sh", "sh", "-c", command, NULL);
proc->childHandle = childHandle;
#endif
proc->env = env;
+#if !defined(PHP_WIN32) && !defined(NETWARE)
+ proc->argv = child_argv;
+#endif
if (pipes != NULL) {
zval_dtor(pipes);
return;
exit_fail:
+#if !defined(PHP_WIN32) && !defined(NETWARE)
+ _php_free_argv(child_argv, is_persistent);
+#endif
_php_free_envp(env, is_persistent);
#if PHP_CAN_DO_PTS
if (dev_ptmx >= 0) {
--- /dev/null
+--TEST--
+proc_open with only one argv
+--DESCRIPTION--
+This test tries out a very esoteric functionality: Passing no argv[0] to a
+program. There's absolutely no reason anyone would do this in practice, but the
+entire point of the bypass_shell patch was to allow 100% control over the child
+process, so the option is there. Keep in mind that actually using this
+"feature" will probably crash most programs one could run, since the expression
+argc > 0 in a main() function is pretty much guaranteed by POSIX. It's
+interesting to note that PHP itself handles this case gracefully.
+--SKIPIF--
+<?php # vim:syn=php
+if (!is_executable($_ENV['TEST_PHP_EXECUTABLE'])) echo "skip";
+if (!function_exists("proc_open")) echo "skip proc_open() is not available";
+if (substr(PHP_OS, 0, 3) == 'WIN') echo "skip this test for non-Windows systems only";
+?>
+--FILE--
+<?php
+$ds = array(
+ 0 => array("pipe", "r"),
+ 1 => array("pipe", "w"),
+ 2 => array("pipe", "w")
+ );
+
+$cat = proc_open(
+ array($_ENV[b'TEST_PHP_EXECUTABLE']),
+ $ds,
+ $pipes,
+ NULL,
+ NULL,
+ array('bypass_shell' => TRUE)
+ );
+
+fprintf($pipes[0], '<?php error_reporting(E_ALL); var_dump($argv); ?>');
+fclose($pipes[0]);
+
+echo stream_get_contents($pipes[1]);
+
+proc_close($cat);
+
+?>
+--EXPECT--
+Notice: Undefined variable: argv in - on line 1
+NULL