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