nxt_application.c (1673:883f2f79c2f6) nxt_application.c (1980:43553aa72111)
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
690nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init)
690nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init,
691 nxt_common_app_conf_t *conf)
691{
692 nxt_port_t *my_port, *main_port, *router_port;
693 nxt_runtime_t *rt;
694
695 nxt_memzero(init, sizeof(nxt_unit_init_t));
696
697 rt = task->thread->runtime;
698
699 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
700 if (nxt_slow_path(main_port == NULL)) {
701 return NXT_ERROR;
702 }
703
704 router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
705 if (nxt_slow_path(router_port == NULL)) {
706 return NXT_ERROR;
707 }
708
709 my_port = nxt_runtime_port_find(rt, nxt_pid, 0);
710 if (nxt_slow_path(my_port == NULL)) {
711 return NXT_ERROR;
712 }
713
714 init->ready_port.id.pid = main_port->pid;
715 init->ready_port.id.id = main_port->id;
716 init->ready_port.in_fd = -1;
717 init->ready_port.out_fd = main_port->pair[1];
718
719 init->ready_stream = my_port->process->stream;
720
721 init->router_port.id.pid = router_port->pid;
722 init->router_port.id.id = router_port->id;
723 init->router_port.in_fd = -1;
724 init->router_port.out_fd = router_port->pair[1];
725
726 init->read_port.id.pid = my_port->pid;
727 init->read_port.id.id = my_port->id;
728 init->read_port.in_fd = my_port->pair[0];
729 init->read_port.out_fd = my_port->pair[1];
730
731 init->log_fd = 2;
732
692{
693 nxt_port_t *my_port, *main_port, *router_port;
694 nxt_runtime_t *rt;
695
696 nxt_memzero(init, sizeof(nxt_unit_init_t));
697
698 rt = task->thread->runtime;
699
700 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
701 if (nxt_slow_path(main_port == NULL)) {
702 return NXT_ERROR;
703 }
704
705 router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
706 if (nxt_slow_path(router_port == NULL)) {
707 return NXT_ERROR;
708 }
709
710 my_port = nxt_runtime_port_find(rt, nxt_pid, 0);
711 if (nxt_slow_path(my_port == NULL)) {
712 return NXT_ERROR;
713 }
714
715 init->ready_port.id.pid = main_port->pid;
716 init->ready_port.id.id = main_port->id;
717 init->ready_port.in_fd = -1;
718 init->ready_port.out_fd = main_port->pair[1];
719
720 init->ready_stream = my_port->process->stream;
721
722 init->router_port.id.pid = router_port->pid;
723 init->router_port.id.id = router_port->id;
724 init->router_port.in_fd = -1;
725 init->router_port.out_fd = router_port->pair[1];
726
727 init->read_port.id.pid = my_port->pid;
728 init->read_port.id.id = my_port->id;
729 init->read_port.in_fd = my_port->pair[0];
730 init->read_port.out_fd = my_port->pair[1];
731
732 init->log_fd = 2;
733
734 init->shm_limit = conf->shm_limit;
735 init->request_limit = conf->request_limit;
736
733 return NXT_OK;
734}
737 return NXT_OK;
738}