proc_open.c 26 KB

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