1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 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 #include "thirdparty/php/standard/proc_open.h"
20 #include "swoole_coroutine_c_api.h"
21
22 using namespace std;
23 using swoole::Coroutine;
24 using swoole::PHPCoroutine;
25 using swoole::coroutine::Socket;
26
27 #if HAVE_SYS_STAT_H
28 #include <sys/stat.h>
29 #endif
30 #if HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33
34 static int le_proc_open;
35 static const char *le_proc_name = "process/coroutine";
36
37 /* {{{ _php_array_to_envp */
_php_array_to_envp(zval * environment)38 static proc_co_env_t _php_array_to_envp(zval *environment) {
39 zval *element;
40 proc_co_env_t env;
41 zend_string *key, *str;
42 char **ep;
43 char *p;
44 size_t cnt, sizeenv = 0;
45 HashTable *env_hash;
46
47 memset(&env, 0, sizeof(env));
48
49 if (!environment) {
50 return env;
51 }
52
53 cnt = zend_hash_num_elements(Z_ARRVAL_P(environment));
54
55 if (cnt < 1) {
56 env.envarray = (char **) ecalloc(1, sizeof(char *));
57 env.envp = (char *) ecalloc(4, 1);
58 return env;
59 }
60
61 ALLOC_HASHTABLE(env_hash);
62 zend_hash_init(env_hash, cnt, NULL, NULL, 0);
63
64 /* first, we have to get the size of all the elements in the hash */
65 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(environment), key, element) {
66 str = zval_get_string(element);
67
68 if (ZSTR_LEN(str) == 0) {
69 zend_string_release(str);
70 continue;
71 }
72
73 sizeenv += ZSTR_LEN(str) + 1;
74
75 if (key && ZSTR_LEN(key)) {
76 sizeenv += ZSTR_LEN(key) + 1;
77 zend_hash_add_ptr(env_hash, key, str);
78 } else {
79 zend_hash_next_index_insert_ptr(env_hash, str);
80 }
81 }
82 ZEND_HASH_FOREACH_END();
83
84 ep = env.envarray = (char **) ecalloc(cnt + 1, sizeof(char *));
85 p = env.envp = (char *) ecalloc(sizeenv + 4, 1);
86
87 void *v1, *v2;
88 ZEND_HASH_FOREACH_STR_KEY_PTR(env_hash, v1, v2) {
89 key = (zend_string *) v1;
90 str = (zend_string *) v2;
91 *ep = p;
92 ++ep;
93
94 if (key) {
95 memcpy(p, ZSTR_VAL(key), ZSTR_LEN(key));
96 p += ZSTR_LEN(key);
97 *p++ = '=';
98 }
99
100 memcpy(p, ZSTR_VAL(str), ZSTR_LEN(str));
101 p += ZSTR_LEN(str);
102 *p++ = '\0';
103 zend_string_release(str);
104 }
105 ZEND_HASH_FOREACH_END();
106
107 assert((uint32_t)(p - env.envp) <= sizeenv);
108
109 zend_hash_destroy(env_hash);
110 FREE_HASHTABLE(env_hash);
111
112 return env;
113 }
114 /* }}} */
115
116 /* {{{ _php_free_envp */
_php_free_envp(proc_co_env_t env,int is_persistent)117 static void _php_free_envp(proc_co_env_t env, int is_persistent) {
118 if (env.envarray) {
119 pefree(env.envarray, is_persistent);
120 }
121 if (env.envp) {
122 pefree(env.envp, is_persistent);
123 }
124 }
125 /* }}} */
126
proc_co_rsrc_dtor(zend_resource * rsrc)127 static void proc_co_rsrc_dtor(zend_resource *rsrc) {
128 proc_co_t *proc = (proc_co_t *) rsrc->ptr;
129 int i;
130 int wstatus = 0;
131
132 /* Close all handles to avoid a deadlock */
133 for (i = 0; i < proc->npipes; i++) {
134 if (proc->pipes[i] != 0) {
135 GC_DELREF(proc->pipes[i]);
136 zend_list_close(proc->pipes[i]);
137 proc->pipes[i] = 0;
138 }
139 }
140
141 if (proc->running) {
142 if (::waitpid(proc->child, &wstatus, WNOHANG) == 0) {
143 swoole_coroutine_waitpid(proc->child, &wstatus, 0);
144 }
145 }
146 if (proc->wstatus) {
147 *proc->wstatus = wstatus;
148 }
149
150 _php_free_envp(proc->env, proc->is_persistent);
151 efree(proc->pipes);
152 efree(proc->command);
153 efree(proc);
154 }
155
swoole_proc_open_init(int module_number)156 void swoole_proc_open_init(int module_number) {
157 le_proc_open = zend_register_list_destructors_ex(proc_co_rsrc_dtor, NULL, le_proc_name, module_number);
158 }
159
160 /* {{{ proto bool proc_terminate(resource process [, int signal])
161 kill a process opened by proc_open */
PHP_FUNCTION(swoole_proc_terminate)162 PHP_FUNCTION(swoole_proc_terminate) {
163 zval *zproc;
164 proc_co_t *proc;
165 zend_long sig_no = SIGTERM;
166
167 ZEND_PARSE_PARAMETERS_START(1, 2)
168 Z_PARAM_RESOURCE(zproc)
169 Z_PARAM_OPTIONAL
170 Z_PARAM_LONG(sig_no)
171 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
172
173 if ((proc = (proc_co_t *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) {
174 RETURN_FALSE;
175 }
176
177 RETURN_BOOL(kill(proc->child, sig_no) == 0);
178 }
179 /* }}} */
180
PHP_FUNCTION(swoole_proc_close)181 PHP_FUNCTION(swoole_proc_close) {
182 zval *zproc;
183 proc_co_t *proc;
184 int wstatus;
185
186 ZEND_PARSE_PARAMETERS_START(1, 1)
187 Z_PARAM_RESOURCE(zproc)
188 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
189
190 if ((proc = (proc_co_t *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) {
191 RETURN_FALSE;
192 }
193
194 proc->wstatus = &wstatus;
195 zend_list_close(Z_RES_P(zproc));
196 RETURN_LONG(wstatus);
197 }
198
PHP_FUNCTION(swoole_proc_get_status)199 PHP_FUNCTION(swoole_proc_get_status) {
200 zval *zproc;
201 proc_co_t *proc;
202 int wstatus;
203 pid_t wait_pid;
204 int running = 1, signaled = 0, stopped = 0;
205 int exitcode = -1, termsig = 0, stopsig = 0;
206
207 ZEND_PARSE_PARAMETERS_START(1, 1)
208 Z_PARAM_RESOURCE(zproc)
209 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
210
211 if ((proc = (proc_co_t *) zend_fetch_resource(Z_RES_P(zproc), le_proc_name, le_proc_open)) == NULL) {
212 RETURN_FALSE;
213 }
214
215 array_init(return_value);
216
217 add_assoc_string(return_value, "command", proc->command);
218 add_assoc_long(return_value, "pid", (zend_long) proc->child);
219
220 errno = 0;
221 wait_pid = swoole_coroutine_waitpid(proc->child, &wstatus, WNOHANG | WUNTRACED);
222
223 if (wait_pid == proc->child) {
224 if (WIFEXITED(wstatus)) {
225 running = 0;
226 exitcode = WEXITSTATUS(wstatus);
227 }
228 if (WIFSIGNALED(wstatus)) {
229 running = 0;
230 signaled = 1;
231
232 termsig = WTERMSIG(wstatus);
233 }
234 if (WIFSTOPPED(wstatus)) {
235 stopped = 1;
236 stopsig = WSTOPSIG(wstatus);
237 }
238 } else if (wait_pid == -1) {
239 running = 0;
240 }
241
242 proc->running = running;
243
244 add_assoc_bool(return_value, "running", running);
245 add_assoc_bool(return_value, "signaled", signaled);
246 add_assoc_bool(return_value, "stopped", stopped);
247 add_assoc_long(return_value, "exitcode", exitcode);
248 add_assoc_long(return_value, "termsig", termsig);
249 add_assoc_long(return_value, "stopsig", stopsig);
250 }
251 /* }}} */
252
253 #define DESC_PIPE 1
254 #define DESC_FILE 2
255 #define DESC_REDIRECT 3
256 #define DESC_PARENT_MODE_WRITE 8
257
258 struct php_proc_open_descriptor_item {
259 int index; /* desired fd number in child process */
260 int parentend, childend; /* fds for pipes in parent/child */
261 int mode; /* mode for proc_open code */
262 int mode_flags; /* mode flags for opening fds */
263 };
264 /* }}} */
265
get_valid_arg_string(zval * zv,int elem_num)266 static zend_string *get_valid_arg_string(zval *zv, int elem_num) {
267 zend_string *str = zval_get_string(zv);
268 if (!str) {
269 return NULL;
270 }
271
272 if (strlen(ZSTR_VAL(str)) != ZSTR_LEN(str)) {
273 php_error_docref(NULL, E_WARNING, "Command array element %d contains a null byte", elem_num);
274 zend_string_release(str);
275 return NULL;
276 }
277
278 return str;
279 }
280
281 /* {{{ proto resource proc_open(string|array command, array descriptorspec, array &pipes [, string cwd [, array env [,
282 array other_options]]]) Run a process with more control over it's file descriptors */
PHP_FUNCTION(swoole_proc_open)283 PHP_FUNCTION(swoole_proc_open) {
284 zval *command_zv;
285 char *command = NULL, *cwd = NULL;
286 size_t cwd_len = 0;
287 zval *descriptorspec;
288 zval *pipes;
289 zval *environment = NULL;
290 zval *other_options = NULL;
291 proc_co_env_t env;
292 int ndesc = 0;
293 int i;
294 zval *descitem = NULL;
295 zend_string *str_index;
296 zend_ulong nindex;
297 struct php_proc_open_descriptor_item *descriptors = NULL;
298 int ndescriptors_array;
299
300 char **argv = NULL;
301
302 pid_t child;
303 proc_co_t *proc;
304 int is_persistent = 0; /* TODO: ensure that persistent procs will work */
305
306 ZEND_PARSE_PARAMETERS_START(3, 6)
307 Z_PARAM_ZVAL(command_zv)
308 Z_PARAM_ARRAY(descriptorspec)
309 #if PHP_VERSION_ID >= 70400
310 Z_PARAM_ZVAL(pipes)
311 #else
312 Z_PARAM_ZVAL_DEREF(pipes)
313 #endif
314 Z_PARAM_OPTIONAL
315 Z_PARAM_STRING_EX(cwd, cwd_len, 1, 0)
316 Z_PARAM_ARRAY_EX(environment, 1, 0)
317 Z_PARAM_ARRAY_EX(other_options, 1, 0)
318 ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
319
320 memset(&env, 0, sizeof(env));
321
322 if (Z_TYPE_P(command_zv) == IS_ARRAY) {
323 zval *arg_zv;
324 uint32_t num_elems = zend_hash_num_elements(Z_ARRVAL_P(command_zv));
325 if (num_elems == 0) {
326 php_error_docref(NULL, E_WARNING, "Command array must have at least one element");
327 RETURN_FALSE;
328 }
329
330 argv = (char **) safe_emalloc(sizeof(char *), num_elems + 1, 0);
331 i = 0;
332 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(command_zv), arg_zv) {
333 zend_string *arg_str = get_valid_arg_string(arg_zv, i + 1);
334 if (!arg_str) {
335 argv[i] = NULL;
336 goto exit_fail;
337 }
338
339 if (i == 0) {
340 command = pestrdup(ZSTR_VAL(arg_str), is_persistent);
341 }
342
343 argv[i++] = estrdup(ZSTR_VAL(arg_str));
344 zend_string_release(arg_str);
345 }
346 ZEND_HASH_FOREACH_END();
347 argv[i] = NULL;
348
349 /* As the array is non-empty, we should have found a command. */
350 ZEND_ASSERT(command);
351 } else {
352 convert_to_string(command_zv);
353 command = pestrdup(Z_STRVAL_P(command_zv), is_persistent);
354 }
355
356 php_swoole_check_reactor();
357 if (php_swoole_signal_isset_handler(SIGCHLD)) {
358 php_swoole_error(E_WARNING, "The signal [SIGCHLD] is registered, cannot execute swoole_proc_open");
359 RETURN_FALSE;
360 }
361
362 Coroutine::get_current_safe();
363
364 if (environment) {
365 env = _php_array_to_envp(environment);
366 } else {
367 memset(&env, 0, sizeof(env));
368 }
369
370 ndescriptors_array = zend_hash_num_elements(Z_ARRVAL_P(descriptorspec));
371
372 descriptors = (struct php_proc_open_descriptor_item *) safe_emalloc(
373 sizeof(struct php_proc_open_descriptor_item), ndescriptors_array, 0);
374
375 memset(descriptors, 0, sizeof(struct php_proc_open_descriptor_item) * ndescriptors_array);
376
377 /* walk the descriptor spec and set up files/pipes */
378 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(descriptorspec), nindex, str_index, descitem) {
379 zval *ztype;
380
381 if (str_index) {
382 php_swoole_fatal_error(E_WARNING, "descriptor spec must be an integer indexed array");
383 goto exit_fail;
384 }
385
386 descriptors[ndesc].index = (int) nindex;
387
388 if (Z_TYPE_P(descitem) == IS_RESOURCE) {
389 /* should be a stream - try and dup the descriptor */
390 php_stream *stream;
391 php_socket_t fd;
392
393 php_stream_from_zval(stream, descitem);
394
395 if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS)) {
396 goto exit_fail;
397 }
398
399 descriptors[ndesc].childend = dup(fd);
400 if (descriptors[ndesc].childend < 0) {
401 php_swoole_fatal_error(E_WARNING,
402 "unable to dup File-Handle for descriptor " ZEND_ULONG_FMT " - %s",
403 nindex,
404 strerror(errno));
405 goto exit_fail;
406 }
407
408 descriptors[ndesc].mode = DESC_FILE;
409
410 } else if (Z_TYPE_P(descitem) != IS_ARRAY) {
411 php_swoole_fatal_error(E_WARNING, "Descriptor item must be either an array or a File-Handle");
412 goto exit_fail;
413 } else {
414 if ((ztype = zend_hash_index_find(Z_ARRVAL_P(descitem), 0)) != NULL) {
415 convert_to_string_ex(ztype);
416 } else {
417 php_swoole_fatal_error(E_WARNING, "Missing handle qualifier in array");
418 goto exit_fail;
419 }
420
421 if (strcmp(Z_STRVAL_P(ztype), "pipe") == 0) {
422 php_file_descriptor_t newpipe[2];
423 zval *zmode;
424
425 if ((zmode = zend_hash_index_find(Z_ARRVAL_P(descitem), 1)) != NULL) {
426 convert_to_string_ex(zmode);
427 } else {
428 php_swoole_fatal_error(E_WARNING, "Missing mode parameter for 'pipe'");
429 goto exit_fail;
430 }
431
432 descriptors[ndesc].mode = DESC_PIPE;
433
434 if (0 != socketpair(AF_UNIX, SOCK_STREAM, 0, newpipe)) {
435 php_swoole_fatal_error(E_WARNING, "unable to create pipe %s", strerror(errno));
436 goto exit_fail;
437 }
438
439 if (strncmp(Z_STRVAL_P(zmode), "w", 1) != 0) {
440 descriptors[ndesc].parentend = newpipe[1];
441 descriptors[ndesc].childend = newpipe[0];
442 descriptors[ndesc].mode |= DESC_PARENT_MODE_WRITE;
443 } else {
444 descriptors[ndesc].parentend = newpipe[0];
445 descriptors[ndesc].childend = newpipe[1];
446 }
447 descriptors[ndesc].mode_flags = descriptors[ndesc].mode & DESC_PARENT_MODE_WRITE ? O_WRONLY : O_RDONLY;
448
449 } else if (strcmp(Z_STRVAL_P(ztype), "file") == 0) {
450 zval *zfile, *zmode;
451 php_socket_t fd;
452 php_stream *stream;
453
454 descriptors[ndesc].mode = DESC_FILE;
455
456 if ((zfile = zend_hash_index_find(Z_ARRVAL_P(descitem), 1)) != NULL) {
457 #if PHP_VERSION_ID >= 70400
458 if (!try_convert_to_string(zfile)) {
459 goto exit_fail;
460 }
461 #else
462 convert_to_string_ex(zfile);
463 #endif
464 } else {
465 php_swoole_fatal_error(E_WARNING, "Missing file name parameter for 'file'");
466 goto exit_fail;
467 }
468
469 if ((zmode = zend_hash_index_find(Z_ARRVAL_P(descitem), 2)) != NULL) {
470 #if PHP_VERSION_ID >= 70400
471 if (!try_convert_to_string(zmode)) {
472 goto exit_fail;
473 }
474 #else
475 convert_to_string_ex(zmode);
476 #endif
477 } else {
478 php_swoole_fatal_error(E_WARNING, "Missing mode parameter for 'file'");
479 goto exit_fail;
480 }
481
482 /* try a wrapper */
483 stream = php_stream_open_wrapper(
484 Z_STRVAL_P(zfile), Z_STRVAL_P(zmode), REPORT_ERRORS | STREAM_WILL_CAST, NULL);
485
486 /* force into an fd */
487 if (stream == NULL ||
488 FAILURE == php_stream_cast(
489 stream, PHP_STREAM_CAST_RELEASE | PHP_STREAM_AS_FD, (void **) &fd, REPORT_ERRORS)) {
490 goto exit_fail;
491 }
492
493 descriptors[ndesc].childend = fd;
494 } else if (strcmp(Z_STRVAL_P(ztype), "redirect") == 0) {
495 zval *ztarget = zend_hash_index_find_deref(Z_ARRVAL_P(descitem), 1);
496 struct php_proc_open_descriptor_item *target = NULL;
497 php_file_descriptor_t childend;
498
499 if (!ztarget) {
500 php_error_docref(NULL, E_WARNING, "Missing redirection target");
501 goto exit_fail;
502 }
503 if (Z_TYPE_P(ztarget) != IS_LONG) {
504 php_error_docref(NULL, E_WARNING, "Redirection target must be an integer");
505 goto exit_fail;
506 }
507
508 for (i = 0; i < ndesc; i++) {
509 if (descriptors[i].index == Z_LVAL_P(ztarget)) {
510 target = &descriptors[i];
511 break;
512 }
513 }
514 if (target) {
515 childend = target->childend;
516 } else {
517 if (Z_LVAL_P(ztarget) < 0 || Z_LVAL_P(ztarget) > 2) {
518 php_error_docref(
519 NULL, E_WARNING, "Redirection target " ZEND_LONG_FMT " not found", Z_LVAL_P(ztarget));
520 goto exit_fail;
521 }
522
523 /* Support referring to a stdin/stdout/stderr pipe adopted from the parent,
524 * which happens whenever an explicit override is not provided. */
525 #ifndef PHP_WIN32
526 childend = Z_LVAL_P(ztarget);
527 #else
528 switch (Z_LVAL_P(ztarget)) {
529 case 0:
530 childend = GetStdHandle(STD_INPUT_HANDLE);
531 break;
532 case 1:
533 childend = GetStdHandle(STD_OUTPUT_HANDLE);
534 break;
535 case 2:
536 childend = GetStdHandle(STD_ERROR_HANDLE);
537 break;
538 EMPTY_SWITCH_DEFAULT_CASE()
539 }
540 #endif
541 }
542
543 #ifdef PHP_WIN32
544 descriptors[ndesc].childend = dup_handle(childend, TRUE, FALSE);
545 if (descriptors[ndesc].childend == NULL) {
546 php_error_docref(NULL, E_WARNING, "Failed to dup() for descriptor " ZEND_LONG_FMT, nindex);
547 goto exit_fail;
548 }
549 #else
550 descriptors[ndesc].childend = dup(childend);
551 if (descriptors[ndesc].childend < 0) {
552 php_error_docref(NULL,
553 E_WARNING,
554 "Failed to dup() for descriptor " ZEND_LONG_FMT " - %s",
555 nindex,
556 strerror(errno));
557 goto exit_fail;
558 }
559 #endif
560 descriptors[ndesc].mode = DESC_REDIRECT;
561 } else if (strcmp(Z_STRVAL_P(ztype), "null") == 0) {
562 #ifndef PHP_WIN32
563 descriptors[ndesc].childend = open("/dev/null", O_RDWR);
564 if (descriptors[ndesc].childend < 0) {
565 php_error_docref(NULL, E_WARNING, "Failed to open /dev/null - %s", strerror(errno));
566 goto exit_fail;
567 }
568 #else
569 descriptors[ndesc].childend = CreateFileA("nul",
570 GENERIC_READ | GENERIC_WRITE,
571 FILE_SHARE_READ | FILE_SHARE_WRITE,
572 NULL,
573 OPEN_EXISTING,
574 0,
575 NULL);
576 if (descriptors[ndesc].childend == NULL) {
577 php_error_docref(NULL, E_WARNING, "Failed to open nul");
578 goto exit_fail;
579 }
580 #endif
581 descriptors[ndesc].mode = DESC_FILE;
582 } else if (strcmp(Z_STRVAL_P(ztype), "pty") == 0) {
583 php_swoole_fatal_error(E_WARNING, "pty pseudo terminal not supported on this system");
584 goto exit_fail;
585 } else {
586 php_swoole_fatal_error(E_WARNING, "%s is not a valid descriptor spec/mode", Z_STRVAL_P(ztype));
587 goto exit_fail;
588 }
589 }
590 ndesc++;
591 }
592 ZEND_HASH_FOREACH_END();
593
594 /* the unix way */
595 child = swoole_fork(SW_FORK_EXEC);
596
597 if (child == 0) {
598 /* this is the child process */
599
600 /* close those descriptors that we just opened for the parent stuff,
601 * dup new descriptors into required descriptors and close the original
602 * cruft */
603 for (i = 0; i < ndesc; i++) {
604 switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) {
605 case DESC_PIPE:
606 close(descriptors[i].parentend);
607 break;
608 }
609 if (dup2(descriptors[i].childend, descriptors[i].index) < 0) {
610 perror("dup2");
611 }
612 if (descriptors[i].childend != descriptors[i].index) {
613 close(descriptors[i].childend);
614 }
615 }
616
617 if (cwd) {
618 php_ignore_value(chdir(cwd));
619 }
620
621 if (argv) {
622 /* execvpe() is non-portable, use environ instead. */
623 if (env.envarray) {
624 environ = env.envarray;
625 }
626 execvp(command, argv);
627 } else {
628 if (env.envarray) {
629 execle("/bin/sh", "sh", "-c", command, NULL, env.envarray);
630 } else {
631 execl("/bin/sh", "sh", "-c", command, NULL);
632 }
633 }
634 _exit(127);
635
636 } else if (child < 0) {
637 /* failed to fork() */
638
639 /* clean up all the descriptors */
640 for (i = 0; i < ndesc; i++) {
641 close(descriptors[i].childend);
642 if (descriptors[i].parentend) {
643 close(descriptors[i].parentend);
644 }
645 }
646
647 php_swoole_fatal_error(E_WARNING, "fork failed - %s", strerror(errno));
648
649 goto exit_fail;
650 }
651
652 /* we forked/spawned and this is the parent */
653
654 #if PHP_VERSION_ID >= 70400
655 pipes = zend_try_array_init(pipes);
656 if (!pipes) {
657 goto exit_fail;
658 }
659 #else
660 zval_ptr_dtor(pipes);
661 array_init(pipes);
662 #endif
663
664 proc = (proc_co_t *) pemalloc(sizeof(proc_co_t), is_persistent);
665 proc->is_persistent = is_persistent;
666 proc->wstatus = nullptr;
667 proc->running = true;
668 proc->command = command;
669 proc->pipes = (zend_resource **) emalloc(sizeof(zend_resource *) * ndesc);
670 proc->npipes = ndesc;
671 proc->child = child;
672 proc->env = env;
673
674 /* clean up all the child ends and then open streams on the parent
675 * ends, where appropriate */
676 for (i = 0; i < ndesc; i++) {
677 php_stream *stream = NULL;
678
679 close(descriptors[i].childend);
680
681 switch (descriptors[i].mode & ~DESC_PARENT_MODE_WRITE) {
682 case DESC_PIPE:
683 stream = php_swoole_create_stream_from_socket(descriptors[i].parentend, AF_UNIX, SOCK_STREAM, 0 STREAMS_CC);
684 /* mark the descriptor close-on-exec, so that it won't be inherited by potential other children */
685 fcntl(descriptors[i].parentend, F_SETFD, FD_CLOEXEC);
686 if (stream) {
687 zval retfp;
688
689 /* nasty hack; don't copy it */
690 stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
691
692 php_stream_to_zval(stream, &retfp);
693 (void) add_index_zval(pipes, descriptors[i].index, &retfp);
694
695 proc->pipes[i] = Z_RES(retfp);
696 Z_ADDREF(retfp);
697 }
698 break;
699 default:
700 proc->pipes[i] = NULL;
701 }
702 }
703
704 if (argv) {
705 char **arg = argv;
706 while (*arg != NULL) {
707 efree(*arg);
708 arg++;
709 }
710 efree(argv);
711 }
712
713 efree(descriptors);
714 ZVAL_RES(return_value, zend_register_resource(proc, le_proc_open));
715 return;
716
717 exit_fail:
718 if (descriptors) {
719 efree(descriptors);
720 }
721 _php_free_envp(env, is_persistent);
722 if (command) {
723 pefree(command, is_persistent);
724 }
725 if (argv) {
726 char **arg = argv;
727 while (*arg != NULL) {
728 efree(*arg);
729 arg++;
730 }
731 efree(argv);
732 }
733 RETURN_FALSE;
734 }
735 /* }}} */
736