1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1997-2014 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: thttpd.c,v 1.1.1.4 2014/06/15 20:04:02 misho Exp $ */
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:
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:
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:
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:
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:
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:
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:
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:
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>