1 2/* 3 * Copyright (C) Max Romanov 4 * Copyright (C) Igor Sysoev 5 * Copyright (C) Valentin V. Bartenev 6 * Copyright (C) NGINX, Inc. 7 */ 8 9#include <nxt_main.h> 10#include <nxt_runtime.h> 11#include <nxt_main_process.h> 12#include <nxt_router.h> 13#include <nxt_http.h> 14#include <nxt_application.h> 15#include <nxt_unit.h> 16#include <nxt_port_memory_int.h> 17#include <nxt_isolation.h> 18 19#include <glob.h> 20 21#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) 22#include <sys/prctl.h> 23#endif 24 25 26typedef struct { 27 nxt_app_type_t type; 28 nxt_str_t version; 29 nxt_str_t file; 30 nxt_array_t *mounts; 31} nxt_module_t; 32 33 34static nxt_int_t nxt_discovery_start(nxt_task_t *task, 35 nxt_process_data_t *data); 36static nxt_buf_t *nxt_discovery_modules(nxt_task_t *task, const char *path); 37static nxt_int_t nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, 38 nxt_array_t *modules, const char *name); 39static void nxt_discovery_completion_handler(nxt_task_t *task, void *obj, 40 void *data); 41static void nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, 42 void *data); 43static nxt_app_module_t *nxt_app_module_load(nxt_task_t *task, 44 const char *name); 45static nxt_int_t nxt_app_setup(nxt_task_t *task, nxt_process_t *process); 46static nxt_int_t nxt_app_set_environment(nxt_conf_value_t *environment); 47static u_char *nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src); 48 49 50nxt_str_t nxt_server = nxt_string(NXT_SERVER); 51 52 53static uint32_t compat[] = { 54 NXT_VERNUM, NXT_DEBUG, 55}; 56 57 58static nxt_app_module_t *nxt_app; 59 60 61static const nxt_port_handlers_t nxt_discovery_process_port_handlers = { 62 .quit = nxt_signal_quit_handler, 63 .new_port = nxt_port_new_port_handler, 64 .change_file = nxt_port_change_log_file_handler, 65 .mmap = nxt_port_mmap_handler, 66 .data = nxt_port_data_handler, 67 .remove_pid = nxt_port_remove_pid_handler, 68 .rpc_ready = nxt_port_rpc_handler, 69 .rpc_error = nxt_port_rpc_handler, 70}; 71 72 73static const nxt_port_handlers_t nxt_app_process_port_handlers = { 74 .quit = nxt_signal_quit_handler, 75 .rpc_ready = nxt_port_rpc_handler, 76 .rpc_error = nxt_port_rpc_handler, 77}; 78 79 80const nxt_process_init_t nxt_discovery_process = { 81 .name = "discovery", 82 .type = NXT_PROCESS_DISCOVERY, 83 .prefork = NULL, 84 .restart = 0, 85 .setup = nxt_process_core_setup, 86 .start = nxt_discovery_start, 87 .port_handlers = &nxt_discovery_process_port_handlers, 88 .signals = nxt_process_signals, 89}; 90 91 92const nxt_process_init_t nxt_app_process = { 93 .type = NXT_PROCESS_APP, 94 .setup = nxt_app_setup, 95 .prefork = nxt_isolation_main_prefork, 96 .restart = 0, 97 .start = NULL, /* set to module->start */ 98 .port_handlers = &nxt_app_process_port_handlers, 99 .signals = nxt_process_signals, 100}; 101 102 103static nxt_int_t 104nxt_discovery_start(nxt_task_t *task, nxt_process_data_t *data) 105{ 106 uint32_t stream; 107 nxt_buf_t *b; 108 nxt_int_t ret; 109 nxt_port_t *main_port, *discovery_port; 110 nxt_runtime_t *rt; 111 112 nxt_log(task, NXT_LOG_INFO, "discovery started"); 113 114 rt = task->thread->runtime; 115 116 b = nxt_discovery_modules(task, rt->modules); 117 if (nxt_slow_path(b == NULL)) { 118 return NXT_ERROR; 119 } 120 121 main_port = rt->port_by_type[NXT_PROCESS_MAIN]; 122 discovery_port = rt->port_by_type[NXT_PROCESS_DISCOVERY]; 123 124 stream = nxt_port_rpc_register_handler(task, discovery_port, 125 nxt_discovery_quit, 126 nxt_discovery_quit, 127 main_port->pid, NULL); 128 129 if (nxt_slow_path(stream == 0)) { 130 return NXT_ERROR; 131 } 132 133 ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_MODULES, -1, 134 stream, discovery_port->id, b); 135 136 if (nxt_slow_path(ret != NXT_OK)) { 137 nxt_port_rpc_cancel(task, discovery_port, stream); 138 return NXT_ERROR; 139 } 140 141 return NXT_OK; 142} 143 144 145static nxt_buf_t * 146nxt_discovery_modules(nxt_task_t *task, const char *path) 147{ 148 char *name; 149 u_char *p, *end; 150 size_t size; 151 glob_t glb; 152 nxt_mp_t *mp; 153 nxt_buf_t *b; 154 nxt_int_t ret; 155 nxt_uint_t i, n, j; 156 nxt_array_t *modules, *mounts; 157 nxt_module_t *module; 158 nxt_fs_mount_t *mnt; 159 160 b = NULL; 161 162 mp = nxt_mp_create(1024, 128, 256, 32); 163 if (mp == NULL) { 164 return b; 165 } 166 167 ret = glob(path, 0, NULL, &glb); 168 169 n = glb.gl_pathc; 170 171 if (ret != 0) { 172 nxt_log(task, NXT_LOG_NOTICE, 173 "no modules matching: \"%s\" found", path); 174 n = 0; 175 } 176 177 modules = nxt_array_create(mp, n, sizeof(nxt_module_t)); 178 if (modules == NULL) { 179 goto fail; 180 } 181 182 for (i = 0; i < n; i++) { 183 name = glb.gl_pathv[i]; 184 185 ret = nxt_discovery_module(task, mp, modules, name); 186 if (ret != NXT_OK) { 187 goto fail; 188 } 189 } 190 191 size = nxt_length("[]"); 192 module = modules->elts; 193 n = modules->nelts; 194 195 for (i = 0; i < n; i++) { 196 nxt_debug(task, "module: %d %V %V", 197 module[i].type, &module[i].version, &module[i].file); 198 199 size += nxt_length("{\"type\": ,"); 200 size += nxt_length(" \"version\": \"\","); 201 size += nxt_length(" \"file\": \"\","); 202 size += nxt_length(" \"mounts\": []},"); 203 204 size += NXT_INT_T_LEN 205 + module[i].version.length 206 + module[i].file.length; 207 208 mounts = module[i].mounts; 209 210 size += mounts->nelts * nxt_length("{\"src\": \"\", \"dst\": \"\", " 211 "\"type\": , \"name\": \"\", " 212 "\"flags\": , \"data\": \"\"},"); 213 214 mnt = mounts->elts; 215 216 for (j = 0; j < mounts->nelts; j++) { 217 size += nxt_strlen(mnt[j].src) + nxt_strlen(mnt[j].dst) 218 + nxt_strlen(mnt[j].name) + (2 * NXT_INT_T_LEN) 219 + (mnt[j].data == NULL ? 0 : nxt_strlen(mnt[j].data)); 220 } 221 } 222 223 b = nxt_buf_mem_alloc(mp, size, 0); 224 if (b == NULL) { 225 goto fail; 226 } 227 228 b->completion_handler = nxt_discovery_completion_handler; 229 230 p = b->mem.free; 231 end = b->mem.end; 232 *p++ = '['; 233 234 for (i = 0; i < n; i++) { 235 mounts = module[i].mounts; 236 237 p = nxt_sprintf(p, end, "{\"type\": %d, \"version\": \"%V\", " 238 "\"file\": \"%V\", \"mounts\": [", 239 module[i].type, &module[i].version, &module[i].file); 240 241 mnt = mounts->elts; 242 for (j = 0; j < mounts->nelts; j++) { 243 p = nxt_sprintf(p, end, 244 "{\"src\": \"%s\", \"dst\": \"%s\", " 245 "\"name\": \"%s\", \"type\": %d, \"flags\": %d, " 246 "\"data\": \"%s\"},", 247 mnt[j].src, mnt[j].dst, mnt[j].name, mnt[j].type, 248 mnt[j].flags, 249 mnt[j].data == NULL ? (u_char *) "" : mnt[j].data); 250 } 251 252 *p++ = ']'; 253 *p++ = '}'; 254 *p++ = ','; 255 } 256 257 *p++ = ']'; 258 259 if (nxt_slow_path(p > end)) { 260 nxt_alert(task, "discovery write past the buffer"); 261 goto fail; 262 } 263 264 b->mem.free = p; 265 266fail: 267 268 globfree(&glb); 269 270 return b; 271} 272 273 274static nxt_int_t 275nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules, 276 const char *name) 277{ 278 void *dl; 279 nxt_str_t version; 280 nxt_int_t ret; 281 nxt_uint_t i, j, n; 282 nxt_array_t *mounts; 283 nxt_module_t *module; 284 nxt_app_type_t type; 285 nxt_fs_mount_t *to; 286 nxt_app_module_t *app; 287 const nxt_fs_mount_t *from; 288 289 /* 290 * Only memory allocation failure should return NXT_ERROR. 291 * Any module processing errors are ignored. 292 */ 293 ret = NXT_ERROR; 294 295 dl = dlopen(name, RTLD_GLOBAL | RTLD_NOW); 296 297 if (dl == NULL) { 298 nxt_alert(task, "dlopen(\"%s\"), failed: \"%s\"", name, dlerror()); 299 return NXT_OK; 300 } 301 302 app = dlsym(dl, "nxt_app_module"); 303 304 if (app != NULL) { 305 nxt_log(task, NXT_LOG_NOTICE, "module: %V %s \"%s\"", 306 &app->type, app->version, name); 307 308 if (app->compat_length != sizeof(compat) 309 || nxt_memcmp(app->compat, compat, sizeof(compat)) != 0) 310 { 311 nxt_log(task, NXT_LOG_NOTICE, "incompatible module %s", name); 312 313 goto done; 314 } 315 316 type = nxt_app_parse_type(app->type.start, app->type.length); 317 318 if (type == NXT_APP_UNKNOWN) { 319 nxt_log(task, NXT_LOG_NOTICE, "unknown module type %V", &app->type); 320 321 goto done; 322 } 323 324 module = modules->elts; 325 n = modules->nelts; 326 327 version.start = (u_char *) app->version; 328 version.length = nxt_strlen(app->version); 329 330 for (i = 0; i < n; i++) { 331 if (type == module[i].type 332 && nxt_strstr_eq(&module[i].version, &version)) 333 { 334 nxt_log(task, NXT_LOG_NOTICE, 335 "ignoring %s module with the same " 336 "application language version %V %V as in %V", 337 name, &app->type, &version, &module[i].file); 338 339 goto done; 340 } 341 } 342 343 module = nxt_array_add(modules); 344 if (module == NULL) { 345 goto fail; 346 } 347 348 module->type = type; 349 350 nxt_str_dup(mp, &module->version, &version); 351 if (module->version.start == NULL) { 352 goto fail; 353 } 354 355 module->file.length = nxt_strlen(name); 356 357 module->file.start = nxt_mp_alloc(mp, module->file.length); 358 if (module->file.start == NULL) { 359 goto fail; 360 } 361 362 nxt_memcpy(module->file.start, name, module->file.length); 363 364 module->mounts = nxt_array_create(mp, app->nmounts, 365 sizeof(nxt_fs_mount_t)); 366 367 if (nxt_slow_path(module->mounts == NULL)) { 368 goto fail; 369 } 370 371 mounts = module->mounts; 372 373 for (j = 0; j < app->nmounts; j++) { 374 from = &app->mounts[j]; 375 to = nxt_array_zero_add(mounts); 376 if (nxt_slow_path(to == NULL)) { 377 goto fail; 378 } 379 380 to->src = nxt_cstr_dup(mp, to->src, from->src); 381 if (nxt_slow_path(to->src == NULL)) { 382 goto fail; 383 } 384 385 to->dst = nxt_cstr_dup(mp, to->dst, from->dst); 386 if (nxt_slow_path(to->dst == NULL)) { 387 goto fail; 388 } 389 390 to->name = nxt_cstr_dup(mp, to->name, from->name); 391 if (nxt_slow_path(to->name == NULL)) { 392 goto fail; 393 } 394 395 to->type = from->type; 396 397 if (from->data != NULL) { 398 to->data = nxt_cstr_dup(mp, to->data, from->data); 399 if (nxt_slow_path(to->data == NULL)) { 400 goto fail; 401 } 402 } 403 404 to->flags = from->flags; 405 } 406 407 } else { 408 nxt_alert(task, "dlsym(\"%s\"), failed: \"%s\"", name, dlerror()); 409 } 410 411done: 412 413 ret = NXT_OK; 414 415fail: 416 417 if (dlclose(dl) != 0) { 418 nxt_alert(task, "dlclose(\"%s\"), failed: \"%s\"", name, dlerror()); 419 } 420 421 return ret; 422} 423 424 425static void 426nxt_discovery_completion_handler(nxt_task_t *task, void *obj, void *data) 427{ 428 nxt_mp_t *mp; 429 nxt_buf_t *b; 430 431 b = obj; 432 mp = b->data; 433 434 nxt_mp_destroy(mp); 435} 436 437 438static void 439nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) 440{ 441 nxt_signal_quit_handler(task, msg); 442} 443 444 445static nxt_int_t 446nxt_app_setup(nxt_task_t *task, nxt_process_t *process) 447{ 448 nxt_int_t ret; 449 nxt_process_init_t *init; 450 nxt_app_lang_module_t *lang; 451 nxt_common_app_conf_t *app_conf; 452 453 app_conf = process->data.app; 454 455 lang = nxt_app_lang_module(task->thread->runtime, &app_conf->type); 456 if (nxt_slow_path(lang == NULL)) { 457 nxt_alert(task, "unknown application type: \"%V\"", &app_conf->type); 458 return NXT_ERROR; 459 } 460 461 nxt_app = lang->module; 462 463 if (nxt_app == NULL) { 464 nxt_debug(task, "application language module: %s \"%s\"", 465 lang->version, lang->file); 466 467 nxt_app = nxt_app_module_load(task, lang->file); 468 if (nxt_slow_path(nxt_app == NULL)) { 469 return NXT_ERROR; 470 } 471 } 472 473 if (nxt_slow_path(nxt_app_set_environment(app_conf->environment) 474 != NXT_OK)) 475 { 476 nxt_alert(task, "failed to set environment"); 477 return NXT_ERROR; 478 } 479 480 if (nxt_app->setup != NULL) { 481 ret = nxt_app->setup(task, process, app_conf); 482 483 if (nxt_slow_path(ret != NXT_OK)) { 484 return ret; 485 } 486 } 487 488#if (NXT_HAVE_ISOLATION_ROOTFS) 489 if (process->isolation.rootfs != NULL) { 490 if (process->isolation.mounts != NULL) { 491 ret = nxt_isolation_prepare_rootfs(task, process); 492 if (nxt_slow_path(ret != NXT_OK)) { 493 return ret; 494 } 495 } 496 497 ret = nxt_isolation_change_root(task, process); 498 if (nxt_slow_path(ret != NXT_OK)) { 499 return NXT_ERROR; 500 } 501 } 502#endif 503 504 if (app_conf->working_directory != NULL 505 && app_conf->working_directory[0] != 0) 506 { 507 ret = chdir(app_conf->working_directory); 508 509 if (nxt_slow_path(ret != 0)) { 510 nxt_log(task, NXT_LOG_WARN, "chdir(%s) failed %E", 511 app_conf->working_directory, nxt_errno); 512 513 return NXT_ERROR; 514 } 515 } 516 517 init = nxt_process_init(process); 518 519 init->start = nxt_app->start; 520 521 process->state = NXT_PROCESS_STATE_CREATED; 522 523 return NXT_OK; 524} 525 526 527static nxt_app_module_t * 528nxt_app_module_load(nxt_task_t *task, const char *name) 529{ 530 void *dl; 531 532 dl = dlopen(name, RTLD_GLOBAL | RTLD_LAZY); 533 534 if (dl != NULL) { 535 return dlsym(dl, "nxt_app_module"); 536 } 537 538 nxt_alert(task, "dlopen(\"%s\"), failed: \"%s\"", name, dlerror()); 539 540 return NULL; 541} 542 543 544static nxt_int_t 545nxt_app_set_environment(nxt_conf_value_t *environment) 546{ 547 char *env, *p; 548 uint32_t next; 549 nxt_str_t name, value; 550 nxt_conf_value_t *value_obj; 551 552 if (environment != NULL) { 553 next = 0; 554 555 for ( ;; ) { 556 value_obj = nxt_conf_next_object_member(environment, &name, &next); 557 if (value_obj == NULL) { 558 break; 559 } 560 561 nxt_conf_get_string(value_obj, &value); 562 563 env = nxt_malloc(name.length + value.length + 2); 564 if (nxt_slow_path(env == NULL)) { 565 return NXT_ERROR; 566 } 567 568 p = nxt_cpymem(env, name.start, name.length); 569 *p++ = '='; 570 p = nxt_cpymem(p, value.start, value.length); 571 *p = '\0'; 572 573 if (nxt_slow_path(putenv(env) != 0)) { 574 return NXT_ERROR; 575 } 576 } 577 } 578 579 return NXT_OK; 580} 581 582 583static u_char * 584nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src) 585{ 586 u_char *p; 587 size_t len; 588 589 len = nxt_strlen(src); 590 591 if (dst == NULL) { 592 dst = nxt_mp_alloc(mp, len + 1); 593 if (nxt_slow_path(dst == NULL)) { 594 return NULL; 595 } 596 } 597 598 p = nxt_cpymem(dst, src, len); 599 *p = '\0'; 600 601 return dst; 602} 603 604 605nxt_app_lang_module_t * 606nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name) 607{ 608 u_char *p, *end, *version; 609 size_t version_length; 610 nxt_uint_t i, n; 611 nxt_app_type_t type; 612 nxt_app_lang_module_t *lang; 613 614 end = name->start + name->length; 615 version = end; 616 617 for (p = name->start; p < end; p++) { 618 if (*p == ' ') { 619 version = p + 1; 620 break; 621 } 622 623 if (*p >= '0' && *p <= '9') { 624 version = p; 625 break; 626 } 627 } 628 629 type = nxt_app_parse_type(name->start, p - name->start); 630 631 if (type == NXT_APP_UNKNOWN) { 632 return NULL; 633 } 634 635 version_length = end - version; 636 637 lang = rt->languages->elts; 638 n = rt->languages->nelts; 639 640 for (i = 0; i < n; i++) { 641 642 /* 643 * Versions are sorted in descending order 644 * so first match chooses the highest version. 645 */ 646 647 if (lang[i].type == type 648 && nxt_strvers_match(lang[i].version, version, version_length)) 649 { 650 return &lang[i]; 651 } 652 } 653 654 return NULL; 655} 656 657 658nxt_app_type_t 659nxt_app_parse_type(u_char *p, size_t length) 660{ 661 nxt_str_t str; 662 663 str.length = length; 664 str.start = p; 665 666 if (nxt_str_eq(&str, "external", 8) || nxt_str_eq(&str, "go", 2)) { 667 return NXT_APP_EXTERNAL; 668 669 } else if (nxt_str_eq(&str, "python", 6)) { 670 return NXT_APP_PYTHON; 671 672 } else if (nxt_str_eq(&str, "php", 3)) { 673 return NXT_APP_PHP; 674 675 } else if (nxt_str_eq(&str, "perl", 4)) { 676 return NXT_APP_PERL; 677 678 } else if (nxt_str_eq(&str, "ruby", 4)) { 679 return NXT_APP_RUBY; 680 681 } else if (nxt_str_eq(&str, "java", 4)) { 682 return NXT_APP_JAVA; 683 } 684 685 return NXT_APP_UNKNOWN; 686} 687 688 689nxt_int_t
| 1 2/* 3 * Copyright (C) Max Romanov 4 * Copyright (C) Igor Sysoev 5 * Copyright (C) Valentin V. Bartenev 6 * Copyright (C) NGINX, Inc. 7 */ 8 9#include <nxt_main.h> 10#include <nxt_runtime.h> 11#include <nxt_main_process.h> 12#include <nxt_router.h> 13#include <nxt_http.h> 14#include <nxt_application.h> 15#include <nxt_unit.h> 16#include <nxt_port_memory_int.h> 17#include <nxt_isolation.h> 18 19#include <glob.h> 20 21#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) 22#include <sys/prctl.h> 23#endif 24 25 26typedef struct { 27 nxt_app_type_t type; 28 nxt_str_t version; 29 nxt_str_t file; 30 nxt_array_t *mounts; 31} nxt_module_t; 32 33 34static nxt_int_t nxt_discovery_start(nxt_task_t *task, 35 nxt_process_data_t *data); 36static nxt_buf_t *nxt_discovery_modules(nxt_task_t *task, const char *path); 37static nxt_int_t nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, 38 nxt_array_t *modules, const char *name); 39static void nxt_discovery_completion_handler(nxt_task_t *task, void *obj, 40 void *data); 41static void nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, 42 void *data); 43static nxt_app_module_t *nxt_app_module_load(nxt_task_t *task, 44 const char *name); 45static nxt_int_t nxt_app_setup(nxt_task_t *task, nxt_process_t *process); 46static nxt_int_t nxt_app_set_environment(nxt_conf_value_t *environment); 47static u_char *nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src); 48 49 50nxt_str_t nxt_server = nxt_string(NXT_SERVER); 51 52 53static uint32_t compat[] = { 54 NXT_VERNUM, NXT_DEBUG, 55}; 56 57 58static nxt_app_module_t *nxt_app; 59 60 61static const nxt_port_handlers_t nxt_discovery_process_port_handlers = { 62 .quit = nxt_signal_quit_handler, 63 .new_port = nxt_port_new_port_handler, 64 .change_file = nxt_port_change_log_file_handler, 65 .mmap = nxt_port_mmap_handler, 66 .data = nxt_port_data_handler, 67 .remove_pid = nxt_port_remove_pid_handler, 68 .rpc_ready = nxt_port_rpc_handler, 69 .rpc_error = nxt_port_rpc_handler, 70}; 71 72 73static const nxt_port_handlers_t nxt_app_process_port_handlers = { 74 .quit = nxt_signal_quit_handler, 75 .rpc_ready = nxt_port_rpc_handler, 76 .rpc_error = nxt_port_rpc_handler, 77}; 78 79 80const nxt_process_init_t nxt_discovery_process = { 81 .name = "discovery", 82 .type = NXT_PROCESS_DISCOVERY, 83 .prefork = NULL, 84 .restart = 0, 85 .setup = nxt_process_core_setup, 86 .start = nxt_discovery_start, 87 .port_handlers = &nxt_discovery_process_port_handlers, 88 .signals = nxt_process_signals, 89}; 90 91 92const nxt_process_init_t nxt_app_process = { 93 .type = NXT_PROCESS_APP, 94 .setup = nxt_app_setup, 95 .prefork = nxt_isolation_main_prefork, 96 .restart = 0, 97 .start = NULL, /* set to module->start */ 98 .port_handlers = &nxt_app_process_port_handlers, 99 .signals = nxt_process_signals, 100}; 101 102 103static nxt_int_t 104nxt_discovery_start(nxt_task_t *task, nxt_process_data_t *data) 105{ 106 uint32_t stream; 107 nxt_buf_t *b; 108 nxt_int_t ret; 109 nxt_port_t *main_port, *discovery_port; 110 nxt_runtime_t *rt; 111 112 nxt_log(task, NXT_LOG_INFO, "discovery started"); 113 114 rt = task->thread->runtime; 115 116 b = nxt_discovery_modules(task, rt->modules); 117 if (nxt_slow_path(b == NULL)) { 118 return NXT_ERROR; 119 } 120 121 main_port = rt->port_by_type[NXT_PROCESS_MAIN]; 122 discovery_port = rt->port_by_type[NXT_PROCESS_DISCOVERY]; 123 124 stream = nxt_port_rpc_register_handler(task, discovery_port, 125 nxt_discovery_quit, 126 nxt_discovery_quit, 127 main_port->pid, NULL); 128 129 if (nxt_slow_path(stream == 0)) { 130 return NXT_ERROR; 131 } 132 133 ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_MODULES, -1, 134 stream, discovery_port->id, b); 135 136 if (nxt_slow_path(ret != NXT_OK)) { 137 nxt_port_rpc_cancel(task, discovery_port, stream); 138 return NXT_ERROR; 139 } 140 141 return NXT_OK; 142} 143 144 145static nxt_buf_t * 146nxt_discovery_modules(nxt_task_t *task, const char *path) 147{ 148 char *name; 149 u_char *p, *end; 150 size_t size; 151 glob_t glb; 152 nxt_mp_t *mp; 153 nxt_buf_t *b; 154 nxt_int_t ret; 155 nxt_uint_t i, n, j; 156 nxt_array_t *modules, *mounts; 157 nxt_module_t *module; 158 nxt_fs_mount_t *mnt; 159 160 b = NULL; 161 162 mp = nxt_mp_create(1024, 128, 256, 32); 163 if (mp == NULL) { 164 return b; 165 } 166 167 ret = glob(path, 0, NULL, &glb); 168 169 n = glb.gl_pathc; 170 171 if (ret != 0) { 172 nxt_log(task, NXT_LOG_NOTICE, 173 "no modules matching: \"%s\" found", path); 174 n = 0; 175 } 176 177 modules = nxt_array_create(mp, n, sizeof(nxt_module_t)); 178 if (modules == NULL) { 179 goto fail; 180 } 181 182 for (i = 0; i < n; i++) { 183 name = glb.gl_pathv[i]; 184 185 ret = nxt_discovery_module(task, mp, modules, name); 186 if (ret != NXT_OK) { 187 goto fail; 188 } 189 } 190 191 size = nxt_length("[]"); 192 module = modules->elts; 193 n = modules->nelts; 194 195 for (i = 0; i < n; i++) { 196 nxt_debug(task, "module: %d %V %V", 197 module[i].type, &module[i].version, &module[i].file); 198 199 size += nxt_length("{\"type\": ,"); 200 size += nxt_length(" \"version\": \"\","); 201 size += nxt_length(" \"file\": \"\","); 202 size += nxt_length(" \"mounts\": []},"); 203 204 size += NXT_INT_T_LEN 205 + module[i].version.length 206 + module[i].file.length; 207 208 mounts = module[i].mounts; 209 210 size += mounts->nelts * nxt_length("{\"src\": \"\", \"dst\": \"\", " 211 "\"type\": , \"name\": \"\", " 212 "\"flags\": , \"data\": \"\"},"); 213 214 mnt = mounts->elts; 215 216 for (j = 0; j < mounts->nelts; j++) { 217 size += nxt_strlen(mnt[j].src) + nxt_strlen(mnt[j].dst) 218 + nxt_strlen(mnt[j].name) + (2 * NXT_INT_T_LEN) 219 + (mnt[j].data == NULL ? 0 : nxt_strlen(mnt[j].data)); 220 } 221 } 222 223 b = nxt_buf_mem_alloc(mp, size, 0); 224 if (b == NULL) { 225 goto fail; 226 } 227 228 b->completion_handler = nxt_discovery_completion_handler; 229 230 p = b->mem.free; 231 end = b->mem.end; 232 *p++ = '['; 233 234 for (i = 0; i < n; i++) { 235 mounts = module[i].mounts; 236 237 p = nxt_sprintf(p, end, "{\"type\": %d, \"version\": \"%V\", " 238 "\"file\": \"%V\", \"mounts\": [", 239 module[i].type, &module[i].version, &module[i].file); 240 241 mnt = mounts->elts; 242 for (j = 0; j < mounts->nelts; j++) { 243 p = nxt_sprintf(p, end, 244 "{\"src\": \"%s\", \"dst\": \"%s\", " 245 "\"name\": \"%s\", \"type\": %d, \"flags\": %d, " 246 "\"data\": \"%s\"},", 247 mnt[j].src, mnt[j].dst, mnt[j].name, mnt[j].type, 248 mnt[j].flags, 249 mnt[j].data == NULL ? (u_char *) "" : mnt[j].data); 250 } 251 252 *p++ = ']'; 253 *p++ = '}'; 254 *p++ = ','; 255 } 256 257 *p++ = ']'; 258 259 if (nxt_slow_path(p > end)) { 260 nxt_alert(task, "discovery write past the buffer"); 261 goto fail; 262 } 263 264 b->mem.free = p; 265 266fail: 267 268 globfree(&glb); 269 270 return b; 271} 272 273 274static nxt_int_t 275nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules, 276 const char *name) 277{ 278 void *dl; 279 nxt_str_t version; 280 nxt_int_t ret; 281 nxt_uint_t i, j, n; 282 nxt_array_t *mounts; 283 nxt_module_t *module; 284 nxt_app_type_t type; 285 nxt_fs_mount_t *to; 286 nxt_app_module_t *app; 287 const nxt_fs_mount_t *from; 288 289 /* 290 * Only memory allocation failure should return NXT_ERROR. 291 * Any module processing errors are ignored. 292 */ 293 ret = NXT_ERROR; 294 295 dl = dlopen(name, RTLD_GLOBAL | RTLD_NOW); 296 297 if (dl == NULL) { 298 nxt_alert(task, "dlopen(\"%s\"), failed: \"%s\"", name, dlerror()); 299 return NXT_OK; 300 } 301 302 app = dlsym(dl, "nxt_app_module"); 303 304 if (app != NULL) { 305 nxt_log(task, NXT_LOG_NOTICE, "module: %V %s \"%s\"", 306 &app->type, app->version, name); 307 308 if (app->compat_length != sizeof(compat) 309 || nxt_memcmp(app->compat, compat, sizeof(compat)) != 0) 310 { 311 nxt_log(task, NXT_LOG_NOTICE, "incompatible module %s", name); 312 313 goto done; 314 } 315 316 type = nxt_app_parse_type(app->type.start, app->type.length); 317 318 if (type == NXT_APP_UNKNOWN) { 319 nxt_log(task, NXT_LOG_NOTICE, "unknown module type %V", &app->type); 320 321 goto done; 322 } 323 324 module = modules->elts; 325 n = modules->nelts; 326 327 version.start = (u_char *) app->version; 328 version.length = nxt_strlen(app->version); 329 330 for (i = 0; i < n; i++) { 331 if (type == module[i].type 332 && nxt_strstr_eq(&module[i].version, &version)) 333 { 334 nxt_log(task, NXT_LOG_NOTICE, 335 "ignoring %s module with the same " 336 "application language version %V %V as in %V", 337 name, &app->type, &version, &module[i].file); 338 339 goto done; 340 } 341 } 342 343 module = nxt_array_add(modules); 344 if (module == NULL) { 345 goto fail; 346 } 347 348 module->type = type; 349 350 nxt_str_dup(mp, &module->version, &version); 351 if (module->version.start == NULL) { 352 goto fail; 353 } 354 355 module->file.length = nxt_strlen(name); 356 357 module->file.start = nxt_mp_alloc(mp, module->file.length); 358 if (module->file.start == NULL) { 359 goto fail; 360 } 361 362 nxt_memcpy(module->file.start, name, module->file.length); 363 364 module->mounts = nxt_array_create(mp, app->nmounts, 365 sizeof(nxt_fs_mount_t)); 366 367 if (nxt_slow_path(module->mounts == NULL)) { 368 goto fail; 369 } 370 371 mounts = module->mounts; 372 373 for (j = 0; j < app->nmounts; j++) { 374 from = &app->mounts[j]; 375 to = nxt_array_zero_add(mounts); 376 if (nxt_slow_path(to == NULL)) { 377 goto fail; 378 } 379 380 to->src = nxt_cstr_dup(mp, to->src, from->src); 381 if (nxt_slow_path(to->src == NULL)) { 382 goto fail; 383 } 384 385 to->dst = nxt_cstr_dup(mp, to->dst, from->dst); 386 if (nxt_slow_path(to->dst == NULL)) { 387 goto fail; 388 } 389 390 to->name = nxt_cstr_dup(mp, to->name, from->name); 391 if (nxt_slow_path(to->name == NULL)) { 392 goto fail; 393 } 394 395 to->type = from->type; 396 397 if (from->data != NULL) { 398 to->data = nxt_cstr_dup(mp, to->data, from->data); 399 if (nxt_slow_path(to->data == NULL)) { 400 goto fail; 401 } 402 } 403 404 to->flags = from->flags; 405 } 406 407 } else { 408 nxt_alert(task, "dlsym(\"%s\"), failed: \"%s\"", name, dlerror()); 409 } 410 411done: 412 413 ret = NXT_OK; 414 415fail: 416 417 if (dlclose(dl) != 0) { 418 nxt_alert(task, "dlclose(\"%s\"), failed: \"%s\"", name, dlerror()); 419 } 420 421 return ret; 422} 423 424 425static void 426nxt_discovery_completion_handler(nxt_task_t *task, void *obj, void *data) 427{ 428 nxt_mp_t *mp; 429 nxt_buf_t *b; 430 431 b = obj; 432 mp = b->data; 433 434 nxt_mp_destroy(mp); 435} 436 437 438static void 439nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) 440{ 441 nxt_signal_quit_handler(task, msg); 442} 443 444 445static nxt_int_t 446nxt_app_setup(nxt_task_t *task, nxt_process_t *process) 447{ 448 nxt_int_t ret; 449 nxt_process_init_t *init; 450 nxt_app_lang_module_t *lang; 451 nxt_common_app_conf_t *app_conf; 452 453 app_conf = process->data.app; 454 455 lang = nxt_app_lang_module(task->thread->runtime, &app_conf->type); 456 if (nxt_slow_path(lang == NULL)) { 457 nxt_alert(task, "unknown application type: \"%V\"", &app_conf->type); 458 return NXT_ERROR; 459 } 460 461 nxt_app = lang->module; 462 463 if (nxt_app == NULL) { 464 nxt_debug(task, "application language module: %s \"%s\"", 465 lang->version, lang->file); 466 467 nxt_app = nxt_app_module_load(task, lang->file); 468 if (nxt_slow_path(nxt_app == NULL)) { 469 return NXT_ERROR; 470 } 471 } 472 473 if (nxt_slow_path(nxt_app_set_environment(app_conf->environment) 474 != NXT_OK)) 475 { 476 nxt_alert(task, "failed to set environment"); 477 return NXT_ERROR; 478 } 479 480 if (nxt_app->setup != NULL) { 481 ret = nxt_app->setup(task, process, app_conf); 482 483 if (nxt_slow_path(ret != NXT_OK)) { 484 return ret; 485 } 486 } 487 488#if (NXT_HAVE_ISOLATION_ROOTFS) 489 if (process->isolation.rootfs != NULL) { 490 if (process->isolation.mounts != NULL) { 491 ret = nxt_isolation_prepare_rootfs(task, process); 492 if (nxt_slow_path(ret != NXT_OK)) { 493 return ret; 494 } 495 } 496 497 ret = nxt_isolation_change_root(task, process); 498 if (nxt_slow_path(ret != NXT_OK)) { 499 return NXT_ERROR; 500 } 501 } 502#endif 503 504 if (app_conf->working_directory != NULL 505 && app_conf->working_directory[0] != 0) 506 { 507 ret = chdir(app_conf->working_directory); 508 509 if (nxt_slow_path(ret != 0)) { 510 nxt_log(task, NXT_LOG_WARN, "chdir(%s) failed %E", 511 app_conf->working_directory, nxt_errno); 512 513 return NXT_ERROR; 514 } 515 } 516 517 init = nxt_process_init(process); 518 519 init->start = nxt_app->start; 520 521 process->state = NXT_PROCESS_STATE_CREATED; 522 523 return NXT_OK; 524} 525 526 527static nxt_app_module_t * 528nxt_app_module_load(nxt_task_t *task, const char *name) 529{ 530 void *dl; 531 532 dl = dlopen(name, RTLD_GLOBAL | RTLD_LAZY); 533 534 if (dl != NULL) { 535 return dlsym(dl, "nxt_app_module"); 536 } 537 538 nxt_alert(task, "dlopen(\"%s\"), failed: \"%s\"", name, dlerror()); 539 540 return NULL; 541} 542 543 544static nxt_int_t 545nxt_app_set_environment(nxt_conf_value_t *environment) 546{ 547 char *env, *p; 548 uint32_t next; 549 nxt_str_t name, value; 550 nxt_conf_value_t *value_obj; 551 552 if (environment != NULL) { 553 next = 0; 554 555 for ( ;; ) { 556 value_obj = nxt_conf_next_object_member(environment, &name, &next); 557 if (value_obj == NULL) { 558 break; 559 } 560 561 nxt_conf_get_string(value_obj, &value); 562 563 env = nxt_malloc(name.length + value.length + 2); 564 if (nxt_slow_path(env == NULL)) { 565 return NXT_ERROR; 566 } 567 568 p = nxt_cpymem(env, name.start, name.length); 569 *p++ = '='; 570 p = nxt_cpymem(p, value.start, value.length); 571 *p = '\0'; 572 573 if (nxt_slow_path(putenv(env) != 0)) { 574 return NXT_ERROR; 575 } 576 } 577 } 578 579 return NXT_OK; 580} 581 582 583static u_char * 584nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src) 585{ 586 u_char *p; 587 size_t len; 588 589 len = nxt_strlen(src); 590 591 if (dst == NULL) { 592 dst = nxt_mp_alloc(mp, len + 1); 593 if (nxt_slow_path(dst == NULL)) { 594 return NULL; 595 } 596 } 597 598 p = nxt_cpymem(dst, src, len); 599 *p = '\0'; 600 601 return dst; 602} 603 604 605nxt_app_lang_module_t * 606nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name) 607{ 608 u_char *p, *end, *version; 609 size_t version_length; 610 nxt_uint_t i, n; 611 nxt_app_type_t type; 612 nxt_app_lang_module_t *lang; 613 614 end = name->start + name->length; 615 version = end; 616 617 for (p = name->start; p < end; p++) { 618 if (*p == ' ') { 619 version = p + 1; 620 break; 621 } 622 623 if (*p >= '0' && *p <= '9') { 624 version = p; 625 break; 626 } 627 } 628 629 type = nxt_app_parse_type(name->start, p - name->start); 630 631 if (type == NXT_APP_UNKNOWN) { 632 return NULL; 633 } 634 635 version_length = end - version; 636 637 lang = rt->languages->elts; 638 n = rt->languages->nelts; 639 640 for (i = 0; i < n; i++) { 641 642 /* 643 * Versions are sorted in descending order 644 * so first match chooses the highest version. 645 */ 646 647 if (lang[i].type == type 648 && nxt_strvers_match(lang[i].version, version, version_length)) 649 { 650 return &lang[i]; 651 } 652 } 653 654 return NULL; 655} 656 657 658nxt_app_type_t 659nxt_app_parse_type(u_char *p, size_t length) 660{ 661 nxt_str_t str; 662 663 str.length = length; 664 str.start = p; 665 666 if (nxt_str_eq(&str, "external", 8) || nxt_str_eq(&str, "go", 2)) { 667 return NXT_APP_EXTERNAL; 668 669 } else if (nxt_str_eq(&str, "python", 6)) { 670 return NXT_APP_PYTHON; 671 672 } else if (nxt_str_eq(&str, "php", 3)) { 673 return NXT_APP_PHP; 674 675 } else if (nxt_str_eq(&str, "perl", 4)) { 676 return NXT_APP_PERL; 677 678 } else if (nxt_str_eq(&str, "ruby", 4)) { 679 return NXT_APP_RUBY; 680 681 } else if (nxt_str_eq(&str, "java", 4)) { 682 return NXT_APP_JAVA; 683 } 684 685 return NXT_APP_UNKNOWN; 686} 687 688 689nxt_int_t
|