nxt_php_sapi.c (1091:2a71417d297f) nxt_php_sapi.c (1102:29421134ed99)
1/*
2 * Copyright (C) Max Romanov
3 * Copyright (C) Valentin V. Bartenev
4 * Copyright (C) NGINX, Inc.
5 */
6
7#include "php.h"
8#include "SAPI.h"
9#include "php_main.h"
10#include "php_variables.h"
11
12#include <nxt_main.h>
13#include <nxt_router.h>
14#include <nxt_unit.h>
15#include <nxt_unit_request.h>
16
17
18#if PHP_MAJOR_VERSION >= 7
19# define NXT_PHP7 1
20# if PHP_MINOR_VERSION >= 1
21# define NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1
22# else
23# define NXT_HAVE_PHP_INTERRUPTS 1
24# endif
25# define NXT_HAVE_PHP_IGNORE_CWD 1
26#else
27# define NXT_HAVE_PHP_INTERRUPTS 1
28# if PHP_MINOR_VERSION >= 4
29# define NXT_HAVE_PHP_IGNORE_CWD 1
30# endif
31#endif
32
33
34typedef struct nxt_php_run_ctx_s nxt_php_run_ctx_t;
35
36#ifdef NXT_PHP7
37typedef int (*nxt_php_disable_t)(char *p, size_t size);
38#else
39typedef int (*nxt_php_disable_t)(char *p, uint TSRMLS_DC);
40#endif
41
42
43static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
44
45static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t);
46static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t);
47nxt_inline u_char *nxt_realpath(const void *c);
48
49static void nxt_php_request_handler(nxt_unit_request_info_t *req);
50
51static int nxt_php_startup(sapi_module_struct *sapi_module);
52static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options,
53 int type);
54static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value,
55 int type);
56static void nxt_php_disable(nxt_task_t *task, const char *type,
57 nxt_str_t *value, char **ptr, nxt_php_disable_t disable);
58static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC);
59static char *nxt_php_read_cookies(TSRMLS_D);
60static void nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name,
61 nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC);
62nxt_inline void nxt_php_set_str(nxt_unit_request_info_t *req, const char *name,
63 nxt_str_t *s, zval *track_vars_array TSRMLS_DC);
64static void nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name,
65 const char *str, uint32_t len, zval *track_vars_array TSRMLS_DC);
66static void nxt_php_register_variables(zval *track_vars_array TSRMLS_DC);
67#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
68static void nxt_php_log_message(char *message, int syslog_type_int);
69#else
70static void nxt_php_log_message(char *message TSRMLS_DC);
71#endif
72
73#ifdef NXT_PHP7
74static size_t nxt_php_unbuffered_write(const char *str,
75 size_t str_length TSRMLS_DC);
76static size_t nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC);
77#else
78static int nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC);
79static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC);
80#endif
81
82
83static sapi_module_struct nxt_php_sapi_module =
84{
85 (char *) "cli-server",
86 (char *) "unit",
87
88 nxt_php_startup, /* startup */
89 php_module_shutdown_wrapper, /* shutdown */
90
91 NULL, /* activate */
92 NULL, /* deactivate */
93
94 nxt_php_unbuffered_write, /* unbuffered write */
95 NULL, /* flush */
96 NULL, /* get uid */
97 NULL, /* getenv */
98
99 php_error, /* error handler */
100
101 NULL, /* header handler */
102 nxt_php_send_headers, /* send headers handler */
103 NULL, /* send header handler */
104
105 nxt_php_read_post, /* read POST data */
106 nxt_php_read_cookies, /* read Cookies */
107
108 nxt_php_register_variables, /* register server variables */
109 nxt_php_log_message, /* log message */
110 NULL, /* get request time */
111 NULL, /* terminate process */
112
113 NULL, /* php_ini_path_override */
114#ifdef NXT_HAVE_PHP_INTERRUPTS
115 NULL, /* block_interruptions */
116 NULL, /* unblock_interruptions */
117#endif
118 NULL, /* default_post_reader */
119 NULL, /* treat_data */
120 NULL, /* executable_location */
121
122 0, /* php_ini_ignore */
123#ifdef NXT_HAVE_PHP_IGNORE_CWD
124 1, /* php_ini_ignore_cwd */
125#endif
126 NULL, /* get_fd */
127
128 NULL, /* force_http_10 */
129
130 NULL, /* get_target_uid */
131 NULL, /* get_target_gid */
132
133 NULL, /* input_filter */
134
135 NULL, /* ini_defaults */
136 0, /* phpinfo_as_text */
137
138 NULL, /* ini_entries */
139 NULL, /* additional_functions */
140 NULL /* input_filter_init */
141};
142
143
144struct nxt_php_run_ctx_s {
145 char *cookie;
146 nxt_str_t path_info;
147 nxt_str_t script_name;
148 nxt_str_t script_filename;
149 nxt_unit_request_info_t *req;
150};
151
152
153static nxt_str_t nxt_php_root;
154static nxt_str_t nxt_php_script_name;
155static nxt_str_t nxt_php_script_filename;
156static nxt_str_t nxt_php_index = nxt_string("index.php");
157
158
159static uint32_t compat[] = {
160 NXT_VERNUM, NXT_DEBUG,
161};
162
163
164NXT_EXPORT nxt_app_module_t nxt_app_module = {
165 sizeof(compat),
166 compat,
167 nxt_string("php"),
168 PHP_VERSION,
169 NULL,
170 nxt_php_init,
171};
172
173
174static nxt_task_t *nxt_php_task;
175#ifdef ZTS
176static void ***tsrm_ls;
177#endif
178
179
180static nxt_int_t
181nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
182{
1/*
2 * Copyright (C) Max Romanov
3 * Copyright (C) Valentin V. Bartenev
4 * Copyright (C) NGINX, Inc.
5 */
6
7#include "php.h"
8#include "SAPI.h"
9#include "php_main.h"
10#include "php_variables.h"
11
12#include <nxt_main.h>
13#include <nxt_router.h>
14#include <nxt_unit.h>
15#include <nxt_unit_request.h>
16
17
18#if PHP_MAJOR_VERSION >= 7
19# define NXT_PHP7 1
20# if PHP_MINOR_VERSION >= 1
21# define NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1
22# else
23# define NXT_HAVE_PHP_INTERRUPTS 1
24# endif
25# define NXT_HAVE_PHP_IGNORE_CWD 1
26#else
27# define NXT_HAVE_PHP_INTERRUPTS 1
28# if PHP_MINOR_VERSION >= 4
29# define NXT_HAVE_PHP_IGNORE_CWD 1
30# endif
31#endif
32
33
34typedef struct nxt_php_run_ctx_s nxt_php_run_ctx_t;
35
36#ifdef NXT_PHP7
37typedef int (*nxt_php_disable_t)(char *p, size_t size);
38#else
39typedef int (*nxt_php_disable_t)(char *p, uint TSRMLS_DC);
40#endif
41
42
43static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf);
44
45static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t);
46static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t);
47nxt_inline u_char *nxt_realpath(const void *c);
48
49static void nxt_php_request_handler(nxt_unit_request_info_t *req);
50
51static int nxt_php_startup(sapi_module_struct *sapi_module);
52static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options,
53 int type);
54static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value,
55 int type);
56static void nxt_php_disable(nxt_task_t *task, const char *type,
57 nxt_str_t *value, char **ptr, nxt_php_disable_t disable);
58static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC);
59static char *nxt_php_read_cookies(TSRMLS_D);
60static void nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name,
61 nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC);
62nxt_inline void nxt_php_set_str(nxt_unit_request_info_t *req, const char *name,
63 nxt_str_t *s, zval *track_vars_array TSRMLS_DC);
64static void nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name,
65 const char *str, uint32_t len, zval *track_vars_array TSRMLS_DC);
66static void nxt_php_register_variables(zval *track_vars_array TSRMLS_DC);
67#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
68static void nxt_php_log_message(char *message, int syslog_type_int);
69#else
70static void nxt_php_log_message(char *message TSRMLS_DC);
71#endif
72
73#ifdef NXT_PHP7
74static size_t nxt_php_unbuffered_write(const char *str,
75 size_t str_length TSRMLS_DC);
76static size_t nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC);
77#else
78static int nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC);
79static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC);
80#endif
81
82
83static sapi_module_struct nxt_php_sapi_module =
84{
85 (char *) "cli-server",
86 (char *) "unit",
87
88 nxt_php_startup, /* startup */
89 php_module_shutdown_wrapper, /* shutdown */
90
91 NULL, /* activate */
92 NULL, /* deactivate */
93
94 nxt_php_unbuffered_write, /* unbuffered write */
95 NULL, /* flush */
96 NULL, /* get uid */
97 NULL, /* getenv */
98
99 php_error, /* error handler */
100
101 NULL, /* header handler */
102 nxt_php_send_headers, /* send headers handler */
103 NULL, /* send header handler */
104
105 nxt_php_read_post, /* read POST data */
106 nxt_php_read_cookies, /* read Cookies */
107
108 nxt_php_register_variables, /* register server variables */
109 nxt_php_log_message, /* log message */
110 NULL, /* get request time */
111 NULL, /* terminate process */
112
113 NULL, /* php_ini_path_override */
114#ifdef NXT_HAVE_PHP_INTERRUPTS
115 NULL, /* block_interruptions */
116 NULL, /* unblock_interruptions */
117#endif
118 NULL, /* default_post_reader */
119 NULL, /* treat_data */
120 NULL, /* executable_location */
121
122 0, /* php_ini_ignore */
123#ifdef NXT_HAVE_PHP_IGNORE_CWD
124 1, /* php_ini_ignore_cwd */
125#endif
126 NULL, /* get_fd */
127
128 NULL, /* force_http_10 */
129
130 NULL, /* get_target_uid */
131 NULL, /* get_target_gid */
132
133 NULL, /* input_filter */
134
135 NULL, /* ini_defaults */
136 0, /* phpinfo_as_text */
137
138 NULL, /* ini_entries */
139 NULL, /* additional_functions */
140 NULL /* input_filter_init */
141};
142
143
144struct nxt_php_run_ctx_s {
145 char *cookie;
146 nxt_str_t path_info;
147 nxt_str_t script_name;
148 nxt_str_t script_filename;
149 nxt_unit_request_info_t *req;
150};
151
152
153static nxt_str_t nxt_php_root;
154static nxt_str_t nxt_php_script_name;
155static nxt_str_t nxt_php_script_filename;
156static nxt_str_t nxt_php_index = nxt_string("index.php");
157
158
159static uint32_t compat[] = {
160 NXT_VERNUM, NXT_DEBUG,
161};
162
163
164NXT_EXPORT nxt_app_module_t nxt_app_module = {
165 sizeof(compat),
166 compat,
167 nxt_string("php"),
168 PHP_VERSION,
169 NULL,
170 nxt_php_init,
171};
172
173
174static nxt_task_t *nxt_php_task;
175#ifdef ZTS
176static void ***tsrm_ls;
177#endif
178
179
180static nxt_int_t
181nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf)
182{
183 u_char *p;
183 u_char *p, *tmp;
184 nxt_str_t ini_path;
185 nxt_str_t *root, *script_filename, *script_name, *index;
186 nxt_port_t *my_port, *main_port;
187 nxt_runtime_t *rt;
188 nxt_unit_ctx_t *unit_ctx;
189 nxt_unit_init_t php_init;
190 nxt_conf_value_t *value;
191 nxt_php_app_conf_t *c;
192
193 static nxt_str_t file_str = nxt_string("file");
194 static nxt_str_t user_str = nxt_string("user");
195 static nxt_str_t admin_str = nxt_string("admin");
196
197 nxt_php_task = task;
198
199 c = &conf->u.php;
200
201 if (c->root == NULL) {
202 nxt_alert(task, "php root is empty");
203 return NXT_ERROR;
204 }
205
206 root = &nxt_php_root;
207 script_filename = &nxt_php_script_filename;
208 script_name = &nxt_php_script_name;
209 index = &nxt_php_index;
210
211 root->start = nxt_realpath(c->root);
212 if (nxt_slow_path(root->start == NULL)) {
213 nxt_alert(task, "root realpath(%s) failed %E", c->root, nxt_errno);
214 return NXT_ERROR;
215 }
216
217 root->length = nxt_strlen(root->start);
218
219 nxt_php_str_trim_trail(root, '/');
220
221 if (c->script.length > 0) {
222 nxt_php_str_trim_lead(&c->script, '/');
223
184 nxt_str_t ini_path;
185 nxt_str_t *root, *script_filename, *script_name, *index;
186 nxt_port_t *my_port, *main_port;
187 nxt_runtime_t *rt;
188 nxt_unit_ctx_t *unit_ctx;
189 nxt_unit_init_t php_init;
190 nxt_conf_value_t *value;
191 nxt_php_app_conf_t *c;
192
193 static nxt_str_t file_str = nxt_string("file");
194 static nxt_str_t user_str = nxt_string("user");
195 static nxt_str_t admin_str = nxt_string("admin");
196
197 nxt_php_task = task;
198
199 c = &conf->u.php;
200
201 if (c->root == NULL) {
202 nxt_alert(task, "php root is empty");
203 return NXT_ERROR;
204 }
205
206 root = &nxt_php_root;
207 script_filename = &nxt_php_script_filename;
208 script_name = &nxt_php_script_name;
209 index = &nxt_php_index;
210
211 root->start = nxt_realpath(c->root);
212 if (nxt_slow_path(root->start == NULL)) {
213 nxt_alert(task, "root realpath(%s) failed %E", c->root, nxt_errno);
214 return NXT_ERROR;
215 }
216
217 root->length = nxt_strlen(root->start);
218
219 nxt_php_str_trim_trail(root, '/');
220
221 if (c->script.length > 0) {
222 nxt_php_str_trim_lead(&c->script, '/');
223
224 p = nxt_malloc(root->length + 1 + c->script.length + 1);
225 if (nxt_slow_path(p == NULL)) {
224 tmp = nxt_malloc(root->length + 1 + c->script.length + 1);
225 if (nxt_slow_path(tmp == NULL)) {
226 return NXT_ERROR;
227 }
228
226 return NXT_ERROR;
227 }
228
229 p = tmp;
230
229 p = nxt_cpymem(p, root->start, root->length);
230 *p++ = '/';
231
232 p = nxt_cpymem(p, c->script.start, c->script.length);
233 *p = '\0';
234
231 p = nxt_cpymem(p, root->start, root->length);
232 *p++ = '/';
233
234 p = nxt_cpymem(p, c->script.start, c->script.length);
235 *p = '\0';
236
235 script_filename->start = nxt_realpath(p);
237 script_filename->start = nxt_realpath(tmp);
236 if (nxt_slow_path(script_filename->start == NULL)) {
238 if (nxt_slow_path(script_filename->start == NULL)) {
237 nxt_alert(task, "script realpath(%s) failed %E", p, nxt_errno);
239 nxt_alert(task, "script realpath(%s) failed %E", tmp, nxt_errno);
238 return NXT_ERROR;
239 }
240
240 return NXT_ERROR;
241 }
242
241 nxt_free(p);
243 nxt_free(tmp);
242
243 script_filename->length = nxt_strlen(script_filename->start);
244
245 if (!nxt_str_start(script_filename, root->start, root->length)) {
246 nxt_alert(task, "script is not under php root");
247 return NXT_ERROR;
248 }
249
250 script_name->length = c->script.length + 1;
251 script_name->start = nxt_malloc(script_name->length);
252 if (nxt_slow_path(script_name->start == NULL)) {
253 return NXT_ERROR;
254 }
255
256 script_name->start[0] = '/';
257 nxt_memcpy(script_name->start + 1, c->script.start, c->script.length);
258
259 nxt_log_error(NXT_LOG_INFO, task->log,
260 "(ABS_MODE) php script \"%V\" root: \"%V\"",
261 script_name, root);
262
263 } else {
264 nxt_log_error(NXT_LOG_INFO, task->log,
265 "(non ABS_MODE) php root: \"%V\"", root);
266 }
267
268 if (c->index.length > 0) {
269 index->length = c->index.length;
270 index->start = nxt_malloc(index->length);
271 if (nxt_slow_path(index->start == NULL)) {
272 return NXT_ERROR;
273 }
274
275 nxt_memcpy(index->start, c->index.start, c->index.length);
276 }
277
278#ifdef ZTS
279 tsrm_startup(1, 1, 0, NULL);
280 tsrm_ls = ts_resource(0);
281#endif
282
283#if defined(NXT_PHP7) && defined(ZEND_SIGNALS)
284
285#if (NXT_ZEND_SIGNAL_STARTUP)
286 zend_signal_startup();
287#elif defined(ZTS)
288#error PHP is built with thread safety and broken signals.
289#endif
290
291#endif
292
293 sapi_startup(&nxt_php_sapi_module);
294
295 if (c->options != NULL) {
296 value = nxt_conf_get_object_member(c->options, &file_str, NULL);
297
298 if (value != NULL) {
299 nxt_conf_get_string(value, &ini_path);
300
301 p = nxt_malloc(ini_path.length + 1);
302 if (nxt_slow_path(p == NULL)) {
303 return NXT_ERROR;
304 }
305
306 nxt_php_sapi_module.php_ini_path_override = (char *) p;
307
308 p = nxt_cpymem(p, ini_path.start, ini_path.length);
309 *p = '\0';
310 }
311 }
312
313 nxt_php_startup(&nxt_php_sapi_module);
314
315 if (c->options != NULL) {
316 value = nxt_conf_get_object_member(c->options, &admin_str, NULL);
317 nxt_php_set_options(task, value, ZEND_INI_SYSTEM);
318
319 value = nxt_conf_get_object_member(c->options, &user_str, NULL);
320 nxt_php_set_options(task, value, ZEND_INI_USER);
321 }
322
323 nxt_memzero(&php_init, sizeof(nxt_unit_init_t));
324
325 rt = task->thread->runtime;
326
327 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
328 if (nxt_slow_path(main_port == NULL)) {
329 return NXT_ERROR;
330 }
331
332 my_port = nxt_runtime_port_find(rt, nxt_pid, 0);
333 if (nxt_slow_path(my_port == NULL)) {
334 return NXT_ERROR;
335 }
336
337 php_init.callbacks.request_handler = nxt_php_request_handler;
338 php_init.ready_port.id.pid = main_port->pid;
339 php_init.ready_port.id.id = main_port->id;
340 php_init.ready_port.out_fd = main_port->pair[1];
341
342 nxt_fd_blocking(task, main_port->pair[1]);
343
344 php_init.ready_stream = my_port->process->init->stream;
345
346 php_init.read_port.id.pid = my_port->pid;
347 php_init.read_port.id.id = my_port->id;
348 php_init.read_port.in_fd = my_port->pair[0];
349
350 nxt_fd_blocking(task, my_port->pair[0]);
351
352 php_init.log_fd = 2;
353
354 unit_ctx = nxt_unit_init(&php_init);
355 if (nxt_slow_path(unit_ctx == NULL)) {
356 return NXT_ERROR;
357 }
358
359 nxt_unit_run(unit_ctx);
360
361 nxt_unit_done(unit_ctx);
362
363 exit(0);
364
365 return NXT_OK;
366}
367
368
369static void
370nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type)
371{
372 uint32_t next;
373 nxt_str_t name, value;
374 nxt_conf_value_t *value_obj;
375
376 if (options != NULL) {
377 next = 0;
378
379 for ( ;; ) {
380 value_obj = nxt_conf_next_object_member(options, &name, &next);
381 if (value_obj == NULL) {
382 break;
383 }
384
385 nxt_conf_get_string(value_obj, &value);
386
387 if (nxt_php_alter_option(&name, &value, type) != NXT_OK) {
388 nxt_log(task, NXT_LOG_ERR,
389 "setting PHP option \"%V: %V\" failed", &name, &value);
390 continue;
391 }
392
393 if (nxt_str_eq(&name, "disable_functions", 17)) {
394 nxt_php_disable(task, "function", &value,
395 &PG(disable_functions),
396 zend_disable_function);
397 continue;
398 }
399
400 if (nxt_str_eq(&name, "disable_classes", 15)) {
401 nxt_php_disable(task, "class", &value,
402 &PG(disable_classes),
403 zend_disable_class);
404 continue;
405 }
406 }
407 }
408}
409
410
411#if (NXT_PHP7)
412
413static nxt_int_t
414nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
415{
416 zend_string *zs;
417 zend_ini_entry *ini_entry;
418
419 ini_entry = zend_hash_str_find_ptr(EG(ini_directives),
420 (char *) name->start, name->length);
421
422 if (ini_entry == NULL) {
423 return NXT_ERROR;
424 }
425
426 /* PHP exits on memory allocation errors. */
427 zs = zend_string_init((char *) value->start, value->length, 1);
428
429 if (ini_entry->on_modify
430 && ini_entry->on_modify(ini_entry, zs, ini_entry->mh_arg1,
431 ini_entry->mh_arg2, ini_entry->mh_arg3,
432 ZEND_INI_STAGE_ACTIVATE)
433 != SUCCESS)
434 {
435 zend_string_release(zs);
436 return NXT_ERROR;
437 }
438
439 ini_entry->value = zs;
440 ini_entry->modifiable = type;
441
442 return NXT_OK;
443}
444
445#else /* PHP 5. */
446
447static nxt_int_t
448nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
449{
450 char *cstr;
451 zend_ini_entry *ini_entry;
452 char buf[256];
453
454 if (nxt_slow_path(name->length >= sizeof(buf))) {
455 return NXT_ERROR;
456 }
457
458 nxt_memcpy(buf, name->start, name->length);
459 buf[name->length] = '\0';
460
461 if (zend_hash_find(EG(ini_directives), buf, name->length + 1,
462 (void **) &ini_entry)
463 == FAILURE)
464 {
465 return NXT_ERROR;
466 }
467
468 cstr = nxt_malloc(value->length + 1);
469 if (nxt_slow_path(cstr == NULL)) {
470 return NXT_ERROR;
471 }
472
473 nxt_memcpy(cstr, value->start, value->length);
474 cstr[value->length] = '\0';
475
476 if (ini_entry->on_modify
477 && ini_entry->on_modify(ini_entry, cstr, value->length,
478 ini_entry->mh_arg1, ini_entry->mh_arg2,
479 ini_entry->mh_arg3, ZEND_INI_STAGE_ACTIVATE
480 TSRMLS_CC)
481 != SUCCESS)
482 {
483 nxt_free(cstr);
484 return NXT_ERROR;
485 }
486
487 ini_entry->value = cstr;
488 ini_entry->value_length = value->length;
489 ini_entry->modifiable = type;
490
491 return NXT_OK;
492}
493
494#endif
495
496
497static void
498nxt_php_disable(nxt_task_t *task, const char *type, nxt_str_t *value,
499 char **ptr, nxt_php_disable_t disable)
500{
501 char c, *p, *start;
502
503 p = nxt_malloc(value->length + 1);
504 if (nxt_slow_path(p == NULL)) {
505 return;
506 }
507
508 /*
509 * PHP frees this memory on module shutdown.
510 * See core_globals_dtor() for details.
511 */
512 *ptr = p;
513
514 nxt_memcpy(p, value->start, value->length);
515 p[value->length] = '\0';
516
517 start = p;
518
519 do {
520 c = *p;
521
522 if (c == ' ' || c == ',' || c == '\0') {
523
524 if (p != start) {
525 *p = '\0';
526
527#ifdef NXT_PHP7
528 if (disable(start, p - start)
529#else
530 if (disable(start, p - start TSRMLS_CC)
531#endif
532 != SUCCESS)
533 {
534 nxt_log(task, NXT_LOG_ERR,
535 "PHP: failed to disable \"%s\": no such %s",
536 start, type);
537 }
538 }
539
540 start = p + 1;
541 }
542
543 p++;
544
545 } while (c != '\0');
546}
547
548
549static void
550nxt_php_str_trim_trail(nxt_str_t *str, u_char t)
551{
552 while (str->length > 0 && str->start[str->length - 1] == t) {
553 str->length--;
554 }
555
556 str->start[str->length] = '\0';
557}
558
559
560static void
561nxt_php_str_trim_lead(nxt_str_t *str, u_char t)
562{
563 while (str->length > 0 && str->start[0] == t) {
564 str->length--;
565 str->start++;
566 }
567}
568
569
570nxt_inline u_char *
571nxt_realpath(const void *c)
572{
573 return (u_char *) realpath(c, NULL);
574}
575
576
577static void
578nxt_php_request_handler(nxt_unit_request_info_t *req)
579{
580 int rc;
581 u_char *p;
582 nxt_str_t path, script_name;
583 nxt_unit_field_t *f;
584 zend_file_handle file_handle;
585 nxt_php_run_ctx_t run_ctx, *ctx;
586 nxt_unit_request_t *r;
587
588 nxt_memzero(&run_ctx, sizeof(run_ctx));
589
590 ctx = &run_ctx;
591 ctx->req = req;
592
593 r = req->request;
594
595 path.length = r->path_length;
596 path.start = nxt_unit_sptr_get(&r->path);
597
598 if (nxt_php_script_filename.start == NULL) {
599 ctx->path_info.start = (u_char *) strstr((char *) path.start, ".php/");
600 if (ctx->path_info.start != NULL) {
601 ctx->path_info.start += 4;
602 path.length = ctx->path_info.start - path.start;
603
604 ctx->path_info.length = r->path_length - path.length;
605 }
606
607 if (path.start[path.length - 1] == '/') {
608 script_name = nxt_php_index;
609
610 } else {
611 script_name.length = 0;
612 script_name.start = NULL;
613 }
614
615 ctx->script_filename.length = nxt_php_root.length + path.length
616 + script_name.length;
617 p = nxt_malloc(ctx->script_filename.length + 1);
618 if (nxt_slow_path(p == NULL)) {
619 nxt_unit_request_done(req, NXT_UNIT_ERROR);
620
621 return;
622 }
623
624 ctx->script_filename.start = p;
625
626 ctx->script_name.length = path.length + script_name.length;
627 ctx->script_name.start = p + nxt_php_root.length;
628
629 p = nxt_cpymem(p, nxt_php_root.start, nxt_php_root.length);
630 p = nxt_cpymem(p, path.start, path.length);
631
632 if (script_name.length > 0) {
633 p = nxt_cpymem(p, script_name.start, script_name.length);
634 }
635
636 *p = '\0';
637
638 } else {
639 ctx->script_filename = nxt_php_script_filename;
640 ctx->script_name = nxt_php_script_name;
641 }
642
643 SG(server_context) = ctx;
644 SG(request_info).request_uri = nxt_unit_sptr_get(&r->target);
645 SG(request_info).request_method = nxt_unit_sptr_get(&r->method);
646
647 SG(request_info).proto_num = 1001;
648
649 SG(request_info).query_string = r->query.offset
650 ? nxt_unit_sptr_get(&r->query) : NULL;
651 SG(request_info).content_length = r->content_length;
652
653 if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
654 f = r->fields + r->content_type_field;
655
656 SG(request_info).content_type = nxt_unit_sptr_get(&f->value);
657 }
658
659 if (r->cookie_field != NXT_UNIT_NONE_FIELD) {
660 f = r->fields + r->cookie_field;
661
662 ctx->cookie = nxt_unit_sptr_get(&f->value);
663 }
664
665 SG(sapi_headers).http_response_code = 200;
666
667 SG(request_info).path_translated = NULL;
668
669 file_handle.type = ZEND_HANDLE_FILENAME;
670 file_handle.filename = (char *) ctx->script_filename.start;
671 file_handle.free_filename = 0;
672 file_handle.opened_path = NULL;
673
674 nxt_unit_req_debug(req, "handle.filename = '%s'",
675 ctx->script_filename.start);
676
677 if (nxt_php_script_filename.start != NULL) {
678 nxt_unit_req_debug(req, "run script %.*s in absolute mode",
679 (int) nxt_php_script_filename.length,
680 (char *) nxt_php_script_filename.start);
681
682 } else {
683 nxt_unit_req_debug(req, "run script %.*s",
684 (int) ctx->script_filename.length,
685 (char *) ctx->script_filename.start);
686 }
687
688#if (NXT_PHP7)
689 if (nxt_slow_path(php_request_startup() == FAILURE)) {
690#else
691 if (nxt_slow_path(php_request_startup(TSRMLS_C) == FAILURE)) {
692#endif
693 nxt_unit_req_debug(req, "php_request_startup() failed");
694 rc = NXT_UNIT_ERROR;
695
696 goto fail;
697 }
698
699 rc = NXT_UNIT_OK;
700
701 php_execute_script(&file_handle TSRMLS_CC);
702 php_request_shutdown(NULL);
703
704fail:
705
706 nxt_unit_request_done(req, rc);
707
708 if (ctx->script_filename.start != nxt_php_script_filename.start) {
709 nxt_free(ctx->script_filename.start);
710 }
711}
712
713
714static int
715nxt_php_startup(sapi_module_struct *sapi_module)
716{
717 return php_module_startup(sapi_module, NULL, 0);
718}
719
720
721#ifdef NXT_PHP7
722static size_t
723nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC)
724#else
725static int
726nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC)
727#endif
728{
729 int rc;
730 nxt_php_run_ctx_t *ctx;
731
732 ctx = SG(server_context);
733
734 rc = nxt_unit_response_write(ctx->req, str, str_length);
735 if (nxt_fast_path(rc == NXT_UNIT_OK)) {
736 return str_length;
737 }
738
739 php_handle_aborted_connection();
740 return 0;
741}
742
743
744static int
745nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
746{
747 int rc, fields_count;
748 char *colon, *value;
749 uint16_t status;
750 uint32_t resp_size;
751 nxt_php_run_ctx_t *ctx;
752 sapi_header_struct *h;
753 zend_llist_position zpos;
754 nxt_unit_request_info_t *req;
755
756 ctx = SG(server_context);
757 req = ctx->req;
758
759 nxt_unit_req_debug(req, "nxt_php_send_headers");
760
761 if (SG(request_info).no_headers == 1) {
762 rc = nxt_unit_response_init(req, 200, 0, 0);
763 if (nxt_slow_path(rc != NXT_UNIT_OK)) {
764 return SAPI_HEADER_SEND_FAILED;
765 }
766
767 return SAPI_HEADER_SENT_SUCCESSFULLY;
768 }
769
770 resp_size = 0;
771 fields_count = zend_llist_count(&sapi_headers->headers);
772
773 for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos);
774 h;
775 h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos))
776 {
777 resp_size += h->header_len;
778 }
779
780 status = SG(sapi_headers).http_response_code;
781
782 rc = nxt_unit_response_init(req, status, fields_count, resp_size);
783 if (nxt_slow_path(rc != NXT_UNIT_OK)) {
784 return SAPI_HEADER_SEND_FAILED;
785 }
786
787 for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos);
788 h;
789 h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos))
790 {
791 colon = memchr(h->header, ':', h->header_len);
792 if (nxt_slow_path(colon == NULL)) {
793 nxt_unit_req_warn(req, "colon not found in header '%.*s'",
794 (int) h->header_len, h->header);
795 continue;
796 }
797
798 value = colon + 1;
799 while(isspace(*value)) {
800 value++;
801 }
802
803 nxt_unit_response_add_field(req, h->header, colon - h->header,
804 value,
805 h->header_len - (value - h->header));
806 }
807
808 rc = nxt_unit_response_send(req);
809 if (nxt_slow_path(rc != NXT_UNIT_OK)) {
810 nxt_unit_req_debug(req, "failed to send response");
811
812 return SAPI_HEADER_SEND_FAILED;
813 }
814
815 return SAPI_HEADER_SENT_SUCCESSFULLY;
816}
817
818
819#ifdef NXT_PHP7
820static size_t
821nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC)
822#else
823static int
824nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC)
825#endif
826{
827 nxt_php_run_ctx_t *ctx;
828
829 ctx = SG(server_context);
830
831 nxt_unit_req_debug(ctx->req, "nxt_php_read_post %d", (int) count_bytes);
832
833 return nxt_unit_request_read(ctx->req, buffer, count_bytes);
834}
835
836
837static char *
838nxt_php_read_cookies(TSRMLS_D)
839{
840 nxt_php_run_ctx_t *ctx;
841
842 ctx = SG(server_context);
843
844 nxt_unit_req_debug(ctx->req, "nxt_php_read_cookies");
845
846 return ctx->cookie;
847}
848
849
850static void
851nxt_php_register_variables(zval *track_vars_array TSRMLS_DC)
852{
853 const char *name;
854 nxt_unit_field_t *f, *f_end;
855 nxt_php_run_ctx_t *ctx;
856 nxt_unit_request_t *r;
857 nxt_unit_request_info_t *req;
858
859 ctx = SG(server_context);
860
861 req = ctx->req;
862 r = req->request;
863
864 nxt_unit_req_debug(req, "nxt_php_register_variables");
865
866 php_register_variable_safe((char *) "SERVER_SOFTWARE",
867 (char *) nxt_server.start,
868 nxt_server.length, track_vars_array TSRMLS_CC);
869
870 nxt_php_set_sptr(req, "SERVER_PROTOCOL", &r->version, r->version_length,
871 track_vars_array TSRMLS_CC);
872
873 /*
874 * 'PHP_SELF'
875 * The filename of the currently executing script, relative to the document
876 * root. For instance, $_SERVER['PHP_SELF'] in a script at the address
877 * http://example.com/foo/bar.php would be /foo/bar.php. The __FILE__
878 * constant contains the full path and filename of the current (i.e.
879 * included) file. If PHP is running as a command-line processor this
880 * variable contains the script name since PHP 4.3.0. Previously it was not
881 * available.
882 */
883
884 if (nxt_php_script_name.start != NULL) {
885 /* ABS_MODE */
886 nxt_php_set_str(req, "PHP_SELF", &nxt_php_script_name,
887 track_vars_array TSRMLS_CC);
888
889 } else {
890 nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length,
891 track_vars_array TSRMLS_CC);
892 }
893
894 if (ctx->path_info.length != 0) {
895 nxt_php_set_str(req, "PATH_INFO", &ctx->path_info,
896 track_vars_array TSRMLS_CC);
897 }
898
899 /*
900 * 'SCRIPT_NAME'
901 * Contains the current script's path. This is useful for pages which need
902 * to point to themselves. The __FILE__ constant contains the full path and
903 * filename of the current (i.e. included) file.
904 */
905
906 nxt_php_set_str(req, "SCRIPT_NAME", &ctx->script_name,
907 track_vars_array TSRMLS_CC);
908
909 /*
910 * 'SCRIPT_FILENAME'
911 * The absolute pathname of the currently executing script.
912 */
913
914 nxt_php_set_str(req, "SCRIPT_FILENAME", &ctx->script_filename,
915 track_vars_array TSRMLS_CC);
916
917 /*
918 * 'DOCUMENT_ROOT'
919 * The document root directory under which the current script is executing,
920 * as defined in the server's configuration file.
921 */
922
923 nxt_php_set_str(req, "DOCUMENT_ROOT", &nxt_php_root,
924 track_vars_array TSRMLS_CC);
925
926 nxt_php_set_sptr(req, "REQUEST_METHOD", &r->method, r->method_length,
927 track_vars_array TSRMLS_CC);
928 nxt_php_set_sptr(req, "REQUEST_URI", &r->target, r->target_length,
929 track_vars_array TSRMLS_CC);
930 nxt_php_set_sptr(req, "QUERY_STRING", &r->query, r->query_length,
931 track_vars_array TSRMLS_CC);
932
933 nxt_php_set_sptr(req, "REMOTE_ADDR", &r->remote, r->remote_length,
934 track_vars_array TSRMLS_CC);
935 nxt_php_set_sptr(req, "SERVER_ADDR", &r->local, r->local_length,
936 track_vars_array TSRMLS_CC);
937
938 nxt_php_set_sptr(req, "SERVER_NAME", &r->server_name, r->server_name_length,
939 track_vars_array TSRMLS_CC);
940 nxt_php_set_cstr(req, "SERVER_PORT", "80", 2, track_vars_array TSRMLS_CC);
941
942 if (r->tls) {
943 nxt_php_set_cstr(req, "HTTPS", "on", 2, track_vars_array TSRMLS_CC);
944 }
945
946 f_end = r->fields + r->fields_count;
947 for (f = r->fields; f < f_end; f++) {
948 name = nxt_unit_sptr_get(&f->name);
949
950 nxt_php_set_sptr(req, name, &f->value, f->value_length,
951 track_vars_array TSRMLS_CC);
952 }
953
954 if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
955 f = r->fields + r->content_length_field;
956
957 nxt_php_set_sptr(req, "CONTENT_LENGTH", &f->value, f->value_length,
958 track_vars_array TSRMLS_CC);
959 }
960
961 if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
962 f = r->fields + r->content_type_field;
963
964 nxt_php_set_sptr(req, "CONTENT_TYPE", &f->value, f->value_length,
965 track_vars_array TSRMLS_CC);
966 }
967}
968
969
970static void
971nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name,
972 nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC)
973{
974 char *str;
975
976 str = nxt_unit_sptr_get(v);
977
978 nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, str);
979
980 php_register_variable_safe((char *) name, str, len,
981 track_vars_array TSRMLS_CC);
982}
983
984
985nxt_inline void
986nxt_php_set_str(nxt_unit_request_info_t *req, const char *name,
987 nxt_str_t *s, zval *track_vars_array TSRMLS_DC)
988{
989 nxt_php_set_cstr(req, name, (char *) s->start, s->length,
990 track_vars_array TSRMLS_CC);
991}
992
993
994static void
995nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name,
996 const char *cstr, uint32_t len, zval *track_vars_array TSRMLS_DC)
997{
998 if (nxt_slow_path(cstr == NULL)) {
999 return;
1000 }
1001
1002 nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, cstr);
1003
1004 php_register_variable_safe((char *) name, (char *) cstr, len,
1005 track_vars_array TSRMLS_CC);
1006}
1007
1008
1009#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
1010static void
1011nxt_php_log_message(char *message, int syslog_type_int)
1012#else
1013static void
1014nxt_php_log_message(char *message TSRMLS_DC)
1015#endif
1016{
1017 nxt_log(nxt_php_task, NXT_LOG_NOTICE, "php message: %s", message);
1018}
244
245 script_filename->length = nxt_strlen(script_filename->start);
246
247 if (!nxt_str_start(script_filename, root->start, root->length)) {
248 nxt_alert(task, "script is not under php root");
249 return NXT_ERROR;
250 }
251
252 script_name->length = c->script.length + 1;
253 script_name->start = nxt_malloc(script_name->length);
254 if (nxt_slow_path(script_name->start == NULL)) {
255 return NXT_ERROR;
256 }
257
258 script_name->start[0] = '/';
259 nxt_memcpy(script_name->start + 1, c->script.start, c->script.length);
260
261 nxt_log_error(NXT_LOG_INFO, task->log,
262 "(ABS_MODE) php script \"%V\" root: \"%V\"",
263 script_name, root);
264
265 } else {
266 nxt_log_error(NXT_LOG_INFO, task->log,
267 "(non ABS_MODE) php root: \"%V\"", root);
268 }
269
270 if (c->index.length > 0) {
271 index->length = c->index.length;
272 index->start = nxt_malloc(index->length);
273 if (nxt_slow_path(index->start == NULL)) {
274 return NXT_ERROR;
275 }
276
277 nxt_memcpy(index->start, c->index.start, c->index.length);
278 }
279
280#ifdef ZTS
281 tsrm_startup(1, 1, 0, NULL);
282 tsrm_ls = ts_resource(0);
283#endif
284
285#if defined(NXT_PHP7) && defined(ZEND_SIGNALS)
286
287#if (NXT_ZEND_SIGNAL_STARTUP)
288 zend_signal_startup();
289#elif defined(ZTS)
290#error PHP is built with thread safety and broken signals.
291#endif
292
293#endif
294
295 sapi_startup(&nxt_php_sapi_module);
296
297 if (c->options != NULL) {
298 value = nxt_conf_get_object_member(c->options, &file_str, NULL);
299
300 if (value != NULL) {
301 nxt_conf_get_string(value, &ini_path);
302
303 p = nxt_malloc(ini_path.length + 1);
304 if (nxt_slow_path(p == NULL)) {
305 return NXT_ERROR;
306 }
307
308 nxt_php_sapi_module.php_ini_path_override = (char *) p;
309
310 p = nxt_cpymem(p, ini_path.start, ini_path.length);
311 *p = '\0';
312 }
313 }
314
315 nxt_php_startup(&nxt_php_sapi_module);
316
317 if (c->options != NULL) {
318 value = nxt_conf_get_object_member(c->options, &admin_str, NULL);
319 nxt_php_set_options(task, value, ZEND_INI_SYSTEM);
320
321 value = nxt_conf_get_object_member(c->options, &user_str, NULL);
322 nxt_php_set_options(task, value, ZEND_INI_USER);
323 }
324
325 nxt_memzero(&php_init, sizeof(nxt_unit_init_t));
326
327 rt = task->thread->runtime;
328
329 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
330 if (nxt_slow_path(main_port == NULL)) {
331 return NXT_ERROR;
332 }
333
334 my_port = nxt_runtime_port_find(rt, nxt_pid, 0);
335 if (nxt_slow_path(my_port == NULL)) {
336 return NXT_ERROR;
337 }
338
339 php_init.callbacks.request_handler = nxt_php_request_handler;
340 php_init.ready_port.id.pid = main_port->pid;
341 php_init.ready_port.id.id = main_port->id;
342 php_init.ready_port.out_fd = main_port->pair[1];
343
344 nxt_fd_blocking(task, main_port->pair[1]);
345
346 php_init.ready_stream = my_port->process->init->stream;
347
348 php_init.read_port.id.pid = my_port->pid;
349 php_init.read_port.id.id = my_port->id;
350 php_init.read_port.in_fd = my_port->pair[0];
351
352 nxt_fd_blocking(task, my_port->pair[0]);
353
354 php_init.log_fd = 2;
355
356 unit_ctx = nxt_unit_init(&php_init);
357 if (nxt_slow_path(unit_ctx == NULL)) {
358 return NXT_ERROR;
359 }
360
361 nxt_unit_run(unit_ctx);
362
363 nxt_unit_done(unit_ctx);
364
365 exit(0);
366
367 return NXT_OK;
368}
369
370
371static void
372nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type)
373{
374 uint32_t next;
375 nxt_str_t name, value;
376 nxt_conf_value_t *value_obj;
377
378 if (options != NULL) {
379 next = 0;
380
381 for ( ;; ) {
382 value_obj = nxt_conf_next_object_member(options, &name, &next);
383 if (value_obj == NULL) {
384 break;
385 }
386
387 nxt_conf_get_string(value_obj, &value);
388
389 if (nxt_php_alter_option(&name, &value, type) != NXT_OK) {
390 nxt_log(task, NXT_LOG_ERR,
391 "setting PHP option \"%V: %V\" failed", &name, &value);
392 continue;
393 }
394
395 if (nxt_str_eq(&name, "disable_functions", 17)) {
396 nxt_php_disable(task, "function", &value,
397 &PG(disable_functions),
398 zend_disable_function);
399 continue;
400 }
401
402 if (nxt_str_eq(&name, "disable_classes", 15)) {
403 nxt_php_disable(task, "class", &value,
404 &PG(disable_classes),
405 zend_disable_class);
406 continue;
407 }
408 }
409 }
410}
411
412
413#if (NXT_PHP7)
414
415static nxt_int_t
416nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
417{
418 zend_string *zs;
419 zend_ini_entry *ini_entry;
420
421 ini_entry = zend_hash_str_find_ptr(EG(ini_directives),
422 (char *) name->start, name->length);
423
424 if (ini_entry == NULL) {
425 return NXT_ERROR;
426 }
427
428 /* PHP exits on memory allocation errors. */
429 zs = zend_string_init((char *) value->start, value->length, 1);
430
431 if (ini_entry->on_modify
432 && ini_entry->on_modify(ini_entry, zs, ini_entry->mh_arg1,
433 ini_entry->mh_arg2, ini_entry->mh_arg3,
434 ZEND_INI_STAGE_ACTIVATE)
435 != SUCCESS)
436 {
437 zend_string_release(zs);
438 return NXT_ERROR;
439 }
440
441 ini_entry->value = zs;
442 ini_entry->modifiable = type;
443
444 return NXT_OK;
445}
446
447#else /* PHP 5. */
448
449static nxt_int_t
450nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type)
451{
452 char *cstr;
453 zend_ini_entry *ini_entry;
454 char buf[256];
455
456 if (nxt_slow_path(name->length >= sizeof(buf))) {
457 return NXT_ERROR;
458 }
459
460 nxt_memcpy(buf, name->start, name->length);
461 buf[name->length] = '\0';
462
463 if (zend_hash_find(EG(ini_directives), buf, name->length + 1,
464 (void **) &ini_entry)
465 == FAILURE)
466 {
467 return NXT_ERROR;
468 }
469
470 cstr = nxt_malloc(value->length + 1);
471 if (nxt_slow_path(cstr == NULL)) {
472 return NXT_ERROR;
473 }
474
475 nxt_memcpy(cstr, value->start, value->length);
476 cstr[value->length] = '\0';
477
478 if (ini_entry->on_modify
479 && ini_entry->on_modify(ini_entry, cstr, value->length,
480 ini_entry->mh_arg1, ini_entry->mh_arg2,
481 ini_entry->mh_arg3, ZEND_INI_STAGE_ACTIVATE
482 TSRMLS_CC)
483 != SUCCESS)
484 {
485 nxt_free(cstr);
486 return NXT_ERROR;
487 }
488
489 ini_entry->value = cstr;
490 ini_entry->value_length = value->length;
491 ini_entry->modifiable = type;
492
493 return NXT_OK;
494}
495
496#endif
497
498
499static void
500nxt_php_disable(nxt_task_t *task, const char *type, nxt_str_t *value,
501 char **ptr, nxt_php_disable_t disable)
502{
503 char c, *p, *start;
504
505 p = nxt_malloc(value->length + 1);
506 if (nxt_slow_path(p == NULL)) {
507 return;
508 }
509
510 /*
511 * PHP frees this memory on module shutdown.
512 * See core_globals_dtor() for details.
513 */
514 *ptr = p;
515
516 nxt_memcpy(p, value->start, value->length);
517 p[value->length] = '\0';
518
519 start = p;
520
521 do {
522 c = *p;
523
524 if (c == ' ' || c == ',' || c == '\0') {
525
526 if (p != start) {
527 *p = '\0';
528
529#ifdef NXT_PHP7
530 if (disable(start, p - start)
531#else
532 if (disable(start, p - start TSRMLS_CC)
533#endif
534 != SUCCESS)
535 {
536 nxt_log(task, NXT_LOG_ERR,
537 "PHP: failed to disable \"%s\": no such %s",
538 start, type);
539 }
540 }
541
542 start = p + 1;
543 }
544
545 p++;
546
547 } while (c != '\0');
548}
549
550
551static void
552nxt_php_str_trim_trail(nxt_str_t *str, u_char t)
553{
554 while (str->length > 0 && str->start[str->length - 1] == t) {
555 str->length--;
556 }
557
558 str->start[str->length] = '\0';
559}
560
561
562static void
563nxt_php_str_trim_lead(nxt_str_t *str, u_char t)
564{
565 while (str->length > 0 && str->start[0] == t) {
566 str->length--;
567 str->start++;
568 }
569}
570
571
572nxt_inline u_char *
573nxt_realpath(const void *c)
574{
575 return (u_char *) realpath(c, NULL);
576}
577
578
579static void
580nxt_php_request_handler(nxt_unit_request_info_t *req)
581{
582 int rc;
583 u_char *p;
584 nxt_str_t path, script_name;
585 nxt_unit_field_t *f;
586 zend_file_handle file_handle;
587 nxt_php_run_ctx_t run_ctx, *ctx;
588 nxt_unit_request_t *r;
589
590 nxt_memzero(&run_ctx, sizeof(run_ctx));
591
592 ctx = &run_ctx;
593 ctx->req = req;
594
595 r = req->request;
596
597 path.length = r->path_length;
598 path.start = nxt_unit_sptr_get(&r->path);
599
600 if (nxt_php_script_filename.start == NULL) {
601 ctx->path_info.start = (u_char *) strstr((char *) path.start, ".php/");
602 if (ctx->path_info.start != NULL) {
603 ctx->path_info.start += 4;
604 path.length = ctx->path_info.start - path.start;
605
606 ctx->path_info.length = r->path_length - path.length;
607 }
608
609 if (path.start[path.length - 1] == '/') {
610 script_name = nxt_php_index;
611
612 } else {
613 script_name.length = 0;
614 script_name.start = NULL;
615 }
616
617 ctx->script_filename.length = nxt_php_root.length + path.length
618 + script_name.length;
619 p = nxt_malloc(ctx->script_filename.length + 1);
620 if (nxt_slow_path(p == NULL)) {
621 nxt_unit_request_done(req, NXT_UNIT_ERROR);
622
623 return;
624 }
625
626 ctx->script_filename.start = p;
627
628 ctx->script_name.length = path.length + script_name.length;
629 ctx->script_name.start = p + nxt_php_root.length;
630
631 p = nxt_cpymem(p, nxt_php_root.start, nxt_php_root.length);
632 p = nxt_cpymem(p, path.start, path.length);
633
634 if (script_name.length > 0) {
635 p = nxt_cpymem(p, script_name.start, script_name.length);
636 }
637
638 *p = '\0';
639
640 } else {
641 ctx->script_filename = nxt_php_script_filename;
642 ctx->script_name = nxt_php_script_name;
643 }
644
645 SG(server_context) = ctx;
646 SG(request_info).request_uri = nxt_unit_sptr_get(&r->target);
647 SG(request_info).request_method = nxt_unit_sptr_get(&r->method);
648
649 SG(request_info).proto_num = 1001;
650
651 SG(request_info).query_string = r->query.offset
652 ? nxt_unit_sptr_get(&r->query) : NULL;
653 SG(request_info).content_length = r->content_length;
654
655 if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
656 f = r->fields + r->content_type_field;
657
658 SG(request_info).content_type = nxt_unit_sptr_get(&f->value);
659 }
660
661 if (r->cookie_field != NXT_UNIT_NONE_FIELD) {
662 f = r->fields + r->cookie_field;
663
664 ctx->cookie = nxt_unit_sptr_get(&f->value);
665 }
666
667 SG(sapi_headers).http_response_code = 200;
668
669 SG(request_info).path_translated = NULL;
670
671 file_handle.type = ZEND_HANDLE_FILENAME;
672 file_handle.filename = (char *) ctx->script_filename.start;
673 file_handle.free_filename = 0;
674 file_handle.opened_path = NULL;
675
676 nxt_unit_req_debug(req, "handle.filename = '%s'",
677 ctx->script_filename.start);
678
679 if (nxt_php_script_filename.start != NULL) {
680 nxt_unit_req_debug(req, "run script %.*s in absolute mode",
681 (int) nxt_php_script_filename.length,
682 (char *) nxt_php_script_filename.start);
683
684 } else {
685 nxt_unit_req_debug(req, "run script %.*s",
686 (int) ctx->script_filename.length,
687 (char *) ctx->script_filename.start);
688 }
689
690#if (NXT_PHP7)
691 if (nxt_slow_path(php_request_startup() == FAILURE)) {
692#else
693 if (nxt_slow_path(php_request_startup(TSRMLS_C) == FAILURE)) {
694#endif
695 nxt_unit_req_debug(req, "php_request_startup() failed");
696 rc = NXT_UNIT_ERROR;
697
698 goto fail;
699 }
700
701 rc = NXT_UNIT_OK;
702
703 php_execute_script(&file_handle TSRMLS_CC);
704 php_request_shutdown(NULL);
705
706fail:
707
708 nxt_unit_request_done(req, rc);
709
710 if (ctx->script_filename.start != nxt_php_script_filename.start) {
711 nxt_free(ctx->script_filename.start);
712 }
713}
714
715
716static int
717nxt_php_startup(sapi_module_struct *sapi_module)
718{
719 return php_module_startup(sapi_module, NULL, 0);
720}
721
722
723#ifdef NXT_PHP7
724static size_t
725nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC)
726#else
727static int
728nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC)
729#endif
730{
731 int rc;
732 nxt_php_run_ctx_t *ctx;
733
734 ctx = SG(server_context);
735
736 rc = nxt_unit_response_write(ctx->req, str, str_length);
737 if (nxt_fast_path(rc == NXT_UNIT_OK)) {
738 return str_length;
739 }
740
741 php_handle_aborted_connection();
742 return 0;
743}
744
745
746static int
747nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
748{
749 int rc, fields_count;
750 char *colon, *value;
751 uint16_t status;
752 uint32_t resp_size;
753 nxt_php_run_ctx_t *ctx;
754 sapi_header_struct *h;
755 zend_llist_position zpos;
756 nxt_unit_request_info_t *req;
757
758 ctx = SG(server_context);
759 req = ctx->req;
760
761 nxt_unit_req_debug(req, "nxt_php_send_headers");
762
763 if (SG(request_info).no_headers == 1) {
764 rc = nxt_unit_response_init(req, 200, 0, 0);
765 if (nxt_slow_path(rc != NXT_UNIT_OK)) {
766 return SAPI_HEADER_SEND_FAILED;
767 }
768
769 return SAPI_HEADER_SENT_SUCCESSFULLY;
770 }
771
772 resp_size = 0;
773 fields_count = zend_llist_count(&sapi_headers->headers);
774
775 for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos);
776 h;
777 h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos))
778 {
779 resp_size += h->header_len;
780 }
781
782 status = SG(sapi_headers).http_response_code;
783
784 rc = nxt_unit_response_init(req, status, fields_count, resp_size);
785 if (nxt_slow_path(rc != NXT_UNIT_OK)) {
786 return SAPI_HEADER_SEND_FAILED;
787 }
788
789 for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos);
790 h;
791 h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos))
792 {
793 colon = memchr(h->header, ':', h->header_len);
794 if (nxt_slow_path(colon == NULL)) {
795 nxt_unit_req_warn(req, "colon not found in header '%.*s'",
796 (int) h->header_len, h->header);
797 continue;
798 }
799
800 value = colon + 1;
801 while(isspace(*value)) {
802 value++;
803 }
804
805 nxt_unit_response_add_field(req, h->header, colon - h->header,
806 value,
807 h->header_len - (value - h->header));
808 }
809
810 rc = nxt_unit_response_send(req);
811 if (nxt_slow_path(rc != NXT_UNIT_OK)) {
812 nxt_unit_req_debug(req, "failed to send response");
813
814 return SAPI_HEADER_SEND_FAILED;
815 }
816
817 return SAPI_HEADER_SENT_SUCCESSFULLY;
818}
819
820
821#ifdef NXT_PHP7
822static size_t
823nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC)
824#else
825static int
826nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC)
827#endif
828{
829 nxt_php_run_ctx_t *ctx;
830
831 ctx = SG(server_context);
832
833 nxt_unit_req_debug(ctx->req, "nxt_php_read_post %d", (int) count_bytes);
834
835 return nxt_unit_request_read(ctx->req, buffer, count_bytes);
836}
837
838
839static char *
840nxt_php_read_cookies(TSRMLS_D)
841{
842 nxt_php_run_ctx_t *ctx;
843
844 ctx = SG(server_context);
845
846 nxt_unit_req_debug(ctx->req, "nxt_php_read_cookies");
847
848 return ctx->cookie;
849}
850
851
852static void
853nxt_php_register_variables(zval *track_vars_array TSRMLS_DC)
854{
855 const char *name;
856 nxt_unit_field_t *f, *f_end;
857 nxt_php_run_ctx_t *ctx;
858 nxt_unit_request_t *r;
859 nxt_unit_request_info_t *req;
860
861 ctx = SG(server_context);
862
863 req = ctx->req;
864 r = req->request;
865
866 nxt_unit_req_debug(req, "nxt_php_register_variables");
867
868 php_register_variable_safe((char *) "SERVER_SOFTWARE",
869 (char *) nxt_server.start,
870 nxt_server.length, track_vars_array TSRMLS_CC);
871
872 nxt_php_set_sptr(req, "SERVER_PROTOCOL", &r->version, r->version_length,
873 track_vars_array TSRMLS_CC);
874
875 /*
876 * 'PHP_SELF'
877 * The filename of the currently executing script, relative to the document
878 * root. For instance, $_SERVER['PHP_SELF'] in a script at the address
879 * http://example.com/foo/bar.php would be /foo/bar.php. The __FILE__
880 * constant contains the full path and filename of the current (i.e.
881 * included) file. If PHP is running as a command-line processor this
882 * variable contains the script name since PHP 4.3.0. Previously it was not
883 * available.
884 */
885
886 if (nxt_php_script_name.start != NULL) {
887 /* ABS_MODE */
888 nxt_php_set_str(req, "PHP_SELF", &nxt_php_script_name,
889 track_vars_array TSRMLS_CC);
890
891 } else {
892 nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length,
893 track_vars_array TSRMLS_CC);
894 }
895
896 if (ctx->path_info.length != 0) {
897 nxt_php_set_str(req, "PATH_INFO", &ctx->path_info,
898 track_vars_array TSRMLS_CC);
899 }
900
901 /*
902 * 'SCRIPT_NAME'
903 * Contains the current script's path. This is useful for pages which need
904 * to point to themselves. The __FILE__ constant contains the full path and
905 * filename of the current (i.e. included) file.
906 */
907
908 nxt_php_set_str(req, "SCRIPT_NAME", &ctx->script_name,
909 track_vars_array TSRMLS_CC);
910
911 /*
912 * 'SCRIPT_FILENAME'
913 * The absolute pathname of the currently executing script.
914 */
915
916 nxt_php_set_str(req, "SCRIPT_FILENAME", &ctx->script_filename,
917 track_vars_array TSRMLS_CC);
918
919 /*
920 * 'DOCUMENT_ROOT'
921 * The document root directory under which the current script is executing,
922 * as defined in the server's configuration file.
923 */
924
925 nxt_php_set_str(req, "DOCUMENT_ROOT", &nxt_php_root,
926 track_vars_array TSRMLS_CC);
927
928 nxt_php_set_sptr(req, "REQUEST_METHOD", &r->method, r->method_length,
929 track_vars_array TSRMLS_CC);
930 nxt_php_set_sptr(req, "REQUEST_URI", &r->target, r->target_length,
931 track_vars_array TSRMLS_CC);
932 nxt_php_set_sptr(req, "QUERY_STRING", &r->query, r->query_length,
933 track_vars_array TSRMLS_CC);
934
935 nxt_php_set_sptr(req, "REMOTE_ADDR", &r->remote, r->remote_length,
936 track_vars_array TSRMLS_CC);
937 nxt_php_set_sptr(req, "SERVER_ADDR", &r->local, r->local_length,
938 track_vars_array TSRMLS_CC);
939
940 nxt_php_set_sptr(req, "SERVER_NAME", &r->server_name, r->server_name_length,
941 track_vars_array TSRMLS_CC);
942 nxt_php_set_cstr(req, "SERVER_PORT", "80", 2, track_vars_array TSRMLS_CC);
943
944 if (r->tls) {
945 nxt_php_set_cstr(req, "HTTPS", "on", 2, track_vars_array TSRMLS_CC);
946 }
947
948 f_end = r->fields + r->fields_count;
949 for (f = r->fields; f < f_end; f++) {
950 name = nxt_unit_sptr_get(&f->name);
951
952 nxt_php_set_sptr(req, name, &f->value, f->value_length,
953 track_vars_array TSRMLS_CC);
954 }
955
956 if (r->content_length_field != NXT_UNIT_NONE_FIELD) {
957 f = r->fields + r->content_length_field;
958
959 nxt_php_set_sptr(req, "CONTENT_LENGTH", &f->value, f->value_length,
960 track_vars_array TSRMLS_CC);
961 }
962
963 if (r->content_type_field != NXT_UNIT_NONE_FIELD) {
964 f = r->fields + r->content_type_field;
965
966 nxt_php_set_sptr(req, "CONTENT_TYPE", &f->value, f->value_length,
967 track_vars_array TSRMLS_CC);
968 }
969}
970
971
972static void
973nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name,
974 nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC)
975{
976 char *str;
977
978 str = nxt_unit_sptr_get(v);
979
980 nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, str);
981
982 php_register_variable_safe((char *) name, str, len,
983 track_vars_array TSRMLS_CC);
984}
985
986
987nxt_inline void
988nxt_php_set_str(nxt_unit_request_info_t *req, const char *name,
989 nxt_str_t *s, zval *track_vars_array TSRMLS_DC)
990{
991 nxt_php_set_cstr(req, name, (char *) s->start, s->length,
992 track_vars_array TSRMLS_CC);
993}
994
995
996static void
997nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name,
998 const char *cstr, uint32_t len, zval *track_vars_array TSRMLS_DC)
999{
1000 if (nxt_slow_path(cstr == NULL)) {
1001 return;
1002 }
1003
1004 nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, cstr);
1005
1006 php_register_variable_safe((char *) name, (char *) cstr, len,
1007 track_vars_array TSRMLS_CC);
1008}
1009
1010
1011#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE
1012static void
1013nxt_php_log_message(char *message, int syslog_type_int)
1014#else
1015static void
1016nxt_php_log_message(char *message TSRMLS_DC)
1017#endif
1018{
1019 nxt_log(nxt_php_task, NXT_LOG_NOTICE, "php message: %s", message);
1020}