xref: /unit/src/nxt_application.c (revision 1489:4a3ec07f4b19)
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 
18 #include <glob.h>
19 
20 #if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
21 #include <sys/prctl.h>
22 #endif
23 
24 
25 typedef struct {
26     nxt_app_type_t  type;
27     nxt_str_t       version;
28     nxt_str_t       file;
29     nxt_array_t     *mounts;
30 } nxt_module_t;
31 
32 
33 static nxt_int_t nxt_discovery_start(nxt_task_t *task,
34     nxt_process_data_t *data);
35 static nxt_buf_t *nxt_discovery_modules(nxt_task_t *task, const char *path);
36 static nxt_int_t nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp,
37     nxt_array_t *modules, const char *name);
38 static void nxt_discovery_completion_handler(nxt_task_t *task, void *obj,
39     void *data);
40 static void nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg,
41     void *data);
42 static nxt_app_module_t *nxt_app_module_load(nxt_task_t *task,
43     const char *name);
44 static nxt_int_t nxt_app_prefork(nxt_task_t *task, nxt_process_t *process,
45     nxt_mp_t *mp);
46 static nxt_int_t nxt_app_setup(nxt_task_t *task, nxt_process_t *process);
47 static nxt_int_t nxt_app_set_environment(nxt_conf_value_t *environment);
48 static u_char *nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src);
49 
50 #if (NXT_HAVE_ISOLATION_ROOTFS)
51 static nxt_int_t nxt_app_prepare_rootfs(nxt_task_t *task,
52     nxt_process_t *process);
53 static nxt_int_t nxt_app_prepare_lang_mounts(nxt_task_t *task,
54     nxt_process_t *process, nxt_array_t *syspaths);
55 static nxt_int_t nxt_app_set_isolation_rootfs(nxt_task_t *task,
56     nxt_conf_value_t *isolation, nxt_process_t *process);
57 #endif
58 
59 static nxt_int_t nxt_app_set_isolation(nxt_task_t *task,
60     nxt_conf_value_t *isolation, nxt_process_t *process);
61 
62 #if (NXT_HAVE_CLONE)
63 static nxt_int_t nxt_app_set_isolation_namespaces(nxt_task_t *task,
64     nxt_conf_value_t *isolation, nxt_process_t *process);
65 static nxt_int_t nxt_app_clone_flags(nxt_task_t *task,
66     nxt_conf_value_t *namespaces, nxt_clone_t *clone);
67 #endif
68 
69 #if (NXT_HAVE_CLONE_NEWUSER)
70 static nxt_int_t nxt_app_set_isolation_creds(nxt_task_t *task,
71     nxt_conf_value_t *isolation, nxt_process_t *process);
72 static nxt_int_t nxt_app_isolation_credential_map(nxt_task_t *task,
73     nxt_mp_t *mem_pool, nxt_conf_value_t *map_array,
74     nxt_clone_credential_map_t *map);
75 #endif
76 
77 #if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
78 static nxt_int_t nxt_app_set_isolation_new_privs(nxt_task_t *task,
79     nxt_conf_value_t *isolation, nxt_process_t *process);
80 #endif
81 
82 nxt_str_t  nxt_server = nxt_string(NXT_SERVER);
83 
84 
85 static uint32_t  compat[] = {
86     NXT_VERNUM, NXT_DEBUG,
87 };
88 
89 
90 static nxt_app_module_t  *nxt_app;
91 
92 
93 static const nxt_port_handlers_t  nxt_discovery_process_port_handlers = {
94     .quit         = nxt_signal_quit_handler,
95     .new_port     = nxt_port_new_port_handler,
96     .change_file  = nxt_port_change_log_file_handler,
97     .mmap         = nxt_port_mmap_handler,
98     .data         = nxt_port_data_handler,
99     .remove_pid   = nxt_port_remove_pid_handler,
100     .rpc_ready    = nxt_port_rpc_handler,
101     .rpc_error    = nxt_port_rpc_handler,
102 };
103 
104 
105 static const nxt_port_handlers_t  nxt_app_process_port_handlers = {
106     .quit         = nxt_signal_quit_handler,
107     .rpc_ready    = nxt_port_rpc_handler,
108     .rpc_error    = nxt_port_rpc_handler,
109 };
110 
111 
112 const nxt_process_init_t  nxt_discovery_process = {
113     .name           = "discovery",
114     .type           = NXT_PROCESS_DISCOVERY,
115     .prefork        = NULL,
116     .restart        = 0,
117     .setup          = nxt_process_core_setup,
118     .start          = nxt_discovery_start,
119     .port_handlers  = &nxt_discovery_process_port_handlers,
120     .signals        = nxt_process_signals,
121 };
122 
123 
124 const nxt_process_init_t  nxt_app_process = {
125     .type           = NXT_PROCESS_APP,
126     .setup          = nxt_app_setup,
127     .prefork        = nxt_app_prefork,
128     .restart        = 0,
129     .start          = NULL,     /* set to module->start */
130     .port_handlers  = &nxt_app_process_port_handlers,
131     .signals        = nxt_process_signals,
132 };
133 
134 
135 static nxt_int_t
136 nxt_discovery_start(nxt_task_t *task, nxt_process_data_t *data)
137 {
138     uint32_t       stream;
139     nxt_buf_t      *b;
140     nxt_int_t      ret;
141     nxt_port_t     *main_port, *discovery_port;
142     nxt_runtime_t  *rt;
143 
144     nxt_log(task, NXT_LOG_INFO, "discovery started");
145 
146     rt = task->thread->runtime;
147 
148     b = nxt_discovery_modules(task, rt->modules);
149     if (nxt_slow_path(b == NULL)) {
150         return NXT_ERROR;
151     }
152 
153     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
154     discovery_port = rt->port_by_type[NXT_PROCESS_DISCOVERY];
155 
156     stream = nxt_port_rpc_register_handler(task, discovery_port,
157                                            nxt_discovery_quit,
158                                            nxt_discovery_quit,
159                                            main_port->pid, NULL);
160 
161     if (nxt_slow_path(stream == 0)) {
162         return NXT_ERROR;
163     }
164 
165     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_MODULES, -1,
166                                 stream, discovery_port->id, b);
167 
168     if (nxt_slow_path(ret != NXT_OK)) {
169         nxt_port_rpc_cancel(task, discovery_port, stream);
170         return NXT_ERROR;
171     }
172 
173     return NXT_OK;
174 }
175 
176 
177 static nxt_buf_t *
178 nxt_discovery_modules(nxt_task_t *task, const char *path)
179 {
180     char            *name;
181     u_char          *p, *end;
182     size_t          size;
183     glob_t          glb;
184     nxt_mp_t        *mp;
185     nxt_buf_t       *b;
186     nxt_int_t       ret;
187     nxt_uint_t      i, n, j;
188     nxt_array_t     *modules, *mounts;
189     nxt_module_t    *module;
190     nxt_fs_mount_t  *mnt;
191 
192     b = NULL;
193 
194     mp = nxt_mp_create(1024, 128, 256, 32);
195     if (mp == NULL) {
196         return b;
197     }
198 
199     ret = glob(path, 0, NULL, &glb);
200 
201     n = glb.gl_pathc;
202 
203     if (ret != 0) {
204         nxt_log(task, NXT_LOG_NOTICE,
205                 "no modules matching: \"%s\" found", path);
206         n = 0;
207     }
208 
209     modules = nxt_array_create(mp, n, sizeof(nxt_module_t));
210     if (modules == NULL) {
211         goto fail;
212     }
213 
214     for (i = 0; i < n; i++) {
215         name = glb.gl_pathv[i];
216 
217         ret = nxt_discovery_module(task, mp, modules, name);
218         if (ret != NXT_OK) {
219             goto fail;
220         }
221     }
222 
223     size = nxt_length("[]");
224     module = modules->elts;
225     n = modules->nelts;
226 
227     for (i = 0; i < n; i++) {
228         nxt_debug(task, "module: %d %V %V",
229                   module[i].type, &module[i].version, &module[i].file);
230 
231         size += nxt_length("{\"type\": ,");
232         size += nxt_length(" \"version\": \"\",");
233         size += nxt_length(" \"file\": \"\",");
234         size += nxt_length(" \"mounts\": []},");
235 
236         size += NXT_INT_T_LEN
237                 + module[i].version.length
238                 + module[i].file.length;
239 
240         mounts = module[i].mounts;
241 
242         size += mounts->nelts * nxt_length("{\"src\": \"\", \"dst\": \"\", "
243                                             "\"fstype\": \"\", \"flags\": , "
244                                             "\"data\": \"\"},");
245 
246         mnt = mounts->elts;
247 
248         for (j = 0; j < mounts->nelts; j++) {
249             size += nxt_strlen(mnt[j].src) + nxt_strlen(mnt[j].dst)
250                     + nxt_strlen(mnt[j].fstype) + NXT_INT_T_LEN
251                     + (mnt[j].data == NULL ? 0 : nxt_strlen(mnt[j].data));
252         }
253     }
254 
255     b = nxt_buf_mem_alloc(mp, size, 0);
256     if (b == NULL) {
257         goto fail;
258     }
259 
260     b->completion_handler = nxt_discovery_completion_handler;
261 
262     p = b->mem.free;
263     end = b->mem.end;
264     *p++ = '[';
265 
266     for (i = 0; i < n; i++) {
267         mounts = module[i].mounts;
268 
269         p = nxt_sprintf(p, end, "{\"type\": %d, \"version\": \"%V\", "
270                         "\"file\": \"%V\", \"mounts\": [",
271                         module[i].type, &module[i].version, &module[i].file);
272 
273         mnt = mounts->elts;
274         for (j = 0; j < mounts->nelts; j++) {
275             p = nxt_sprintf(p, end,
276                             "{\"src\": \"%s\", \"dst\": \"%s\", "
277                             "\"fstype\": \"%s\", \"flags\": %d, "
278                             "\"data\": \"%s\"},",
279                             mnt[j].src, mnt[j].dst, mnt[j].fstype, mnt[j].flags,
280                             mnt[j].data == NULL ? (u_char *) "" : mnt[j].data);
281         }
282 
283         *p++ = ']';
284         *p++ = '}';
285         *p++ = ',';
286     }
287 
288     *p++ = ']';
289 
290     if (nxt_slow_path(p >= end)) {
291         nxt_alert(task, "discovery write past the buffer");
292         goto fail;
293     }
294 
295     b->mem.free = p;
296 
297 fail:
298 
299     globfree(&glb);
300 
301     return b;
302 }
303 
304 
305 static nxt_int_t
306 nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules,
307     const char *name)
308 {
309     void                  *dl;
310     nxt_str_t             version;
311     nxt_int_t             ret;
312     nxt_uint_t            i, j, n;
313     nxt_array_t           *mounts;
314     nxt_module_t          *module;
315     nxt_app_type_t        type;
316     nxt_fs_mount_t        *to;
317     nxt_app_module_t      *app;
318     const nxt_fs_mount_t  *from;
319 
320     /*
321      * Only memory allocation failure should return NXT_ERROR.
322      * Any module processing errors are ignored.
323      */
324     ret = NXT_ERROR;
325 
326     dl = dlopen(name, RTLD_GLOBAL | RTLD_NOW);
327 
328     if (dl == NULL) {
329         nxt_alert(task, "dlopen(\"%s\"), failed: \"%s\"", name, dlerror());
330         return NXT_OK;
331     }
332 
333     app = dlsym(dl, "nxt_app_module");
334 
335     if (app != NULL) {
336         nxt_log(task, NXT_LOG_NOTICE, "module: %V %s \"%s\"",
337                 &app->type, app->version, name);
338 
339         if (app->compat_length != sizeof(compat)
340             || nxt_memcmp(app->compat, compat, sizeof(compat)) != 0)
341         {
342             nxt_log(task, NXT_LOG_NOTICE, "incompatible module %s", name);
343 
344             goto done;
345         }
346 
347         type = nxt_app_parse_type(app->type.start, app->type.length);
348 
349         if (type == NXT_APP_UNKNOWN) {
350             nxt_log(task, NXT_LOG_NOTICE, "unknown module type %V", &app->type);
351 
352             goto done;
353         }
354 
355         module = modules->elts;
356         n = modules->nelts;
357 
358         version.start = (u_char *) app->version;
359         version.length = nxt_strlen(app->version);
360 
361         for (i = 0; i < n; i++) {
362             if (type == module[i].type
363                 && nxt_strstr_eq(&module[i].version, &version))
364             {
365                 nxt_log(task, NXT_LOG_NOTICE,
366                         "ignoring %s module with the same "
367                         "application language version %V %V as in %V",
368                         name, &app->type, &version, &module[i].file);
369 
370                 goto done;
371             }
372         }
373 
374         module = nxt_array_add(modules);
375         if (module == NULL) {
376             goto fail;
377         }
378 
379         module->type = type;
380 
381         nxt_str_dup(mp, &module->version, &version);
382         if (module->version.start == NULL) {
383             goto fail;
384         }
385 
386         module->file.length = nxt_strlen(name);
387 
388         module->file.start = nxt_mp_alloc(mp, module->file.length);
389         if (module->file.start == NULL) {
390             goto fail;
391         }
392 
393         nxt_memcpy(module->file.start, name, module->file.length);
394 
395         module->mounts = nxt_array_create(mp, app->nmounts,
396                                           sizeof(nxt_fs_mount_t));
397 
398         if (nxt_slow_path(module->mounts == NULL)) {
399             goto fail;
400         }
401 
402         mounts = module->mounts;
403 
404         for (j = 0; j < app->nmounts; j++) {
405             from = &app->mounts[j];
406             to = nxt_array_zero_add(mounts);
407             if (nxt_slow_path(to == NULL)) {
408                 goto fail;
409             }
410 
411             to->src = nxt_cstr_dup(mp, to->src, from->src);
412             if (nxt_slow_path(to->src == NULL)) {
413                 goto fail;
414             }
415 
416             to->dst = nxt_cstr_dup(mp, to->dst, from->dst);
417             if (nxt_slow_path(to->dst == NULL)) {
418                 goto fail;
419             }
420 
421             to->fstype = nxt_cstr_dup(mp, to->fstype, from->fstype);
422             if (nxt_slow_path(to->fstype == NULL)) {
423                 goto fail;
424             }
425 
426             if (from->data != NULL) {
427                 to->data = nxt_cstr_dup(mp, to->data, from->data);
428                 if (nxt_slow_path(to->data == NULL)) {
429                     goto fail;
430                 }
431             }
432 
433             to->flags = from->flags;
434         }
435 
436     } else {
437         nxt_alert(task, "dlsym(\"%s\"), failed: \"%s\"", name, dlerror());
438     }
439 
440 done:
441 
442     ret = NXT_OK;
443 
444 fail:
445 
446     if (dlclose(dl) != 0) {
447         nxt_alert(task, "dlclose(\"%s\"), failed: \"%s\"", name, dlerror());
448     }
449 
450     return ret;
451 }
452 
453 
454 static void
455 nxt_discovery_completion_handler(nxt_task_t *task, void *obj, void *data)
456 {
457     nxt_mp_t   *mp;
458     nxt_buf_t  *b;
459 
460     b = obj;
461     mp = b->data;
462 
463     nxt_mp_destroy(mp);
464 }
465 
466 
467 static void
468 nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data)
469 {
470     nxt_signal_quit_handler(task, msg);
471 }
472 
473 
474 static nxt_int_t
475 nxt_app_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp)
476 {
477     nxt_int_t              cap_setid, cap_chroot;
478     nxt_int_t              ret;
479     nxt_runtime_t          *rt;
480     nxt_common_app_conf_t  *app_conf;
481     nxt_app_lang_module_t  *lang;
482 
483     rt = task->thread->runtime;
484     app_conf = process->data.app;
485     cap_setid = rt->capabilities.setid;
486     cap_chroot = rt->capabilities.chroot;
487 
488     lang = nxt_app_lang_module(rt, &app_conf->type);
489 
490     nxt_assert(lang != NULL);
491 
492     if (app_conf->isolation != NULL) {
493         ret = nxt_app_set_isolation(task, app_conf->isolation, process);
494         if (nxt_slow_path(ret != NXT_OK)) {
495             return ret;
496         }
497     }
498 
499 #if (NXT_HAVE_CLONE_NEWUSER)
500     if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) {
501         cap_setid = 1;
502         cap_chroot = 1;
503     }
504 #endif
505 
506 #if (NXT_HAVE_ISOLATION_ROOTFS)
507     if (process->isolation.rootfs != NULL) {
508         if (!cap_chroot) {
509             nxt_log(task, NXT_LOG_ERR,
510                     "The \"rootfs\" field requires privileges");
511 
512             return NXT_ERROR;
513         }
514 
515         if (lang->mounts != NULL && lang->mounts->nelts > 0) {
516             ret = nxt_app_prepare_lang_mounts(task, process, lang->mounts);
517             if (nxt_slow_path(ret != NXT_OK)) {
518                 return NXT_ERROR;
519             }
520         }
521     }
522 #endif
523 
524     if (cap_setid) {
525         ret = nxt_process_creds_set(task, process, &app_conf->user,
526                                     &app_conf->group);
527 
528         if (nxt_slow_path(ret != NXT_OK)) {
529             return ret;
530         }
531 
532     } else {
533         if (!nxt_str_eq(&app_conf->user, (u_char *) rt->user_cred.user,
534                         nxt_strlen(rt->user_cred.user)))
535         {
536             nxt_alert(task, "cannot set user \"%V\" for app \"%V\": "
537                       "missing capabilities", &app_conf->user, &app_conf->name);
538 
539             return NXT_ERROR;
540         }
541 
542         if (app_conf->group.length > 0
543             && !nxt_str_eq(&app_conf->group, (u_char *) rt->group,
544                            nxt_strlen(rt->group)))
545         {
546             nxt_alert(task, "cannot set group \"%V\" for app \"%V\": "
547                             "missing capabilities", &app_conf->group,
548                             &app_conf->name);
549 
550             return NXT_ERROR;
551         }
552     }
553 
554 #if (NXT_HAVE_CLONE_NEWUSER)
555     ret = nxt_process_vldt_isolation_creds(task, process);
556     if (nxt_slow_path(ret != NXT_OK)) {
557         return ret;
558     }
559 #endif
560 
561     return NXT_OK;
562 }
563 
564 
565 static nxt_int_t
566 nxt_app_setup(nxt_task_t *task, nxt_process_t *process)
567 {
568     nxt_int_t              ret;
569     nxt_process_init_t     *init;
570     nxt_app_lang_module_t  *lang;
571     nxt_common_app_conf_t  *app_conf;
572 
573     app_conf = process->data.app;
574 
575     lang = nxt_app_lang_module(task->thread->runtime, &app_conf->type);
576     if (nxt_slow_path(lang == NULL)) {
577         nxt_alert(task, "unknown application type: \"%V\"", &app_conf->type);
578         return NXT_ERROR;
579     }
580 
581     nxt_app = lang->module;
582 
583     if (nxt_app == NULL) {
584         nxt_debug(task, "application language module: %s \"%s\"",
585                   lang->version, lang->file);
586 
587         nxt_app = nxt_app_module_load(task, lang->file);
588         if (nxt_slow_path(nxt_app == NULL)) {
589             return NXT_ERROR;
590         }
591     }
592 
593     if (nxt_slow_path(nxt_app_set_environment(app_conf->environment)
594                       != NXT_OK))
595     {
596         nxt_alert(task, "failed to set environment");
597         return NXT_ERROR;
598     }
599 
600     if (nxt_app->setup != NULL) {
601         ret = nxt_app->setup(task, process, app_conf);
602 
603         if (nxt_slow_path(ret != NXT_OK)) {
604             return ret;
605         }
606     }
607 
608 #if (NXT_HAVE_ISOLATION_ROOTFS)
609     if (process->isolation.rootfs != NULL) {
610         if (process->isolation.mounts != NULL) {
611             ret = nxt_app_prepare_rootfs(task, process);
612             if (nxt_slow_path(ret != NXT_OK)) {
613                 return ret;
614             }
615         }
616 
617         ret = nxt_process_change_root(task, process);
618         if (nxt_slow_path(ret != NXT_OK)) {
619             return NXT_ERROR;
620         }
621     }
622 #endif
623 
624     if (app_conf->working_directory != NULL
625         && app_conf->working_directory[0] != 0)
626     {
627         ret = chdir(app_conf->working_directory);
628 
629         if (nxt_slow_path(ret != 0)) {
630             nxt_log(task, NXT_LOG_WARN, "chdir(%s) failed %E",
631                     app_conf->working_directory, nxt_errno);
632 
633             return NXT_ERROR;
634         }
635     }
636 
637     init = nxt_process_init(process);
638 
639     init->start = nxt_app->start;
640 
641     process->state = NXT_PROCESS_STATE_CREATED;
642 
643     return NXT_OK;
644 }
645 
646 
647 static nxt_app_module_t *
648 nxt_app_module_load(nxt_task_t *task, const char *name)
649 {
650     void  *dl;
651 
652     dl = dlopen(name, RTLD_GLOBAL | RTLD_LAZY);
653 
654     if (dl != NULL) {
655         return dlsym(dl, "nxt_app_module");
656     }
657 
658     nxt_alert(task, "dlopen(\"%s\"), failed: \"%s\"", name, dlerror());
659 
660     return NULL;
661 }
662 
663 
664 static nxt_int_t
665 nxt_app_set_environment(nxt_conf_value_t *environment)
666 {
667     char              *env, *p;
668     uint32_t          next;
669     nxt_str_t         name, value;
670     nxt_conf_value_t  *value_obj;
671 
672     if (environment != NULL) {
673         next = 0;
674 
675         for ( ;; ) {
676             value_obj = nxt_conf_next_object_member(environment, &name, &next);
677             if (value_obj == NULL) {
678                 break;
679             }
680 
681             nxt_conf_get_string(value_obj, &value);
682 
683             env = nxt_malloc(name.length + value.length + 2);
684             if (nxt_slow_path(env == NULL)) {
685                 return NXT_ERROR;
686             }
687 
688             p = nxt_cpymem(env, name.start, name.length);
689             *p++ = '=';
690             p = nxt_cpymem(p, value.start, value.length);
691             *p = '\0';
692 
693             if (nxt_slow_path(putenv(env) != 0)) {
694                 return NXT_ERROR;
695             }
696         }
697     }
698 
699     return NXT_OK;
700 }
701 
702 
703 static nxt_int_t
704 nxt_app_set_isolation(nxt_task_t *task, nxt_conf_value_t *isolation,
705     nxt_process_t *process)
706 {
707 #if (NXT_HAVE_CLONE)
708     if (nxt_slow_path(nxt_app_set_isolation_namespaces(task, isolation, process)
709                       != NXT_OK))
710     {
711         return NXT_ERROR;
712     }
713 #endif
714 
715 #if (NXT_HAVE_CLONE_NEWUSER)
716     if (nxt_slow_path(nxt_app_set_isolation_creds(task, isolation, process)
717                       != NXT_OK))
718     {
719         return NXT_ERROR;
720     }
721 #endif
722 
723 #if (NXT_HAVE_ISOLATION_ROOTFS)
724     if (nxt_slow_path(nxt_app_set_isolation_rootfs(task, isolation, process)
725                       != NXT_OK))
726     {
727         return NXT_ERROR;
728     }
729 #endif
730 
731 #if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
732     if (nxt_slow_path(nxt_app_set_isolation_new_privs(task, isolation, process)
733                       != NXT_OK))
734     {
735         return NXT_ERROR;
736     }
737 #endif
738 
739     return NXT_OK;
740 }
741 
742 
743 #if (NXT_HAVE_CLONE)
744 
745 static nxt_int_t
746 nxt_app_set_isolation_namespaces(nxt_task_t *task, nxt_conf_value_t *isolation,
747     nxt_process_t *process)
748 {
749     nxt_int_t         ret;
750     nxt_conf_value_t  *obj;
751 
752     static nxt_str_t  nsname = nxt_string("namespaces");
753 
754     obj = nxt_conf_get_object_member(isolation, &nsname, NULL);
755     if (obj != NULL) {
756         ret = nxt_app_clone_flags(task, obj, &process->isolation.clone);
757         if (nxt_slow_path(ret != NXT_OK)) {
758             return NXT_ERROR;
759         }
760     }
761 
762     return NXT_OK;
763 }
764 
765 #endif
766 
767 
768 #if (NXT_HAVE_ISOLATION_ROOTFS)
769 
770 static nxt_int_t
771 nxt_app_set_isolation_rootfs(nxt_task_t *task, nxt_conf_value_t *isolation,
772     nxt_process_t *process)
773 {
774     nxt_str_t         str;
775     nxt_conf_value_t  *obj;
776 
777     static nxt_str_t  rootfs_name = nxt_string("rootfs");
778 
779     obj = nxt_conf_get_object_member(isolation, &rootfs_name, NULL);
780     if (obj != NULL) {
781         nxt_conf_get_string(obj, &str);
782 
783         if (nxt_slow_path(str.length <= 1 || str.start[0] != '/')) {
784             nxt_log(task, NXT_LOG_ERR, "rootfs requires an absolute path other "
785                     "than \"/\" but given \"%V\"", &str);
786 
787             return NXT_ERROR;
788         }
789 
790         if (str.start[str.length - 1] == '/') {
791             str.length--;
792         }
793 
794         process->isolation.rootfs = nxt_mp_alloc(process->mem_pool,
795                                                  str.length + 1);
796 
797         if (nxt_slow_path(process->isolation.rootfs == NULL)) {
798             return NXT_ERROR;
799         }
800 
801         nxt_memcpy(process->isolation.rootfs, str.start, str.length);
802 
803         process->isolation.rootfs[str.length] = '\0';
804     }
805 
806     return NXT_OK;
807 }
808 
809 #endif
810 
811 
812 #if (NXT_HAVE_PR_SET_NO_NEW_PRIVS)
813 
814 static nxt_int_t
815 nxt_app_set_isolation_new_privs(nxt_task_t *task, nxt_conf_value_t *isolation,
816     nxt_process_t *process)
817 {
818     nxt_conf_value_t  *obj;
819 
820     static nxt_str_t  new_privs_name = nxt_string("new_privs");
821 
822     obj = nxt_conf_get_object_member(isolation, &new_privs_name, NULL);
823     if (obj != NULL) {
824         process->isolation.new_privs = nxt_conf_get_boolean(obj);
825     }
826 
827     return NXT_OK;
828 }
829 
830 #endif
831 
832 
833 #if (NXT_HAVE_CLONE_NEWUSER)
834 
835 static nxt_int_t
836 nxt_app_set_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation,
837     nxt_process_t *process)
838 {
839     nxt_int_t         ret;
840     nxt_clone_t       *clone;
841     nxt_conf_value_t  *array;
842 
843     static nxt_str_t uidname = nxt_string("uidmap");
844     static nxt_str_t gidname = nxt_string("gidmap");
845 
846     clone = &process->isolation.clone;
847 
848     array = nxt_conf_get_object_member(isolation, &uidname, NULL);
849     if (array != NULL) {
850         ret = nxt_app_isolation_credential_map(task, process->mem_pool, array,
851                                                 &clone->uidmap);
852 
853         if (nxt_slow_path(ret != NXT_OK)) {
854             return NXT_ERROR;
855         }
856     }
857 
858     array = nxt_conf_get_object_member(isolation, &gidname, NULL);
859     if (array != NULL) {
860         ret = nxt_app_isolation_credential_map(task, process->mem_pool, array,
861                                                 &clone->gidmap);
862 
863         if (nxt_slow_path(ret != NXT_OK)) {
864             return NXT_ERROR;
865         }
866     }
867 
868     return NXT_OK;
869 }
870 
871 
872 static nxt_int_t
873 nxt_app_isolation_credential_map(nxt_task_t *task, nxt_mp_t *mp,
874     nxt_conf_value_t *map_array, nxt_clone_credential_map_t *map)
875 {
876     nxt_int_t         ret;
877     nxt_uint_t        i;
878     nxt_conf_value_t  *obj;
879 
880     static nxt_conf_map_t  nxt_clone_map_entry_conf[] = {
881         {
882             nxt_string("container"),
883             NXT_CONF_MAP_INT,
884             offsetof(nxt_clone_map_entry_t, container),
885         },
886 
887         {
888             nxt_string("host"),
889             NXT_CONF_MAP_INT,
890             offsetof(nxt_clone_map_entry_t, host),
891         },
892 
893         {
894             nxt_string("size"),
895             NXT_CONF_MAP_INT,
896             offsetof(nxt_clone_map_entry_t, size),
897         },
898     };
899 
900     map->size = nxt_conf_array_elements_count(map_array);
901 
902     if (map->size == 0) {
903         return NXT_OK;
904     }
905 
906     map->map = nxt_mp_alloc(mp, map->size * sizeof(nxt_clone_map_entry_t));
907     if (nxt_slow_path(map->map == NULL)) {
908         return NXT_ERROR;
909     }
910 
911     for (i = 0; i < map->size; i++) {
912         obj = nxt_conf_get_array_element(map_array, i);
913 
914         ret = nxt_conf_map_object(mp, obj, nxt_clone_map_entry_conf,
915                                   nxt_nitems(nxt_clone_map_entry_conf),
916                                   map->map + i);
917         if (nxt_slow_path(ret != NXT_OK)) {
918             nxt_alert(task, "clone map entry map error");
919             return NXT_ERROR;
920         }
921     }
922 
923     return NXT_OK;
924 }
925 
926 #endif
927 
928 #if (NXT_HAVE_CLONE)
929 
930 static nxt_int_t
931 nxt_app_clone_flags(nxt_task_t *task, nxt_conf_value_t *namespaces,
932     nxt_clone_t *clone)
933 {
934     uint32_t          index;
935     nxt_str_t         name;
936     nxt_int_t         flag;
937     nxt_conf_value_t  *value;
938 
939     index = 0;
940 
941     for ( ;; ) {
942         value = nxt_conf_next_object_member(namespaces, &name, &index);
943 
944         if (value == NULL) {
945             break;
946         }
947 
948         flag = 0;
949 
950 #if (NXT_HAVE_CLONE_NEWUSER)
951         if (nxt_str_eq(&name, "credential", 10)) {
952             flag = CLONE_NEWUSER;
953         }
954 #endif
955 
956 #if (NXT_HAVE_CLONE_NEWPID)
957         if (nxt_str_eq(&name, "pid", 3)) {
958             flag = CLONE_NEWPID;
959         }
960 #endif
961 
962 #if (NXT_HAVE_CLONE_NEWNET)
963         if (nxt_str_eq(&name, "network", 7)) {
964             flag = CLONE_NEWNET;
965         }
966 #endif
967 
968 #if (NXT_HAVE_CLONE_NEWUTS)
969         if (nxt_str_eq(&name, "uname", 5)) {
970             flag = CLONE_NEWUTS;
971         }
972 #endif
973 
974 #if (NXT_HAVE_CLONE_NEWNS)
975         if (nxt_str_eq(&name, "mount", 5)) {
976             flag = CLONE_NEWNS;
977         }
978 #endif
979 
980 #if (NXT_HAVE_CLONE_NEWCGROUP)
981         if (nxt_str_eq(&name, "cgroup", 6)) {
982             flag = CLONE_NEWCGROUP;
983         }
984 #endif
985 
986         if (!flag) {
987             nxt_alert(task, "unknown namespace flag: \"%V\"", &name);
988             return NXT_ERROR;
989         }
990 
991         if (nxt_conf_get_boolean(value)) {
992             clone->flags |= flag;
993         }
994     }
995 
996     return NXT_OK;
997 }
998 
999 #endif
1000 
1001 
1002 #if (NXT_HAVE_ISOLATION_ROOTFS)
1003 
1004 static nxt_int_t
1005 nxt_app_prepare_lang_mounts(nxt_task_t *task, nxt_process_t *process,
1006     nxt_array_t *lang_mounts)
1007 {
1008     u_char          *p;
1009     size_t          i, n, rootfs_len, len;
1010     nxt_mp_t        *mp;
1011     nxt_array_t     *mounts;
1012     const u_char    *rootfs;
1013     nxt_fs_mount_t  *mnt, *lang_mnt;
1014 
1015     rootfs = process->isolation.rootfs;
1016     rootfs_len = nxt_strlen(rootfs);
1017     mp = process->mem_pool;
1018 
1019     /* copy to init mem pool */
1020     mounts = nxt_array_copy(mp, NULL, lang_mounts);
1021     if (mounts == NULL) {
1022         return NXT_ERROR;
1023     }
1024 
1025     n = mounts->nelts;
1026     mnt = mounts->elts;
1027     lang_mnt = lang_mounts->elts;
1028 
1029     for (i = 0; i < n; i++) {
1030         len = nxt_strlen(lang_mnt[i].dst);
1031 
1032         mnt[i].dst = nxt_mp_alloc(mp, rootfs_len + len + 1);
1033         if (mnt[i].dst == NULL) {
1034             return NXT_ERROR;
1035         }
1036 
1037         p = nxt_cpymem(mnt[i].dst, rootfs, rootfs_len);
1038         p = nxt_cpymem(p, lang_mnt[i].dst, len);
1039         *p = '\0';
1040     }
1041 
1042     process->isolation.mounts = mounts;
1043 
1044     return NXT_OK;
1045 }
1046 
1047 
1048 
1049 static nxt_int_t
1050 nxt_app_prepare_rootfs(nxt_task_t *task, nxt_process_t *process)
1051 {
1052     size_t          i, n;
1053     nxt_int_t       ret, hasproc;
1054     struct stat     st;
1055     nxt_array_t     *mounts;
1056     const u_char    *dst;
1057     nxt_fs_mount_t  *mnt;
1058 
1059     hasproc = 0;
1060 
1061 #if (NXT_HAVE_CLONE_NEWPID) && (NXT_HAVE_CLONE_NEWNS)
1062     nxt_fs_mount_t  mount;
1063 
1064     if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID)
1065         && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS))
1066     {
1067         /*
1068          * This mount point will automatically be gone when the namespace is
1069          * destroyed.
1070          */
1071 
1072         mount.fstype = (u_char *) "proc";
1073         mount.src = (u_char *) "proc";
1074         mount.dst = (u_char *) "/proc";
1075         mount.data = (u_char *) "";
1076         mount.flags = 0;
1077 
1078         ret = nxt_fs_mkdir_all(mount.dst, S_IRWXU | S_IRWXG | S_IRWXO);
1079         if (nxt_fast_path(ret == NXT_OK)) {
1080             ret = nxt_fs_mount(task, &mount);
1081             if (nxt_fast_path(ret == NXT_OK)) {
1082                 hasproc = 1;
1083             }
1084 
1085         } else {
1086             nxt_log(task, NXT_LOG_WARN, "mkdir(%s) %E", mount.dst, nxt_errno);
1087         }
1088     }
1089 #endif
1090 
1091     mounts = process->isolation.mounts;
1092 
1093     n = mounts->nelts;
1094     mnt = mounts->elts;
1095 
1096     for (i = 0; i < n; i++) {
1097         dst = mnt[i].dst;
1098 
1099         if (nxt_slow_path(nxt_memcmp(mnt[i].fstype, "bind", 4) == 0
1100                           && stat((const char *) mnt[i].src, &st) != 0))
1101         {
1102             nxt_log(task, NXT_LOG_WARN, "host path not found: %s", mnt[i].src);
1103             continue;
1104         }
1105 
1106         if (hasproc && nxt_memcmp(mnt[i].fstype, "proc", 4) == 0
1107             && nxt_memcmp(mnt[i].dst, "/proc", 5) == 0)
1108         {
1109             continue;
1110         }
1111 
1112         ret = nxt_fs_mkdir_all(dst, S_IRWXU | S_IRWXG | S_IRWXO);
1113         if (nxt_slow_path(ret != NXT_OK)) {
1114             nxt_alert(task, "mkdir(%s) %E", dst, nxt_errno);
1115             goto undo;
1116         }
1117 
1118         ret = nxt_fs_mount(task, &mnt[i]);
1119         if (nxt_slow_path(ret != NXT_OK)) {
1120             goto undo;
1121         }
1122     }
1123 
1124     return NXT_OK;
1125 
1126 undo:
1127 
1128     n = i + 1;
1129 
1130     for (i = 0; i < n; i++) {
1131         nxt_fs_unmount(mnt[i].dst);
1132     }
1133 
1134     return NXT_ERROR;
1135 }
1136 
1137 #endif
1138 
1139 
1140 static u_char *
1141 nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src)
1142 {
1143     u_char  *p;
1144     size_t  len;
1145 
1146     len = nxt_strlen(src);
1147 
1148     if (dst == NULL) {
1149         dst = nxt_mp_alloc(mp, len + 1);
1150         if (nxt_slow_path(dst == NULL)) {
1151             return NULL;
1152         }
1153     }
1154 
1155     p = nxt_cpymem(dst, src, len);
1156     *p = '\0';
1157 
1158     return dst;
1159 }
1160 
1161 
1162 nxt_app_lang_module_t *
1163 nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name)
1164 {
1165     u_char                 *p, *end, *version;
1166     size_t                 version_length;
1167     nxt_uint_t             i, n;
1168     nxt_app_type_t         type;
1169     nxt_app_lang_module_t  *lang;
1170 
1171     end = name->start + name->length;
1172     version = end;
1173 
1174     for (p = name->start; p < end; p++) {
1175         if (*p == ' ') {
1176             version = p + 1;
1177             break;
1178         }
1179 
1180         if (*p >= '0' && *p <= '9') {
1181             version = p;
1182             break;
1183         }
1184     }
1185 
1186     type = nxt_app_parse_type(name->start, p - name->start);
1187 
1188     if (type == NXT_APP_UNKNOWN) {
1189         return NULL;
1190     }
1191 
1192     version_length = end - version;
1193 
1194     lang = rt->languages->elts;
1195     n = rt->languages->nelts;
1196 
1197     for (i = 0; i < n; i++) {
1198 
1199         /*
1200          * Versions are sorted in descending order
1201          * so first match chooses the highest version.
1202          */
1203 
1204         if (lang[i].type == type
1205             && nxt_strvers_match(lang[i].version, version, version_length))
1206         {
1207             return &lang[i];
1208         }
1209     }
1210 
1211     return NULL;
1212 }
1213 
1214 
1215 nxt_app_type_t
1216 nxt_app_parse_type(u_char *p, size_t length)
1217 {
1218     nxt_str_t str;
1219 
1220     str.length = length;
1221     str.start = p;
1222 
1223     if (nxt_str_eq(&str, "external", 8) || nxt_str_eq(&str, "go", 2)) {
1224         return NXT_APP_EXTERNAL;
1225 
1226     } else if (nxt_str_eq(&str, "python", 6)) {
1227         return NXT_APP_PYTHON;
1228 
1229     } else if (nxt_str_eq(&str, "php", 3)) {
1230         return NXT_APP_PHP;
1231 
1232     } else if (nxt_str_eq(&str, "perl", 4)) {
1233         return NXT_APP_PERL;
1234 
1235     } else if (nxt_str_eq(&str, "ruby", 4)) {
1236         return NXT_APP_RUBY;
1237 
1238     } else if (nxt_str_eq(&str, "java", 4)) {
1239         return NXT_APP_JAVA;
1240     }
1241 
1242     return NXT_APP_UNKNOWN;
1243 }
1244 
1245 
1246 nxt_int_t
1247 nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init)
1248 {
1249     nxt_port_t     *my_port, *main_port;
1250     nxt_runtime_t  *rt;
1251 
1252     nxt_memzero(init, sizeof(nxt_unit_init_t));
1253 
1254     rt = task->thread->runtime;
1255 
1256     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
1257     if (nxt_slow_path(main_port == NULL)) {
1258         return NXT_ERROR;
1259     }
1260 
1261     my_port = nxt_runtime_port_find(rt, nxt_pid, 0);
1262     if (nxt_slow_path(my_port == NULL)) {
1263         return NXT_ERROR;
1264     }
1265 
1266     init->ready_port.id.pid = main_port->pid;
1267     init->ready_port.id.id = main_port->id;
1268     init->ready_port.out_fd = main_port->pair[1];
1269 
1270     nxt_fd_blocking(task, main_port->pair[1]);
1271 
1272     init->ready_stream = my_port->process->stream;
1273 
1274     init->read_port.id.pid = my_port->pid;
1275     init->read_port.id.id = my_port->id;
1276     init->read_port.in_fd = my_port->pair[0];
1277 
1278     nxt_fd_blocking(task, my_port->pair[0]);
1279 
1280     init->log_fd = 2;
1281 
1282     return NXT_OK;
1283 }
1284