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