proc_open.c 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Author: Wez Furlong <wez@thebrainroom.com> |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include "php.h"
  17. #include <stdio.h>
  18. #include <ctype.h>
  19. #include <signal.h>
  20. #include "php_string.h"
  21. #include "ext/standard/head.h"
  22. #include "ext/standard/basic_functions.h"
  23. #include "ext/standard/file.h"
  24. #include "exec.h"
  25. #include "php_globals.h"
  26. #include "SAPI.h"
  27. #include "main/php_network.h"
  28. #include "zend_smart_str.h"
  29. #if HAVE_SYS_WAIT_H
  30. #include <sys/wait.h>
  31. #endif
  32. #if HAVE_FCNTL_H
  33. #include <fcntl.h>
  34. #endif
  35. /* This symbol is defined in ext/standard/config.m4.
  36. * Essentially, it is set if you HAVE_FORK || PHP_WIN32
  37. * Other platforms may modify that configure check and add suitable #ifdefs
  38. * around the alternate code. */
  39. #ifdef PHP_CAN_SUPPORT_PROC_OPEN
  40. #if HAVE_OPENPTY
  41. # if HAVE_PTY_H
  42. # include <pty.h>
  43. # elif defined(__FreeBSD__)
  44. /* FreeBSD defines `openpty` in <libutil.h> */
  45. # include <libutil.h>
  46. # elif defined(__NetBSD__) || defined(__DragonFly__)
  47. /* On recent NetBSD/DragonFlyBSD releases the emalloc, estrdup ... calls had been introduced in libutil */
  48. # if defined(__NetBSD__)
  49. # include <sys/termios.h>
  50. # else
  51. # include <termios.h>
  52. # endif
  53. extern int openpty(int *, int *, char *, struct termios *, struct winsize *);
  54. # elif defined(__sun)
  55. # include <termios.h>
  56. # else
  57. /* Mac OS X (and some BSDs) define `openpty` in <util.h> */
  58. # include <util.h>
  59. # endif
  60. #elif defined(__sun)
  61. # include <fcntl.h>
  62. # include <stropts.h>
  63. # include <termios.h>
  64. # define HAVE_OPENPTY 1
  65. /* Solaris before version 11.4 and Illumos do not have any openpty implementation */
  66. int openpty(int *master, int *slave, char *name, struct termios *termp, struct winsize *winp)
  67. {
  68. int fd, sd;
  69. const char *slaveid;
  70. assert(master);
  71. assert(slave);
  72. sd = *master = *slave = -1;
  73. fd = open("/dev/ptmx", O_NOCTTY|O_RDWR);
  74. if (fd == -1) {
  75. return -1;
  76. }
  77. /* Checking if we can have to the pseudo terminal */
  78. if (grantpt(fd) != 0 || unlockpt(fd) != 0) {
  79. goto fail;
  80. }
  81. slaveid = ptsname(fd);
  82. if (!slaveid) {
  83. goto fail;
  84. }
  85. /* Getting the slave path and pushing pseudo terminal */
  86. sd = open(slaveid, O_NOCTTY|O_RDONLY);
  87. if (sd == -1 || ioctl(sd, I_PUSH, "ptem") == -1) {
  88. goto fail;
  89. }
  90. if (termp) {
  91. if (tcgetattr(sd, termp) < 0) {
  92. goto fail;
  93. }
  94. }
  95. if (winp) {
  96. if (ioctl(sd, TIOCSWINSZ, winp) == -1) {
  97. goto fail;
  98. }
  99. }
  100. *slave = sd;
  101. *master = fd;
  102. return 0;
  103. fail:
  104. if (sd != -1) {
  105. close(sd);
  106. }
  107. if (fd != -1) {
  108. close(fd);
  109. }
  110. return -1;
  111. }
  112. #endif
  113. #include "proc_open.h"
  114. static int le_proc_open; /* Resource number for `proc` resources */
  115. /* {{{ _php_array_to_envp
  116. * Process the `environment` argument to `proc_open`
  117. * Convert into data structures which can be passed to underlying OS APIs like `exec` on POSIX or
  118. * `CreateProcessW` on Win32 */
  119. static php_process_env _php_array_to_envp(zval *environment)
  120. {
  121. zval *element;
  122. php_process_env env;
  123. zend_string *key, *str;
  124. #ifndef PHP_WIN32
  125. char **ep;
  126. #endif
  127. char *p;
  128. size_t sizeenv = 0;
  129. HashTable *env_hash; /* temporary PHP array used as helper */
  130. memset(&env, 0, sizeof(env));
  131. if (!environment) {
  132. return env;
  133. }
  134. uint32_t cnt = zend_hash_num_elements(Z_ARRVAL_P(environment));
  135. if (cnt < 1) {
  136. #ifndef PHP_WIN32
  137. env.envarray = (char **) ecalloc(1, sizeof(char *));
  138. #endif
  139. env.envp = (char *) ecalloc(4, 1);
  140. return env;
  141. }
  142. ALLOC_HASHTABLE(env_hash);
  143. zend_hash_init(env_hash, cnt, NULL, NULL, 0);
  144. /* first, we have to get the size of all the elements in the hash */
  145. ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(environment), key, element) {
  146. str = zval_get_string(element);
  147. if (ZSTR_LEN(str) == 0) {
  148. zend_string_release_ex(str, 0);
  149. continue;
  150. }
  151. sizeenv += ZSTR_LEN(str) + 1;
  152. if (key && ZSTR_LEN(key)) {
  153. sizeenv += ZSTR_LEN(key) + 1;
  154. zend_hash_add_ptr(env_hash, key, str);
  155. } else {
  156. zend_hash_next_index_insert_ptr(env_hash, str);
  157. }
  158. } ZEND_HASH_FOREACH_END();
  159. #ifndef PHP_WIN32
  160. ep = env.envarray = (char **) ecalloc(cnt + 1, sizeof(char *));
  161. #endif
  162. p = env.envp = (char *) ecalloc(sizeenv + 4, 1);
  163. ZEND_HASH_FOREACH_STR_KEY_PTR(env_hash, key, str) {
  164. #ifndef PHP_WIN32
  165. *ep = p;
  166. ++ep;
  167. #endif
  168. if (key) {
  169. memcpy(p, ZSTR_VAL(key), ZSTR_LEN(key));
  170. p += ZSTR_LEN(key);
  171. *p++ = '=';
  172. }
  173. memcpy(p, ZSTR_VAL(str), ZSTR_LEN(str));
  174. p += ZSTR_LEN(str);
  175. *p++ = '\0';
  176. zend_string_release_ex(str, 0);
  177. } ZEND_HASH_FOREACH_END();
  178. assert((uint32_t)(p - env.envp) <= sizeenv);
  179. zend_hash_destroy(env_hash);
  180. FREE_HASHTABLE(env_hash);
  181. return env;
  182. }
  183. /* }}} */
  184. /* {{{ _php_free_envp
  185. * Free the structures allocated by `_php_array_to_envp` */
  186. static void _php_free_envp(php_process_env env)
  187. {
  188. #ifndef PHP_WIN32
  189. if (env.envarray) {
  190. efree(env.envarray);
  191. }
  192. #endif
  193. if (env.envp) {
  194. efree(env.envp);
  195. }
  196. }
  197. /* }}} */
  198. /* {{{ proc_open_rsrc_dtor
  199. * Free `proc` resource, either because all references to it were dropped or because `pclose` or
  200. * `proc_close` were called */
  201. static void proc_open_rsrc_dtor(zend_resource *rsrc)
  202. {
  203. php_process_handle *proc = (php_process_handle*)rsrc->ptr;
  204. #ifdef PHP_WIN32
  205. DWORD wstatus;
  206. #elif HAVE_SYS_WAIT_H
  207. int wstatus;
  208. int waitpid_options = 0;
  209. pid_t wait_pid;
  210. #endif
  211. /* Close all handles to avoid a deadlock */
  212. for (int i = 0; i < proc->npipes; i++) {
  213. if (proc->pipes[i] != NULL) {
  214. GC_DELREF(proc->pipes[i]);
  215. zend_list_close(proc->pipes[i]);
  216. proc->pipes[i] = NULL;
  217. }
  218. }
  219. /* `pclose_wait` tells us: Are we freeing this resource because `pclose` or `proc_close` were
  220. * called? If so, we need to wait until the child process exits, because its exit code is
  221. * needed as the return value of those functions.
  222. * But if we're freeing the resource because of GC, don't wait. */
  223. #ifdef PHP_WIN32
  224. if (FG(pclose_wait)) {
  225. WaitForSingleObject(proc->childHandle, INFINITE);
  226. }
  227. GetExitCodeProcess(proc->childHandle, &wstatus);
  228. if (wstatus == STILL_ACTIVE) {
  229. FG(pclose_ret) = -1;
  230. } else {
  231. FG(pclose_ret) = wstatus;
  232. }
  233. CloseHandle(proc->childHandle);
  234. #elif HAVE_SYS_WAIT_H
  235. if (!FG(pclose_wait)) {
  236. waitpid_options = WNOHANG;
  237. }
  238. do {
  239. wait_pid = waitpid(proc->child, &wstatus, waitpid_options);
  240. } while (wait_pid == -1 && errno == EINTR);
  241. if (wait_pid <= 0) {
  242. FG(pclose_ret) = -1;
  243. } else {
  244. if (WIFEXITED(wstatus)) {
  245. wstatus = WEXITSTATUS(wstatus);
  246. }
  247. FG(pclose_ret) = wstatus;
  248. }
  249. #else
  250. FG(pclose_ret) = -1;
  251. #endif
  252. _php_free_envp(proc->env);
  253. efree(proc->pipes);
  254. zend_string_release_ex(proc->command, false);
  255. efree(proc);
  256. }
  257. /* }}} */
  258. /* {{{ PHP_MINIT_FUNCTION(proc_open) */
  259. PHP_MINIT_FUNCTION(proc_open)
  260. {
  261. le_proc_open = zend_register_list_destructors_ex(proc_open_rsrc_dtor, NULL, "process",
  262. module_number);
  263. return SUCCESS;
  264. }
  265. /* }}} */
  266. /* {{{ Kill a process opened by `proc_open` */
  267. PHP_FUNCTION(proc_terminate)
  268. {
  269. zval *zproc;
  270. php_process_handle *proc;
  271. zend_long sig_no = SIGTERM;
  272. ZEND_PARSE_PARAMETERS_START(1, 2)
  273. Z_PARAM_RESOURCE(zproc)
  274. Z_PARAM_OPTIONAL
  275. Z_PARAM_LONG(sig_no)
  276. ZEND_PARSE_PARAMETERS_END();
  277. proc = (php_process_handle*)zend_fetch_resource(Z_RES_P(zproc), "process", le_proc_open);
  278. if (proc == NULL) {
  279. RETURN_THROWS();
  280. }
  281. #ifdef PHP_WIN32
  282. RETURN_BOOL(TerminateProcess(proc->childHandle, 255));
  283. #else
  284. RETURN_BOOL(kill(proc->child, sig_no) == 0);
  285. #endif
  286. }
  287. /* }}} */
  288. /* {{{ Close a process opened by `proc_open` */
  289. PHP_FUNCTION(proc_close)
  290. {
  291. zval *zproc;
  292. php_process_handle *proc;
  293. ZEND_PARSE_PARAMETERS_START(1, 1)
  294. Z_PARAM_RESOURCE(zproc)
  295. ZEND_PARSE_PARAMETERS_END();
  296. proc = (php_process_handle*)zend_fetch_resource(Z_RES_P(zproc), "process", le_proc_open);
  297. if (proc == NULL) {
  298. RETURN_THROWS();
  299. }
  300. FG(pclose_wait) = 1; /* See comment in `proc_open_rsrc_dtor` */
  301. zend_list_close(Z_RES_P(zproc));
  302. FG(pclose_wait) = 0;
  303. RETURN_LONG(FG(pclose_ret));
  304. }
  305. /* }}} */
  306. /* {{{ Get information about a process opened by `proc_open` */
  307. PHP_FUNCTION(proc_get_status)
  308. {
  309. zval *zproc;
  310. php_process_handle *proc;
  311. #ifdef PHP_WIN32
  312. DWORD wstatus;
  313. #elif HAVE_SYS_WAIT_H
  314. int wstatus;
  315. pid_t wait_pid;
  316. #endif
  317. bool running = 1, signaled = 0, stopped = 0;
  318. int exitcode = -1, termsig = 0, stopsig = 0;
  319. ZEND_PARSE_PARAMETERS_START(1, 1)
  320. Z_PARAM_RESOURCE(zproc)
  321. ZEND_PARSE_PARAMETERS_END();
  322. proc = (php_process_handle*)zend_fetch_resource(Z_RES_P(zproc), "process", le_proc_open);
  323. if (proc == NULL) {
  324. RETURN_THROWS();
  325. }
  326. array_init(return_value);
  327. add_assoc_str(return_value, "command", zend_string_copy(proc->command));
  328. add_assoc_long(return_value, "pid", (zend_long)proc->child);
  329. #ifdef PHP_WIN32
  330. GetExitCodeProcess(proc->childHandle, &wstatus);
  331. running = wstatus == STILL_ACTIVE;
  332. exitcode = running ? -1 : wstatus;
  333. #elif HAVE_SYS_WAIT_H
  334. wait_pid = waitpid(proc->child, &wstatus, WNOHANG|WUNTRACED);
  335. if (wait_pid == proc->child) {
  336. if (WIFEXITED(wstatus)) {
  337. running = 0;
  338. exitcode = WEXITSTATUS(wstatus);
  339. }
  340. if (WIFSIGNALED(wstatus)) {
  341. running = 0;
  342. signaled = 1;
  343. termsig = WTERMSIG(wstatus);
  344. }
  345. if (WIFSTOPPED(wstatus)) {
  346. stopped = 1;
  347. stopsig = WSTOPSIG(wstatus);
  348. }
  349. } else if (wait_pid == -1) {
  350. /* The only error which could occur here is ECHILD, which means that the PID we were
  351. * looking for either does not exist or is not a child of this process */
  352. running = 0;
  353. }
  354. #endif
  355. add_assoc_bool(return_value, "running", running);
  356. add_assoc_bool(return_value, "signaled", signaled);
  357. add_assoc_bool(return_value, "stopped", stopped);
  358. add_assoc_long(return_value, "exitcode", exitcode);
  359. add_assoc_long(return_value, "termsig", termsig);
  360. add_assoc_long(return_value, "stopsig", stopsig);
  361. }
  362. /* }}} */
  363. #ifdef PHP_WIN32
  364. /* We use this to allow child processes to inherit handles
  365. * One static instance can be shared and used for all calls to `proc_open`, since the values are
  366. * never changed */
  367. SECURITY_ATTRIBUTES php_proc_open_security = {
  368. .nLength = sizeof(SECURITY_ATTRIBUTES),
  369. .lpSecurityDescriptor = NULL,
  370. .bInheritHandle = TRUE
  371. };
  372. # define pipe(pair) (CreatePipe(&pair[0], &pair[1], &php_proc_open_security, 0) ? 0 : -1)
  373. # define COMSPEC_NT "cmd.exe"
  374. static inline HANDLE dup_handle(HANDLE src, BOOL inherit, BOOL closeorig)
  375. {
  376. HANDLE copy, self = GetCurrentProcess();
  377. if (!DuplicateHandle(self, src, self, &copy, 0, inherit, DUPLICATE_SAME_ACCESS |
  378. (closeorig ? DUPLICATE_CLOSE_SOURCE : 0)))
  379. return NULL;
  380. return copy;
  381. }
  382. static inline HANDLE dup_fd_as_handle(int fd)
  383. {
  384. return dup_handle((HANDLE)_get_osfhandle(fd), TRUE, FALSE);
  385. }
  386. # define close_descriptor(fd) CloseHandle(fd)
  387. #else /* !PHP_WIN32 */
  388. # define close_descriptor(fd) close(fd)
  389. #endif
  390. /* Determines the type of a descriptor item. */
  391. typedef enum _descriptor_type {
  392. DESCRIPTOR_TYPE_STD,
  393. DESCRIPTOR_TYPE_PIPE,
  394. DESCRIPTOR_TYPE_SOCKET
  395. } descriptor_type;
  396. /* One instance of this struct is created for each item in `$descriptorspec` argument to `proc_open`
  397. * They are used within `proc_open` and freed before it returns */
  398. typedef struct _descriptorspec_item {
  399. int index; /* desired FD # in child process */
  400. descriptor_type type;
  401. php_file_descriptor_t childend; /* FD # opened for use in child
  402. * (will be copied to `index` in child) */
  403. php_file_descriptor_t parentend; /* FD # opened for use in parent
  404. * (for pipes only; will be 0 otherwise) */
  405. int mode_flags; /* mode for opening FDs: r/o, r/w, binary (on Win32), etc */
  406. } descriptorspec_item;
  407. static zend_string *get_valid_arg_string(zval *zv, int elem_num) {
  408. zend_string *str = zval_get_string(zv);
  409. if (!str) {
  410. return NULL;
  411. }
  412. if (strlen(ZSTR_VAL(str)) != ZSTR_LEN(str)) {
  413. zend_value_error("Command array element %d contains a null byte", elem_num);
  414. zend_string_release(str);
  415. return NULL;
  416. }
  417. return str;
  418. }
  419. #ifdef PHP_WIN32
  420. static void append_backslashes(smart_str *str, size_t num_bs)
  421. {
  422. for (size_t i = 0; i < num_bs; i++) {
  423. smart_str_appendc(str, '\\');
  424. }
  425. }
  426. /* See https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments */
  427. static void append_win_escaped_arg(smart_str *str, zend_string *arg)
  428. {
  429. size_t num_bs = 0;
  430. smart_str_appendc(str, '"');
  431. for (size_t i = 0; i < ZSTR_LEN(arg); ++i) {
  432. char c = ZSTR_VAL(arg)[i];
  433. if (c == '\\') {
  434. num_bs++;
  435. continue;
  436. }
  437. if (c == '"') {
  438. /* Backslashes before " need to be doubled. */
  439. num_bs = num_bs * 2 + 1;
  440. }
  441. append_backslashes(str, num_bs);
  442. smart_str_appendc(str, c);
  443. num_bs = 0;
  444. }
  445. append_backslashes(str, num_bs * 2);
  446. smart_str_appendc(str, '"');
  447. }
  448. static zend_string *create_win_command_from_args(HashTable *args)
  449. {
  450. smart_str str = {0};
  451. zval *arg_zv;
  452. bool is_prog_name = 1;
  453. int elem_num = 0;
  454. ZEND_HASH_FOREACH_VAL(args, arg_zv) {
  455. zend_string *arg_str = get_valid_arg_string(arg_zv, ++elem_num);
  456. if (!arg_str) {
  457. smart_str_free(&str);
  458. return NULL;
  459. }
  460. if (!is_prog_name) {
  461. smart_str_appendc(&str, ' ');
  462. }
  463. append_win_escaped_arg(&str, arg_str);
  464. is_prog_name = 0;
  465. zend_string_release(arg_str);
  466. } ZEND_HASH_FOREACH_END();
  467. smart_str_0(&str);
  468. return str.s;
  469. }
  470. /* Get a boolean option from the `other_options` array which can be passed to `proc_open`.
  471. * (Currently, all options apply on Windows only.) */
  472. static bool get_option(zval *other_options, char *opt_name, size_t opt_name_len)
  473. {
  474. HashTable *opt_ary = Z_ARRVAL_P(other_options);
  475. zval *item = zend_hash_str_find_deref(opt_ary, opt_name, opt_name_len);
  476. return item != NULL &&
  477. (Z_TYPE_P(item) == IS_TRUE ||
  478. ((Z_TYPE_P(item) == IS_LONG) && Z_LVAL_P(item)));
  479. }
  480. /* Initialize STARTUPINFOW struct, used on Windows when spawning a process.
  481. * Ref: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow */
  482. static void init_startup_info(STARTUPINFOW *si, descriptorspec_item *descriptors, int ndesc)
  483. {
  484. memset(si, 0, sizeof(STARTUPINFOW));
  485. si->cb = sizeof(STARTUPINFOW);
  486. si->dwFlags = STARTF_USESTDHANDLES;
  487. si->hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  488. si->hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  489. si->hStdError = GetStdHandle(STD_ERROR_HANDLE);
  490. /* redirect stdin/stdout/stderr if requested */
  491. for (int i = 0; i < ndesc; i++) {
  492. switch (descriptors[i].index) {
  493. case 0:
  494. si->hStdInput = descriptors[i].childend;
  495. break;
  496. case 1:
  497. si->hStdOutput = descriptors[i].childend;
  498. break;
  499. case 2:
  500. si->hStdError = descriptors[i].childend;
  501. break;
  502. }
  503. }
  504. }
  505. static void init_process_info(PROCESS_INFORMATION *pi)
  506. {
  507. memset(&pi, 0, sizeof(pi));
  508. }
  509. static zend_result convert_command_to_use_shell(wchar_t **cmdw, size_t cmdw_len)
  510. {
  511. size_t len = sizeof(COMSPEC_NT) + sizeof(" /s /c ") + cmdw_len + 3;
  512. wchar_t *cmdw_shell = (wchar_t *)malloc(len * sizeof(wchar_t));
  513. if (cmdw_shell == NULL) {
  514. php_error_docref(NULL, E_WARNING, "Command conversion failed");
  515. return FAILURE;
  516. }
  517. if (_snwprintf(cmdw_shell, len, L"%hs /s /c \"%s\"", COMSPEC_NT, *cmdw) == -1) {
  518. free(cmdw_shell);
  519. php_error_docref(NULL, E_WARNING, "Command conversion failed");
  520. return FAILURE;
  521. }
  522. free(*cmdw);
  523. *cmdw = cmdw_shell;
  524. return SUCCESS;
  525. }
  526. #endif
  527. /* Convert command parameter array passed as first argument to `proc_open` into command string */
  528. static zend_string* get_command_from_array(HashTable *array, char ***argv, int num_elems)
  529. {
  530. zval *arg_zv;
  531. zend_string *command = NULL;
  532. int i = 0;
  533. *argv = safe_emalloc(sizeof(char *), num_elems + 1, 0);
  534. ZEND_HASH_FOREACH_VAL(array, arg_zv) {
  535. zend_string *arg_str = get_valid_arg_string(arg_zv, i + 1);
  536. if (!arg_str) {
  537. /* Terminate with NULL so exit_fail code knows how many entries to free */
  538. (*argv)[i] = NULL;
  539. if (command != NULL) {
  540. zend_string_release_ex(command, false);
  541. }
  542. return NULL;
  543. }
  544. if (i == 0) {
  545. command = zend_string_copy(arg_str);
  546. }
  547. (*argv)[i++] = estrdup(ZSTR_VAL(arg_str));
  548. zend_string_release(arg_str);
  549. } ZEND_HASH_FOREACH_END();
  550. (*argv)[i] = NULL;
  551. return command;
  552. }
  553. static descriptorspec_item* alloc_descriptor_array(HashTable *descriptorspec)
  554. {
  555. uint32_t ndescriptors = zend_hash_num_elements(descriptorspec);
  556. return ecalloc(sizeof(descriptorspec_item), ndescriptors);
  557. }
  558. static zend_string* get_string_parameter(zval *array, int index, char *param_name)
  559. {
  560. zval *array_item;
  561. if ((array_item = zend_hash_index_find(Z_ARRVAL_P(array), index)) == NULL) {
  562. zend_value_error("Missing %s", param_name);
  563. return NULL;
  564. }
  565. return zval_try_get_string(array_item);
  566. }
  567. static zend_result set_proc_descriptor_to_blackhole(descriptorspec_item *desc)
  568. {
  569. #ifdef PHP_WIN32
  570. desc->childend = CreateFileA("nul", GENERIC_READ | GENERIC_WRITE,
  571. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
  572. if (desc->childend == NULL) {
  573. php_error_docref(NULL, E_WARNING, "Failed to open nul");
  574. return FAILURE;
  575. }
  576. #else
  577. desc->childend = open("/dev/null", O_RDWR);
  578. if (desc->childend < 0) {
  579. php_error_docref(NULL, E_WARNING, "Failed to open /dev/null: %s", strerror(errno));
  580. return FAILURE;
  581. }
  582. #endif
  583. return SUCCESS;
  584. }
  585. static zend_result set_proc_descriptor_to_pty(descriptorspec_item *desc, int *master_fd, int *slave_fd)
  586. {
  587. #if HAVE_OPENPTY
  588. /* All FDs set to PTY in the child process will go to the slave end of the same PTY.
  589. * Likewise, all the corresponding entries in `$pipes` in the parent will all go to the master
  590. * end of the same PTY.
  591. * If this is the first descriptorspec set to 'pty', find an available PTY and get master and
  592. * slave FDs. */
  593. if (*master_fd == -1) {
  594. if (openpty(master_fd, slave_fd, NULL, NULL, NULL)) {
  595. php_error_docref(NULL, E_WARNING, "Could not open PTY (pseudoterminal): %s", strerror(errno));
  596. return FAILURE;
  597. }
  598. }
  599. desc->type = DESCRIPTOR_TYPE_PIPE;
  600. desc->childend = dup(*slave_fd);
  601. desc->parentend = dup(*master_fd);
  602. desc->mode_flags = O_RDWR;
  603. return SUCCESS;
  604. #else
  605. php_error_docref(NULL, E_WARNING, "PTY (pseudoterminal) not supported on this system");
  606. return FAILURE;
  607. #endif
  608. }
  609. /* Mark the descriptor close-on-exec, so it won't be inherited by children */
  610. static php_file_descriptor_t make_descriptor_cloexec(php_file_descriptor_t fd)
  611. {
  612. #ifdef PHP_WIN32
  613. return dup_handle(fd, FALSE, TRUE);
  614. #else
  615. #if defined(F_SETFD) && defined(FD_CLOEXEC)
  616. fcntl(fd, F_SETFD, FD_CLOEXEC);
  617. #endif
  618. return fd;
  619. #endif
  620. }
  621. static zend_result set_proc_descriptor_to_pipe(descriptorspec_item *desc, zend_string *zmode)
  622. {
  623. php_file_descriptor_t newpipe[2];
  624. if (pipe(newpipe)) {
  625. php_error_docref(NULL, E_WARNING, "Unable to create pipe %s", strerror(errno));
  626. return FAILURE;
  627. }
  628. desc->type = DESCRIPTOR_TYPE_PIPE;
  629. if (strncmp(ZSTR_VAL(zmode), "w", 1) != 0) {
  630. desc->parentend = newpipe[1];
  631. desc->childend = newpipe[0];
  632. desc->mode_flags = O_WRONLY;
  633. } else {
  634. desc->parentend = newpipe[0];
  635. desc->childend = newpipe[1];
  636. desc->mode_flags = O_RDONLY;
  637. }
  638. desc->parentend = make_descriptor_cloexec(desc->parentend);
  639. #ifdef PHP_WIN32
  640. if (ZSTR_LEN(zmode) >= 2 && ZSTR_VAL(zmode)[1] == 'b')
  641. desc->mode_flags |= O_BINARY;
  642. #endif
  643. return SUCCESS;
  644. }
  645. #ifdef PHP_WIN32
  646. #define create_socketpair(socks) socketpair_win32(AF_INET, SOCK_STREAM, 0, (socks), 0)
  647. #else
  648. #define create_socketpair(socks) socketpair(AF_UNIX, SOCK_STREAM, 0, (socks))
  649. #endif
  650. static zend_result set_proc_descriptor_to_socket(descriptorspec_item *desc)
  651. {
  652. php_socket_t sock[2];
  653. if (create_socketpair(sock)) {
  654. zend_string *err = php_socket_error_str(php_socket_errno());
  655. php_error_docref(NULL, E_WARNING, "Unable to create socket pair: %s", ZSTR_VAL(err));
  656. zend_string_release(err);
  657. return FAILURE;
  658. }
  659. desc->type = DESCRIPTOR_TYPE_SOCKET;
  660. desc->parentend = make_descriptor_cloexec((php_file_descriptor_t) sock[0]);
  661. /* Pass sock[1] to child because it will never use overlapped IO on Windows. */
  662. desc->childend = (php_file_descriptor_t) sock[1];
  663. return SUCCESS;
  664. }
  665. static zend_result set_proc_descriptor_to_file(descriptorspec_item *desc, zend_string *file_path,
  666. zend_string *file_mode)
  667. {
  668. php_socket_t fd;
  669. /* try a wrapper */
  670. php_stream *stream = php_stream_open_wrapper(ZSTR_VAL(file_path), ZSTR_VAL(file_mode),
  671. REPORT_ERRORS|STREAM_WILL_CAST, NULL);
  672. if (stream == NULL) {
  673. return FAILURE;
  674. }
  675. /* force into an fd */
  676. if (php_stream_cast(stream, PHP_STREAM_CAST_RELEASE|PHP_STREAM_AS_FD, (void **)&fd,
  677. REPORT_ERRORS) == FAILURE) {
  678. return FAILURE;
  679. }
  680. #ifdef PHP_WIN32
  681. desc->childend = dup_fd_as_handle((int)fd);
  682. _close((int)fd);
  683. /* Simulate the append mode by fseeking to the end of the file
  684. * This introduces a potential race condition, but it is the best we can do */
  685. if (strchr(ZSTR_VAL(file_mode), 'a')) {
  686. SetFilePointer(desc->childend, 0, NULL, FILE_END);
  687. }
  688. #else
  689. desc->childend = fd;
  690. #endif
  691. return SUCCESS;
  692. }
  693. static zend_result dup_proc_descriptor(php_file_descriptor_t from, php_file_descriptor_t *to,
  694. zend_ulong nindex)
  695. {
  696. #ifdef PHP_WIN32
  697. *to = dup_handle(from, TRUE, FALSE);
  698. if (*to == NULL) {
  699. php_error_docref(NULL, E_WARNING, "Failed to dup() for descriptor " ZEND_LONG_FMT, nindex);
  700. return FAILURE;
  701. }
  702. #else
  703. *to = dup(from);
  704. if (*to < 0) {
  705. php_error_docref(NULL, E_WARNING, "Failed to dup() for descriptor " ZEND_LONG_FMT ": %s",
  706. nindex, strerror(errno));
  707. return FAILURE;
  708. }
  709. #endif
  710. return SUCCESS;
  711. }
  712. static zend_result redirect_proc_descriptor(descriptorspec_item *desc, int target,
  713. descriptorspec_item *descriptors, int ndesc, int nindex)
  714. {
  715. php_file_descriptor_t redirect_to = PHP_INVALID_FD;
  716. for (int i = 0; i < ndesc; i++) {
  717. if (descriptors[i].index == target) {
  718. redirect_to = descriptors[i].childend;
  719. break;
  720. }
  721. }
  722. if (redirect_to == PHP_INVALID_FD) { /* Didn't find the index we wanted */
  723. if (target < 0 || target > 2) {
  724. php_error_docref(NULL, E_WARNING, "Redirection target %d not found", target);
  725. return FAILURE;
  726. }
  727. /* Support referring to a stdin/stdout/stderr pipe adopted from the parent,
  728. * which happens whenever an explicit override is not provided. */
  729. #ifndef PHP_WIN32
  730. redirect_to = target;
  731. #else
  732. switch (target) {
  733. case 0: redirect_to = GetStdHandle(STD_INPUT_HANDLE); break;
  734. case 1: redirect_to = GetStdHandle(STD_OUTPUT_HANDLE); break;
  735. case 2: redirect_to = GetStdHandle(STD_ERROR_HANDLE); break;
  736. EMPTY_SWITCH_DEFAULT_CASE()
  737. }
  738. #endif
  739. }
  740. return dup_proc_descriptor(redirect_to, &desc->childend, nindex);
  741. }
  742. /* Process one item from `$descriptorspec` argument to `proc_open` */
  743. static zend_result set_proc_descriptor_from_array(zval *descitem, descriptorspec_item *descriptors,
  744. int ndesc, int nindex, int *pty_master_fd, int *pty_slave_fd) {
  745. zend_string *ztype = get_string_parameter(descitem, 0, "handle qualifier");
  746. if (!ztype) {
  747. return FAILURE;
  748. }
  749. zend_string *zmode = NULL, *zfile = NULL;
  750. zend_result retval = FAILURE;
  751. if (zend_string_equals_literal(ztype, "pipe")) {
  752. /* Set descriptor to pipe */
  753. zmode = get_string_parameter(descitem, 1, "mode parameter for 'pipe'");
  754. if (zmode == NULL) {
  755. goto finish;
  756. }
  757. retval = set_proc_descriptor_to_pipe(&descriptors[ndesc], zmode);
  758. } else if (zend_string_equals_literal(ztype, "socket")) {
  759. /* Set descriptor to socketpair */
  760. retval = set_proc_descriptor_to_socket(&descriptors[ndesc]);
  761. } else if (zend_string_equals_literal(ztype, "file")) {
  762. /* Set descriptor to file */
  763. if ((zfile = get_string_parameter(descitem, 1, "file name parameter for 'file'")) == NULL) {
  764. goto finish;
  765. }
  766. if ((zmode = get_string_parameter(descitem, 2, "mode parameter for 'file'")) == NULL) {
  767. goto finish;
  768. }
  769. retval = set_proc_descriptor_to_file(&descriptors[ndesc], zfile, zmode);
  770. } else if (zend_string_equals_literal(ztype, "redirect")) {
  771. /* Redirect descriptor to whatever another descriptor is set to */
  772. zval *ztarget = zend_hash_index_find_deref(Z_ARRVAL_P(descitem), 1);
  773. if (!ztarget) {
  774. zend_value_error("Missing redirection target");
  775. goto finish;
  776. }
  777. if (Z_TYPE_P(ztarget) != IS_LONG) {
  778. zend_value_error("Redirection target must be of type int, %s given", zend_zval_type_name(ztarget));
  779. goto finish;
  780. }
  781. retval = redirect_proc_descriptor(
  782. &descriptors[ndesc], (int)Z_LVAL_P(ztarget), descriptors, ndesc, nindex);
  783. } else if (zend_string_equals_literal(ztype, "null")) {
  784. /* Set descriptor to blackhole (discard all data written) */
  785. retval = set_proc_descriptor_to_blackhole(&descriptors[ndesc]);
  786. } else if (zend_string_equals_literal(ztype, "pty")) {
  787. /* Set descriptor to slave end of PTY */
  788. retval = set_proc_descriptor_to_pty(&descriptors[ndesc], pty_master_fd, pty_slave_fd);
  789. } else {
  790. php_error_docref(NULL, E_WARNING, "%s is not a valid descriptor spec/mode", ZSTR_VAL(ztype));
  791. goto finish;
  792. }
  793. finish:
  794. if (zmode) zend_string_release(zmode);
  795. if (zfile) zend_string_release(zfile);
  796. zend_string_release(ztype);
  797. return retval;
  798. }
  799. static zend_result set_proc_descriptor_from_resource(zval *resource, descriptorspec_item *desc, int nindex)
  800. {
  801. /* Should be a stream - try and dup the descriptor */
  802. php_stream *stream = (php_stream*)zend_fetch_resource(Z_RES_P(resource), "stream",
  803. php_file_le_stream());
  804. if (stream == NULL) {
  805. return FAILURE;
  806. }
  807. php_socket_t fd;
  808. zend_result status = php_stream_cast(stream, PHP_STREAM_AS_FD, (void **)&fd, REPORT_ERRORS);
  809. if (status == FAILURE) {
  810. return FAILURE;
  811. }
  812. #ifdef PHP_WIN32
  813. php_file_descriptor_t fd_t = (php_file_descriptor_t)_get_osfhandle(fd);
  814. #else
  815. php_file_descriptor_t fd_t = fd;
  816. #endif
  817. return dup_proc_descriptor(fd_t, &desc->childend, nindex);
  818. }
  819. #ifndef PHP_WIN32
  820. static zend_result close_parentends_of_pipes(descriptorspec_item *descriptors, int ndesc)
  821. {
  822. /* We are running in child process
  823. * Close the 'parent end' of pipes which were opened before forking/spawning
  824. * Also, dup() the child end of all pipes as necessary so they will use the FD
  825. * number which the user requested */
  826. for (int i = 0; i < ndesc; i++) {
  827. if (descriptors[i].type != DESCRIPTOR_TYPE_STD) {
  828. close(descriptors[i].parentend);
  829. }
  830. if (descriptors[i].childend != descriptors[i].index) {
  831. if (dup2(descriptors[i].childend, descriptors[i].index) < 0) {
  832. php_error_docref(NULL, E_WARNING, "Unable to copy file descriptor %d (for pipe) into " \
  833. "file descriptor %d: %s", descriptors[i].childend, descriptors[i].index, strerror(errno));
  834. return FAILURE;
  835. }
  836. close(descriptors[i].childend);
  837. }
  838. }
  839. return SUCCESS;
  840. }
  841. #endif
  842. static void close_all_descriptors(descriptorspec_item *descriptors, int ndesc)
  843. {
  844. for (int i = 0; i < ndesc; i++) {
  845. close_descriptor(descriptors[i].childend);
  846. if (descriptors[i].parentend)
  847. close_descriptor(descriptors[i].parentend);
  848. }
  849. }
  850. static void efree_argv(char **argv)
  851. {
  852. if (argv) {
  853. char **arg = argv;
  854. while (*arg != NULL) {
  855. efree(*arg);
  856. arg++;
  857. }
  858. efree(argv);
  859. }
  860. }
  861. /* {{{ Execute a command, with specified files used for input/output */
  862. PHP_FUNCTION(proc_open)
  863. {
  864. zend_string *command_str;
  865. HashTable *command_ht;
  866. HashTable *descriptorspec; /* Mandatory argument */
  867. zval *pipes; /* Mandatory argument */
  868. char *cwd = NULL; /* Optional argument */
  869. size_t cwd_len = 0; /* Optional argument */
  870. zval *environment = NULL, *other_options = NULL; /* Optional arguments */
  871. php_process_env env;
  872. int ndesc = 0;
  873. int i;
  874. zval *descitem = NULL;
  875. zend_string *str_index;
  876. zend_ulong nindex;
  877. descriptorspec_item *descriptors = NULL;
  878. #ifdef PHP_WIN32
  879. PROCESS_INFORMATION pi;
  880. HANDLE childHandle;
  881. STARTUPINFOW si;
  882. BOOL newprocok;
  883. DWORD dwCreateFlags = 0;
  884. UINT old_error_mode;
  885. char cur_cwd[MAXPATHLEN];
  886. wchar_t *cmdw = NULL, *cwdw = NULL, *envpw = NULL;
  887. size_t cmdw_len;
  888. bool suppress_errors = 0;
  889. bool bypass_shell = 0;
  890. bool blocking_pipes = 0;
  891. bool create_process_group = 0;
  892. bool create_new_console = 0;
  893. #else
  894. char **argv = NULL;
  895. #endif
  896. int pty_master_fd = -1, pty_slave_fd = -1;
  897. php_process_id_t child;
  898. php_process_handle *proc;
  899. ZEND_PARSE_PARAMETERS_START(3, 6)
  900. Z_PARAM_ARRAY_HT_OR_STR(command_ht, command_str)
  901. Z_PARAM_ARRAY_HT(descriptorspec)
  902. Z_PARAM_ZVAL(pipes)
  903. Z_PARAM_OPTIONAL
  904. Z_PARAM_STRING_OR_NULL(cwd, cwd_len)
  905. Z_PARAM_ARRAY_OR_NULL(environment)
  906. Z_PARAM_ARRAY_OR_NULL(other_options)
  907. ZEND_PARSE_PARAMETERS_END();
  908. memset(&env, 0, sizeof(env));
  909. if (command_ht) {
  910. uint32_t num_elems = zend_hash_num_elements(command_ht);
  911. if (num_elems == 0) {
  912. zend_argument_value_error(1, "must have at least one element");
  913. RETURN_THROWS();
  914. }
  915. #ifdef PHP_WIN32
  916. /* Automatically bypass shell if command is given as an array */
  917. bypass_shell = 1;
  918. command_str = create_win_command_from_args(command_ht);
  919. #else
  920. command_str = get_command_from_array(command_ht, &argv, num_elems);
  921. #endif
  922. if (!command_str) {
  923. #ifndef PHP_WIN32
  924. efree_argv(argv);
  925. #endif
  926. RETURN_FALSE;
  927. }
  928. } else {
  929. zend_string_addref(command_str);
  930. }
  931. #ifdef PHP_WIN32
  932. if (other_options) {
  933. suppress_errors = get_option(other_options, "suppress_errors", strlen("suppress_errors"));
  934. /* TODO: Deprecate in favor of array command? */
  935. bypass_shell = bypass_shell || get_option(other_options, "bypass_shell", strlen("bypass_shell"));
  936. blocking_pipes = get_option(other_options, "blocking_pipes", strlen("blocking_pipes"));
  937. create_process_group = get_option(other_options, "create_process_group", strlen("create_process_group"));
  938. create_new_console = get_option(other_options, "create_new_console", strlen("create_new_console"));
  939. }
  940. #endif
  941. if (environment) {
  942. env = _php_array_to_envp(environment);
  943. }
  944. descriptors = alloc_descriptor_array(descriptorspec);
  945. /* Walk the descriptor spec and set up files/pipes */
  946. ZEND_HASH_FOREACH_KEY_VAL(descriptorspec, nindex, str_index, descitem) {
  947. if (str_index) {
  948. zend_argument_value_error(2, "must be an integer indexed array");
  949. goto exit_fail;
  950. }
  951. descriptors[ndesc].index = (int)nindex;
  952. if (Z_TYPE_P(descitem) == IS_RESOURCE) {
  953. if (set_proc_descriptor_from_resource(descitem, &descriptors[ndesc], ndesc) == FAILURE) {
  954. goto exit_fail;
  955. }
  956. } else if (Z_TYPE_P(descitem) == IS_ARRAY) {
  957. if (set_proc_descriptor_from_array(descitem, descriptors, ndesc, (int)nindex,
  958. &pty_master_fd, &pty_slave_fd) == FAILURE) {
  959. goto exit_fail;
  960. }
  961. } else {
  962. zend_argument_value_error(2, "must only contain arrays and streams");
  963. goto exit_fail;
  964. }
  965. ndesc++;
  966. } ZEND_HASH_FOREACH_END();
  967. #ifdef PHP_WIN32
  968. if (cwd == NULL) {
  969. char *getcwd_result = VCWD_GETCWD(cur_cwd, MAXPATHLEN);
  970. if (!getcwd_result) {
  971. php_error_docref(NULL, E_WARNING, "Cannot get current directory");
  972. goto exit_fail;
  973. }
  974. cwd = cur_cwd;
  975. }
  976. cwdw = php_win32_cp_any_to_w(cwd);
  977. if (!cwdw) {
  978. php_error_docref(NULL, E_WARNING, "CWD conversion failed");
  979. goto exit_fail;
  980. }
  981. init_startup_info(&si, descriptors, ndesc);
  982. init_process_info(&pi);
  983. if (suppress_errors) {
  984. old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
  985. }
  986. dwCreateFlags = NORMAL_PRIORITY_CLASS;
  987. if(strcmp(sapi_module.name, "cli") != 0) {
  988. dwCreateFlags |= CREATE_NO_WINDOW;
  989. }
  990. if (create_process_group) {
  991. dwCreateFlags |= CREATE_NEW_PROCESS_GROUP;
  992. }
  993. if (create_new_console) {
  994. dwCreateFlags |= CREATE_NEW_CONSOLE;
  995. }
  996. envpw = php_win32_cp_env_any_to_w(env.envp);
  997. if (envpw) {
  998. dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT;
  999. } else {
  1000. if (env.envp) {
  1001. php_error_docref(NULL, E_WARNING, "ENV conversion failed");
  1002. goto exit_fail;
  1003. }
  1004. }
  1005. cmdw = php_win32_cp_conv_any_to_w(ZSTR_VAL(command_str), ZSTR_LEN(command_str), &cmdw_len);
  1006. if (!cmdw) {
  1007. php_error_docref(NULL, E_WARNING, "Command conversion failed");
  1008. goto exit_fail;
  1009. }
  1010. if (!bypass_shell) {
  1011. if (convert_command_to_use_shell(&cmdw, cmdw_len) == FAILURE) {
  1012. goto exit_fail;
  1013. }
  1014. }
  1015. newprocok = CreateProcessW(NULL, cmdw, &php_proc_open_security,
  1016. &php_proc_open_security, TRUE, dwCreateFlags, envpw, cwdw, &si, &pi);
  1017. if (suppress_errors) {
  1018. SetErrorMode(old_error_mode);
  1019. }
  1020. if (newprocok == FALSE) {
  1021. DWORD dw = GetLastError();
  1022. close_all_descriptors(descriptors, ndesc);
  1023. php_error_docref(NULL, E_WARNING, "CreateProcess failed, error code: %u", dw);
  1024. goto exit_fail;
  1025. }
  1026. childHandle = pi.hProcess;
  1027. child = pi.dwProcessId;
  1028. CloseHandle(pi.hThread);
  1029. #elif HAVE_FORK
  1030. /* the Unix way */
  1031. child = fork();
  1032. if (child == 0) {
  1033. /* This is the child process */
  1034. if (close_parentends_of_pipes(descriptors, ndesc) == FAILURE) {
  1035. /* We are already in child process and can't do anything to make
  1036. * `proc_open` return an error in the parent
  1037. * All we can do is exit with a non-zero (error) exit code */
  1038. _exit(127);
  1039. }
  1040. if (cwd) {
  1041. php_ignore_value(chdir(cwd));
  1042. }
  1043. if (argv) {
  1044. /* execvpe() is non-portable, use environ instead. */
  1045. if (env.envarray) {
  1046. environ = env.envarray;
  1047. }
  1048. execvp(ZSTR_VAL(command_str), argv);
  1049. } else {
  1050. if (env.envarray) {
  1051. execle("/bin/sh", "sh", "-c", ZSTR_VAL(command_str), NULL, env.envarray);
  1052. } else {
  1053. execl("/bin/sh", "sh", "-c", ZSTR_VAL(command_str), NULL);
  1054. }
  1055. }
  1056. /* If execvp/execle/execl are successful, we will never reach here
  1057. * Display error and exit with non-zero (error) status code */
  1058. php_error_docref(NULL, E_WARNING, "Exec failed: %s", strerror(errno));
  1059. _exit(127);
  1060. } else if (child < 0) {
  1061. /* Failed to fork() */
  1062. close_all_descriptors(descriptors, ndesc);
  1063. php_error_docref(NULL, E_WARNING, "Fork failed: %s", strerror(errno));
  1064. goto exit_fail;
  1065. }
  1066. #else
  1067. # error You lose (configure should not have let you get here)
  1068. #endif
  1069. /* We forked/spawned and this is the parent */
  1070. pipes = zend_try_array_init(pipes);
  1071. if (!pipes) {
  1072. goto exit_fail;
  1073. }
  1074. proc = (php_process_handle*) emalloc(sizeof(php_process_handle));
  1075. proc->command = zend_string_copy(command_str);
  1076. proc->pipes = emalloc(sizeof(zend_resource *) * ndesc);
  1077. proc->npipes = ndesc;
  1078. proc->child = child;
  1079. #ifdef PHP_WIN32
  1080. proc->childHandle = childHandle;
  1081. #endif
  1082. proc->env = env;
  1083. /* Clean up all the child ends and then open streams on the parent
  1084. * ends, where appropriate */
  1085. for (i = 0; i < ndesc; i++) {
  1086. php_stream *stream = NULL;
  1087. close_descriptor(descriptors[i].childend);
  1088. if (descriptors[i].type == DESCRIPTOR_TYPE_PIPE) {
  1089. char *mode_string = NULL;
  1090. switch (descriptors[i].mode_flags) {
  1091. #ifdef PHP_WIN32
  1092. case O_WRONLY|O_BINARY:
  1093. mode_string = "wb";
  1094. break;
  1095. case O_RDONLY|O_BINARY:
  1096. mode_string = "rb";
  1097. break;
  1098. #endif
  1099. case O_WRONLY:
  1100. mode_string = "w";
  1101. break;
  1102. case O_RDONLY:
  1103. mode_string = "r";
  1104. break;
  1105. case O_RDWR:
  1106. mode_string = "r+";
  1107. break;
  1108. }
  1109. #ifdef PHP_WIN32
  1110. stream = php_stream_fopen_from_fd(_open_osfhandle((zend_intptr_t)descriptors[i].parentend,
  1111. descriptors[i].mode_flags), mode_string, NULL);
  1112. php_stream_set_option(stream, PHP_STREAM_OPTION_PIPE_BLOCKING, blocking_pipes, NULL);
  1113. #else
  1114. stream = php_stream_fopen_from_fd(descriptors[i].parentend, mode_string, NULL);
  1115. #endif
  1116. } else if (descriptors[i].type == DESCRIPTOR_TYPE_SOCKET) {
  1117. stream = php_stream_sock_open_from_socket((php_socket_t) descriptors[i].parentend, NULL);
  1118. } else {
  1119. proc->pipes[i] = NULL;
  1120. }
  1121. if (stream) {
  1122. zval retfp;
  1123. /* nasty hack; don't copy it */
  1124. stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
  1125. php_stream_to_zval(stream, &retfp);
  1126. add_index_zval(pipes, descriptors[i].index, &retfp);
  1127. proc->pipes[i] = Z_RES(retfp);
  1128. Z_ADDREF(retfp);
  1129. }
  1130. }
  1131. if (1) {
  1132. RETVAL_RES(zend_register_resource(proc, le_proc_open));
  1133. } else {
  1134. exit_fail:
  1135. _php_free_envp(env);
  1136. RETVAL_FALSE;
  1137. }
  1138. zend_string_release_ex(command_str, false);
  1139. #ifdef PHP_WIN32
  1140. free(cwdw);
  1141. free(cmdw);
  1142. free(envpw);
  1143. #else
  1144. efree_argv(argv);
  1145. #endif
  1146. #if HAVE_OPENPTY
  1147. if (pty_master_fd != -1) {
  1148. close(pty_master_fd);
  1149. }
  1150. if (pty_slave_fd != -1) {
  1151. close(pty_slave_fd);
  1152. }
  1153. #endif
  1154. if (descriptors) {
  1155. efree(descriptors);
  1156. }
  1157. }
  1158. /* }}} */
  1159. #endif /* PHP_CAN_SUPPORT_PROC_OPEN */