root/ext/standard/proc_open.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _php_array_to_envp
  2. _php_free_envp
  3. proc_open_rsrc_dtor
  4. PHP_MINIT_FUNCTION
  5. PHP_FUNCTION
  6. PHP_FUNCTION
  7. PHP_FUNCTION
  8. dup_handle
  9. dup_fd_as_handle
  10. PHP_FUNCTION

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

/* [<][>][^][v][top][bottom][index][help] */