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