]> granicus.if.org Git - php/commitdiff
- Move proc_open code to a source file of it's own.
authorWez Furlong <wez@php.net>
Wed, 15 Jan 2003 16:29:00 +0000 (16:29 +0000)
committerWez Furlong <wez@php.net>
Wed, 15 Jan 2003 16:29:00 +0000 (16:29 +0000)
- Tidy up that netware mess by performing a configure check which will
  define the symbol PHP_CAN_SUPPORT_PROC_OPEN if PHP can support proc_open.
- Protected the proc_open specific code with #ifdef PHP_CAN_SUPPORT_PROC_OPEN
  so that user-space scripts can use function_exists and react accordingly.

** Heads Up Win32: You need to add ext/standard/proc_open.c to the DSP and
** #define PHP_CAN_SUPPORT_PROC_OPEN in the w32 config header.

ext/standard/basic_functions.c
ext/standard/config.m4
ext/standard/exec.c
ext/standard/proc_open.c [new file with mode: 0644]

index 68cf56964dfc9d5885af580ad0363677269f73c2..5ebd8c538887cd5edab256dc25f1ebc026189b92 100644 (file)
@@ -411,8 +411,10 @@ function_entry basic_functions[] = {
        PHP_FE(escapeshellarg,                                                                                                  NULL)
        PHP_FE(passthru,                                second_arg_force_ref)
        PHP_FE(shell_exec,                                                                                                              NULL)
+#ifdef PHP_CAN_SUPPORT_PROC_OPEN
        PHP_FE(proc_open,                               third_arg_force_ref)
        PHP_FE(proc_close,                                                                                                              NULL)
+#endif
 
        PHP_FE(rand,                                                                                                                    NULL)
        PHP_FE(srand,                                                                                                                   NULL)
@@ -1063,7 +1065,9 @@ PHP_MINIT_FUNCTION(basic)
        PHP_MINIT(array)(INIT_FUNC_ARGS_PASSTHRU);
        PHP_MINIT(assert)(INIT_FUNC_ARGS_PASSTHRU);
        PHP_MINIT(url_scanner_ex)(INIT_FUNC_ARGS_PASSTHRU);
+#ifdef PHP_CAN_SUPPORT_PROC_OPEN
        PHP_MINIT(proc_open)(INIT_FUNC_ARGS_PASSTHRU);
+#endif
 
        PHP_MINIT(user_streams)(INIT_FUNC_ARGS_PASSTHRU);
        PHP_MINIT(imagetypes)(INIT_FUNC_ARGS_PASSTHRU);
index f74c7ed7e44773e99f944bd151fdbe95cf5bee39..cdea6be2803ce0ee155007e177ad23d5e0242b6b 100644 (file)
@@ -225,6 +225,31 @@ AC_ARG_WITH(regex,
 
 AC_FUNC_FNMATCH        
 
+dnl Take a look and see if there is a support means of creating a new process
+dnl and defining which handles it receives
+AC_DEFUN([PHP_CHECK_IF_SUPPORT_PROC_OPEN],[
+  
+  AC_CACHE_VAL(php_can_support_proc_open,[
+    AC_CHECK_FUNCS(fork CreateProcess, [
+      php_can_support_proc_open=yes
+      break
+    ],[
+      php_can_support_proc_open=no
+    ])
+  ])
+  
+  AC_MSG_CHECKING([if your OS can spawn processes with inherited handles])
+  if test "$php_can_support_proc_open" = "yes"; then
+    AC_MSG_RESULT(yes)
+    AC_DEFINE(PHP_CAN_SUPPORT_PROC_OPEN,1, [Define if your system has fork/vfork/CreateProcess])
+  else
+    AC_MSG_RESULT(no)
+  fi
+
+])
+
+PHP_CHECK_IF_SUPPORT_PROC_OPEN
+
 dnl getopt long options disabled for now
 dnl as we can't be sure that we get the right getopt.h here
 dnl using the standard AC_CHECK macros
@@ -256,6 +281,6 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32.
                             incomplete_class.c url_scanner_ex.c ftp_fopen_wrapper.c \
                             http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \
                             var_unserializer.c ftok.c aggregation.c sha1.c user_filters.c \
-                                                       filters.c )
+                            filters.c proc_open.c )
 
 PHP_ADD_MAKEFILE_FRAGMENT
index e3c3644ace893fa2b35b93ea181133a7e8f5bebc..d617e8522645f37c0a8e1f90cee065fd5f54352b 100644 (file)
 #include <fcntl.h>
 #endif
 
