2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 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: David Hedbor <neotron@php.net> |
16 | Based on aolserver SAPI by Sascha Schumann <sascha@schumann.cx> |
17 +----------------------------------------------------------------------+
26 #include "php_globals.h"
29 #include "ext/standard/info.h"
31 #include "php_version.h"
34 /* Only valid if thread safety is enabled. */
41 * conflicts with pike avoided by only using long names. Requires a new
42 * Pike 0.7 since it was implemented for this interface only.
45 #define NO_PIKE_SHORTHAND
49 #include <pike_types.h>
50 #include <interpret.h>
51 #include <module_support.h>
59 #include <builtin_functions.h>
60 #include <operators.h>
62 #undef HIDE_GLOBAL_VARIABLES
63 #undef REVEAL_GLOBAL_VARIABLES
64 #define HIDE_GLOBAL_VARIABLES()
65 #define REVEAL_GLOBAL_VARIABLES()
67 /* php_roxen_request is per-request object storage */
71 struct mapping
*request_data
;
72 struct object
*my_fd_obj
;
78 /* Defines to get to the data supplied when the script is started. */
82 /* ZTS does work now, but it seems like it's faster using the "serialization"
83 * method I previously used. Thus it's not used unless ROXEN_USE_ZTS is defined.
86 /* Per thread storage area id... */
87 static int roxen_globals_id
;
89 # define GET_THIS() php_roxen_request *_request = ts_resource(roxen_globals_id)
90 # define THIS _request
92 static php_roxen_request
*current_request
= NULL
;
94 # define GET_THIS() current_request = ((php_roxen_request *)Pike_fp->current_storage)
95 # define THIS current_request
98 /* File descriptor integer. Used to write directly to the FD without
101 #define MY_FD (THIS->my_fd)
103 /* FD object. Really a PHPScript object from Pike which implements a couple
104 * of functions to handle headers, writing and buffering.
106 #define MY_FD_OBJ ((struct object *)(THIS->my_fd_obj))
108 /* Mapping with data supplied from the calling Roxen module. Contains
109 * a mapping with headers, an FD object etc.
111 #define REQUEST_DATA ((struct mapping *)(THIS->request_data))
114 #if defined(_REENTRANT) && !defined(ROXEN_USE_ZTS)
115 /* Lock used to serialize the PHP execution. If ROXEN_USE_ZTS is defined, we
116 * are using the PHP thread safe mechanism instead.
118 static PIKE_MUTEX_T roxen_php_execution_lock
;
119 # define PHP_INIT_LOCK() mt_init(&roxen_php_execution_lock)
120 # define PHP_LOCK(X) THREADS_ALLOW();mt_lock(&roxen_php_execution_lock);THREADS_DISALLOW()
121 # define PHP_UNLOCK(X) mt_unlock(&roxen_php_execution_lock);
122 # define PHP_DESTROY() mt_destroy(&roxen_php_execution_lock)
123 #else /* !_REENTRANT */
124 # define PHP_INIT_LOCK()
126 # define PHP_UNLOCK(X)
127 # define PHP_DESTROY()
128 #endif /* _REENTRANT */
130 extern int fd_from_object(struct object
*o
);
131 static unsigned char roxen_php_initialized
;
133 /* This allows calling of pike functions from the PHP callbacks,
134 * which requires the Pike interpreter to be locked.
136 #define THREAD_SAFE_RUN(COMMAND, what) do {\
137 struct thread_state *state;\
138 if((state = thread_state_for_id(th_self()))!=NULL) {\
139 if(!state->swapped) {\
142 mt_lock(&interpreter_lock);\
143 SWAP_IN_THREAD(state);\
145 SWAP_OUT_THREAD(state);\
146 mt_unlock(&interpreter_lock);\
151 struct program
*php_program
;
154 /* To avoid executing a PHP script from a PHP callback, which would
155 * create a deadlock, a global thread id is used. If the thread calling the
156 * php-script is the same as the current thread, it fails.
158 static int current_thread
= -1;
161 /* Low level header lookup. Basically looks for the named header in the mapping
162 * headers in the supplied options mapping.
165 static INLINE
struct svalue
*lookup_header(char *headername
)
167 struct svalue
*headers
, *value
;
168 struct pike_string
*sind
;
172 sind
= make_shared_string("env");
173 headers
= low_mapping_string_lookup(REQUEST_DATA
, sind
);
175 if(!headers
|| headers
->type
!= PIKE_T_MAPPING
) return NULL
;
176 sind
= make_shared_string(headername
);
177 value
= low_mapping_string_lookup(headers
->u
.mapping
, sind
);
179 if(!value
) return NULL
;
183 /* Lookup a header in the mapping and return the value as a string, or
184 * return the default if it's missing
186 INLINE
static char *lookup_string_header(char *headername
, char *default_value
)
188 struct svalue
*head
= NULL
;
189 THREAD_SAFE_RUN(head
= lookup_header(headername
), "header lookup");
190 if(!head
|| head
->type
!= PIKE_T_STRING
)
191 return default_value
;
192 return head
->u
.string
->str
;
195 /* Lookup a header in the mapping and return the value as if it's an integer
196 * and otherwise return the default.
198 INLINE
static int lookup_integer_header(char *headername
, int default_value
)
200 struct svalue
*head
= NULL
;
201 THREAD_SAFE_RUN(head
= lookup_header(headername
), "header lookup");
202 if(!head
|| head
->type
!= PIKE_T_INT
)
203 return default_value
;
204 return head
->u
.integer
;
208 * php_roxen_low_ub_write() writes data to the client connection. Might be
209 * rewritten to do more direct IO to save CPU and the need to lock the *
210 * interpreter for better threading.
214 php_roxen_low_ub_write(const char *str
, uint str_length TSRMLS_DC
) {
216 struct pike_string
*to_write
= NULL
;
221 if(!MY_FD_OBJ
->prog
) {
222 PG(connection_status
) = PHP_CONNECTION_ABORTED
;
226 to_write
= make_shared_binary_string(str
, str_length
);
227 push_string(to_write
);
228 safe_apply(MY_FD_OBJ
, "write", 1);
229 if(Pike_sp
[-1].type
== PIKE_T_INT
)
230 sent_bytes
= Pike_sp
[-1].u
.integer
;
232 if(sent_bytes
!= str_length
) {
233 /* This means the connection is closed. Dead. Gone. *sniff* */
234 php_handle_aborted_connection();
240 * php_roxen_sapi_ub_write() calls php_roxen_low_ub_write in a Pike thread
245 php_roxen_sapi_ub_write(const char *str
, uint str_length TSRMLS_DC
)
251 int sent_bytes
= 0, fd
= MY_FD
;
254 for(sent_bytes
=0;sent_bytes
< str_length
;)
257 written
= fd_write(fd
, str
+ sent_bytes
, str_length
- sent_bytes
);
263 /* This means the connection is closed. Dead. Gone. *sniff* */
264 PG(connection_status
) = PHP_CONNECTION_ABORTED
;
273 sent_bytes
+= written
;
277 THREAD_SAFE_RUN(sent_bytes
= php_roxen_low_ub_write(str
, str_length TSRMLS_CC
),
283 /* php_roxen_set_header() sets a header in the header mapping. Called in a
284 * thread safe manner from php_roxen_sapi_header_handler.
286 static void php_roxen_set_header(char *header_name
, char *value
, char *p
)
289 struct pike_string
*hval
, *ind
, *hind
;
290 struct mapping
*headermap
;
291 struct svalue
*s_headermap
;
295 hval
= make_shared_string(value
);
296 ind
= make_shared_string(" _headers");
297 hind
= make_shared_binary_string(header_name
,
298 (int)(p
- header_name
));
300 s_headermap
= low_mapping_string_lookup(REQUEST_DATA
, ind
);
303 struct svalue mappie
;
304 mappie
.type
= PIKE_T_MAPPING
;
305 headermap
= allocate_mapping(1);
306 mappie
.u
.mapping
= headermap
;
307 mapping_string_insert(REQUEST_DATA
, ind
, &mappie
);
308 free_mapping(headermap
);
310 headermap
= s_headermap
->u
.mapping
;
312 hsval
.type
= PIKE_T_STRING
;
313 hsval
.u
.string
= hval
;
314 mapping_string_insert(headermap
, hind
, &hsval
);
322 * php_roxen_sapi_header_handler() sets a HTTP reply header to be
323 * sent to the client.
326 php_roxen_sapi_header_handler(sapi_header_struct
*sapi_header
,
327 sapi_headers_struct
*sapi_headers TSRMLS_DC
)
329 char *header_name
, *header_content
, *p
;
330 header_name
= sapi_header
->header
;
331 header_content
= p
= strchr(header_name
, ':');
336 } while(*header_content
== ' ');
337 THREAD_SAFE_RUN(php_roxen_set_header(header_name
, header_content
, p
), "header handler");
339 sapi_free_header(sapi_header
);
344 * php_roxen_sapi_send_headers() flushes the headers to the client.
345 * Called before real content is sent by PHP.
349 php_roxen_low_send_headers(sapi_headers_struct
*sapi_headers TSRMLS_DC
)
351 struct pike_string
*ind
;
352 struct svalue
*s_headermap
;
357 if(!MY_FD_OBJ
->prog
) {
358 PG(connection_status
) = PHP_CONNECTION_ABORTED
;
360 return SAPI_HEADER_SEND_FAILED
;
362 ind
= make_shared_string(" _headers");
363 s_headermap
= low_mapping_string_lookup(REQUEST_DATA
, ind
);
366 push_int(SG(sapi_headers
).http_response_code
);
367 if(s_headermap
&& s_headermap
->type
== PIKE_T_MAPPING
)
368 ref_push_mapping(s_headermap
->u
.mapping
);
371 safe_apply(MY_FD_OBJ
, "send_headers", 2);
374 return SAPI_HEADER_SENT_SUCCESSFULLY
;
378 php_roxen_sapi_send_headers(sapi_headers_struct
*sapi_headers TSRMLS_DC
)
381 THREAD_SAFE_RUN(res
= php_roxen_low_send_headers(sapi_headers TSRMLS_CC
), "send headers");
386 * php_roxen_sapi_read_post() reads a specified number of bytes from
387 * the client. Used for POST/PUT requests.
390 INLINE
static int php_roxen_low_read_post(char *buf
, uint count_bytes
)
400 PG(connection_status
) = PHP_CONNECTION_ABORTED
;
404 push_int(count_bytes
);
405 safe_apply(MY_FD_OBJ
, "read_post", 1);
406 if(Pike_sp
[-1].type
== PIKE_T_STRING
) {
407 MEMCPY(buf
, Pike_sp
[-1].u
.string
->str
,
408 (total_read
= Pike_sp
[-1].u
.string
->len
));
409 buf
[total_read
] = '\0';
417 php_roxen_sapi_read_post(char *buf
, uint count_bytes TSRMLS_DC
)
420 THREAD_SAFE_RUN(total_read
= php_roxen_low_read_post(buf
, count_bytes
), "read post");
425 * php_roxen_sapi_read_cookies() returns the Cookie header from
426 * the HTTP request header
430 php_roxen_sapi_read_cookies(TSRMLS_D
)
433 cookies
= lookup_string_header("HTTP_COOKIE", NULL
);
437 static void php_info_roxen(ZEND_MODULE_INFO_FUNC_ARGS
)
440 php_info_print_table_start();
441 php_info_print_table_row(2, "SAPI module version", "$Id$");
442 /* php_info_print_table_row(2, "Build date", Ns_InfoBuildDate());
443 php_info_print_table_row(2, "Config file path", Ns_InfoConfigFile());
444 php_info_print_table_row(2, "Error Log path", Ns_InfoErrorLog());
445 php_info_print_table_row(2, "Installation path", Ns_InfoHomePath());
446 php_info_print_table_row(2, "Hostname of server", Ns_InfoHostname());
447 php_info_print_table_row(2, "Source code label", Ns_InfoLabel());
448 php_info_print_table_row(2, "Server platform", Ns_InfoPlatform());
449 snprintf(buf, 511, "%s/%s", Ns_InfoServerName(), Ns_InfoServerVersion());
450 php_info_print_table_row(2, "Server version", buf);
451 snprintf(buf, 511, "%d day(s), %02d:%02d:%02d",
453 (uptime / 3600) % 24,
456 php_info_print_table_row(2, "Server uptime", buf);
458 php_info_print_table_end();
461 static zend_module_entry php_roxen_module
= {
462 STANDARD_MODULE_HEADER
,
471 STANDARD_MODULE_PROPERTIES
474 static int php_roxen_startup(sapi_module_struct
*sapi_module
)
476 if(php_module_startup(sapi_module
, &php_roxen_module
, 1) == FAILURE
) {
483 /* this structure is static (as in "it does not change") */
485 static sapi_module_struct roxen_sapi_module
= {
488 php_roxen_startup
, /* startup */
489 php_module_shutdown_wrapper
, /* shutdown */
491 NULL
, /* deactivate */
492 php_roxen_sapi_ub_write
, /* unbuffered write */
496 php_error
, /* error handler */
497 php_roxen_sapi_header_handler
, /* header handler */
498 php_roxen_sapi_send_headers
, /* send headers handler */
499 NULL
, /* send header handler */
500 php_roxen_sapi_read_post
, /* read POST data */
501 php_roxen_sapi_read_cookies
, /* read Cookies */
502 NULL
, /* register server variables */
503 NULL
, /* Log message */
504 NULL
, /* Get request time */
505 NULL
, /* Child terminate */
507 STANDARD_SAPI_MODULE_PROPERTIES
511 * php_roxen_hash_environment() populates the php script environment
512 * with a number of variables. HTTP_* variables are created for
513 * the HTTP header data, so that a script can access these.
515 #define ADD_STRING(name) \
516 MAKE_STD_ZVAL(zvalue); \
517 zvalue->type = IS_STRING; \
518 zvalue->value.str.len = strlen(buf); \
519 zvalue->value.str.val = estrndup(buf, zvalue->value.str.len); \
520 zend_hash_update(&EG(symbol_table), name, sizeof(name), \
521 &zvalue, sizeof(zval *), NULL)
524 php_roxen_hash_environment(TSRMLS_D
)
529 struct svalue
*headers
;
530 struct pike_string
*sind
;
531 struct array
*indices
;
532 struct svalue
*ind
, *val
;
536 sind
= make_shared_string("env");
537 headers
= low_mapping_string_lookup(REQUEST_DATA
, sind
);
539 if(headers
&& headers
->type
== PIKE_T_MAPPING
) {
540 indices
= mapping_indices(headers
->u
.mapping
);
541 for(i
= 0; i
< indices
->size
; i
++) {
542 ind
= &indices
->item
[i
];
543 val
= low_mapping_lookup(headers
->u
.mapping
, ind
);
544 if(ind
&& ind
->type
== PIKE_T_STRING
&&
545 val
&& val
->type
== PIKE_T_STRING
) {
547 buf_len
= MIN(511, ind
->u
.string
->len
);
548 strncpy(buf
, ind
->u
.string
->str
, buf_len
);
549 buf
[buf_len
] = '\0'; /* Terminate correctly */
550 MAKE_STD_ZVAL(zvalue
);
551 zvalue
->type
= IS_STRING
;
552 zvalue
->value
.str
.len
= val
->u
.string
->len
;
553 zvalue
->value
.str
.val
= estrndup(val
->u
.string
->str
, zvalue
->value
.str
.len
);
555 zend_hash_update(&EG(symbol_table
), buf
, buf_len
+ 1, &zvalue
, sizeof(zval
*), NULL
);
562 MAKE_STD_ZVAL(zvalue);
563 zvalue->type = IS_LONG;
564 zvalue->value.lval = Ns_InfoBootTime();
565 zend_hash_update(&EG(symbol_table), "SERVER_BOOTTIME", sizeof("SERVER_BOOTTIME"), &zvalue, sizeof(zval *), NULL);
570 * php_roxen_module_main() is called by the per-request handler and
571 * "executes" the script
574 static int php_roxen_module_main(TSRMLS_D
)
578 zend_file_handle file_handle
;
583 file_handle
.type
= ZEND_HANDLE_FILENAME
;
584 file_handle
.filename
= THIS
->filename
;
585 file_handle
.free_filename
= 0;
586 file_handle
.opened_path
= NULL
;
589 res
= php_request_startup(TSRMLS_C
);
594 php_roxen_hash_environment(TSRMLS_C
);
596 php_execute_script(&file_handle TSRMLS_CC
);
597 php_request_shutdown(NULL
);
603 * The php_roxen_request_handler() is called per request and handles
604 * everything for one request.
607 void f_php_roxen_request_handler(INT32 args
)
609 struct object
*my_fd_obj
;
610 struct mapping
*request_data
;
611 struct svalue
*done_callback
, *raw_fd
;
612 struct pike_string
*script
, *ind
;
619 if(current_thread
== th_self())
620 php_error(E_WARNING
, "PHP5.Interpreter->run: Tried to run a PHP-script from a PHP "
622 get_all_args("PHP5.Interpreter->run", args
, "%S%m%O%*", &script
,
623 &request_data
, &my_fd_obj
, &done_callback
);
624 if(done_callback
->type
!= PIKE_T_FUNCTION
)
625 php_error(E_WARNING
, "PHP5.Interpreter->run: Bad argument 4, expected function.\n");
626 PHP_LOCK(THIS
); /* Need to lock here or reusing the same object might cause
627 * problems in changing stuff in that object */
628 #ifndef ROXEN_USE_ZTS
631 THIS
->request_data
= request_data
;
632 THIS
->my_fd_obj
= my_fd_obj
;
633 THIS
->filename
= script
->str
;
634 current_thread
= th_self();
635 SG(request_info
).query_string
= lookup_string_header("QUERY_STRING", 0);
636 SG(server_context
) = (void *)1; /* avoid server_context == NULL */
638 /* path_translated is apparently the absolute path to the file, not
639 the translated PATH_INFO
641 SG(request_info
).path_translated
=
642 lookup_string_header("SCRIPT_FILENAME", NULL
);
643 SG(request_info
).request_uri
= lookup_string_header("DOCUMENT_URI", NULL
);
644 if(!SG(request_info
).request_uri
)
645 SG(request_info
).request_uri
= lookup_string_header("SCRIPT_NAME", NULL
);
646 SG(request_info
).request_method
= lookup_string_header("REQUEST_METHOD", "GET");
647 SG(request_info
).content_length
= lookup_integer_header("HTTP_CONTENT_LENGTH", 0);
648 SG(request_info
).content_type
= lookup_string_header("HTTP_CONTENT_TYPE", NULL
);
649 SG(sapi_headers
).http_response_code
= 200;
651 /* FIXME: Check for auth stuff needs to be fixed... */
652 SG(request_info
).auth_user
= NULL
;
653 SG(request_info
).auth_password
= NULL
;
655 ind
= make_shared_binary_string("my_fd", 5);
656 raw_fd
= low_mapping_string_lookup(THIS
->request_data
, ind
);
657 if(raw_fd
&& raw_fd
->type
== PIKE_T_OBJECT
)
659 int fd
= fd_from_object(raw_fd
->u
.object
);
661 php_error(E_WARNING
, "PHP5.Interpreter->run: my_fd object not open or not an FD.\n");
666 status
= php_roxen_module_main(TSRMLS_C
);
669 apply_svalue(done_callback
, 0);
677 /* Clear the object global struct */
678 static void clear_struct(struct object
*o
)
680 MEMSET(Pike_fp
->current_storage
, 0, sizeof(php_roxen_request
));
685 * pike_module_init() is called by Pike once at startup
687 * This functions allocates basic structures
690 void pike_module_init( void )
692 if (!roxen_php_initialized
) {
694 tsrm_startup(1, 1, 0, NULL
);
696 ts_allocate_id(&roxen_globals_id
, sizeof(php_roxen_request
), NULL
, NULL
);
699 sapi_startup(&roxen_sapi_module
);
700 /*php_roxen_startup(&roxen_sapi_module); removed - should be called from SAPI activation*/
701 roxen_php_initialized
= 1;
704 start_new_program(); /* Text */
705 ADD_STORAGE(php_roxen_request
);
706 set_init_callback(clear_struct
);
707 pike_add_function("run", f_php_roxen_request_handler
,
708 "function(string, mapping, object, function:int)", 0);
709 add_program_constant("Interpreter", (php_program
= end_program()), 0);
713 * pike_module_exit() performs the last steps before the
714 * server exists. Shutdowns basic services and frees memory
717 void pike_module_exit(void)
719 roxen_php_initialized
= 0;
720 roxen_sapi_module
.shutdown(&roxen_sapi_module
);
721 if(php_program
) free_program(php_program
);