File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / standard / proc_open.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:03:57 2014 UTC (10 years, 1 month ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29, HEAD
php 5.4.29

    1: /*
    2:    +----------------------------------------------------------------------+
    3:    | PHP Version 5                                                        |
    4:    +----------------------------------------------------------------------+
    5:    | Copyright (c) 1997-2014 The PHP Group                                |
    6:    +----------------------------------------------------------------------+
    7:    | This source file is subject to version 3.01 of the PHP license,      |
    8:    | that is bundled with this package in the file LICENSE, and is        |
    9:    | available through the world-wide-web at the following url:           |
   10:    | http://www.php.net/license/3_01.txt                                  |
   11:    | If you did not receive a copy of the PHP license and are unable to   |
   12:    | obtain it through the world-wide-web, please send a note to          |
   13:    | license@php.net so we can mail you a copy immediately.               |
   14:    +----------------------------------------------------------------------+
   15:    | Author: Wez Furlong <wez@thebrainroom.com>                           |
   16:    +----------------------------------------------------------------------+
   17:  */
   18: /* $Id: proc_open.c,v 1.1.1.4 2014/06/15 20:03:57 misho Exp $ */
   19: 
   20: #if 0 && (defined(__linux__) || defined(sun) || defined(__IRIX__))
   21: # define _BSD_SOURCE 		/* linux wants this when XOPEN mode is on */
   22: # define _BSD_COMPAT		/* irix: uint */
   23: # define _XOPEN_SOURCE 500  /* turn on Unix98 */
   24: # define __EXTENSIONS__	1	/* Solaris: uint */
   25: #endif
   26: 
   27: #include "php.h"
   28: #include <stdio.h>
   29: #include <ctype.h>
   30: #include "php_string.h"
   31: #include "ext/standard/head.h"
   32: #include "ext/standard/basic_functions.h"
   33: #include "ext/standard/file.h"
   34: #include "exec.h"
   35: #include "php_globals.h"
   36: #include "SAPI.h"
   37: 
   38: #ifdef NETWARE
   39: #include <proc.h>
   40: #include <library.h>
   41: #endif
   42: 
   43: #if HAVE_SYS_WAIT_H
   44: #include <sys/wait.h>
   45: #endif
   46: #if HAVE_SIGNAL_H
   47: #include <signal.h>
   48: #endif
   49: 
   50: #if HAVE_SYS_STAT_H
   51: #include <sys/stat.h>
   52: #endif
   53: #if HAVE_FCNTL_H
   54: #include <fcntl.h>
   55: #endif
   56: 
   57: /* This symbol is defined in ext/standard/config.m4.
   58:  * Essentially, it is set if you HAVE_FORK || PHP_WIN32
   59:  * Other platforms may modify that configure check and add suitable #ifdefs
   60:  * around the alternate code.
   61:  * */
   62: #ifdef PHP_CAN_SUPPORT_PROC_OPEN
   63: 
   64: #if 0 && HAVE_PTSNAME && HAVE_GRANTPT && HAVE_UNLOCKPT && HAVE_SYS_IOCTL_H && HAVE_TERMIOS_H
   65: # include <sys/ioctl.h>
   66: # include <termios.h>
   67: # define PHP_CAN_DO_PTS	1
   68: #endif
   69: 
   70: #include "proc_open.h"
   71: 
   72: static int le_proc_open;
   73: 
   74: /* {{{ _php_array_to_envp */
   75: static php_process_env_t _php_array_to_envp(zval *environment, int is_persistent TSRMLS_DC)
   76: {
   77: 	zval **element;
   78: 	php_process_env_t env;
   79: 	char *string_key, *data;
   80: #ifndef PHP_WIN32
   81: 	char **ep;
   82: #endif
   83: 	char *p;
   84: 	uint string_length, cnt, l, sizeenv=0, el_len;
   85: 	ulong num_key;
   86: 	HashTable *target_hash;
   87: 	HashPosition pos;
   88: 
   89: 	memset(&env, 0, sizeof(env));
   90: 
   91: 	if (!environment) {
   92: 		return env;
   93: 	}
   94: 
   95: 	cnt = zend_hash_num_elements(Z_ARRVAL_P(environment));
   96: 
   97: 	if (cnt < 1) {
   98: #ifndef PHP_WIN32
   99: 		env.envarray = (char **) pecalloc(1, sizeof(char *), is_persistent);
  100: #endif
  101: 		env.envp = (char *) pecalloc(4, 1, is_persistent);
  102: 		return env;
  103: 	}
  104: 
  105: 	target_hash = HASH_OF(environment);
  106: 	if (!target_hash) {
  107: 		return env;
  108: 	}
  109: 
  110: 	/* first, we have to get the size of all the elements in the hash */
  111: 	for (zend_hash_internal_pointer_reset_ex(target_hash, &pos);
  112: 			zend_hash_get_current_data_ex(target_hash, (void **) &element, &pos) == SUCCESS;
  113: 			zend_hash_move_forward_ex(target_hash, &pos)) {
  114: 
  115: 		if (Z_TYPE_PP(element) != IS_STRING) {
  116: 			zval tmp;
  117: 
  118: 			MAKE_COPY_ZVAL(element, &tmp);
  119: 			convert_to_string(&tmp);
  120: 			el_len = Z_STRLEN(tmp);
  121: 
  122: 			zval_dtor(&tmp);
  123: 		} else {
  124: 			el_len = Z_STRLEN_PP(element);
  125: 		}
  126: 		if (el_len == 0) {
  127: 			continue;
  128: 		}
  129: 
  130: 		sizeenv += el_len+1;
  131: 
  132: 		switch (zend_hash_get_current_key_ex(target_hash, &string_key, &string_length, &num_key, 0, &pos)) {
  133: 			case HASH_KEY_IS_STRING:
  134: 				if (string_length == 0) {
  135: 					continue;
  136: 				}
  137: 				sizeenv += string_length;
  138: 				break;
  139: 		}
  140: 	}
  141: 
  142: #ifndef PHP_WIN32
  143: 	ep = env.envarray = (char **) pecalloc(cnt + 1, sizeof(char *), is_persistent);
  144: #endif
  145: 	p = env.envp = (char *) pecalloc(sizeenv + 4, 1, is_persistent);
  146: 
  147: 	for (zend_hash_internal_pointer_reset_ex(target_hash, &pos);
  148: 			zend_hash_get_current_data_ex(target_hash, (void **) &element, &pos) == SUCCESS;
  149: 			zend_hash_move_forward_ex(target_hash, &pos)) {
  150: 		zval tmp;
  151: 
  152: 		if (Z_TYPE_PP(element) != IS_STRING) {
  153: 			MAKE_COPY_ZVAL(element, &tmp);
  154: 			convert_to_string(&tmp);
  155: 		} else {
  156: 			tmp = **element;
  157: 		}
  158: 
  159: 		el_len = Z_STRLEN(tmp);
  160: 
  161: 		if (el_len == 0) {
  162: 			goto next_element;
  163: 		}
  164: 
  165: 		data = Z_STRVAL(tmp);
  166: 		switch (zend_hash_get_current_key_ex(target_hash, &string_key, &string_length, &num_key, 0, &pos)) {
  167: 			case HASH_KEY_IS_STRING:
  168: 				if (string_length == 0) {
  169: 					goto next_element;
  170: 				}
  171: 
  172: 				l = string_length + el_len + 1;
  173: 				memcpy(p, string_key, string_length);
  174: 				strncat(p, "=", 1);
  175: 				strncat(p, data, el_len);
  176: 
  177: #ifndef PHP_WIN32
  178: 				*ep = p;
  179: 				++ep;
  180: #endif
  181: 				p += l;
  182: 				break;
  183: 			case HASH_KEY_IS_LONG:
  184: 				memcpy(p,data,el_len);
  185: #ifndef PHP_WIN32
  186: 				*ep = p;
  187: 				++ep;
  188: #endif
  189: 				p += el_len + 1;
  190: 				break;
  191: 			case HASH_KEY_NON_EXISTANT:
  192: 				break;
  193: 		}
  194: 
  195: next_element:
  196: 		if (Z_TYPE_PP(element) != IS_STRING) {
  197: 			zval_dtor(&tmp);
  198: 		}
  199: 	}
  200: 
  201: 	assert((uint)(p - env.envp) <= sizeenv);
  202: 
  203: 	zend_hash_internal_pointer_reset_ex(target_hash, &pos);
  204: 
  205: 	return env;
  206: }
  207: /* }}} */
  208: 
  209: /* {{{ _php_free_envp */
  210: static void _php_free_envp(php_process_env_t env, int is_persistent)
  211: {
  212: #ifndef PHP_WIN32
  213: 	if (env.envarray) {
  214: 		pefree(env.envarray, is_persistent);
  215: 	}
  216: #endif
  217: 	if (env.envp) {
  218: 		pefree(env.envp, is_persistent);
  219: 	}
  220: }
  221: /* }}} */
  222: 
  223: /* {{{ proc_open_rsrc_dtor */
  224: static void proc_open_rsrc_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
  225: {
  226: 	struct php_process_handle *proc = (struct php_process_handle*)rsrc->ptr;
  227: 	int i;
  228: #ifdef PHP_WIN32
  229: 	DWORD wstatus;
  230: #elif HAVE_SYS_WAIT_H
  231: 	int wstatus;
  232: 	pid_t wait_pid;
  233: #endif
  234: 
  235: 	/* Close all handles to avoid a deadlock */
  236: 	for (i = 0; i < proc->npipes; i++) {
  237: 		if (proc->pipes[i] != 0) {
  238: 			zend_list_delete(proc->pipes[i]);
  239: 			proc->pipes[i] = 0;
  240: 		}
  241: 	}
  242: 
  243: #ifdef PHP_WIN32
  244: 	WaitForSingleObject(proc->childHandle, INFINITE);
  245: 	GetExitCodeProcess(proc->childHandle, &wstatus);
  246: 	FG(pclose_ret) = wstatus;
  247: 	CloseHandle(proc->childHandle);
  248: 
  249: #elif HAVE_SYS_WAIT_H
  250: 
  251: 	do {
  252: 		wait_pid = waitpid(proc->child, &wstatus, 0);
  253: 	} while (wait_pid == -1 && errno == EINTR);
  254: 
  255: 	if (wait_pid == -1) {
  256: 		FG(pclose_ret) = -1;
  257: 	} else {
  258: 		if (WIFEXITED(wstatus))
  259: 			wstatus = WEXITSTATUS(wstatus);
  260: 		FG(pclose_ret) = wstatus;
  261: 	}
  262: 
  263: #else
  264: 	FG(pclose_ret) = -1;
  265: #endif
  266: 	_php_free_envp(proc->env, proc->is_persistent);
  267: 	pefree(proc->command, proc->is_persistent);
  268: 	pefree(proc, proc->is_persistent);
  269: 
  270: }
  271: /* }}} */
  272: 
  273: /* {{{ PHP_MINIT_FUNCTION(proc_open) */
  274: PHP_MINIT_FUNCTION(proc_open)
  275: {
  276: 	le_proc_open = zend_register_list_destructors_ex(proc_open_rsrc_dtor, NULL, "process", module_number);
  277: 	return SUCCESS;
  278: }
  279: /* }}} */
  280: 
  281: /* {{{ proto bool proc_terminate(resource process [, long signal])
  282:    kill a process opened by proc_open */
  283: PHP_FUNCTION(proc_terminate)
  284: {
  285: 	zval *zproc;
  286: 	struct php_process_handle *proc;
  287: 	long sig_no = SIGTERM;
  288: 
  289: 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &zproc, &sig_no) == FAILURE) {
  290: 		RETURN_FALSE;
  291: 	}
  292: 
  293: 	ZEND_FETCH_RESOURCE(proc, struct php_process_handle *, &zproc, -1, "process", le_proc_open);
  294: 
  295: #ifdef PHP_WIN32
  296: 	if (TerminateProcess(proc->childHandle, 255)) {
  297: 		RETURN_TRUE;
  298: 	} else {
  299: 		RETURN_FALSE;
  300: 	}
  301: #else
  302: 	if (kill(proc->child, sig_no) == 0) {
  303: 		RETURN_TRUE;
  304: 	} else {
  305: 		RETURN_FALSE;
  306: 	}
  307: #endif
  308: }
  309: /* }}} */
  310: 
  311: /* {{{ proto int proc_close(resource process)
  312:    close a process opened by proc_open */
  313: PHP_FUNCTION(proc_close)
  314: {
  315: 	zval *zproc;
  316: 	struct php_process_handle *proc;
  317: 
  318: 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zproc) == FAILURE) {
  319: 		RETURN_FALSE;
  320: 	}
  321: 
  322: 	ZEND_FETCH_RESOURCE(proc, struct php_process_handle *, &zproc, -1, "process", le_proc_open);
  323: 
  324: 	zend_list_delete(Z_LVAL_P(zproc));
  325: 	RETURN_LONG(FG(pclose_ret));
  326: }
  327: /* }}} */
  328: 
  329: /* {{{ proto array proc_get_status(resource process)
  330:    get information about a process opened by proc_open */
  331: PHP_FUNCTION(proc_get_status)
  332: {
  333: 	zval *zproc;
  334: 	struct php_process_handle *proc;
  335: #ifdef PHP_WIN32
  336: 	DWORD wstatus;
  337: #elif HAVE_SYS_WAIT_H
  338: 	int wstatus;
  339: 	pid_t wait_pid;
  340: #endif
  341: 	int running = 1, signaled = 0, stopped = 0;
  342: 	int exitcode = -1, termsig = 0, stopsig = 0;
  343: 
  344: 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zproc) == FAILURE) {
  345: 		RETURN_FALSE;
  346: 	}
  347: 
  348: 	ZEND_FETCH_RESOURCE(proc, struct php_process_handle *, &zproc, -1, "process", le_proc_open);
  349: 
  350: 	array_init(return_value);
  351: 
  352: 	add_assoc_string(return_value, "command", proc->command, 1);
  353: 	add_assoc_long(return_value, "pid", (long) proc->child);
  354: 
  355: #ifdef PHP_WIN32
  356: 
  357: 	GetExitCodeProcess(proc->childHandle, &wstatus);
  358: 
  359: 	running = wstatus == STILL_ACTIVE;
  360: 	exitcode = running ? -1 : wstatus;
  361: 
  362: #elif HAVE_SYS_WAIT_H
  363: 
  364: 	errno = 0;
  365: 	wait_pid = waitpid(proc->child, &wstatus, WNOHANG|WUNTRACED);
  366: 
  367: 	if (wait_pid == proc->child) {
  368: 		if (WIFEXITED(wstatus)) {
  369: 			running = 0;
  370: 			exitcode = WEXITSTATUS(wstatus);
  371: 		}
  372: 		if (WIFSIGNALED(wstatus)) {
  373: 			running = 0;
  374: 			signaled = 1;
  375: #ifdef NETWARE
  376: 			termsig = WIFTERMSIG(wstatus);
  377: #else
  378: 			termsig = WTERMSIG(wstatus);
  379: #endif
  380: 		}
  381: 		if (WIFSTOPPED(wstatus)) {
  382: 			stopped = 1;
  383: 			stopsig = WSTOPSIG(wstatus);
  384: 		}
  385: 	} else if (wait_pid == -1) {
  386: 		running = 0;
  387: 	}
  388: #endif
  389: 
  390: 	add_assoc_bool(return_value, "running", running);
  391: 	add_assoc_bool(return_value, "signaled", signaled);
  392: 	add_assoc_bool(return_value, "stopped", stopped);
  393: 	add_assoc_long(return_value, "exitcode", exitcode);
  394: 	add_assoc_long(return_value, "termsig", termsig);
  395: 	add_assoc_long(return_value, "stopsig", stopsig);
  396: }
  397: /* }}} */
  398: 
  399: /* {{{ handy definitions for portability/readability */
  400: #ifdef PHP_WIN32
  401: # define pipe(pair)		(CreatePipe(&pair[0], &pair[1], &security, 0) ? 0 : -1)
  402: 
  403: # define COMSPEC_NT	"cmd.exe"
  404: 
  405: static inline HANDLE dup_handle(HANDLE src, BOOL inherit, BOOL closeorig)
  406: {
  407: 	HANDLE copy, self = GetCurrentProcess();
  408: 
  409: 	if (!DuplicateHandle(self, src, self, &copy, 0, inherit, DUPLICATE_SAME_ACCESS |
  410: 				(closeorig ? DUPLICATE_CLOSE_SOURCE : 0)))
  411: 		return NULL;
  412: 	return copy;
  413: }
  414: 
  415: static inline HANDLE dup_fd_as_handle(int fd)
  416: {
  417: 	return dup_handle((HANDLE)_get_osfhandle(fd), TRUE, FALSE);
  418: }
  419: 
  420: # define close_descriptor(fd)	CloseHandle(fd)
  421: #else
  422: # define close_descriptor(fd)	close(fd)
  423: #endif
  424: 
  425: #define DESC_PIPE		1
  426: #define DESC_FILE		2
  427: #define DESC_PARENT_MODE_WRITE	8
  428: 
  429: struct php_proc_open_descriptor_item {
  430: 	int index; 							/* desired fd number in child process */
  431: 	php_file_descriptor_t parentend, childend;	/* fds for pipes in parent/child */
  432: 	int mode;							/* mode for proc_open code */
  433: 	int mode_flags;						/* mode flags for opening fds */
  434: };
  435: /* }}} */
  436: 
  437: /* {{{ proto resource proc_open(string command, array descriptorspec, array &pipes [, string cwd [, array env [, array other_options]]])
  438:    Run a process with more control over it's file descriptors */
  439: PHP_FUNCTION(proc_open)
  440: {
  441: 	char *command, *cwd=NULL;
  442: 	int command_len, cwd_len = 0;
  443: 	zval *descriptorspec;
  444: 	zval *pipes;
  445: 	zval *environment = NULL;
  446: 	zval *other_options = NULL;
  447: 	php_process_env_t env;
  448: 	int ndesc = 0;
  449: 	int i;
  450: 	zval **descitem = NULL;
  451: 	HashPosition pos;
  452: 	struct php_proc_open_descriptor_item descriptors[PHP_PROC_OPEN_MAX_DESCRIPTORS];
  453: #ifdef PHP_WIN32
  454: 	PROCESS_INFORMATION pi;
  455: 	HANDLE childHandle;
  456: 	STARTUPINFO si;
  457: 	BOOL newprocok;
  458: 	SECURITY_ATTRIBUTES security;
  459: 	DWORD dwCreateFlags = 0;
  460: 	char *command_with_cmd;
  461: 	UINT old_error_mode;
  462: #endif
  463: #ifdef NETWARE
  464: 	char** child_argv = NULL;
  465: 	char* command_dup = NULL;
  466: 	char* orig_cwd = NULL;
  467: 	int command_num_args = 0;
  468: 	wiring_t channel;
  469: #endif
  470: 	php_process_id_t child;
  471: 	struct php_process_handle *proc;
  472: 	int is_persistent = 0; /* TODO: ensure that persistent procs will work */
  473: #ifdef PHP_WIN32
  474: 	int suppress_errors = 0;
  475: 	int bypass_shell = 0;
  476: #endif
  477: #if PHP_CAN_DO_PTS
  478: 	php_file_descriptor_t dev_ptmx = -1;	/* master */
  479: 	php_file_descriptor_t slave_pty = -1;
  480: #endif
  481: 
  482: 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "saz|s!a!a!", &command,
  483: 				&command_len, &descriptorspec, &pipes, &cwd, &cwd_len, &environment,
  484: 				&other_options) == FAILURE) {
  485: 		RETURN_FALSE;
  486: 	}
  487: 
  488: 	command = pestrdup(command, is_persistent);
  489: 
  490: #ifdef PHP_WIN32
  491: 	if (other_options) {
  492: 		zval **item;
  493: 		if (SUCCESS == zend_hash_find(Z_ARRVAL_P(other_options), "suppress_errors", sizeof("suppress_errors"), (void**)&item)) {
  494: 			if ((Z_TYPE_PP(item) == IS_BOOL || Z_TYPE_PP(item) == IS_LONG) &&
  495: 			    Z_LVAL_PP(item)) {
  496: 				suppress_errors = 1;
  497: 			}
  498: 		}
  499: 		if (SUCCESS == zend_hash_find(Z_ARRVAL_P(other_options), "bypass_shell", sizeof("bypass_shell"), (void**)&item)) {
  500: 			if ((Z_TYPE_PP(item) == IS_BOOL || Z_TYPE_PP(item) == IS_LONG) &&
  501: 			    Z_LVAL_PP(item)) {
  502: 				bypass_shell = 1;
  503: 			}
  504: 		}
  505: 	}
  506: #endif
  507: 
  508: 	command_len = strlen(command);
  509: 
  510: 	if (environment) {
  511: 		env = _php_array_to_envp(environment, is_persistent TSRMLS_CC);
  512: 	} else {
  513: 		memset(&env, 0, sizeof(env));
  514: 	}
  515: 
  516: 	memset(descriptors, 0, sizeof(descriptors));
  517: 
  518: #ifdef PHP_WIN32
  519: 	/* we use this to allow the child to inherit handles */
  520: 	memset(&security, 0, sizeof(security));
  521: 	security.nLength = sizeof(security);
  522: 	security.bInheritHandle = TRUE;
  523: 	security.lpSecurityDescriptor = NULL;
  524: #endif
  525: 
  526: 	/* walk the descriptor spec and set up files/pipes */
  527: 	zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(descriptorspec), &pos);
  528: 	while (zend_hash_get_current_data_ex(Z_ARRVAL_P(descriptorspec), (void **)&descitem, &pos) == SUCCESS) {
  529: 		char *str_index;
  530: 		ulong nindex;
  531: 		zval **ztype;
  532: 
  533: 		str_index = NULL;
  534: 		zend_hash_get_current_key_ex(Z_ARRVAL_P(descriptorspec), &str_index, NULL, &nindex, 0, &pos);
  535: 
  536: 		if (str_index) {
  537: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "descriptor spec must be an integer indexed array");
  538: 			goto exit_fail;
  539: 		}
  540: 
  541: 		descriptors[ndesc].index = nindex;
  542: 
  543: 		if (Z_TYPE_PP(descitem) == IS_RESOURCE) {
  544: 			/* should be a stream - try and dup the descriptor */
  545: 			php_stream *stream;
  546: 			int fd;
  547: 
  548: 			php_stream_from_zval(stream, descitem);
  549: 
  550: 			if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_FD, (void **)&fd, REPORT_ERRORS)) {
  551: 				goto exit_fail;
  552: 			}
  553: 
  554: #ifdef PHP_WIN32
  555: 			descriptors[ndesc].childend = dup_fd_as_handle(fd);
  556: 			if (descriptors[ndesc].childend == NULL) {
  557: 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to dup File-Handle for descriptor %d", nindex);
  558: 				goto exit_fail;
  559: 			}
  560: #else
  561: 			descriptors[ndesc].childend = dup(fd);
  562: 			if (descriptors[ndesc].childend < 0) {
  563: 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to dup File-Handle for descriptor %ld - %s", nindex, strerror(errno));
  564: 				goto exit_fail;
  565: 			}
  566: #endif
  567: 			descriptors[ndesc].mode = DESC_FILE;
  568: 
  569: 		} else if (Z_TYPE_PP(descitem) != IS_ARRAY) {
  570: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Descriptor item must be either an array or a File-Handle");
  571: 			goto exit_fail;
  572: 		} else {
  573: 
  574: 			if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 0, (void **)&ztype) == SUCCESS) {
  575: 				convert_to_string_ex(ztype);
  576: 			} else {
  577: 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing handle qualifier in array");
  578: 				goto exit_fail;
  579: 			}
  580: 
  581: 			if (strcmp(Z_STRVAL_PP(ztype), "pipe") == 0) {
  582: 				php_file_descriptor_t newpipe[2];
  583: 				zval **zmode;
  584: 
  585: 				if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 1, (void **)&zmode) == SUCCESS) {
  586: 					convert_to_string_ex(zmode);
  587: 				} else {
  588: 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing mode parameter for 'pipe'");
  589: 					goto exit_fail;
  590: 				}
  591: 
  592: 				descriptors[ndesc].mode = DESC_PIPE;
  593: 
  594: 				if (0 != pipe(newpipe)) {
  595: 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to create pipe %s", strerror(errno));
  596: 					goto exit_fail;
  597: 				}
  598: 
  599: 				if (strncmp(Z_STRVAL_PP(zmode), "w", 1) != 0) {
  600: 					descriptors[ndesc].parentend = newpipe[1];
  601: 					descriptors[ndesc].childend = newpipe[0];
  602: 					descriptors[ndesc].mode |= DESC_PARENT_MODE_WRITE;
  603: 				} else {
  604: 					descriptors[ndesc].parentend = newpipe[0];
  605: 					descriptors[ndesc].childend = newpipe[1];
  606: 				}
  607: #ifdef PHP_WIN32
  608: 				/* don't let the child inherit the parent side of the pipe */
  609: 				descriptors[ndesc].parentend = dup_handle(descriptors[ndesc].parentend, FALSE, TRUE);
  610: #endif
  611: 				descriptors[ndesc].mode_flags = descriptors[ndesc].mode & DESC_PARENT_MODE_WRITE ? O_WRONLY : O_RDONLY;
  612: #ifdef PHP_WIN32
  613: 				if (Z_STRLEN_PP(zmode) >= 2 && Z_STRVAL_PP(zmode)[1] == 'b')
  614: 					descriptors[ndesc].mode_flags |= O_BINARY;
  615: #endif
  616: 
  617: 			} else if (strcmp(Z_STRVAL_PP(ztype), "file") == 0) {
  618: 				zval **zfile, **zmode;
  619: 				int fd;
  620: 				php_stream *stream;
  621: 
  622: 				descriptors[ndesc].mode = DESC_FILE;
  623: 
  624: 				if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 1, (void **)&zfile) == SUCCESS) {
  625: 					convert_to_string_ex(zfile);
  626: 				} else {
  627: 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing file name parameter for 'file'");
  628: 					goto exit_fail;
  629: 				}
  630: 
  631: 				if (zend_hash_index_find(Z_ARRVAL_PP(descitem), 2, (void **)&zmode) == SUCCESS) {
  632: 					convert_to_string_ex(zmode);
  633: 				} else {
  634: 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing mode parameter for 'file'");
  635: 					goto exit_fail;
  636: 				}
  637: 
  638: 				/* try a wrapper */
  639: 				stream = php_stream_open_wrapper(Z_STRVAL_PP(zfile), Z_STRVAL_PP(zmode),
  640: 						REPORT_ERRORS|STREAM_WILL_CAST, NULL);
  641: 
  642: 				/* force into an fd */
  643: 				if (stream == NULL || FAILURE == php_stream_cast(stream,
  644: 							PHP_STREAM_CAST_RELEASE|PHP_STREAM_AS_FD,
  645: 							(void **)&fd, REPORT_ERRORS)) {
  646: 					goto exit_fail;
  647: 				}
  648: 
  649: #ifdef PHP_WIN32
  650: 				descriptors[ndesc].childend = dup_fd_as_handle(fd);
  651: 				_close(fd);
  652: 
  653: 				/* simulate the append mode by fseeking to the end of the file
  654: 				this introduces a potential race-condition, but it is the best we can do, though */
  655: 				if (strchr(Z_STRVAL_PP(zmode), 'a')) {
  656: 					SetFilePointer(descriptors[ndesc].childend, 0, NULL, FILE_END);
  657: 				}
  658: #else
  659: 				descriptors[ndesc].childend = fd;
  660: #endif
  661: 			} else if (strcmp(Z_STRVAL_PP(ztype), "pty") == 0) {
  662: #if PHP_CAN_DO_PTS
  663: 				if (dev_ptmx == -1) {
  664: 					/* open things up */
  665: 					dev_ptmx = open("/dev/ptmx", O_RDWR);
  666: 					if (dev_ptmx == -1) {
  667: 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to open /dev/ptmx, errno %d", errno);
  668: 						goto exit_fail;
  669: 					}
  670: 					grantpt(dev_ptmx);
  671: 					unlockpt(dev_ptmx);
  672: 					slave_pty = open(ptsname(dev_ptmx), O_RDWR);
  673: 
  674: 					if (slave_pty == -1) {
  675: 						php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to open slave pty, errno %d", errno);
  676: 						goto exit_fail;
  677: 					}
  678: 				}
  679: 				descriptors[ndesc].mode = DESC_PIPE;
  680: 				descriptors[ndesc].childend = dup(slave_pty);
  681: 				descriptors[ndesc].parentend = dup(dev_ptmx);
  682: 				descriptors[ndesc].mode_flags = O_RDWR;
  683: #else
  684: 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "pty pseudo terminal not supported on this system");
  685: 				goto exit_fail;
  686: #endif
  687: 			} else {
  688: 				php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s is not a valid descriptor spec/mode", Z_STRVAL_PP(ztype));
  689: 				goto exit_fail;
  690: 			}
  691: 		}
  692: 
  693: 		zend_hash_move_forward_ex(Z_ARRVAL_P(descriptorspec), &pos);
  694: 		if (++ndesc == PHP_PROC_OPEN_MAX_DESCRIPTORS)
  695: 			break;
  696: 	}
  697: 
  698: #ifdef PHP_WIN32
  699: 	if (cwd == NULL) {
  700: 		char cur_cwd[MAXPATHLEN];
  701: 		char *getcwd_result;
  702: 		getcwd_result = VCWD_GETCWD(cur_cwd, MAXPATHLEN);
  703: 		if (!getcwd_result) {
  704: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot get current directory");
  705: 			goto exit_fail;
  706: 		}
  707: 	}
  708: 
  709: 	memset(&si, 0, sizeof(si));
  710: 	si.cb = sizeof(si);
  711: 	si.dwFlags = STARTF_USESTDHANDLES;
  712: 
  713: 	si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  714: 	si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  715: 	si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
  716: 
  717: 	/* redirect stdin/stdout/stderr if requested */
  718: 	for (i = 0; i < ndesc; i++) {
  719: 		switch(descriptors[i].index) {
  720: 			case 0:
  721: 				si.hStdInput = descriptors[i].childend;
  722: 				break;
  723: 			case 1:
  724: 				si.hStdOutput = descriptors[i].childend;
  725: 				break;
  726: 			case 2:
  727: 				si.hStdError = descriptors[i].childend;
  728: 				break;
  729: 		}
  730: 	}
  731: 
  732: 
  733: 	memset(&pi, 0, sizeof(pi));
  734: 
  735: 	if (suppress_errors) {
  736: 		old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
  737: 	}
  738: 
  739: 	dwCreateFlags = NORMAL_PRIORITY_CLASS;
  740: 	if(strcmp(sapi_module.name, "cli") != 0) {
  741: 		dwCreateFlags |= CREATE_NO_WINDOW;
  742: 	}
  743: 
  744: 	if (bypass_shell) {
  745: 		newprocok = CreateProcess(NULL, command, &security, &security, TRUE, dwCreateFlags, env.envp, cwd, &si, &pi);
  746: 	} else {
  747: 		spprintf(&command_with_cmd, 0, "%s /c %s", COMSPEC_NT, command);
  748: 
  749: 		newprocok = CreateProcess(NULL, command_with_cmd, &security, &security, TRUE, dwCreateFlags, env.envp, cwd, &si, &pi);
  750: 
  751: 		efree(command_with_cmd);
  752: 	}
  753: 
  754: 	if (suppress_errors) {
  755: 		SetErrorMode(old_error_mode);
  756: 	}
  757: 
  758: 	if (FALSE == newprocok) {
  759: 		DWORD dw = GetLastError();
  760: 
  761: 		/* clean up all the descriptors */
  762: 		for (i = 0; i < ndesc; i++) {
  763: 			CloseHandle(descriptors[i].childend);
  764: 			if (descriptors[i].parentend) {
  765: 				CloseHandle(descriptors[i].parentend);
  766: 			}
  767: 		}
  768: 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "CreateProcess failed, error code - %u", dw);
  769: 		goto exit_fail;
  770: 	}
  771: 
  772: 	childHandle = pi.hProcess;
  773: 	child       = pi.dwProcessId;
  774: 	CloseHandle(pi.hThread);
  775: 
  776: #elif defined(NETWARE)
  777: 	if (cwd) {
  778: 		orig_cwd = getcwd(NULL, PATH_MAX);
  779: 		chdir2(cwd);
  780: 	}
  781: 	channel.infd = descriptors[0].childend;
  782: 	channel.outfd = descriptors[1].childend;
  783: 	channel.errfd = -1;
  784: 	/* Duplicate the command as processing downwards will modify it*/
  785: 	command_dup = strdup(command);
  786: 	if (!command_dup) {
  787: 		goto exit_fail;
  788: 	}
  789: 	/* get a number of args */
  790: 	construct_argc_argv(command_dup, NULL, &command_num_args, NULL);
  791: 	child_argv = (char**) malloc((command_num_args + 1) * sizeof(char*));
  792: 	if(!child_argv) {
  793: 		free(command_dup);
  794: 		if (cwd && orig_cwd) {
  795: 			chdir2(orig_cwd);
  796: 			free(orig_cwd);
  797: 		}
  798: 	}
  799: 	/* fill the child arg vector */
  800: 	construct_argc_argv(command_dup, NULL, &command_num_args, child_argv);
  801: 	child_argv[command_num_args] = NULL;
  802: 	child = procve(child_argv[0], PROC_DETACHED|PROC_INHERIT_CWD, NULL, &channel, NULL, NULL, 0, NULL, (const char**)child_argv);
  803: 	free(child_argv);
  804: 	free(command_dup);
  805: 	if (cwd && orig_cwd) {
  806: 		chdir2(orig_cwd);
  807: 		free(orig_cwd);
  808: 	}
  809: 	if (child < 0) {
  810: 		/* failed to fork() */
  811: 		/* clean up all the descriptors */
  812: 		for (i = 0; i < ndesc; i++) {
  813: 			close(descriptors[i].childend);
  814: 			if (descriptors[i].parentend)
  815: 				close(descriptors[i].parentend);
  816: 		}
  817: 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "procve failed - %s", strerror(errno));
  818: 		goto exit_fail;
  819: 	}
  820: #elif HAVE_FORK
  821: 	/* the unix way */
  822: 	child = fork();
  823: 
  824: 	if (child == 0) {
  825: 		/* this is the child process */
  826: 
  827: #if PHP_CAN_DO_PTS
  828: 		if (dev_ptmx >= 0) {
  829: 			int my_pid = getpid();
  830: 
  831: #ifdef TIOCNOTTY
  832: 			/* detach from original tty. Might only need this if isatty(0) is true */
  833: 			ioctl(0,TIOCNOTTY,NULL);
  834: #else
  835: 			setsid();
  836: #endif
  837: 			/* become process group leader */
  838: 			setpgid(my_pid, my_pid);
  839: 			tcsetpgrp(0, my_pid);
  840: 		}
  841: #endif
  842: 
  843: 		/* close those descriptors that we just opened for the parent stuff,
  844: 		 * dup new descriptors into required descriptors and close the original
  845: 		 * cruft */
  846: 		for (i = 0; i < ndesc; i++) {
  847: 			switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) {
  848: 				case DESC_PIPE:
  849: 					close(descriptors[i].parentend);
  850: 					break;
  851: 			}
  852: 			if (dup2(descriptors[i].childend, descriptors[i].index) < 0)
  853: 				perror("dup2");
  854: 			if (descriptors[i].childend != descriptors[i].index)
  855: 				close(descriptors[i].childend);
  856: 		}
  857: 
  858: #if PHP_CAN_DO_PTS
  859: 		if (dev_ptmx >= 0) {
  860: 			close(dev_ptmx);
  861: 			close(slave_pty);
  862: 		}
  863: #endif
  864: 
  865: 		if (cwd) {
  866: 			php_ignore_value(chdir(cwd));
  867: 		}
  868: 
  869: 		if (env.envarray) {
  870: 			execle("/bin/sh", "sh", "-c", command, NULL, env.envarray);
  871: 		} else {
  872: 			execl("/bin/sh", "sh", "-c", command, NULL);
  873: 		}
  874: 		_exit(127);
  875: 
  876: 	} else if (child < 0) {
  877: 		/* failed to fork() */
  878: 
  879: 		/* clean up all the descriptors */
  880: 		for (i = 0; i < ndesc; i++) {
  881: 			close(descriptors[i].childend);
  882: 			if (descriptors[i].parentend)
  883: 				close(descriptors[i].parentend);
  884: 		}
  885: 
  886: 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "fork failed - %s", strerror(errno));
  887: 
  888: 		goto exit_fail;
  889: 
  890: 	}
  891: #else
  892: # error You lose (configure should not have let you get here)
  893: #endif
  894: 	/* we forked/spawned and this is the parent */
  895: 
  896: 	proc = (struct php_process_handle*)pemalloc(sizeof(struct php_process_handle), is_persistent);
  897: 	proc->is_persistent = is_persistent;
  898: 	proc->command = command;
  899: 	proc->npipes = ndesc;
  900: 	proc->child = child;
  901: #ifdef PHP_WIN32
  902: 	proc->childHandle = childHandle;
  903: #endif
  904: 	proc->env = env;
  905: 
  906: 	if (pipes != NULL) {
  907: 		zval_dtor(pipes);
  908: 	}
  909: 	array_init(pipes);
  910: 
  911: #if PHP_CAN_DO_PTS
  912: 	if (dev_ptmx >= 0) {
  913: 		close(dev_ptmx);
  914: 		close(slave_pty);
  915: 	}
  916: #endif
  917: 
  918: 	/* clean up all the child ends and then open streams on the parent
  919: 	 * ends, where appropriate */
  920: 	for (i = 0; i < ndesc; i++) {
  921: 		char *mode_string=NULL;
  922: 		php_stream *stream = NULL;
  923: 
  924: 		close_descriptor(descriptors[i].childend);
  925: 
  926: 		switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) {
  927: 			case DESC_PIPE:
  928: 				switch(descriptors[i].mode_flags) {
  929: #ifdef PHP_WIN32
  930: 					case O_WRONLY|O_BINARY:
  931: 						mode_string = "wb";
  932: 						break;
  933: 					case O_RDONLY|O_BINARY:
  934: 						mode_string = "rb";
  935: 						break;
  936: #endif
  937: 					case O_WRONLY:
  938: 						mode_string = "w";
  939: 						break;
  940: 					case O_RDONLY:
  941: 						mode_string = "r";
  942: 						break;
  943: 					case O_RDWR:
  944: 						mode_string = "r+";
  945: 						break;
  946: 				}
  947: #ifdef PHP_WIN32
  948: 				stream = php_stream_fopen_from_fd(_open_osfhandle((zend_intptr_t)descriptors[i].parentend,
  949: 							descriptors[i].mode_flags), mode_string, NULL);
  950: #else
  951: 				stream = php_stream_fopen_from_fd(descriptors[i].parentend, mode_string, NULL);
  952: # if defined(F_SETFD) && defined(FD_CLOEXEC)
  953: 				/* mark the descriptor close-on-exec, so that it won't be inherited by potential other children */
  954: 				fcntl(descriptors[i].parentend, F_SETFD, FD_CLOEXEC);
  955: # endif
  956: #endif
  957: 				if (stream) {
  958: 					zval *retfp;
  959: 
  960: 					/* nasty hack; don't copy it */
  961: 					stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
  962: 
  963: 					MAKE_STD_ZVAL(retfp);
  964: 					php_stream_to_zval(stream, retfp);
  965: 					add_index_zval(pipes, descriptors[i].index, retfp);
  966: 
  967: 					proc->pipes[i] = Z_LVAL_P(retfp);
  968: 				}
  969: 				break;
  970: 			default:
  971: 				proc->pipes[i] = 0;
  972: 		}
  973: 	}
  974: 
  975: 	ZEND_REGISTER_RESOURCE(return_value, proc, le_proc_open);
  976: 	return;
  977: 
  978: exit_fail:
  979: 	_php_free_envp(env, is_persistent);
  980: 	pefree(command, is_persistent);
  981: #if PHP_CAN_DO_PTS
  982: 	if (dev_ptmx >= 0) {
  983: 		close(dev_ptmx);
  984: 	}
  985: 	if (slave_pty >= 0) {
  986: 		close(slave_pty);
  987: 	}
  988: #endif
  989: 	RETURN_FALSE;
  990: 
  991: }
  992: /* }}} */
  993: 
  994: #endif /* PHP_CAN_SUPPORT_PROC_OPEN */
  995: 
  996: /*
  997:  * Local variables:
  998:  * tab-width: 4
  999:  * c-basic-offset: 4
 1000:  * End:
 1001:  * vim600: sw=4 ts=4 fdm=marker
 1002:  * vim<600: sw=4 ts=4
 1003:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>