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