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#include "ext/standard/php_standard.h" 12 13#include <nxt_main.h> 14#include <nxt_router.h> 15#include <nxt_unit.h> 16#include <nxt_unit_request.h> 17 18 19#if PHP_VERSION_ID >= 50400 20#define NXT_HAVE_PHP_IGNORE_CWD 1 21#endif 22 23#if PHP_VERSION_ID >= 70100 24#define NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1 25#else 26#define NXT_HAVE_PHP_INTERRUPTS 1 27#endif 28 29#if PHP_VERSION_ID >= 70000 30#define NXT_PHP7 1 31#endif 32#if PHP_VERSION_ID >= 80000 33#define NXT_PHP8 1 34#endif 35 36/* PHP 8 */ 37#ifndef TSRMLS_CC 38#define TSRMLS_CC 39#define TSRMLS_DC 40#define TSRMLS_D void 41#define TSRMLS_C 42#endif 43 44 45typedef struct { 46 nxt_str_t root; 47 nxt_str_t index; 48 nxt_str_t script_name; 49 nxt_str_t script_dirname; 50 nxt_str_t script_filename; 51} nxt_php_target_t; 52 53 54typedef struct { 55 char *cookie; 56 nxt_str_t *root; 57 nxt_str_t *index; 58 nxt_str_t path_info; 59 nxt_str_t script_name; 60 nxt_str_t script_filename; 61 nxt_str_t script_dirname; 62 nxt_unit_request_info_t *req; 63 64 uint8_t chdir; /* 1 bit */ 65} nxt_php_run_ctx_t; 66 67 68#if NXT_PHP8 69typedef int (*nxt_php_disable_t)(const char *p, size_t size); 70#elif NXT_PHP7 71typedef int (*nxt_php_disable_t)(char *p, size_t size); 72#else 73typedef int (*nxt_php_disable_t)(char *p, uint TSRMLS_DC); 74#endif 75 76#if PHP_VERSION_ID < 70200 77typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS); 78#endif 79 80 81static nxt_int_t nxt_php_setup(nxt_task_t *task, nxt_process_t *process, 82 nxt_common_app_conf_t *conf); 83static nxt_int_t nxt_php_start(nxt_task_t *task, nxt_process_data_t *data); 84static nxt_int_t nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target, 85 nxt_conf_value_t *conf); 86static nxt_int_t nxt_php_set_ini_path(nxt_task_t *task, nxt_str_t *path, 87 char *workdir); 88static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, 89 int type); 90static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, 91 int type); 92#ifdef NXT_PHP8 93static void nxt_php_disable_functions(nxt_str_t *str); 94#endif 95static void nxt_php_disable(nxt_task_t *task, const char *type, 96 nxt_str_t *value, char **ptr, nxt_php_disable_t disable); 97 98static nxt_int_t nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir); 99static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t); 100static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t); 101nxt_inline u_char *nxt_realpath(const void *c); 102 103static void nxt_php_request_handler(nxt_unit_request_info_t *req); 104static void nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, 105 nxt_unit_request_t *r); 106static void nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r); 107nxt_inline void nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir); 108 109static int nxt_php_startup(sapi_module_struct *sapi_module); 110static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC); 111static void *nxt_php_hash_str_find_ptr(const HashTable *ht, 112 const nxt_str_t *str); 113static char *nxt_php_read_cookies(TSRMLS_D); 114static void nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name, 115 nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC); 116nxt_inline void nxt_php_set_str(nxt_unit_request_info_t *req, const char *name, 117 nxt_str_t *s, zval *track_vars_array TSRMLS_DC); 118static void nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name, 119 const char *str, uint32_t len, zval *track_vars_array TSRMLS_DC); 120static void nxt_php_register_variables(zval *track_vars_array TSRMLS_DC); 121#if NXT_PHP8 122static void nxt_php_log_message(const char *message, int syslog_type_int); 123#else 124#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 125static void nxt_php_log_message(char *message, int syslog_type_int); 126#else 127static void nxt_php_log_message(char *message TSRMLS_DC); 128#endif 129#endif 130 131#ifdef NXT_PHP7 132static size_t nxt_php_unbuffered_write(const char *str, 133 size_t str_length TSRMLS_DC); 134static size_t nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC); 135#else 136static int nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC); 137static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC); 138#endif 139 140 141#ifdef NXT_PHP7 142#if PHP_VERSION_ID < 70200 143ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, 144 _IS_BOOL, NULL, 0) 145#else 146ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, 147 _IS_BOOL, 0) 148#endif 149#else /* PHP5 */ 150ZEND_BEGIN_ARG_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, 0) 151#endif 152ZEND_END_ARG_INFO() 153 154ZEND_FUNCTION(fastcgi_finish_request); 155 156PHP_MINIT_FUNCTION(nxt_php_ext); 157ZEND_NAMED_FUNCTION(nxt_php_chdir); 158 159 160static const zend_function_entry nxt_php_ext_functions[] = { 161 ZEND_FE(fastcgi_finish_request, arginfo_fastcgi_finish_request) 162 ZEND_FE_END 163}; 164 165 166zif_handler nxt_php_chdir_handler; 167zend_auto_global *nxt_php_server_ag; 168 169 170static zend_module_entry nxt_php_unit_module = { 171 STANDARD_MODULE_HEADER, 172 "unit", 173 nxt_php_ext_functions, /* function table */ 174 PHP_MINIT(nxt_php_ext), /* initialization */ 175 NULL, /* shutdown */ 176 NULL, /* request initialization */ 177 NULL, /* request shutdown */ 178 NULL, /* information */ 179 NXT_VERSION, 180 STANDARD_MODULE_PROPERTIES 181}; 182 183 184PHP_MINIT_FUNCTION(nxt_php_ext) 185{ 186 zend_function *func; 187 188 static const nxt_str_t chdir = nxt_string("chdir"); 189 190 func = nxt_php_hash_str_find_ptr(CG(function_table), &chdir); 191 if (nxt_slow_path(func == NULL)) { 192 return FAILURE; 193 } 194 195 nxt_php_chdir_handler = func->internal_function.handler; 196 func->internal_function.handler = nxt_php_chdir; 197 198 return SUCCESS; 199} 200 201 202ZEND_NAMED_FUNCTION(nxt_php_chdir) 203{ 204 nxt_php_run_ctx_t *ctx; 205 206 ctx = SG(server_context); 207 ctx->chdir = 1; 208 209 nxt_php_chdir_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); 210} 211 212 213PHP_FUNCTION(fastcgi_finish_request) 214{ 215 zend_auto_global *ag; 216 nxt_php_run_ctx_t *ctx; 217 218 if (nxt_slow_path(zend_parse_parameters_none() == FAILURE)) { 219#ifdef NXT_PHP8 220 RETURN_THROWS(); 221#else 222 return; 223#endif 224 } 225 226 ctx = SG(server_context); 227 228 if (nxt_slow_path(ctx->req == NULL)) { 229 RETURN_FALSE; 230 } 231 232#ifdef NXT_PHP7 233 php_output_end_all(); 234 php_header(); 235#else 236#ifdef PHP_OUTPUT_NEWAPI 237 php_output_end_all(TSRMLS_C); 238#else 239 php_end_ob_buffers(1 TSRMLS_CC); 240#endif 241 242 php_header(TSRMLS_C); 243#endif 244 245 ag = nxt_php_server_ag; 246 247 if (ag->armed) { 248#ifdef NXT_PHP7 249 ag->armed = ag->auto_global_callback(ag->name); 250#else 251 ag->armed = ag->auto_global_callback(ag->name, ag->name_len TSRMLS_CC); 252#endif 253 } 254 255 nxt_unit_request_done(ctx->req, NXT_UNIT_OK); 256 ctx->req = NULL; 257 258 PG(connection_status) = PHP_CONNECTION_ABORTED; 259#ifdef NXT_PHP7 260 php_output_set_status(PHP_OUTPUT_DISABLED); 261#else 262#ifdef PHP_OUTPUT_NEWAPI 263 php_output_set_status(PHP_OUTPUT_DISABLED TSRMLS_CC); 264#else 265 php_output_set_status(0 TSRMLS_CC); 266#endif 267#endif 268 269 RETURN_TRUE; 270} 271 272 273static sapi_module_struct nxt_php_sapi_module = 274{ 275 (char *) "cli-server", 276 (char *) "unit", 277 278 nxt_php_startup, /* startup */ 279 php_module_shutdown_wrapper, /* shutdown */ 280 281 NULL, /* activate */ 282 NULL, /* deactivate */ 283 284 nxt_php_unbuffered_write, /* unbuffered write */ 285 NULL, /* flush */ 286 NULL, /* get uid */ 287 NULL, /* getenv */ 288 289 php_error, /* error handler */ 290 291 NULL, /* header handler */ 292 nxt_php_send_headers, /* send headers handler */ 293 NULL, /* send header handler */ 294 295 nxt_php_read_post, /* read POST data */ 296 nxt_php_read_cookies, /* read Cookies */ 297 298 nxt_php_register_variables, /* register server variables */ 299 nxt_php_log_message, /* log message */ 300 NULL, /* get request time */ 301 NULL, /* terminate process */ 302 303 NULL, /* php_ini_path_override */ 304#ifdef NXT_HAVE_PHP_INTERRUPTS 305 NULL, /* block_interruptions */ 306 NULL, /* unblock_interruptions */ 307#endif 308 NULL, /* default_post_reader */ 309 NULL, /* treat_data */ 310 NULL, /* executable_location */ 311 312 0, /* php_ini_ignore */ 313#ifdef NXT_HAVE_PHP_IGNORE_CWD 314 1, /* php_ini_ignore_cwd */ 315#endif 316 NULL, /* get_fd */ 317 318 NULL, /* force_http_10 */ 319 320 NULL, /* get_target_uid */ 321 NULL, /* get_target_gid */ 322 323 NULL, /* input_filter */ 324 325 NULL, /* ini_defaults */ 326 0, /* phpinfo_as_text */ 327 328 NULL, /* ini_entries */ 329 NULL, /* additional_functions */ 330 NULL /* input_filter_init */ 331}; 332 333 334static uint32_t compat[] = { 335 NXT_VERNUM, NXT_DEBUG, 336}; 337 338 339NXT_EXPORT nxt_app_module_t nxt_app_module = { 340 sizeof(compat), 341 compat, 342 nxt_string("php"), 343 PHP_VERSION, 344 NULL, 345 0, 346 nxt_php_setup, 347 nxt_php_start, 348}; 349 350 351static nxt_php_target_t *nxt_php_targets; 352static nxt_int_t nxt_php_last_target = -1; 353 354static nxt_unit_ctx_t *nxt_php_unit_ctx; 355#if defined(ZTS) && PHP_VERSION_ID < 70400 356static void ***tsrm_ls; 357#endif 358 359 360static nxt_int_t 361nxt_php_setup(nxt_task_t *task, nxt_process_t *process, 362 nxt_common_app_conf_t *conf) 363{ 364 nxt_str_t ini_path; 365 nxt_int_t ret; 366 nxt_conf_value_t *value; 367 nxt_php_app_conf_t *c; 368 369 static nxt_str_t file_str = nxt_string("file"); 370 static nxt_str_t user_str = nxt_string("user"); 371 static nxt_str_t admin_str = nxt_string("admin"); 372 373 c = &conf->u.php; 374 375#ifdef ZTS 376 377#if PHP_VERSION_ID >= 70400 378 php_tsrm_startup(); 379#else 380 tsrm_startup(1, 1, 0, NULL); 381 tsrm_ls = ts_resource(0); 382#endif 383 384#endif 385 386#if defined(NXT_PHP7) && defined(ZEND_SIGNALS) 387 388#if (NXT_ZEND_SIGNAL_STARTUP) 389 zend_signal_startup(); 390#elif defined(ZTS) 391#error PHP is built with thread safety and broken signals. 392#endif 393 394#endif 395 396 sapi_startup(&nxt_php_sapi_module); 397 398 if (c->options != NULL) { 399 value = nxt_conf_get_object_member(c->options, &file_str, NULL); 400 401 if (value != NULL) { 402 nxt_conf_get_string(value, &ini_path); 403 404 ret = nxt_php_set_ini_path(task, &ini_path, 405 conf->working_directory); 406 407 if (nxt_slow_path(ret != NXT_OK)) { 408 return NXT_ERROR; 409 } 410 } 411 } 412 413 if (nxt_slow_path(nxt_php_startup(&nxt_php_sapi_module) == FAILURE)) { 414 nxt_alert(task, "failed to initialize SAPI module and extension"); 415 return NXT_ERROR; 416 } 417 418 if (c->options != NULL) { 419 value = nxt_conf_get_object_member(c->options, &admin_str, NULL); 420 nxt_php_set_options(task, value, ZEND_INI_SYSTEM); 421 422 value = nxt_conf_get_object_member(c->options, &user_str, NULL); 423 nxt_php_set_options(task, value, ZEND_INI_USER); 424 } 425 426#ifdef NXT_PHP7 427 nxt_php_server_ag = zend_hash_str_find_ptr(CG(auto_globals), "_SERVER", 428 nxt_length("_SERVER")); 429#else 430 zend_hash_quick_find(CG(auto_globals), "_SERVER", sizeof("_SERVER"), 431 zend_hash_func("_SERVER", sizeof("_SERVER")), 432 (void **) &nxt_php_server_ag); 433#endif 434 if (nxt_slow_path(nxt_php_server_ag == NULL)) { 435 nxt_alert(task, "failed to find $_SERVER auto global"); 436 return NXT_ERROR; 437 } 438 439 return NXT_OK; 440} 441 442 443static nxt_int_t 444nxt_php_start(nxt_task_t *task, nxt_process_data_t *data) 445{ 446 uint32_t next; 447 nxt_int_t ret; 448 nxt_str_t name; 449 nxt_uint_t n; 450 nxt_unit_ctx_t *unit_ctx; 451 nxt_unit_init_t php_init; 452 nxt_conf_value_t *value; 453 nxt_php_app_conf_t *c; 454 nxt_common_app_conf_t *conf; 455 456 conf = data->app; 457 c = &conf->u.php; 458 459 n = (c->targets != NULL) ? nxt_conf_object_members_count(c->targets) : 1; 460 461 nxt_php_targets = nxt_zalloc(sizeof(nxt_php_target_t) * n); 462 if (nxt_slow_path(nxt_php_targets == NULL)) { 463 return NXT_ERROR; 464 } 465 466 if (c->targets != NULL) { 467 next = 0; 468 469 for (n = 0; /* void */; n++) { 470 value = nxt_conf_next_object_member(c->targets, &name, &next); 471 if (value == NULL) { 472 break; 473 } 474 475 ret = nxt_php_set_target(task, &nxt_php_targets[n], value); 476 if (nxt_slow_path(ret != NXT_OK)) { 477 return NXT_ERROR; 478 } 479 } 480 481 } else { 482 ret = nxt_php_set_target(task, &nxt_php_targets[0], conf->self); 483 if (nxt_slow_path(ret != NXT_OK)) { 484 return NXT_ERROR; 485 } 486 } 487
| 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#include "ext/standard/php_standard.h" 12 13#include <nxt_main.h> 14#include <nxt_router.h> 15#include <nxt_unit.h> 16#include <nxt_unit_request.h> 17 18 19#if PHP_VERSION_ID >= 50400 20#define NXT_HAVE_PHP_IGNORE_CWD 1 21#endif 22 23#if PHP_VERSION_ID >= 70100 24#define NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1 25#else 26#define NXT_HAVE_PHP_INTERRUPTS 1 27#endif 28 29#if PHP_VERSION_ID >= 70000 30#define NXT_PHP7 1 31#endif 32#if PHP_VERSION_ID >= 80000 33#define NXT_PHP8 1 34#endif 35 36/* PHP 8 */ 37#ifndef TSRMLS_CC 38#define TSRMLS_CC 39#define TSRMLS_DC 40#define TSRMLS_D void 41#define TSRMLS_C 42#endif 43 44 45typedef struct { 46 nxt_str_t root; 47 nxt_str_t index; 48 nxt_str_t script_name; 49 nxt_str_t script_dirname; 50 nxt_str_t script_filename; 51} nxt_php_target_t; 52 53 54typedef struct { 55 char *cookie; 56 nxt_str_t *root; 57 nxt_str_t *index; 58 nxt_str_t path_info; 59 nxt_str_t script_name; 60 nxt_str_t script_filename; 61 nxt_str_t script_dirname; 62 nxt_unit_request_info_t *req; 63 64 uint8_t chdir; /* 1 bit */ 65} nxt_php_run_ctx_t; 66 67 68#if NXT_PHP8 69typedef int (*nxt_php_disable_t)(const char *p, size_t size); 70#elif NXT_PHP7 71typedef int (*nxt_php_disable_t)(char *p, size_t size); 72#else 73typedef int (*nxt_php_disable_t)(char *p, uint TSRMLS_DC); 74#endif 75 76#if PHP_VERSION_ID < 70200 77typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS); 78#endif 79 80 81static nxt_int_t nxt_php_setup(nxt_task_t *task, nxt_process_t *process, 82 nxt_common_app_conf_t *conf); 83static nxt_int_t nxt_php_start(nxt_task_t *task, nxt_process_data_t *data); 84static nxt_int_t nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target, 85 nxt_conf_value_t *conf); 86static nxt_int_t nxt_php_set_ini_path(nxt_task_t *task, nxt_str_t *path, 87 char *workdir); 88static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, 89 int type); 90static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, 91 int type); 92#ifdef NXT_PHP8 93static void nxt_php_disable_functions(nxt_str_t *str); 94#endif 95static void nxt_php_disable(nxt_task_t *task, const char *type, 96 nxt_str_t *value, char **ptr, nxt_php_disable_t disable); 97 98static nxt_int_t nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir); 99static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t); 100static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t); 101nxt_inline u_char *nxt_realpath(const void *c); 102 103static void nxt_php_request_handler(nxt_unit_request_info_t *req); 104static void nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, 105 nxt_unit_request_t *r); 106static void nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r); 107nxt_inline void nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir); 108 109static int nxt_php_startup(sapi_module_struct *sapi_module); 110static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC); 111static void *nxt_php_hash_str_find_ptr(const HashTable *ht, 112 const nxt_str_t *str); 113static char *nxt_php_read_cookies(TSRMLS_D); 114static void nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name, 115 nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC); 116nxt_inline void nxt_php_set_str(nxt_unit_request_info_t *req, const char *name, 117 nxt_str_t *s, zval *track_vars_array TSRMLS_DC); 118static void nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name, 119 const char *str, uint32_t len, zval *track_vars_array TSRMLS_DC); 120static void nxt_php_register_variables(zval *track_vars_array TSRMLS_DC); 121#if NXT_PHP8 122static void nxt_php_log_message(const char *message, int syslog_type_int); 123#else 124#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 125static void nxt_php_log_message(char *message, int syslog_type_int); 126#else 127static void nxt_php_log_message(char *message TSRMLS_DC); 128#endif 129#endif 130 131#ifdef NXT_PHP7 132static size_t nxt_php_unbuffered_write(const char *str, 133 size_t str_length TSRMLS_DC); 134static size_t nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC); 135#else 136static int nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC); 137static int nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC); 138#endif 139 140 141#ifdef NXT_PHP7 142#if PHP_VERSION_ID < 70200 143ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, 144 _IS_BOOL, NULL, 0) 145#else 146ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, 147 _IS_BOOL, 0) 148#endif 149#else /* PHP5 */ 150ZEND_BEGIN_ARG_INFO_EX(arginfo_fastcgi_finish_request, 0, 0, 0) 151#endif 152ZEND_END_ARG_INFO() 153 154ZEND_FUNCTION(fastcgi_finish_request); 155 156PHP_MINIT_FUNCTION(nxt_php_ext); 157ZEND_NAMED_FUNCTION(nxt_php_chdir); 158 159 160static const zend_function_entry nxt_php_ext_functions[] = { 161 ZEND_FE(fastcgi_finish_request, arginfo_fastcgi_finish_request) 162 ZEND_FE_END 163}; 164 165 166zif_handler nxt_php_chdir_handler; 167zend_auto_global *nxt_php_server_ag; 168 169 170static zend_module_entry nxt_php_unit_module = { 171 STANDARD_MODULE_HEADER, 172 "unit", 173 nxt_php_ext_functions, /* function table */ 174 PHP_MINIT(nxt_php_ext), /* initialization */ 175 NULL, /* shutdown */ 176 NULL, /* request initialization */ 177 NULL, /* request shutdown */ 178 NULL, /* information */ 179 NXT_VERSION, 180 STANDARD_MODULE_PROPERTIES 181}; 182 183 184PHP_MINIT_FUNCTION(nxt_php_ext) 185{ 186 zend_function *func; 187 188 static const nxt_str_t chdir = nxt_string("chdir"); 189 190 func = nxt_php_hash_str_find_ptr(CG(function_table), &chdir); 191 if (nxt_slow_path(func == NULL)) { 192 return FAILURE; 193 } 194 195 nxt_php_chdir_handler = func->internal_function.handler; 196 func->internal_function.handler = nxt_php_chdir; 197 198 return SUCCESS; 199} 200 201 202ZEND_NAMED_FUNCTION(nxt_php_chdir) 203{ 204 nxt_php_run_ctx_t *ctx; 205 206 ctx = SG(server_context); 207 ctx->chdir = 1; 208 209 nxt_php_chdir_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); 210} 211 212 213PHP_FUNCTION(fastcgi_finish_request) 214{ 215 zend_auto_global *ag; 216 nxt_php_run_ctx_t *ctx; 217 218 if (nxt_slow_path(zend_parse_parameters_none() == FAILURE)) { 219#ifdef NXT_PHP8 220 RETURN_THROWS(); 221#else 222 return; 223#endif 224 } 225 226 ctx = SG(server_context); 227 228 if (nxt_slow_path(ctx->req == NULL)) { 229 RETURN_FALSE; 230 } 231 232#ifdef NXT_PHP7 233 php_output_end_all(); 234 php_header(); 235#else 236#ifdef PHP_OUTPUT_NEWAPI 237 php_output_end_all(TSRMLS_C); 238#else 239 php_end_ob_buffers(1 TSRMLS_CC); 240#endif 241 242 php_header(TSRMLS_C); 243#endif 244 245 ag = nxt_php_server_ag; 246 247 if (ag->armed) { 248#ifdef NXT_PHP7 249 ag->armed = ag->auto_global_callback(ag->name); 250#else 251 ag->armed = ag->auto_global_callback(ag->name, ag->name_len TSRMLS_CC); 252#endif 253 } 254 255 nxt_unit_request_done(ctx->req, NXT_UNIT_OK); 256 ctx->req = NULL; 257 258 PG(connection_status) = PHP_CONNECTION_ABORTED; 259#ifdef NXT_PHP7 260 php_output_set_status(PHP_OUTPUT_DISABLED); 261#else 262#ifdef PHP_OUTPUT_NEWAPI 263 php_output_set_status(PHP_OUTPUT_DISABLED TSRMLS_CC); 264#else 265 php_output_set_status(0 TSRMLS_CC); 266#endif 267#endif 268 269 RETURN_TRUE; 270} 271 272 273static sapi_module_struct nxt_php_sapi_module = 274{ 275 (char *) "cli-server", 276 (char *) "unit", 277 278 nxt_php_startup, /* startup */ 279 php_module_shutdown_wrapper, /* shutdown */ 280 281 NULL, /* activate */ 282 NULL, /* deactivate */ 283 284 nxt_php_unbuffered_write, /* unbuffered write */ 285 NULL, /* flush */ 286 NULL, /* get uid */ 287 NULL, /* getenv */ 288 289 php_error, /* error handler */ 290 291 NULL, /* header handler */ 292 nxt_php_send_headers, /* send headers handler */ 293 NULL, /* send header handler */ 294 295 nxt_php_read_post, /* read POST data */ 296 nxt_php_read_cookies, /* read Cookies */ 297 298 nxt_php_register_variables, /* register server variables */ 299 nxt_php_log_message, /* log message */ 300 NULL, /* get request time */ 301 NULL, /* terminate process */ 302 303 NULL, /* php_ini_path_override */ 304#ifdef NXT_HAVE_PHP_INTERRUPTS 305 NULL, /* block_interruptions */ 306 NULL, /* unblock_interruptions */ 307#endif 308 NULL, /* default_post_reader */ 309 NULL, /* treat_data */ 310 NULL, /* executable_location */ 311 312 0, /* php_ini_ignore */ 313#ifdef NXT_HAVE_PHP_IGNORE_CWD 314 1, /* php_ini_ignore_cwd */ 315#endif 316 NULL, /* get_fd */ 317 318 NULL, /* force_http_10 */ 319 320 NULL, /* get_target_uid */ 321 NULL, /* get_target_gid */ 322 323 NULL, /* input_filter */ 324 325 NULL, /* ini_defaults */ 326 0, /* phpinfo_as_text */ 327 328 NULL, /* ini_entries */ 329 NULL, /* additional_functions */ 330 NULL /* input_filter_init */ 331}; 332 333 334static uint32_t compat[] = { 335 NXT_VERNUM, NXT_DEBUG, 336}; 337 338 339NXT_EXPORT nxt_app_module_t nxt_app_module = { 340 sizeof(compat), 341 compat, 342 nxt_string("php"), 343 PHP_VERSION, 344 NULL, 345 0, 346 nxt_php_setup, 347 nxt_php_start, 348}; 349 350 351static nxt_php_target_t *nxt_php_targets; 352static nxt_int_t nxt_php_last_target = -1; 353 354static nxt_unit_ctx_t *nxt_php_unit_ctx; 355#if defined(ZTS) && PHP_VERSION_ID < 70400 356static void ***tsrm_ls; 357#endif 358 359 360static nxt_int_t 361nxt_php_setup(nxt_task_t *task, nxt_process_t *process, 362 nxt_common_app_conf_t *conf) 363{ 364 nxt_str_t ini_path; 365 nxt_int_t ret; 366 nxt_conf_value_t *value; 367 nxt_php_app_conf_t *c; 368 369 static nxt_str_t file_str = nxt_string("file"); 370 static nxt_str_t user_str = nxt_string("user"); 371 static nxt_str_t admin_str = nxt_string("admin"); 372 373 c = &conf->u.php; 374 375#ifdef ZTS 376 377#if PHP_VERSION_ID >= 70400 378 php_tsrm_startup(); 379#else 380 tsrm_startup(1, 1, 0, NULL); 381 tsrm_ls = ts_resource(0); 382#endif 383 384#endif 385 386#if defined(NXT_PHP7) && defined(ZEND_SIGNALS) 387 388#if (NXT_ZEND_SIGNAL_STARTUP) 389 zend_signal_startup(); 390#elif defined(ZTS) 391#error PHP is built with thread safety and broken signals. 392#endif 393 394#endif 395 396 sapi_startup(&nxt_php_sapi_module); 397 398 if (c->options != NULL) { 399 value = nxt_conf_get_object_member(c->options, &file_str, NULL); 400 401 if (value != NULL) { 402 nxt_conf_get_string(value, &ini_path); 403 404 ret = nxt_php_set_ini_path(task, &ini_path, 405 conf->working_directory); 406 407 if (nxt_slow_path(ret != NXT_OK)) { 408 return NXT_ERROR; 409 } 410 } 411 } 412 413 if (nxt_slow_path(nxt_php_startup(&nxt_php_sapi_module) == FAILURE)) { 414 nxt_alert(task, "failed to initialize SAPI module and extension"); 415 return NXT_ERROR; 416 } 417 418 if (c->options != NULL) { 419 value = nxt_conf_get_object_member(c->options, &admin_str, NULL); 420 nxt_php_set_options(task, value, ZEND_INI_SYSTEM); 421 422 value = nxt_conf_get_object_member(c->options, &user_str, NULL); 423 nxt_php_set_options(task, value, ZEND_INI_USER); 424 } 425 426#ifdef NXT_PHP7 427 nxt_php_server_ag = zend_hash_str_find_ptr(CG(auto_globals), "_SERVER", 428 nxt_length("_SERVER")); 429#else 430 zend_hash_quick_find(CG(auto_globals), "_SERVER", sizeof("_SERVER"), 431 zend_hash_func("_SERVER", sizeof("_SERVER")), 432 (void **) &nxt_php_server_ag); 433#endif 434 if (nxt_slow_path(nxt_php_server_ag == NULL)) { 435 nxt_alert(task, "failed to find $_SERVER auto global"); 436 return NXT_ERROR; 437 } 438 439 return NXT_OK; 440} 441 442 443static nxt_int_t 444nxt_php_start(nxt_task_t *task, nxt_process_data_t *data) 445{ 446 uint32_t next; 447 nxt_int_t ret; 448 nxt_str_t name; 449 nxt_uint_t n; 450 nxt_unit_ctx_t *unit_ctx; 451 nxt_unit_init_t php_init; 452 nxt_conf_value_t *value; 453 nxt_php_app_conf_t *c; 454 nxt_common_app_conf_t *conf; 455 456 conf = data->app; 457 c = &conf->u.php; 458 459 n = (c->targets != NULL) ? nxt_conf_object_members_count(c->targets) : 1; 460 461 nxt_php_targets = nxt_zalloc(sizeof(nxt_php_target_t) * n); 462 if (nxt_slow_path(nxt_php_targets == NULL)) { 463 return NXT_ERROR; 464 } 465 466 if (c->targets != NULL) { 467 next = 0; 468 469 for (n = 0; /* void */; n++) { 470 value = nxt_conf_next_object_member(c->targets, &name, &next); 471 if (value == NULL) { 472 break; 473 } 474 475 ret = nxt_php_set_target(task, &nxt_php_targets[n], value); 476 if (nxt_slow_path(ret != NXT_OK)) { 477 return NXT_ERROR; 478 } 479 } 480 481 } else { 482 ret = nxt_php_set_target(task, &nxt_php_targets[0], conf->self); 483 if (nxt_slow_path(ret != NXT_OK)) { 484 return NXT_ERROR; 485 } 486 } 487
|
496 497 unit_ctx = nxt_unit_init(&php_init); 498 if (nxt_slow_path(unit_ctx == NULL)) { 499 return NXT_ERROR; 500 } 501 502 nxt_php_unit_ctx = unit_ctx; 503 504 nxt_unit_run(nxt_php_unit_ctx); 505 nxt_unit_done(nxt_php_unit_ctx); 506 507 exit(0); 508 509 return NXT_OK; 510} 511 512 513static nxt_int_t 514nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target, 515 nxt_conf_value_t *conf) 516{ 517 u_char *tmp, *p; 518 nxt_str_t str; 519 nxt_int_t ret; 520 nxt_conf_value_t *value; 521 522 static nxt_str_t root_str = nxt_string("root"); 523 static nxt_str_t script_str = nxt_string("script"); 524 static nxt_str_t index_str = nxt_string("index"); 525 526 value = nxt_conf_get_object_member(conf, &root_str, NULL); 527 528 nxt_conf_get_string(value, &str); 529 530 tmp = nxt_malloc(str.length + 1); 531 if (nxt_slow_path(tmp == NULL)) { 532 return NXT_ERROR; 533 } 534 535 p = tmp; 536 537 p = nxt_cpymem(p, str.start, str.length); 538 *p = '\0'; 539 540 p = nxt_realpath(tmp); 541 if (nxt_slow_path(p == NULL)) { 542 nxt_alert(task, "root realpath(%s) failed %E", tmp, nxt_errno); 543 return NXT_ERROR; 544 } 545 546 nxt_free(tmp); 547 548 target->root.length = nxt_strlen(p); 549 target->root.start = p; 550 551 nxt_php_str_trim_trail(&target->root, '/'); 552 553 value = nxt_conf_get_object_member(conf, &script_str, NULL); 554 555 if (value != NULL) { 556 nxt_conf_get_string(value, &str); 557 558 nxt_php_str_trim_lead(&str, '/'); 559 560 tmp = nxt_malloc(target->root.length + 1 + str.length + 1); 561 if (nxt_slow_path(tmp == NULL)) { 562 return NXT_ERROR; 563 } 564 565 p = tmp; 566 567 p = nxt_cpymem(p, target->root.start, target->root.length); 568 *p++ = '/'; 569 570 p = nxt_cpymem(p, str.start, str.length); 571 *p = '\0'; 572 573 p = nxt_realpath(tmp); 574 if (nxt_slow_path(p == NULL)) { 575 nxt_alert(task, "script realpath(%s) failed %E", tmp, nxt_errno); 576 return NXT_ERROR; 577 } 578 579 nxt_free(tmp); 580 581 target->script_filename.length = nxt_strlen(p); 582 target->script_filename.start = p; 583 584 if (!nxt_str_start(&target->script_filename, 585 target->root.start, target->root.length)) 586 { 587 nxt_alert(task, "script is not under php root"); 588 return NXT_ERROR; 589 } 590 591 ret = nxt_php_dirname(&target->script_filename, 592 &target->script_dirname); 593 if (nxt_slow_path(ret != NXT_OK)) { 594 return NXT_ERROR; 595 } 596 597 target->script_name.length = target->script_filename.length 598 - target->root.length; 599 target->script_name.start = target->script_filename.start 600 + target->root.length; 601 602 } else { 603 value = nxt_conf_get_object_member(conf, &index_str, NULL); 604 605 if (value != NULL) { 606 nxt_conf_get_string(value, &str); 607 608 tmp = nxt_malloc(str.length); 609 if (nxt_slow_path(tmp == NULL)) { 610 return NXT_ERROR; 611 } 612 613 nxt_memcpy(tmp, str.start, str.length); 614 615 target->index.length = str.length; 616 target->index.start = tmp; 617 618 } else { 619 nxt_str_set(&target->index, "index.php"); 620 } 621 } 622 623 return NXT_OK; 624} 625 626 627static nxt_int_t 628nxt_php_set_ini_path(nxt_task_t *task, nxt_str_t *ini_path, char *workdir) 629{ 630 size_t wdlen; 631 u_char *p, *start; 632 633 if (ini_path->start[0] == '/' || workdir == NULL) { 634 p = nxt_malloc(ini_path->length + 1); 635 if (nxt_slow_path(p == NULL)) { 636 return NXT_ERROR; 637 } 638 639 start = p; 640 641 } else { 642 wdlen = nxt_strlen(workdir); 643 644 p = nxt_malloc(wdlen + ini_path->length + 2); 645 if (nxt_slow_path(p == NULL)) { 646 return NXT_ERROR; 647 } 648 649 start = p; 650 651 p = nxt_cpymem(p, workdir, wdlen); 652 653 if (workdir[wdlen - 1] != '/') { 654 *p++ = '/'; 655 } 656 } 657 658 p = nxt_cpymem(p, ini_path->start, ini_path->length); 659 *p = '\0'; 660 661 nxt_php_sapi_module.php_ini_path_override = (char *) start; 662 663 return NXT_OK; 664} 665 666 667static void 668nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type) 669{ 670 uint32_t next; 671 nxt_str_t name, value; 672 nxt_conf_value_t *value_obj; 673 674 if (options != NULL) { 675 next = 0; 676 677 for ( ;; ) { 678 value_obj = nxt_conf_next_object_member(options, &name, &next); 679 if (value_obj == NULL) { 680 break; 681 } 682 683 nxt_conf_get_string(value_obj, &value); 684 685 if (nxt_php_alter_option(&name, &value, type) != NXT_OK) { 686 nxt_log(task, NXT_LOG_ERR, 687 "setting PHP option \"%V: %V\" failed", &name, &value); 688 continue; 689 } 690 691 if (nxt_str_eq(&name, "disable_functions", 17)) { 692#ifdef NXT_PHP8 693 nxt_php_disable_functions(&value); 694#else 695 nxt_php_disable(task, "function", &value, 696 &PG(disable_functions), 697 zend_disable_function); 698#endif 699 continue; 700 } 701 702 if (nxt_str_eq(&name, "disable_classes", 15)) { 703 nxt_php_disable(task, "class", &value, 704 &PG(disable_classes), 705 zend_disable_class); 706 continue; 707 } 708 } 709 } 710} 711 712 713#ifdef NXT_PHP7 714 715static nxt_int_t 716nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type) 717{ 718 zend_string *zs; 719 zend_ini_entry *ini_entry; 720 721 ini_entry = nxt_php_hash_str_find_ptr(EG(ini_directives), name); 722 if (nxt_slow_path(ini_entry == NULL)) { 723 return NXT_ERROR; 724 } 725 726 /* PHP exits on memory allocation errors. */ 727 zs = zend_string_init((char *) value->start, value->length, 1); 728 729 if (ini_entry->on_modify 730 && ini_entry->on_modify(ini_entry, zs, ini_entry->mh_arg1, 731 ini_entry->mh_arg2, ini_entry->mh_arg3, 732 ZEND_INI_STAGE_ACTIVATE) 733 != SUCCESS) 734 { 735 zend_string_release(zs); 736 return NXT_ERROR; 737 } 738 739 ini_entry->value = zs; 740 ini_entry->modifiable = type; 741 742 return NXT_OK; 743} 744 745#else /* PHP 5. */ 746 747static nxt_int_t 748nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type) 749{ 750 char *cstr; 751 zend_ini_entry *ini_entry; 752 753 ini_entry = nxt_php_hash_str_find_ptr(EG(ini_directives), name); 754 if (nxt_slow_path(ini_entry == NULL)) { 755 return NXT_ERROR; 756 } 757 758 cstr = nxt_malloc(value->length + 1); 759 if (nxt_slow_path(cstr == NULL)) { 760 return NXT_ERROR; 761 } 762 763 nxt_memcpy(cstr, value->start, value->length); 764 cstr[value->length] = '\0'; 765 766 if (ini_entry->on_modify 767 && ini_entry->on_modify(ini_entry, cstr, value->length, 768 ini_entry->mh_arg1, ini_entry->mh_arg2, 769 ini_entry->mh_arg3, ZEND_INI_STAGE_ACTIVATE 770 TSRMLS_CC) 771 != SUCCESS) 772 { 773 nxt_free(cstr); 774 return NXT_ERROR; 775 } 776 777 ini_entry->value = cstr; 778 ini_entry->value_length = value->length; 779 ini_entry->modifiable = type; 780 781 return NXT_OK; 782} 783 784#endif 785 786 787#ifdef NXT_PHP8 788 789static void 790nxt_php_disable_functions(nxt_str_t *str) 791{ 792 char *p; 793 794 p = nxt_malloc(str->length + 1); 795 if (nxt_slow_path(p == NULL)) { 796 return; 797 } 798 799 nxt_memcpy(p, str->start, str->length); 800 p[str->length] = '\0'; 801 802 zend_disable_functions(p); 803 804 nxt_free(p); 805} 806 807#endif 808 809 810static void 811nxt_php_disable(nxt_task_t *task, const char *type, nxt_str_t *value, 812 char **ptr, nxt_php_disable_t disable) 813{ 814 char c, *p, *start; 815 816 p = nxt_malloc(value->length + 1); 817 if (nxt_slow_path(p == NULL)) { 818 return; 819 } 820 821 /* 822 * PHP frees this memory on module shutdown. 823 * See core_globals_dtor() for details. 824 */ 825 *ptr = p; 826 827 nxt_memcpy(p, value->start, value->length); 828 p[value->length] = '\0'; 829 830 start = p; 831 832 do { 833 c = *p; 834 835 if (c == ' ' || c == ',' || c == '\0') { 836 837 if (p != start) { 838 *p = '\0'; 839 840#ifdef NXT_PHP7 841 if (disable(start, p - start) 842#else 843 if (disable(start, p - start TSRMLS_CC) 844#endif 845 != SUCCESS) 846 { 847 nxt_log(task, NXT_LOG_ERR, 848 "PHP: failed to disable \"%s\": no such %s", 849 start, type); 850 } 851 } 852 853 start = p + 1; 854 } 855 856 p++; 857 858 } while (c != '\0'); 859} 860 861 862static nxt_int_t 863nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir) 864{ 865 size_t length; 866 867 if (file->length == 0 || file->start[0] != '/') { 868 nxt_unit_alert(NULL, "php_dirname: invalid file name " 869 "(not starts from '/')"); 870 return NXT_ERROR; 871 } 872 873 length = file->length; 874 875 while (file->start[length - 1] != '/') { 876 length--; 877 } 878 879 dir->length = length; 880 dir->start = nxt_malloc(length + 1); 881 if (nxt_slow_path(dir->start == NULL)) { 882 return NXT_ERROR; 883 } 884 885 nxt_memcpy(dir->start, file->start, length); 886 887 dir->start[length] = '\0'; 888 889 return NXT_OK; 890} 891 892 893static void 894nxt_php_str_trim_trail(nxt_str_t *str, u_char t) 895{ 896 while (str->length > 0 && str->start[str->length - 1] == t) { 897 str->length--; 898 } 899 900 str->start[str->length] = '\0'; 901} 902 903 904static void 905nxt_php_str_trim_lead(nxt_str_t *str, u_char t) 906{ 907 while (str->length > 0 && str->start[0] == t) { 908 str->length--; 909 str->start++; 910 } 911} 912 913 914nxt_inline u_char * 915nxt_realpath(const void *c) 916{ 917 return (u_char *) realpath(c, NULL); 918} 919 920 921static void 922nxt_php_request_handler(nxt_unit_request_info_t *req) 923{ 924 nxt_php_target_t *target; 925 nxt_php_run_ctx_t ctx; 926 nxt_unit_request_t *r; 927 928 r = req->request; 929 target = &nxt_php_targets[r->app_target]; 930 931 nxt_memzero(&ctx, sizeof(ctx)); 932 933 ctx.req = req; 934 ctx.root = &target->root; 935 ctx.index = &target->index; 936 937 if (target->script_filename.length == 0) { 938 nxt_php_dynamic_request(&ctx, r); 939 return; 940 } 941 942 ctx.script_filename = target->script_filename; 943 ctx.script_dirname = target->script_dirname; 944 ctx.script_name = target->script_name; 945 946 ctx.chdir = (r->app_target != nxt_php_last_target); 947 948 nxt_php_execute(&ctx, r); 949 950 nxt_php_last_target = ctx.chdir ? -1 : r->app_target; 951} 952 953 954static void 955nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) 956{ 957 u_char *p; 958 nxt_str_t path, script_name; 959 nxt_int_t ret; 960 961 path.length = r->path_length; 962 path.start = nxt_unit_sptr_get(&r->path); 963 964 nxt_str_null(&script_name); 965 966 ctx->path_info.start = (u_char *) strstr((char *) path.start, ".php/"); 967 if (ctx->path_info.start != NULL) { 968 ctx->path_info.start += 4; 969 path.length = ctx->path_info.start - path.start; 970 971 ctx->path_info.length = r->path_length - path.length; 972 973 } else if (path.start[path.length - 1] == '/') { 974 script_name = *ctx->index; 975 976 } else { 977 if (nxt_slow_path(path.length < 4 978 || nxt_memcmp(path.start + (path.length - 4), 979 ".php", 4))) 980 { 981 nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); 982 983 return; 984 } 985 } 986 987 ctx->script_filename.length = ctx->root->length 988 + path.length 989 + script_name.length; 990 991 p = nxt_malloc(ctx->script_filename.length + 1); 992 if (nxt_slow_path(p == NULL)) { 993 nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); 994 995 return; 996 } 997 998 ctx->script_filename.start = p; 999 1000 ctx->script_name.length = path.length + script_name.length; 1001 ctx->script_name.start = p + ctx->root->length; 1002 1003 p = nxt_cpymem(p, ctx->root->start, ctx->root->length); 1004 p = nxt_cpymem(p, path.start, path.length); 1005 1006 if (script_name.length > 0) { 1007 p = nxt_cpymem(p, script_name.start, script_name.length); 1008 } 1009 1010 *p = '\0'; 1011 1012 ctx->chdir = 1; 1013 1014 ret = nxt_php_dirname(&ctx->script_filename, &ctx->script_dirname); 1015 if (nxt_slow_path(ret != NXT_OK)) { 1016 nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); 1017 nxt_free(ctx->script_filename.start); 1018 1019 return; 1020 } 1021 1022 nxt_php_execute(ctx, r); 1023 1024 nxt_free(ctx->script_filename.start); 1025 nxt_free(ctx->script_dirname.start); 1026 1027 nxt_php_last_target = -1; 1028} 1029 1030 1031static void 1032nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) 1033{ 1034#if (PHP_VERSION_ID < 50600) 1035 void *read_post; 1036#endif 1037 nxt_unit_field_t *f; 1038 zend_file_handle file_handle; 1039 1040 nxt_unit_req_debug(ctx->req, "PHP execute script %s", 1041 ctx->script_filename.start); 1042 1043 SG(server_context) = ctx; 1044 SG(options) |= SAPI_OPTION_NO_CHDIR; 1045 SG(request_info).request_uri = nxt_unit_sptr_get(&r->target); 1046 SG(request_info).request_method = nxt_unit_sptr_get(&r->method); 1047 1048 SG(request_info).proto_num = 1001; 1049 1050 SG(request_info).query_string = r->query.offset 1051 ? nxt_unit_sptr_get(&r->query) : NULL; 1052 SG(request_info).content_length = r->content_length; 1053 1054 if (r->content_type_field != NXT_UNIT_NONE_FIELD) { 1055 f = r->fields + r->content_type_field; 1056 1057 SG(request_info).content_type = nxt_unit_sptr_get(&f->value); 1058 } 1059 1060 if (r->cookie_field != NXT_UNIT_NONE_FIELD) { 1061 f = r->fields + r->cookie_field; 1062 1063 ctx->cookie = nxt_unit_sptr_get(&f->value); 1064 } 1065 1066 if (r->authorization_field != NXT_UNIT_NONE_FIELD) { 1067 f = r->fields + r->authorization_field; 1068 1069#ifdef NXT_PHP7 1070 php_handle_auth_data(nxt_unit_sptr_get(&f->value)); 1071#else 1072 php_handle_auth_data(nxt_unit_sptr_get(&f->value) TSRMLS_CC); 1073#endif 1074 1075 } else { 1076 SG(request_info).auth_digest = NULL; 1077 SG(request_info).auth_user = NULL; 1078 SG(request_info).auth_password = NULL; 1079 } 1080 1081 SG(sapi_headers).http_response_code = 200; 1082 1083 SG(request_info).path_translated = NULL; 1084 1085#ifdef NXT_PHP7 1086 if (nxt_slow_path(php_request_startup() == FAILURE)) { 1087#else 1088 if (nxt_slow_path(php_request_startup(TSRMLS_C) == FAILURE)) { 1089#endif 1090 nxt_unit_req_debug(ctx->req, "php_request_startup() failed"); 1091 1092 nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); 1093 return; 1094 } 1095 1096 if (ctx->chdir) { 1097 ctx->chdir = 0; 1098 nxt_php_vcwd_chdir(ctx->req, ctx->script_dirname.start); 1099 } 1100 1101 nxt_memzero(&file_handle, sizeof(file_handle)); 1102 1103 file_handle.type = ZEND_HANDLE_FILENAME; 1104#if (PHP_VERSION_ID >= 80100) 1105 file_handle.filename = zend_string_init((char *) ctx->script_filename.start, 1106 ctx->script_filename.length, 0); 1107 file_handle.primary_script = 1; 1108#else 1109 file_handle.filename = (char *) ctx->script_filename.start; 1110#endif 1111 1112 php_execute_script(&file_handle TSRMLS_CC); 1113 1114#if (PHP_VERSION_ID >= 80100) 1115 zend_destroy_file_handle(&file_handle); 1116#endif 1117 1118 /* Prevention of consuming possible unread request body. */ 1119#if (PHP_VERSION_ID < 50600) 1120 read_post = sapi_module.read_post; 1121 sapi_module.read_post = NULL; 1122#else 1123 SG(post_read) = 1; 1124#endif 1125 1126 php_request_shutdown(NULL); 1127 1128 if (ctx->req != NULL) { 1129 nxt_unit_request_done(ctx->req, NXT_UNIT_OK); 1130 } 1131 1132#if (PHP_VERSION_ID < 50600) 1133 sapi_module.read_post = read_post; 1134#endif 1135} 1136 1137 1138nxt_inline void 1139nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir) 1140{ 1141 if (nxt_slow_path(VCWD_CHDIR((char *) dir) != 0)) { 1142 nxt_unit_req_alert(req, "VCWD_CHDIR(%s) failed (%d: %s)", 1143 dir, errno, strerror(errno)); 1144 } 1145} 1146 1147 1148static int 1149nxt_php_startup(sapi_module_struct *sapi_module) 1150{ 1151 return php_module_startup(sapi_module, &nxt_php_unit_module, 1); 1152} 1153 1154 1155#ifdef NXT_PHP7 1156static size_t 1157nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC) 1158#else 1159static int 1160nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC) 1161#endif 1162{ 1163 int rc; 1164 nxt_php_run_ctx_t *ctx; 1165 1166 ctx = SG(server_context); 1167 1168 rc = nxt_unit_response_write(ctx->req, str, str_length); 1169 if (nxt_fast_path(rc == NXT_UNIT_OK)) { 1170 return str_length; 1171 } 1172 1173 php_handle_aborted_connection(); 1174 return 0; 1175} 1176 1177 1178static int 1179nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) 1180{ 1181 int rc, fields_count; 1182 char *colon, *value; 1183 uint16_t status; 1184 uint32_t resp_size; 1185 nxt_php_run_ctx_t *ctx; 1186 sapi_header_struct *h; 1187 zend_llist_position zpos; 1188 nxt_unit_request_info_t *req; 1189 1190 ctx = SG(server_context); 1191 req = ctx->req; 1192 1193 nxt_unit_req_debug(req, "nxt_php_send_headers"); 1194 1195 if (SG(request_info).no_headers == 1) { 1196 rc = nxt_unit_response_init(req, 200, 0, 0); 1197 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1198 return SAPI_HEADER_SEND_FAILED; 1199 } 1200 1201 return SAPI_HEADER_SENT_SUCCESSFULLY; 1202 } 1203 1204 resp_size = 0; 1205 fields_count = zend_llist_count(&sapi_headers->headers); 1206 1207 for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos); 1208 h; 1209 h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos)) 1210 { 1211 resp_size += h->header_len; 1212 } 1213 1214 status = SG(sapi_headers).http_response_code; 1215 1216 rc = nxt_unit_response_init(req, status, fields_count, resp_size); 1217 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1218 return SAPI_HEADER_SEND_FAILED; 1219 } 1220 1221 for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos); 1222 h; 1223 h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos)) 1224 { 1225 colon = memchr(h->header, ':', h->header_len); 1226 if (nxt_slow_path(colon == NULL)) { 1227 nxt_unit_req_warn(req, "colon not found in header '%.*s'", 1228 (int) h->header_len, h->header); 1229 continue; 1230 } 1231 1232 value = colon + 1; 1233 while(isspace(*value)) { 1234 value++; 1235 } 1236 1237 nxt_unit_response_add_field(req, h->header, colon - h->header, 1238 value, 1239 h->header_len - (value - h->header)); 1240 } 1241 1242 rc = nxt_unit_response_send(req); 1243 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1244 nxt_unit_req_debug(req, "failed to send response"); 1245 1246 return SAPI_HEADER_SEND_FAILED; 1247 } 1248 1249 return SAPI_HEADER_SENT_SUCCESSFULLY; 1250} 1251 1252 1253#ifdef NXT_PHP7 1254static size_t 1255nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC) 1256#else 1257static int 1258nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC) 1259#endif 1260{ 1261 nxt_php_run_ctx_t *ctx; 1262 1263 ctx = SG(server_context); 1264 1265 nxt_unit_req_debug(ctx->req, "nxt_php_read_post %d", (int) count_bytes); 1266 1267 return nxt_unit_request_read(ctx->req, buffer, count_bytes); 1268} 1269 1270 1271static char * 1272nxt_php_read_cookies(TSRMLS_D) 1273{ 1274 nxt_php_run_ctx_t *ctx; 1275 1276 ctx = SG(server_context); 1277 1278 nxt_unit_req_debug(ctx->req, "nxt_php_read_cookies"); 1279 1280 return ctx->cookie; 1281} 1282 1283 1284static void 1285nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) 1286{ 1287 const char *name; 1288 nxt_unit_field_t *f, *f_end; 1289 nxt_php_run_ctx_t *ctx; 1290 nxt_unit_request_t *r; 1291 nxt_unit_request_info_t *req; 1292 1293 ctx = SG(server_context); 1294 1295 req = ctx->req; 1296 r = req->request; 1297 1298 nxt_unit_req_debug(req, "nxt_php_register_variables"); 1299 1300 php_register_variable_safe((char *) "SERVER_SOFTWARE", 1301 (char *) nxt_server.start, 1302 nxt_server.length, track_vars_array TSRMLS_CC); 1303 1304 nxt_php_set_sptr(req, "SERVER_PROTOCOL", &r->version, r->version_length, 1305 track_vars_array TSRMLS_CC); 1306 1307 /* 1308 * 'PHP_SELF' 1309 * The filename of the currently executing script, relative to the document 1310 * root. For instance, $_SERVER['PHP_SELF'] in a script at the address 1311 * http://example.com/foo/bar.php would be /foo/bar.php. The __FILE__ 1312 * constant contains the full path and filename of the current (i.e. 1313 * included) file. If PHP is running as a command-line processor this 1314 * variable contains the script name since PHP 4.3.0. Previously it was not 1315 * available. 1316 */ 1317 1318 if (ctx->path_info.length != 0) { 1319 nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length, 1320 track_vars_array TSRMLS_CC); 1321 1322 nxt_php_set_str(req, "PATH_INFO", &ctx->path_info, 1323 track_vars_array TSRMLS_CC); 1324 1325 } else { 1326 nxt_php_set_str(req, "PHP_SELF", &ctx->script_name, 1327 track_vars_array TSRMLS_CC); 1328 } 1329 1330 /* 1331 * 'SCRIPT_NAME' 1332 * Contains the current script's path. This is useful for pages which need 1333 * to point to themselves. The __FILE__ constant contains the full path and 1334 * filename of the current (i.e. included) file. 1335 */ 1336 1337 nxt_php_set_str(req, "SCRIPT_NAME", &ctx->script_name, 1338 track_vars_array TSRMLS_CC); 1339 1340 /* 1341 * 'SCRIPT_FILENAME' 1342 * The absolute pathname of the currently executing script. 1343 */ 1344 1345 nxt_php_set_str(req, "SCRIPT_FILENAME", &ctx->script_filename, 1346 track_vars_array TSRMLS_CC); 1347 1348 /* 1349 * 'DOCUMENT_ROOT' 1350 * The document root directory under which the current script is executing, 1351 * as defined in the server's configuration file. 1352 */ 1353 1354 nxt_php_set_str(req, "DOCUMENT_ROOT", ctx->root, 1355 track_vars_array TSRMLS_CC); 1356 1357 nxt_php_set_sptr(req, "REQUEST_METHOD", &r->method, r->method_length, 1358 track_vars_array TSRMLS_CC); 1359 nxt_php_set_sptr(req, "REQUEST_URI", &r->target, r->target_length, 1360 track_vars_array TSRMLS_CC); 1361 nxt_php_set_sptr(req, "QUERY_STRING", &r->query, r->query_length, 1362 track_vars_array TSRMLS_CC); 1363 1364 nxt_php_set_sptr(req, "REMOTE_ADDR", &r->remote, r->remote_length, 1365 track_vars_array TSRMLS_CC); 1366 nxt_php_set_sptr(req, "SERVER_ADDR", &r->local, r->local_length, 1367 track_vars_array TSRMLS_CC); 1368 1369 nxt_php_set_sptr(req, "SERVER_NAME", &r->server_name, r->server_name_length, 1370 track_vars_array TSRMLS_CC); 1371 nxt_php_set_cstr(req, "SERVER_PORT", "80", 2, track_vars_array TSRMLS_CC); 1372 1373 if (r->tls) { 1374 nxt_php_set_cstr(req, "HTTPS", "on", 2, track_vars_array TSRMLS_CC); 1375 } 1376 1377 f_end = r->fields + r->fields_count; 1378 for (f = r->fields; f < f_end; f++) { 1379 name = nxt_unit_sptr_get(&f->name); 1380 1381 nxt_php_set_sptr(req, name, &f->value, f->value_length, 1382 track_vars_array TSRMLS_CC); 1383 } 1384 1385 if (r->content_length_field != NXT_UNIT_NONE_FIELD) { 1386 f = r->fields + r->content_length_field; 1387 1388 nxt_php_set_sptr(req, "CONTENT_LENGTH", &f->value, f->value_length, 1389 track_vars_array TSRMLS_CC); 1390 } 1391 1392 if (r->content_type_field != NXT_UNIT_NONE_FIELD) { 1393 f = r->fields + r->content_type_field; 1394 1395 nxt_php_set_sptr(req, "CONTENT_TYPE", &f->value, f->value_length, 1396 track_vars_array TSRMLS_CC); 1397 } 1398} 1399 1400 1401static void 1402nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name, 1403 nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC) 1404{ 1405 char *str; 1406 1407 str = nxt_unit_sptr_get(v); 1408 1409 nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, str); 1410 1411 php_register_variable_safe((char *) name, str, len, 1412 track_vars_array TSRMLS_CC); 1413} 1414 1415 1416nxt_inline void 1417nxt_php_set_str(nxt_unit_request_info_t *req, const char *name, 1418 nxt_str_t *s, zval *track_vars_array TSRMLS_DC) 1419{ 1420 nxt_php_set_cstr(req, name, (char *) s->start, s->length, 1421 track_vars_array TSRMLS_CC); 1422} 1423 1424 1425#ifdef NXT_PHP7 1426 1427static void * 1428nxt_php_hash_str_find_ptr(const HashTable *ht, const nxt_str_t *str) 1429{ 1430 return zend_hash_str_find_ptr(ht, (const char *) str->start, str->length); 1431} 1432 1433#else 1434 1435static void * 1436nxt_php_hash_str_find_ptr(const HashTable *ht, const nxt_str_t *str) 1437{ 1438 int ret; 1439 void *entry; 1440 char buf[256]; 1441 1442 if (nxt_slow_path(str->length >= (sizeof(buf) - 1))) { 1443 return NULL; 1444 } 1445 1446 nxt_memcpy(buf, str->start, str->length); 1447 buf[str->length] = '\0'; 1448 1449 ret = zend_hash_find(ht, buf, str->length + 1, &entry); 1450 if (nxt_fast_path(ret == SUCCESS)) { 1451 return entry; 1452 } 1453 1454 return NULL; 1455} 1456 1457#endif 1458 1459 1460static void 1461nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name, 1462 const char *cstr, uint32_t len, zval *track_vars_array TSRMLS_DC) 1463{ 1464 if (nxt_slow_path(cstr == NULL)) { 1465 return; 1466 } 1467 1468 nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, cstr); 1469 1470 php_register_variable_safe((char *) name, (char *) cstr, len, 1471 track_vars_array TSRMLS_CC); 1472} 1473 1474 1475#if NXT_PHP8 1476static void 1477nxt_php_log_message(const char *message, int syslog_type_int) 1478#else 1479#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1480static void 1481nxt_php_log_message(char *message, int syslog_type_int) 1482#else 1483static void 1484nxt_php_log_message(char *message TSRMLS_DC) 1485#endif 1486#endif 1487{ 1488 nxt_php_run_ctx_t *ctx; 1489 1490 ctx = SG(server_context); 1491 1492 if (ctx != NULL) { 1493 nxt_unit_req_log(ctx->req, NXT_UNIT_LOG_NOTICE, 1494 "php message: %s", message); 1495 1496 } else { 1497 nxt_unit_log(nxt_php_unit_ctx, NXT_UNIT_LOG_NOTICE, 1498 "php message: %s", message); 1499 } 1500}
| 495 496 unit_ctx = nxt_unit_init(&php_init); 497 if (nxt_slow_path(unit_ctx == NULL)) { 498 return NXT_ERROR; 499 } 500 501 nxt_php_unit_ctx = unit_ctx; 502 503 nxt_unit_run(nxt_php_unit_ctx); 504 nxt_unit_done(nxt_php_unit_ctx); 505 506 exit(0); 507 508 return NXT_OK; 509} 510 511 512static nxt_int_t 513nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target, 514 nxt_conf_value_t *conf) 515{ 516 u_char *tmp, *p; 517 nxt_str_t str; 518 nxt_int_t ret; 519 nxt_conf_value_t *value; 520 521 static nxt_str_t root_str = nxt_string("root"); 522 static nxt_str_t script_str = nxt_string("script"); 523 static nxt_str_t index_str = nxt_string("index"); 524 525 value = nxt_conf_get_object_member(conf, &root_str, NULL); 526 527 nxt_conf_get_string(value, &str); 528 529 tmp = nxt_malloc(str.length + 1); 530 if (nxt_slow_path(tmp == NULL)) { 531 return NXT_ERROR; 532 } 533 534 p = tmp; 535 536 p = nxt_cpymem(p, str.start, str.length); 537 *p = '\0'; 538 539 p = nxt_realpath(tmp); 540 if (nxt_slow_path(p == NULL)) { 541 nxt_alert(task, "root realpath(%s) failed %E", tmp, nxt_errno); 542 return NXT_ERROR; 543 } 544 545 nxt_free(tmp); 546 547 target->root.length = nxt_strlen(p); 548 target->root.start = p; 549 550 nxt_php_str_trim_trail(&target->root, '/'); 551 552 value = nxt_conf_get_object_member(conf, &script_str, NULL); 553 554 if (value != NULL) { 555 nxt_conf_get_string(value, &str); 556 557 nxt_php_str_trim_lead(&str, '/'); 558 559 tmp = nxt_malloc(target->root.length + 1 + str.length + 1); 560 if (nxt_slow_path(tmp == NULL)) { 561 return NXT_ERROR; 562 } 563 564 p = tmp; 565 566 p = nxt_cpymem(p, target->root.start, target->root.length); 567 *p++ = '/'; 568 569 p = nxt_cpymem(p, str.start, str.length); 570 *p = '\0'; 571 572 p = nxt_realpath(tmp); 573 if (nxt_slow_path(p == NULL)) { 574 nxt_alert(task, "script realpath(%s) failed %E", tmp, nxt_errno); 575 return NXT_ERROR; 576 } 577 578 nxt_free(tmp); 579 580 target->script_filename.length = nxt_strlen(p); 581 target->script_filename.start = p; 582 583 if (!nxt_str_start(&target->script_filename, 584 target->root.start, target->root.length)) 585 { 586 nxt_alert(task, "script is not under php root"); 587 return NXT_ERROR; 588 } 589 590 ret = nxt_php_dirname(&target->script_filename, 591 &target->script_dirname); 592 if (nxt_slow_path(ret != NXT_OK)) { 593 return NXT_ERROR; 594 } 595 596 target->script_name.length = target->script_filename.length 597 - target->root.length; 598 target->script_name.start = target->script_filename.start 599 + target->root.length; 600 601 } else { 602 value = nxt_conf_get_object_member(conf, &index_str, NULL); 603 604 if (value != NULL) { 605 nxt_conf_get_string(value, &str); 606 607 tmp = nxt_malloc(str.length); 608 if (nxt_slow_path(tmp == NULL)) { 609 return NXT_ERROR; 610 } 611 612 nxt_memcpy(tmp, str.start, str.length); 613 614 target->index.length = str.length; 615 target->index.start = tmp; 616 617 } else { 618 nxt_str_set(&target->index, "index.php"); 619 } 620 } 621 622 return NXT_OK; 623} 624 625 626static nxt_int_t 627nxt_php_set_ini_path(nxt_task_t *task, nxt_str_t *ini_path, char *workdir) 628{ 629 size_t wdlen; 630 u_char *p, *start; 631 632 if (ini_path->start[0] == '/' || workdir == NULL) { 633 p = nxt_malloc(ini_path->length + 1); 634 if (nxt_slow_path(p == NULL)) { 635 return NXT_ERROR; 636 } 637 638 start = p; 639 640 } else { 641 wdlen = nxt_strlen(workdir); 642 643 p = nxt_malloc(wdlen + ini_path->length + 2); 644 if (nxt_slow_path(p == NULL)) { 645 return NXT_ERROR; 646 } 647 648 start = p; 649 650 p = nxt_cpymem(p, workdir, wdlen); 651 652 if (workdir[wdlen - 1] != '/') { 653 *p++ = '/'; 654 } 655 } 656 657 p = nxt_cpymem(p, ini_path->start, ini_path->length); 658 *p = '\0'; 659 660 nxt_php_sapi_module.php_ini_path_override = (char *) start; 661 662 return NXT_OK; 663} 664 665 666static void 667nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type) 668{ 669 uint32_t next; 670 nxt_str_t name, value; 671 nxt_conf_value_t *value_obj; 672 673 if (options != NULL) { 674 next = 0; 675 676 for ( ;; ) { 677 value_obj = nxt_conf_next_object_member(options, &name, &next); 678 if (value_obj == NULL) { 679 break; 680 } 681 682 nxt_conf_get_string(value_obj, &value); 683 684 if (nxt_php_alter_option(&name, &value, type) != NXT_OK) { 685 nxt_log(task, NXT_LOG_ERR, 686 "setting PHP option \"%V: %V\" failed", &name, &value); 687 continue; 688 } 689 690 if (nxt_str_eq(&name, "disable_functions", 17)) { 691#ifdef NXT_PHP8 692 nxt_php_disable_functions(&value); 693#else 694 nxt_php_disable(task, "function", &value, 695 &PG(disable_functions), 696 zend_disable_function); 697#endif 698 continue; 699 } 700 701 if (nxt_str_eq(&name, "disable_classes", 15)) { 702 nxt_php_disable(task, "class", &value, 703 &PG(disable_classes), 704 zend_disable_class); 705 continue; 706 } 707 } 708 } 709} 710 711 712#ifdef NXT_PHP7 713 714static nxt_int_t 715nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type) 716{ 717 zend_string *zs; 718 zend_ini_entry *ini_entry; 719 720 ini_entry = nxt_php_hash_str_find_ptr(EG(ini_directives), name); 721 if (nxt_slow_path(ini_entry == NULL)) { 722 return NXT_ERROR; 723 } 724 725 /* PHP exits on memory allocation errors. */ 726 zs = zend_string_init((char *) value->start, value->length, 1); 727 728 if (ini_entry->on_modify 729 && ini_entry->on_modify(ini_entry, zs, ini_entry->mh_arg1, 730 ini_entry->mh_arg2, ini_entry->mh_arg3, 731 ZEND_INI_STAGE_ACTIVATE) 732 != SUCCESS) 733 { 734 zend_string_release(zs); 735 return NXT_ERROR; 736 } 737 738 ini_entry->value = zs; 739 ini_entry->modifiable = type; 740 741 return NXT_OK; 742} 743 744#else /* PHP 5. */ 745 746static nxt_int_t 747nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, int type) 748{ 749 char *cstr; 750 zend_ini_entry *ini_entry; 751 752 ini_entry = nxt_php_hash_str_find_ptr(EG(ini_directives), name); 753 if (nxt_slow_path(ini_entry == NULL)) { 754 return NXT_ERROR; 755 } 756 757 cstr = nxt_malloc(value->length + 1); 758 if (nxt_slow_path(cstr == NULL)) { 759 return NXT_ERROR; 760 } 761 762 nxt_memcpy(cstr, value->start, value->length); 763 cstr[value->length] = '\0'; 764 765 if (ini_entry->on_modify 766 && ini_entry->on_modify(ini_entry, cstr, value->length, 767 ini_entry->mh_arg1, ini_entry->mh_arg2, 768 ini_entry->mh_arg3, ZEND_INI_STAGE_ACTIVATE 769 TSRMLS_CC) 770 != SUCCESS) 771 { 772 nxt_free(cstr); 773 return NXT_ERROR; 774 } 775 776 ini_entry->value = cstr; 777 ini_entry->value_length = value->length; 778 ini_entry->modifiable = type; 779 780 return NXT_OK; 781} 782 783#endif 784 785 786#ifdef NXT_PHP8 787 788static void 789nxt_php_disable_functions(nxt_str_t *str) 790{ 791 char *p; 792 793 p = nxt_malloc(str->length + 1); 794 if (nxt_slow_path(p == NULL)) { 795 return; 796 } 797 798 nxt_memcpy(p, str->start, str->length); 799 p[str->length] = '\0'; 800 801 zend_disable_functions(p); 802 803 nxt_free(p); 804} 805 806#endif 807 808 809static void 810nxt_php_disable(nxt_task_t *task, const char *type, nxt_str_t *value, 811 char **ptr, nxt_php_disable_t disable) 812{ 813 char c, *p, *start; 814 815 p = nxt_malloc(value->length + 1); 816 if (nxt_slow_path(p == NULL)) { 817 return; 818 } 819 820 /* 821 * PHP frees this memory on module shutdown. 822 * See core_globals_dtor() for details. 823 */ 824 *ptr = p; 825 826 nxt_memcpy(p, value->start, value->length); 827 p[value->length] = '\0'; 828 829 start = p; 830 831 do { 832 c = *p; 833 834 if (c == ' ' || c == ',' || c == '\0') { 835 836 if (p != start) { 837 *p = '\0'; 838 839#ifdef NXT_PHP7 840 if (disable(start, p - start) 841#else 842 if (disable(start, p - start TSRMLS_CC) 843#endif 844 != SUCCESS) 845 { 846 nxt_log(task, NXT_LOG_ERR, 847 "PHP: failed to disable \"%s\": no such %s", 848 start, type); 849 } 850 } 851 852 start = p + 1; 853 } 854 855 p++; 856 857 } while (c != '\0'); 858} 859 860 861static nxt_int_t 862nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir) 863{ 864 size_t length; 865 866 if (file->length == 0 || file->start[0] != '/') { 867 nxt_unit_alert(NULL, "php_dirname: invalid file name " 868 "(not starts from '/')"); 869 return NXT_ERROR; 870 } 871 872 length = file->length; 873 874 while (file->start[length - 1] != '/') { 875 length--; 876 } 877 878 dir->length = length; 879 dir->start = nxt_malloc(length + 1); 880 if (nxt_slow_path(dir->start == NULL)) { 881 return NXT_ERROR; 882 } 883 884 nxt_memcpy(dir->start, file->start, length); 885 886 dir->start[length] = '\0'; 887 888 return NXT_OK; 889} 890 891 892static void 893nxt_php_str_trim_trail(nxt_str_t *str, u_char t) 894{ 895 while (str->length > 0 && str->start[str->length - 1] == t) { 896 str->length--; 897 } 898 899 str->start[str->length] = '\0'; 900} 901 902 903static void 904nxt_php_str_trim_lead(nxt_str_t *str, u_char t) 905{ 906 while (str->length > 0 && str->start[0] == t) { 907 str->length--; 908 str->start++; 909 } 910} 911 912 913nxt_inline u_char * 914nxt_realpath(const void *c) 915{ 916 return (u_char *) realpath(c, NULL); 917} 918 919 920static void 921nxt_php_request_handler(nxt_unit_request_info_t *req) 922{ 923 nxt_php_target_t *target; 924 nxt_php_run_ctx_t ctx; 925 nxt_unit_request_t *r; 926 927 r = req->request; 928 target = &nxt_php_targets[r->app_target]; 929 930 nxt_memzero(&ctx, sizeof(ctx)); 931 932 ctx.req = req; 933 ctx.root = &target->root; 934 ctx.index = &target->index; 935 936 if (target->script_filename.length == 0) { 937 nxt_php_dynamic_request(&ctx, r); 938 return; 939 } 940 941 ctx.script_filename = target->script_filename; 942 ctx.script_dirname = target->script_dirname; 943 ctx.script_name = target->script_name; 944 945 ctx.chdir = (r->app_target != nxt_php_last_target); 946 947 nxt_php_execute(&ctx, r); 948 949 nxt_php_last_target = ctx.chdir ? -1 : r->app_target; 950} 951 952 953static void 954nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) 955{ 956 u_char *p; 957 nxt_str_t path, script_name; 958 nxt_int_t ret; 959 960 path.length = r->path_length; 961 path.start = nxt_unit_sptr_get(&r->path); 962 963 nxt_str_null(&script_name); 964 965 ctx->path_info.start = (u_char *) strstr((char *) path.start, ".php/"); 966 if (ctx->path_info.start != NULL) { 967 ctx->path_info.start += 4; 968 path.length = ctx->path_info.start - path.start; 969 970 ctx->path_info.length = r->path_length - path.length; 971 972 } else if (path.start[path.length - 1] == '/') { 973 script_name = *ctx->index; 974 975 } else { 976 if (nxt_slow_path(path.length < 4 977 || nxt_memcmp(path.start + (path.length - 4), 978 ".php", 4))) 979 { 980 nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); 981 982 return; 983 } 984 } 985 986 ctx->script_filename.length = ctx->root->length 987 + path.length 988 + script_name.length; 989 990 p = nxt_malloc(ctx->script_filename.length + 1); 991 if (nxt_slow_path(p == NULL)) { 992 nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); 993 994 return; 995 } 996 997 ctx->script_filename.start = p; 998 999 ctx->script_name.length = path.length + script_name.length; 1000 ctx->script_name.start = p + ctx->root->length; 1001 1002 p = nxt_cpymem(p, ctx->root->start, ctx->root->length); 1003 p = nxt_cpymem(p, path.start, path.length); 1004 1005 if (script_name.length > 0) { 1006 p = nxt_cpymem(p, script_name.start, script_name.length); 1007 } 1008 1009 *p = '\0'; 1010 1011 ctx->chdir = 1; 1012 1013 ret = nxt_php_dirname(&ctx->script_filename, &ctx->script_dirname); 1014 if (nxt_slow_path(ret != NXT_OK)) { 1015 nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); 1016 nxt_free(ctx->script_filename.start); 1017 1018 return; 1019 } 1020 1021 nxt_php_execute(ctx, r); 1022 1023 nxt_free(ctx->script_filename.start); 1024 nxt_free(ctx->script_dirname.start); 1025 1026 nxt_php_last_target = -1; 1027} 1028 1029 1030static void 1031nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) 1032{ 1033#if (PHP_VERSION_ID < 50600) 1034 void *read_post; 1035#endif 1036 nxt_unit_field_t *f; 1037 zend_file_handle file_handle; 1038 1039 nxt_unit_req_debug(ctx->req, "PHP execute script %s", 1040 ctx->script_filename.start); 1041 1042 SG(server_context) = ctx; 1043 SG(options) |= SAPI_OPTION_NO_CHDIR; 1044 SG(request_info).request_uri = nxt_unit_sptr_get(&r->target); 1045 SG(request_info).request_method = nxt_unit_sptr_get(&r->method); 1046 1047 SG(request_info).proto_num = 1001; 1048 1049 SG(request_info).query_string = r->query.offset 1050 ? nxt_unit_sptr_get(&r->query) : NULL; 1051 SG(request_info).content_length = r->content_length; 1052 1053 if (r->content_type_field != NXT_UNIT_NONE_FIELD) { 1054 f = r->fields + r->content_type_field; 1055 1056 SG(request_info).content_type = nxt_unit_sptr_get(&f->value); 1057 } 1058 1059 if (r->cookie_field != NXT_UNIT_NONE_FIELD) { 1060 f = r->fields + r->cookie_field; 1061 1062 ctx->cookie = nxt_unit_sptr_get(&f->value); 1063 } 1064 1065 if (r->authorization_field != NXT_UNIT_NONE_FIELD) { 1066 f = r->fields + r->authorization_field; 1067 1068#ifdef NXT_PHP7 1069 php_handle_auth_data(nxt_unit_sptr_get(&f->value)); 1070#else 1071 php_handle_auth_data(nxt_unit_sptr_get(&f->value) TSRMLS_CC); 1072#endif 1073 1074 } else { 1075 SG(request_info).auth_digest = NULL; 1076 SG(request_info).auth_user = NULL; 1077 SG(request_info).auth_password = NULL; 1078 } 1079 1080 SG(sapi_headers).http_response_code = 200; 1081 1082 SG(request_info).path_translated = NULL; 1083 1084#ifdef NXT_PHP7 1085 if (nxt_slow_path(php_request_startup() == FAILURE)) { 1086#else 1087 if (nxt_slow_path(php_request_startup(TSRMLS_C) == FAILURE)) { 1088#endif 1089 nxt_unit_req_debug(ctx->req, "php_request_startup() failed"); 1090 1091 nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); 1092 return; 1093 } 1094 1095 if (ctx->chdir) { 1096 ctx->chdir = 0; 1097 nxt_php_vcwd_chdir(ctx->req, ctx->script_dirname.start); 1098 } 1099 1100 nxt_memzero(&file_handle, sizeof(file_handle)); 1101 1102 file_handle.type = ZEND_HANDLE_FILENAME; 1103#if (PHP_VERSION_ID >= 80100) 1104 file_handle.filename = zend_string_init((char *) ctx->script_filename.start, 1105 ctx->script_filename.length, 0); 1106 file_handle.primary_script = 1; 1107#else 1108 file_handle.filename = (char *) ctx->script_filename.start; 1109#endif 1110 1111 php_execute_script(&file_handle TSRMLS_CC); 1112 1113#if (PHP_VERSION_ID >= 80100) 1114 zend_destroy_file_handle(&file_handle); 1115#endif 1116 1117 /* Prevention of consuming possible unread request body. */ 1118#if (PHP_VERSION_ID < 50600) 1119 read_post = sapi_module.read_post; 1120 sapi_module.read_post = NULL; 1121#else 1122 SG(post_read) = 1; 1123#endif 1124 1125 php_request_shutdown(NULL); 1126 1127 if (ctx->req != NULL) { 1128 nxt_unit_request_done(ctx->req, NXT_UNIT_OK); 1129 } 1130 1131#if (PHP_VERSION_ID < 50600) 1132 sapi_module.read_post = read_post; 1133#endif 1134} 1135 1136 1137nxt_inline void 1138nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir) 1139{ 1140 if (nxt_slow_path(VCWD_CHDIR((char *) dir) != 0)) { 1141 nxt_unit_req_alert(req, "VCWD_CHDIR(%s) failed (%d: %s)", 1142 dir, errno, strerror(errno)); 1143 } 1144} 1145 1146 1147static int 1148nxt_php_startup(sapi_module_struct *sapi_module) 1149{ 1150 return php_module_startup(sapi_module, &nxt_php_unit_module, 1); 1151} 1152 1153 1154#ifdef NXT_PHP7 1155static size_t 1156nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC) 1157#else 1158static int 1159nxt_php_unbuffered_write(const char *str, uint str_length TSRMLS_DC) 1160#endif 1161{ 1162 int rc; 1163 nxt_php_run_ctx_t *ctx; 1164 1165 ctx = SG(server_context); 1166 1167 rc = nxt_unit_response_write(ctx->req, str, str_length); 1168 if (nxt_fast_path(rc == NXT_UNIT_OK)) { 1169 return str_length; 1170 } 1171 1172 php_handle_aborted_connection(); 1173 return 0; 1174} 1175 1176 1177static int 1178nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) 1179{ 1180 int rc, fields_count; 1181 char *colon, *value; 1182 uint16_t status; 1183 uint32_t resp_size; 1184 nxt_php_run_ctx_t *ctx; 1185 sapi_header_struct *h; 1186 zend_llist_position zpos; 1187 nxt_unit_request_info_t *req; 1188 1189 ctx = SG(server_context); 1190 req = ctx->req; 1191 1192 nxt_unit_req_debug(req, "nxt_php_send_headers"); 1193 1194 if (SG(request_info).no_headers == 1) { 1195 rc = nxt_unit_response_init(req, 200, 0, 0); 1196 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1197 return SAPI_HEADER_SEND_FAILED; 1198 } 1199 1200 return SAPI_HEADER_SENT_SUCCESSFULLY; 1201 } 1202 1203 resp_size = 0; 1204 fields_count = zend_llist_count(&sapi_headers->headers); 1205 1206 for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos); 1207 h; 1208 h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos)) 1209 { 1210 resp_size += h->header_len; 1211 } 1212 1213 status = SG(sapi_headers).http_response_code; 1214 1215 rc = nxt_unit_response_init(req, status, fields_count, resp_size); 1216 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1217 return SAPI_HEADER_SEND_FAILED; 1218 } 1219 1220 for (h = zend_llist_get_first_ex(&sapi_headers->headers, &zpos); 1221 h; 1222 h = zend_llist_get_next_ex(&sapi_headers->headers, &zpos)) 1223 { 1224 colon = memchr(h->header, ':', h->header_len); 1225 if (nxt_slow_path(colon == NULL)) { 1226 nxt_unit_req_warn(req, "colon not found in header '%.*s'", 1227 (int) h->header_len, h->header); 1228 continue; 1229 } 1230 1231 value = colon + 1; 1232 while(isspace(*value)) { 1233 value++; 1234 } 1235 1236 nxt_unit_response_add_field(req, h->header, colon - h->header, 1237 value, 1238 h->header_len - (value - h->header)); 1239 } 1240 1241 rc = nxt_unit_response_send(req); 1242 if (nxt_slow_path(rc != NXT_UNIT_OK)) { 1243 nxt_unit_req_debug(req, "failed to send response"); 1244 1245 return SAPI_HEADER_SEND_FAILED; 1246 } 1247 1248 return SAPI_HEADER_SENT_SUCCESSFULLY; 1249} 1250 1251 1252#ifdef NXT_PHP7 1253static size_t 1254nxt_php_read_post(char *buffer, size_t count_bytes TSRMLS_DC) 1255#else 1256static int 1257nxt_php_read_post(char *buffer, uint count_bytes TSRMLS_DC) 1258#endif 1259{ 1260 nxt_php_run_ctx_t *ctx; 1261 1262 ctx = SG(server_context); 1263 1264 nxt_unit_req_debug(ctx->req, "nxt_php_read_post %d", (int) count_bytes); 1265 1266 return nxt_unit_request_read(ctx->req, buffer, count_bytes); 1267} 1268 1269 1270static char * 1271nxt_php_read_cookies(TSRMLS_D) 1272{ 1273 nxt_php_run_ctx_t *ctx; 1274 1275 ctx = SG(server_context); 1276 1277 nxt_unit_req_debug(ctx->req, "nxt_php_read_cookies"); 1278 1279 return ctx->cookie; 1280} 1281 1282 1283static void 1284nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) 1285{ 1286 const char *name; 1287 nxt_unit_field_t *f, *f_end; 1288 nxt_php_run_ctx_t *ctx; 1289 nxt_unit_request_t *r; 1290 nxt_unit_request_info_t *req; 1291 1292 ctx = SG(server_context); 1293 1294 req = ctx->req; 1295 r = req->request; 1296 1297 nxt_unit_req_debug(req, "nxt_php_register_variables"); 1298 1299 php_register_variable_safe((char *) "SERVER_SOFTWARE", 1300 (char *) nxt_server.start, 1301 nxt_server.length, track_vars_array TSRMLS_CC); 1302 1303 nxt_php_set_sptr(req, "SERVER_PROTOCOL", &r->version, r->version_length, 1304 track_vars_array TSRMLS_CC); 1305 1306 /* 1307 * 'PHP_SELF' 1308 * The filename of the currently executing script, relative to the document 1309 * root. For instance, $_SERVER['PHP_SELF'] in a script at the address 1310 * http://example.com/foo/bar.php would be /foo/bar.php. The __FILE__ 1311 * constant contains the full path and filename of the current (i.e. 1312 * included) file. If PHP is running as a command-line processor this 1313 * variable contains the script name since PHP 4.3.0. Previously it was not 1314 * available. 1315 */ 1316 1317 if (ctx->path_info.length != 0) { 1318 nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length, 1319 track_vars_array TSRMLS_CC); 1320 1321 nxt_php_set_str(req, "PATH_INFO", &ctx->path_info, 1322 track_vars_array TSRMLS_CC); 1323 1324 } else { 1325 nxt_php_set_str(req, "PHP_SELF", &ctx->script_name, 1326 track_vars_array TSRMLS_CC); 1327 } 1328 1329 /* 1330 * 'SCRIPT_NAME' 1331 * Contains the current script's path. This is useful for pages which need 1332 * to point to themselves. The __FILE__ constant contains the full path and 1333 * filename of the current (i.e. included) file. 1334 */ 1335 1336 nxt_php_set_str(req, "SCRIPT_NAME", &ctx->script_name, 1337 track_vars_array TSRMLS_CC); 1338 1339 /* 1340 * 'SCRIPT_FILENAME' 1341 * The absolute pathname of the currently executing script. 1342 */ 1343 1344 nxt_php_set_str(req, "SCRIPT_FILENAME", &ctx->script_filename, 1345 track_vars_array TSRMLS_CC); 1346 1347 /* 1348 * 'DOCUMENT_ROOT' 1349 * The document root directory under which the current script is executing, 1350 * as defined in the server's configuration file. 1351 */ 1352 1353 nxt_php_set_str(req, "DOCUMENT_ROOT", ctx->root, 1354 track_vars_array TSRMLS_CC); 1355 1356 nxt_php_set_sptr(req, "REQUEST_METHOD", &r->method, r->method_length, 1357 track_vars_array TSRMLS_CC); 1358 nxt_php_set_sptr(req, "REQUEST_URI", &r->target, r->target_length, 1359 track_vars_array TSRMLS_CC); 1360 nxt_php_set_sptr(req, "QUERY_STRING", &r->query, r->query_length, 1361 track_vars_array TSRMLS_CC); 1362 1363 nxt_php_set_sptr(req, "REMOTE_ADDR", &r->remote, r->remote_length, 1364 track_vars_array TSRMLS_CC); 1365 nxt_php_set_sptr(req, "SERVER_ADDR", &r->local, r->local_length, 1366 track_vars_array TSRMLS_CC); 1367 1368 nxt_php_set_sptr(req, "SERVER_NAME", &r->server_name, r->server_name_length, 1369 track_vars_array TSRMLS_CC); 1370 nxt_php_set_cstr(req, "SERVER_PORT", "80", 2, track_vars_array TSRMLS_CC); 1371 1372 if (r->tls) { 1373 nxt_php_set_cstr(req, "HTTPS", "on", 2, track_vars_array TSRMLS_CC); 1374 } 1375 1376 f_end = r->fields + r->fields_count; 1377 for (f = r->fields; f < f_end; f++) { 1378 name = nxt_unit_sptr_get(&f->name); 1379 1380 nxt_php_set_sptr(req, name, &f->value, f->value_length, 1381 track_vars_array TSRMLS_CC); 1382 } 1383 1384 if (r->content_length_field != NXT_UNIT_NONE_FIELD) { 1385 f = r->fields + r->content_length_field; 1386 1387 nxt_php_set_sptr(req, "CONTENT_LENGTH", &f->value, f->value_length, 1388 track_vars_array TSRMLS_CC); 1389 } 1390 1391 if (r->content_type_field != NXT_UNIT_NONE_FIELD) { 1392 f = r->fields + r->content_type_field; 1393 1394 nxt_php_set_sptr(req, "CONTENT_TYPE", &f->value, f->value_length, 1395 track_vars_array TSRMLS_CC); 1396 } 1397} 1398 1399 1400static void 1401nxt_php_set_sptr(nxt_unit_request_info_t *req, const char *name, 1402 nxt_unit_sptr_t *v, uint32_t len, zval *track_vars_array TSRMLS_DC) 1403{ 1404 char *str; 1405 1406 str = nxt_unit_sptr_get(v); 1407 1408 nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, str); 1409 1410 php_register_variable_safe((char *) name, str, len, 1411 track_vars_array TSRMLS_CC); 1412} 1413 1414 1415nxt_inline void 1416nxt_php_set_str(nxt_unit_request_info_t *req, const char *name, 1417 nxt_str_t *s, zval *track_vars_array TSRMLS_DC) 1418{ 1419 nxt_php_set_cstr(req, name, (char *) s->start, s->length, 1420 track_vars_array TSRMLS_CC); 1421} 1422 1423 1424#ifdef NXT_PHP7 1425 1426static void * 1427nxt_php_hash_str_find_ptr(const HashTable *ht, const nxt_str_t *str) 1428{ 1429 return zend_hash_str_find_ptr(ht, (const char *) str->start, str->length); 1430} 1431 1432#else 1433 1434static void * 1435nxt_php_hash_str_find_ptr(const HashTable *ht, const nxt_str_t *str) 1436{ 1437 int ret; 1438 void *entry; 1439 char buf[256]; 1440 1441 if (nxt_slow_path(str->length >= (sizeof(buf) - 1))) { 1442 return NULL; 1443 } 1444 1445 nxt_memcpy(buf, str->start, str->length); 1446 buf[str->length] = '\0'; 1447 1448 ret = zend_hash_find(ht, buf, str->length + 1, &entry); 1449 if (nxt_fast_path(ret == SUCCESS)) { 1450 return entry; 1451 } 1452 1453 return NULL; 1454} 1455 1456#endif 1457 1458 1459static void 1460nxt_php_set_cstr(nxt_unit_request_info_t *req, const char *name, 1461 const char *cstr, uint32_t len, zval *track_vars_array TSRMLS_DC) 1462{ 1463 if (nxt_slow_path(cstr == NULL)) { 1464 return; 1465 } 1466 1467 nxt_unit_req_debug(req, "php: register %s='%.*s'", name, (int) len, cstr); 1468 1469 php_register_variable_safe((char *) name, (char *) cstr, len, 1470 track_vars_array TSRMLS_CC); 1471} 1472 1473 1474#if NXT_PHP8 1475static void 1476nxt_php_log_message(const char *message, int syslog_type_int) 1477#else 1478#ifdef NXT_HAVE_PHP_LOG_MESSAGE_WITH_SYSLOG_TYPE 1479static void 1480nxt_php_log_message(char *message, int syslog_type_int) 1481#else 1482static void 1483nxt_php_log_message(char *message TSRMLS_DC) 1484#endif 1485#endif 1486{ 1487 nxt_php_run_ctx_t *ctx; 1488 1489 ctx = SG(server_context); 1490 1491 if (ctx != NULL) { 1492 nxt_unit_req_log(ctx->req, NXT_UNIT_LOG_NOTICE, 1493 "php message: %s", message); 1494 1495 } else { 1496 nxt_unit_log(nxt_php_unit_ctx, NXT_UNIT_LOG_NOTICE, 1497 "php message: %s", message); 1498 } 1499}
|