xref: /PHP-7.4/sapi/cgi/cgi_main.c (revision a054ef2a)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca>                       |
16    |          Stig Bakken <ssb@php.net>                                   |
17    |          Zeev Suraski <zeev@php.net>                                 |
18    | FastCGI: Ben Mansell <php@slimyhorror.com>                           |
19    |          Shane Caraveo <shane@caraveo.com>                           |
20    |          Dmitry Stogov <dmitry@php.net>                              |
21    +----------------------------------------------------------------------+
22 */
23 
24 #include "php.h"
25 #include "php_globals.h"
26 #include "php_variables.h"
27 #include "zend_modules.h"
28 
29 #include "SAPI.h"
30 
31 #include <stdio.h>
32 
33 #ifdef PHP_WIN32
34 # include "win32/time.h"
35 # include "win32/signal.h"
36 # include "win32/winutil.h"
37 # include <process.h>
38 #endif
39 
40 #if HAVE_SYS_TIME_H
41 # include <sys/time.h>
42 #endif
43 
44 #if HAVE_UNISTD_H
45 # include <unistd.h>
46 #endif
47 
48 #include <signal.h>
49 
50 #include <locale.h>
51 
52 #if HAVE_SYS_TYPES_H
53 # include <sys/types.h>
54 #endif
55 
56 #if HAVE_SYS_WAIT_H
57 # include <sys/wait.h>
58 #endif
59 
60 #include "zend.h"
61 #include "zend_extensions.h"
62 #include "php_ini.h"
63 #include "php_globals.h"
64 #include "php_main.h"
65 #include "fopen_wrappers.h"
66 #include "http_status_codes.h"
67 #include "ext/standard/php_standard.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 
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(const void * a,const void * b)243 static int module_name_cmp(const void *a, const void *b)
244 {
245 	Bucket *f = (Bucket *) a;
246 	Bucket *s = (Bucket *) b;
247 
248 	return strcasecmp(	((zend_module_entry *)Z_PTR(f->val))->name,
249 						((zend_module_entry *)Z_PTR(s->val))->name);
250 }
251 
print_modules(void)252 static void print_modules(void)
253 {
254 	HashTable sorted_registry;
255 	zend_module_entry *module;
256 
257 	zend_hash_init(&sorted_registry, 64, NULL, NULL, 1);
258 	zend_hash_copy(&sorted_registry, &module_registry, NULL);
259 	zend_hash_sort(&sorted_registry, module_name_cmp, 0);
260 	ZEND_HASH_FOREACH_PTR(&sorted_registry, module) {
261 		php_printf("%s\n", module->name);
262 	} ZEND_HASH_FOREACH_END();
263 	zend_hash_destroy(&sorted_registry);
264 }
265 
print_extension_info(zend_extension * ext,void * arg)266 static int print_extension_info(zend_extension *ext, void *arg)
267 {
268 	php_printf("%s\n", ext->name);
269 	return 0;
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_with_argument(&sorted_exts, (llist_apply_with_arg_func_t) print_extension_info, NULL);
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 (
362 		!parent &&
363 		request && !fcgi_flush(request, 0)) {
364 
365 		php_handle_aborted_connection();
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 	zend_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 		zend_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 > UINT_MAX) ? UINT_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(char * name,size_t name_len)563 static char *sapi_cgi_getenv(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(char * name,size_t name_len)572 static char *sapi_fcgi_getenv(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 !HAVE_SETENV || !HAVE_UNSETENV
602 	size_t len;
603 	char *buf;
604 #endif
605 
606 #if HAVE_SETENV
607 	if (value) {
608 		setenv(name, value, 1);
609 	}
610 #endif
611 #if HAVE_UNSETENV
612 	if (!value) {
613 		unsetenv(name);
614 	}
615 #endif
616 
617 #if !HAVE_SETENV || !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 !HAVE_SETENV
630 	if (value) {
631 		len = slprintf(buf, len - 1, "%s=%s", name, value);
632 		putenv(buf);
633 	}
634 #endif
635 #if !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(char * var,unsigned int var_len,char * val,unsigned int val_len,void * arg)656 static void cgi_php_load_env_var(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_str("_ENV", sizeof("_ENV")-1);
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(char * message,int syslog_type_int)757 static void sapi_cgi_log_message(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
784  */
php_cgi_ini_activate_user_config(char * path,size_t path_len,const char * doc_root,size_t doc_root_len)785 static void php_cgi_ini_activate_user_config(char *path, size_t path_len, const char *doc_root, size_t doc_root_len)
786 {
787 	user_config_cache_entry *new_entry, *entry;
788 	time_t request_time = (time_t)sapi_get_request_time();
789 
790 	/* Find cached config entry: If not found, create one */
791 	if ((entry = zend_hash_str_find_ptr(&CGIG(user_config_cache), path, path_len)) == NULL) {
792 		new_entry = pemalloc(sizeof(user_config_cache_entry), 1);
793 		new_entry->expires = 0;
794 		new_entry->user_config = (HashTable *) pemalloc(sizeof(HashTable), 1);
795 		zend_hash_init(new_entry->user_config, 8, NULL, (dtor_func_t) config_zval_dtor, 1);
796 		entry = zend_hash_str_update_ptr(&CGIG(user_config_cache), path, path_len, new_entry);
797 	}
798 
799 	/* Check whether cache entry has expired and rescan if it is */
800 	if (request_time > entry->expires) {
801 		char *real_path = NULL;
802 		char *s1, *s2;
803 		size_t s_len;
804 
805 		/* Clear the expired config */
806 		zend_hash_clean(entry->user_config);
807 
808 		if (!IS_ABSOLUTE_PATH(path, path_len)) {
809 			size_t real_path_len;
810 			real_path = tsrm_realpath(path, NULL);
811 			if (real_path == NULL) {
812 				return;
813 			}
814 			real_path_len = strlen(real_path);
815 			path = real_path;
816 			path_len = real_path_len;
817 		}
818 
819 		if (path_len > doc_root_len) {
820 			s1 = (char *) doc_root;
821 			s2 = path;
822 			s_len = doc_root_len;
823 		} else {
824 			s1 = path;
825 			s2 = (char *) doc_root;
826 			s_len = path_len;
827 		}
828 
829 		/* we have to test if path is part of DOCUMENT_ROOT.
830 		  if it is inside the docroot, we scan the tree up to the docroot
831 			to find more user.ini, if not we only scan the current path.
832 		  */
833 #ifdef PHP_WIN32
834 		if (strnicmp(s1, s2, s_len) == 0) {
835 #else
836 		if (strncmp(s1, s2, s_len) == 0) {
837 #endif
838 			char *ptr = s2 + doc_root_len;
839 #ifdef PHP_WIN32
840 			while ((ptr = strpbrk(ptr, "\\/")) != NULL) {
841 #else
842 			while ((ptr = strchr(ptr, DEFAULT_SLASH)) != NULL) {
843 #endif
844 				*ptr = 0;
845 				php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config);
846 				*ptr = '/';
847 				ptr++;
848 			}
849 		} else {
850 			php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config);
851 		}
852 
853 		if (real_path) {
854 			efree(real_path);
855 		}
856 		entry->expires = request_time + PG(user_ini_cache_ttl);
857 	}
858 
859 	/* Activate ini entries with values from the user config hash */
860 	php_ini_activate_config(entry->user_config, PHP_INI_PERDIR, PHP_INI_STAGE_HTACCESS);
861 }
862 /* }}} */
863 
864 static int sapi_cgi_activate(void)
865 {
866 	/* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */
867 	if (!SG(request_info).path_translated) {
868 		return FAILURE;
869 	}
870 
871 	if (php_ini_has_per_host_config()) {
872 		char *server_name;
873 
874 		/* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */
875 		if (fcgi_is_fastcgi()) {
876 			fcgi_request *request = (fcgi_request*) SG(server_context);
877 
878 			server_name = FCGI_GETENV(request, "SERVER_NAME");
879 		} else {
880 			server_name = getenv("SERVER_NAME");
881 		}
882 		/* SERVER_NAME should also be defined at this stage..but better check it anyway */
883 		if (server_name) {
884 			size_t server_name_len = strlen(server_name);
885 			server_name = estrndup(server_name, server_name_len);
886 			zend_str_tolower(server_name, server_name_len);
887 			php_ini_activate_per_host_config(server_name, server_name_len);
888 			efree(server_name);
889 		}
890 	}
891 
892 	if (php_ini_has_per_dir_config() ||
893 		(PG(user_ini_filename) && *PG(user_ini_filename))
894 	) {
895 		char *path;
896 		size_t path_len;
897 
898 		/* Prepare search path */
899 		path_len = strlen(SG(request_info).path_translated);
900 
901 		/* Make sure we have trailing slash! */
902 		if (!IS_SLASH(SG(request_info).path_translated[path_len])) {
903 			path = emalloc(path_len + 2);
904 			memcpy(path, SG(request_info).path_translated, path_len + 1);
905 			path_len = zend_dirname(path, path_len);
906 			path[path_len++] = DEFAULT_SLASH;
907 		} else {
908 			path = estrndup(SG(request_info).path_translated, path_len);
909 			path_len = zend_dirname(path, path_len);
910 		}
911 		path[path_len] = 0;
912 
913 		/* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */
914 		php_ini_activate_per_dir_config(path, path_len); /* Note: for global settings sake we check from root to path */
915 
916 		/* Load and activate user ini files in path starting from DOCUMENT_ROOT */
917 		if (PG(user_ini_filename) && *PG(user_ini_filename)) {
918 			char *doc_root;
919 
920 			if (fcgi_is_fastcgi()) {
921 				fcgi_request *request = (fcgi_request*) SG(server_context);
922 
923 				doc_root = FCGI_GETENV(request, "DOCUMENT_ROOT");
924 			} else {
925 				doc_root = getenv("DOCUMENT_ROOT");
926 			}
927 			/* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */
928 			if (doc_root) {
929 				size_t doc_root_len = strlen(doc_root);
930 				if (doc_root_len > 0 && IS_SLASH(doc_root[doc_root_len - 1])) {
931 					--doc_root_len;
932 				}
933 #ifdef PHP_WIN32
934 				/* paths on windows should be case-insensitive */
935 				doc_root = estrndup(doc_root, doc_root_len);
936 				zend_str_tolower(doc_root, doc_root_len);
937 #endif
938 				php_cgi_ini_activate_user_config(path, path_len, doc_root, doc_root_len);
939 
940 #ifdef PHP_WIN32
941 				efree(doc_root);
942 #endif
943 			}
944 		}
945 
946 		efree(path);
947 	}
948 
949 	return SUCCESS;
950 }
951 
952 static int sapi_cgi_deactivate(void)
953 {
954 	/* flush only when SAPI was started. The reasons are:
955 		1. SAPI Deactivate is called from two places: module init and request shutdown
956 		2. When the first call occurs and the request is not set up, flush fails on FastCGI.
957 	*/
958 	if (SG(sapi_started)) {
959 		if (fcgi_is_fastcgi()) {
960 			if (
961 				!parent &&
962 				!fcgi_finish_request((fcgi_request*)SG(server_context), 0)) {
963 				php_handle_aborted_connection();
964 			}
965 		} else {
966 			sapi_cgi_flush(SG(server_context));
967 		}
968 	}
969 	return SUCCESS;
970 }
971 
972 static int php_cgi_startup(sapi_module_struct *sapi_module)
973 {
974 	if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) {
975 		return FAILURE;
976 	}
977 	return SUCCESS;
978 }
979 
980 /* {{{ sapi_module_struct cgi_sapi_module
981  */
982 static sapi_module_struct cgi_sapi_module = {
983 	"cgi-fcgi",						/* name */
984 	"CGI/FastCGI",					/* pretty name */
985 
986 	php_cgi_startup,				/* startup */
987 	php_module_shutdown_wrapper,	/* shutdown */
988 
989 	sapi_cgi_activate,				/* activate */
990 	sapi_cgi_deactivate,			/* deactivate */
991 
992 	sapi_cgi_ub_write,				/* unbuffered write */
993 	sapi_cgi_flush,					/* flush */
994 	NULL,							/* get uid */
995 	sapi_cgi_getenv,				/* getenv */
996 
997 	php_error,						/* error handler */
998 
999 	NULL,							/* header handler */
1000 	sapi_cgi_send_headers,			/* send headers handler */
1001 	NULL,							/* send header handler */
1002 
1003 	sapi_cgi_read_post,				/* read POST data */
1004 	sapi_cgi_read_cookies,			/* read Cookies */
1005 
1006 	sapi_cgi_register_variables,	/* register server variables */
1007 	sapi_cgi_log_message,			/* Log message */
1008 	NULL,							/* Get request time */
1009 	NULL,							/* Child terminate */
1010 
1011 	STANDARD_SAPI_MODULE_PROPERTIES
1012 };
1013 /* }}} */
1014 
1015 /* {{{ arginfo ext/standard/dl.c */
1016 ZEND_BEGIN_ARG_INFO(arginfo_dl, 0)
1017 	ZEND_ARG_INFO(0, extension_filename)
1018 ZEND_END_ARG_INFO()
1019 /* }}} */
1020 
1021 static const zend_function_entry additional_functions[] = {
1022 	ZEND_FE(dl, arginfo_dl)
1023 	PHP_FE_END
1024 };
1025 
1026 /* {{{ php_cgi_usage
1027  */
1028 static void php_cgi_usage(char *argv0)
1029 {
1030 	char *prog;
1031 
1032 	prog = strrchr(argv0, '/');
1033 	if (prog) {
1034 		prog++;
1035 	} else {
1036 		prog = "php";
1037 	}
1038 
1039 	php_printf(	"Usage: %s [-q] [-h] [-s] [-v] [-i] [-f <file>]\n"
1040 				"       %s <file> [args...]\n"
1041 				"  -a               Run interactively\n"
1042 				"  -b <address:port>|<port> Bind Path for external FASTCGI Server mode\n"
1043 				"  -C               Do not chdir to the script's directory\n"
1044 				"  -c <path>|<file> Look for php.ini file in this directory\n"
1045 				"  -n               No php.ini file will be used\n"
1046 				"  -d foo[=bar]     Define INI entry foo with value 'bar'\n"
1047 				"  -e               Generate extended information for debugger/profiler\n"
1048 				"  -f <file>        Parse <file>.  Implies `-q'\n"
1049 				"  -h               This help\n"
1050 				"  -i               PHP information\n"
1051 				"  -l               Syntax check only (lint)\n"
1052 				"  -m               Show compiled in modules\n"
1053 				"  -q               Quiet-mode.  Suppress HTTP Header output.\n"
1054 				"  -s               Display colour syntax highlighted source.\n"
1055 				"  -v               Version number\n"
1056 				"  -w               Display source with stripped comments and whitespace.\n"
1057 				"  -z <file>        Load Zend extension <file>.\n"
1058 				"  -T <count>       Measure execution time of script repeated <count> times.\n",
1059 				prog, prog);
1060 }
1061 /* }}} */
1062 
1063 /* {{{ is_valid_path
1064  *
1065  * some server configurations allow '..' to slip through in the
1066  * translated path.   We'll just refuse to handle such a path.
1067  */
1068 static int is_valid_path(const char *path)
1069 {
1070 	const char *p = path;
1071 
1072 	if (UNEXPECTED(!p)) {
1073 		return 0;
1074 	}
1075 	if (UNEXPECTED(*p == '.') && *(p+1) == '.' && (!*(p+2) || IS_SLASH(*(p+2)))) {
1076 		return 0;
1077 	}
1078 	while (*p) {
1079 		if (IS_SLASH(*p)) {
1080 			p++;
1081 			if (UNEXPECTED(*p == '.')) {
1082 				p++;
1083 				if (UNEXPECTED(*p == '.')) {
1084 					p++;
1085 					if (UNEXPECTED(!*p) || UNEXPECTED(IS_SLASH(*p))) {
1086 						return 0;
1087 					}
1088 				}
1089 			}
1090 		}
1091 		p++;
1092 	}
1093 	return 1;
1094 }
1095 /* }}} */
1096 
1097 #define CGI_GETENV(name) \
1098 	((has_env) ? \
1099 		FCGI_GETENV(request, name) : \
1100     	getenv(name))
1101 
1102 #define CGI_PUTENV(name, value) \
1103 	((has_env) ? \
1104 		FCGI_PUTENV(request, name, value) : \
1105 		_sapi_cgi_putenv(name, sizeof(name)-1, value))
1106 
1107 /* {{{ init_request_info
1108 
1109   initializes request_info structure
1110 
1111   specificly in this section we handle proper translations
1112   for:
1113 
1114   PATH_INFO
1115 	derived from the portion of the URI path following
1116 	the script name but preceding any query data
1117 	may be empty
1118 
1119   PATH_TRANSLATED
1120     derived by taking any path-info component of the
1121 	request URI and performing any virtual-to-physical
1122 	translation appropriate to map it onto the server's
1123 	document repository structure
1124 
1125 	empty if PATH_INFO is empty
1126 
1127 	The env var PATH_TRANSLATED **IS DIFFERENT** than the
1128 	request_info.path_translated variable, the latter should
1129 	match SCRIPT_FILENAME instead.
1130 
1131   SCRIPT_NAME
1132     set to a URL path that could identify the CGI script
1133 	rather than the interpreter.  PHP_SELF is set to this
1134 
1135   REQUEST_URI
1136     uri section following the domain:port part of a URI
1137 
1138   SCRIPT_FILENAME
1139     The virtual-to-physical translation of SCRIPT_NAME (as per
1140 	PATH_TRANSLATED)
1141 
1142   These settings are documented at
1143   http://cgi-spec.golux.com/
1144 
1145 
1146   Based on the following URL request:
1147 
1148   http://localhost/info.php/test?a=b
1149 
1150   should produce, which btw is the same as if
1151   we were running under mod_cgi on apache (ie. not
1152   using ScriptAlias directives):
1153 
1154   PATH_INFO=/test
1155   PATH_TRANSLATED=/docroot/test
1156   SCRIPT_NAME=/info.php
1157   REQUEST_URI=/info.php/test?a=b
1158   SCRIPT_FILENAME=/docroot/info.php
1159   QUERY_STRING=a=b
1160 
1161   but what we get is (cgi/mod_fastcgi under apache):
1162 
1163   PATH_INFO=/info.php/test
1164   PATH_TRANSLATED=/docroot/info.php/test
1165   SCRIPT_NAME=/php/php-cgi  (from the Action setting I suppose)
1166   REQUEST_URI=/info.php/test?a=b
1167   SCRIPT_FILENAME=/path/to/php/bin/php-cgi  (Action setting translated)
1168   QUERY_STRING=a=b
1169 
1170   Comments in the code below refer to using the above URL in a request
1171 
1172  */
1173 static void init_request_info(fcgi_request *request)
1174 {
1175 	int has_env = fcgi_has_env(request);
1176 	char *env_script_filename = CGI_GETENV("SCRIPT_FILENAME");
1177 	char *env_path_translated = CGI_GETENV("PATH_TRANSLATED");
1178 	char *script_path_translated = env_script_filename;
1179 
1180 	/* some broken servers do not have script_filename or argv0
1181 	 * an example, IIS configured in some ways.  then they do more
1182 	 * broken stuff and set path_translated to the cgi script location */
1183 	if (!script_path_translated && env_path_translated) {
1184 		script_path_translated = env_path_translated;
1185 	}
1186 
1187 	/* initialize the defaults */
1188 	SG(request_info).path_translated = NULL;
1189 	SG(request_info).request_method = NULL;
1190 	SG(request_info).proto_num = 1000;
1191 	SG(request_info).query_string = NULL;
1192 	SG(request_info).request_uri = NULL;
1193 	SG(request_info).content_type = NULL;
1194 	SG(request_info).content_length = 0;
1195 	SG(sapi_headers).http_response_code = 200;
1196 
1197 	/* script_path_translated being set is a good indication that
1198 	 * we are running in a cgi environment, since it is always
1199 	 * null otherwise.  otherwise, the filename
1200 	 * of the script will be retrieved later via argc/argv */
1201 	if (script_path_translated) {
1202 		const char *auth;
1203 		char *content_length = CGI_GETENV("CONTENT_LENGTH");
1204 		char *content_type = CGI_GETENV("CONTENT_TYPE");
1205 		char *env_path_info = CGI_GETENV("PATH_INFO");
1206 		char *env_script_name = CGI_GETENV("SCRIPT_NAME");
1207 
1208 #ifdef PHP_WIN32
1209 		/* Hack for buggy IIS that sets incorrect PATH_INFO */
1210 		char *env_server_software = CGI_GETENV("SERVER_SOFTWARE");
1211 
1212 		if (env_server_software &&
1213 			env_script_name &&
1214 			env_path_info &&
1215 			strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS")-1) == 0 &&
1216 			strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0
1217 		) {
1218 			env_path_info = CGI_PUTENV("ORIG_PATH_INFO", env_path_info);
1219 			env_path_info += strlen(env_script_name);
1220 			if (*env_path_info == 0) {
1221 				env_path_info = NULL;
1222 			}
1223 			env_path_info = CGI_PUTENV("PATH_INFO", env_path_info);
1224 		}
1225 #endif
1226 
1227 		if (CGIG(fix_pathinfo)) {
1228 			zend_stat_t st;
1229 			char *real_path = NULL;
1230 			char *env_redirect_url = CGI_GETENV("REDIRECT_URL");
1231 			char *env_document_root = CGI_GETENV("DOCUMENT_ROOT");
1232 			char *orig_path_translated = env_path_translated;
1233 			char *orig_path_info = env_path_info;
1234 			char *orig_script_name = env_script_name;
1235 			char *orig_script_filename = env_script_filename;
1236 			size_t script_path_translated_len;
1237 
1238 			if (!env_document_root && PG(doc_root)) {
1239 				env_document_root = CGI_PUTENV("DOCUMENT_ROOT", PG(doc_root));
1240 				/* fix docroot */
1241 				TRANSLATE_SLASHES(env_document_root);
1242 			}
1243 
1244 			if (env_path_translated != NULL && env_redirect_url != NULL &&
1245  			    env_path_translated != script_path_translated &&
1246  			    strcmp(env_path_translated, script_path_translated) != 0) {
1247 				/*
1248 				 * pretty much apache specific.  If we have a redirect_url
1249 				 * then our script_filename and script_name point to the
1250 				 * php executable
1251 				 */
1252 				script_path_translated = env_path_translated;
1253 				/* we correct SCRIPT_NAME now in case we don't have PATH_INFO */
1254 				env_script_name = env_redirect_url;
1255 			}
1256 
1257 #ifdef __riscos__
1258 			/* Convert path to unix format*/
1259 			__riscosify_control |= __RISCOSIFY_DONT_CHECK_DIR;
1260 			script_path_translated = __unixify(script_path_translated, 0, NULL, 1, 0);
1261 #endif
1262 
1263 			/*
1264 			 * if the file doesn't exist, try to extract PATH_INFO out
1265 			 * of it by stat'ing back through the '/'
1266 			 * this fixes url's like /info.php/test
1267 			 */
1268 			if (script_path_translated &&
1269 				(script_path_translated_len = strlen(script_path_translated)) > 0 &&
1270 				(script_path_translated[script_path_translated_len-1] == '/' ||
1271 #ifdef PHP_WIN32
1272 				script_path_translated[script_path_translated_len-1] == '\\' ||
1273 #endif
1274 				(real_path = tsrm_realpath(script_path_translated, NULL)) == NULL)
1275 			) {
1276 				char *pt = estrndup(script_path_translated, script_path_translated_len);
1277 				size_t len = script_path_translated_len;
1278 				char *ptr;
1279 
1280 				while ((ptr = strrchr(pt, '/')) || (ptr = strrchr(pt, '\\'))) {
1281 					*ptr = 0;
1282 					if (zend_stat(pt, &st) == 0 && S_ISREG(st.st_mode)) {
1283 						/*
1284 						 * okay, we found the base script!
1285 						 * work out how many chars we had to strip off;
1286 						 * then we can modify PATH_INFO
1287 						 * accordingly
1288 						 *
1289 						 * we now have the makings of
1290 						 * PATH_INFO=/test
1291 						 * SCRIPT_FILENAME=/docroot/info.php
1292 						 *
1293 						 * we now need to figure out what docroot is.
1294 						 * if DOCUMENT_ROOT is set, this is easy, otherwise,
1295 						 * we have to play the game of hide and seek to figure
1296 						 * out what SCRIPT_NAME should be
1297 						 */
1298 						size_t slen = len - strlen(pt);
1299 						size_t pilen = env_path_info ? strlen(env_path_info) : 0;
1300 						char *path_info = env_path_info ? env_path_info + pilen - slen : NULL;
1301 
1302 						if (orig_path_info != path_info) {
1303 							if (orig_path_info) {
1304 								char old;
1305 
1306 								CGI_PUTENV("ORIG_PATH_INFO", orig_path_info);
1307 								old = path_info[0];
1308 								path_info[0] = 0;
1309 								if (!orig_script_name ||
1310 									strcmp(orig_script_name, env_path_info) != 0) {
1311 									if (orig_script_name) {
1312 										CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
1313 									}
1314 									SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_path_info);
1315 								} else {
1316 									SG(request_info).request_uri = orig_script_name;
1317 								}
1318 								path_info[0] = old;
1319 							}
1320 							env_path_info = CGI_PUTENV("PATH_INFO", path_info);
1321 						}
1322 						if (!orig_script_filename ||
1323 							strcmp(orig_script_filename, pt) != 0) {
1324 							if (orig_script_filename) {
1325 								CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
1326 							}
1327 							script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", pt);
1328 						}
1329 						TRANSLATE_SLASHES(pt);
1330 
1331 						/* figure out docroot
1332 						 * SCRIPT_FILENAME minus SCRIPT_NAME
1333 						 */
1334 						if (env_document_root) {
1335 							size_t l = strlen(env_document_root);
1336 							size_t path_translated_len = 0;
1337 							char *path_translated = NULL;
1338 
1339 							if (l && env_document_root[l - 1] == '/') {
1340 								--l;
1341 							}
1342 
1343 							/* we have docroot, so we should have:
1344 							 * DOCUMENT_ROOT=/docroot
1345 							 * SCRIPT_FILENAME=/docroot/info.php
1346 							 */
1347 
1348 							/* PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO */
1349 							path_translated_len = l + (env_path_info ? strlen(env_path_info) : 0);
1350 							path_translated = (char *) emalloc(path_translated_len + 1);
1351 							memcpy(path_translated, env_document_root, l);
1352 							if (env_path_info) {
1353 								memcpy(path_translated + l, env_path_info, (path_translated_len - l));
1354 							}
1355 							path_translated[path_translated_len] = '\0';
1356 							if (orig_path_translated) {
1357 								CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
1358 							}
1359 							env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated);
1360 							efree(path_translated);
1361 						} else if (	env_script_name &&
1362 									strstr(pt, env_script_name)
1363 						) {
1364 							/* PATH_TRANSLATED = PATH_TRANSLATED - SCRIPT_NAME + PATH_INFO */
1365 							size_t ptlen = strlen(pt) - strlen(env_script_name);
1366 							size_t path_translated_len = ptlen + (env_path_info ? strlen(env_path_info) : 0);
1367 
1368 							char *path_translated = (char *) emalloc(path_translated_len + 1);
1369 							memcpy(path_translated, pt, ptlen);
1370 							if (env_path_info) {
1371 								memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen);
1372 							}
1373 							path_translated[path_translated_len] = '\0';
1374 							if (orig_path_translated) {
1375 								CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
1376 							}
1377 							env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated);
1378 							efree(path_translated);
1379 						}
1380 						break;
1381 					}
1382 				}
1383 				if (!ptr) {
1384 					/*
1385 					 * if we stripped out all the '/' and still didn't find
1386 					 * a valid path... we will fail, badly. of course we would
1387 					 * have failed anyway... we output 'no input file' now.
1388 					 */
1389 					if (orig_script_filename) {
1390 						CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
1391 					}
1392 					script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", NULL);
1393 					SG(sapi_headers).http_response_code = 404;
1394 				}
1395 				if (!SG(request_info).request_uri) {
1396 					if (!orig_script_name ||
1397 						strcmp(orig_script_name, env_script_name) != 0) {
1398 						if (orig_script_name) {
1399 							CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
1400 						}
1401 						SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name);
1402 					} else {
1403 						SG(request_info).request_uri = orig_script_name;
1404 					}
1405 				}
1406 				if (pt) {
1407 					efree(pt);
1408 				}
1409 			} else {
1410 				/* make sure path_info/translated are empty */
1411 				if (!orig_script_filename ||
1412 					(script_path_translated != orig_script_filename &&
1413 					strcmp(script_path_translated, orig_script_filename) != 0)) {
1414 					if (orig_script_filename) {
1415 						CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
1416 					}
1417 					script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", script_path_translated);
1418 				}
1419 				if (env_redirect_url) {
1420 					if (orig_path_info) {
1421 						CGI_PUTENV("ORIG_PATH_INFO", orig_path_info);
1422 						CGI_PUTENV("PATH_INFO", NULL);
1423 					}
1424 					if (orig_path_translated) {
1425 						CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
1426 						CGI_PUTENV("PATH_TRANSLATED", NULL);
1427 					}
1428 				}
1429 				if (env_script_name != orig_script_name) {
1430 					if (orig_script_name) {
1431 						CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
1432 					}
1433 					SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name);
1434 				} else {
1435 					SG(request_info).request_uri = env_script_name;
1436 				}
1437 				efree(real_path);
1438 			}
1439 		} else {
1440 			/* pre 4.3 behaviour, shouldn't be used but provides BC */
1441 			if (env_path_info) {
1442 				SG(request_info).request_uri = env_path_info;
1443 			} else {
1444 				SG(request_info).request_uri = env_script_name;
1445 			}
1446 			if (!CGIG(discard_path) && env_path_translated) {
1447 				script_path_translated = env_path_translated;
1448 			}
1449 		}
1450 
1451 		if (is_valid_path(script_path_translated)) {
1452 			SG(request_info).path_translated = estrdup(script_path_translated);
1453 		}
1454 
1455 		SG(request_info).request_method = CGI_GETENV("REQUEST_METHOD");
1456 		/* FIXME - Work out proto_num here */
1457 		SG(request_info).query_string = CGI_GETENV("QUERY_STRING");
1458 		SG(request_info).content_type = (content_type ? content_type : "" );
1459 		SG(request_info).content_length = (content_length ? atol(content_length) : 0);
1460 
1461 		/* The CGI RFC allows servers to pass on unvalidated Authorization data */
1462 		auth = CGI_GETENV("HTTP_AUTHORIZATION");
1463 		php_handle_auth_data(auth);
1464 	}
1465 }
1466 /* }}} */
1467 
1468 #ifndef PHP_WIN32
1469 /**
1470  * Clean up child processes upon exit
1471  */
1472 void fastcgi_cleanup(int signal)
1473 {
1474 #ifdef DEBUG_FASTCGI
1475 	fprintf(stderr, "FastCGI shutdown, pid %d\n", getpid());
1476 #endif
1477 
1478 	sigaction(SIGTERM, &old_term, 0);
1479 
1480 	/* Kill all the processes in our process group */
1481 	kill(-pgroup, SIGTERM);
1482 
1483 	if (parent && parent_waiting) {
1484 		exit_signal = 1;
1485 	} else {
1486 		exit(0);
1487 	}
1488 }
1489 #else
1490 BOOL WINAPI fastcgi_cleanup(DWORD sig)
1491 {
1492 	int i = kids;
1493 
1494 	EnterCriticalSection(&cleanup_lock);
1495 	cleaning_up = 1;
1496 	LeaveCriticalSection(&cleanup_lock);
1497 
1498 	while (0 < i--) {
1499 		if (NULL == kid_cgi_ps[i]) {
1500 				continue;
1501 		}
1502 
1503 		TerminateProcess(kid_cgi_ps[i], 0);
1504 		CloseHandle(kid_cgi_ps[i]);
1505 		kid_cgi_ps[i] = NULL;
1506 	}
1507 
1508 	if (job) {
1509 		CloseHandle(job);
1510 	}
1511 
1512 	parent = 0;
1513 
1514 	return TRUE;
1515 }
1516 #endif
1517 
1518 PHP_INI_BEGIN()
1519 	STD_PHP_INI_ENTRY("cgi.rfc2616_headers",     "0",  PHP_INI_ALL,    OnUpdateBool,   rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
1520 	STD_PHP_INI_ENTRY("cgi.nph",                 "0",  PHP_INI_ALL,    OnUpdateBool,   nph, php_cgi_globals_struct, php_cgi_globals)
1521 	STD_PHP_INI_ENTRY("cgi.check_shebang_line",  "1",  PHP_INI_SYSTEM, OnUpdateBool,   check_shebang_line, php_cgi_globals_struct, php_cgi_globals)
1522 	STD_PHP_INI_ENTRY("cgi.force_redirect",      "1",  PHP_INI_SYSTEM, OnUpdateBool,   force_redirect, php_cgi_globals_struct, php_cgi_globals)
1523 	STD_PHP_INI_ENTRY("cgi.redirect_status_env", NULL, PHP_INI_SYSTEM, OnUpdateString, redirect_status_env, php_cgi_globals_struct, php_cgi_globals)
1524 	STD_PHP_INI_ENTRY("cgi.fix_pathinfo",        "1",  PHP_INI_SYSTEM, OnUpdateBool,   fix_pathinfo, php_cgi_globals_struct, php_cgi_globals)
1525 	STD_PHP_INI_ENTRY("cgi.discard_path",        "0",  PHP_INI_SYSTEM, OnUpdateBool,   discard_path, php_cgi_globals_struct, php_cgi_globals)
1526 	STD_PHP_INI_ENTRY("fastcgi.logging",         "1",  PHP_INI_SYSTEM, OnUpdateBool,   fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
1527 #ifdef PHP_WIN32
1528 	STD_PHP_INI_ENTRY("fastcgi.impersonate",     "0",  PHP_INI_SYSTEM, OnUpdateBool,   impersonate, php_cgi_globals_struct, php_cgi_globals)
1529 #endif
1530 PHP_INI_END()
1531 
1532 /* {{{ php_cgi_globals_ctor
1533  */
1534 static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals)
1535 {
1536 #if defined(ZTS) && defined(PHP_WIN32)
1537 	ZEND_TSRMLS_CACHE_UPDATE();
1538 #endif
1539 	php_cgi_globals->rfc2616_headers = 0;
1540 	php_cgi_globals->nph = 0;
1541 	php_cgi_globals->check_shebang_line = 1;
1542 	php_cgi_globals->force_redirect = 1;
1543 	php_cgi_globals->redirect_status_env = NULL;
1544 	php_cgi_globals->fix_pathinfo = 1;
1545 	php_cgi_globals->discard_path = 0;
1546 	php_cgi_globals->fcgi_logging = 1;
1547 #ifdef PHP_WIN32
1548 	php_cgi_globals->impersonate = 0;
1549 #endif
1550 	zend_hash_init(&php_cgi_globals->user_config_cache, 8, NULL, user_config_cache_entry_dtor, 1);
1551 }
1552 /* }}} */
1553 
1554 /* {{{ PHP_MINIT_FUNCTION
1555  */
1556 static PHP_MINIT_FUNCTION(cgi)
1557 {
1558 	REGISTER_INI_ENTRIES();
1559 	return SUCCESS;
1560 }
1561 /* }}} */
1562 
1563 /* {{{ PHP_MSHUTDOWN_FUNCTION
1564  */
1565 static PHP_MSHUTDOWN_FUNCTION(cgi)
1566 {
1567 	zend_hash_destroy(&CGIG(user_config_cache));
1568 
1569 	UNREGISTER_INI_ENTRIES();
1570 	return SUCCESS;
1571 }
1572 /* }}} */
1573 
1574 /* {{{ PHP_MINFO_FUNCTION
1575  */
1576 static PHP_MINFO_FUNCTION(cgi)
1577 {
1578 	DISPLAY_INI_ENTRIES();
1579 }
1580 /* }}} */
1581 
1582 PHP_FUNCTION(apache_child_terminate) /* {{{ */
1583 {
1584 	if (zend_parse_parameters_none()) {
1585 		return;
1586 	}
1587 	if (fcgi_is_fastcgi()) {
1588 		fcgi_terminate();
1589 	}
1590 }
1591 /* }}} */
1592 
1593 
1594 PHP_FUNCTION(apache_request_headers) /* {{{ */
1595 {
1596 	if (zend_parse_parameters_none()) {
1597 		return;
1598 	}
1599 	array_init(return_value);
1600 	if (fcgi_is_fastcgi()) {
1601 		fcgi_request *request = (fcgi_request*) SG(server_context);
1602 
1603 		fcgi_loadenv(request, sapi_add_request_header, return_value);
1604 	} else {
1605 		char buf[128];
1606 		char **env, *p, *q, *var, *val, *t = buf;
1607 		size_t alloc_size = sizeof(buf);
1608 		zend_ulong var_len;
1609 
1610 		for (env = environ; env != NULL && *env != NULL; env++) {
1611 			val = strchr(*env, '=');
1612 			if (!val) {				/* malformed entry? */
1613 				continue;
1614 			}
1615 			var_len = val - *env;
1616 			if (var_len >= alloc_size) {
1617 				alloc_size = var_len + 64;
1618 				t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size));
1619 			}
1620 			var = *env;
1621 			if (var_len > 5 &&
1622 			    var[0] == 'H' &&
1623 			    var[1] == 'T' &&
1624 			    var[2] == 'T' &&
1625 			    var[3] == 'P' &&
1626 			    var[4] == '_') {
1627 
1628 				var_len -= 5;
1629 
1630 				if (var_len >= alloc_size) {
1631 					alloc_size = var_len + 64;
1632 					t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size));
1633 				}
1634 				p = var + 5;
1635 
1636 				var = q = t;
1637 				/* First char keep uppercase */
1638 				*q++ = *p++;
1639 				while (*p) {
1640 					if (*p == '=') {
1641 						/* End of name */
1642 						break;
1643 					} else if (*p == '_') {
1644 						*q++ = '-';
1645 						p++;
1646 						/* First char after - keep uppercase */
1647 						if (*p && *p!='=') {
1648 							*q++ = *p++;
1649 						}
1650 					} else if (*p >= 'A' && *p <= 'Z') {
1651 						/* lowercase */
1652 						*q++ = (*p++ - 'A' + 'a');
1653 					} else {
1654 						*q++ = *p++;
1655 					}
1656 				}
1657 				*q = 0;
1658 			} else if (var_len == sizeof("CONTENT_TYPE")-1 &&
1659 			           memcmp(var, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1) == 0) {
1660 				var = "Content-Type";
1661 			} else if (var_len == sizeof("CONTENT_LENGTH")-1 &&
1662 			           memcmp(var, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1) == 0) {
1663 				var = "Content-Length";
1664 			} else {
1665 				continue;
1666 			}
1667 			val++;
1668 			add_assoc_string_ex(return_value, var, var_len, val);
1669 		}
1670 		if (t != buf && t != NULL) {
1671 			efree(t);
1672 		}
1673 	}
1674 }
1675 /* }}} */
1676 
1677 static void add_response_header(sapi_header_struct *h, zval *return_value) /* {{{ */
1678 {
1679 	if (h->header_len > 0) {
1680 		char *s;
1681 		size_t len = 0;
1682 		ALLOCA_FLAG(use_heap)
1683 
1684 		char *p = strchr(h->header, ':');
1685 		if (NULL != p) {
1686 			len = p - h->header;
1687 		}
1688 		if (len > 0) {
1689 			while (len != 0 && (h->header[len-1] == ' ' || h->header[len-1] == '\t')) {
1690 				len--;
1691 			}
1692 			if (len) {
1693 				s = do_alloca(len + 1, use_heap);
1694 				memcpy(s, h->header, len);
1695 				s[len] = 0;
1696 				do {
1697 					p++;
1698 				} while (*p == ' ' || *p == '\t');
1699 				add_assoc_stringl_ex(return_value, s, len, p, h->header_len - (p - h->header));
1700 				free_alloca(s, use_heap);
1701 			}
1702 		}
1703 	}
1704 }
1705 /* }}} */
1706 
1707 PHP_FUNCTION(apache_response_headers) /* {{{ */
1708 {
1709 	if (zend_parse_parameters_none() == FAILURE) {
1710 		return;
1711 	}
1712 
1713 	array_init(return_value);
1714 	zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t)add_response_header, return_value);
1715 }
1716 /* }}} */
1717 
1718 ZEND_BEGIN_ARG_INFO(arginfo_no_args, 0)
1719 ZEND_END_ARG_INFO()
1720 
1721 static const zend_function_entry cgi_functions[] = {
1722 	PHP_FE(apache_child_terminate, arginfo_no_args)
1723 	PHP_FE(apache_request_headers, arginfo_no_args)
1724 	PHP_FE(apache_response_headers, arginfo_no_args)
1725 	PHP_FALIAS(getallheaders, apache_request_headers, arginfo_no_args)
1726 	PHP_FE_END
1727 };
1728 
1729 static zend_module_entry cgi_module_entry = {
1730 	STANDARD_MODULE_HEADER,
1731 	"cgi-fcgi",
1732 	cgi_functions,
1733 	PHP_MINIT(cgi),
1734 	PHP_MSHUTDOWN(cgi),
1735 	NULL,
1736 	NULL,
1737 	PHP_MINFO(cgi),
1738 	PHP_VERSION,
1739 	STANDARD_MODULE_PROPERTIES
1740 };
1741 
1742 /* {{{ main
1743  */
1744 int main(int argc, char *argv[])
1745 {
1746 	int free_query_string = 0;
1747 	int exit_status = SUCCESS;
1748 	int cgi = 0, c, i;
1749 	size_t len;
1750 	zend_file_handle file_handle;
1751 	char *s;
1752 
1753 	/* temporary locals */
1754 	int behavior = PHP_MODE_STANDARD;
1755 	int no_headers = 0;
1756 	int orig_optind = php_optind;
1757 	char *orig_optarg = php_optarg;
1758 	char *script_file = NULL;
1759 	size_t ini_entries_len = 0;
1760 	/* end of temporary locals */
1761 
1762 	int max_requests = 500;
1763 	int requests = 0;
1764 	int fastcgi;
1765 	char *bindpath = NULL;
1766 	int fcgi_fd = 0;
1767 	fcgi_request *request = NULL;
1768 	int warmup_repeats = 0;
1769 	int repeats = 1;
1770 	int benchmark = 0;
1771 #if HAVE_GETTIMEOFDAY
1772 	struct timeval start, end;
1773 #else
1774 	time_t start, end;
1775 #endif
1776 #ifndef PHP_WIN32
1777 	int status = 0;
1778 #endif
1779 	char *query_string;
1780 	char *decoded_query_string;
1781 	int skip_getopt = 0;
1782 
1783 #if defined(SIGPIPE) && defined(SIG_IGN)
1784 	signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
1785 								that sockets created via fsockopen()
1786 								don't kill PHP if the remote site
1787 								closes it.  in apache|apxs mode apache
1788 								does that for us!  thies@thieso.net
1789 								20000419 */
1790 #endif
1791 
1792 #ifdef ZTS
1793 	php_tsrm_startup();
1794 # ifdef PHP_WIN32
1795 	ZEND_TSRMLS_CACHE_UPDATE();
1796 # endif
1797 #endif
1798 
1799 	zend_signal_startup();
1800 
1801 #ifdef ZTS
1802 	ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
1803 #else
1804 	php_cgi_globals_ctor(&php_cgi_globals);
1805 #endif
1806 
1807 	sapi_startup(&cgi_sapi_module);
1808 	fastcgi = fcgi_is_fastcgi();
1809 	cgi_sapi_module.php_ini_path_override = NULL;
1810 
1811 #ifdef PHP_WIN32
1812 	_fmode = _O_BINARY; /* sets default for file streams to binary */
1813 	setmode(_fileno(stdin),  O_BINARY);	/* make the stdio mode be binary */
1814 	setmode(_fileno(stdout), O_BINARY);	/* make the stdio mode be binary */
1815 	setmode(_fileno(stderr), O_BINARY);	/* make the stdio mode be binary */
1816 #endif
1817 
1818 	if (!fastcgi) {
1819 		/* Make sure we detect we are a cgi - a bit redundancy here,
1820 		 * but the default case is that we have to check only the first one. */
1821 		if (getenv("SERVER_SOFTWARE") ||
1822 			getenv("SERVER_NAME") ||
1823 			getenv("GATEWAY_INTERFACE") ||
1824 			getenv("REQUEST_METHOD")
1825 		) {
1826 			cgi = 1;
1827 		}
1828 	}
1829 
1830 	if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) {
1831 		/* we've got query string that has no = - apache CGI will pass it to command line */
1832 		unsigned char *p;
1833 		decoded_query_string = strdup(query_string);
1834 		php_url_decode(decoded_query_string, strlen(decoded_query_string));
1835 		for (p = (unsigned char *)decoded_query_string; *p &&  *p <= ' '; p++) {
1836 			/* skip all leading spaces */
1837 		}
1838 		if(*p == '-') {
1839 			skip_getopt = 1;
1840 		}
1841 		free(decoded_query_string);
1842 	}
1843 
1844 	while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
1845 		switch (c) {
1846 			case 'c':
1847 				if (cgi_sapi_module.php_ini_path_override) {
1848 					free(cgi_sapi_module.php_ini_path_override);
1849 				}
1850 				cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
1851 				break;
1852 			case 'n':
1853 				cgi_sapi_module.php_ini_ignore = 1;
1854 				break;
1855 			case 'd': {
1856 				/* define ini entries on command line */
1857 				size_t len = strlen(php_optarg);
1858 				char *val;
1859 
1860 				if ((val = strchr(php_optarg, '='))) {
1861 					val++;
1862 					if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
1863 						cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
1864 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
1865 						ini_entries_len += (val - php_optarg);
1866 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"", 1);
1867 						ini_entries_len++;
1868 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, val, len - (val - php_optarg));
1869 						ini_entries_len += len - (val - php_optarg);
1870 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
1871 						ini_entries_len += sizeof("\n\0\"") - 2;
1872 					} else {
1873 						cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\n\0"));
1874 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
1875 						memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
1876 						ini_entries_len += len + sizeof("\n\0") - 2;
1877 					}
1878 				} else {
1879 					cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
1880 					memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
1881 					memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
1882 					ini_entries_len += len + sizeof("=1\n\0") - 2;
1883 				}
1884 				break;
1885 			}
1886 			/* if we're started on command line, check to see if
1887 			 * we are being started as an 'external' fastcgi
1888 			 * server by accepting a bindpath parameter. */
1889 			case 'b':
1890 				if (!fastcgi) {
1891 					bindpath = strdup(php_optarg);
1892 				}
1893 				break;
1894 			case 's': /* generate highlighted HTML from source */
1895 				behavior = PHP_MODE_HIGHLIGHT;
1896 				break;
1897 		}
1898 	}
1899 	php_optind = orig_optind;
1900 	php_optarg = orig_optarg;
1901 
1902 	if (fastcgi || bindpath) {
1903 		/* Override SAPI callbacks */
1904 		cgi_sapi_module.ub_write     = sapi_fcgi_ub_write;
1905 		cgi_sapi_module.flush        = sapi_fcgi_flush;
1906 		cgi_sapi_module.read_post    = sapi_fcgi_read_post;
1907 		cgi_sapi_module.getenv       = sapi_fcgi_getenv;
1908 		cgi_sapi_module.read_cookies = sapi_fcgi_read_cookies;
1909 	}
1910 
1911 #ifdef ZTS
1912 	SG(request_info).path_translated = NULL;
1913 #endif
1914 
1915 	cgi_sapi_module.executable_location = argv[0];
1916 	if (!cgi && !fastcgi && !bindpath) {
1917 		cgi_sapi_module.additional_functions = additional_functions;
1918 	}
1919 
1920 	/* startup after we get the above ini override se we get things right */
1921 	if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
1922 #ifdef ZTS
1923 		tsrm_shutdown();
1924 #endif
1925 		free(bindpath);
1926 		return FAILURE;
1927 	}
1928 
1929 	/* check force_cgi after startup, so we have proper output */
1930 	if (cgi && CGIG(force_redirect)) {
1931 		/* Apache will generate REDIRECT_STATUS,
1932 		 * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.
1933 		 * redirect.so and installation instructions available from
1934 		 * http://www.koehntopp.de/php.
1935 		 *   -- kk@netuse.de
1936 		 */
1937 		if (!getenv("REDIRECT_STATUS") &&
1938 			!getenv ("HTTP_REDIRECT_STATUS") &&
1939 			/* this is to allow a different env var to be configured
1940 			 * in case some server does something different than above */
1941 			(!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env)))
1942 		) {
1943 			zend_try {
1944 				SG(sapi_headers).http_response_code = 400;
1945 				PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\
1946 <p>This PHP CGI binary was compiled with force-cgi-redirect enabled.  This\n\
1947 means that a page will only be served up if the REDIRECT_STATUS CGI variable is\n\
1948 set, e.g. via an Apache Action directive.</p>\n\
1949 <p>For more information as to <i>why</i> this behaviour exists, see the <a href=\"http://php.net/security.cgi-bin\">\
1950 manual page for CGI security</a>.</p>\n\
1951 <p>For more information about changing this behaviour or re-enabling this webserver,\n\
1952 consult the installation file that came with this distribution, or visit \n\
1953 <a href=\"http://php.net/install.windows\">the manual page</a>.</p>\n");
1954 			} zend_catch {
1955 			} zend_end_try();
1956 #if defined(ZTS) && !defined(PHP_DEBUG)
1957 			/* XXX we're crashing here in msvc6 debug builds at
1958 			 * php_message_handler_for_zend:839 because
1959 			 * SG(request_info).path_translated is an invalid pointer.
1960 			 * It still happens even though I set it to null, so something
1961 			 * weird is going on.
1962 			 */
1963 			tsrm_shutdown();
1964 #endif
1965 			free(bindpath);
1966 			return FAILURE;
1967 		}
1968 	}
1969 
1970 #ifndef HAVE_ATTRIBUTE_WEAK
1971 	fcgi_set_logger(fcgi_log);
1972 #endif
1973 
1974 	if (bindpath) {
1975 		int backlog = 128;
1976 		if (getenv("PHP_FCGI_BACKLOG")) {
1977 			backlog = atoi(getenv("PHP_FCGI_BACKLOG"));
1978 		}
1979 		fcgi_fd = fcgi_listen(bindpath, backlog);
1980 		if (fcgi_fd < 0) {
1981 			fprintf(stderr, "Couldn't create FastCGI listen socket on port %s\n", bindpath);
1982 #ifdef ZTS
1983 			tsrm_shutdown();
1984 #endif
1985 			return FAILURE;
1986 		}
1987 		fastcgi = fcgi_is_fastcgi();
1988 	}
1989 
1990 	/* make php call us to get _ENV vars */
1991 	php_php_import_environment_variables = php_import_environment_variables;
1992 	php_import_environment_variables = cgi_php_import_environment_variables;
1993 
1994 	if (fastcgi) {
1995 		/* How many times to run PHP scripts before dying */
1996 		if (getenv("PHP_FCGI_MAX_REQUESTS")) {
1997 			max_requests = atoi(getenv("PHP_FCGI_MAX_REQUESTS"));
1998 			if (max_requests < 0) {
1999 				fprintf(stderr, "PHP_FCGI_MAX_REQUESTS is not valid\n");
2000 				return FAILURE;
2001 			}
2002 		}
2003 
2004 		/* library is already initialized, now init our request */
2005 		request = fcgi_init_request(fcgi_fd, NULL, NULL, NULL);
2006 
2007 		/* Pre-fork or spawn, if required */
2008 		if (getenv("PHP_FCGI_CHILDREN")) {
2009 			char * children_str = getenv("PHP_FCGI_CHILDREN");
2010 			children = atoi(children_str);
2011 			if (children < 0) {
2012 				fprintf(stderr, "PHP_FCGI_CHILDREN is not valid\n");
2013 				return FAILURE;
2014 			}
2015 			fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, children_str, strlen(children_str));
2016 			/* This is the number of concurrent requests, equals FCGI_MAX_CONNS */
2017 			fcgi_set_mgmt_var("FCGI_MAX_REQS",  sizeof("FCGI_MAX_REQS")-1,  children_str, strlen(children_str));
2018 		} else {
2019 #ifdef PHP_WIN32
2020 			/* If this env var is set, the process was invoked as a child. Let
2021 				it show the original PHP_FCGI_CHILDREN value, while don't care
2022 				otherwise. */
2023 			char * children_str = getenv("PHP_FCGI_CHILDREN_FOR_KID");
2024 			if (children_str) {
2025 				char putenv_buf[sizeof("PHP_FCGI_CHILDREN")+5];
2026 
2027 				snprintf(putenv_buf, sizeof(putenv_buf), "%s=%s", "PHP_FCGI_CHILDREN", children_str);
2028 				putenv(putenv_buf);
2029 				putenv("PHP_FCGI_CHILDREN_FOR_KID=");
2030 
2031 				SetEnvironmentVariable("PHP_FCGI_CHILDREN", children_str);
2032 				SetEnvironmentVariable("PHP_FCGI_CHILDREN_FOR_KID", NULL);
2033 			}
2034 #endif
2035 			fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, "1", sizeof("1")-1);
2036 			fcgi_set_mgmt_var("FCGI_MAX_REQS",  sizeof("FCGI_MAX_REQS")-1,  "1", sizeof("1")-1);
2037 		}
2038 
2039 #ifndef PHP_WIN32
2040 		if (children) {
2041 			int running = 0;
2042 			pid_t pid;
2043 
2044 			/* Create a process group for ourself & children */
2045 			setsid();
2046 			pgroup = getpgrp();
2047 #ifdef DEBUG_FASTCGI
2048 			fprintf(stderr, "Process group %d\n", pgroup);
2049 #endif
2050 
2051 			/* Set up handler to kill children upon exit */
2052 			act.sa_flags = 0;
2053 			act.sa_handler = fastcgi_cleanup;
2054 			if (sigaction(SIGTERM, &act, &old_term) ||
2055 				sigaction(SIGINT,  &act, &old_int)  ||
2056 				sigaction(SIGQUIT, &act, &old_quit)
2057 			) {
2058 				perror("Can't set signals");
2059 				exit(1);
2060 			}
2061 
2062 			if (fcgi_in_shutdown()) {
2063 				goto parent_out;
2064 			}
2065 
2066 			while (parent) {
2067 				do {
2068 #ifdef DEBUG_FASTCGI
2069 					fprintf(stderr, "Forking, %d running\n", running);
2070 #endif
2071 					pid = fork();
2072 					switch (pid) {
2073 					case 0:
2074 						/* One of the children.
2075 						 * Make sure we don't go round the
2076 						 * fork loop any more
2077 						 */
2078 						parent = 0;
2079 
2080 						/* don't catch our signals */
2081 						sigaction(SIGTERM, &old_term, 0);
2082 						sigaction(SIGQUIT, &old_quit, 0);
2083 						sigaction(SIGINT,  &old_int,  0);
2084 						zend_signal_init();
2085 						break;
2086 					case -1:
2087 						perror("php (pre-forking)");
2088 						exit(1);
2089 						break;
2090 					default:
2091 						/* Fine */
2092 						running++;
2093 						break;
2094 					}
2095 				} while (parent && (running < children));
2096 
2097 				if (parent) {
2098 #ifdef DEBUG_FASTCGI
2099 					fprintf(stderr, "Wait for kids, pid %d\n", getpid());
2100 #endif
2101 					parent_waiting = 1;
2102 					while (1) {
2103 						if (wait(&status) >= 0) {
2104 							running--;
2105 							break;
2106 						} else if (exit_signal) {
2107 							break;
2108 						}
2109 					}
2110 					if (exit_signal) {
2111 #if 0
2112 						while (running > 0) {
2113 							while (wait(&status) < 0) {
2114 							}
2115 							running--;
2116 						}
2117 #endif
2118 						goto parent_out;
2119 					}
2120 				}
2121 			}
2122 		} else {
2123 			parent = 0;
2124 			zend_signal_init();
2125 		}
2126 
2127 #else
2128 		if (children) {
2129 			wchar_t *cmd_line_tmp, cmd_line[PHP_WIN32_IOUTIL_MAXPATHLEN];
2130 			size_t cmd_line_len;
2131 			char kid_buf[16];
2132 			int i;
2133 
2134 			ZeroMemory(&kid_cgi_ps, sizeof(kid_cgi_ps));
2135 			kids = children < WIN32_MAX_SPAWN_CHILDREN ? children : WIN32_MAX_SPAWN_CHILDREN;
2136 
2137 			InitializeCriticalSection(&cleanup_lock);
2138 			SetConsoleCtrlHandler(fastcgi_cleanup, TRUE);
2139 
2140 			/* kids will inherit the env, don't let them spawn */
2141 			SetEnvironmentVariable("PHP_FCGI_CHILDREN", NULL);
2142 			/* instead, set a temporary env var, so then the child can read and
2143 				show the actual setting correctly. */
2144 			snprintf(kid_buf, 16, "%d", children);
2145 			SetEnvironmentVariable("PHP_FCGI_CHILDREN_FOR_KID", kid_buf);
2146 
2147 			/* The current command line is used as is. This should normally be no issue,
2148 				even if there were some I/O redirection. If some issues turn out, an
2149 				extra parsing might be needed here. */
2150 			cmd_line_tmp = GetCommandLineW();
2151 			if (!cmd_line_tmp) {
2152 				DWORD err = GetLastError();
2153 				char *err_text = php_win32_error_to_msg(err);
2154 
2155 				fprintf(stderr, "unable to get current command line: [0x%08lx]: %s\n", err, err_text);
2156 				php_win32_error_msg_free(err_text);
2157 
2158 				goto parent_out;
2159 			}
2160 
2161 			cmd_line_len = wcslen(cmd_line_tmp);
2162 			if (cmd_line_len > sizeof(cmd_line) - 1) {
2163 				fprintf(stderr, "command line is too long\n");
2164 				goto parent_out;
2165 			}
2166 			memmove(cmd_line, cmd_line_tmp, (cmd_line_len + 1)*sizeof(wchar_t));
2167 
2168 			job = CreateJobObject(NULL, NULL);
2169 			if (!job) {
2170 				DWORD err = GetLastError();
2171 				char *err_text = php_win32_error_to_msg(err);
2172 
2173 				fprintf(stderr, "unable to create job object: [0x%08lx]: %s\n", err, err_text);
2174 
2175 				php_win32_error_msg_free(err_text);
2176 
2177 				goto parent_out;
2178 			}
2179 
2180 			job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
2181 			if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation, &job_info, sizeof(job_info))) {
2182 				DWORD err = GetLastError();
2183 				char *err_text = php_win32_error_to_msg(err);
2184 
2185 				fprintf(stderr, "unable to configure job object: [0x%08lx]: %s\n", err, err_text);
2186 				php_win32_error_msg_free(err_text);
2187 			}
2188 
2189 			while (parent) {
2190 				EnterCriticalSection(&cleanup_lock);
2191 				if (cleaning_up) {
2192 					goto parent_loop_end;
2193 				}
2194 				LeaveCriticalSection(&cleanup_lock);
2195 
2196 				i = kids;
2197 				while (0 < i--) {
2198 					DWORD status;
2199 
2200 					if (NULL != kid_cgi_ps[i]) {
2201 						if(!GetExitCodeProcess(kid_cgi_ps[i], &status) || status != STILL_ACTIVE) {
2202 							CloseHandle(kid_cgi_ps[i]);
2203 							kid_cgi_ps[i] = NULL;
2204 						}
2205 					}
2206 				}
2207 
2208 				i = kids;
2209 				while (0 < i--) {
2210 					PROCESS_INFORMATION pi;
2211 					STARTUPINFOW si;
2212 
2213 					if (NULL != kid_cgi_ps[i]) {
2214 						continue;
2215 					}
2216 
2217 					ZeroMemory(&si, sizeof(si));
2218 					si.cb = sizeof(si);
2219 					ZeroMemory(&pi, sizeof(pi));
2220 
2221 					si.dwFlags = STARTF_USESTDHANDLES;
2222 					si.hStdOutput = INVALID_HANDLE_VALUE;
2223 					si.hStdInput  = (HANDLE)_get_osfhandle(fcgi_fd);
2224 					si.hStdError  = INVALID_HANDLE_VALUE;
2225 
2226 					if (CreateProcessW(NULL, cmd_line, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
2227 						kid_cgi_ps[i] = pi.hProcess;
2228 						if (!AssignProcessToJobObject(job, pi.hProcess)) {
2229 							DWORD err = GetLastError();
2230 							char *err_text = php_win32_error_to_msg(err);
2231 
2232 							fprintf(stderr, "unable to assign child process to job object: [0x%08lx]: %s\n", err, err_text);
2233 							php_win32_error_msg_free(err_text);
2234 						}
2235 						CloseHandle(pi.hThread);
2236 					} else {
2237 						DWORD err = GetLastError();
2238 						char *err_text = php_win32_error_to_msg(err);
2239 
2240 						kid_cgi_ps[i] = NULL;
2241 
2242 						fprintf(stderr, "unable to spawn: [0x%08lx]: %s\n", err, err_text);
2243 						php_win32_error_msg_free(err_text);
2244 					}
2245 				}
2246 
2247 				WaitForMultipleObjects(kids, kid_cgi_ps, FALSE, INFINITE);
2248 			}
2249 
2250 parent_loop_end:
2251 			/* restore my env */
2252 			SetEnvironmentVariable("PHP_FCGI_CHILDREN", kid_buf);
2253 
2254 			DeleteCriticalSection(&cleanup_lock);
2255 
2256 			goto parent_out;
2257 		} else {
2258 			parent = 0;
2259 		}
2260 #endif /* WIN32 */
2261 	}
2262 
2263 	zend_first_try {
2264 		while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 1, 2)) != -1) {
2265 			switch (c) {
2266 				case 'T':
2267 					benchmark = 1;
2268 					{
2269 						char *comma = strchr(php_optarg, ',');
2270 						if (comma) {
2271 							warmup_repeats = atoi(php_optarg);
2272 							repeats = atoi(comma + 1);
2273 #ifdef HAVE_VALGRIND
2274 							if (warmup_repeats > 0) {
2275 								CALLGRIND_STOP_INSTRUMENTATION;
2276 							}
2277 #endif
2278 						} else {
2279 							repeats = atoi(php_optarg);
2280 						}
2281 					}
2282 #ifdef HAVE_GETTIMEOFDAY
2283 					gettimeofday(&start, NULL);
2284 #else
2285 					time(&start);
2286 #endif
2287 					break;
2288 				case 'h':
2289 				case '?':
2290 				case PHP_GETOPT_INVALID_ARG:
2291 					if (request) {
2292 						fcgi_destroy_request(request);
2293 					}
2294 					fcgi_shutdown();
2295 					no_headers = 1;
2296 					SG(headers_sent) = 1;
2297 					php_cgi_usage(argv[0]);
2298 					php_output_end_all();
2299 					exit_status = 0;
2300 					if (c == PHP_GETOPT_INVALID_ARG) {
2301 						exit_status = 1;
2302 					}
2303 					goto out;
2304 			}
2305 		}
2306 		php_optind = orig_optind;
2307 		php_optarg = orig_optarg;
2308 
2309 		/* start of FAST CGI loop */
2310 		/* Initialise FastCGI request structure */
2311 #ifdef PHP_WIN32
2312 		/* attempt to set security impersonation for fastcgi
2313 		 * will only happen on NT based OS, others will ignore it. */
2314 		if (fastcgi && CGIG(impersonate)) {
2315 			fcgi_impersonate();
2316 		}
2317 #endif
2318 		while (!fastcgi || fcgi_accept_request(request) >= 0) {
2319 			SG(server_context) = fastcgi ? (void *)request : (void *) 1;
2320 			init_request_info(request);
2321 
2322 			if (!cgi && !fastcgi) {
2323 				while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
2324 					switch (c) {
2325 
2326 						case 'a':	/* interactive mode */
2327 							printf("Interactive mode enabled\n\n");
2328 							break;
2329 
2330 						case 'C': /* don't chdir to the script directory */
2331 							SG(options) |= SAPI_OPTION_NO_CHDIR;
2332 							break;
2333 
2334 						case 'e': /* enable extended info output */
2335 							CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
2336 							break;
2337 
2338 						case 'f': /* parse file */
2339 							if (script_file) {
2340 								efree(script_file);
2341 							}
2342 							script_file = estrdup(php_optarg);
2343 							no_headers = 1;
2344 							break;
2345 
2346 						case 'i': /* php info & quit */
2347 							if (script_file) {
2348 								efree(script_file);
2349 							}
2350 							if (php_request_startup() == FAILURE) {
2351 								SG(server_context) = NULL;
2352 								php_module_shutdown();
2353 								free(bindpath);
2354 								return FAILURE;
2355 							}
2356 							if (no_headers) {
2357 								SG(headers_sent) = 1;
2358 								SG(request_info).no_headers = 1;
2359 							}
2360 							php_print_info(0xFFFFFFFF);
2361 							php_request_shutdown((void *) 0);
2362 							fcgi_shutdown();
2363 							exit_status = 0;
2364 							goto out;
2365 
2366 						case 'l': /* syntax check mode */
2367 							no_headers = 1;
2368 							behavior = PHP_MODE_LINT;
2369 							break;
2370 
2371 						case 'm': /* list compiled in modules */
2372 							if (script_file) {
2373 								efree(script_file);
2374 							}
2375 							SG(headers_sent) = 1;
2376 							php_printf("[PHP Modules]\n");
2377 							print_modules();
2378 							php_printf("\n[Zend Modules]\n");
2379 							print_extensions();
2380 							php_printf("\n");
2381 							php_output_end_all();
2382 							fcgi_shutdown();
2383 							exit_status = 0;
2384 							goto out;
2385 
2386 						case 'q': /* do not generate HTTP headers */
2387 							no_headers = 1;
2388 							break;
2389 
2390 						case 'v': /* show php version & quit */
2391 							if (script_file) {
2392 								efree(script_file);
2393 							}
2394 							no_headers = 1;
2395 							if (php_request_startup() == FAILURE) {
2396 								SG(server_context) = NULL;
2397 								php_module_shutdown();
2398 								free(bindpath);
2399 								return FAILURE;
2400 							}
2401 							SG(headers_sent) = 1;
2402 							SG(request_info).no_headers = 1;
2403 #if ZEND_DEBUG
2404 							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());
2405 #else
2406 							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());
2407 #endif
2408 							php_request_shutdown((void *) 0);
2409 							fcgi_shutdown();
2410 							exit_status = 0;
2411 							goto out;
2412 
2413 						case 'w':
2414 							behavior = PHP_MODE_STRIP;
2415 							break;
2416 
2417 						case 'z': /* load extension file */
2418 							zend_load_extension(php_optarg);
2419 							break;
2420 
2421 						default:
2422 							break;
2423 					}
2424 				}
2425 
2426 				if (script_file) {
2427 					/* override path_translated if -f on command line */
2428 					if (SG(request_info).path_translated) efree(SG(request_info).path_translated);
2429 					SG(request_info).path_translated = script_file;
2430 					/* before registering argv to module exchange the *new* argv[0] */
2431 					/* we can achieve this without allocating more memory */
2432 					SG(request_info).argc = argc - (php_optind - 1);
2433 					SG(request_info).argv = &argv[php_optind - 1];
2434 					SG(request_info).argv[0] = script_file;
2435 				} else if (argc > php_optind) {
2436 					/* file is on command line, but not in -f opt */
2437 					if (SG(request_info).path_translated) efree(SG(request_info).path_translated);
2438 					SG(request_info).path_translated = estrdup(argv[php_optind]);
2439 					/* arguments after the file are considered script args */
2440 					SG(request_info).argc = argc - php_optind;
2441 					SG(request_info).argv = &argv[php_optind];
2442 				}
2443 
2444 				if (no_headers) {
2445 					SG(headers_sent) = 1;
2446 					SG(request_info).no_headers = 1;
2447 				}
2448 
2449 				/* all remaining arguments are part of the query string
2450 				 * this section of code concatenates all remaining arguments
2451 				 * into a single string, separating args with a &
2452 				 * this allows command lines like:
2453 				 *
2454 				 *  test.php v1=test v2=hello+world!
2455 				 *  test.php "v1=test&v2=hello world!"
2456 				 *  test.php v1=test "v2=hello world!"
2457 				*/
2458 				if (!SG(request_info).query_string && argc > php_optind) {
2459 					size_t slen = strlen(PG(arg_separator).input);
2460 					len = 0;
2461 					for (i = php_optind; i < argc; i++) {
2462 						if (i < (argc - 1)) {
2463 							len += strlen(argv[i]) + slen;
2464 						} else {
2465 							len += strlen(argv[i]);
2466 						}
2467 					}
2468 
2469 					len += 2;
2470 					s = malloc(len);
2471 					*s = '\0';			/* we are pretending it came from the environment  */
2472 					for (i = php_optind; i < argc; i++) {
2473 						strlcat(s, argv[i], len);
2474 						if (i < (argc - 1)) {
2475 							strlcat(s, PG(arg_separator).input, len);
2476 						}
2477 					}
2478 					SG(request_info).query_string = s;
2479 					free_query_string = 1;
2480 				}
2481 			} /* end !cgi && !fastcgi */
2482 
2483 			/*
2484 				we never take stdin if we're (f)cgi, always
2485 				rely on the web server giving us the info
2486 				we need in the environment.
2487 			*/
2488 			if (SG(request_info).path_translated || cgi || fastcgi) {
2489 				zend_stream_init_filename(&file_handle, SG(request_info).path_translated);
2490 			} else {
2491 				zend_stream_init_fp(&file_handle, stdin, "Standard input code");
2492 			}
2493 
2494 			/* request startup only after we've done all we can to
2495 			 * get path_translated */
2496 			if (php_request_startup() == FAILURE) {
2497 				if (fastcgi) {
2498 					fcgi_finish_request(request, 1);
2499 				}
2500 				SG(server_context) = NULL;
2501 				php_module_shutdown();
2502 				return FAILURE;
2503 			}
2504 			if (no_headers) {
2505 				SG(headers_sent) = 1;
2506 				SG(request_info).no_headers = 1;
2507 			}
2508 
2509 			/*
2510 				at this point path_translated will be set if:
2511 				1. we are running from shell and got filename was there
2512 				2. we are running as cgi or fastcgi
2513 			*/
2514 			if (cgi || fastcgi || SG(request_info).path_translated) {
2515 				if (php_fopen_primary_script(&file_handle) == FAILURE) {
2516 					zend_try {
2517 						if (errno == EACCES) {
2518 							SG(sapi_headers).http_response_code = 403;
2519 							PUTS("Access denied.\n");
2520 						} else {
2521 							SG(sapi_headers).http_response_code = 404;
2522 							PUTS("No input file specified.\n");
2523 						}
2524 					} zend_catch {
2525 					} zend_end_try();
2526 					/* we want to serve more requests if this is fastcgi
2527 					 * so cleanup and continue, request shutdown is
2528 					 * handled later */
2529 					if (fastcgi) {
2530 						goto fastcgi_request_done;
2531 					}
2532 
2533 					if (SG(request_info).path_translated) {
2534 						efree(SG(request_info).path_translated);
2535 						SG(request_info).path_translated = NULL;
2536 					}
2537 
2538 					if (free_query_string && SG(request_info).query_string) {
2539 						free(SG(request_info).query_string);
2540 						SG(request_info).query_string = NULL;
2541 					}
2542 
2543 					php_request_shutdown((void *) 0);
2544 					SG(server_context) = NULL;
2545 					php_module_shutdown();
2546 					sapi_shutdown();
2547 #ifdef ZTS
2548 					tsrm_shutdown();
2549 #endif
2550 					free(bindpath);
2551 					return FAILURE;
2552 				}
2553 			}
2554 
2555 			if (CGIG(check_shebang_line)) {
2556 				CG(skip_shebang) = 1;
2557 			}
2558 
2559 			switch (behavior) {
2560 				case PHP_MODE_STANDARD:
2561 					php_execute_script(&file_handle);
2562 					break;
2563 				case PHP_MODE_LINT:
2564 					PG(during_request_startup) = 0;
2565 					exit_status = php_lint_script(&file_handle);
2566 					if (exit_status == SUCCESS) {
2567 						zend_printf("No syntax errors detected in %s\n", file_handle.filename);
2568 					} else {
2569 						zend_printf("Errors parsing %s\n", file_handle.filename);
2570 					}
2571 					break;
2572 				case PHP_MODE_STRIP:
2573 					if (open_file_for_scanning(&file_handle) == SUCCESS) {
2574 						zend_strip();
2575 						zend_file_handle_dtor(&file_handle);
2576 						php_output_teardown();
2577 					}
2578 					return SUCCESS;
2579 					break;
2580 				case PHP_MODE_HIGHLIGHT:
2581 					{
2582 						zend_syntax_highlighter_ini syntax_highlighter_ini;
2583 
2584 						if (open_file_for_scanning(&file_handle) == SUCCESS) {
2585 							php_get_highlight_struct(&syntax_highlighter_ini);
2586 							zend_highlight(&syntax_highlighter_ini);
2587 							if (fastcgi) {
2588 								goto fastcgi_request_done;
2589 							}
2590 							zend_file_handle_dtor(&file_handle);
2591 							php_output_teardown();
2592 						}
2593 						return SUCCESS;
2594 					}
2595 					break;
2596 			}
2597 
2598 fastcgi_request_done:
2599 			{
2600 				if (SG(request_info).path_translated) {
2601 					efree(SG(request_info).path_translated);
2602 					SG(request_info).path_translated = NULL;
2603 				}
2604 
2605 				php_request_shutdown((void *) 0);
2606 
2607 				if (exit_status == 0) {
2608 					exit_status = EG(exit_status);
2609 				}
2610 
2611 				if (free_query_string && SG(request_info).query_string) {
2612 					free(SG(request_info).query_string);
2613 					SG(request_info).query_string = NULL;
2614 				}
2615 			}
2616 
2617 			if (!fastcgi) {
2618 				if (benchmark) {
2619 					if (warmup_repeats) {
2620 						warmup_repeats--;
2621 						if (!warmup_repeats) {
2622 #ifdef HAVE_GETTIMEOFDAY
2623 							gettimeofday(&start, NULL);
2624 #else
2625 							time(&start);
2626 #endif
2627 #ifdef HAVE_VALGRIND
2628 							CALLGRIND_START_INSTRUMENTATION;
2629 #endif
2630 						}
2631 						continue;
2632 					} else {
2633 						repeats--;
2634 						if (repeats > 0) {
2635 							script_file = NULL;
2636 							php_optind = orig_optind;
2637 							php_optarg = orig_optarg;
2638 							continue;
2639 						}
2640 					}
2641 				}
2642 				break;
2643 			}
2644 
2645 			/* only fastcgi will get here */
2646 			requests++;
2647 			if (max_requests && (requests == max_requests)) {
2648 				fcgi_finish_request(request, 1);
2649 				free(bindpath);
2650 				if (max_requests != 1) {
2651 					/* no need to return exit_status of the last request */
2652 					exit_status = 0;
2653 				}
2654 				break;
2655 			}
2656 			/* end of fastcgi loop */
2657 		}
2658 
2659 		if (request) {
2660 			fcgi_destroy_request(request);
2661 		}
2662 		fcgi_shutdown();
2663 
2664 		if (cgi_sapi_module.php_ini_path_override) {
2665 			free(cgi_sapi_module.php_ini_path_override);
2666 		}
2667 		if (cgi_sapi_module.ini_entries) {
2668 			free(cgi_sapi_module.ini_entries);
2669 		}
2670 	} zend_catch {
2671 		exit_status = 255;
2672 	} zend_end_try();
2673 
2674 out:
2675 	if (benchmark) {
2676 		int sec;
2677 #ifdef HAVE_GETTIMEOFDAY
2678 		int usec;
2679 
2680 		gettimeofday(&end, NULL);
2681 		sec = (int)(end.tv_sec - start.tv_sec);
2682 		if (end.tv_usec >= start.tv_usec) {
2683 			usec = (int)(end.tv_usec - start.tv_usec);
2684 		} else {
2685 			sec -= 1;
2686 			usec = (int)(end.tv_usec + 1000000 - start.tv_usec);
2687 		}
2688 		fprintf(stderr, "\nElapsed time: %d.%06d sec\n", sec, usec);
2689 #else
2690 		time(&end);
2691 		sec = (int)(end - start);
2692 		fprintf(stderr, "\nElapsed time: %d sec\n", sec);
2693 #endif
2694 	}
2695 
2696 parent_out:
2697 
2698 	SG(server_context) = NULL;
2699 	php_module_shutdown();
2700 	sapi_shutdown();
2701 
2702 #ifdef ZTS
2703 	tsrm_shutdown();
2704 #endif
2705 
2706 #if defined(PHP_WIN32) && ZEND_DEBUG && 0
2707 	_CrtDumpMemoryLeaks();
2708 #endif
2709 
2710 	return exit_status;
2711 }
2712 /* }}} */
2713