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