Annotation of embedaddon/php/ext/standard/proc_open.c, revision 1.1.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, &copy, 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>