-/* {{{ php_make_safe_mode_command */
-static int php_make_safe_mode_command(char *cmd, char **safecmd TSRMLS_DC)
-{
-       int lcmd, larg0, ldir, len, overflow_limit;
-       char *space, *sep, *arg0;
-
-       if (!PG(safe_mode)) {
-               *safecmd = estrdup(cmd);
-               return SUCCESS;
-       }
-
-       lcmd = strlen(cmd);
-       ldir = strlen(PG(safe_mode_exec_dir));
-       len = lcmd + ldir + 2;
-       overflow_limit = len;
-
-       arg0 = emalloc(len);
-       
-       strcpy(arg0, cmd);
-       
-       space = strchr(arg0, ' ');
-       if (space) {
-               *space = '\0';
-       }
-       larg0 = strlen(arg0);
-
-       if (strstr(arg0, "..")) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "No '..' components allowed in path");
-               efree(arg0);
-               return FAILURE;
-       }
-
-       *safecmd = emalloc(len);
-       strcpy(*safecmd, PG(safe_mode_exec_dir));
-       overflow_limit -= ldir;
-       
-       sep = strrchr(arg0, PHP_DIR_SEPARATOR);
-       if (sep) {
-               strcat(*safecmd, sep);
-               overflow_limit -= strlen(sep);
-       } else {
-               strcat(*safecmd, "/");
-               strcat(*safecmd, arg0);
-               overflow_limit -= larg0 + 1;
-       }
-       if (space) {
-               strncat(*safecmd, cmd + larg0, overflow_limit);
-       }
-       efree(arg0);
-       arg0 = php_escape_shell_cmd(*safecmd);
-       efree(*safecmd);
-       *safecmd = arg0;
-
-       return SUCCESS;
-}
-/* }}} */
-
 /* {{{ php_Exec
  * If type==0, only last line of output is returned (exec)
  * If type==1, all lines will be printed and last lined returned (system)
@@ -556,453 +499,6 @@ PHP_FUNCTION(shell_exec)
 }
 /* }}} */
 
