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