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 | Author: Edin Kadribasic <edink@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php_embed.h"
18 #include "ext/standard/php_standard.h"
19 #include "ext/standard/dl_arginfo.h"
20
21 #ifdef PHP_WIN32
22 #include <io.h>
23 #include <fcntl.h>
24 #endif
25
26 const char HARDCODED_INI[] =
27 "html_errors=0\n"
28 "register_argc_argv=1\n"
29 "implicit_flush=1\n"
30 "output_buffering=0\n"
31 "max_execution_time=0\n"
32 "max_input_time=-1\n\0";
33
34 #if defined(PHP_WIN32) && defined(ZTS)
ZEND_TSRMLS_CACHE_DEFINE()35 ZEND_TSRMLS_CACHE_DEFINE()
36 #endif
37
38 static char* php_embed_read_cookies(void)
39 {
40 return NULL;
41 }
42
php_embed_deactivate(void)43 static int php_embed_deactivate(void)
44 {
45 fflush(stdout);
46 return SUCCESS;
47 }
48
49 /* Here we prefer to use write(), which is unbuffered, over fwrite(), which is
50 * buffered. Using an unbuffered write operation to stdout will ensure PHP's
51 * output buffering feature does not compete with a SAPI output buffer and
52 * therefore we avoid situations wherein flushing the output buffer results in
53 * nondeterministic behavior.
54 */
php_embed_single_write(const char * str,size_t str_length)55 static inline size_t php_embed_single_write(const char *str, size_t str_length)
56 {
57 #ifdef PHP_WRITE_STDOUT
58 zend_long ret;
59
60 ret = write(STDOUT_FILENO, str, str_length);
61 if (ret <= 0) return 0;
62 return ret;
63 #else
64 size_t ret;
65
66 ret = fwrite(str, 1, MIN(str_length, 16384), stdout);
67 return ret;
68 #endif
69 }
70
71 /* SAPIs only have unbuffered write operations. This is because PHP's output
72 * buffering feature will handle any buffering of the output and invoke the
73 * SAPI unbuffered write operation when it flushes the buffer.
74 */
php_embed_ub_write(const char * str,size_t str_length)75 static size_t php_embed_ub_write(const char *str, size_t str_length)
76 {
77 const char *ptr = str;
78 size_t remaining = str_length;
79 size_t ret;
80
81 while (remaining > 0) {
82 ret = php_embed_single_write(ptr, remaining);
83 if (!ret) {
84 php_handle_aborted_connection();
85 }
86 ptr += ret;
87 remaining -= ret;
88 }
89
90 return str_length;
91 }
92
php_embed_flush(void * server_context)93 static void php_embed_flush(void *server_context)
94 {
95 if (fflush(stdout)==EOF) {
96 php_handle_aborted_connection();
97 }
98 }
99
php_embed_send_header(sapi_header_struct * sapi_header,void * server_context)100 static void php_embed_send_header(sapi_header_struct *sapi_header, void *server_context)
101 {
102 }
103
104 /* The SAPI error logger that is called when the 'error_log' INI setting is not
105 * set.
106 *
107 * https://www.php.net/manual/en/errorfunc.configuration.php#ini.error-log
108 */
php_embed_log_message(const char * message,int syslog_type_int)109 static void php_embed_log_message(const char *message, int syslog_type_int)
110 {
111 fprintf(stderr, "%s\n", message);
112 }
113
php_embed_register_variables(zval * track_vars_array)114 static void php_embed_register_variables(zval *track_vars_array)
115 {
116 php_import_environment_variables(track_vars_array);
117 }
118
119 /* Module initialization (MINIT) */
php_embed_startup(sapi_module_struct * sapi_module)120 static int php_embed_startup(sapi_module_struct *sapi_module)
121 {
122 if (php_module_startup(sapi_module, NULL, 0) == FAILURE) {
123 return FAILURE;
124 }
125 return SUCCESS;
126 }
127
128 EMBED_SAPI_API sapi_module_struct php_embed_module = {
129 "embed", /* name */
130 "PHP Embedded Library", /* pretty name */
131
132 php_embed_startup, /* startup */
133 php_module_shutdown_wrapper, /* shutdown */
134
135 NULL, /* activate */
136 php_embed_deactivate, /* deactivate */
137
138 php_embed_ub_write, /* unbuffered write */
139 php_embed_flush, /* flush */
140 NULL, /* get uid */
141 NULL, /* getenv */
142
143 php_error, /* error handler */
144
145 NULL, /* header handler */
146 NULL, /* send headers handler */
147 php_embed_send_header, /* send header handler */
148
149 NULL, /* read POST data */
150 php_embed_read_cookies, /* read Cookies */
151
152 php_embed_register_variables, /* register server variables */
153 php_embed_log_message, /* Log message */
154 NULL, /* Get request time */
155 NULL, /* Child terminate */
156
157 STANDARD_SAPI_MODULE_PROPERTIES
158 };
159 /* }}} */
160
161 static const zend_function_entry additional_functions[] = {
162 ZEND_FE(dl, arginfo_dl)
163 ZEND_FE_END
164 };
165
php_embed_init(int argc,char ** argv)166 EMBED_SAPI_API int php_embed_init(int argc, char **argv)
167 {
168 #if defined(SIGPIPE) && defined(SIG_IGN)
169 signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
170 that sockets created via fsockopen()
171 don't kill PHP if the remote site
172 closes it. in apache|apxs mode apache
173 does that for us! thies@thieso.net
174 20000419 */
175 #endif
176
177 #ifdef ZTS
178 php_tsrm_startup();
179 # ifdef PHP_WIN32
180 ZEND_TSRMLS_CACHE_UPDATE();
181 # endif
182 #endif
183
184 zend_signal_startup();
185
186 /* SAPI initialization (SINIT)
187 *
188 * Initialize the SAPI globals (memset to 0). After this point we can set
189 * SAPI globals via the SG() macro.
190 *
191 * Reentrancy startup.
192 *
193 * This also sets 'php_embed_module.ini_entries = NULL' so we cannot
194 * allocate the INI entries until after this call.
195 */
196 sapi_startup(&php_embed_module);
197
198 #ifdef PHP_WIN32
199 _fmode = _O_BINARY; /*sets default for file streams to binary */
200 setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
201 setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
202 setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
203 #endif
204
205 /* This hard-coded string of INI settings is parsed and read into PHP's
206 * configuration hash table at the very end of php_init_config(). This
207 * means these settings will overwrite any INI settings that were set from
208 * an INI file.
209 *
210 * To provide overwritable INI defaults, hook the ini_defaults function
211 * pointer that is part of the sapi_module_struct
212 * (php_embed_module.ini_defaults).
213 *
214 * void (*ini_defaults)(HashTable *configuration_hash);
215 *
216 * This callback is invoked as soon as the configuration hash table is
217 * allocated so any INI settings added via this callback will have the
218 * lowest precedence and will allow INI files to overwrite them.
219 */
220 php_embed_module.ini_entries = malloc(sizeof(HARDCODED_INI));
221 memcpy(php_embed_module.ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI));
222
223 /* SAPI-provided functions. */
224 php_embed_module.additional_functions = additional_functions;
225
226 if (argv) {
227 php_embed_module.executable_location = argv[0];
228 }
229
230 /* Module initialization (MINIT) */
231 if (php_embed_module.startup(&php_embed_module) == FAILURE) {
232 return FAILURE;
233 }
234
235 /* Do not chdir to the script's directory. This is akin to calling the CGI
236 * SAPI with '-C'.
237 */
238 SG(options) |= SAPI_OPTION_NO_CHDIR;
239
240 SG(request_info).argc=argc;
241 SG(request_info).argv=argv;
242
243 /* Request initialization (RINIT) */
244 if (php_request_startup() == FAILURE) {
245 php_module_shutdown();
246 return FAILURE;
247 }
248
249 SG(headers_sent) = 1;
250 SG(request_info).no_headers = 1;
251 php_register_variable("PHP_SELF", "-", NULL);
252
253 return SUCCESS;
254 }
255
php_embed_shutdown(void)256 EMBED_SAPI_API void php_embed_shutdown(void)
257 {
258 /* Request shutdown (RSHUTDOWN) */
259 php_request_shutdown((void *) 0);
260
261 /* Module shutdown (MSHUTDOWN) */
262 php_module_shutdown();
263
264 /* SAPI shutdown (SSHUTDOWN) */
265 sapi_shutdown();
266
267 #ifdef ZTS
268 tsrm_shutdown();
269 #endif
270
271 if (php_embed_module.ini_entries) {
272 free(php_embed_module.ini_entries);
273 php_embed_module.ini_entries = NULL;
274 }
275 }
276