-/* {{{ proc_open resource management */
-static int le_proc_open;
-
-static void proc_open_rsrc_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
-{
-#ifdef PHP_WIN32
-       HANDLE child;
-       DWORD wstatus;
-       
-       child = (HANDLE)rsrc->ptr;
-       WaitForSingleObject(child, INFINITE);
-       GetExitCodeProcess(child, &wstatus);
-       FG(pclose_ret) = wstatus;
-#else
-# if HAVE_SYS_WAIT_H
-       int wstatus;
-       pid_t child, wait_pid;
-       
-       child = (pid_t)rsrc->ptr;
-
-       do {
-               wait_pid = waitpid(child, &wstatus, 0);
-       } while (wait_pid == -1 && errno == EINTR);
-       
-       if (wait_pid == -1)
-               FG(pclose_ret) = -1;
-       else {
-               if (WIFEXITED(wstatus))
-                       wstatus = WEXITSTATUS(wstatus);
-               FG(pclose_ret) = wstatus;
-       }
-       
-# else
-       FG(pclose_ret) = -1;
-# endif
-#endif
-}
-
-PHP_MINIT_FUNCTION(proc_open)
-{
-       le_proc_open = zend_register_list_destructors_ex(proc_open_rsrc_dtor, NULL, "process", module_number);
-       return SUCCESS;
-}
-/* }}} */
-
-/* {{{ proto int proc_close(resource process)
-   close a process opened by proc_open */
-PHP_FUNCTION(proc_close)
-{
-#ifndef NETWARE        /* This is removed for NetWare because there is not way to execute a new process using fork() */
-       zval *proc;
-       void *child;
-       
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &proc) == FAILURE) {
-               RETURN_FALSE;
-       }
-
-       ZEND_FETCH_RESOURCE(child, void *, &proc, -1, "process", le_proc_open);
-       
-       zend_list_delete(Z_LVAL_P(proc));
-       RETURN_LONG(FG(pclose_ret));
-#endif /* NETWARE */
-}
-/* }}} */
-
-/* {{{ handy definitions for portability/readability */
-#ifdef PHP_WIN32
-typedef HANDLE descriptor_t;
-# define pipe(pair)            (CreatePipe(&pair[0], &pair[1], &security, 2048L) ? 0 : -1)
-
-# define COMSPEC_NT    "cmd.exe"
-# define COMSPEC_9X    "command.com"
-
-static inline HANDLE dup_handle(HANDLE src, BOOL inherit, BOOL closeorig)
-{
-       HANDLE copy, self = GetCurrentProcess();
-       
-       if (!DuplicateHandle(self, src, self, &copy, 0, inherit, DUPLICATE_SAME_ACCESS |
-                               (closeorig ? DUPLICATE_CLOSE_SOURCE : 0)))
-               return NULL;
-       return copy;
-}
-
-
-static inline HANDLE dup_fd_as_handle(int fd)
-{
-       return dup_handle((HANDLE)_get_osfhandle(fd), TRUE, FALSE);
-}
-
-# define close_descriptor(fd)  CloseHandle(fd)
-#else
-typedef int descriptor_t;
-# define close_descriptor(fd)  close(fd)
-
-#endif
-
-#define DESC_PIPE              1
-#define DESC_FILE              2
-#define DESC_PARENT_MODE_WRITE 8
-
-struct php_proc_open_descriptor_item {
-       int index;                                                      /* desired fd number in child process */
-       descriptor_t parentend, childend;       /* fds for pipes in parent/child */
-       int mode;                                                       /* mode for proc_open code */
-       int mode_flags;                                         /* mode flags for opening fds */
-};
-/* }}} */
-
-/* {{{ proto resource proc_open(string command, array descriptorspec, array &pipes)
-   Run a process with more control over it's file descriptors */
-PHP_FUNCTION(proc_open)
-{
-#ifndef NETWARE        /* This is removed for NetWare because there is not way to execute a new process using fork() */
-
-#define MAX_DESCRIPTORS        16
-
-       char *command;
-       long command_len;
-       zval *descriptorspec;
-       zval *pipes;
-       int ndesc = 0;
-       int i;
-       zval **descitem = NULL;
-       HashPosition pos;
-       struct php_proc_open_descriptor_item descriptors[MAX_DESCRIPTORS];
-#ifdef PHP_WIN32
-       PROCESS_INFORMATION pi;
-       STARTUPINFO si;
-       BOOL newprocok;
-       HANDLE child;
-       SECURITY_ATTRIBUTES security;
-       char *command_with_cmd;
-#else
-       pid_t child;
-#endif
-
-       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "saz/", &command,
-                               &command_len, &descriptorspec, &pipes) == FAILURE) {
-               RETURN_FALSE;
-       }
-
-       if (FAILURE == php_make_safe_mode_command(command, &command TSRMLS_CC)) {
-               RETURN_FALSE;
-       }
-
-       command_len = strlen(command);
-       
-       memset(descriptors, 0, sizeof(descriptors));
-
-#ifdef PHP_WIN32
-       /* we use this to allow the child to inherit handles */
-       memset(&security, 0, sizeof(security));
-       security.nLength = sizeof(security);
-       security.bInheritHandle = TRUE;
-       security.lpSecurityDescriptor = NULL;
-#endif
-       
-       /* walk the descriptor spec and set up files/pipes */
-       zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(descriptorspec), &pos);
-       while (zend_hash_get_current_data_ex(Z_ARRVAL_P(descriptorspec), (void **)&descitem, &pos) == SUCCESS) {
-               char *str_index;
-               ulong nindex;
-               zval **ztype;
-
-               str_index = NULL;
-               zend_hash_get_current_key_ex(Z_ARRVAL_P(descriptorspec), &str_index, NULL, &nindex, 0, &pos);
-
-               if (str_index) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "descriptor spec must be an integer indexed array");
-                       goto exit_fail;
-               }
-
-               descriptors[ndesc].index = nindex;
-
-               if (Z_TYPE_PP(descitem) == IS_RESOURCE) {
-                       /* should be a stream - try and dup the descriptor */
-                       php_stream *stream;
-                       int fd;
-
-                       php_stream_from_zval(stream, descitem);
-
-                       if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_FD, (void **)&fd, REPORT_ERRORS)) {
-                               goto exit_fail;
-                       }
-
-#ifdef PHP_WIN32
-                       descriptors[ndesc].childend = dup_fd_as_handle(fd);
-                       if (descriptors[ndesc].childend == NULL) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to dup File-Handle for descriptor %d", nindex);
-                               goto exit_fail;
-                       }
-#else
-                       descriptors[ndesc].childend = dup(fd);
-                       if (descriptors[ndesc].childend < 0) {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to dup File-Handle for descriptor %d - %s", nindex, strerror(errno));
-                               goto exit_fail;
-                       }
-#endif
-                       descriptors[ndesc].mode = DESC_FILE;
-
-               } else if (Z_TYPE_PP(descitem) != IS_ARRAY) {
-                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Descriptor item must be either an array or a File-Handle");
-                       goto exit_fail;
-               } else {
-
-                       if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 0, (void **)&ztype) == SUCCESS) {
-                               convert_to_string_ex(ztype);
-                       } else {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing handle qualifier in array");
-                               goto exit_fail;
-                       }
-
-                       if (strcmp(Z_STRVAL_PP(ztype), "pipe") == 0) {
-                               descriptor_t newpipe[2];
-                               zval **zmode;
-
-                               if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 1, (void **)&zmode) == SUCCESS) {
-                                       convert_to_string_ex(zmode);
-                               } else {
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing mode parameter for 'pipe'", Z_STRVAL_PP(ztype));
-                                       goto exit_fail;
-                               }
-
-                               descriptors[ndesc].mode = DESC_PIPE;
-
-                               if (0 != pipe(newpipe)) {
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create pipe %s", strerror(errno));
-                                       goto exit_fail;
-                               }
-
-                               if (strcmp(Z_STRVAL_PP(zmode), "w") != 0) {
-                                       descriptors[ndesc].parentend = newpipe[1];
-                                       descriptors[ndesc].childend = newpipe[0];
-                                       descriptors[ndesc].mode |= DESC_PARENT_MODE_WRITE;
-                               } else {
-                                       descriptors[ndesc].parentend = newpipe[0];
-                                       descriptors[ndesc].childend = newpipe[1];
-                               }
-#ifdef PHP_WIN32
-                               /* don't let the child inherit the parent side of the pipe */
-                               descriptors[ndesc].parentend = dup_handle(descriptors[ndesc].parentend, FALSE, TRUE);
-#endif
-                               descriptors[ndesc].mode_flags = descriptors[ndesc].mode & DESC_PARENT_MODE_WRITE ? O_WRONLY : O_RDONLY;
-#ifdef PHP_WIN32
-                               if (Z_STRLEN_PP(zmode) >= 2 && Z_STRVAL_PP(zmode)[1] == 'b')
-                                       descriptors[ndesc].mode_flags |= O_BINARY;
-#endif
-
-                               
-
-                       } else if (strcmp(Z_STRVAL_PP(ztype), "file") == 0) {
-                               zval **zfile, **zmode;
-                               int fd;
-                               php_stream *stream;
-                               size_t old_size = FG(def_chunk_size);
-
-                               descriptors[ndesc].mode = DESC_FILE;
-
-                               if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 1, (void **)&zfile) == SUCCESS) {
-                                       convert_to_string_ex(zfile);
-                               } else {
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing file name parameter for 'file'", Z_STRVAL_PP(ztype));
-                                       goto exit_fail;
-                               }
-
-                               if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 2, (void **)&zmode) == SUCCESS) {
-                                       convert_to_string_ex(zmode);
-                               } else {
-                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing mode parameter for 'file'", Z_STRVAL_PP(ztype));
-                                       goto exit_fail;
-                               }
-
-                               /* try a wrapper */
-
-                               FG(def_chunk_size) = 1;
-                               stream = php_stream_open_wrapper(Z_STRVAL_PP(zfile), Z_STRVAL_PP(zmode),
-                                               ENFORCE_SAFE_MODE|REPORT_ERRORS, NULL);
-                               FG(def_chunk_size) = old_size;
-
-                               /* force into an fd */
-                               if (stream == NULL || FAILURE == php_stream_cast(stream,
-                                                       PHP_STREAM_CAST_RELEASE|PHP_STREAM_AS_FD,
-                                                       (void **)&fd, REPORT_ERRORS)) {
-                                       goto exit_fail;
-                               }
-
-#ifdef PHP_WIN32
-                               descriptors[ndesc].childend = (HANDLE)_get_osfhandle(fd);
-#else
-                               descriptors[ndesc].childend = fd;
-#endif
-
-                       } else {
-                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not a valid descriptor spec/mode", Z_STRVAL_PP(ztype));
-                               goto exit_fail;
-                       }
-               }
-
-               zend_hash_move_forward_ex(Z_ARRVAL_P(descriptorspec), &pos);
-               if (++ndesc == MAX_DESCRIPTORS)
-                       break;
-       }
-
-#ifdef PHP_WIN32
-       memset(&si, 0, sizeof(si));
-       si.cb = sizeof(si);
-       si.dwFlags = STARTF_USESTDHANDLES;
-       
-       si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
-       si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
-       si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
-       
-       /* redirect stdin/stdout/stderr if requested */
-       for (i = 0; i < ndesc; i++) {
-               switch(descriptors[i].index) {
-                       case 0:
-                               si.hStdInput = descriptors[i].childend;
-                               break;
-                       case 1:
-                               si.hStdOutput = descriptors[i].childend;
-                               break;
-                       case 2:
-                               si.hStdError = descriptors[i].childend;
-                               break;
-               }
-       }
-
-       
-       memset(&pi, 0, sizeof(pi));
-       
-       command_with_cmd = emalloc(command_len + sizeof(COMSPEC_9X) + 1 + sizeof(" /c "));
-       sprintf(command_with_cmd, "%s /c %s", GetVersion() < 0x80000000 ? COMSPEC_NT : COMSPEC_9X, command);
-       newprocok = CreateProcess(NULL, command_with_cmd, &security, &security, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
-       efree(command_with_cmd);
-
-       if (FALSE == newprocok) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "CreateProcess failed");
-               goto exit_fail;
-       }
-
-       child = pi.hProcess;
-       CloseHandle(pi.hThread);
-
-#else
-       /* the unix way */
-
-       child = fork();
-
-       if (child == 0) {
-               /* this is the child process */
-
-               /* close those descriptors that we just opened for the parent stuff,
-                * dup new descriptors into required descriptors and close the original
-                * cruft */
-               for (i = 0; i < ndesc; i++) {
-                       switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) {
-                               case DESC_PIPE:
-                                       close(descriptors[i].parentend);
-                                       break;
-                       }
-                       if (dup2(descriptors[i].childend, descriptors[i].index) < 0)
-                               perror("dup2");
-                       if (descriptors[i].childend != descriptors[i].index)
-                               close(descriptors[i].childend);
-               }
-
-               execl("/bin/sh", "sh", "-c", command, NULL);
-               _exit(127);
-
-       } else if (child < 0) {
-               /* failed to fork() */
-
-               /* clean up all the descriptors */
-               for (i = 0; i < ndesc; i++) {
-                       close(descriptors[i].childend);
-                       close(descriptors[i].parentend);
-               }
-
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "fork failed - %s", strerror(errno));
-
-               goto exit_fail;
-
-       }
-#endif
-       /* we forked/spawned and this is the parent */
-
-       efree(command);
-       array_init(pipes);
-
-       /* clean up all the child ends and then open streams on the parent
-        * ends, where appropriate */
-       for (i = 0; i < ndesc; i++) {
-               FILE *fp;
-               char *mode_string=NULL;
-               php_stream *stream;
-
-               close_descriptor(descriptors[i].childend);
-
-               switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) {
-                       case DESC_PIPE:
-                               switch(descriptors[i].mode_flags) {
-#ifdef PHP_WIN32
-                                       case O_WRONLY|O_BINARY:
-                                               mode_string = "wb";
-                                               break;
-                                       case O_RDONLY|O_BINARY:
-                                               mode_string = "rb";
-                                               break;
-#endif
-                                       case O_WRONLY:
-                                               mode_string = "w";
-                                               break;
-                                       case O_RDONLY:
-                                               mode_string = "r";
-                                               break;
-                               }
-#ifdef PHP_WIN32
-                               fp = _fdopen(_open_osfhandle((long)descriptors[i].parentend,
-                                                       descriptors[i].mode_flags), mode_string);
-#else
-                               fp = fdopen(descriptors[i].parentend, mode_string);
-#endif
-                               if (fp) {
-                                       stream = php_stream_fopen_from_file(fp, mode_string);
-                                       if (stream) {
-                                               zval *retfp;
-
-                                               MAKE_STD_ZVAL(retfp);
-                                               php_stream_to_zval(stream, retfp);
-                                               add_index_zval(pipes, descriptors[i].index, retfp);
-                                       }
-                               }
-                               break;
-               }
-       }
-
-       ZEND_REGISTER_RESOURCE(return_value, (void*)child, le_proc_open);
-       return;
-
-exit_fail:
-       efree(command);
-       RETURN_FALSE;
-
-#endif /* NETWARE */
-}
-/* }}} */
-
 /*
  * Local variables:
  * tab-width: 4
diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c
new file mode 100644 (file)
index 0000000..0ca4519
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 4                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2003 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.txt.                                 |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Wez Furlong                                                  |
+   +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include <stdio.h>
+#include "php.h"
+#include <ctype.h>
+#include "php_string.h"
+#include "safe_mode.h"
+#include "ext/standard/head.h"
+#include "ext/standard/file.h"
+#include "exec.h"
+#include "php_globals.h"
+#include "SAPI.h"
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+/* This symbol is defined in ext/standard/config.m4.
+ * Essentially, it is set if you HAVE_FORK || PHP_WIN32
+ * Otherplatforms may modify that configure check and add suitable #ifdefs
+ * around the alternate code.
+ * */
+#ifdef PHP_CAN_SUPPORT_PROC_OPEN
+
+static int le_proc_open;
+
+static void proc_open_rsrc_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+#ifdef PHP_WIN32
+       HANDLE child;
+       DWORD wstatus;
+       
+       child = (HANDLE)rsrc->ptr;
+       WaitForSingleObject(child, INFINITE);
+       GetExitCodeProcess(child, &wstatus);
+       FG(pclose_ret) = wstatus;
+#else
+# if HAVE_SYS_WAIT_H
+       int wstatus;
+       pid_t child, wait_pid;
+       
+       child = (pid_t)rsrc->ptr;
+
+       do {
+               wait_pid = waitpid(child, &wstatus, 0);
+       } while (wait_pid == -1 && errno == EINTR);
+       
+       if (wait_pid == -1)
+               FG(pclose_ret) = -1;
+       else {
+               if (WIFEXITED(wstatus))
+                       wstatus = WEXITSTATUS(wstatus);
+               FG(pclose_ret) = wstatus;
+       }
+       
+# else
+       FG(pclose_ret) = -1;
+# endif
+#endif
+}
+
+/* {{{ php_make_safe_mode_command */
+static int php_make_safe_mode_command(char *cmd, char **safecmd TSRMLS_DC)
+{
+       int lcmd, larg0, ldir, len, overflow_limit;
+       char *space, *sep, *arg0;
+
+       if (!PG(safe_mode)) {
+               *safecmd = estrdup(cmd);
+               return SUCCESS;
+       }
+
+       lcmd = strlen(cmd);
+       ldir = strlen(PG(safe_mode_exec_dir));
+       len = lcmd + ldir + 2;
+       overflow_limit = len;
+
+       arg0 = emalloc(len);
+       
+       strcpy(arg0, cmd);
+       
+       space = strchr(arg0, ' ');
+       if (space) {
+               *space = '\0';
+       }
+       larg0 = strlen(arg0);
+
+       if (strstr(arg0, "..")) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "No '..' components allowed in path");
+               efree(arg0);
+               return FAILURE;
+       }
+
+       *safecmd = emalloc(len);
+       strcpy(*safecmd, PG(safe_mode_exec_dir));
+       overflow_limit -= ldir;
+       
+       sep = strrchr(arg0, PHP_DIR_SEPARATOR);
+       if (sep) {
+               strcat(*safecmd, sep);
+               overflow_limit -= strlen(sep);
+       } else {
+               strcat(*safecmd, "/");
+               strcat(*safecmd, arg0);
+               overflow_limit -= larg0 + 1;
+       }
+       if (space) {
+               strncat(*safecmd, cmd + larg0, overflow_limit);
+       }
+       efree(arg0);
+       arg0 = php_escape_shell_cmd(*safecmd);
+       efree(*safecmd);
+       *safecmd = arg0;
+
+       return SUCCESS;
+}
+/* }}} */
+
+
+
+PHP_MINIT_FUNCTION(proc_open)
+{
+       le_proc_open = zend_register_list_destructors_ex(proc_open_rsrc_dtor, NULL, "process", module_number);
+       return SUCCESS;
+}
+
+/* {{{ proto int proc_close(resource process)
+   close a process opened by proc_open */
+PHP_FUNCTION(proc_close)
+{
+       zval *proc;
+       void *child;
+       
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &proc) == FAILURE) {
+               RETURN_FALSE;
+       }
+
+       ZEND_FETCH_RESOURCE(child, void *, &proc, -1, "process", le_proc_open);
+       
+       zend_list_delete(Z_LVAL_P(proc));
+       RETURN_LONG(FG(pclose_ret));
+}
+/* }}} */
+
+/* {{{ handy definitions for portability/readability */
+#ifdef PHP_WIN32
+typedef HANDLE descriptor_t;
+# define pipe(pair)            (CreatePipe(&pair[0], &pair[1], &security, 2048L) ? 0 : -1)
+
+# define COMSPEC_NT    "cmd.exe"
+# define COMSPEC_9X    "command.com"
+
+static inline HANDLE dup_handle(HANDLE src, BOOL inherit, BOOL closeorig)
+{
+       HANDLE copy, self = GetCurrentProcess();
+       
+       if (!DuplicateHandle(self, src, self, &copy, 0, inherit, DUPLICATE_SAME_ACCESS |
+                               (closeorig ? DUPLICATE_CLOSE_SOURCE : 0)))
+               return NULL;
+       return copy;
+}
+
+static inline HANDLE dup_fd_as_handle(int fd)
+{
+       return dup_handle((HANDLE)_get_osfhandle(fd), TRUE, FALSE);
+}
+
+# define close_descriptor(fd)  CloseHandle(fd)
+#else
+typedef int descriptor_t;
+# define close_descriptor(fd)  close(fd)
+
+#endif
+
+#define DESC_PIPE              1
+#define DESC_FILE              2
+#define DESC_PARENT_MODE_WRITE 8
+
+struct php_proc_open_descriptor_item {
+       int index;                                                      /* desired fd number in child process */
+       descriptor_t parentend, childend;       /* fds for pipes in parent/child */
+       int mode;                                                       /* mode for proc_open code */
+       int mode_flags;                                         /* mode flags for opening fds */
+};
+/* }}} */
+
+/* {{{ proto resource proc_open(string command, array descriptorspec, array &pipes)
+   Run a process with more control over it's file descriptors */
+PHP_FUNCTION(proc_open)
+{
+#define MAX_DESCRIPTORS        16
+
+       char *command;
+       long command_len;
+       zval *descriptorspec;
+       zval *pipes;
+       int ndesc = 0;
+       int i;
+       zval **descitem = NULL;
+       HashPosition pos;
+       struct php_proc_open_descriptor_item descriptors[MAX_DESCRIPTORS];
+#ifdef PHP_WIN32
+       PROCESS_INFORMATION pi;
+       STARTUPINFO si;
+       BOOL newprocok;
+       HANDLE child;
+       SECURITY_ATTRIBUTES security;
+       char *command_with_cmd;
+#else
+       pid_t child;
+#endif
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "saz/", &command,
+                               &command_len, &descriptorspec, &pipes) == FAILURE) {
+               RETURN_FALSE;
+       }
+
+       if (FAILURE == php_make_safe_mode_command(command, &command TSRMLS_CC)) {
+               RETURN_FALSE;
+       }
+
+       command_len = strlen(command);
+       
+       memset(descriptors, 0, sizeof(descriptors));
+
+#ifdef PHP_WIN32
+       /* we use this to allow the child to inherit handles */
+       memset(&security, 0, sizeof(security));
+       security.nLength = sizeof(security);
+       security.bInheritHandle = TRUE;
+       security.lpSecurityDescriptor = NULL;
+#endif
+       
+       /* walk the descriptor spec and set up files/pipes */
+       zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(descriptorspec), &pos);
+       while (zend_hash_get_current_data_ex(Z_ARRVAL_P(descriptorspec), (void **)&descitem, &pos) == SUCCESS) {
+               char *str_index;
+               ulong nindex;
+               zval **ztype;
+
+               str_index = NULL;
+               zend_hash_get_current_key_ex(Z_ARRVAL_P(descriptorspec), &str_index, NULL, &nindex, 0, &pos);
+
+               if (str_index) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "descriptor spec must be an integer indexed array");
+                       goto exit_fail;
+               }
+
+               descriptors[ndesc].index = nindex;
+
+               if (Z_TYPE_PP(descitem) == IS_RESOURCE) {
+                       /* should be a stream - try and dup the descriptor */
+                       php_stream *stream;
+                       int fd;
+
+                       php_stream_from_zval(stream, descitem);
+
+                       if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_FD, (void **)&fd, REPORT_ERRORS)) {
+                               goto exit_fail;
+                       }
+
+#ifdef PHP_WIN32
+                       descriptors[ndesc].childend = dup_fd_as_handle(fd);
+                       if (descriptors[ndesc].childend == NULL) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to dup File-Handle for descriptor %d", nindex);
+                               goto exit_fail;
+                       }
+#else
+                       descriptors[ndesc].childend = dup(fd);
+                       if (descriptors[ndesc].childend < 0) {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to dup File-Handle for descriptor %d - %s", nindex, strerror(errno));
+                               goto exit_fail;
+                       }
+#endif
+                       descriptors[ndesc].mode = DESC_FILE;
+
+               } else if (Z_TYPE_PP(descitem) != IS_ARRAY) {
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Descriptor item must be either an array or a File-Handle");
+                       goto exit_fail;
+               } else {
+
+                       if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 0, (void **)&ztype) == SUCCESS) {
+                               convert_to_string_ex(ztype);
+                       } else {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing handle qualifier in array");
+                               goto exit_fail;
+                       }
+
+                       if (strcmp(Z_STRVAL_PP(ztype), "pipe") == 0) {
+                               descriptor_t newpipe[2];
+                               zval **zmode;
+
+                               if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 1, (void **)&zmode) == SUCCESS) {
+                                       convert_to_string_ex(zmode);
+                               } else {
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing mode parameter for 'pipe'", Z_STRVAL_PP(ztype));
+                                       goto exit_fail;
+                               }
+
+                               descriptors[ndesc].mode = DESC_PIPE;
+
+                               if (0 != pipe(newpipe)) {
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create pipe %s", strerror(errno));
+                                       goto exit_fail;
+                               }
+
+                               if (strcmp(Z_STRVAL_PP(zmode), "w") != 0) {
+                                       descriptors[ndesc].parentend = newpipe[1];
+                                       descriptors[ndesc].childend = newpipe[0];
+                                       descriptors[ndesc].mode |= DESC_PARENT_MODE_WRITE;
+                               } else {
+                                       descriptors[ndesc].parentend = newpipe[0];
+                                       descriptors[ndesc].childend = newpipe[1];
+                               }
+#ifdef PHP_WIN32
+                               /* don't let the child inherit the parent side of the pipe */
+                               descriptors[ndesc].parentend = dup_handle(descriptors[ndesc].parentend, FALSE, TRUE);
+#endif
+                               descriptors[ndesc].mode_flags = descriptors[ndesc].mode & DESC_PARENT_MODE_WRITE ? O_WRONLY : O_RDONLY;
+#ifdef PHP_WIN32
+                               if (Z_STRLEN_PP(zmode) >= 2 && Z_STRVAL_PP(zmode)[1] == 'b')
+                                       descriptors[ndesc].mode_flags |= O_BINARY;
+#endif
+
+                               
+
+                       } else if (strcmp(Z_STRVAL_PP(ztype), "file") == 0) {
+                               zval **zfile, **zmode;
+                               int fd;
+                               php_stream *stream;
+                               size_t old_size = FG(def_chunk_size);
+
+                               descriptors[ndesc].mode = DESC_FILE;
+
+                               if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 1, (void **)&zfile) == SUCCESS) {
+                                       convert_to_string_ex(zfile);
+                               } else {
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing file name parameter for 'file'", Z_STRVAL_PP(ztype));
+                                       goto exit_fail;
+                               }
+
+                               if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 2, (void **)&zmode) == SUCCESS) {
+                                       convert_to_string_ex(zmode);
+                               } else {
+                                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing mode parameter for 'file'", Z_STRVAL_PP(ztype));
+                                       goto exit_fail;
+                               }
+
+                               /* try a wrapper */
+
+                               FG(def_chunk_size) = 1;
+                               stream = php_stream_open_wrapper(Z_STRVAL_PP(zfile), Z_STRVAL_PP(zmode),
+                                               ENFORCE_SAFE_MODE|REPORT_ERRORS, NULL);
+                               FG(def_chunk_size) = old_size;
+
+                               /* force into an fd */
+                               if (stream == NULL || FAILURE == php_stream_cast(stream,
+                                                       PHP_STREAM_CAST_RELEASE|PHP_STREAM_AS_FD,
+                                                       (void **)&fd, REPORT_ERRORS)) {
+                                       goto exit_fail;
+                               }
+
+#ifdef PHP_WIN32
+                               descriptors[ndesc].childend = (HANDLE)_get_osfhandle(fd);
+#else
+                               descriptors[ndesc].childend = fd;
+#endif
+
+                       } else {
+                               php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not a valid descriptor spec/mode", Z_STRVAL_PP(ztype));
+                               goto exit_fail;
+                       }
+               }
+
+               zend_hash_move_forward_ex(Z_ARRVAL_P(descriptorspec), &pos);
+               if (++ndesc == MAX_DESCRIPTORS)
+                       break;
+       }
+
+#ifdef PHP_WIN32
+       memset(&si, 0, sizeof(si));
+       si.cb = sizeof(si);
+       si.dwFlags = STARTF_USESTDHANDLES;
+       
+       si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+       si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+       si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+       
+       /* redirect stdin/stdout/stderr if requested */
+       for (i = 0; i < ndesc; i++) {
+               switch(descriptors[i].index) {
+                       case 0:
+                               si.hStdInput = descriptors[i].childend;
+                               break;
+                       case 1:
+                               si.hStdOutput = descriptors[i].childend;
+                               break;
+                       case 2:
+                               si.hStdError = descriptors[i].childend;
+                               break;
+               }
+       }
+
+       
+       memset(&pi, 0, sizeof(pi));
+       
+       command_with_cmd = emalloc(command_len + sizeof(COMSPEC_9X) + 1 + sizeof(" /c "));
+       sprintf(command_with_cmd, "%s /c %s", GetVersion() < 0x80000000 ? COMSPEC_NT : COMSPEC_9X, command);
+       newprocok = CreateProcess(NULL, command_with_cmd, &security, &security, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
+       efree(command_with_cmd);
+
+       if (FALSE == newprocok) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "CreateProcess failed");
+               goto exit_fail;
+       }
+
+       child = pi.hProcess;
+       CloseHandle(pi.hThread);
+
+#else
+       /* the unix way */
+
+       child = fork();
+
+       if (child == 0) {
+               /* this is the child process */
+
+               /* close those descriptors that we just opened for the parent stuff,
+                * dup new descriptors into required descriptors and close the original
+                * cruft */
+               for (i = 0; i < ndesc; i++) {
+                       switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) {
+                               case DESC_PIPE:
+                                       close(descriptors[i].parentend);
+                                       break;
+                       }
+                       if (dup2(descriptors[i].childend, descriptors[i].index) < 0)
+                               perror("dup2");
+                       if (descriptors[i].childend != descriptors[i].index)
+                               close(descriptors[i].childend);
+               }
+
+               execl("/bin/sh", "sh", "-c", command, NULL);
+               _exit(127);
+
+       } else if (child < 0) {
+               /* failed to fork() */
+
+               /* clean up all the descriptors */
+               for (i = 0; i < ndesc; i++) {
+                       close(descriptors[i].childend);
+                       close(descriptors[i].parentend);
+               }
+
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "fork failed - %s", strerror(errno));
+
+               goto exit_fail;
+
+       }
+#endif
+       /* we forked/spawned and this is the parent */
+
+       efree(command);
+       array_init(pipes);
+
+       /* clean up all the child ends and then open streams on the parent
+        * ends, where appropriate */
+       for (i = 0; i < ndesc; i++) {
+               FILE *fp;
+               char *mode_string=NULL;
+               php_stream *stream;
+
+               close_descriptor(descriptors[i].childend);
+
+               switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) {
+                       case DESC_PIPE:
+                               switch(descriptors[i].mode_flags) {
+#ifdef PHP_WIN32
+                                       case O_WRONLY|O_BINARY:
+                                               mode_string = "wb";
+                                               break;
+                                       case O_RDONLY|O_BINARY:
+                                               mode_string = "rb";
+                                               break;
+#endif
+                                       case O_WRONLY:
+                                               mode_string = "w";
+                                               break;
+                                       case O_RDONLY:
+                                               mode_string = "r";
+                                               break;
+                               }
+#ifdef PHP_WIN32
+                               fp = _fdopen(_open_osfhandle((long)descriptors[i].parentend,
+                                                       descriptors[i].mode_flags), mode_string);
+#else
+                               fp = fdopen(descriptors[i].parentend, mode_string);
+#endif
+                               if (fp) {
+                                       stream = php_stream_fopen_from_file(fp, mode_string);
+                                       if (stream) {
+                                               zval *retfp;
+
+                                               MAKE_STD_ZVAL(retfp);
+                                               php_stream_to_zval(stream, retfp);
+                                               add_index_zval(pipes, descriptors[i].index, retfp);
+                                       }
+                               }
+                               break;
+               }
+       }
+
+       ZEND_REGISTER_RESOURCE(return_value, (void*)child, le_proc_open);
+       return;
+
+exit_fail:
+       efree(command);
+       RETURN_FALSE;
+
+}
+/* }}} */
+
+#endif /* PHP_CAN_SUPPORT_PROC_OPEN */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */