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