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