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