Annotation of embedaddon/php/ext/standard/proc_open.c, revision 1.1.1.4

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

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