PageRenderTime 63ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/sapi/cgi/cgi_main.c

http://github.com/infusion/PHP
C | 2267 lines | 1713 code | 228 blank | 326 comment | 350 complexity | 14e2d2b4e9ff8ecfc059ea3c39ac6ae4 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-3-Clause
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 5 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2011 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. | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
  16. | Stig Bakken <ssb@php.net> |
  17. | Zeev Suraski <zeev@zend.com> |
  18. | FastCGI: Ben Mansell <php@slimyhorror.com> |
  19. | Shane Caraveo <shane@caraveo.com> |
  20. | Dmitry Stogov <dmitry@zend.com> |
  21. +----------------------------------------------------------------------+
  22. */
  23. /* $Id: cgi_main.c 306939 2011-01-01 02:19:59Z felipe $ */
  24. #include "php.h"
  25. #include "php_globals.h"
  26. #include "php_variables.h"
  27. #include "zend_modules.h"
  28. #include "SAPI.h"
  29. #include <stdio.h>
  30. #include "php.h"
  31. #ifdef PHP_WIN32
  32. # include "win32/time.h"
  33. # include "win32/signal.h"
  34. # include <process.h>
  35. #endif
  36. #if HAVE_SYS_TIME_H
  37. # include <sys/time.h>
  38. #endif
  39. #if HAVE_UNISTD_H
  40. # include <unistd.h>
  41. #endif
  42. #if HAVE_SIGNAL_H
  43. # include <signal.h>
  44. #endif
  45. #if HAVE_SETLOCALE
  46. # include <locale.h>
  47. #endif
  48. #if HAVE_SYS_TYPES_H
  49. # include <sys/types.h>
  50. #endif
  51. #if HAVE_SYS_WAIT_H
  52. # include <sys/wait.h>
  53. #endif
  54. #include "zend.h"
  55. #include "zend_extensions.h"
  56. #include "php_ini.h"
  57. #include "php_globals.h"
  58. #include "php_main.h"
  59. #include "fopen_wrappers.h"
  60. #include "ext/standard/php_standard.h"
  61. #ifdef PHP_WIN32
  62. # include <io.h>
  63. # include <fcntl.h>
  64. # include "win32/php_registry.h"
  65. #endif
  66. #ifdef __riscos__
  67. # include <unixlib/local.h>
  68. int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS;
  69. #endif
  70. #include "zend_compile.h"
  71. #include "zend_execute.h"
  72. #include "zend_highlight.h"
  73. #include "zend_indent.h"
  74. #include "php_getopt.h"
  75. #include "fastcgi.h"
  76. #ifndef PHP_WIN32
  77. /* XXX this will need to change later when threaded fastcgi is implemented. shane */
  78. struct sigaction act, old_term, old_quit, old_int;
  79. #endif
  80. static void (*php_php_import_environment_variables)(zval *array_ptr TSRMLS_DC);
  81. #ifndef PHP_WIN32
  82. /* these globals used for forking children on unix systems */
  83. /**
  84. * Number of child processes that will get created to service requests
  85. */
  86. static int children = 0;
  87. /**
  88. * Set to non-zero if we are the parent process
  89. */
  90. static int parent = 1;
  91. /* Did parent received exit signals SIG_TERM/SIG_INT/SIG_QUIT */
  92. static int exit_signal = 0;
  93. /* Is Parent waiting for children to exit */
  94. static int parent_waiting = 0;
  95. /**
  96. * Process group
  97. */
  98. static pid_t pgroup;
  99. #endif
  100. #define PHP_MODE_STANDARD 1
  101. #define PHP_MODE_HIGHLIGHT 2
  102. #define PHP_MODE_INDENT 3
  103. #define PHP_MODE_LINT 4
  104. #define PHP_MODE_STRIP 5
  105. static char *php_optarg = NULL;
  106. static int php_optind = 1;
  107. static zend_module_entry cgi_module_entry;
  108. static const opt_struct OPTIONS[] = {
  109. {'a', 0, "interactive"},
  110. {'b', 1, "bindpath"},
  111. {'C', 0, "no-chdir"},
  112. {'c', 1, "php-ini"},
  113. {'d', 1, "define"},
  114. {'e', 0, "profile-info"},
  115. {'f', 1, "file"},
  116. {'h', 0, "help"},
  117. {'i', 0, "info"},
  118. {'l', 0, "syntax-check"},
  119. {'m', 0, "modules"},
  120. {'n', 0, "no-php-ini"},
  121. {'q', 0, "no-header"},
  122. {'s', 0, "syntax-highlight"},
  123. {'s', 0, "syntax-highlighting"},
  124. {'w', 0, "strip"},
  125. {'?', 0, "usage"},/* help alias (both '?' and 'usage') */
  126. {'v', 0, "version"},
  127. {'z', 1, "zend-extension"},
  128. {'T', 1, "timing"},
  129. {'-', 0, NULL} /* end of args */
  130. };
  131. typedef struct _php_cgi_globals_struct {
  132. zend_bool rfc2616_headers;
  133. zend_bool nph;
  134. zend_bool check_shebang_line;
  135. zend_bool fix_pathinfo;
  136. zend_bool force_redirect;
  137. zend_bool discard_path;
  138. zend_bool fcgi_logging;
  139. char *redirect_status_env;
  140. #ifdef PHP_WIN32
  141. zend_bool impersonate;
  142. #endif
  143. HashTable user_config_cache;
  144. } php_cgi_globals_struct;
  145. /* {{{ user_config_cache
  146. *
  147. * Key for each cache entry is dirname(PATH_TRANSLATED).
  148. *
  149. * NOTE: Each cache entry config_hash contains the combination from all user ini files found in
  150. * the path starting from doc_root throught to dirname(PATH_TRANSLATED). There is no point
  151. * storing per-file entries as it would not be possible to detect added / deleted entries
  152. * between separate files.
  153. */
  154. typedef struct _user_config_cache_entry {
  155. time_t expires;
  156. HashTable *user_config;
  157. } user_config_cache_entry;
  158. static void user_config_cache_entry_dtor(user_config_cache_entry *entry)
  159. {
  160. zend_hash_destroy(entry->user_config);
  161. free(entry->user_config);
  162. }
  163. /* }}} */
  164. #ifdef ZTS
  165. static int php_cgi_globals_id;
  166. #define CGIG(v) TSRMG(php_cgi_globals_id, php_cgi_globals_struct *, v)
  167. #else
  168. static php_cgi_globals_struct php_cgi_globals;
  169. #define CGIG(v) (php_cgi_globals.v)
  170. #endif
  171. #ifdef PHP_WIN32
  172. #define TRANSLATE_SLASHES(path) \
  173. { \
  174. char *tmp = path; \
  175. while (*tmp) { \
  176. if (*tmp == '\\') *tmp = '/'; \
  177. tmp++; \
  178. } \
  179. }
  180. #else
  181. #define TRANSLATE_SLASHES(path)
  182. #endif
  183. static int print_module_info(zend_module_entry *module, void *arg TSRMLS_DC)
  184. {
  185. php_printf("%s\n", module->name);
  186. return 0;
  187. }
  188. static int module_name_cmp(const void *a, const void *b TSRMLS_DC)
  189. {
  190. Bucket *f = *((Bucket **) a);
  191. Bucket *s = *((Bucket **) b);
  192. return strcasecmp( ((zend_module_entry *)f->pData)->name,
  193. ((zend_module_entry *)s->pData)->name);
  194. }
  195. static void print_modules(TSRMLS_D)
  196. {
  197. HashTable sorted_registry;
  198. zend_module_entry tmp;
  199. zend_hash_init(&sorted_registry, 50, NULL, NULL, 1);
  200. zend_hash_copy(&sorted_registry, &module_registry, NULL, &tmp, sizeof(zend_module_entry));
  201. zend_hash_sort(&sorted_registry, zend_qsort, module_name_cmp, 0 TSRMLS_CC);
  202. zend_hash_apply_with_argument(&sorted_registry, (apply_func_arg_t) print_module_info, NULL TSRMLS_CC);
  203. zend_hash_destroy(&sorted_registry);
  204. }
  205. static int print_extension_info(zend_extension *ext, void *arg TSRMLS_DC)
  206. {
  207. php_printf("%s\n", ext->name);
  208. return 0;
  209. }
  210. static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s TSRMLS_DC)
  211. {
  212. return strcmp( ((zend_extension *)(*f)->data)->name,
  213. ((zend_extension *)(*s)->data)->name);
  214. }
  215. static void print_extensions(TSRMLS_D)
  216. {
  217. zend_llist sorted_exts;
  218. zend_llist_copy(&sorted_exts, &zend_extensions);
  219. sorted_exts.dtor = NULL;
  220. zend_llist_sort(&sorted_exts, extension_name_cmp TSRMLS_CC);
  221. zend_llist_apply_with_argument(&sorted_exts, (llist_apply_with_arg_func_t) print_extension_info, NULL TSRMLS_CC);
  222. zend_llist_destroy(&sorted_exts);
  223. }
  224. #ifndef STDOUT_FILENO
  225. #define STDOUT_FILENO 1
  226. #endif
  227. static inline size_t sapi_cgibin_single_write(const char *str, uint str_length TSRMLS_DC)
  228. {
  229. #ifdef PHP_WRITE_STDOUT
  230. long ret;
  231. #else
  232. size_t ret;
  233. #endif
  234. if (fcgi_is_fastcgi()) {
  235. fcgi_request *request = (fcgi_request*) SG(server_context);
  236. long ret = fcgi_write(request, FCGI_STDOUT, str, str_length);
  237. if (ret <= 0) {
  238. return 0;
  239. }
  240. return ret;
  241. }
  242. #ifdef PHP_WRITE_STDOUT
  243. ret = write(STDOUT_FILENO, str, str_length);
  244. if (ret <= 0) return 0;
  245. return ret;
  246. #else
  247. ret = fwrite(str, 1, MIN(str_length, 16384), stdout);
  248. return ret;
  249. #endif
  250. }
  251. static int sapi_cgibin_ub_write(const char *str, uint str_length TSRMLS_DC)
  252. {
  253. const char *ptr = str;
  254. uint remaining = str_length;
  255. size_t ret;
  256. while (remaining > 0) {
  257. ret = sapi_cgibin_single_write(ptr, remaining TSRMLS_CC);
  258. if (!ret) {
  259. php_handle_aborted_connection();
  260. return str_length - remaining;
  261. }
  262. ptr += ret;
  263. remaining -= ret;
  264. }
  265. return str_length;
  266. }
  267. static void sapi_cgibin_flush(void *server_context)
  268. {
  269. if (fcgi_is_fastcgi()) {
  270. fcgi_request *request = (fcgi_request*) server_context;
  271. if (
  272. #ifndef PHP_WIN32
  273. !parent &&
  274. #endif
  275. request && !fcgi_flush(request, 0)) {
  276. php_handle_aborted_connection();
  277. }
  278. return;
  279. }
  280. if (fflush(stdout) == EOF) {
  281. php_handle_aborted_connection();
  282. }
  283. }
  284. #define SAPI_CGI_MAX_HEADER_LENGTH 1024
  285. typedef struct _http_error {
  286. int code;
  287. const char* msg;
  288. } http_error;
  289. static const http_error http_error_codes[] = {
  290. {100, "Continue"},
  291. {101, "Switching Protocols"},
  292. {200, "OK"},
  293. {201, "Created"},
  294. {202, "Accepted"},
  295. {203, "Non-Authoritative Information"},
  296. {204, "No Content"},
  297. {205, "Reset Content"},
  298. {206, "Partial Content"},
  299. {300, "Multiple Choices"},
  300. {301, "Moved Permanently"},
  301. {302, "Moved Temporarily"},
  302. {303, "See Other"},
  303. {304, "Not Modified"},
  304. {305, "Use Proxy"},
  305. {400, "Bad Request"},
  306. {401, "Unauthorized"},
  307. {402, "Payment Required"},
  308. {403, "Forbidden"},
  309. {404, "Not Found"},
  310. {405, "Method Not Allowed"},
  311. {406, "Not Acceptable"},
  312. {407, "Proxy Authentication Required"},
  313. {408, "Request Time-out"},
  314. {409, "Conflict"},
  315. {410, "Gone"},
  316. {411, "Length Required"},
  317. {412, "Precondition Failed"},
  318. {413, "Request Entity Too Large"},
  319. {414, "Request-URI Too Large"},
  320. {415, "Unsupported Media Type"},
  321. {500, "Internal Server Error"},
  322. {501, "Not Implemented"},
  323. {502, "Bad Gateway"},
  324. {503, "Service Unavailable"},
  325. {504, "Gateway Time-out"},
  326. {505, "HTTP Version not supported"},
  327. {0, NULL}
  328. };
  329. static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
  330. {
  331. char buf[SAPI_CGI_MAX_HEADER_LENGTH];
  332. sapi_header_struct *h;
  333. zend_llist_position pos;
  334. zend_bool ignore_status = 0;
  335. int response_status = SG(sapi_headers).http_response_code;
  336. if (SG(request_info).no_headers == 1) {
  337. return SAPI_HEADER_SENT_SUCCESSFULLY;
  338. }
  339. if (CGIG(nph) || SG(sapi_headers).http_response_code != 200)
  340. {
  341. int len;
  342. zend_bool has_status = 0;
  343. if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) {
  344. char *s;
  345. len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s\r\n", SG(sapi_headers).http_status_line);
  346. if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) {
  347. response_status = atoi((s + 1));
  348. }
  349. if (len > SAPI_CGI_MAX_HEADER_LENGTH) {
  350. len = SAPI_CGI_MAX_HEADER_LENGTH;
  351. }
  352. } else {
  353. char *s;
  354. if (SG(sapi_headers).http_status_line &&
  355. (s = strchr(SG(sapi_headers).http_status_line, ' ')) != 0 &&
  356. (s - SG(sapi_headers).http_status_line) >= 5 &&
  357. strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0
  358. ) {
  359. len = slprintf(buf, sizeof(buf), "Status:%s\r\n", s);
  360. response_status = atoi((s + 1));
  361. } else {
  362. h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
  363. while (h) {
  364. if (h->header_len > sizeof("Status:")-1 &&
  365. strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
  366. ) {
  367. has_status = 1;
  368. break;
  369. }
  370. h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
  371. }
  372. if (!has_status) {
  373. http_error *err = (http_error*)http_error_codes;
  374. while (err->code != 0) {
  375. if (err->code == SG(sapi_headers).http_response_code) {
  376. break;
  377. }
  378. err++;
  379. }
  380. if (err->msg) {
  381. len = slprintf(buf, sizeof(buf), "Status: %d %s\r\n", SG(sapi_headers).http_response_code, err->msg);
  382. } else {
  383. len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code);
  384. }
  385. }
  386. }
  387. }
  388. if (!has_status) {
  389. PHPWRITE_H(buf, len);
  390. ignore_status = 1;
  391. }
  392. }
  393. h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
  394. while (h) {
  395. /* prevent CRLFCRLF */
  396. if (h->header_len) {
  397. if (h->header_len > sizeof("Status:")-1 &&
  398. strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
  399. ) {
  400. if (!ignore_status) {
  401. ignore_status = 1;
  402. PHPWRITE_H(h->header, h->header_len);
  403. PHPWRITE_H("\r\n", 2);
  404. }
  405. } else if (response_status == 304 && h->header_len > sizeof("Content-Type:")-1 &&
  406. strncasecmp(h->header, "Content-Type:", sizeof("Content-Type:")-1) == 0
  407. ) {
  408. h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
  409. continue;
  410. } else {
  411. PHPWRITE_H(h->header, h->header_len);
  412. PHPWRITE_H("\r\n", 2);
  413. }
  414. }
  415. h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
  416. }
  417. PHPWRITE_H("\r\n", 2);
  418. return SAPI_HEADER_SENT_SUCCESSFULLY;
  419. }
  420. #ifndef STDIN_FILENO
  421. # define STDIN_FILENO 0
  422. #endif
  423. static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
  424. {
  425. uint read_bytes = 0;
  426. int tmp_read_bytes;
  427. count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes));
  428. while (read_bytes < count_bytes) {
  429. if (fcgi_is_fastcgi()) {
  430. fcgi_request *request = (fcgi_request*) SG(server_context);
  431. tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes);
  432. } else {
  433. tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes);
  434. }
  435. if (tmp_read_bytes <= 0) {
  436. break;
  437. }
  438. read_bytes += tmp_read_bytes;
  439. }
  440. return read_bytes;
  441. }
  442. static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC)
  443. {
  444. /* when php is started by mod_fastcgi, no regular environment
  445. * is provided to PHP. It is always sent to PHP at the start
  446. * of a request. So we have to do our own lookup to get env
  447. * vars. This could probably be faster somehow. */
  448. if (fcgi_is_fastcgi()) {
  449. fcgi_request *request = (fcgi_request*) SG(server_context);
  450. return fcgi_getenv(request, name, name_len);
  451. }
  452. /* if cgi, or fastcgi and not found in fcgi env
  453. check the regular environment */
  454. return getenv(name);
  455. }
  456. static char *_sapi_cgibin_putenv(char *name, char *value TSRMLS_DC)
  457. {
  458. int name_len;
  459. #if !HAVE_SETENV || !HAVE_UNSETENV
  460. int len;
  461. char *buf;
  462. #endif
  463. if (!name) {
  464. return NULL;
  465. }
  466. name_len = strlen(name);
  467. /* when php is started by mod_fastcgi, no regular environment
  468. * is provided to PHP. It is always sent to PHP at the start
  469. * of a request. So we have to do our own lookup to get env
  470. * vars. This could probably be faster somehow. */
  471. if (fcgi_is_fastcgi()) {
  472. fcgi_request *request = (fcgi_request*) SG(server_context);
  473. return fcgi_putenv(request, name, name_len, value);
  474. }
  475. #if HAVE_SETENV
  476. if (value) {
  477. setenv(name, value, 1);
  478. }
  479. #endif
  480. #if HAVE_UNSETENV
  481. if (!value) {
  482. unsetenv(name);
  483. }
  484. #endif
  485. #if !HAVE_SETENV || !HAVE_UNSETENV
  486. /* if cgi, or fastcgi and not found in fcgi env
  487. check the regular environment
  488. this leaks, but it's only cgi anyway, we'll fix
  489. it for 5.0
  490. */
  491. len = name_len + (value ? strlen(value) : 0) + sizeof("=") + 2;
  492. buf = (char *) malloc(len);
  493. if (buf == NULL) {
  494. return getenv(name);
  495. }
  496. #endif
  497. #if !HAVE_SETENV
  498. if (value) {
  499. len = slprintf(buf, len - 1, "%s=%s", name, value);
  500. putenv(buf);
  501. }
  502. #endif
  503. #if !HAVE_UNSETENV
  504. if (!value) {
  505. len = slprintf(buf, len - 1, "%s=", name);
  506. putenv(buf);
  507. }
  508. #endif
  509. return getenv(name);
  510. }
  511. static char *sapi_cgi_read_cookies(TSRMLS_D)
  512. {
  513. return sapi_cgibin_getenv((char *) "HTTP_COOKIE", sizeof("HTTP_COOKIE")-1 TSRMLS_CC);
  514. }
  515. void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC)
  516. {
  517. if (PG(http_globals)[TRACK_VARS_ENV] &&
  518. array_ptr != PG(http_globals)[TRACK_VARS_ENV] &&
  519. Z_TYPE_P(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY &&
  520. zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_ENV])) > 0
  521. ) {
  522. zval_dtor(array_ptr);
  523. *array_ptr = *PG(http_globals)[TRACK_VARS_ENV];
  524. INIT_PZVAL(array_ptr);
  525. zval_copy_ctor(array_ptr);
  526. return;
  527. } else if (PG(http_globals)[TRACK_VARS_SERVER] &&
  528. array_ptr != PG(http_globals)[TRACK_VARS_SERVER] &&
  529. Z_TYPE_P(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY &&
  530. zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER])) > 0
  531. ) {
  532. zval_dtor(array_ptr);
  533. *array_ptr = *PG(http_globals)[TRACK_VARS_SERVER];
  534. INIT_PZVAL(array_ptr);
  535. zval_copy_ctor(array_ptr);
  536. return;
  537. }
  538. /* call php's original import as a catch-all */
  539. php_php_import_environment_variables(array_ptr TSRMLS_CC);
  540. if (fcgi_is_fastcgi()) {
  541. fcgi_request *request = (fcgi_request*) SG(server_context);
  542. HashPosition pos;
  543. char *var, **val;
  544. uint var_len;
  545. ulong idx;
  546. int filter_arg = (array_ptr == PG(http_globals)[TRACK_VARS_ENV])?PARSE_ENV:PARSE_SERVER;
  547. for (zend_hash_internal_pointer_reset_ex(request->env, &pos);
  548. zend_hash_get_current_key_ex(request->env, &var, &var_len, &idx, 0, &pos) == HASH_KEY_IS_STRING &&
  549. zend_hash_get_current_data_ex(request->env, (void **) &val, &pos) == SUCCESS;
  550. zend_hash_move_forward_ex(request->env, &pos)
  551. ) {
  552. unsigned int new_val_len;
  553. if (sapi_module.input_filter(filter_arg, var, val, strlen(*val), &new_val_len TSRMLS_CC)) {
  554. php_register_variable_safe(var, *val, new_val_len, array_ptr TSRMLS_CC);
  555. }
  556. }
  557. }
  558. }
  559. static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC)
  560. {
  561. unsigned int php_self_len;
  562. char *php_self;
  563. /* In CGI mode, we consider the environment to be a part of the server
  564. * variables
  565. */
  566. php_import_environment_variables(track_vars_array TSRMLS_CC);
  567. if (CGIG(fix_pathinfo)) {
  568. char *script_name = SG(request_info).request_uri;
  569. unsigned int script_name_len = script_name ? strlen(script_name) : 0;
  570. char *path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO")-1 TSRMLS_CC);
  571. unsigned int path_info_len = path_info ? strlen(path_info) : 0;
  572. php_self_len = script_name_len + path_info_len;
  573. php_self = emalloc(php_self_len + 1);
  574. if (script_name) {
  575. memcpy(php_self, script_name, script_name_len + 1);
  576. }
  577. if (path_info) {
  578. memcpy(php_self + script_name_len, path_info, path_info_len + 1);
  579. }
  580. /* Build the special-case PHP_SELF variable for the CGI version */
  581. if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
  582. php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
  583. }
  584. efree(php_self);
  585. } else {
  586. php_self = SG(request_info).request_uri ? SG(request_info).request_uri : "";
  587. php_self_len = strlen(php_self);
  588. if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
  589. php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
  590. }
  591. }
  592. }
  593. static void sapi_cgi_log_message(char *message)
  594. {
  595. TSRMLS_FETCH();
  596. if (fcgi_is_fastcgi() && CGIG(fcgi_logging)) {
  597. fcgi_request *request;
  598. request = (fcgi_request*) SG(server_context);
  599. if (request) {
  600. int len = strlen(message);
  601. char *buf = malloc(len+2);
  602. memcpy(buf, message, len);
  603. memcpy(buf + len, "\n", sizeof("\n"));
  604. fcgi_write(request, FCGI_STDERR, buf, len+1);
  605. free(buf);
  606. } else {
  607. fprintf(stderr, "%s\n", message);
  608. }
  609. /* ignore return code */
  610. } else {
  611. fprintf(stderr, "%s\n", message);
  612. }
  613. }
  614. static time_t sapi_cgi_request_time(TSRMLS_D)
  615. {
  616. char *rq_time = sapi_cgibin_getenv("RAW_TIME", sizeof("RAW_TIME") - 1 TSRMLS_CC);
  617. if (rq_time) {
  618. return atol(rq_time);
  619. } else {
  620. return 0;
  621. }
  622. }
  623. /* {{{ php_cgi_ini_activate_user_config
  624. */
  625. static void php_cgi_ini_activate_user_config(char *path, int path_len, const char *doc_root, int doc_root_len, int start TSRMLS_DC)
  626. {
  627. char *ptr;
  628. user_config_cache_entry *new_entry, *entry;
  629. time_t request_time = sapi_get_request_time(TSRMLS_C);
  630. /* Find cached config entry: If not found, create one */
  631. if (zend_hash_find(&CGIG(user_config_cache), path, path_len + 1, (void **) &entry) == FAILURE) {
  632. new_entry = pemalloc(sizeof(user_config_cache_entry), 1);
  633. new_entry->expires = 0;
  634. new_entry->user_config = (HashTable *) pemalloc(sizeof(HashTable), 1);
  635. zend_hash_init(new_entry->user_config, 0, NULL, (dtor_func_t) config_zval_dtor, 1);
  636. zend_hash_update(&CGIG(user_config_cache), path, path_len + 1, new_entry, sizeof(user_config_cache_entry), (void **) &entry);
  637. free(new_entry);
  638. }
  639. /* Check whether cache entry has expired and rescan if it is */
  640. if (request_time > entry->expires) {
  641. char * real_path;
  642. int real_path_len;
  643. char *s1, *s2;
  644. int s_len;
  645. /* Clear the expired config */
  646. zend_hash_clean(entry->user_config);
  647. if (!IS_ABSOLUTE_PATH(path, path_len)) {
  648. real_path = tsrm_realpath(path, NULL TSRMLS_CC);
  649. /* see #51688, looks like we may get invalid path as doc root using cgi with apache */
  650. if (real_path == NULL) {
  651. return;
  652. }
  653. real_path_len = strlen(real_path);
  654. path = real_path;
  655. path_len = real_path_len;
  656. }
  657. if (path_len > doc_root_len) {
  658. s1 = (char *) doc_root;
  659. s2 = path;
  660. s_len = doc_root_len;
  661. } else {
  662. s1 = path;
  663. s2 = (char *) doc_root;
  664. s_len = path_len;
  665. }
  666. /* we have to test if path is part of DOCUMENT_ROOT.
  667. if it is inside the docroot, we scan the tree up to the docroot
  668. to find more user.ini, if not we only scan the current path.
  669. */
  670. #ifdef PHP_WIN32
  671. if (strnicmp(s1, s2, s_len) == 0) {
  672. #else
  673. if (strncmp(s1, s2, s_len) == 0) {
  674. #endif
  675. ptr = s2 + start; /* start is the point where doc_root ends! */
  676. while ((ptr = strchr(ptr, DEFAULT_SLASH)) != NULL) {
  677. *ptr = 0;
  678. php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
  679. *ptr = '/';
  680. ptr++;
  681. }
  682. } else {
  683. php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
  684. }
  685. entry->expires = request_time + PG(user_ini_cache_ttl);
  686. }
  687. /* Activate ini entries with values from the user config hash */
  688. php_ini_activate_config(entry->user_config, PHP_INI_PERDIR, PHP_INI_STAGE_HTACCESS TSRMLS_CC);
  689. }
  690. /* }}} */
  691. static int sapi_cgi_activate(TSRMLS_D)
  692. {
  693. char *path, *doc_root, *server_name;
  694. uint path_len, doc_root_len, server_name_len;
  695. /* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */
  696. if (!SG(request_info).path_translated) {
  697. return FAILURE;
  698. }
  699. if (php_ini_has_per_host_config()) {
  700. /* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */
  701. server_name = sapi_cgibin_getenv("SERVER_NAME", sizeof("SERVER_NAME") - 1 TSRMLS_CC);
  702. /* SERVER_NAME should also be defined at this stage..but better check it anyway */
  703. if (server_name) {
  704. server_name_len = strlen(server_name);
  705. server_name = estrndup(server_name, server_name_len);
  706. zend_str_tolower(server_name, server_name_len);
  707. php_ini_activate_per_host_config(server_name, server_name_len + 1 TSRMLS_CC);
  708. efree(server_name);
  709. }
  710. }
  711. if (php_ini_has_per_dir_config() ||
  712. (PG(user_ini_filename) && *PG(user_ini_filename))
  713. ) {
  714. /* Prepare search path */
  715. path_len = strlen(SG(request_info).path_translated);
  716. /* Make sure we have trailing slash! */
  717. if (!IS_SLASH(SG(request_info).path_translated[path_len])) {
  718. path = emalloc(path_len + 2);
  719. memcpy(path, SG(request_info).path_translated, path_len + 1);
  720. path_len = zend_dirname(path, path_len);
  721. path[path_len++] = DEFAULT_SLASH;
  722. } else {
  723. path = estrndup(SG(request_info).path_translated, path_len);
  724. path_len = zend_dirname(path, path_len);
  725. }
  726. path[path_len] = 0;
  727. /* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */
  728. php_ini_activate_per_dir_config(path, path_len TSRMLS_CC); /* Note: for global settings sake we check from root to path */
  729. /* Load and activate user ini files in path starting from DOCUMENT_ROOT */
  730. if (PG(user_ini_filename) && *PG(user_ini_filename)) {
  731. doc_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1 TSRMLS_CC);
  732. /* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */
  733. if (doc_root) {
  734. doc_root_len = strlen(doc_root);
  735. if (doc_root_len > 0 && IS_SLASH(doc_root[doc_root_len - 1])) {
  736. --doc_root_len;
  737. }
  738. #ifdef PHP_WIN32
  739. /* paths on windows should be case-insensitive */
  740. doc_root = estrndup(doc_root, doc_root_len);
  741. zend_str_tolower(doc_root, doc_root_len);
  742. #endif
  743. php_cgi_ini_activate_user_config(path, path_len, doc_root, doc_root_len, doc_root_len - 1 TSRMLS_CC);
  744. }
  745. }
  746. #ifdef PHP_WIN32
  747. efree(doc_root);
  748. #endif
  749. efree(path);
  750. }
  751. return SUCCESS;
  752. }
  753. static int sapi_cgi_deactivate(TSRMLS_D)
  754. {
  755. /* flush only when SAPI was started. The reasons are:
  756. 1. SAPI Deactivate is called from two places: module init and request shutdown
  757. 2. When the first call occurs and the request is not set up, flush fails on FastCGI.
  758. */
  759. if (SG(sapi_started)) {
  760. if (fcgi_is_fastcgi()) {
  761. if (
  762. #ifndef PHP_WIN32
  763. !parent &&
  764. #endif
  765. !fcgi_finish_request((fcgi_request*)SG(server_context), 0)) {
  766. php_handle_aborted_connection();
  767. }
  768. } else {
  769. sapi_cgibin_flush(SG(server_context));
  770. }
  771. }
  772. return SUCCESS;
  773. }
  774. static int php_cgi_startup(sapi_module_struct *sapi_module)
  775. {
  776. if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) {
  777. return FAILURE;
  778. }
  779. return SUCCESS;
  780. }
  781. /* {{{ sapi_module_struct cgi_sapi_module
  782. */
  783. static sapi_module_struct cgi_sapi_module = {
  784. "cgi-fcgi", /* name */
  785. "CGI/FastCGI", /* pretty name */
  786. php_cgi_startup, /* startup */
  787. php_module_shutdown_wrapper, /* shutdown */
  788. sapi_cgi_activate, /* activate */
  789. sapi_cgi_deactivate, /* deactivate */
  790. sapi_cgibin_ub_write, /* unbuffered write */
  791. sapi_cgibin_flush, /* flush */
  792. NULL, /* get uid */
  793. sapi_cgibin_getenv, /* getenv */
  794. php_error, /* error handler */
  795. NULL, /* header handler */
  796. sapi_cgi_send_headers, /* send headers handler */
  797. NULL, /* send header handler */
  798. sapi_cgi_read_post, /* read POST data */
  799. sapi_cgi_read_cookies, /* read Cookies */
  800. sapi_cgi_register_variables, /* register server variables */
  801. sapi_cgi_log_message, /* Log message */
  802. sapi_cgi_request_time, /* Get request time */
  803. NULL, /* Child terminate */
  804. STANDARD_SAPI_MODULE_PROPERTIES
  805. };
  806. /* }}} */
  807. /* {{{ arginfo ext/standard/dl.c */
  808. ZEND_BEGIN_ARG_INFO(arginfo_dl, 0)
  809. ZEND_ARG_INFO(0, extension_filename)
  810. ZEND_END_ARG_INFO()
  811. /* }}} */
  812. static const zend_function_entry additional_functions[] = {
  813. ZEND_FE(dl, arginfo_dl)
  814. {NULL, NULL, NULL}
  815. };
  816. /* {{{ php_cgi_usage
  817. */
  818. static void php_cgi_usage(char *argv0)
  819. {
  820. char *prog;
  821. prog = strrchr(argv0, '/');
  822. if (prog) {
  823. prog++;
  824. } else {
  825. prog = "php";
  826. }
  827. php_printf( "Usage: %s [-q] [-h] [-s] [-v] [-i] [-f <file>]\n"
  828. " %s <file> [args...]\n"
  829. " -a Run interactively\n"
  830. " -b <address:port>|<port> Bind Path for external FASTCGI Server mode\n"
  831. " -C Do not chdir to the script's directory\n"
  832. " -c <path>|<file> Look for php.ini file in this directory\n"
  833. " -n No php.ini file will be used\n"
  834. " -d foo[=bar] Define INI entry foo with value 'bar'\n"
  835. " -e Generate extended information for debugger/profiler\n"
  836. " -f <file> Parse <file>. Implies `-q'\n"
  837. " -h This help\n"
  838. " -i PHP information\n"
  839. " -l Syntax check only (lint)\n"
  840. " -m Show compiled in modules\n"
  841. " -q Quiet-mode. Suppress HTTP Header output.\n"
  842. " -s Display colour syntax highlighted source.\n"
  843. " -v Version number\n"
  844. " -w Display source with stripped comments and whitespace.\n"
  845. " -z <file> Load Zend extension <file>.\n"
  846. " -T <count> Measure execution time of script repeated <count> times.\n",
  847. prog, prog);
  848. }
  849. /* }}} */
  850. /* {{{ is_valid_path
  851. *
  852. * some server configurations allow '..' to slip through in the
  853. * translated path. We'll just refuse to handle such a path.
  854. */
  855. static int is_valid_path(const char *path)
  856. {
  857. const char *p;
  858. if (!path) {
  859. return 0;
  860. }
  861. p = strstr(path, "..");
  862. if (p) {
  863. if ((p == path || IS_SLASH(*(p-1))) &&
  864. (*(p+2) == 0 || IS_SLASH(*(p+2)))
  865. ) {
  866. return 0;
  867. }
  868. while (1) {
  869. p = strstr(p+1, "..");
  870. if (!p) {
  871. break;
  872. }
  873. if (IS_SLASH(*(p-1)) &&
  874. (*(p+2) == 0 || IS_SLASH(*(p+2)))
  875. ) {
  876. return 0;
  877. }
  878. }
  879. }
  880. return 1;
  881. }
  882. /* }}} */
  883. /* {{{ init_request_info
  884. initializes request_info structure
  885. specificly in this section we handle proper translations
  886. for:
  887. PATH_INFO
  888. derived from the portion of the URI path following
  889. the script name but preceding any query data
  890. may be empty
  891. PATH_TRANSLATED
  892. derived by taking any path-info component of the
  893. request URI and performing any virtual-to-physical
  894. translation appropriate to map it onto the server's
  895. document repository structure
  896. empty if PATH_INFO is empty
  897. The env var PATH_TRANSLATED **IS DIFFERENT** than the
  898. request_info.path_translated variable, the latter should
  899. match SCRIPT_FILENAME instead.
  900. SCRIPT_NAME
  901. set to a URL path that could identify the CGI script
  902. rather than the interpreter. PHP_SELF is set to this
  903. REQUEST_URI
  904. uri section following the domain:port part of a URI
  905. SCRIPT_FILENAME
  906. The virtual-to-physical translation of SCRIPT_NAME (as per
  907. PATH_TRANSLATED)
  908. These settings are documented at
  909. http://cgi-spec.golux.com/
  910. Based on the following URL request:
  911. http://localhost/info.php/test?a=b
  912. should produce, which btw is the same as if
  913. we were running under mod_cgi on apache (ie. not
  914. using ScriptAlias directives):
  915. PATH_INFO=/test
  916. PATH_TRANSLATED=/docroot/test
  917. SCRIPT_NAME=/info.php
  918. REQUEST_URI=/info.php/test?a=b
  919. SCRIPT_FILENAME=/docroot/info.php
  920. QUERY_STRING=a=b
  921. but what we get is (cgi/mod_fastcgi under apache):
  922. PATH_INFO=/info.php/test
  923. PATH_TRANSLATED=/docroot/info.php/test
  924. SCRIPT_NAME=/php/php-cgi (from the Action setting I suppose)
  925. REQUEST_URI=/info.php/test?a=b
  926. SCRIPT_FILENAME=/path/to/php/bin/php-cgi (Action setting translated)
  927. QUERY_STRING=a=b
  928. Comments in the code below refer to using the above URL in a request
  929. */
  930. static void init_request_info(TSRMLS_D)
  931. {
  932. char *env_script_filename = sapi_cgibin_getenv("SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1 TSRMLS_CC);
  933. char *env_path_translated = sapi_cgibin_getenv("PATH_TRANSLATED", sizeof("PATH_TRANSLATED")-1 TSRMLS_CC);
  934. char *script_path_translated = env_script_filename;
  935. /* some broken servers do not have script_filename or argv0
  936. * an example, IIS configured in some ways. then they do more
  937. * broken stuff and set path_translated to the cgi script location */
  938. if (!script_path_translated && env_path_translated) {
  939. script_path_translated = env_path_translated;
  940. }
  941. /* initialize the defaults */
  942. SG(request_info).path_translated = NULL;
  943. SG(request_info).request_method = NULL;
  944. SG(request_info).proto_num = 1000;
  945. SG(request_info).query_string = NULL;
  946. SG(request_info).request_uri = NULL;
  947. SG(request_info).content_type = NULL;
  948. SG(request_info).content_length = 0;
  949. SG(sapi_headers).http_response_code = 200;
  950. /* script_path_translated being set is a good indication that
  951. * we are running in a cgi environment, since it is always
  952. * null otherwise. otherwise, the filename
  953. * of the script will be retreived later via argc/argv */
  954. if (script_path_translated) {
  955. const char *auth;
  956. char *content_length = sapi_cgibin_getenv("CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1 TSRMLS_CC);
  957. char *content_type = sapi_cgibin_getenv("CONTENT_TYPE", sizeof("CONTENT_TYPE")-1 TSRMLS_CC);
  958. char *env_path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO")-1 TSRMLS_CC);
  959. char *env_script_name = sapi_cgibin_getenv("SCRIPT_NAME", sizeof("SCRIPT_NAME")-1 TSRMLS_CC);
  960. /* Hack for buggy IIS that sets incorrect PATH_INFO */
  961. char *env_server_software = sapi_cgibin_getenv("SERVER_SOFTWARE", sizeof("SERVER_SOFTWARE")-1 TSRMLS_CC);
  962. if (env_server_software &&
  963. env_script_name &&
  964. env_path_info &&
  965. strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS")-1) == 0 &&
  966. strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0
  967. ) {
  968. env_path_info = _sapi_cgibin_putenv("ORIG_PATH_INFO", env_path_info TSRMLS_CC);
  969. env_path_info += strlen(env_script_name);
  970. if (*env_path_info == 0) {
  971. env_path_info = NULL;
  972. }
  973. env_path_info = _sapi_cgibin_putenv("PATH_INFO", env_path_info TSRMLS_CC);
  974. }
  975. if (CGIG(fix_pathinfo)) {
  976. struct stat st;
  977. char *real_path = NULL;
  978. char *env_redirect_url = sapi_cgibin_getenv("REDIRECT_URL", sizeof("REDIRECT_URL")-1 TSRMLS_CC);
  979. char *env_document_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT")-1 TSRMLS_CC);
  980. char *orig_path_translated = env_path_translated;
  981. char *orig_path_info = env_path_info;
  982. char *orig_script_name = env_script_name;
  983. char *orig_script_filename = env_script_filename;
  984. int script_path_translated_len;
  985. if (!env_document_root && PG(doc_root)) {
  986. env_document_root = _sapi_cgibin_putenv("DOCUMENT_ROOT", PG(doc_root) TSRMLS_CC);
  987. /* fix docroot */
  988. TRANSLATE_SLASHES(env_document_root);
  989. }
  990. if (env_path_translated != NULL && env_redirect_url != NULL &&
  991. env_path_translated != script_path_translated &&
  992. strcmp(env_path_translated, script_path_translated) != 0) {
  993. /*
  994. * pretty much apache specific. If we have a redirect_url
  995. * then our script_filename and script_name point to the
  996. * php executable
  997. */
  998. script_path_translated = env_path_translated;
  999. /* we correct SCRIPT_NAME now in case we don't have PATH_INFO */
  1000. env_script_name = env_redirect_url;
  1001. }
  1002. #ifdef __riscos__
  1003. /* Convert path to unix format*/
  1004. __riscosify_control |= __RISCOSIFY_DONT_CHECK_DIR;
  1005. script_path_translated = __unixify(script_path_translated, 0, NULL, 1, 0);
  1006. #endif
  1007. /*
  1008. * if the file doesn't exist, try to extract PATH_INFO out
  1009. * of it by stat'ing back through the '/'
  1010. * this fixes url's like /info.php/test
  1011. */
  1012. if (script_path_translated &&
  1013. (script_path_translated_len = strlen(script_path_translated)) > 0 &&
  1014. (script_path_translated[script_path_translated_len-1] == '/' ||
  1015. #ifdef PHP_WIN32
  1016. script_path_translated[script_path_translated_len-1] == '\\' ||
  1017. #endif
  1018. (real_path = tsrm_realpath(script_path_translated, NULL TSRMLS_CC)) == NULL)
  1019. ) {
  1020. char *pt = estrndup(script_path_translated, script_path_translated_len);
  1021. int len = script_path_translated_len;
  1022. char *ptr;
  1023. while ((ptr = strrchr(pt, '/')) || (ptr = strrchr(pt, '\\'))) {
  1024. *ptr = 0;
  1025. if (stat(pt, &st) == 0 && S_ISREG(st.st_mode)) {
  1026. /*
  1027. * okay, we found the base script!
  1028. * work out how many chars we had to strip off;
  1029. * then we can modify PATH_INFO
  1030. * accordingly
  1031. *
  1032. * we now have the makings of
  1033. * PATH_INFO=/test
  1034. * SCRIPT_FILENAME=/docroot/info.php
  1035. *
  1036. * we now need to figure out what docroot is.
  1037. * if DOCUMENT_ROOT is set, this is easy, otherwise,
  1038. * we have to play the game of hide and seek to figure
  1039. * out what SCRIPT_NAME should be
  1040. */
  1041. int slen = len - strlen(pt);
  1042. int pilen = env_path_info ? strlen(env_path_info) : 0;
  1043. char *path_info = env_path_info ? env_path_info + pilen - slen : NULL;
  1044. if (orig_path_info != path_info) {
  1045. if (orig_path_info) {
  1046. char old;
  1047. _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC);
  1048. old = path_info[0];
  1049. path_info[0] = 0;
  1050. if (!orig_script_name ||
  1051. strcmp(orig_script_name, env_path_info) != 0) {
  1052. if (orig_script_name) {
  1053. _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
  1054. }
  1055. SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_path_info TSRMLS_CC);
  1056. } else {
  1057. SG(request_info).request_uri = orig_script_name;
  1058. }
  1059. path_info[0] = old;
  1060. }
  1061. env_path_info = _sapi_cgibin_putenv("PATH_INFO", path_info TSRMLS_CC);
  1062. }
  1063. if (!orig_script_filename ||
  1064. strcmp(orig_script_filename, pt) != 0) {
  1065. if (orig_script_filename) {
  1066. _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
  1067. }
  1068. script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", pt TSRMLS_CC);
  1069. }
  1070. TRANSLATE_SLASHES(pt);
  1071. /* figure out docroot
  1072. * SCRIPT_FILENAME minus SCRIPT_NAME
  1073. */
  1074. if (env_document_root) {
  1075. int l = strlen(env_document_root);
  1076. int path_translated_len = 0;
  1077. char *path_translated = NULL;
  1078. if (l && env_document_root[l - 1] == '/') {
  1079. --l;
  1080. }
  1081. /* we have docroot, so we should have:
  1082. * DOCUMENT_ROOT=/docroot
  1083. * SCRIPT_FILENAME=/docroot/info.php
  1084. */
  1085. /* PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO */
  1086. path_translated_len = l + (env_path_info ? strlen(env_path_info) : 0);
  1087. path_translated = (char *) emalloc(path_translated_len + 1);
  1088. memcpy(path_translated, env_document_root, l);
  1089. if (env_path_info) {
  1090. memcpy(path_translated + l, env_path_info, (path_translated_len - l));
  1091. }
  1092. path_translated[path_translated_len] = '\0';
  1093. if (orig_path_translated) {
  1094. _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
  1095. }
  1096. env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC);
  1097. efree(path_translated);
  1098. } else if ( env_script_name &&
  1099. strstr(pt, env_script_name)
  1100. ) {
  1101. /* PATH_TRANSLATED = PATH_TRANSLATED - SCRIPT_NAME + PATH_INFO */
  1102. int ptlen = strlen(pt) - strlen(env_script_name);
  1103. int path_translated_len = ptlen + (env_path_info ? strlen(env_path_info) : 0);
  1104. char *path_translated = NULL;
  1105. path_translated = (char *) emalloc(path_translated_len + 1);
  1106. memcpy(path_translated, pt, ptlen);
  1107. if (env_path_info) {
  1108. memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen);
  1109. }
  1110. path_translated[path_translated_len] = '\0';
  1111. if (orig_path_translated) {
  1112. _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
  1113. }
  1114. env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC);
  1115. efree(path_translated);
  1116. }
  1117. break;
  1118. }
  1119. }
  1120. if (!ptr) {
  1121. /*
  1122. * if we stripped out all the '/' and still didn't find
  1123. * a valid path... we will fail, badly. of course we would
  1124. * have failed anyway... we output 'no input file' now.
  1125. */
  1126. if (orig_script_filename) {
  1127. _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
  1128. }
  1129. script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", NULL TSRMLS_CC);
  1130. SG(sapi_headers).http_response_code = 404;
  1131. }
  1132. if (!SG(request_info).request_uri) {
  1133. if (!orig_script_name ||
  1134. strcmp(orig_script_name, env_script_name) != 0) {
  1135. if (orig_script_name) {
  1136. _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
  1137. }
  1138. SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC);
  1139. } else {
  1140. SG(request_info).request_uri = orig_script_name;
  1141. }
  1142. }
  1143. if (pt) {
  1144. efree(pt);
  1145. }
  1146. } else {
  1147. /* make sure path_info/translated are empty */
  1148. if (!orig_script_filename ||
  1149. (script_path_translated != orig_script_filename &&
  1150. strcmp(script_path_translated, orig_script_filename) != 0)) {
  1151. if (orig_script_filename) {
  1152. _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
  1153. }
  1154. script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", script_path_translated TSRMLS_CC);
  1155. }
  1156. if (env_redirect_url) {
  1157. if (orig_path_info) {
  1158. _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC);
  1159. _sapi_cgibin_putenv("PATH_INFO", NULL TSRMLS_CC);
  1160. }
  1161. if (orig_path_translated) {
  1162. _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
  1163. _sapi_cgibin_putenv("PATH_TRANSLATED", NULL TSRMLS_CC);
  1164. }
  1165. }
  1166. if (env_script_name != orig_script_name) {
  1167. if (orig_script_name) {
  1168. _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
  1169. }
  1170. SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC);
  1171. } else {
  1172. SG(request_info).request_uri = env_script_name;
  1173. }
  1174. free(real_path);
  1175. }
  1176. } else {
  1177. /* pre 4.3 behaviour, shouldn't be used but provides BC */
  1178. if (env_path_info) {
  1179. SG(request_info).request_uri = env_path_info;
  1180. } else {
  1181. SG(request_info).request_uri = env_script_name;
  1182. }
  1183. if (!CGIG(discard_path) && env_path_translated) {
  1184. script_path_translated = env_path_translated;
  1185. }
  1186. }
  1187. if (is_valid_path(script_path_translated)) {
  1188. SG(request_info).path_translated = estrdup(script_path_translated);
  1189. }
  1190. SG(request_info).request_method = sapi_cgibin_getenv("REQUEST_METHOD", sizeof("REQUEST_METHOD")-1 TSRMLS_CC);
  1191. /* FIXME - Work out proto_num here */
  1192. SG(request_info).query_string = sapi_cgibin_getenv("QUERY_STRING", sizeof("QUERY_STRING")-1 TSRMLS_CC);
  1193. SG(request_info).content_type = (content_type ? content_type : "" );
  1194. SG(request_info).content_length = (content_length ? atoi(content_length) : 0);
  1195. /* The CGI RFC allows servers to pass on unvalidated Authorization data */
  1196. auth = sapi_cgibin_getenv("HTTP_AUTHORIZATION", sizeof("HTTP_AUTHORIZATION")-1 TSRMLS_CC);
  1197. php_handle_auth_data(auth TSRMLS_CC);
  1198. }
  1199. }
  1200. /* }}} */
  1201. #ifndef PHP_WIN32
  1202. /**
  1203. * Clean up child processes upon exit
  1204. */
  1205. void fastcgi_cleanup(int signal)
  1206. {
  1207. #ifdef DEBUG_FASTCGI
  1208. fprintf(stderr, "FastCGI shutdown, pid %d\n", getpid());
  1209. #endif
  1210. sigaction(SIGTERM, &old_term, 0);
  1211. /* Kill all the processes in our process group */
  1212. kill(-pgroup, SIGTERM);
  1213. if (parent && parent_waiting) {
  1214. exit_signal = 1;
  1215. } else {
  1216. exit(0);
  1217. }
  1218. }
  1219. #endif
  1220. PHP_INI_BEGIN()
  1221. STD_PHP_INI_ENTRY("cgi.rfc2616_headers", "0", PHP_INI_ALL, OnUpdateBool, rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
  1222. STD_PHP_INI_ENTRY("cgi.nph", "0", PHP_INI_ALL, OnUpdateBool, nph, php_cgi_globals_struct, php_cgi_globals)
  1223. STD_PHP_INI_ENTRY("cgi.check_shebang_line", "1", PHP_INI_SYSTEM, OnUpdateBool, check_shebang_line, php_cgi_globals_struct, php_cgi_globals)
  1224. STD_PHP_INI_ENTRY("cgi.force_redirect", "1", PHP_INI_SYSTEM, OnUpdateBool, force_redirect, php_cgi_globals_struct, php_cgi_globals)
  1225. STD_PHP_INI_ENTRY("cgi.redirect_status_env", NULL, PHP_INI_SYSTEM, OnUpdateString, redirect_status_env, php_cgi_globals_struct, php_cgi_globals)
  1226. STD_PHP_INI_ENTRY("cgi.fix_pathinfo", "1", PHP_INI_SYSTEM, OnUpdateBool, fix_pathinfo, php_cgi_globals_struct, php_cgi_globals)
  1227. STD_PHP_INI_ENTRY("cgi.discard_path", "0", PHP_INI_SYSTEM, OnUpdateBool, discard_path, php_cgi_globals_struct, php_cgi_globals)
  1228. STD_PHP_INI_ENTRY("fastcgi.logging", "1", PHP_INI_SYSTEM, OnUpdateBool, fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
  1229. #ifdef PHP_WIN32
  1230. STD_PHP_INI_ENTRY("fastcgi.impersonate", "0", PHP_INI_SYSTEM, OnUpdateBool, impersonate, php_cgi_globals_struct, php_cgi_globals)
  1231. #endif
  1232. PHP_INI_END()
  1233. /* {{{ php_cgi_globals_ctor
  1234. */
  1235. static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals TSRMLS_DC)
  1236. {
  1237. php_cgi_globals->rfc2616_headers = 0;
  1238. php_cgi_globals->nph = 0;
  1239. php_cgi_globals->check_shebang_line = 1;
  1240. php_cgi_globals->force_redirect = 1;
  1241. php_cgi_globals->redirect_status_env = NULL;
  1242. php_cgi_globals->fix_pathinfo = 1;
  1243. php_cgi_globals->discard_path = 0;
  1244. php_cgi_globals->fcgi_logging = 1;
  1245. #ifdef PHP_WIN32
  1246. php_cgi_globals->impersonate = 0;
  1247. #endif
  1248. zend_hash_init(&php_cgi_globals->user_config_cache, 0, NULL, (dtor_func_t) user_config_cache_entry_dtor, 1);
  1249. }
  1250. /* }}} */
  1251. /* {{{ PHP_MINIT_FUNCTION
  1252. */
  1253. static PHP_MINIT_FUNCTION(cgi)
  1254. {
  1255. #ifdef ZTS
  1256. ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
  1257. #else
  1258. php_cgi_globals_ctor(&php_cgi_globals TSRMLS_CC);
  1259. #endif
  1260. REGISTER_INI_ENTRIES();
  1261. return SUCCESS;
  1262. }
  1263. /* }}} */
  1264. /* {{{ PHP_MSHUTDOWN_FUNCTION
  1265. */
  1266. static PHP_MSHUTDOWN_FUNCTION(cgi)
  1267. {
  1268. zend_hash_destroy(&CGIG(user_config_cache));
  1269. UNREGISTER_INI_ENTRIES();
  1270. return SUCCESS;
  1271. }
  1272. /* }}} */
  1273. /* {{{ PHP_MINFO_FUNCTION
  1274. */
  1275. static PHP_MINFO_FUNCTION(cgi)
  1276. {
  1277. DISPLAY_INI_ENTRIES();
  1278. }
  1279. /* }}} */
  1280. static zend_module_entry cgi_module_entry = {
  1281. STANDARD_MODULE_HEADER,
  1282. "cgi-fcgi",
  1283. NULL,
  1284. PHP_MINIT(cgi),
  1285. PHP_MSHUTDOWN(cgi),
  1286. NULL,
  1287. NULL,
  1288. PHP_MINFO(cgi),
  1289. NO_VERSION_YET,
  1290. STANDARD_MODULE_PROPERTIES
  1291. };
  1292. /* {{{ main
  1293. */
  1294. int main(int argc, char *argv[])
  1295. {
  1296. int free_query_string = 0;
  1297. int exit_status = SUCCESS;
  1298. int cgi = 0, c, i, len;
  1299. zend_file_handle file_handle;
  1300. char *s;
  1301. /* temporary locals */
  1302. int behavior = PHP_MODE_STANDARD;
  1303. int no_headers = 0;
  1304. int orig_optind = php_optind;
  1305. char *orig_optarg = php_optarg;
  1306. char *script_file = NULL;
  1307. int ini_entries_len = 0;
  1308. /* end of temporary locals */
  1309. #ifdef ZTS
  1310. void ***tsrm_ls;
  1311. #endif
  1312. int max_requests = 500;
  1313. int requests = 0;
  1314. int fastcgi = fcgi_is_fastcgi();
  1315. char *bindpath = NULL;
  1316. int fcgi_fd = 0;
  1317. fcgi_request request;
  1318. int repeats = 1;
  1319. int benchmark = 0;
  1320. #if HAVE_GETTIMEOFDAY
  1321. struct timeval start, end;
  1322. #else
  1323. time_t start, end;
  1324. #endif
  1325. #ifndef PHP_WIN32
  1326. int status = 0;
  1327. #endif
  1328. #if 0 && defined(PHP_DEBUG)
  1329. /* IIS is always making things more difficult. This allows
  1330. * us to stop PHP and attach a debugger before much gets started */
  1331. {
  1332. char szMessage [256];
  1333. wsprintf (szMessage, "Please attach a debugger to the process 0x%X [%d] (%s) and click OK", GetCurrentProcessId(), GetCurrentProcessId(), argv[0]);
  1334. MessageBox(NULL, szMessage, "CGI Debug Time!", MB_OK|MB_SERVICE_NOTIFICATION);
  1335. }
  1336. #endif
  1337. #ifdef HAVE_SIGNAL_H
  1338. #if defined(SIGPIPE) && defined(SIG_IGN)
  1339. signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
  1340. that sockets created via fsockopen()
  1341. don't kill PHP if the remote site
  1342. closes it. in apache|apxs mode apache
  1343. does that for us! thies@thieso.net
  1344. 20000419 */
  1345. #endif
  1346. #endif
  1347. #ifdef ZTS
  1348. tsrm_startup(1, 1, 0, NULL);
  1349. tsrm_ls = ts_resource(0);
  1350. #endif
  1351. sapi_startup(&cgi_sapi_module);
  1352. cgi_sapi_module.php_ini_path_override = NULL;
  1353. #ifdef PHP_WIN32
  1354. _fmode = _O_BINARY; /* sets default for file streams to binary */
  1355. setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
  1356. setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
  1357. setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
  1358. #endif
  1359. if (!fastcgi) {
  1360. /* Make sure we detect we are a cgi - a bit redundancy here,
  1361. * but the default case is that we have to check only the first one. */
  1362. if (getenv("SERVER_SOFTWARE") ||
  1363. getenv("SERVER_NAME") ||
  1364. getenv("GATEWAY_INTERFACE") ||
  1365. getenv("REQUEST_METHOD")
  1366. ) {
  1367. cgi = 1;
  1368. }
  1369. }
  1370. while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
  1371. switch (c) {
  1372. case 'c':
  1373. if (cgi_sapi_module.php_ini_path_override) {
  1374. free(cgi_sapi_module.php_ini_path_override);
  1375. }
  1376. cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
  1377. break;
  1378. case 'n':
  1379. cgi_sapi_module.php_ini_ignore = 1;
  1380. break;
  1381. case 'd': {
  1382. /* define ini entries on command line */
  1383. int len = strlen(php_optarg);
  1384. char *val;
  1385. if ((val = strchr(php_optarg, '='))) {
  1386. val++;
  1387. if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
  1388. cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
  1389. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
  1390. ini_entries_len += (val - php_optarg);
  1391. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"", 1);
  1392. ini_entries_len++;
  1393. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, val, len - (val - php_optarg));
  1394. ini_entries_len += len - (val - php_optarg);
  1395. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
  1396. ini_entries_len += sizeof("\n\0\"") - 2;
  1397. } else {
  1398. cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\n\0"));
  1399. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
  1400. memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
  1401. ini_entries_len += len + sizeof("\n\0") - 2;
  1402. }
  1403. } else {
  1404. cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
  1405. memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
  1406. memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
  1407. ini_entries_len += len + sizeof("=1\n\0") - 2;
  1408. }
  1409. break;
  1410. }
  1411. /* if we're started on command line, check to see if
  1412. * we are being started as an 'external' fastcgi
  1413. * server by accepting a bindpath parameter. */
  1414. case 'b':
  1415. if (!fastcgi) {
  1416. bindpath = strdup(php_optarg);
  1417. }
  1418. break;
  1419. case 's': /* generate highlighted HTML from source */
  1420. behavior = PHP_MODE_HIGHLIGHT;
  1421. break;
  1422. }
  1423. }
  1424. php_optind = orig_optind;
  1425. php_optarg = orig_optarg;
  1426. #ifdef ZTS
  1427. SG(request_info).path_translated = NULL;
  1428. #endif
  1429. cgi_sapi_module.executable_location = argv[0];
  1430. if (!cgi && !fastcgi && !bindpath) {
  1431. cgi_sapi_module.additional_functions = additional_functions;
  1432. }
  1433. /* startup after we get the above ini override se we get things right */
  1434. if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
  1435. #ifdef ZTS
  1436. tsrm_shutdown();
  1437. #endif
  1438. return FAILURE;
  1439. }
  1440. /* check force_cgi after startup, so we have proper output */
  1441. if (cgi && CGIG(force_redirect)) {
  1442. /* Apache will generate REDIRECT_STATUS,
  1443. * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.
  1444. * redirect.so and installation instructions available from
  1445. * http://www.koehntopp.de/php.
  1446. * -- kk@netuse.de
  1447. */
  1448. if (!getenv("REDIRECT_STATUS") &&
  1449. !getenv ("HTTP_REDIRECT_STATUS") &&
  1450. /* this is to allow a different env var to be configured
  1451. * in case some server does something different than above */
  1452. (!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env)))
  1453. ) {
  1454. zend_try {
  1455. SG(sapi_headers).http_response_code = 400;
  1456. PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\
  1457. <p>This PHP CGI binary was compiled with force-cgi-redirect enabled. This\n\
  1458. means that a page will only be served up if the REDIRECT_STATUS CGI variable is\n\
  1459. set, e.g. via an Apache Action directive.</p>\n\
  1460. <p>For more information as to <i>why</i> this behaviour exists, see the <a href=\"http://php.net/security.cgi-bin\">\
  1461. manual page for CGI security</a>.</p>\n\
  1462. <p>For more information about changing this behaviour or re-enabling this webserver,\n\
  1463. consult the installation file that came with this distribution, or visit \n\
  1464. <a href=\"http://php.net/install.windows\">the manual page</a>.</p>\n");
  1465. } zend_catch {
  1466. } zend_end_try();
  1467. #if defined(ZTS) && !defined(PHP_DEBUG)
  1468. /* XXX we're crashing here in msvc6 debug builds at
  1469. * php_message_handler_for_zend:839 because
  1470. * SG(request_info).path_translated is an invalid pointer.
  1471. * It still happens even though I set it to null, so something
  1472. * weird is going on.
  1473. */
  1474. tsrm_shutdown();
  1475. #endif
  1476. return FAILURE;
  1477. }
  1478. }
  1479. if (bindpath) {
  1480. fcgi_fd = fcgi_listen(bindpath, 128);
  1481. if (fcgi_fd < 0) {
  1482. fprintf(stderr, "Couldn't create FastCGI listen socket on port %s\n", bindpath);
  1483. #ifdef ZTS
  1484. tsrm_shutdown();
  1485. #endif
  1486. return FAILURE;
  1487. }
  1488. fastcgi = fcgi_is_fastcgi();
  1489. }
  1490. if (fastcgi) {
  1491. /* How many times to run PHP scripts before dying */
  1492. if (getenv("PHP_FCGI_MAX_REQUESTS")) {
  1493. max_requests = atoi(getenv("PHP_FCGI_MAX_REQUESTS"));
  1494. if (max_requests < 0) {
  1495. fprintf(stderr, "PHP_FCGI_MAX_REQUESTS is not valid\n");
  1496. return FAILURE;
  1497. }
  1498. }
  1499. /* make php call us to get _ENV vars */
  1500. php_php_import_environment_variables = php_import_environment_variables;
  1501. php_import_environment_variables = cgi_php_import_environment_variables;
  1502. /* library is already initialized, now init our request */
  1503. fcgi_init_request(&request, fcgi_fd);
  1504. #ifndef PHP_WIN32
  1505. /* Pre-fork, if required */
  1506. if (getenv("PHP_FCGI_CHILDREN")) {
  1507. char * children_str = getenv("PHP_FCGI_CHILDREN");
  1508. children = atoi(children_str);
  1509. if (children < 0) {
  1510. fprintf(stderr, "PHP_FCGI_CHILDREN is not valid\n");
  1511. return FAILURE;
  1512. }
  1513. fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, children_str, strlen(children_str));
  1514. /* This is the number of concurrent requests, equals FCGI_MAX_CONNS */
  1515. fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, children_str, strlen(children_str));
  1516. } else {
  1517. fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, "1", sizeof("1")-1);
  1518. fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, "1", sizeof("1")-1);
  1519. }
  1520. if (children) {
  1521. int running = 0;
  1522. pid_t pid;
  1523. /* Create a process group for ourself & children */
  1524. setsid();
  1525. pgroup = getpgrp();
  1526. #ifdef DEBUG_FASTCGI
  1527. fprintf(stderr, "Process group %d\n", pgroup);
  1528. #endif
  1529. /* Set up handler to kill children upon exit */
  1530. act.sa_flags = 0;
  1531. act.sa_handler = fastcgi_cleanup;
  1532. if (sigaction(SIGTERM, &act, &old_term) ||
  1533. sigaction(SIGINT, &act, &old_int) ||
  1534. sigaction(SIGQUIT, &act, &old_quit)
  1535. ) {
  1536. perror("Can't set signals");
  1537. exit(1);
  1538. }
  1539. if (fcgi_in_shutdown()) {
  1540. goto parent_out;
  1541. }
  1542. while (parent) {
  1543. do {
  1544. #ifdef DEBUG_FASTCGI
  1545. fprintf(stderr, "Forking, %d running\n", running);
  1546. #endif
  1547. pid = fork();
  1548. switch (pid) {
  1549. case 0:
  1550. /* One of the children.
  1551. * Make sure we don't go round the
  1552. * fork loop any more
  1553. */
  1554. parent = 0;
  1555. /* don't catch our signals */
  1556. sigaction(SIGTERM, &old_term, 0);
  1557. sigaction(SIGQUIT, &old_quit, 0);
  1558. sigaction(SIGINT, &old_int, 0);
  1559. break;
  1560. case -1:
  1561. perror("php (pre-forking)");
  1562. exit(1);
  1563. break;
  1564. default:
  1565. /* Fine */
  1566. running++;
  1567. break;
  1568. }
  1569. } while (parent && (running < children));
  1570. if (parent) {
  1571. #ifdef DEBUG_FASTCGI
  1572. fprintf(stderr, "Wait for kids, pid %d\n", getpid());
  1573. #endif
  1574. parent_waiting = 1;
  1575. while (1) {
  1576. if (wait(&status) >= 0) {
  1577. running--;
  1578. break;
  1579. } else if (exit_signal) {
  1580. break;
  1581. }
  1582. }
  1583. if (exit_signal) {
  1584. #if 0
  1585. while (running > 0) {
  1586. while (wait(&status) < 0) {
  1587. }
  1588. running--;
  1589. }
  1590. #endif
  1591. goto parent_out;
  1592. }
  1593. }
  1594. }
  1595. } else {
  1596. parent = 0;
  1597. }
  1598. #endif /* WIN32 */
  1599. }
  1600. zend_first_try {
  1601. while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 1, 2)) != -1) {
  1602. switch (c) {
  1603. case 'T':
  1604. benchmark = 1;
  1605. repeats = atoi(php_optarg);
  1606. #ifdef HAVE_GETTIMEOFDAY
  1607. gettimeofday(&start, NULL);
  1608. #else
  1609. time(&start);
  1610. #endif
  1611. break;
  1612. case 'h':
  1613. case '?':
  1614. fcgi_shutdown();
  1615. no_headers = 1;
  1616. php_output_startup();
  1617. php_output_activate(TSRMLS_C);
  1618. SG(headers_sent) = 1;
  1619. php_cgi_usage(argv[0]);
  1620. php_end_ob_buffers(1 TSRMLS_CC);
  1621. exit_status = 0;
  1622. goto out;
  1623. }
  1624. }
  1625. php_optind = orig_optind;
  1626. php_optarg = orig_optarg;
  1627. /* start of FAST CGI loop */
  1628. /* Initialise FastCGI request structure */
  1629. #ifdef PHP_WIN32
  1630. /* attempt to set security impersonation for fastcgi
  1631. * will only happen on NT based OS, others will ignore it. */
  1632. if (fastcgi && CGIG(impersonate)) {
  1633. fcgi_impersonate();
  1634. }
  1635. #endif
  1636. while (!fastcgi || fcgi_accept_request(&request) >= 0) {
  1637. SG(server_context) = (void *) &request;
  1638. init_request_info(TSRMLS_C);
  1639. CG(interactive) = 0;
  1640. if (!cgi && !fastcgi) {
  1641. while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
  1642. switch (c) {
  1643. case 'a': /* interactive mode */
  1644. printf("Interactive mode enabled\n\n");
  1645. CG(interactive) = 1;
  1646. break;
  1647. case 'C': /* don't chdir to the script directory */
  1648. SG(options) |= SAPI_OPTION_NO_CHDIR;
  1649. break;
  1650. case 'e': /* enable extended info output */
  1651. CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
  1652. break;
  1653. case 'f': /* parse file */
  1654. if (script_file) {
  1655. efree(script_file);
  1656. }
  1657. script_file = estrdup(php_optarg);
  1658. no_headers = 1;
  1659. break;
  1660. case 'i': /* php info & quit */
  1661. if (script_file) {
  1662. efree(script_file);
  1663. }
  1664. if (php_request_startup(TSRMLS_C) == FAILURE) {
  1665. SG(server_context) = NULL;
  1666. php_module_shutdown(TSRMLS_C);
  1667. return FAILURE;
  1668. }
  1669. if (no_headers) {
  1670. SG(headers_sent) = 1;
  1671. SG(request_info).no_headers = 1;
  1672. }
  1673. php_print_info(0xFFFFFFFF TSRMLS_CC);
  1674. php_request_shutdown((void *) 0);
  1675. fcgi_shutdown();
  1676. exit_status = 0;
  1677. goto out;
  1678. case 'l': /* syntax check mode */
  1679. no_headers = 1;
  1680. behavior = PHP_MODE_LINT;
  1681. break;
  1682. case 'm': /* list compiled in modules */
  1683. if (script_file) {
  1684. efree(script_file);
  1685. }
  1686. php_output_startup();
  1687. php_output_activate(TSRMLS_C);
  1688. SG(headers_sent) = 1;
  1689. php_printf("[PHP Modules]\n");
  1690. print_modules(TSRMLS_C);
  1691. php_printf("\n[Zend Modules]\n");
  1692. print_extensions(TSRMLS_C);
  1693. php_printf("\n");
  1694. php_end_ob_buffers(1 TSRMLS_CC);
  1695. fcgi_shutdown();
  1696. exit_status = 0;
  1697. goto out;
  1698. #if 0 /* not yet operational, see also below ... */
  1699. case '': /* generate indented source mode*/
  1700. behavior=PHP_MODE_INDENT;
  1701. break;
  1702. #endif
  1703. case 'q': /* do not generate HTTP headers */
  1704. no_headers = 1;
  1705. break;
  1706. case 'v': /* show php version & quit */
  1707. if (script_file) {
  1708. efree(script_file);
  1709. }
  1710. no_headers = 1;
  1711. if (php_request_startup(TSRMLS_C) == FAILURE) {
  1712. SG(server_context) = NULL;
  1713. php_module_shutdown(TSRMLS_C);
  1714. return FAILURE;
  1715. }
  1716. if (no_headers) {
  1717. SG(headers_sent) = 1;
  1718. SG(request_info).no_headers = 1;
  1719. }
  1720. #if ZEND_DEBUG
  1721. php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2011 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
  1722. #else
  1723. php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2011 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
  1724. #endif
  1725. php_request_shutdown((void *) 0);
  1726. fcgi_shutdown();
  1727. exit_status = 0;
  1728. goto out;
  1729. case 'w':
  1730. behavior = PHP_MODE_STRIP;
  1731. break;
  1732. case 'z': /* load extension file */
  1733. zend_load_extension(php_optarg);
  1734. break;
  1735. default:
  1736. break;
  1737. }
  1738. }
  1739. if (script_file) {
  1740. /* override path_translated if -f on command line */
  1741. STR_FREE(SG(request_info).path_translated);
  1742. SG(request_info).path_translated = script_file;
  1743. /* before registering argv to module exchange the *new* argv[0] */
  1744. /* we can achieve this without allocating more memory */
  1745. SG(request_info).argc = argc - (php_optind - 1);
  1746. SG(request_info).argv = &argv[php_optind - 1];
  1747. SG(request_info).argv[0] = script_file;
  1748. } else if (argc > php_optind) {
  1749. /* file is on command line, but not in -f opt */
  1750. STR_FREE(SG(request_info).path_translated);
  1751. SG(request_info).path_translated = estrdup(argv[php_optind]);
  1752. /* arguments after the file are considered script args */
  1753. SG(request_info).argc = argc - php_optind;
  1754. SG(request_info).argv = &argv[php_optind];
  1755. }
  1756. if (no_headers) {
  1757. SG(headers_sent) = 1;
  1758. SG(request_info).no_headers = 1;
  1759. }
  1760. /* all remaining arguments are part of the query string
  1761. * this section of code concatenates all remaining arguments
  1762. * into a single string, seperating args with a &
  1763. * this allows command lines like:
  1764. *
  1765. * test.php v1=test v2=hello+world!
  1766. * test.php "v1=test&v2=hello world!"
  1767. * test.php v1=test "v2=hello world!"
  1768. */
  1769. if (!SG(request_info).query_string && argc > php_optind) {
  1770. int slen = strlen(PG(arg_separator).input);
  1771. len = 0;
  1772. for (i = php_optind; i < argc; i++) {
  1773. if (i < (argc - 1)) {
  1774. len += strlen(argv[i]) + slen;
  1775. } else {
  1776. len += strlen(argv[i]);
  1777. }
  1778. }
  1779. len += 2;
  1780. s = malloc(len);
  1781. *s = '\0'; /* we are pretending it came from the environment */
  1782. for (i = php_optind; i < argc; i++) {
  1783. strlcat(s, argv[i], len);
  1784. if (i < (argc - 1)) {
  1785. strlcat(s, PG(arg_separator).input, len);
  1786. }
  1787. }
  1788. SG(request_info).query_string = s;
  1789. free_query_string = 1;
  1790. }
  1791. } /* end !cgi && !fastcgi */
  1792. /*
  1793. we never take stdin if we're (f)cgi, always
  1794. rely on the web server giving us the info
  1795. we need in the environment.
  1796. */
  1797. if (SG(request_info).path_translated || cgi || fastcgi) {
  1798. file_handle.type = ZEND_HANDLE_FILENAME;
  1799. file_handle.filename = SG(request_info).path_translated;
  1800. file_handle.handle.fp = NULL;
  1801. } else {
  1802. file_handle.filename = "-";
  1803. file_handle.type = ZEND_HANDLE_FP;
  1804. file_handle.handle.fp = stdin;
  1805. }
  1806. file_handle.opened_path = NULL;
  1807. file_handle.free_filename = 0;
  1808. /* request startup only after we've done all we can to
  1809. * get path_translated */
  1810. if (php_request_startup(TSRMLS_C) == FAILURE) {
  1811. if (fastcgi) {
  1812. fcgi_finish_request(&request, 1);
  1813. }
  1814. SG(server_context) = NULL;
  1815. php_module_shutdown(TSRMLS_C);
  1816. return FAILURE;
  1817. }
  1818. if (no_headers) {
  1819. SG(headers_sent) = 1;
  1820. SG(request_info).no_headers = 1;
  1821. }
  1822. /*
  1823. at this point path_translated will be set if:
  1824. 1. we are running from shell and got filename was there
  1825. 2. we are running as cgi or fastcgi
  1826. */
  1827. if (cgi || fastcgi || SG(request_info).path_translated) {
  1828. if (php_fopen_primary_script(&file_handle TSRMLS_CC) == FAILURE) {
  1829. zend_try {
  1830. if (errno == EACCES) {
  1831. SG(sapi_headers).http_response_code = 403;
  1832. PUTS("Access denied.\n");
  1833. } else {
  1834. SG(sapi_headers).http_response_code = 404;
  1835. PUTS("No input file specified.\n");
  1836. }
  1837. } zend_catch {
  1838. } zend_end_try();
  1839. /* we want to serve more requests if this is fastcgi
  1840. * so cleanup and continue, request shutdown is
  1841. * handled later */
  1842. if (fastcgi) {
  1843. goto fastcgi_request_done;
  1844. }
  1845. STR_FREE(SG(request_info).path_translated);
  1846. if (free_query_string && SG(request_info).query_string) {
  1847. free(SG(request_info).query_string);
  1848. SG(request_info).query_string = NULL;
  1849. }
  1850. php_request_shutdown((void *) 0);
  1851. SG(server_context) = NULL;
  1852. php_module_shutdown(TSRMLS_C);
  1853. sapi_shutdown();
  1854. #ifdef ZTS
  1855. tsrm_shutdown();
  1856. #endif
  1857. return FAILURE;
  1858. }
  1859. }
  1860. if (CGIG(check_shebang_line) && file_handle.handle.fp && (file_handle.handle.fp != stdin)) {
  1861. /* #!php support */
  1862. c = fgetc(file_handle.handle.fp);
  1863. if (c == '#') {
  1864. while (c != '\n' && c != '\r' && c != EOF) {
  1865. c = fgetc(file_handle.handle.fp); /* skip to end of line */
  1866. }
  1867. /* handle situations where line is terminated by \r\n */
  1868. if (c == '\r') {
  1869. if (fgetc(file_handle.handle.fp) != '\n') {
  1870. long pos = ftell(file_handle.handle.fp);
  1871. fseek(file_handle.handle.fp, pos - 1, SEEK_SET);
  1872. }
  1873. }
  1874. CG(start_lineno) = 2;
  1875. } else {
  1876. rewind(file_handle.handle.fp);
  1877. }
  1878. }
  1879. switch (behavior) {
  1880. case PHP_MODE_STANDARD:
  1881. php_execute_script(&file_handle TSRMLS_CC);
  1882. break;
  1883. case PHP_MODE_LINT:
  1884. PG(during_request_startup) = 0;
  1885. exit_status = php_lint_script(&file_handle TSRMLS_CC);
  1886. if (exit_status == SUCCESS) {
  1887. zend_printf("No syntax errors detected in %s\n", file_handle.filename);
  1888. } else {
  1889. zend_printf("Errors parsing %s\n", file_handle.filename);
  1890. }
  1891. break;
  1892. case PHP_MODE_STRIP:
  1893. if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {
  1894. zend_strip(TSRMLS_C);
  1895. zend_file_handle_dtor(&file_handle TSRMLS_CC);
  1896. php_end_ob_buffers(1 TSRMLS_CC);
  1897. }
  1898. return SUCCESS;
  1899. break;
  1900. case PHP_MODE_HIGHLIGHT:
  1901. {
  1902. zend_syntax_highlighter_ini syntax_highlighter_ini;
  1903. if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {
  1904. php_get_highlight_struct(&syntax_highlighter_ini);
  1905. zend_highlight(&syntax_highlighter_ini TSRMLS_CC);
  1906. if (fastcgi) {
  1907. goto fastcgi_request_done;
  1908. }
  1909. zend_file_handle_dtor(&file_handle TSRMLS_CC);
  1910. php_end_ob_buffers(1 TSRMLS_CC);
  1911. }
  1912. return SUCCESS;
  1913. }
  1914. break;
  1915. #if 0
  1916. /* Zeev might want to do something with this one day */
  1917. case PHP_MODE_INDENT:
  1918. open_file_for_scanning(&file_handle TSRMLS_CC);
  1919. zend_indent();
  1920. zend_file_handle_dtor(&file_handle TSRMLS_CC);
  1921. return SUCCESS;
  1922. break;
  1923. #endif
  1924. }
  1925. fastcgi_request_done:
  1926. {
  1927. STR_FREE(SG(request_info).path_translated);
  1928. php_request_shutdown((void *) 0);
  1929. if (exit_status == 0) {
  1930. exit_status = EG(exit_status);
  1931. }
  1932. if (free_query_string && SG(request_info).query_string) {
  1933. free(SG(request_info).query_string);
  1934. SG(request_info).query_string = NULL;
  1935. }
  1936. }
  1937. if (!fastcgi) {
  1938. if (benchmark) {
  1939. repeats--;
  1940. if (repeats > 0) {
  1941. script_file = NULL;
  1942. php_optind = orig_optind;
  1943. php_optarg = orig_optarg;
  1944. continue;
  1945. }
  1946. }
  1947. break;
  1948. }
  1949. /* only fastcgi will get here */
  1950. requests++;
  1951. if (max_requests && (requests == max_requests)) {
  1952. fcgi_finish_request(&request, 1);
  1953. if (bindpath) {
  1954. free(bindpath);
  1955. }
  1956. if (max_requests != 1) {
  1957. /* no need to return exit_status of the last request */
  1958. exit_status = 0;
  1959. }
  1960. break;
  1961. }
  1962. /* end of fastcgi loop */
  1963. }
  1964. fcgi_shutdown();
  1965. if (cgi_sapi_module.php_ini_path_override) {
  1966. free(cgi_sapi_module.php_ini_path_override);
  1967. }
  1968. if (cgi_sapi_module.ini_entries) {
  1969. free(cgi_sapi_module.ini_entries);
  1970. }
  1971. } zend_catch {
  1972. exit_status = 255;
  1973. } zend_end_try();
  1974. out:
  1975. if (benchmark) {
  1976. int sec;
  1977. #ifdef HAVE_GETTIMEOFDAY
  1978. int usec;
  1979. gettimeofday(&end, NULL);
  1980. sec = (int)(end.tv_sec - start.tv_sec);
  1981. if (end.tv_usec >= start.tv_usec) {
  1982. usec = (int)(end.tv_usec - start.tv_usec);
  1983. } else {
  1984. sec -= 1;
  1985. usec = (int)(end.tv_usec + 1000000 - start.tv_usec);
  1986. }
  1987. fprintf(stderr, "\nElapsed time: %d.%06d sec\n", sec, usec);
  1988. #else
  1989. time(&end);
  1990. sec = (int)(end - start);
  1991. fprintf(stderr, "\nElapsed time: %d sec\n", sec);
  1992. #endif
  1993. }
  1994. #ifndef PHP_WIN32
  1995. parent_out:
  1996. #endif
  1997. SG(server_context) = NULL;
  1998. php_module_shutdown(TSRMLS_C);
  1999. sapi_shutdown();
  2000. #ifdef ZTS
  2001. tsrm_shutdown();
  2002. #endif
  2003. #if defined(PHP_WIN32) && ZEND_DEBUG && 0
  2004. _CrtDumpMemoryLeaks();
  2005. #endif
  2006. return exit_status;
  2007. }
  2008. /* }}} */
  2009. /*
  2010. * Local variables:
  2011. * tab-width: 4
  2012. * c-basic-offset: 4
  2013. * End:
  2014. * vim600: sw=4 ts=4 fdm=marker
  2015. * vim<600: sw=4 ts=4
  2016. */