xref: /unit/src/nxt_application.c (revision 1673:883f2f79c2f6)
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 
26 typedef 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 
34 static nxt_int_t nxt_discovery_start(nxt_task_t *task,
35     nxt_process_data_t *data);
36 static nxt_buf_t *nxt_discovery_modules(nxt_task_t *task, const char *path);
37 static nxt_int_t nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp,
38     nxt_array_t *modules, const char *name);
39 static void nxt_discovery_completion_handler(nxt_task_t *task, void *obj,
40     void *data);
41 static void nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg,
42     void *data);
43 static nxt_app_module_t *nxt_app_module_load(nxt_task_t *task,
44     const char *name);
45 static nxt_int_t nxt_app_setup(nxt_task_t *task, nxt_process_t *process);
46 static nxt_int_t nxt_app_set_environment(nxt_conf_value_t *environment);
47 static u_char *nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src);
48 
49 
50 nxt_str_t  nxt_server = nxt_string(NXT_SERVER);
51 
52 
53 static uint32_t  compat[] = {
54     NXT_VERNUM, NXT_DEBUG,
55 };
56 
57 
58 static nxt_app_module_t  *nxt_app;
59 
60 
61 static 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 
73 static 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 
80 const 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 
92 const 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 
103 static nxt_int_t
104 nxt_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 
145 static nxt_buf_t *
146 nxt_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 
266 fail:
267 
268     globfree(&glb);
269 
270     return b;
271 }
272 
273 
274 static nxt_int_t
275 nxt_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 
411 done:
412 
413     ret = NXT_OK;
414 
415 fail:
416 
417     if (dlclose(dl) != 0) {
418         nxt_alert(task, "dlclose(\"%s\"), failed: \"%s\"", name, dlerror());
419     }
420 
421     return ret;
422 }
423 
424 
425 static void
426 nxt_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 
438 static void
439 nxt_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 
445 static nxt_int_t
446 nxt_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 
527 static nxt_app_module_t *
528 nxt_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 
544 static nxt_int_t
545 nxt_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 
583 static u_char *
584 nxt_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 
605 nxt_app_lang_module_t *
606 nxt_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 
658 nxt_app_type_t
659 nxt_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 
689 nxt_int_t
690 nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init)
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 
733     return NXT_OK;
734 }
735