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

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