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