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