1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2016 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | 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$ */
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
35#ifdef PHP_WIN32
36# include "win32/time.h"
37# include "win32/signal.h"
38# include "win32/winutil.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 "http_status_codes.h"
73#include "ext/standard/php_standard.h"
74#include "ext/standard/url.h"
75
76#ifdef PHP_WIN32
77# include <io.h>
78# include <fcntl.h>
79# include "win32/php_registry.h"
80#endif
81
82#ifdef __riscos__
83# include <unixlib/local.h>
84int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS;
85#endif
86
87#include "zend_compile.h"
88#include "zend_execute.h"
89#include "zend_highlight.h"
90
91#include "php_getopt.h"
92
93#include "fastcgi.h"
94
95#if defined(PHP_WIN32) && defined(HAVE_OPENSSL)
96# include "openssl/applink.c"
97#endif
98
99#ifndef PHP_WIN32
100/* XXX this will need to change later when threaded fastcgi is implemented.  shane */
101struct sigaction act, old_term, old_quit, old_int;
102#endif
103
104static void (*php_php_import_environment_variables)(zval *array_ptr);
105
106/* these globals used for forking children on unix systems */
107/**
108 * Number of child processes that will get created to service requests
109 */
110static int children = 0;
111
112
113/**
114 * Set to non-zero if we are the parent process
115 */
116static int parent = 1;
117
118#ifndef PHP_WIN32
119/* Did parent received exit signals SIG_TERM/SIG_INT/SIG_QUIT */
120static int exit_signal = 0;
121
122/* Is Parent waiting for children to exit */
123static int parent_waiting = 0;
124
125/**
126 * Process group
127 */
128static pid_t pgroup;
129#endif
130
131#define PHP_MODE_STANDARD	1
132#define PHP_MODE_HIGHLIGHT	2
133#define PHP_MODE_LINT		4
134#define PHP_MODE_STRIP		5
135
136static char *php_optarg = NULL;
137static int php_optind = 1;
138static zend_module_entry cgi_module_entry;
139
140static const opt_struct OPTIONS[] = {
141	{'a', 0, "interactive"},
142	{'b', 1, "bindpath"},
143	{'C', 0, "no-chdir"},
144	{'c', 1, "php-ini"},
145	{'d', 1, "define"},
146	{'e', 0, "profile-info"},
147	{'f', 1, "file"},
148	{'h', 0, "help"},
149	{'i', 0, "info"},
150	{'l', 0, "syntax-check"},
151	{'m', 0, "modules"},
152	{'n', 0, "no-php-ini"},
153	{'q', 0, "no-header"},
154	{'s', 0, "syntax-highlight"},
155	{'s', 0, "syntax-highlighting"},
156	{'w', 0, "strip"},
157	{'?', 0, "usage"},/* help alias (both '?' and 'usage') */
158	{'v', 0, "version"},
159	{'z', 1, "zend-extension"},
160 	{'T', 1, "timing"},
161	{'-', 0, NULL} /* end of args */
162};
163
164typedef struct _php_cgi_globals_struct {
165	HashTable user_config_cache;
166	char *redirect_status_env;
167	zend_bool rfc2616_headers;
168	zend_bool nph;
169	zend_bool check_shebang_line;
170	zend_bool fix_pathinfo;
171	zend_bool force_redirect;
172	zend_bool discard_path;
173	zend_bool fcgi_logging;
174#ifdef PHP_WIN32
175	zend_bool impersonate;
176#endif
177} php_cgi_globals_struct;
178
179/* {{{ user_config_cache
180 *
181 * Key for each cache entry is dirname(PATH_TRANSLATED).
182 *
183 * NOTE: Each cache entry config_hash contains the combination from all user ini files found in
184 *       the path starting from doc_root throught to dirname(PATH_TRANSLATED).  There is no point
185 *       storing per-file entries as it would not be possible to detect added / deleted entries
186 *       between separate files.
187 */
188typedef struct _user_config_cache_entry {
189	time_t expires;
190	HashTable *user_config;
191} user_config_cache_entry;
192
193static void user_config_cache_entry_dtor(zval *el)
194{
195	user_config_cache_entry *entry = (user_config_cache_entry *)Z_PTR_P(el);
196	zend_hash_destroy(entry->user_config);
197	free(entry->user_config);
198	free(entry);
199}
200/* }}} */
201
202#ifdef ZTS
203static int php_cgi_globals_id;
204#define CGIG(v) ZEND_TSRMG(php_cgi_globals_id, php_cgi_globals_struct *, v)
205#if defined(PHP_WIN32)
206ZEND_TSRMLS_CACHE_DEFINE();
207#endif
208#else
209static php_cgi_globals_struct php_cgi_globals;
210#define CGIG(v) (php_cgi_globals.v)
211#endif
212
213#ifdef PHP_WIN32
214#define TRANSLATE_SLASHES(path) \
215	{ \
216		char *tmp = path; \
217		while (*tmp) { \
218			if (*tmp == '\\') *tmp = '/'; \
219			tmp++; \
220		} \
221	}
222#else
223#define TRANSLATE_SLASHES(path)
224#endif
225
226#ifdef PHP_WIN32
227#define WIN32_MAX_SPAWN_CHILDREN 64
228HANDLE kid_cgi_ps[WIN32_MAX_SPAWN_CHILDREN];
229int kids;
230HANDLE job = NULL;
231JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = { 0 };
232#endif
233
234#ifndef HAVE_ATTRIBUTE_WEAK
235static void fcgi_log(int type, const char *format, ...) {
236	va_list ap;
237
238	va_start(ap, format);
239	vfprintf(stderr, format, ap);
240	va_end(ap);
241}
242#endif
243
244static int print_module_info(zval *element)
245{
246	zend_module_entry *module = Z_PTR_P(element);
247	php_printf("%s\n", module->name);
248	return ZEND_HASH_APPLY_KEEP;
249}
250
251static int module_name_cmp(const void *a, const void *b)
252{
253	Bucket *f = (Bucket *) a;
254	Bucket *s = (Bucket *) b;
255
256	return strcasecmp(	((zend_module_entry *)Z_PTR(f->val))->name,
257						((zend_module_entry *)Z_PTR(s->val))->name);
258}
259
260static void print_modules(void)
261{
262	HashTable sorted_registry;
263
264	zend_hash_init(&sorted_registry, 64, NULL, NULL, 1);
265	zend_hash_copy(&sorted_registry, &module_registry, NULL);
266	zend_hash_sort(&sorted_registry, module_name_cmp, 0);
267	zend_hash_apply(&sorted_registry, print_module_info);
268	zend_hash_destroy(&sorted_registry);
269}
270
271static int print_extension_info(zend_extension *ext, void *arg)
272{
273	php_printf("%s\n", ext->name);
274	return 0;
275}
276
277static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s)
278{
279	return strcmp(	((zend_extension *)(*f)->data)->name,
280					((zend_extension *)(*s)->data)->name);
281}
282
283static void print_extensions(void)
284{
285	zend_llist sorted_exts;
286
287	zend_llist_copy(&sorted_exts, &zend_extensions);
288	sorted_exts.dtor = NULL;
289	zend_llist_sort(&sorted_exts, extension_name_cmp);
290	zend_llist_apply_with_argument(&sorted_exts, (llist_apply_with_arg_func_t) print_extension_info, NULL);
291	zend_llist_destroy(&sorted_exts);
292}
293
294#ifndef STDOUT_FILENO
295#define STDOUT_FILENO 1
296#endif
297
298static inline size_t sapi_cgi_single_write(const char *str, size_t str_length)
299{
300#ifdef PHP_WRITE_STDOUT
301	int ret;
302
303	ret = write(STDOUT_FILENO, str, str_length);
304	if (ret <= 0) return 0;
305	return ret;
306#else
307	size_t ret;
308
309	ret = fwrite(str, 1, MIN(str_length, 16384), stdout);
310	return ret;
311#endif
312}
313
314static size_t sapi_cgi_ub_write(const char *str, size_t str_length)
315{
316	const char *ptr = str;
317	size_t remaining = str_length;
318	size_t ret;
319
320	while (remaining > 0) {
321		ret = sapi_cgi_single_write(ptr, remaining);
322		if (!ret) {
323			php_handle_aborted_connection();
324			return str_length - remaining;
325		}
326		ptr += ret;
327		remaining -= ret;
328	}
329
330	return str_length;
331}
332
333static size_t sapi_fcgi_ub_write(const char *str, size_t str_length)
334{
335	const char *ptr = str;
336	size_t remaining = str_length;
337	fcgi_request *request = (fcgi_request*) SG(server_context);
338
339	while (remaining > 0) {
340		int to_write = remaining > INT_MAX ? INT_MAX : (int)remaining;
341		int ret = fcgi_write(request, FCGI_STDOUT, ptr, to_write);
342
343		if (ret <= 0) {
344			php_handle_aborted_connection();
345			return str_length - remaining;
346		}
347		ptr += ret;
348		remaining -= ret;
349	}
350
351	return str_length;
352}
353
354static void sapi_cgi_flush(void *server_context)
355{
356	if (fflush(stdout) == EOF) {
357		php_handle_aborted_connection();
358	}
359}
360
361static void sapi_fcgi_flush(void *server_context)
362{
363	fcgi_request *request = (fcgi_request*) server_context;
364
365	if (
366		!parent &&
367		request && !fcgi_flush(request, 0)) {
368
369		php_handle_aborted_connection();
370	}
371}
372
373#define SAPI_CGI_MAX_HEADER_LENGTH 1024
374
375static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers)
376{
377	char buf[SAPI_CGI_MAX_HEADER_LENGTH];
378	sapi_header_struct *h;
379	zend_llist_position pos;
380	zend_bool ignore_status = 0;
381	int response_status = SG(sapi_headers).http_response_code;
382
383	if (SG(request_info).no_headers == 1) {
384		return  SAPI_HEADER_SENT_SUCCESSFULLY;
385	}
386
387	if (CGIG(nph) || SG(sapi_headers).http_response_code != 200)
388	{
389		int len;
390		zend_bool has_status = 0;
391
392		if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) {
393			char *s;
394			len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s\r\n", SG(sapi_headers).http_status_line);
395			if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) {
396				response_status = atoi((s + 1));
397			}
398
399			if (len > SAPI_CGI_MAX_HEADER_LENGTH) {
400				len = SAPI_CGI_MAX_HEADER_LENGTH;
401			}
402
403		} else {
404			char *s;
405
406			if (SG(sapi_headers).http_status_line &&
407				(s = strchr(SG(sapi_headers).http_status_line, ' ')) != 0 &&
408				(s - SG(sapi_headers).http_status_line) >= 5 &&
409				strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0
410			) {
411				len = slprintf(buf, sizeof(buf), "Status:%s\r\n", s);
412				response_status = atoi((s + 1));
413			} else {
414				h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
415				while (h) {
416					if (h->header_len > sizeof("Status:")-1 &&
417						strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
418					) {
419						has_status = 1;
420						break;
421					}
422					h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
423				}
424				if (!has_status) {
425					http_response_status_code_pair *err = (http_response_status_code_pair*)http_status_map;
426
427					while (err->code != 0) {
428						if (err->code == SG(sapi_headers).http_response_code) {
429							break;
430						}
431						err++;
432					}
433					if (err->str) {
434						len = slprintf(buf, sizeof(buf), "Status: %d %s\r\n", SG(sapi_headers).http_response_code, err->str);
435					} else {
436						len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code);
437					}
438				}
439			}
440		}
441
442		if (!has_status) {
443			PHPWRITE_H(buf, len);
444			ignore_status = 1;
445		}
446	}
447
448	h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
449	while (h) {
450		/* prevent CRLFCRLF */
451		if (h->header_len) {
452			if (h->header_len > sizeof("Status:")-1 &&
453				strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
454			) {
455				if (!ignore_status) {
456					ignore_status = 1;
457					PHPWRITE_H(h->header, h->header_len);
458					PHPWRITE_H("\r\n", 2);
459				}
460			} else if (response_status == 304 && h->header_len > sizeof("Content-Type:")-1 &&
461				strncasecmp(h->header, "Content-Type:", sizeof("Content-Type:")-1) == 0
462			) {
463				h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
464				continue;
465			} else {
466				PHPWRITE_H(h->header, h->header_len);
467				PHPWRITE_H("\r\n", 2);
468			}
469		}
470		h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
471	}
472	PHPWRITE_H("\r\n", 2);
473
474	return SAPI_HEADER_SENT_SUCCESSFULLY;
475}
476
477#ifndef STDIN_FILENO
478# define STDIN_FILENO 0
479#endif
480
481static size_t sapi_cgi_read_post(char *buffer, size_t count_bytes)
482{
483	size_t read_bytes = 0;
484	int tmp_read_bytes;
485	size_t remaining_bytes;
486
487	assert(SG(request_info).content_length >= SG(read_post_bytes));
488
489	remaining_bytes = (size_t)(SG(request_info).content_length - SG(read_post_bytes));
490
491	count_bytes = MIN(count_bytes, remaining_bytes);
492	while (read_bytes < count_bytes) {
493#ifdef PHP_WIN32
494		size_t diff = count_bytes - read_bytes;
495		unsigned int to_read = (diff > UINT_MAX) ? UINT_MAX : (unsigned int)diff;
496
497		tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, to_read);
498#else
499		tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes);
500#endif
501		if (tmp_read_bytes <= 0) {
502			break;
503		}
504		read_bytes += tmp_read_bytes;
505	}
506	return read_bytes;
507}
508
509static size_t sapi_fcgi_read_post(char *buffer, size_t count_bytes)
510{
511	size_t read_bytes = 0;
512	int tmp_read_bytes;
513	fcgi_request *request = (fcgi_request*) SG(server_context);
514	size_t remaining = SG(request_info).content_length - SG(read_post_bytes);
515
516	if (remaining < count_bytes) {
517		count_bytes = remaining;
518	}
519	while (read_bytes < count_bytes) {
520		size_t diff = count_bytes - read_bytes;
521		int to_read = (diff > INT_MAX) ? INT_MAX : (int)diff;
522
523		tmp_read_bytes = fcgi_read(request, buffer + read_bytes, to_read);
524		if (tmp_read_bytes <= 0) {
525			break;
526		}
527		read_bytes += tmp_read_bytes;
528	}
529	return read_bytes;
530}
531
532static char *sapi_cgi_getenv(char *name, size_t name_len)
533{
534	return getenv(name);
535}
536
537static char *sapi_fcgi_getenv(char *name, size_t name_len)
538{
539	/* when php is started by mod_fastcgi, no regular environment
540	 * is provided to PHP.  It is always sent to PHP at the start
541	 * of a request.  So we have to do our own lookup to get env
542	 * vars.  This could probably be faster somehow.  */
543	fcgi_request *request = (fcgi_request*) SG(server_context);
544	char *ret = fcgi_getenv(request, name, (int)name_len);
545
546	if (ret) return ret;
547	/*  if cgi, or fastcgi and not found in fcgi env
548		check the regular environment */
549	return getenv(name);
550}
551
552static char *_sapi_cgi_putenv(char *name, size_t name_len, char *value)
553{
554#if !HAVE_SETENV || !HAVE_UNSETENV
555	size_t len;
556	char *buf;
557#endif
558
559#if HAVE_SETENV
560	if (value) {
561		setenv(name, value, 1);
562	}
563#endif
564#if HAVE_UNSETENV
565	if (!value) {
566		unsetenv(name);
567	}
568#endif
569
570#if !HAVE_SETENV || !HAVE_UNSETENV
571	/*  if cgi, or fastcgi and not found in fcgi env
572		check the regular environment
573		this leaks, but it's only cgi anyway, we'll fix
574		it for 5.0
575	*/
576	len = name_len + (value ? strlen(value) : 0) + sizeof("=") + 2;
577	buf = (char *) malloc(len);
578	if (buf == NULL) {
579		return getenv(name);
580	}
581#endif
582#if !HAVE_SETENV
583	if (value) {
584		len = slprintf(buf, len - 1, "%s=%s", name, value);
585		putenv(buf);
586	}
587#endif
588#if !HAVE_UNSETENV
589	if (!value) {
590		len = slprintf(buf, len - 1, "%s=", name);
591		putenv(buf);
592	}
593#endif
594	return getenv(name);
595}
596
597static char *sapi_cgi_read_cookies(void)
598{
599	return getenv("HTTP_COOKIE");
600}
601
602static char *sapi_fcgi_read_cookies(void)
603{
604	fcgi_request *request = (fcgi_request*) SG(server_context);
605
606	return FCGI_GETENV(request, "HTTP_COOKIE");
607}
608
609static void cgi_php_load_env_var(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg)
610{
611	zval *array_ptr = (zval*)arg;
612	int filter_arg = (Z_ARR_P(array_ptr) == Z_ARR(PG(http_globals)[TRACK_VARS_ENV]))?PARSE_ENV:PARSE_SERVER;
613	size_t new_val_len;
614
615	if (sapi_module.input_filter(filter_arg, var, &val, strlen(val), &new_val_len)) {
616		php_register_variable_safe(var, val, new_val_len, array_ptr);
617	}
618}
619
620static void cgi_php_import_environment_variables(zval *array_ptr)
621{
622	if (Z_TYPE(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY &&
623		Z_ARR_P(array_ptr) != Z_ARR(PG(http_globals)[TRACK_VARS_ENV]) &&
624		zend_hash_num_elements(Z_ARRVAL(PG(http_globals)[TRACK_VARS_ENV])) > 0
625	) {
626		zval_dtor(array_ptr);
627		ZVAL_DUP(array_ptr, &PG(http_globals)[TRACK_VARS_ENV]);
628		return;
629	} else if (Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY &&
630		Z_ARR_P(array_ptr) != Z_ARR(PG(http_globals)[TRACK_VARS_SERVER]) &&
631		zend_hash_num_elements(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER])) > 0
632	) {
633		zval_dtor(array_ptr);
634		ZVAL_DUP(array_ptr, &PG(http_globals)[TRACK_VARS_SERVER]);
635		return;
636	}
637
638	/* call php's original import as a catch-all */
639	php_php_import_environment_variables(array_ptr);
640
641	if (fcgi_is_fastcgi()) {
642		fcgi_request *request = (fcgi_request*) SG(server_context);
643		fcgi_loadenv(request, cgi_php_load_env_var, array_ptr);
644	}
645}
646
647static void sapi_cgi_register_variables(zval *track_vars_array)
648{
649	size_t php_self_len;
650	char *php_self;
651
652	/* In CGI mode, we consider the environment to be a part of the server
653	 * variables
654	 */
655	php_import_environment_variables(track_vars_array);
656
657	if (CGIG(fix_pathinfo)) {
658		char *script_name = SG(request_info).request_uri;
659		char *path_info;
660		int free_php_self;
661		ALLOCA_FLAG(use_heap)
662
663		if (fcgi_is_fastcgi()) {
664			fcgi_request *request = (fcgi_request*) SG(server_context);
665
666			path_info = FCGI_GETENV(request, "PATH_INFO");
667		} else {
668			path_info = getenv("PATH_INFO");
669		}
670
671		if (path_info) {
672			size_t path_info_len = strlen(path_info);
673
674			if (script_name) {
675				size_t script_name_len = strlen(script_name);
676
677				php_self_len = script_name_len + path_info_len;
678				php_self = do_alloca(php_self_len + 1, use_heap);
679				memcpy(php_self, script_name, script_name_len + 1);
680				memcpy(php_self + script_name_len, path_info, path_info_len + 1);
681				free_php_self = 1;
682			}  else {
683				php_self = path_info;
684				php_self_len = path_info_len;
685				free_php_self = 0;
686			}
687		} else if (script_name) {
688			php_self = script_name;
689			php_self_len = strlen(script_name);
690			free_php_self = 0;
691		} else {
692			php_self = "";
693			php_self_len = 0;
694			free_php_self = 0;
695		}
696
697		/* Build the special-case PHP_SELF variable for the CGI version */
698		if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len)) {
699			php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array);
700		}
701		if (free_php_self) {
702			free_alloca(php_self, use_heap);
703		}
704	} else {
705		php_self = SG(request_info).request_uri ? SG(request_info).request_uri : "";
706		php_self_len = strlen(php_self);
707		if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len)) {
708			php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array);
709		}
710	}
711}
712
713static void sapi_cgi_log_message(char *message)
714{
715	if (fcgi_is_fastcgi() && CGIG(fcgi_logging)) {
716		fcgi_request *request;
717
718		request = (fcgi_request*) SG(server_context);
719		if (request) {
720			int ret, len = (int)strlen(message);
721			char *buf = malloc(len+2);
722
723			memcpy(buf, message, len);
724			memcpy(buf + len, "\n", sizeof("\n"));
725			ret = fcgi_write(request, FCGI_STDERR, buf, (int)(len + 1));
726			free(buf);
727			if (ret < 0) {
728				php_handle_aborted_connection();
729			}
730		} else {
731			fprintf(stderr, "%s\n", message);
732		}
733		/* ignore return code */
734	} else {
735		fprintf(stderr, "%s\n", message);
736	}
737}
738
739/* {{{ php_cgi_ini_activate_user_config
740 */
741static void php_cgi_ini_activate_user_config(char *path, size_t path_len, const char *doc_root, size_t doc_root_len, int start)
742{
743	char *ptr;
744	user_config_cache_entry *new_entry, *entry;
745	time_t request_time = (time_t)sapi_get_request_time();
746
747	/* Find cached config entry: If not found, create one */
748	if ((entry = zend_hash_str_find_ptr(&CGIG(user_config_cache), path, path_len)) == NULL) {
749		new_entry = pemalloc(sizeof(user_config_cache_entry), 1);
750		new_entry->expires = 0;
751		new_entry->user_config = (HashTable *) pemalloc(sizeof(HashTable), 1);
752		zend_hash_init(new_entry->user_config, 8, NULL, (dtor_func_t) config_zval_dtor, 1);
753		entry = zend_hash_str_update_ptr(&CGIG(user_config_cache), path, path_len, new_entry);
754	}
755
756	/* Check whether cache entry has expired and rescan if it is */
757	if (request_time > entry->expires) {
758		char *real_path = NULL;
759		size_t real_path_len;
760		char *s1, *s2;
761		size_t s_len;
762
763		/* Clear the expired config */
764		zend_hash_clean(entry->user_config);
765
766		if (!IS_ABSOLUTE_PATH(path, path_len)) {
767			real_path = tsrm_realpath(path, NULL);
768			if (real_path == NULL) {
769				return;
770			}
771			real_path_len = strlen(real_path);
772			path = real_path;
773			path_len = real_path_len;
774		}
775
776		if (path_len > doc_root_len) {
777			s1 = (char *) doc_root;
778			s2 = path;
779			s_len = doc_root_len;
780		} else {
781			s1 = path;
782			s2 = (char *) doc_root;
783			s_len = path_len;
784		}
785
786		/* we have to test if path is part of DOCUMENT_ROOT.
787		  if it is inside the docroot, we scan the tree up to the docroot
788			to find more user.ini, if not we only scan the current path.
789		  */
790#ifdef PHP_WIN32
791		if (strnicmp(s1, s2, s_len) == 0) {
792#else
793		if (strncmp(s1, s2, s_len) == 0) {
794#endif
795			ptr = s2 + start;  /* start is the point where doc_root ends! */
796			while ((ptr = strchr(ptr, DEFAULT_SLASH)) != NULL) {
797				*ptr = 0;
798				php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config);
799				*ptr = '/';
800				ptr++;
801			}
802		} else {
803			php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config);
804		}
805
806		if (real_path) {
807			efree(real_path);
808		}
809		entry->expires = request_time + PG(user_ini_cache_ttl);
810	}
811
812	/* Activate ini entries with values from the user config hash */
813	php_ini_activate_config(entry->user_config, PHP_INI_PERDIR, PHP_INI_STAGE_HTACCESS);
814}
815/* }}} */
816
817static int sapi_cgi_activate(void)
818{
819	char *path, *doc_root, *server_name;
820	size_t path_len, doc_root_len, server_name_len;
821
822	/* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */
823	if (!SG(request_info).path_translated) {
824		return FAILURE;
825	}
826
827	if (php_ini_has_per_host_config()) {
828		/* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */
829		if (fcgi_is_fastcgi()) {
830			fcgi_request *request = (fcgi_request*) SG(server_context);
831
832			server_name = FCGI_GETENV(request, "SERVER_NAME");
833		} else {
834			server_name = getenv("SERVER_NAME");
835		}
836		/* SERVER_NAME should also be defined at this stage..but better check it anyway */
837		if (server_name) {
838			server_name_len = strlen(server_name);
839			server_name = estrndup(server_name, server_name_len);
840			zend_str_tolower(server_name, server_name_len);
841			php_ini_activate_per_host_config(server_name, server_name_len);
842			efree(server_name);
843		}
844	}
845
846	if (php_ini_has_per_dir_config() ||
847		(PG(user_ini_filename) && *PG(user_ini_filename))
848	) {
849		/* Prepare search path */
850		path_len = strlen(SG(request_info).path_translated);
851
852		/* Make sure we have trailing slash! */
853		if (!IS_SLASH(SG(request_info).path_translated[path_len])) {
854			path = emalloc(path_len + 2);
855			memcpy(path, SG(request_info).path_translated, path_len + 1);
856			path_len = zend_dirname(path, path_len);
857			path[path_len++] = DEFAULT_SLASH;
858		} else {
859			path = estrndup(SG(request_info).path_translated, path_len);
860			path_len = zend_dirname(path, path_len);
861		}
862		path[path_len] = 0;
863
864		/* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */
865		php_ini_activate_per_dir_config(path, path_len); /* Note: for global settings sake we check from root to path */
866
867		/* Load and activate user ini files in path starting from DOCUMENT_ROOT */
868		if (PG(user_ini_filename) && *PG(user_ini_filename)) {
869			if (fcgi_is_fastcgi()) {
870				fcgi_request *request = (fcgi_request*) SG(server_context);
871
872				doc_root = FCGI_GETENV(request, "DOCUMENT_ROOT");
873			} else {
874				doc_root = getenv("DOCUMENT_ROOT");
875			}
876			/* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */
877			if (doc_root) {
878				doc_root_len = strlen(doc_root);
879				if (doc_root_len > 0 && IS_SLASH(doc_root[doc_root_len - 1])) {
880					--doc_root_len;
881				}
882#ifdef PHP_WIN32
883				/* paths on windows should be case-insensitive */
884				doc_root = estrndup(doc_root, doc_root_len);
885				zend_str_tolower(doc_root, doc_root_len);
886#endif
887				php_cgi_ini_activate_user_config(path, path_len, doc_root, doc_root_len, (doc_root_len > 0 && (doc_root_len - 1)));
888
889#ifdef PHP_WIN32
890				efree(doc_root);
891#endif
892			}
893		}
894
895		efree(path);
896	}
897
898	return SUCCESS;
899}
900
901static int sapi_cgi_deactivate(void)
902{
903	/* flush only when SAPI was started. The reasons are:
904		1. SAPI Deactivate is called from two places: module init and request shutdown
905		2. When the first call occurs and the request is not set up, flush fails on FastCGI.
906	*/
907	if (SG(sapi_started)) {
908		if (fcgi_is_fastcgi()) {
909			if (
910				!parent &&
911				!fcgi_finish_request((fcgi_request*)SG(server_context), 0)) {
912				php_handle_aborted_connection();
913			}
914		} else {
915			sapi_cgi_flush(SG(server_context));
916		}
917	}
918	return SUCCESS;
919}
920
921static int php_cgi_startup(sapi_module_struct *sapi_module)
922{
923	if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) {
924		return FAILURE;
925	}
926	return SUCCESS;
927}
928
929/* {{{ sapi_module_struct cgi_sapi_module
930 */
931static sapi_module_struct cgi_sapi_module = {
932	"cgi-fcgi",						/* name */
933	"CGI/FastCGI",					/* pretty name */
934
935	php_cgi_startup,				/* startup */
936	php_module_shutdown_wrapper,	/* shutdown */
937
938	sapi_cgi_activate,				/* activate */
939	sapi_cgi_deactivate,			/* deactivate */
940
941	sapi_cgi_ub_write,				/* unbuffered write */
942	sapi_cgi_flush,					/* flush */
943	NULL,							/* get uid */
944	sapi_cgi_getenv,				/* getenv */
945
946	php_error,						/* error handler */
947
948	NULL,							/* header handler */
949	sapi_cgi_send_headers,			/* send headers handler */
950	NULL,							/* send header handler */
951
952	sapi_cgi_read_post,				/* read POST data */
953	sapi_cgi_read_cookies,			/* read Cookies */
954
955	sapi_cgi_register_variables,	/* register server variables */
956	sapi_cgi_log_message,			/* Log message */
957	NULL,							/* Get request time */
958	NULL,							/* Child terminate */
959
960	STANDARD_SAPI_MODULE_PROPERTIES
961};
962/* }}} */
963
964/* {{{ arginfo ext/standard/dl.c */
965ZEND_BEGIN_ARG_INFO(arginfo_dl, 0)
966	ZEND_ARG_INFO(0, extension_filename)
967ZEND_END_ARG_INFO()
968/* }}} */
969
970static const zend_function_entry additional_functions[] = {
971	ZEND_FE(dl, arginfo_dl)
972	{NULL, NULL, NULL}
973};
974
975/* {{{ php_cgi_usage
976 */
977static void php_cgi_usage(char *argv0)
978{
979	char *prog;
980
981	prog = strrchr(argv0, '/');
982	if (prog) {
983		prog++;
984	} else {
985		prog = "php";
986	}
987
988	php_printf(	"Usage: %s [-q] [-h] [-s] [-v] [-i] [-f <file>]\n"
989				"       %s <file> [args...]\n"
990				"  -a               Run interactively\n"
991				"  -b <address:port>|<port> Bind Path for external FASTCGI Server mode\n"
992				"  -C               Do not chdir to the script's directory\n"
993				"  -c <path>|<file> Look for php.ini file in this directory\n"
994				"  -n               No php.ini file will be used\n"
995				"  -d foo[=bar]     Define INI entry foo with value 'bar'\n"
996				"  -e               Generate extended information for debugger/profiler\n"
997				"  -f <file>        Parse <file>.  Implies `-q'\n"
998				"  -h               This help\n"
999				"  -i               PHP information\n"
1000				"  -l               Syntax check only (lint)\n"
1001				"  -m               Show compiled in modules\n"
1002				"  -q               Quiet-mode.  Suppress HTTP Header output.\n"
1003				"  -s               Display colour syntax highlighted source.\n"
1004				"  -v               Version number\n"
1005				"  -w               Display source with stripped comments and whitespace.\n"
1006				"  -z <file>        Load Zend extension <file>.\n"
1007				"  -T <count>       Measure execution time of script repeated <count> times.\n",
1008				prog, prog);
1009}
1010/* }}} */
1011
1012/* {{{ is_valid_path
1013 *
1014 * some server configurations allow '..' to slip through in the
1015 * translated path.   We'll just refuse to handle such a path.
1016 */
1017static int is_valid_path(const char *path)
1018{
1019	const char *p = path;
1020
1021	if (UNEXPECTED(!p)) {
1022		return 0;
1023	}
1024	if (UNEXPECTED(*p == '.') && *(p+1) == '.' && (!*(p+2) || IS_SLASH(*(p+2)))) {
1025		return 0;
1026	}
1027	while (*p) {
1028		if (IS_SLASH(*p)) {
1029			p++;
1030			if (UNEXPECTED(*p == '.')) {
1031				p++;
1032				if (UNEXPECTED(*p == '.')) {
1033					p++;
1034					if (UNEXPECTED(!*p) || UNEXPECTED(IS_SLASH(*p))) {
1035						return 0;
1036					}
1037				}
1038			}
1039		}
1040		p++;
1041	}
1042	return 1;
1043}
1044/* }}} */
1045
1046#define CGI_GETENV(name) \
1047	((has_env) ? \
1048		FCGI_GETENV(request, name) : \
1049    	getenv(name))
1050
1051#define CGI_PUTENV(name, value) \
1052	((has_env) ? \
1053		FCGI_PUTENV(request, name, value) : \
1054		_sapi_cgi_putenv(name, sizeof(name)-1, value))
1055
1056/* {{{ init_request_info
1057
1058  initializes request_info structure
1059
1060  specificly in this section we handle proper translations
1061  for:
1062
1063  PATH_INFO
1064	derived from the portion of the URI path following
1065	the script name but preceding any query data
1066	may be empty
1067
1068  PATH_TRANSLATED
1069    derived by taking any path-info component of the
1070	request URI and performing any virtual-to-physical
1071	translation appropriate to map it onto the server's
1072	document repository structure
1073
1074	empty if PATH_INFO is empty
1075
1076	The env var PATH_TRANSLATED **IS DIFFERENT** than the
1077	request_info.path_translated variable, the latter should
1078	match SCRIPT_FILENAME instead.
1079
1080  SCRIPT_NAME
1081    set to a URL path that could identify the CGI script
1082	rather than the interpreter.  PHP_SELF is set to this
1083
1084  REQUEST_URI
1085    uri section following the domain:port part of a URI
1086
1087  SCRIPT_FILENAME
1088    The virtual-to-physical translation of SCRIPT_NAME (as per
1089	PATH_TRANSLATED)
1090
1091  These settings are documented at
1092  http://cgi-spec.golux.com/
1093
1094
1095  Based on the following URL request:
1096
1097  http://localhost/info.php/test?a=b
1098
1099  should produce, which btw is the same as if
1100  we were running under mod_cgi on apache (ie. not
1101  using ScriptAlias directives):
1102
1103  PATH_INFO=/test
1104  PATH_TRANSLATED=/docroot/test
1105  SCRIPT_NAME=/info.php
1106  REQUEST_URI=/info.php/test?a=b
1107  SCRIPT_FILENAME=/docroot/info.php
1108  QUERY_STRING=a=b
1109
1110  but what we get is (cgi/mod_fastcgi under apache):
1111
1112  PATH_INFO=/info.php/test
1113  PATH_TRANSLATED=/docroot/info.php/test
1114  SCRIPT_NAME=/php/php-cgi  (from the Action setting I suppose)
1115  REQUEST_URI=/info.php/test?a=b
1116  SCRIPT_FILENAME=/path/to/php/bin/php-cgi  (Action setting translated)
1117  QUERY_STRING=a=b
1118
1119  Comments in the code below refer to using the above URL in a request
1120
1121 */
1122static void init_request_info(fcgi_request *request)
1123{
1124	int has_env = fcgi_has_env(request);
1125	char *env_script_filename = CGI_GETENV("SCRIPT_FILENAME");
1126	char *env_path_translated = CGI_GETENV("PATH_TRANSLATED");
1127	char *script_path_translated = env_script_filename;
1128
1129	/* some broken servers do not have script_filename or argv0
1130	 * an example, IIS configured in some ways.  then they do more
1131	 * broken stuff and set path_translated to the cgi script location */
1132	if (!script_path_translated && env_path_translated) {
1133		script_path_translated = env_path_translated;
1134	}
1135
1136	/* initialize the defaults */
1137	SG(request_info).path_translated = NULL;
1138	SG(request_info).request_method = NULL;
1139	SG(request_info).proto_num = 1000;
1140	SG(request_info).query_string = NULL;
1141	SG(request_info).request_uri = NULL;
1142	SG(request_info).content_type = NULL;
1143	SG(request_info).content_length = 0;
1144	SG(sapi_headers).http_response_code = 200;
1145
1146	/* script_path_translated being set is a good indication that
1147	 * we are running in a cgi environment, since it is always
1148	 * null otherwise.  otherwise, the filename
1149	 * of the script will be retreived later via argc/argv */
1150	if (script_path_translated) {
1151		const char *auth;
1152		char *content_length = CGI_GETENV("CONTENT_LENGTH");
1153		char *content_type = CGI_GETENV("CONTENT_TYPE");
1154		char *env_path_info = CGI_GETENV("PATH_INFO");
1155		char *env_script_name = CGI_GETENV("SCRIPT_NAME");
1156
1157#ifdef PHP_WIN32
1158		/* Hack for buggy IIS that sets incorrect PATH_INFO */
1159		char *env_server_software = CGI_GETENV("SERVER_SOFTWARE");
1160
1161		if (env_server_software &&
1162			env_script_name &&
1163			env_path_info &&
1164			strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS")-1) == 0 &&
1165			strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0
1166		) {
1167			env_path_info = CGI_PUTENV("ORIG_PATH_INFO", env_path_info);
1168			env_path_info += strlen(env_script_name);
1169			if (*env_path_info == 0) {
1170				env_path_info = NULL;
1171			}
1172			env_path_info = CGI_PUTENV("PATH_INFO", env_path_info);
1173		}
1174#endif
1175
1176		if (CGIG(fix_pathinfo)) {
1177			zend_stat_t st;
1178			char *real_path = NULL;
1179			char *env_redirect_url = CGI_GETENV("REDIRECT_URL");
1180			char *env_document_root = CGI_GETENV("DOCUMENT_ROOT");
1181			char *orig_path_translated = env_path_translated;
1182			char *orig_path_info = env_path_info;
1183			char *orig_script_name = env_script_name;
1184			char *orig_script_filename = env_script_filename;
1185			size_t script_path_translated_len;
1186
1187			if (!env_document_root && PG(doc_root)) {
1188				env_document_root = CGI_PUTENV("DOCUMENT_ROOT", PG(doc_root));
1189				/* fix docroot */
1190				TRANSLATE_SLASHES(env_document_root);
1191			}
1192
1193			if (env_path_translated != NULL && env_redirect_url != NULL &&
1194 			    env_path_translated != script_path_translated &&
1195 			    strcmp(env_path_translated, script_path_translated) != 0) {
1196				/*
1197				 * pretty much apache specific.  If we have a redirect_url
1198				 * then our script_filename and script_name point to the
1199				 * php executable
1200				 */
1201				script_path_translated = env_path_translated;
1202				/* we correct SCRIPT_NAME now in case we don't have PATH_INFO */
1203				env_script_name = env_redirect_url;
1204			}
1205
1206#ifdef __riscos__
1207			/* Convert path to unix format*/
1208			__riscosify_control |= __RISCOSIFY_DONT_CHECK_DIR;
1209			script_path_translated = __unixify(script_path_translated, 0, NULL, 1, 0);
1210#endif
1211
1212			/*
1213			 * if the file doesn't exist, try to extract PATH_INFO out
1214			 * of it by stat'ing back through the '/'
1215			 * this fixes url's like /info.php/test
1216			 */
1217			if (script_path_translated &&
1218				(script_path_translated_len = strlen(script_path_translated)) > 0 &&
1219				(script_path_translated[script_path_translated_len-1] == '/' ||
1220#ifdef PHP_WIN32
1221				script_path_translated[script_path_translated_len-1] == '\\' ||
1222#endif
1223				(real_path = tsrm_realpath(script_path_translated, NULL)) == NULL)
1224			) {
1225				char *pt = estrndup(script_path_translated, script_path_translated_len);
1226				size_t len = script_path_translated_len;
1227				char *ptr;
1228
1229				while ((ptr = strrchr(pt, '/')) || (ptr = strrchr(pt, '\\'))) {
1230					*ptr = 0;
1231					if (zend_stat(pt, &st) == 0 && S_ISREG(st.st_mode)) {
1232						/*
1233						 * okay, we found the base script!
1234						 * work out how many chars we had to strip off;
1235						 * then we can modify PATH_INFO
1236						 * accordingly
1237						 *
1238						 * we now have the makings of
1239						 * PATH_INFO=/test
1240						 * SCRIPT_FILENAME=/docroot/info.php
1241						 *
1242						 * we now need to figure out what docroot is.
1243						 * if DOCUMENT_ROOT is set, this is easy, otherwise,
1244						 * we have to play the game of hide and seek to figure
1245						 * out what SCRIPT_NAME should be
1246						 */
1247						size_t slen = len - strlen(pt);
1248						size_t pilen = env_path_info ? strlen(env_path_info) : 0;
1249						char *path_info = env_path_info ? env_path_info + pilen - slen : NULL;
1250
1251						if (orig_path_info != path_info) {
1252							if (orig_path_info) {
1253								char old;
1254
1255								CGI_PUTENV("ORIG_PATH_INFO", orig_path_info);
1256								old = path_info[0];
1257								path_info[0] = 0;
1258								if (!orig_script_name ||
1259									strcmp(orig_script_name, env_path_info) != 0) {
1260									if (orig_script_name) {
1261										CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
1262									}
1263									SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_path_info);
1264								} else {
1265									SG(request_info).request_uri = orig_script_name;
1266								}
1267								path_info[0] = old;
1268							}
1269							env_path_info = CGI_PUTENV("PATH_INFO", path_info);
1270						}
1271						if (!orig_script_filename ||
1272							strcmp(orig_script_filename, pt) != 0) {
1273							if (orig_script_filename) {
1274								CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
1275							}
1276							script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", pt);
1277						}
1278						TRANSLATE_SLASHES(pt);
1279
1280						/* figure out docroot
1281						 * SCRIPT_FILENAME minus SCRIPT_NAME
1282						 */
1283						if (env_document_root) {
1284							size_t l = strlen(env_document_root);
1285							size_t path_translated_len = 0;
1286							char *path_translated = NULL;
1287
1288							if (l && env_document_root[l - 1] == '/') {
1289								--l;
1290							}
1291
1292							/* we have docroot, so we should have:
1293							 * DOCUMENT_ROOT=/docroot
1294							 * SCRIPT_FILENAME=/docroot/info.php
1295							 */
1296
1297							/* PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO */
1298							path_translated_len = l + (env_path_info ? strlen(env_path_info) : 0);
1299							path_translated = (char *) emalloc(path_translated_len + 1);
1300							memcpy(path_translated, env_document_root, l);
1301							if (env_path_info) {
1302								memcpy(path_translated + l, env_path_info, (path_translated_len - l));
1303							}
1304							path_translated[path_translated_len] = '\0';
1305							if (orig_path_translated) {
1306								CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
1307							}
1308							env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated);
1309							efree(path_translated);
1310						} else if (	env_script_name &&
1311									strstr(pt, env_script_name)
1312						) {
1313							/* PATH_TRANSLATED = PATH_TRANSLATED - SCRIPT_NAME + PATH_INFO */
1314							size_t ptlen = strlen(pt) - strlen(env_script_name);
1315							size_t path_translated_len = ptlen + (env_path_info ? strlen(env_path_info) : 0);
1316							char *path_translated = NULL;
1317
1318							path_translated = (char *) emalloc(path_translated_len + 1);
1319							memcpy(path_translated, pt, ptlen);
1320							if (env_path_info) {
1321								memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen);
1322							}
1323							path_translated[path_translated_len] = '\0';
1324							if (orig_path_translated) {
1325								CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
1326							}
1327							env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated);
1328							efree(path_translated);
1329						}
1330						break;
1331					}
1332				}
1333				if (!ptr) {
1334					/*
1335					 * if we stripped out all the '/' and still didn't find
1336					 * a valid path... we will fail, badly. of course we would
1337					 * have failed anyway... we output 'no input file' now.
1338					 */
1339					if (orig_script_filename) {
1340						CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
1341					}
1342					script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", NULL);
1343					SG(sapi_headers).http_response_code = 404;
1344				}
1345				if (!SG(request_info).request_uri) {
1346					if (!orig_script_name ||
1347						strcmp(orig_script_name, env_script_name) != 0) {
1348						if (orig_script_name) {
1349							CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
1350						}
1351						SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name);
1352					} else {
1353						SG(request_info).request_uri = orig_script_name;
1354					}
1355				}
1356				if (pt) {
1357					efree(pt);
1358				}
1359			} else {
1360				/* make sure path_info/translated are empty */
1361				if (!orig_script_filename ||
1362					(script_path_translated != orig_script_filename &&
1363					strcmp(script_path_translated, orig_script_filename) != 0)) {
1364					if (orig_script_filename) {
1365						CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
1366					}
1367					script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", script_path_translated);
1368				}
1369				if (env_redirect_url) {
1370					if (orig_path_info) {
1371						CGI_PUTENV("ORIG_PATH_INFO", orig_path_info);
1372						CGI_PUTENV("PATH_INFO", NULL);
1373					}
1374					if (orig_path_translated) {
1375						CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
1376						CGI_PUTENV("PATH_TRANSLATED", NULL);
1377					}
1378				}
1379				if (env_script_name != orig_script_name) {
1380					if (orig_script_name) {
1381						CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
1382					}
1383					SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name);
1384				} else {
1385					SG(request_info).request_uri = env_script_name;
1386				}
1387				efree(real_path);
1388			}
1389		} else {
1390			/* pre 4.3 behaviour, shouldn't be used but provides BC */
1391			if (env_path_info) {
1392				SG(request_info).request_uri = env_path_info;
1393			} else {
1394				SG(request_info).request_uri = env_script_name;
1395			}
1396			if (!CGIG(discard_path) && env_path_translated) {
1397				script_path_translated = env_path_translated;
1398			}
1399		}
1400
1401		if (is_valid_path(script_path_translated)) {
1402			SG(request_info).path_translated = estrdup(script_path_translated);
1403		}
1404
1405		SG(request_info).request_method = CGI_GETENV("REQUEST_METHOD");
1406		/* FIXME - Work out proto_num here */
1407		SG(request_info).query_string = CGI_GETENV("QUERY_STRING");
1408		SG(request_info).content_type = (content_type ? content_type : "" );
1409		SG(request_info).content_length = (content_length ? atol(content_length) : 0);
1410
1411		/* The CGI RFC allows servers to pass on unvalidated Authorization data */
1412		auth = CGI_GETENV("HTTP_AUTHORIZATION");
1413		php_handle_auth_data(auth);
1414	}
1415}
1416/* }}} */
1417
1418#ifndef PHP_WIN32
1419/**
1420 * Clean up child processes upon exit
1421 */
1422void fastcgi_cleanup(int signal)
1423{
1424#ifdef DEBUG_FASTCGI
1425	fprintf(stderr, "FastCGI shutdown, pid %d\n", getpid());
1426#endif
1427
1428	sigaction(SIGTERM, &old_term, 0);
1429
1430	/* Kill all the processes in our process group */
1431	kill(-pgroup, SIGTERM);
1432
1433	if (parent && parent_waiting) {
1434		exit_signal = 1;
1435	} else {
1436		exit(0);
1437	}
1438}
1439#else
1440BOOL fastcgi_cleanup(DWORD sig)
1441{
1442	int i = kids;
1443
1444	while (0 < i--) {
1445		if (NULL == kid_cgi_ps[i]) {
1446				continue;
1447		}
1448
1449		TerminateProcess(kid_cgi_ps[i], 0);
1450		CloseHandle(kid_cgi_ps[i]);
1451		kid_cgi_ps[i] = NULL;
1452	}
1453
1454	if (job) {
1455		CloseHandle(job);
1456	}
1457
1458	parent = 0;
1459
1460	return TRUE;
1461}
1462#endif
1463
1464PHP_INI_BEGIN()
1465	STD_PHP_INI_ENTRY("cgi.rfc2616_headers",     "0",  PHP_INI_ALL,    OnUpdateBool,   rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
1466	STD_PHP_INI_ENTRY("cgi.nph",                 "0",  PHP_INI_ALL,    OnUpdateBool,   nph, php_cgi_globals_struct, php_cgi_globals)
1467	STD_PHP_INI_ENTRY("cgi.check_shebang_line",  "1",  PHP_INI_SYSTEM, OnUpdateBool,   check_shebang_line, php_cgi_globals_struct, php_cgi_globals)
1468	STD_PHP_INI_ENTRY("cgi.force_redirect",      "1",  PHP_INI_SYSTEM, OnUpdateBool,   force_redirect, php_cgi_globals_struct, php_cgi_globals)
1469	STD_PHP_INI_ENTRY("cgi.redirect_status_env", NULL, PHP_INI_SYSTEM, OnUpdateString, redirect_status_env, php_cgi_globals_struct, php_cgi_globals)
1470	STD_PHP_INI_ENTRY("cgi.fix_pathinfo",        "1",  PHP_INI_SYSTEM, OnUpdateBool,   fix_pathinfo, php_cgi_globals_struct, php_cgi_globals)
1471	STD_PHP_INI_ENTRY("cgi.discard_path",        "0",  PHP_INI_SYSTEM, OnUpdateBool,   discard_path, php_cgi_globals_struct, php_cgi_globals)
1472	STD_PHP_INI_ENTRY("fastcgi.logging",         "1",  PHP_INI_SYSTEM, OnUpdateBool,   fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
1473#ifdef PHP_WIN32
1474	STD_PHP_INI_ENTRY("fastcgi.impersonate",     "0",  PHP_INI_SYSTEM, OnUpdateBool,   impersonate, php_cgi_globals_struct, php_cgi_globals)
1475#endif
1476PHP_INI_END()
1477
1478/* {{{ php_cgi_globals_ctor
1479 */
1480static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals)
1481{
1482#ifdef ZTS
1483	ZEND_TSRMLS_CACHE_UPDATE();
1484#endif
1485	php_cgi_globals->rfc2616_headers = 0;
1486	php_cgi_globals->nph = 0;
1487	php_cgi_globals->check_shebang_line = 1;
1488	php_cgi_globals->force_redirect = 1;
1489	php_cgi_globals->redirect_status_env = NULL;
1490	php_cgi_globals->fix_pathinfo = 1;
1491	php_cgi_globals->discard_path = 0;
1492	php_cgi_globals->fcgi_logging = 1;
1493#ifdef PHP_WIN32
1494	php_cgi_globals->impersonate = 0;
1495#endif
1496	zend_hash_init(&php_cgi_globals->user_config_cache, 8, NULL, user_config_cache_entry_dtor, 1);
1497}
1498/* }}} */
1499
1500/* {{{ PHP_MINIT_FUNCTION
1501 */
1502static PHP_MINIT_FUNCTION(cgi)
1503{
1504	REGISTER_INI_ENTRIES();
1505	return SUCCESS;
1506}
1507/* }}} */
1508
1509/* {{{ PHP_MSHUTDOWN_FUNCTION
1510 */
1511static PHP_MSHUTDOWN_FUNCTION(cgi)
1512{
1513	zend_hash_destroy(&CGIG(user_config_cache));
1514
1515	UNREGISTER_INI_ENTRIES();
1516	return SUCCESS;
1517}
1518/* }}} */
1519
1520/* {{{ PHP_MINFO_FUNCTION
1521 */
1522static PHP_MINFO_FUNCTION(cgi)
1523{
1524	DISPLAY_INI_ENTRIES();
1525}
1526/* }}} */
1527
1528PHP_FUNCTION(apache_child_terminate) /* {{{ */
1529{
1530	if (zend_parse_parameters_none()) {
1531		return;
1532	}
1533	if (fcgi_is_fastcgi()) {
1534		fcgi_terminate();
1535	}
1536}
1537/* }}} */
1538
1539static void add_request_header(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg) /* {{{ */
1540{
1541	zval *return_value = (zval*)arg;
1542	char *str = NULL;
1543	char *p;
1544	ALLOCA_FLAG(use_heap)
1545
1546	if (var_len > 5 &&
1547	    var[0] == 'H' &&
1548	    var[1] == 'T' &&
1549	    var[2] == 'T' &&
1550	    var[3] == 'P' &&
1551	    var[4] == '_') {
1552
1553		var_len -= 5;
1554		p = var + 5;
1555		var = str = do_alloca(var_len + 1, use_heap);
1556		*str++ = *p++;
1557		while (*p) {
1558			if (*p == '_') {
1559				*str++ = '-';
1560				p++;
1561				if (*p) {
1562					*str++ = *p++;
1563				}
1564			} else if (*p >= 'A' && *p <= 'Z') {
1565				*str++ = (*p++ - 'A' + 'a');
1566			} else {
1567				*str++ = *p++;
1568			}
1569		}
1570		*str = 0;
1571	} else if (var_len == sizeof("CONTENT_TYPE")-1 &&
1572	           memcmp(var, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1) == 0) {
1573		var = "Content-Type";
1574	} else if (var_len == sizeof("CONTENT_LENGTH")-1 &&
1575	           memcmp(var, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1) == 0) {
1576		var = "Content-Length";
1577	} else {
1578		return;
1579	}
1580	add_assoc_stringl_ex(return_value, var, var_len, val, val_len);
1581	if (str) {
1582		free_alloca(var, use_heap);
1583	}
1584}
1585/* }}} */
1586
1587PHP_FUNCTION(apache_request_headers) /* {{{ */
1588{
1589	if (zend_parse_parameters_none()) {
1590		return;
1591	}
1592	array_init(return_value);
1593	if (fcgi_is_fastcgi()) {
1594		fcgi_request *request = (fcgi_request*) SG(server_context);
1595
1596		fcgi_loadenv(request, add_request_header, return_value);
1597	} else {
1598		char buf[128];
1599		char **env, *p, *q, *var, *val, *t = buf;
1600		size_t alloc_size = sizeof(buf);
1601		zend_ulong var_len;
1602
1603		for (env = environ; env != NULL && *env != NULL; env++) {
1604			val = strchr(*env, '=');
1605			if (!val) {				/* malformed entry? */
1606				continue;
1607			}
1608			var_len = val - *env;
1609			if (var_len >= alloc_size) {
1610				alloc_size = var_len + 64;
1611				t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size));
1612			}
1613			var = *env;
1614			if (var_len > 5 &&
1615			    var[0] == 'H' &&
1616			    var[1] == 'T' &&
1617			    var[2] == 'T' &&
1618			    var[3] == 'P' &&
1619			    var[4] == '_') {
1620
1621				var_len -= 5;
1622
1623				if (var_len >= alloc_size) {
1624					alloc_size = var_len + 64;
1625					t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size));
1626				}
1627				p = var + 5;
1628
1629				var = q = t;
1630				/* First char keep uppercase */
1631				*q++ = *p++;
1632				while (*p) {
1633					if (*p == '=') {
1634						/* End of name */
1635						break;
1636					} else if (*p == '_') {
1637						*q++ = '-';
1638						p++;
1639						/* First char after - keep uppercase */
1640						if (*p && *p!='=') {
1641							*q++ = *p++;
1642						}
1643					} else if (*p >= 'A' && *p <= 'Z') {
1644						/* lowercase */
1645						*q++ = (*p++ - 'A' + 'a');
1646					} else {
1647						*q++ = *p++;
1648					}
1649				}
1650				*q = 0;
1651			} else if (var_len == sizeof("CONTENT_TYPE")-1 &&
1652			           memcmp(var, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1) == 0) {
1653				var = "Content-Type";
1654			} else if (var_len == sizeof("CONTENT_LENGTH")-1 &&
1655			           memcmp(var, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1) == 0) {
1656				var = "Content-Length";
1657			} else {
1658				continue;
1659			}
1660			val++;
1661			add_assoc_string_ex(return_value, var, var_len, val);
1662		}
1663		if (t != buf && t != NULL) {
1664			efree(t);
1665		}
1666	}
1667}
1668/* }}} */
1669
1670static void add_response_header(sapi_header_struct *h, zval *return_value) /* {{{ */
1671{
1672	char *s, *p;
1673	size_t len = 0;
1674	ALLOCA_FLAG(use_heap)
1675
1676	if (h->header_len > 0) {
1677		p = strchr(h->header, ':');
1678		if (NULL != p) {
1679			len = p - h->header;
1680		}
1681		if (len > 0) {
1682			do {
1683				len--;
1684			} while (len != 0 && (h->header[len-1] == ' ' || h->header[len-1] == '\t'));
1685			if (len) {
1686				s = do_alloca(len + 1, use_heap);
1687				memcpy(s, h->header, len);
1688				s[len] = 0;
1689				do {
1690					p++;
1691				} while (*p == ' ' || *p == '\t');
1692				add_assoc_stringl_ex(return_value, s, len, p, h->header_len - (p - h->header));
1693				free_alloca(s, use_heap);
1694			}
1695		}
1696	}
1697}
1698/* }}} */
1699
1700PHP_FUNCTION(apache_response_headers) /* {{{ */
1701{
1702	if (zend_parse_parameters_none() == FAILURE) {
1703		return;
1704	}
1705
1706	array_init(return_value);
1707	zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t)add_response_header, return_value);
1708}
1709/* }}} */
1710
1711ZEND_BEGIN_ARG_INFO(arginfo_no_args, 0)
1712ZEND_END_ARG_INFO()
1713
1714const zend_function_entry cgi_functions[] = {
1715	PHP_FE(apache_child_terminate, arginfo_no_args)
1716	PHP_FE(apache_request_headers, arginfo_no_args)
1717	PHP_FE(apache_response_headers, arginfo_no_args)
1718	PHP_FALIAS(getallheaders, apache_request_headers, arginfo_no_args)
1719	{NULL, NULL, NULL}
1720};
1721
1722static zend_module_entry cgi_module_entry = {
1723	STANDARD_MODULE_HEADER,
1724	"cgi-fcgi",
1725	cgi_functions,
1726	PHP_MINIT(cgi),
1727	PHP_MSHUTDOWN(cgi),
1728	NULL,
1729	NULL,
1730	PHP_MINFO(cgi),
1731	NO_VERSION_YET,
1732	STANDARD_MODULE_PROPERTIES
1733};
1734
1735/* {{{ main
1736 */
1737int main(int argc, char *argv[])
1738{
1739	int free_query_string = 0;
1740	int exit_status = SUCCESS;
1741	int cgi = 0, c, i;
1742	size_t len;
1743	zend_file_handle file_handle;
1744	char *s;
1745
1746	/* temporary locals */
1747	int behavior = PHP_MODE_STANDARD;
1748	int no_headers = 0;
1749	int orig_optind = php_optind;
1750	char *orig_optarg = php_optarg;
1751	char *script_file = NULL;
1752	size_t ini_entries_len = 0;
1753	/* end of temporary locals */
1754
1755	int max_requests = 500;
1756	int requests = 0;
1757	int fastcgi;
1758	char *bindpath = NULL;
1759	int fcgi_fd = 0;
1760	fcgi_request *request = NULL;
1761	int warmup_repeats = 0;
1762	int repeats = 1;
1763	int benchmark = 0;
1764#if HAVE_GETTIMEOFDAY
1765	struct timeval start, end;
1766#else
1767	time_t start, end;
1768#endif
1769#ifndef PHP_WIN32
1770	int status = 0;
1771#endif
1772	char *query_string;
1773	char *decoded_query_string;
1774	int skip_getopt = 0;
1775
1776#if 0 && defined(PHP_DEBUG)
1777	/* IIS is always making things more difficult.  This allows
1778	 * us to stop PHP and attach a debugger before much gets started */
1779	{
1780		char szMessage [256];
1781		wsprintf (szMessage, "Please attach a debugger to the process 0x%X [%d] (%s) and click OK", GetCurrentProcessId(), GetCurrentProcessId(), argv[0]);
1782		MessageBox(NULL, szMessage, "CGI Debug Time!", MB_OK|MB_SERVICE_NOTIFICATION);
1783	}
1784#endif
1785
1786#ifdef HAVE_SIGNAL_H
1787#if defined(SIGPIPE) && defined(SIG_IGN)
1788	signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
1789								that sockets created via fsockopen()
1790								don't kill PHP if the remote site
1791								closes it.  in apache|apxs mode apache
1792								does that for us!  thies@thieso.net
1793								20000419 */
1794#endif
1795#endif
1796
1797#ifdef ZTS
1798	tsrm_startup(1, 1, 0, NULL);
1799	(void)ts_resource(0);
1800	ZEND_TSRMLS_CACHE_UPDATE();
1801#endif
1802
1803#ifdef ZEND_SIGNALS
1804	zend_signal_startup();
1805#endif
1806
1807#ifdef ZTS
1808	ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
1809#else
1810	php_cgi_globals_ctor(&php_cgi_globals);
1811#endif
1812
1813	sapi_startup(&cgi_sapi_module);
1814	fastcgi = fcgi_is_fastcgi();
1815	cgi_sapi_module.php_ini_path_override = NULL;
1816
1817#ifdef PHP_WIN32
1818	_fmode = _O_BINARY; /* sets default for file streams to binary */
1819	setmode(_fileno(stdin),  O_BINARY);	/* make the stdio mode be binary */
1820	setmode(_fileno(stdout), O_BINARY);	/* make the stdio mode be binary */
1821	setmode(_fileno(stderr), O_BINARY);	/* make the stdio mode be binary */
1822#endif
1823
1824	if (!fastcgi) {
1825		/* Make sure we detect we are a cgi - a bit redundancy here,
1826		 * but the default case is that we have to check only the first one. */
1827		if (getenv("SERVER_SOFTWARE") ||
1828			getenv("SERVER_NAME") ||
1829			getenv("GATEWAY_INTERFACE") ||
1830			getenv("REQUEST_METHOD")
1831		) {
1832			cgi = 1;
1833		}
1834	}
1835
1836	if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) {
1837		/* we've got query string that has no = - apache CGI will pass it to command line */
1838		unsigned char *p;
1839		decoded_query_string = strdup(query_string);
1840		php_url_decode(decoded_query_string, strlen(decoded_query_string));
1841		for (p = (unsigned char *)decoded_query_string; *p &&  *p <= ' '; p++) {
1842			/* skip all leading spaces */
1843		}
1844		if(*p == '-') {
1845			skip_getopt = 1;
1846		}
1847		free(decoded_query_string);
1848	}
1849
1850	while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
1851		switch (c) {
1852			case 'c':
1853				if (cgi_sapi_module.php_ini_path_override) {
1854					free(cgi_sapi_module.php_ini_path_override);
1855				}
1856				cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
1857				break;
1858			case 'n':
1859				cgi_sapi_module.php_ini_ignore = 1;
1860				break;
1861			case 'd': {
1862				/* define ini entries on command line */
1863				size_t len = strlen(php_optarg);
1864				char *val;
1865
1866				if ((val = strchr(php_optarg, '='))) {
1867					val++;
1868					if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
1869						cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
1870						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
1871						ini_entries_len += (val - php_optarg);
1872						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"", 1);
1873						ini_entries_len++;
1874						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, val, len - (val - php_optarg));
1875						ini_entries_len += len - (val - php_optarg);
1876						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
1877						ini_entries_len += sizeof("\n\0\"") - 2;
1878					} else {
1879						cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\n\0"));
1880						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
1881						memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
1882						ini_entries_len += len + sizeof("\n\0") - 2;
1883					}
1884				} else {
1885					cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
1886					memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
1887					memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
1888					ini_entries_len += len + sizeof("=1\n\0") - 2;
1889				}
1890				break;
1891			}
1892			/* if we're started on command line, check to see if
1893			 * we are being started as an 'external' fastcgi
1894			 * server by accepting a bindpath parameter. */
1895			case 'b':
1896				if (!fastcgi) {
1897					bindpath = strdup(php_optarg);
1898				}
1899				break;
1900			case 's': /* generate highlighted HTML from source */
1901				behavior = PHP_MODE_HIGHLIGHT;
1902				break;
1903		}
1904	}
1905	php_optind = orig_optind;
1906	php_optarg = orig_optarg;
1907
1908	if (fastcgi || bindpath) {
1909		/* Override SAPI callbacks */
1910		cgi_sapi_module.ub_write     = sapi_fcgi_ub_write;
1911		cgi_sapi_module.flush        = sapi_fcgi_flush;
1912		cgi_sapi_module.read_post    = sapi_fcgi_read_post;
1913		cgi_sapi_module.getenv       = sapi_fcgi_getenv;
1914		cgi_sapi_module.read_cookies = sapi_fcgi_read_cookies;
1915	}
1916
1917#ifdef ZTS
1918	SG(request_info).path_translated = NULL;
1919#endif
1920
1921	cgi_sapi_module.executable_location = argv[0];
1922	if (!cgi && !fastcgi && !bindpath) {
1923		cgi_sapi_module.additional_functions = additional_functions;
1924	}
1925
1926	/* startup after we get the above ini override se we get things right */
1927	if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
1928#ifdef ZTS
1929		tsrm_shutdown();
1930#endif
1931		return FAILURE;
1932	}
1933
1934	/* check force_cgi after startup, so we have proper output */
1935	if (cgi && CGIG(force_redirect)) {
1936		/* Apache will generate REDIRECT_STATUS,
1937		 * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.
1938		 * redirect.so and installation instructions available from
1939		 * http://www.koehntopp.de/php.
1940		 *   -- kk@netuse.de
1941		 */
1942		if (!getenv("REDIRECT_STATUS") &&
1943			!getenv ("HTTP_REDIRECT_STATUS") &&
1944			/* this is to allow a different env var to be configured
1945			 * in case some server does something different than above */
1946			(!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env)))
1947		) {
1948			zend_try {
1949				SG(sapi_headers).http_response_code = 400;
1950				PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\
1951<p>This PHP CGI binary was compiled with force-cgi-redirect enabled.  This\n\
1952means that a page will only be served up if the REDIRECT_STATUS CGI variable is\n\
1953set, e.g. via an Apache Action directive.</p>\n\
1954<p>For more information as to <i>why</i> this behaviour exists, see the <a href=\"http://php.net/security.cgi-bin\">\
1955manual page for CGI security</a>.</p>\n\
1956<p>For more information about changing this behaviour or re-enabling this webserver,\n\
1957consult the installation file that came with this distribution, or visit \n\
1958<a href=\"http://php.net/install.windows\">the manual page</a>.</p>\n");
1959			} zend_catch {
1960			} zend_end_try();
1961#if defined(ZTS) && !defined(PHP_DEBUG)
1962			/* XXX we're crashing here in msvc6 debug builds at
1963			 * php_message_handler_for_zend:839 because
1964			 * SG(request_info).path_translated is an invalid pointer.
1965			 * It still happens even though I set it to null, so something
1966			 * weird is going on.
1967			 */
1968			tsrm_shutdown();
1969#endif
1970			return FAILURE;
1971		}
1972	}
1973
1974#ifndef HAVE_ATTRIBUTE_WEAK
1975	fcgi_set_logger(fcgi_log);
1976#endif
1977
1978	if (bindpath) {
1979		int backlog = 128;
1980		if (getenv("PHP_FCGI_BACKLOG")) {
1981			backlog = atoi(getenv("PHP_FCGI_BACKLOG"));
1982		}
1983		fcgi_fd = fcgi_listen(bindpath, backlog);
1984		if (fcgi_fd < 0) {
1985			fprintf(stderr, "Couldn't create FastCGI listen socket on port %s\n", bindpath);
1986#ifdef ZTS
1987			tsrm_shutdown();
1988#endif
1989			return FAILURE;
1990		}
1991		fastcgi = fcgi_is_fastcgi();
1992	}
1993	if (fastcgi) {
1994		/* How many times to run PHP scripts before dying */
1995		if (getenv("PHP_FCGI_MAX_REQUESTS")) {
1996			max_requests = atoi(getenv("PHP_FCGI_MAX_REQUESTS"));
1997			if (max_requests < 0) {
1998				fprintf(stderr, "PHP_FCGI_MAX_REQUESTS is not valid\n");
1999				return FAILURE;
2000			}
2001		}
2002
2003		/* make php call us to get _ENV vars */
2004		php_php_import_environment_variables = php_import_environment_variables;
2005		php_import_environment_variables = cgi_php_import_environment_variables;
2006
2007		/* library is already initialized, now init our request */
2008		request = fcgi_init_request(fcgi_fd, NULL, NULL, NULL);
2009
2010		/* Pre-fork or spawn, if required */
2011		if (getenv("PHP_FCGI_CHILDREN")) {
2012			char * children_str = getenv("PHP_FCGI_CHILDREN");
2013			children = atoi(children_str);
2014			if (children < 0) {
2015				fprintf(stderr, "PHP_FCGI_CHILDREN is not valid\n");
2016				return FAILURE;
2017			}
2018			fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, children_str, strlen(children_str));
2019			/* This is the number of concurrent requests, equals FCGI_MAX_CONNS */
2020			fcgi_set_mgmt_var("FCGI_MAX_REQS",  sizeof("FCGI_MAX_REQS")-1,  children_str, strlen(children_str));
2021		} else {
2022			fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, "1", sizeof("1")-1);
2023			fcgi_set_mgmt_var("FCGI_MAX_REQS",  sizeof("FCGI_MAX_REQS")-1,  "1", sizeof("1")-1);
2024		}
2025
2026#ifndef PHP_WIN32
2027		if (children) {
2028			int running = 0;
2029			pid_t pid;
2030
2031			/* Create a process group for ourself & children */
2032			setsid();
2033			pgroup = getpgrp();
2034#ifdef DEBUG_FASTCGI
2035			fprintf(stderr, "Process group %d\n", pgroup);
2036#endif
2037
2038			/* Set up handler to kill children upon exit */
2039			act.sa_flags = 0;
2040			act.sa_handler = fastcgi_cleanup;
2041			if (sigaction(SIGTERM, &act, &old_term) ||
2042				sigaction(SIGINT,  &act, &old_int)  ||
2043				sigaction(SIGQUIT, &act, &old_quit)
2044			) {
2045				perror("Can't set signals");
2046				exit(1);
2047			}
2048
2049			if (fcgi_in_shutdown()) {
2050				goto parent_out;
2051			}
2052
2053			while (parent) {
2054				do {
2055#ifdef DEBUG_FASTCGI
2056					fprintf(stderr, "Forking, %d running\n", running);
2057#endif
2058					pid = fork();
2059					switch (pid) {
2060					case 0:
2061						/* One of the children.
2062						 * Make sure we don't go round the
2063						 * fork loop any more
2064						 */
2065						parent = 0;
2066
2067						/* don't catch our signals */
2068						sigaction(SIGTERM, &old_term, 0);
2069						sigaction(SIGQUIT, &old_quit, 0);
2070						sigaction(SIGINT,  &old_int,  0);
2071						break;
2072					case -1:
2073						perror("php (pre-forking)");
2074						exit(1);
2075						break;
2076					default:
2077						/* Fine */
2078						running++;
2079						break;
2080					}
2081				} while (parent && (running < children));
2082
2083				if (parent) {
2084#ifdef DEBUG_FASTCGI
2085					fprintf(stderr, "Wait for kids, pid %d\n", getpid());
2086#endif
2087					parent_waiting = 1;
2088					while (1) {
2089						if (wait(&status) >= 0) {
2090							running--;
2091							break;
2092						} else if (exit_signal) {
2093							break;
2094						}
2095					}
2096					if (exit_signal) {
2097#if 0
2098						while (running > 0) {
2099							while (wait(&status) < 0) {
2100							}
2101							running--;
2102						}
2103#endif
2104						goto parent_out;
2105					}
2106				}
2107			}
2108		} else {
2109			parent = 0;
2110		}
2111
2112#else
2113		if (children) {
2114			char *cmd_line;
2115			char kid_buf[16];
2116			char my_name[MAX_PATH] = {0};
2117			int i;
2118
2119			ZeroMemory(&kid_cgi_ps, sizeof(kid_cgi_ps));
2120			kids = children < WIN32_MAX_SPAWN_CHILDREN ? children : WIN32_MAX_SPAWN_CHILDREN;
2121
2122			SetConsoleCtrlHandler(fastcgi_cleanup, TRUE);
2123
2124			/* kids will inherit the env, don't let them spawn */
2125			SetEnvironmentVariable("PHP_FCGI_CHILDREN", NULL);
2126
2127			GetModuleFileName(NULL, my_name, MAX_PATH);
2128			cmd_line = my_name;
2129
2130			job = CreateJobObject(NULL, NULL);
2131			if (!job) {
2132				DWORD err = GetLastError();
2133				char *err_text = php_win32_error_to_msg(err);
2134
2135				fprintf(stderr, "unable to create job object: [0x%08lx]: %s\n", err, err_text);
2136
2137				goto parent_out;
2138			}
2139
2140			job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
2141			if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof(job_info))) {
2142				DWORD err = GetLastError();
2143				char *err_text = php_win32_error_to_msg(err);
2144
2145				fprintf(stderr, "unable to configure job object: [0x%08lx]: %s\n", err, err_text);
2146			}
2147
2148			while (parent) {
2149				i = kids;
2150				while (0 < i--) {
2151					DWORD status;
2152
2153					if (NULL != kid_cgi_ps[i]) {
2154						if(!GetExitCodeProcess(kid_cgi_ps[i], &status) || status != STILL_ACTIVE) {
2155							CloseHandle(kid_cgi_ps[i]);
2156							kid_cgi_ps[i] = NULL;
2157						}
2158					}
2159				}
2160
2161				i = kids;
2162				while (0 < i--) {
2163					PROCESS_INFORMATION pi;
2164					STARTUPINFO si;
2165
2166					if (NULL != kid_cgi_ps[i]) {
2167						continue;
2168					}
2169
2170					ZeroMemory(&si, sizeof(si));
2171					si.cb = sizeof(si);
2172					ZeroMemory(&pi, sizeof(pi));
2173
2174					si.dwFlags = STARTF_USESTDHANDLES;
2175					si.hStdOutput = INVALID_HANDLE_VALUE;
2176					si.hStdInput  = (HANDLE)_get_osfhandle(fcgi_fd);
2177					si.hStdError  = INVALID_HANDLE_VALUE;
2178
2179					if (CreateProcess(NULL, cmd_line, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
2180						kid_cgi_ps[i] = pi.hProcess;
2181						if (!AssignProcessToJobObject(job, pi.hProcess)) {
2182							DWORD err = GetLastError();
2183							char *err_text = php_win32_error_to_msg(err);
2184
2185							fprintf(stderr, "unable to assign child process to job object: [0x%08lx]: %s\n", err, err_text);
2186						}
2187						CloseHandle(pi.hThread);
2188					} else {
2189						DWORD err = GetLastError();
2190						char *err_text = php_win32_error_to_msg(err);
2191
2192						kid_cgi_ps[i] = NULL;
2193
2194						fprintf(stderr, "unable to spawn: [0x%08lx]: %s\n", err, err_text);
2195					}
2196				}
2197
2198				WaitForMultipleObjects(kids, kid_cgi_ps, FALSE, INFINITE);
2199			}
2200
2201			snprintf(kid_buf, 16, "%d", children);
2202			/* restore my env */
2203			SetEnvironmentVariable("PHP_FCGI_CHILDREN", kid_buf);
2204
2205			goto parent_out;
2206		} else {
2207			parent = 0;
2208		}
2209#endif /* WIN32 */
2210	}
2211
2212	zend_first_try {
2213		while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 1, 2)) != -1) {
2214			switch (c) {
2215				case 'T':
2216					benchmark = 1;
2217					{
2218						char *comma = strchr(php_optarg, ',');
2219						if (comma) {
2220							warmup_repeats = atoi(php_optarg);
2221							repeats = atoi(comma + 1);
2222						} else {
2223							repeats = atoi(php_optarg);
2224						}
2225					}
2226#ifdef HAVE_GETTIMEOFDAY
2227					gettimeofday(&start, NULL);
2228#else
2229					time(&start);
2230#endif
2231					break;
2232				case 'h':
2233				case '?':
2234					if (request) {
2235						fcgi_destroy_request(request);
2236					}
2237					fcgi_shutdown();
2238					no_headers = 1;
2239					SG(headers_sent) = 1;
2240					php_cgi_usage(argv[0]);
2241					php_output_end_all();
2242					exit_status = 0;
2243					goto out;
2244			}
2245		}
2246		php_optind = orig_optind;
2247		php_optarg = orig_optarg;
2248
2249		/* start of FAST CGI loop */
2250		/* Initialise FastCGI request structure */
2251#ifdef PHP_WIN32
2252		/* attempt to set security impersonation for fastcgi
2253		 * will only happen on NT based OS, others will ignore it. */
2254		if (fastcgi && CGIG(impersonate)) {
2255			fcgi_impersonate();
2256		}
2257#endif
2258		while (!fastcgi || fcgi_accept_request(request) >= 0) {
2259			SG(server_context) = fastcgi ? (void *)request : (void *) 1;
2260			init_request_info(request);
2261
2262			if (!cgi && !fastcgi) {
2263				while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
2264					switch (c) {
2265
2266						case 'a':	/* interactive mode */
2267							printf("Interactive mode enabled\n\n");
2268							break;
2269
2270						case 'C': /* don't chdir to the script directory */
2271							SG(options) |= SAPI_OPTION_NO_CHDIR;
2272							break;
2273
2274						case 'e': /* enable extended info output */
2275							CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
2276							break;
2277
2278						case 'f': /* parse file */
2279							if (script_file) {
2280								efree(script_file);
2281							}
2282							script_file = estrdup(php_optarg);
2283							no_headers = 1;
2284							break;
2285
2286						case 'i': /* php info & quit */
2287							if (script_file) {
2288								efree(script_file);
2289							}
2290							if (php_request_startup() == FAILURE) {
2291								SG(server_context) = NULL;
2292								php_module_shutdown();
2293								return FAILURE;
2294							}
2295							if (no_headers) {
2296								SG(headers_sent) = 1;
2297								SG(request_info).no_headers = 1;
2298							}
2299							php_print_info(0xFFFFFFFF);
2300							php_request_shutdown((void *) 0);
2301							fcgi_shutdown();
2302							exit_status = 0;
2303							goto out;
2304
2305						case 'l': /* syntax check mode */
2306							no_headers = 1;
2307							behavior = PHP_MODE_LINT;
2308							break;
2309
2310						case 'm': /* list compiled in modules */
2311							if (script_file) {
2312								efree(script_file);
2313							}
2314							SG(headers_sent) = 1;
2315							php_printf("[PHP Modules]\n");
2316							print_modules();
2317							php_printf("\n[Zend Modules]\n");
2318							print_extensions();
2319							php_printf("\n");
2320							php_output_end_all();
2321							fcgi_shutdown();
2322							exit_status = 0;
2323							goto out;
2324
2325						case 'q': /* do not generate HTTP headers */
2326							no_headers = 1;
2327							break;
2328
2329						case 'v': /* show php version & quit */
2330							if (script_file) {
2331								efree(script_file);
2332							}
2333							no_headers = 1;
2334							if (php_request_startup() == FAILURE) {
2335								SG(server_context) = NULL;
2336								php_module_shutdown();
2337								return FAILURE;
2338							}
2339							if (no_headers) {
2340								SG(headers_sent) = 1;
2341								SG(request_info).no_headers = 1;
2342							}
2343#if ZEND_DEBUG
2344							php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2016 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
2345#else
2346							php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2016 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
2347#endif
2348							php_request_shutdown((void *) 0);
2349							fcgi_shutdown();
2350							exit_status = 0;
2351							goto out;
2352
2353						case 'w':
2354							behavior = PHP_MODE_STRIP;
2355							break;
2356
2357						case 'z': /* load extension file */
2358							zend_load_extension(php_optarg);
2359							break;
2360
2361						default:
2362							break;
2363					}
2364				}
2365
2366				if (script_file) {
2367					/* override path_translated if -f on command line */
2368					if (SG(request_info).path_translated) efree(SG(request_info).path_translated);
2369					SG(request_info).path_translated = script_file;
2370					/* before registering argv to module exchange the *new* argv[0] */
2371					/* we can achieve this without allocating more memory */
2372					SG(request_info).argc = argc - (php_optind - 1);
2373					SG(request_info).argv = &argv[php_optind - 1];
2374					SG(request_info).argv[0] = script_file;
2375				} else if (argc > php_optind) {
2376					/* file is on command line, but not in -f opt */
2377					if (SG(request_info).path_translated) efree(SG(request_info).path_translated);
2378					SG(request_info).path_translated = estrdup(argv[php_optind]);
2379					/* arguments after the file are considered script args */
2380					SG(request_info).argc = argc - php_optind;
2381					SG(request_info).argv = &argv[php_optind];
2382				}
2383
2384				if (no_headers) {
2385					SG(headers_sent) = 1;
2386					SG(request_info).no_headers = 1;
2387				}
2388
2389				/* all remaining arguments are part of the query string
2390				 * this section of code concatenates all remaining arguments
2391				 * into a single string, separating args with a &
2392				 * this allows command lines like:
2393				 *
2394				 *  test.php v1=test v2=hello+world!
2395				 *  test.php "v1=test&v2=hello world!"
2396				 *  test.php v1=test "v2=hello world!"
2397				*/
2398				if (!SG(request_info).query_string && argc > php_optind) {
2399					size_t slen = strlen(PG(arg_separator).input);
2400					len = 0;
2401					for (i = php_optind; i < argc; i++) {
2402						if (i < (argc - 1)) {
2403							len += strlen(argv[i]) + slen;
2404						} else {
2405							len += strlen(argv[i]);
2406						}
2407					}
2408
2409					len += 2;
2410					s = malloc(len);
2411					*s = '\0';			/* we are pretending it came from the environment  */
2412					for (i = php_optind; i < argc; i++) {
2413						strlcat(s, argv[i], len);
2414						if (i < (argc - 1)) {
2415							strlcat(s, PG(arg_separator).input, len);
2416						}
2417					}
2418					SG(request_info).query_string = s;
2419					free_query_string = 1;
2420				}
2421			} /* end !cgi && !fastcgi */
2422
2423			/*
2424				we never take stdin if we're (f)cgi, always
2425				rely on the web server giving us the info
2426				we need in the environment.
2427			*/
2428			if (SG(request_info).path_translated || cgi || fastcgi) {
2429				file_handle.type = ZEND_HANDLE_FILENAME;
2430				file_handle.filename = SG(request_info).path_translated;
2431				file_handle.handle.fp = NULL;
2432			} else {
2433				file_handle.filename = "-";
2434				file_handle.type = ZEND_HANDLE_FP;
2435				file_handle.handle.fp = stdin;
2436			}
2437
2438			file_handle.opened_path = NULL;
2439			file_handle.free_filename = 0;
2440
2441			/* request startup only after we've done all we can to
2442			 * get path_translated */
2443			if (php_request_startup() == FAILURE) {
2444				if (fastcgi) {
2445					fcgi_finish_request(request, 1);
2446				}
2447				SG(server_context) = NULL;
2448				php_module_shutdown();
2449				return FAILURE;
2450			}
2451			if (no_headers) {
2452				SG(headers_sent) = 1;
2453				SG(request_info).no_headers = 1;
2454			}
2455
2456			/*
2457				at this point path_translated will be set if:
2458				1. we are running from shell and got filename was there
2459				2. we are running as cgi or fastcgi
2460			*/
2461			if (cgi || fastcgi || SG(request_info).path_translated) {
2462				if (php_fopen_primary_script(&file_handle) == FAILURE) {
2463					zend_try {
2464						if (errno == EACCES) {
2465							SG(sapi_headers).http_response_code = 403;
2466							PUTS("Access denied.\n");
2467						} else {
2468							SG(sapi_headers).http_response_code = 404;
2469							PUTS("No input file specified.\n");
2470						}
2471					} zend_catch {
2472					} zend_end_try();
2473					/* we want to serve more requests if this is fastcgi
2474					 * so cleanup and continue, request shutdown is
2475					 * handled later */
2476					if (fastcgi) {
2477						goto fastcgi_request_done;
2478					}
2479
2480					if (SG(request_info).path_translated) {
2481						efree(SG(request_info).path_translated);
2482						SG(request_info).path_translated = NULL;
2483					}
2484
2485					if (free_query_string && SG(request_info).query_string) {
2486						free(SG(request_info).query_string);
2487						SG(request_info).query_string = NULL;
2488					}
2489
2490					php_request_shutdown((void *) 0);
2491					SG(server_context) = NULL;
2492					php_module_shutdown();
2493					sapi_shutdown();
2494#ifdef ZTS
2495					tsrm_shutdown();
2496#endif
2497					return FAILURE;
2498				}
2499			}
2500
2501			if (CGIG(check_shebang_line)) {
2502				/* #!php support */
2503				switch (file_handle.type) {
2504					case ZEND_HANDLE_FD:
2505						if (file_handle.handle.fd < 0) {
2506							break;
2507						}
2508						file_handle.type = ZEND_HANDLE_FP;
2509						file_handle.handle.fp = fdopen(file_handle.handle.fd, "rb");
2510						/* break missing intentionally */
2511					case ZEND_HANDLE_FP:
2512						if (!file_handle.handle.fp ||
2513						    (file_handle.handle.fp == stdin)) {
2514							break;
2515						}
2516						c = fgetc(file_handle.handle.fp);
2517						if (c == '#') {
2518							while (c != '\n' && c != '\r' && c != EOF) {
2519								c = fgetc(file_handle.handle.fp);	/* skip to end of line */
2520							}
2521							/* handle situations where line is terminated by \r\n */
2522							if (c == '\r') {
2523								if (fgetc(file_handle.handle.fp) != '\n') {
2524									zend_long pos = zend_ftell(file_handle.handle.fp);
2525									zend_fseek(file_handle.handle.fp, pos - 1, SEEK_SET);
2526								}
2527							}
2528							CG(start_lineno) = 2;
2529						} else {
2530							rewind(file_handle.handle.fp);
2531						}
2532						break;
2533					case ZEND_HANDLE_STREAM:
2534						c = php_stream_getc((php_stream*)file_handle.handle.stream.handle);
2535						if (c == '#') {
2536							while (c != '\n' && c != '\r' && c != EOF) {
2537								c = php_stream_getc((php_stream*)file_handle.handle.stream.handle);	/* skip to end of line */
2538							}
2539							/* handle situations where line is terminated by \r\n */
2540							if (c == '\r') {
2541								if (php_stream_getc((php_stream*)file_handle.handle.stream.handle) != '\n') {
2542									zend_off_t pos = php_stream_tell((php_stream*)file_handle.handle.stream.handle);
2543									php_stream_seek((php_stream*)file_handle.handle.stream.handle, pos - 1, SEEK_SET);
2544								}
2545							}
2546							CG(start_lineno) = 2;
2547						} else {
2548							php_stream_rewind((php_stream*)file_handle.handle.stream.handle);
2549						}
2550						break;
2551					case ZEND_HANDLE_MAPPED:
2552						if (file_handle.handle.stream.mmap.buf[0] == '#') {
2553						    size_t i = 1;
2554
2555						    c = file_handle.handle.stream.mmap.buf[i++];
2556							while (c != '\n' && c != '\r' && i < file_handle.handle.stream.mmap.len) {
2557								c = file_handle.handle.stream.mmap.buf[i++];
2558							}
2559							if (c == '\r') {
2560								if (i < file_handle.handle.stream.mmap.len && file_handle.handle.stream.mmap.buf[i] == '\n') {
2561									i++;
2562								}
2563							}
2564							if(i > file_handle.handle.stream.mmap.len) {
2565								i = file_handle.handle.stream.mmap.len;
2566							}
2567							file_handle.handle.stream.mmap.buf += i;
2568							file_handle.handle.stream.mmap.len -= i;
2569						}
2570						break;
2571					default:
2572						break;
2573				}
2574			}
2575
2576			switch (behavior) {
2577				case PHP_MODE_STANDARD:
2578					php_execute_script(&file_handle);
2579					break;
2580				case PHP_MODE_LINT:
2581					PG(during_request_startup) = 0;
2582					exit_status = php_lint_script(&file_handle);
2583					if (exit_status == SUCCESS) {
2584						zend_printf("No syntax errors detected in %s\n", file_handle.filename);
2585					} else {
2586						zend_printf("Errors parsing %s\n", file_handle.filename);
2587					}
2588					break;
2589				case PHP_MODE_STRIP:
2590					if (open_file_for_scanning(&file_handle) == SUCCESS) {
2591						zend_strip();
2592						zend_file_handle_dtor(&file_handle);
2593						php_output_teardown();
2594					}
2595					return SUCCESS;
2596					break;
2597				case PHP_MODE_HIGHLIGHT:
2598					{
2599						zend_syntax_highlighter_ini syntax_highlighter_ini;
2600
2601						if (open_file_for_scanning(&file_handle) == SUCCESS) {
2602							php_get_highlight_struct(&syntax_highlighter_ini);
2603							zend_highlight(&syntax_highlighter_ini);
2604							if (fastcgi) {
2605								goto fastcgi_request_done;
2606							}
2607							zend_file_handle_dtor(&file_handle);
2608							php_output_teardown();
2609						}
2610						return SUCCESS;
2611					}
2612					break;
2613			}
2614
2615fastcgi_request_done:
2616			{
2617				if (SG(request_info).path_translated) {
2618					efree(SG(request_info).path_translated);
2619					SG(request_info).path_translated = NULL;
2620				}
2621
2622				php_request_shutdown((void *) 0);
2623
2624				if (exit_status == 0) {
2625					exit_status = EG(exit_status);
2626				}
2627
2628				if (free_query_string && SG(request_info).query_string) {
2629					free(SG(request_info).query_string);
2630					SG(request_info).query_string = NULL;
2631				}
2632			}
2633
2634			if (!fastcgi) {
2635				if (benchmark) {
2636					if (warmup_repeats) {
2637						warmup_repeats--;
2638						if (!warmup_repeats) {
2639#ifdef HAVE_GETTIMEOFDAY
2640							gettimeofday(&start, NULL);
2641#else
2642							time(&start);
2643#endif
2644						}
2645						continue;
2646					} else {
2647						repeats--;
2648						if (repeats > 0) {
2649							script_file = NULL;
2650							php_optind = orig_optind;
2651							php_optarg = orig_optarg;
2652							continue;
2653						}
2654					}
2655				}
2656				break;
2657			}
2658
2659			/* only fastcgi will get here */
2660			requests++;
2661			if (max_requests && (requests == max_requests)) {
2662				fcgi_finish_request(request, 1);
2663				if (bindpath) {
2664					free(bindpath);
2665				}
2666				if (max_requests != 1) {
2667					/* no need to return exit_status of the last request */
2668					exit_status = 0;
2669				}
2670				break;
2671			}
2672			/* end of fastcgi loop */
2673		}
2674
2675		if (request) {
2676			fcgi_destroy_request(request);
2677		}
2678		fcgi_shutdown();
2679
2680		if (cgi_sapi_module.php_ini_path_override) {
2681			free(cgi_sapi_module.php_ini_path_override);
2682		}
2683		if (cgi_sapi_module.ini_entries) {
2684			free(cgi_sapi_module.ini_entries);
2685		}
2686	} zend_catch {
2687		exit_status = 255;
2688	} zend_end_try();
2689
2690out:
2691	if (benchmark) {
2692		int sec;
2693#ifdef HAVE_GETTIMEOFDAY
2694		int usec;
2695
2696		gettimeofday(&end, NULL);
2697		sec = (int)(end.tv_sec - start.tv_sec);
2698		if (end.tv_usec >= start.tv_usec) {
2699			usec = (int)(end.tv_usec - start.tv_usec);
2700		} else {
2701			sec -= 1;
2702			usec = (int)(end.tv_usec + 1000000 - start.tv_usec);
2703		}
2704		fprintf(stderr, "\nElapsed time: %d.%06d sec\n", sec, usec);
2705#else
2706		time(&end);
2707		sec = (int)(end - start);
2708		fprintf(stderr, "\nElapsed time: %d sec\n", sec);
2709#endif
2710	}
2711
2712parent_out:
2713
2714	SG(server_context) = NULL;
2715	php_module_shutdown();
2716	sapi_shutdown();
2717
2718#ifdef ZTS
2719	tsrm_shutdown();
2720#endif
2721
2722#if defined(PHP_WIN32) && ZEND_DEBUG && 0
2723	_CrtDumpMemoryLeaks();
2724#endif
2725
2726	return exit_status;
2727}
2728/* }}} */
2729
2730/*
2731 * Local variables:
2732 * tab-width: 4
2733 * c-basic-offset: 4
2734 * End:
2735 * vim600: sw=4 ts=4 fdm=marker
2736 * vim<600: sw=4 ts=4
2737 */
2738