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