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