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, ©, 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>