1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2016 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Sascha Schumann <sascha@schumann.cx> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* $Id$ */
20
21 #include "php.h"
22 #include "SAPI.h"
23 #include "php_main.h"
24 #include "php_thttpd.h"
25 #include "php_variables.h"
26 #include "version.h"
27 #include "php_ini.h"
28 #include "zend_highlight.h"
29
30 #include "ext/standard/php_smart_str.h"
31
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/uio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37
38 #ifdef HAVE_GETNAMEINFO
39 #include <sys/socket.h>
40 #include <netdb.h>
41 #endif
42
43 typedef struct {
44 httpd_conn *hc;
45 void (*on_close)(int);
46
47 size_t unconsumed_length;
48 smart_str sbuf;
49 int seen_cl;
50 int seen_cn;
51 } php_thttpd_globals;
52
53 #define PHP_SYS_CALL(x) do { x } while (n == -1 && errno == EINTR)
54
55 #ifdef PREMIUM_THTTPD
56 # define do_keep_alive persistent
57 #endif
58
59 #ifdef ZTS
60 static int thttpd_globals_id;
61 #define TG(v) TSRMG(thttpd_globals_id, php_thttpd_globals *, v)
62 #else
63 static php_thttpd_globals thttpd_globals;
64 #define TG(v) (thttpd_globals.v)
65 #endif
66
sapi_thttpd_ub_write(const char * str,uint str_length TSRMLS_DC)67 static int sapi_thttpd_ub_write(const char *str, uint str_length TSRMLS_DC)
68 {
69 int n;
70 uint sent = 0;
71
72 if (TG(sbuf).c != 0) {
73 smart_str_appendl_ex(&TG(sbuf), str, str_length, 1);
74 return str_length;
75 }
76
77 while (str_length > 0) {
78 PHP_SYS_CALL(n = send(TG(hc)->conn_fd, str, str_length, 0););
79
80 if (n == -1) {
81 if (errno == EAGAIN) {
82 smart_str_appendl_ex(&TG(sbuf), str, str_length, 1);
83
84 return sent + str_length;
85 } else
86 php_handle_aborted_connection();
87 }
88
89 TG(hc)->bytes_sent += n;
90 str += n;
91 sent += n;
92 str_length -= n;
93 }
94
95 return sent;
96 }
97
98 #define COMBINE_HEADERS 64
99
100 #if defined(IOV_MAX)
101 # if IOV_MAX - 64 <= 0
102 # define SERIALIZE_HEADERS
103 # endif
104 #endif
105
do_writev(struct iovec * vec,int nvec,int len TSRMLS_DC)106 static int do_writev(struct iovec *vec, int nvec, int len TSRMLS_DC)
107 {
108 int n;
109
110 assert(nvec <= IOV_MAX);
111
112 if (TG(sbuf).c == 0) {
113 PHP_SYS_CALL(n = writev(TG(hc)->conn_fd, vec, nvec););
114
115 if (n == -1) {
116 if (errno == EAGAIN) {
117 n = 0;
118 } else {
119 php_handle_aborted_connection();
120 }
121 }
122
123
124 TG(hc)->bytes_sent += n;
125 } else {
126 n = 0;
127 }
128
129 if (n < len) {
130 int i;
131
132 /* merge all unwritten data into sbuf */
133 for (i = 0; i < nvec; vec++, i++) {
134 /* has this vector been written completely? */
135 if (n >= vec->iov_len) {
136 /* yes, proceed */
137 n -= vec->iov_len;
138 continue;
139 }
140
141 if (n > 0) {
142 /* adjust vector */
143 vec->iov_base = (char *) vec->iov_base + n;
144 vec->iov_len -= n;
145 n = 0;
146 }
147
148 smart_str_appendl_ex(&TG(sbuf), vec->iov_base, vec->iov_len, 1);
149 }
150 }
151
152 return 0;
153 }
154
155 #ifdef SERIALIZE_HEADERS
156 # define ADD_VEC(str,l) smart_str_appendl(&vec_str, (str), (l))
157 # define VEC_BASE() smart_str vec_str = {0}
158 # define VEC_FREE() smart_str_free(&vec_str)
159 #else
160 # define ADD_VEC(str,l) vec[n].iov_base=str;len += (vec[n].iov_len=l); n++
161 # define VEC_BASE() struct iovec vec[COMBINE_HEADERS]
162 # define VEC_FREE() do {} while (0)
163 #endif
164
165 #define ADD_VEC_S(str) ADD_VEC((str), sizeof(str)-1)
166
167 #define CL_TOKEN "Content-length: "
168 #define CN_TOKEN "Connection: "
169 #define KA_DO "Connection: keep-alive\r\n"
170 #define KA_NO "Connection: close\r\n"
171 #define DEF_CT "Content-Type: text/html\r\n"
172
sapi_thttpd_send_headers(sapi_headers_struct * sapi_headers TSRMLS_DC)173 static int sapi_thttpd_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
174 {
175 char buf[1024], *p;
176 VEC_BASE();
177 int n = 0;
178 zend_llist_position pos;
179 sapi_header_struct *h;
180 size_t len = 0;
181
182 if (!SG(sapi_headers).http_status_line) {
183 ADD_VEC_S("HTTP/1.1 ");
184 p = smart_str_print_long(buf+sizeof(buf)-1,
185 SG(sapi_headers).http_response_code);
186 ADD_VEC(p, strlen(p));
187 ADD_VEC_S(" HTTP\r\n");
188 } else {
189 ADD_VEC(SG(sapi_headers).http_status_line,
190 strlen(SG(sapi_headers).http_status_line));
191 ADD_VEC("\r\n", 2);
192 }
193 TG(hc)->status = SG(sapi_headers).http_response_code;
194
195 if (SG(sapi_headers).send_default_content_type) {
196 ADD_VEC(DEF_CT, strlen(DEF_CT));
197 }
198
199 h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
200 while (h) {
201
202 switch (h->header[0]) {
203 case 'c': case 'C':
204 if (!TG(seen_cl) && strncasecmp(h->header, CL_TOKEN, sizeof(CL_TOKEN)-1) == 0) {
205 TG(seen_cl) = 1;
206 } else if (!TG(seen_cn) && strncasecmp(h->header, CN_TOKEN, sizeof(CN_TOKEN)-1) == 0) {
207 TG(seen_cn) = 1;
208 }
209 }
210
211 ADD_VEC(h->header, h->header_len);
212 #ifndef SERIALIZE_HEADERS
213 if (n >= COMBINE_HEADERS - 1) {
214 len = do_writev(vec, n, len TSRMLS_CC);
215 n = 0;
216 }
217 #endif
218 ADD_VEC("\r\n", 2);
219
220 h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
221 }
222
223 if (TG(seen_cl) && !TG(seen_cn) && TG(hc)->do_keep_alive) {
224 ADD_VEC(KA_DO, sizeof(KA_DO)-1);
225 } else {
226 TG(hc)->do_keep_alive = 0;
227 ADD_VEC(KA_NO, sizeof(KA_NO)-1);
228 }
229
230 ADD_VEC("\r\n", 2);
231
232 #ifdef SERIALIZE_HEADERS
233 sapi_thttpd_ub_write(vec_str.c, vec_str.len TSRMLS_CC);
234 #else
235 do_writev(vec, n, len TSRMLS_CC);
236 #endif
237
238 VEC_FREE();
239
240 return SAPI_HEADER_SENT_SUCCESSFULLY;
241 }
242
243 /* to understand this, read cgi_interpose_input() in libhttpd.c */
244 #define SIZEOF_UNCONSUMED_BYTES() (TG(hc)->read_idx - TG(hc)->checked_idx)
245 #define CONSUME_BYTES(n) do { TG(hc)->checked_idx += (n); } while (0)
246
247
sapi_thttpd_read_post(char * buffer,uint count_bytes TSRMLS_DC)248 static int sapi_thttpd_read_post(char *buffer, uint count_bytes TSRMLS_DC)
249 {
250 size_t read_bytes = 0;
251
252 if (TG(unconsumed_length) > 0) {
253 read_bytes = MIN(TG(unconsumed_length), count_bytes);
254 memcpy(buffer, TG(hc)->read_buf + TG(hc)->checked_idx, read_bytes);
255 TG(unconsumed_length) -= read_bytes;
256 CONSUME_BYTES(read_bytes);
257 }
258
259 return read_bytes;
260 }
261
sapi_thttpd_read_cookies(TSRMLS_D)262 static char *sapi_thttpd_read_cookies(TSRMLS_D)
263 {
264 return TG(hc)->cookie;
265 }
266
267 #define BUF_SIZE 512
268 #define ADD_STRING_EX(name,buf) \
269 php_register_variable(name, buf, track_vars_array TSRMLS_CC)
270 #define ADD_STRING(name) ADD_STRING_EX((name), buf)
271
sapi_thttpd_register_variables(zval * track_vars_array TSRMLS_DC)272 static void sapi_thttpd_register_variables(zval *track_vars_array TSRMLS_DC)
273 {
274 char buf[BUF_SIZE + 1];
275 char *p;
276
277 php_register_variable("PHP_SELF", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
278 php_register_variable("SERVER_SOFTWARE", SERVER_SOFTWARE, track_vars_array TSRMLS_CC);
279 php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
280 php_register_variable("REQUEST_METHOD", (char *) SG(request_info).request_method, track_vars_array TSRMLS_CC);
281 php_register_variable("REQUEST_URI", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
282 php_register_variable("PATH_TRANSLATED", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
283
284 if (TG(hc)->one_one) {
285 php_register_variable("SERVER_PROTOCOL", "HTTP/1.1", track_vars_array TSRMLS_CC);
286 } else {
287 php_register_variable("SERVER_PROTOCOL", "HTTP/1.0", track_vars_array TSRMLS_CC);
288 }
289
290 p = httpd_ntoa(&TG(hc)->client_addr);
291
292 ADD_STRING_EX("REMOTE_ADDR", p);
293 ADD_STRING_EX("REMOTE_HOST", p);
294
295 ADD_STRING_EX("SERVER_PORT",
296 smart_str_print_long(buf + sizeof(buf) - 1,
297 TG(hc)->hs->port));
298
299 buf[0] = '/';
300 memcpy(buf + 1, TG(hc)->pathinfo, strlen(TG(hc)->pathinfo) + 1);
301 ADD_STRING("PATH_INFO");
302
303 buf[0] = '/';
304 memcpy(buf + 1, TG(hc)->origfilename, strlen(TG(hc)->origfilename) + 1);
305 ADD_STRING("SCRIPT_NAME");
306
307 #define CONDADD(name, field) \
308 if (TG(hc)->field[0]) { \
309 php_register_variable(#name, TG(hc)->field, track_vars_array TSRMLS_CC); \
310 }
311
312 CONDADD(QUERY_STRING, query);
313 CONDADD(HTTP_HOST, hdrhost);
314 CONDADD(HTTP_REFERER, referer);
315 CONDADD(HTTP_USER_AGENT, useragent);
316 CONDADD(HTTP_ACCEPT, accept);
317 CONDADD(HTTP_ACCEPT_LANGUAGE, acceptl);
318 CONDADD(HTTP_ACCEPT_ENCODING, accepte);
319 CONDADD(HTTP_COOKIE, cookie);
320 CONDADD(CONTENT_TYPE, contenttype);
321 CONDADD(REMOTE_USER, remoteuser);
322 CONDADD(SERVER_PROTOCOL, protocol);
323
324 if (TG(hc)->contentlength != -1) {
325 ADD_STRING_EX("CONTENT_LENGTH",
326 smart_str_print_long(buf + sizeof(buf) - 1,
327 TG(hc)->contentlength));
328 }
329
330 if (TG(hc)->authorization[0])
331 php_register_variable("AUTH_TYPE", "Basic", track_vars_array TSRMLS_CC);
332 }
333
PHP_MINIT_FUNCTION(thttpd)334 static PHP_MINIT_FUNCTION(thttpd)
335 {
336 return SUCCESS;
337 }
338
339 static zend_module_entry php_thttpd_module = {
340 STANDARD_MODULE_HEADER,
341 "thttpd",
342 NULL,
343 PHP_MINIT(thttpd),
344 NULL,
345 NULL,
346 NULL,
347 NULL, /* info */
348 NULL,
349 STANDARD_MODULE_PROPERTIES
350 };
351
php_thttpd_startup(sapi_module_struct * sapi_module)352 static int php_thttpd_startup(sapi_module_struct *sapi_module)
353 {
354 #if PHP_API_VERSION >= 20020918
355 if (php_module_startup(sapi_module, &php_thttpd_module, 1) == FAILURE) {
356 #else
357 if (php_module_startup(sapi_module) == FAILURE
358 || zend_startup_module(&php_thttpd_module) == FAILURE) {
359 #endif
360 return FAILURE;
361 }
362 return SUCCESS;
363 }
364
365 static int sapi_thttpd_get_fd(int *nfd TSRMLS_DC)
366 {
367 if (nfd) *nfd = TG(hc)->conn_fd;
368 return SUCCESS;
369 }
370
371 static sapi_module_struct thttpd_sapi_module = {
372 "thttpd",
373 "thttpd",
374
375 php_thttpd_startup,
376 php_module_shutdown_wrapper,
377
378 NULL, /* activate */
379 NULL, /* deactivate */
380
381 sapi_thttpd_ub_write,
382 NULL,
383 NULL, /* get uid */
384 NULL, /* getenv */
385
386 php_error,
387
388 NULL,
389 sapi_thttpd_send_headers,
390 NULL,
391 sapi_thttpd_read_post,
392 sapi_thttpd_read_cookies,
393
394 sapi_thttpd_register_variables,
395 NULL, /* Log message */
396 NULL, /* Get request time */
397 NULL, /* Child terminate */
398
399 NULL, /* php.ini path override */
400 NULL, /* Block interruptions */
401 NULL, /* Unblock interruptions */
402
403 NULL,
404 NULL,
405 NULL,
406 0,
407 sapi_thttpd_get_fd
408 };
409
410 static void thttpd_module_main(int show_source TSRMLS_DC)
411 {
412 zend_file_handle file_handle;
413
414 if (php_request_startup(TSRMLS_C) == FAILURE) {
415 return;
416 }
417
418 if (show_source) {
419 zend_syntax_highlighter_ini syntax_highlighter_ini;
420
421 php_get_highlight_struct(&syntax_highlighter_ini);
422 highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini TSRMLS_CC);
423 } else {
424 file_handle.type = ZEND_HANDLE_FILENAME;
425 file_handle.filename = SG(request_info).path_translated;
426 file_handle.free_filename = 0;
427 file_handle.opened_path = NULL;
428
429 php_execute_script(&file_handle TSRMLS_CC);
430 }
431
432 php_request_shutdown(NULL);
433 }
434
435 static void thttpd_request_ctor(TSRMLS_D)
436 {
437 smart_str s = {0};
438
439 TG(seen_cl) = 0;
440 TG(seen_cn) = 0;
441 TG(sbuf).c = 0;
442 SG(request_info).query_string = TG(hc)->query?strdup(TG(hc)->query):NULL;
443
444 smart_str_appends_ex(&s, TG(hc)->hs->cwd, 1);
445 smart_str_appends_ex(&s, TG(hc)->expnfilename, 1);
446 smart_str_0(&s);
447 SG(request_info).path_translated = s.c;
448
449 s.c = NULL;
450 smart_str_appendc_ex(&s, '/', 1);
451 smart_str_appends_ex(&s, TG(hc)->origfilename, 1);
452 smart_str_0(&s);
453 SG(request_info).request_uri = s.c;
454 SG(request_info).request_method = httpd_method_str(TG(hc)->method);
455 if (TG(hc)->one_one) SG(request_info).proto_num = 1001;
456 else SG(request_info).proto_num = 1000;
457 SG(sapi_headers).http_response_code = 200;
458 if (TG(hc)->contenttype)
459 SG(request_info).content_type = strdup(TG(hc)->contenttype);
460 SG(request_info).content_length = TG(hc)->contentlength == -1 ? 0
461 : TG(hc)->contentlength;
462
463 TG(unconsumed_length) = SG(request_info).content_length;
464
465 php_handle_auth_data(TG(hc)->authorization TSRMLS_CC);
466 }
467
468 static void thttpd_request_dtor(TSRMLS_D)
469 {
470 smart_str_free_ex(&TG(sbuf), 1);
471 if (SG(request_info).query_string)
472 free(SG(request_info).query_string);
473 free(SG(request_info).request_uri);
474 free(SG(request_info).path_translated);
475 if (SG(request_info).content_type)
476 free(SG(request_info).content_type);
477 }
478
479 #ifdef ZTS
480
481 #ifdef TSRM_ST
482 #define thread_create_simple_detached(n) st_thread_create(n, NULL, 0, 0)
483 #define thread_usleep(n) st_usleep(n)
484 #define thread_exit() st_thread_exit(NULL)
485 /* No preemption, simple operations are safe */
486 #define thread_atomic_inc(n) (++n)
487 #define thread_atomic_dec(n) (--n)
488 #else
489 #error No thread primitives available
490 #endif
491
492 /* We might want to replace this with a STAILQ */
493 typedef struct qreq {
494 httpd_conn *hc;
495 struct qreq *next;
496 } qreq_t;
497
498 static MUTEX_T qr_lock;
499 static qreq_t *queued_requests;
500 static qreq_t *last_qr;
501 static int nr_free_threads;
502 static int nr_threads;
503 static int max_threads = 50;
504
505 #define HANDLE_STRINGS() { \
506 HANDLE_STR(encodedurl); \
507 HANDLE_STR(decodedurl); \
508 HANDLE_STR(origfilename); \
509 HANDLE_STR(expnfilename); \
510 HANDLE_STR(pathinfo); \
511 HANDLE_STR(query); \
512 HANDLE_STR(referer); \
513 HANDLE_STR(useragent); \
514 HANDLE_STR(accept); \
515 HANDLE_STR(accepte); \
516 HANDLE_STR(acceptl); \
517 HANDLE_STR(cookie); \
518 HANDLE_STR(contenttype); \
519 HANDLE_STR(authorization); \
520 HANDLE_STR(remoteuser); \
521 }
522
523 static httpd_conn *duplicate_conn(httpd_conn *hc, httpd_conn *nhc)
524 {
525 memcpy(nhc, hc, sizeof(*nhc));
526
527 #define HANDLE_STR(m) nhc->m = nhc->m ? strdup(nhc->m) : NULL
528 HANDLE_STRINGS();
529 #undef HANDLE_STR
530
531 return nhc;
532 }
533
534 static void destroy_conn(httpd_conn *hc)
535 {
536 #define HANDLE_STR(m) if (hc->m) free(hc->m)
537 HANDLE_STRINGS();
538 #undef HANDLE_STR
539 }
540
541 static httpd_conn *dequeue_request(void)
542 {
543 httpd_conn *ret = NULL;
544 qreq_t *m;
545
546 tsrm_mutex_lock(qr_lock);
547 if (queued_requests) {
548 m = queued_requests;
549 ret = m->hc;
550 if (!(queued_requests = m->next))
551 last_qr = NULL;
552 free(m);
553 }
554 tsrm_mutex_unlock(qr_lock);
555
556 return ret;
557 }
558
559 static void *worker_thread(void *);
560
561 static void queue_request(httpd_conn *hc)
562 {
563 qreq_t *m;
564 httpd_conn *nhc;
565
566 /* Mark as long-running request */
567 hc->file_address = (char *) 1;
568
569 /*
570 * We cannot synchronously revoke accesses to hc in the worker
571 * thread, so we need to pass a copy of hc to the worker thread.
572 */
573 nhc = malloc(sizeof *nhc);
574 duplicate_conn(hc, nhc);
575
576 /* Allocate request queue container */
577 m = malloc(sizeof *m);
578 m->hc = nhc;
579 m->next = NULL;
580
581 tsrm_mutex_lock(qr_lock);
582 /* Create new threads when reaching a certain threshold */
583 if (nr_threads < max_threads && nr_free_threads < 2) {
584 nr_threads++; /* protected by qr_lock */
585
586 thread_atomic_inc(nr_free_threads);
587 thread_create_simple_detached(worker_thread);
588 }
589 /* Insert container into request queue */
590 if (queued_requests)
591 last_qr->next = m;
592 else
593 queued_requests = m;
594 last_qr = m;
595 tsrm_mutex_unlock(qr_lock);
596 }
597
598 static off_t thttpd_real_php_request(httpd_conn *hc, int TSRMLS_DC);
599
600 static void *worker_thread(void *dummy)
601 {
602 int do_work = 50;
603 httpd_conn *hc;
604
605 while (do_work) {
606 hc = dequeue_request();
607
608 if (!hc) {
609 /* do_work--; */
610 thread_usleep(500000);
611 continue;
612 }
613 /* do_work = 50; */
614
615 thread_atomic_dec(nr_free_threads);
616
617 thttpd_real_php_request(hc, 0 TSRMLS_CC);
618 shutdown(hc->conn_fd, 0);
619 destroy_conn(hc);
620 free(hc);
621
622 thread_atomic_inc(nr_free_threads);
623 }
624 thread_atomic_dec(nr_free_threads);
625 thread_atomic_dec(nr_threads);
626 thread_exit();
627 }
628
629 static void remove_dead_conn(int fd)
630 {
631 qreq_t *m, *prev = NULL;
632
633 tsrm_mutex_lock(qr_lock);
634 m = queued_requests;
635 while (m) {
636 if (m->hc->conn_fd == fd) {
637 if (prev)
638 if (!(prev->next = m->next))
639 last_qr = prev;
640 else
641 if (!(queued_requests = m->next))
642 last_qr = NULL;
643 destroy_conn(m->hc);
644 free(m->hc);
645 free(m);
646 break;
647 }
648 prev = m;
649 m = m->next;
650 }
651 tsrm_mutex_unlock(qr_lock);
652 }
653
654 #endif
655
656 static off_t thttpd_real_php_request(httpd_conn *hc, int show_source TSRMLS_DC)
657 {
658 TG(hc) = hc;
659 hc->bytes_sent = 0;
660
661 if (hc->contentlength != -1) {
662 hc->should_linger = 1;
663 hc->do_keep_alive = 0;
664 }
665
666 if (hc->contentlength != -1
667 && SIZEOF_UNCONSUMED_BYTES() < hc->contentlength) {
668 hc->read_body_into_mem = 1;
669 return 0;
670 }
671
672 thttpd_request_ctor(TSRMLS_C);
673
674 thttpd_module_main(show_source TSRMLS_CC);
675
676 /* disable kl, if no content-length was seen or Connection: was set */
677 if (TG(seen_cl) == 0 || TG(seen_cn) == 1) {
678 TG(hc)->do_keep_alive = 0;
679 }
680
681 if (TG(sbuf).c != 0) {
682 if (TG(hc)->response)
683 free(TG(hc)->response);
684
685 TG(hc)->response = TG(sbuf).c;
686 TG(hc)->responselen = TG(sbuf).len;
687 TG(hc)->maxresponse = TG(sbuf).a;
688
689 TG(sbuf).c = 0;
690 TG(sbuf).len = 0;
691 TG(sbuf).a = 0;
692 }
693
694 thttpd_request_dtor(TSRMLS_C);
695
696 return 0;
697 }
698
699 off_t thttpd_php_request(httpd_conn *hc, int show_source)
700 {
701 #ifdef ZTS
702 queue_request(hc);
703 #else
704 TSRMLS_FETCH();
705 return thttpd_real_php_request(hc, show_source TSRMLS_CC);
706 #endif
707 }
708
709 void thttpd_register_on_close(void (*arg)(int))
710 {
711 TSRMLS_FETCH();
712 TG(on_close) = arg;
713 }
714
715 void thttpd_closed_conn(int fd)
716 {
717 TSRMLS_FETCH();
718 if (TG(on_close)) TG(on_close)(fd);
719 }
720
721 int thttpd_get_fd(void)
722 {
723 TSRMLS_FETCH();
724 return TG(hc)->conn_fd;
725 }
726
727 void thttpd_set_dont_close(void)
728 {
729 TSRMLS_FETCH();
730 #ifndef PREMIUM_THTTPD
731 TG(hc)->file_address = (char *) 1;
732 #endif
733 }
734
735
736 void thttpd_php_init(void)
737 {
738 char *ini;
739
740 #ifdef ZTS
741 tsrm_startup(1, 1, 0, NULL);
742 ts_allocate_id(&thttpd_globals_id, sizeof(php_thttpd_globals), NULL, NULL);
743 qr_lock = tsrm_mutex_alloc();
744 thttpd_register_on_close(remove_dead_conn);
745 #endif
746
747 if ((ini = getenv("PHP_INI_PATH"))) {
748 thttpd_sapi_module.php_ini_path_override = ini;
749 }
750
751 sapi_startup(&thttpd_sapi_module);
752 thttpd_sapi_module.startup(&thttpd_sapi_module);
753
754 {
755 TSRMLS_FETCH();
756
757 SG(server_context) = (void *) 1;
758 }
759 }
760
761 void thttpd_php_shutdown(void)
762 {
763 TSRMLS_FETCH();
764
765 if (SG(server_context) != NULL) {
766 thttpd_sapi_module.shutdown(&thttpd_sapi_module);
767 sapi_shutdown();
768 #ifdef ZTS
769 tsrm_shutdown();
770 #endif
771 }
772 }
773