Coverage Report

Created: 2025-10-10 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/unit/src/nxt_main_process.c
Line
Count
Source
1
2
/*
3
 * Copyright (C) Igor Sysoev
4
 * Copyright (C) NGINX, Inc.
5
 */
6
7
#include <nxt_main.h>
8
#include <nxt_runtime.h>
9
#include <nxt_port.h>
10
#include <nxt_main_process.h>
11
#include <nxt_conf.h>
12
#include <nxt_router.h>
13
#include <nxt_port_queue.h>
14
#if (NXT_TLS)
15
#include <nxt_cert.h>
16
#endif
17
#if (NXT_HAVE_NJS)
18
#include <nxt_script.h>
19
#endif
20
21
#include <sys/mount.h>
22
23
24
typedef struct {
25
    nxt_socket_t        socket;
26
    nxt_socket_error_t  error;
27
    u_char              *start;
28
    u_char              *end;
29
} nxt_listening_socket_t;
30
31
32
typedef struct {
33
    nxt_uint_t          size;
34
    nxt_conf_map_t      *map;
35
} nxt_conf_app_map_t;
36
37
38
static nxt_int_t nxt_main_process_port_create(nxt_task_t *task,
39
    nxt_runtime_t *rt);
40
static void nxt_main_process_title(nxt_task_t *task);
41
static void nxt_main_process_sigterm_handler(nxt_task_t *task, void *obj,
42
    void *data);
43
static void nxt_main_process_sigquit_handler(nxt_task_t *task, void *obj,
44
    void *data);
45
static void nxt_main_process_sigusr1_handler(nxt_task_t *task, void *obj,
46
    void *data);
47
static void nxt_main_process_sigchld_handler(nxt_task_t *task, void *obj,
48
    void *data);
49
static void nxt_main_process_signal_handler(nxt_task_t *task, void *obj,
50
    void *data);
51
static void nxt_main_process_cleanup(nxt_task_t *task, nxt_process_t *process);
52
static void nxt_main_port_socket_handler(nxt_task_t *task,
53
    nxt_port_recv_msg_t *msg);
54
static void nxt_main_port_socket_unlink_handler(nxt_task_t *task,
55
    nxt_port_recv_msg_t *msg);
56
static nxt_int_t nxt_main_listening_socket(nxt_sockaddr_t *sa,
57
    nxt_listening_socket_t *ls);
58
static void nxt_main_port_modules_handler(nxt_task_t *task,
59
    nxt_port_recv_msg_t *msg);
60
static int nxt_cdecl nxt_app_lang_compare(const void *v1, const void *v2);
61
static void nxt_main_process_whoami_handler(nxt_task_t *task,
62
    nxt_port_recv_msg_t *msg);
63
static void nxt_main_port_conf_store_handler(nxt_task_t *task,
64
    nxt_port_recv_msg_t *msg);
65
static nxt_int_t nxt_main_file_store(nxt_task_t *task, const char *tmp_name,
66
    const char *name, u_char *buf, size_t size);
67
static void nxt_main_port_access_log_handler(nxt_task_t *task,
68
    nxt_port_recv_msg_t *msg);
69
70
const nxt_sig_event_t  nxt_main_process_signals[] = {
71
    nxt_event_signal(SIGHUP,  nxt_main_process_signal_handler),
72
    nxt_event_signal(SIGINT,  nxt_main_process_sigterm_handler),
73
    nxt_event_signal(SIGQUIT, nxt_main_process_sigquit_handler),
74
    nxt_event_signal(SIGTERM, nxt_main_process_sigterm_handler),
75
    nxt_event_signal(SIGCHLD, nxt_main_process_sigchld_handler),
76
    nxt_event_signal(SIGUSR1, nxt_main_process_sigusr1_handler),
77
    nxt_event_signal_end,
78
};
79
80
81
nxt_uint_t  nxt_conf_ver;
82
83
static nxt_bool_t  nxt_exiting;
84
85
86
nxt_int_t
87
nxt_main_process_start(nxt_thread_t *thr, nxt_task_t *task,
88
    nxt_runtime_t *rt)
89
0
{
90
0
    rt->type = NXT_PROCESS_MAIN;
91
92
0
    if (nxt_main_process_port_create(task, rt) != NXT_OK) {
93
0
        return NXT_ERROR;
94
0
    }
95
96
0
    nxt_main_process_title(task);
97
98
    /*
99
     * The discovery process will send a message processed by
100
     * nxt_main_port_modules_handler() which starts the controller
101
     * and router processes.
102
     */
103
0
    return nxt_process_init_start(task, nxt_discovery_process);
104
0
}
105
106
107
static nxt_conf_map_t  nxt_common_app_conf[] = {
108
    {
109
        nxt_string("type"),
110
        NXT_CONF_MAP_STR,
111
        offsetof(nxt_common_app_conf_t, type),
112
    },
113
114
    {
115
        nxt_string("user"),
116
        NXT_CONF_MAP_STR,
117
        offsetof(nxt_common_app_conf_t, user),
118
    },
119
120
    {
121
        nxt_string("group"),
122
        NXT_CONF_MAP_STR,
123
        offsetof(nxt_common_app_conf_t, group),
124
    },
125
126
    {
127
        nxt_string("stdout"),
128
        NXT_CONF_MAP_CSTRZ,
129
        offsetof(nxt_common_app_conf_t, stdout_log),
130
    },
131
132
    {
133
        nxt_string("stderr"),
134
        NXT_CONF_MAP_CSTRZ,
135
        offsetof(nxt_common_app_conf_t, stderr_log),
136
    },
137
138
    {
139
        nxt_string("working_directory"),
140
        NXT_CONF_MAP_CSTRZ,
141
        offsetof(nxt_common_app_conf_t, working_directory),
142
    },
143
144
    {
145
        nxt_string("environment"),
146
        NXT_CONF_MAP_PTR,
147
        offsetof(nxt_common_app_conf_t, environment),
148
    },
149
150
    {
151
        nxt_string("isolation"),
152
        NXT_CONF_MAP_PTR,
153
        offsetof(nxt_common_app_conf_t, isolation),
154
    },
155
156
    {
157
        nxt_string("limits"),
158
        NXT_CONF_MAP_PTR,
159
        offsetof(nxt_common_app_conf_t, limits),
160
    },
161
162
};
163
164
165
static nxt_conf_map_t  nxt_common_app_limits_conf[] = {
166
    {
167
        nxt_string("shm"),
168
        NXT_CONF_MAP_SIZE,
169
        offsetof(nxt_common_app_conf_t, shm_limit),
170
    },
171
172
    {
173
        nxt_string("requests"),
174
        NXT_CONF_MAP_INT32,
175
        offsetof(nxt_common_app_conf_t, request_limit),
176
    },
177
178
};
179
180
181
static nxt_conf_map_t  nxt_external_app_conf[] = {
182
    {
183
        nxt_string("executable"),
184
        NXT_CONF_MAP_CSTRZ,
185
        offsetof(nxt_common_app_conf_t, u.external.executable),
186
    },
187
188
    {
189
        nxt_string("arguments"),
190
        NXT_CONF_MAP_PTR,
191
        offsetof(nxt_common_app_conf_t, u.external.arguments),
192
    },
193
194
};
195
196
197
static nxt_conf_map_t  nxt_python_app_conf[] = {
198
    {
199
        nxt_string("home"),
200
        NXT_CONF_MAP_CSTRZ,
201
        offsetof(nxt_common_app_conf_t, u.python.home),
202
    },
203
204
    {
205
        nxt_string("path"),
206
        NXT_CONF_MAP_PTR,
207
        offsetof(nxt_common_app_conf_t, u.python.path),
208
    },
209
210
    {
211
        nxt_string("protocol"),
212
        NXT_CONF_MAP_STR,
213
        offsetof(nxt_common_app_conf_t, u.python.protocol),
214
    },
215
216
    {
217
        nxt_string("threads"),
218
        NXT_CONF_MAP_INT32,
219
        offsetof(nxt_common_app_conf_t, u.python.threads),
220
    },
221
222
    {
223
        nxt_string("targets"),
224
        NXT_CONF_MAP_PTR,
225
        offsetof(nxt_common_app_conf_t, u.python.targets),
226
    },
227
228
    {
229
        nxt_string("thread_stack_size"),
230
        NXT_CONF_MAP_INT32,
231
        offsetof(nxt_common_app_conf_t, u.python.thread_stack_size),
232
    },
233
};
234
235
236
static nxt_conf_map_t  nxt_php_app_conf[] = {
237
    {
238
        nxt_string("targets"),
239
        NXT_CONF_MAP_PTR,
240
        offsetof(nxt_common_app_conf_t, u.php.targets),
241
    },
242
243
    {
244
        nxt_string("options"),
245
        NXT_CONF_MAP_PTR,
246
        offsetof(nxt_common_app_conf_t, u.php.options),
247
    },
248
};
249
250
251
static nxt_conf_map_t  nxt_perl_app_conf[] = {
252
    {
253
        nxt_string("script"),
254
        NXT_CONF_MAP_CSTRZ,
255
        offsetof(nxt_common_app_conf_t, u.perl.script),
256
    },
257
258
    {
259
        nxt_string("threads"),
260
        NXT_CONF_MAP_INT32,
261
        offsetof(nxt_common_app_conf_t, u.perl.threads),
262
    },
263
264
    {
265
        nxt_string("thread_stack_size"),
266
        NXT_CONF_MAP_INT32,
267
        offsetof(nxt_common_app_conf_t, u.perl.thread_stack_size),
268
    },
269
};
270
271
272
static nxt_conf_map_t  nxt_ruby_app_conf[] = {
273
    {
274
        nxt_string("script"),
275
        NXT_CONF_MAP_STR,
276
        offsetof(nxt_common_app_conf_t, u.ruby.script),
277
    },
278
    {
279
        nxt_string("threads"),
280
        NXT_CONF_MAP_INT32,
281
        offsetof(nxt_common_app_conf_t, u.ruby.threads),
282
    },
283
    {
284
        nxt_string("hooks"),
285
        NXT_CONF_MAP_STR,
286
        offsetof(nxt_common_app_conf_t, u.ruby.hooks),
287
    }
288
};
289
290
291
static nxt_conf_map_t  nxt_java_app_conf[] = {
292
    {
293
        nxt_string("classpath"),
294
        NXT_CONF_MAP_PTR,
295
        offsetof(nxt_common_app_conf_t, u.java.classpath),
296
    },
297
    {
298
        nxt_string("webapp"),
299
        NXT_CONF_MAP_CSTRZ,
300
        offsetof(nxt_common_app_conf_t, u.java.webapp),
301
    },
302
    {
303
        nxt_string("options"),
304
        NXT_CONF_MAP_PTR,
305
        offsetof(nxt_common_app_conf_t, u.java.options),
306
    },
307
    {
308
        nxt_string("unit_jars"),
309
        NXT_CONF_MAP_CSTRZ,
310
        offsetof(nxt_common_app_conf_t, u.java.unit_jars),
311
    },
312
    {
313
        nxt_string("threads"),
314
        NXT_CONF_MAP_INT32,
315
        offsetof(nxt_common_app_conf_t, u.java.threads),
316
    },
317
    {
318
        nxt_string("thread_stack_size"),
319
        NXT_CONF_MAP_INT32,
320
        offsetof(nxt_common_app_conf_t, u.java.thread_stack_size),
321
    },
322
323
};
324
325
326
static nxt_conf_map_t  nxt_wasm_app_conf[] = {
327
    {
328
        nxt_string("module"),
329
        NXT_CONF_MAP_CSTRZ,
330
        offsetof(nxt_common_app_conf_t, u.wasm.module),
331
    },
332
    {
333
        nxt_string("request_handler"),
334
        NXT_CONF_MAP_CSTRZ,
335
        offsetof(nxt_common_app_conf_t, u.wasm.request_handler),
336
    },
337
    {
338
        nxt_string("malloc_handler"),
339
        NXT_CONF_MAP_CSTRZ,
340
        offsetof(nxt_common_app_conf_t, u.wasm.malloc_handler),
341
    },
342
    {
343
        nxt_string("free_handler"),
344
        NXT_CONF_MAP_CSTRZ,
345
        offsetof(nxt_common_app_conf_t, u.wasm.free_handler),
346
    },
347
    {
348
        nxt_string("module_init_handler"),
349
        NXT_CONF_MAP_CSTRZ,
350
        offsetof(nxt_common_app_conf_t, u.wasm.module_init_handler),
351
    },
352
    {
353
        nxt_string("module_end_handler"),
354
        NXT_CONF_MAP_CSTRZ,
355
        offsetof(nxt_common_app_conf_t, u.wasm.module_end_handler),
356
    },
357
    {
358
        nxt_string("request_init_handler"),
359
        NXT_CONF_MAP_CSTRZ,
360
        offsetof(nxt_common_app_conf_t, u.wasm.request_init_handler),
361
    },
362
    {
363
        nxt_string("request_end_handler"),
364
        NXT_CONF_MAP_CSTRZ,
365
        offsetof(nxt_common_app_conf_t, u.wasm.request_end_handler),
366
    },
367
    {
368
        nxt_string("response_end_handler"),
369
        NXT_CONF_MAP_CSTRZ,
370
        offsetof(nxt_common_app_conf_t, u.wasm.response_end_handler),
371
    },
372
    {
373
        nxt_string("access"),
374
        NXT_CONF_MAP_PTR,
375
        offsetof(nxt_common_app_conf_t, u.wasm.access),
376
    },
377
};
378
379
380
static nxt_conf_map_t  nxt_wasm_wc_app_conf[] = {
381
    {
382
        nxt_string("component"),
383
        NXT_CONF_MAP_CSTRZ,
384
        offsetof(nxt_common_app_conf_t, u.wasm_wc.component),
385
    },
386
    {
387
        nxt_string("access"),
388
        NXT_CONF_MAP_PTR,
389
        offsetof(nxt_common_app_conf_t, u.wasm_wc.access),
390
    },
391
};
392
393
394
static nxt_conf_app_map_t  nxt_app_maps[] = {
395
    { nxt_nitems(nxt_external_app_conf),  nxt_external_app_conf },
396
    { nxt_nitems(nxt_python_app_conf),    nxt_python_app_conf },
397
    { nxt_nitems(nxt_php_app_conf),       nxt_php_app_conf },
398
    { nxt_nitems(nxt_perl_app_conf),      nxt_perl_app_conf },
399
    { nxt_nitems(nxt_ruby_app_conf),      nxt_ruby_app_conf },
400
    { nxt_nitems(nxt_java_app_conf),      nxt_java_app_conf },
401
    { nxt_nitems(nxt_wasm_app_conf),      nxt_wasm_app_conf },
402
    { nxt_nitems(nxt_wasm_wc_app_conf),   nxt_wasm_wc_app_conf },
403
};
404
405
406
static void
407
nxt_main_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
408
0
{
409
0
    nxt_debug(task, "main data: %*s",
410
0
              nxt_buf_mem_used_size(&msg->buf->mem), msg->buf->mem.pos);
411
0
}
412
413
414
static void
415
nxt_main_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
416
0
{
417
0
    void        *mem;
418
0
    nxt_port_t  *port;
419
420
0
    nxt_port_new_port_handler(task, msg);
421
422
0
    port = msg->u.new_port;
423
424
0
    if (port != NULL
425
0
        && port->type == NXT_PROCESS_APP
426
0
        && msg->fd[1] != -1)
427
0
    {
428
0
        mem = nxt_mem_mmap(NULL, sizeof(nxt_port_queue_t),
429
0
                           PROT_READ | PROT_WRITE, MAP_SHARED, msg->fd[1], 0);
430
0
        if (nxt_fast_path(mem != MAP_FAILED)) {
431
0
            port->queue = mem;
432
0
        }
433
434
0
        nxt_fd_close(msg->fd[1]);
435
0
        msg->fd[1] = -1;
436
0
    }
437
0
}
438
439
440
static void
441
nxt_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
442
0
{
443
0
    u_char                 *start, *p, ch;
444
0
    size_t                 type_len;
445
0
    nxt_int_t              ret;
446
0
    nxt_buf_t              *b;
447
0
    nxt_port_t             *port;
448
0
    nxt_runtime_t          *rt;
449
0
    nxt_process_t          *process;
450
0
    nxt_app_type_t         idx;
451
0
    nxt_conf_value_t       *conf;
452
0
    nxt_process_init_t     *init;
453
0
    nxt_common_app_conf_t  *app_conf;
454
455
0
    rt = task->thread->runtime;
456
457
0
    port = rt->port_by_type[NXT_PROCESS_ROUTER];
458
0
    if (nxt_slow_path(port == NULL)) {
459
0
        nxt_alert(task, "router port not found");
460
0
        goto close_fds;
461
0
    }
462
463
0
    if (nxt_slow_path(port->pid != nxt_recv_msg_cmsg_pid(msg))) {
464
0
        nxt_alert(task, "process %PI cannot start processes",
465
0
                  nxt_recv_msg_cmsg_pid(msg));
466
467
0
        goto close_fds;
468
0
    }
469
470
0
    process = nxt_process_new(rt);
471
0
    if (nxt_slow_path(process == NULL)) {
472
0
        goto close_fds;
473
0
    }
474
475
0
    process->mem_pool = nxt_mp_create(1024, 128, 256, 32);
476
0
    if (process->mem_pool == NULL) {
477
0
        nxt_process_use(task, process, -1);
478
0
        goto close_fds;
479
0
    }
480
481
0
    process->parent_port = rt->port_by_type[NXT_PROCESS_MAIN];
482
483
0
    init = nxt_process_init(process);
484
485
0
    *init = nxt_proto_process;
486
487
0
    b = nxt_buf_chk_make_plain(process->mem_pool, msg->buf, msg->size);
488
0
    if (b == NULL) {
489
0
        goto failed;
490
0
    }
491
492
0
    nxt_debug(task, "main start prototype: %*s", b->mem.free - b->mem.pos,
493
0
              b->mem.pos);
494
495
0
    app_conf = nxt_mp_zalloc(process->mem_pool, sizeof(nxt_common_app_conf_t));
496
0
    if (nxt_slow_path(app_conf == NULL)) {
497
0
        goto failed;
498
0
    }
499
500
0
    app_conf->shared_port_fd = msg->fd[0];
501
0
    app_conf->shared_queue_fd = msg->fd[1];
502
503
0
    start = b->mem.pos;
504
505
0
    app_conf->name.start = start;
506
0
    app_conf->name.length = nxt_strlen(start);
507
508
0
    init->name = (const char *) start;
509
510
0
    process->name = nxt_mp_alloc(process->mem_pool, app_conf->name.length
511
0
                                 + sizeof("\"\" prototype") + 1);
512
513
0
    if (nxt_slow_path(process->name == NULL)) {
514
0
        goto failed;
515
0
    }
516
517
0
    p = (u_char *) process->name;
518
0
    *p++ = '"';
519
0
    p = nxt_cpymem(p, init->name, app_conf->name.length);
520
0
    p = nxt_cpymem(p, "\" prototype", 11);
521
0
    *p = '\0';
522
523
0
    app_conf->shm_limit = 100 * 1024 * 1024;
524
0
    app_conf->request_limit = 0;
525
526
0
    start += app_conf->name.length + 1;
527
528
0
    conf = nxt_conf_json_parse(process->mem_pool, start, b->mem.free, NULL);
529
0
    if (conf == NULL) {
530
0
        nxt_alert(task, "router app configuration parsing error");
531
532
0
        goto failed;
533
0
    }
534
535
0
    rt = task->thread->runtime;
536
537
0
    app_conf->user.start  = (u_char*)rt->user_cred.user;
538
0
    app_conf->user.length = nxt_strlen(rt->user_cred.user);
539
540
0
    ret = nxt_conf_map_object(process->mem_pool, conf, nxt_common_app_conf,
541
0
                              nxt_nitems(nxt_common_app_conf), app_conf);
542
543
0
    if (ret != NXT_OK) {
544
0
        nxt_alert(task, "failed to map common app conf received from router");
545
0
        goto failed;
546
0
    }
547
548
0
    for (type_len = 0; type_len != app_conf->type.length; type_len++) {
549
0
        ch = app_conf->type.start[type_len];
550
551
0
        if (ch == ' ' || nxt_isdigit(ch)) {
552
0
            break;
553
0
        }
554
0
    }
555
556
0
    idx = nxt_app_parse_type(app_conf->type.start, type_len);
557
558
0
    if (nxt_slow_path(idx >= nxt_nitems(nxt_app_maps))) {
559
0
        nxt_alert(task, "invalid app type %d received from router", (int) idx);
560
0
        goto failed;
561
0
    }
562
563
0
    ret = nxt_conf_map_object(process->mem_pool, conf, nxt_app_maps[idx].map,
564
0
                              nxt_app_maps[idx].size, app_conf);
565
566
0
    if (nxt_slow_path(ret != NXT_OK)) {
567
0
        nxt_alert(task, "failed to map app conf received from router");
568
0
        goto failed;
569
0
    }
570
571
0
    if (app_conf->limits != NULL) {
572
0
        ret = nxt_conf_map_object(process->mem_pool, app_conf->limits,
573
0
                                  nxt_common_app_limits_conf,
574
0
                                  nxt_nitems(nxt_common_app_limits_conf),
575
0
                                  app_conf);
576
577
0
        if (nxt_slow_path(ret != NXT_OK)) {
578
0
            nxt_alert(task, "failed to map app limits received from router");
579
0
            goto failed;
580
0
        }
581
0
    }
582
583
0
    app_conf->self = conf;
584
585
0
    process->stream = msg->port_msg.stream;
586
0
    process->data.app = app_conf;
587
588
0
    ret = nxt_process_start(task, process);
589
0
    if (nxt_fast_path(ret == NXT_OK || ret == NXT_AGAIN)) {
590
591
        /* Close shared port fds only in main process. */
592
0
        if (ret == NXT_OK) {
593
0
            nxt_fd_close(app_conf->shared_port_fd);
594
0
            nxt_fd_close(app_conf->shared_queue_fd);
595
0
        }
596
597
        /* Avoid fds close in caller. */
598
0
        msg->fd[0] = -1;
599
0
        msg->fd[1] = -1;
600
601
0
        return;
602
0
    }
603
604
0
failed:
605
606
0
    nxt_process_use(task, process, -1);
607
608
0
    port = nxt_runtime_port_find(rt, msg->port_msg.pid,
609
0
                                 msg->port_msg.reply_port);
610
611
0
    if (nxt_fast_path(port != NULL)) {
612
0
        nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR,
613
0
                              -1, msg->port_msg.stream, 0, NULL);
614
0
    }
615
616
0
close_fds:
617
618
0
    nxt_fd_close(msg->fd[0]);
619
0
    msg->fd[0] = -1;
620
621
0
    nxt_fd_close(msg->fd[1]);
622
0
    msg->fd[1] = -1;
623
0
}
624
625
626
static void
627
nxt_main_process_created_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
628
0
{
629
0
    nxt_port_t     *port;
630
0
    nxt_process_t  *process;
631
0
    nxt_runtime_t  *rt;
632
633
0
    rt = task->thread->runtime;
634
635
0
    port = nxt_runtime_port_find(rt, msg->port_msg.pid,
636
0
                                 msg->port_msg.reply_port);
637
0
    if (nxt_slow_path(port == NULL)) {
638
0
        return;
639
0
    }
640
641
0
    process = port->process;
642
643
0
    nxt_assert(process != NULL);
644
0
    nxt_assert(process->state == NXT_PROCESS_STATE_CREATING);
645
646
0
#if (NXT_HAVE_LINUX_NS && NXT_HAVE_CLONE_NEWUSER)
647
0
    if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) {
648
0
        if (nxt_slow_path(nxt_clone_credential_map(task, process->pid,
649
0
                                                   process->user_cred,
650
0
                                                   &process->isolation.clone)
651
0
                          != NXT_OK))
652
0
        {
653
0
            (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR,
654
0
                                         -1, msg->port_msg.stream, 0, NULL);
655
0
            return;
656
0
        }
657
0
    }
658
659
0
#endif
660
661
0
    process->state = NXT_PROCESS_STATE_CREATED;
662
663
0
    (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_READY_LAST,
664
0
                                 -1, msg->port_msg.stream, 0, NULL);
665
0
}
666
667
668
static nxt_port_handlers_t  nxt_main_process_port_handlers = {
669
    .data             = nxt_main_data_handler,
670
    .new_port         = nxt_main_new_port_handler,
671
    .process_created  = nxt_main_process_created_handler,
672
    .process_ready    = nxt_port_process_ready_handler,
673
    .whoami           = nxt_main_process_whoami_handler,
674
    .remove_pid       = nxt_port_remove_pid_handler,
675
    .start_process    = nxt_main_start_process_handler,
676
    .socket           = nxt_main_port_socket_handler,
677
    .socket_unlink    = nxt_main_port_socket_unlink_handler,
678
    .modules          = nxt_main_port_modules_handler,
679
    .conf_store       = nxt_main_port_conf_store_handler,
680
#if (NXT_TLS)
681
    .cert_get         = nxt_cert_store_get_handler,
682
    .cert_delete      = nxt_cert_store_delete_handler,
683
#endif
684
#if (NXT_HAVE_NJS)
685
    .script_get       = nxt_script_store_get_handler,
686
    .script_delete    = nxt_script_store_delete_handler,
687
#endif
688
    .access_log       = nxt_main_port_access_log_handler,
689
    .rpc_ready        = nxt_port_rpc_handler,
690
    .rpc_error        = nxt_port_rpc_handler,
691
};
692
693
694
static void
695
nxt_main_process_whoami_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
696
0
{
697
0
    nxt_buf_t      *buf;
698
0
    nxt_pid_t      pid, ppid;
699
0
    nxt_port_t     *port;
700
0
    nxt_runtime_t  *rt;
701
0
    nxt_process_t  *pprocess;
702
703
0
    nxt_assert(msg->port_msg.reply_port == 0);
704
705
0
    if (nxt_slow_path(msg->buf == NULL
706
0
        || nxt_buf_used_size(msg->buf) != sizeof(nxt_pid_t)))
707
0
    {
708
0
        nxt_alert(task, "whoami: buffer is NULL or unexpected size");
709
0
        goto fail;
710
0
    }
711
712
0
    nxt_memcpy(&ppid, msg->buf->mem.pos, sizeof(nxt_pid_t));
713
714
0
    rt = task->thread->runtime;
715
716
0
    pprocess = nxt_runtime_process_find(rt, ppid);
717
0
    if (nxt_slow_path(pprocess == NULL)) {
718
0
        nxt_alert(task, "whoami: parent process %PI not found", ppid);
719
0
        goto fail;
720
0
    }
721
722
0
    pid = nxt_recv_msg_cmsg_pid(msg);
723
724
0
    nxt_debug(task, "whoami: from %PI, parent %PI, fd %d", pid, ppid,
725
0
              msg->fd[0]);
726
727
0
    if (msg->fd[0] != -1) {
728
0
        port = nxt_runtime_process_port_create(task, rt, pid, 0,
729
0
                                               NXT_PROCESS_APP);
730
0
        if (nxt_slow_path(port == NULL)) {
731
0
            goto fail;
732
0
        }
733
734
0
        nxt_fd_nonblocking(task, msg->fd[0]);
735
736
0
        port->pair[0] = -1;
737
0
        port->pair[1] = msg->fd[0];
738
0
        msg->fd[0] = -1;
739
740
0
        port->max_size = 16 * 1024;
741
0
        port->max_share = 64 * 1024;
742
0
        port->socket.task = task;
743
744
0
        nxt_port_write_enable(task, port);
745
746
0
    } else {
747
0
        port = nxt_runtime_port_find(rt, pid, 0);
748
0
        if (nxt_slow_path(port == NULL)) {
749
0
            goto fail;
750
0
        }
751
0
    }
752
753
0
    if (ppid != nxt_pid) {
754
0
        nxt_queue_insert_tail(&pprocess->children, &port->process->link);
755
0
    }
756
757
0
    buf = nxt_buf_mem_alloc(task->thread->engine->mem_pool,
758
0
                            sizeof(nxt_pid_t), 0);
759
0
    if (nxt_slow_path(buf == NULL)) {
760
0
        goto fail;
761
0
    }
762
763
0
    buf->mem.free = nxt_cpymem(buf->mem.free, &pid, sizeof(nxt_pid_t));
764
765
0
    (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_READY_LAST, -1,
766
0
                                 msg->port_msg.stream, 0, buf);
767
768
0
fail:
769
770
0
    if (msg->fd[0] != -1) {
771
0
        nxt_fd_close(msg->fd[0]);
772
0
    }
773
0
}
774
775
776
static nxt_int_t
777
nxt_main_process_port_create(nxt_task_t *task, nxt_runtime_t *rt)
778
0
{
779
0
    nxt_int_t      ret;
780
0
    nxt_port_t     *port;
781
0
    nxt_process_t  *process;
782
783
0
    port = nxt_runtime_process_port_create(task, rt, nxt_pid, 0,
784
0
                                           NXT_PROCESS_MAIN);
785
0
    if (nxt_slow_path(port == NULL)) {
786
0
        return NXT_ERROR;
787
0
    }
788
789
0
    process = port->process;
790
791
0
    ret = nxt_port_socket_init(task, port, 0);
792
0
    if (nxt_slow_path(ret != NXT_OK)) {
793
0
        nxt_port_use(task, port, -1);
794
0
        return ret;
795
0
    }
796
797
    /*
798
     * A main process port.  A write port is not closed
799
     * since it should be inherited by processes.
800
     */
801
0
    nxt_port_enable(task, port, &nxt_main_process_port_handlers);
802
803
0
    process->state = NXT_PROCESS_STATE_READY;
804
805
0
    return NXT_OK;
806
0
}
807
808
809
static void
810
nxt_main_process_title(nxt_task_t *task)
811
0
{
812
0
    u_char      *p, *end;
813
0
    nxt_uint_t  i;
814
0
    u_char      title[2048];
815
816
0
    end = title + sizeof(title) - 1;
817
818
0
    p = nxt_sprintf(title, end, "unit: main v" NXT_VERSION " [%s",
819
0
                    nxt_process_argv[0]);
820
821
0
    for (i = 1; nxt_process_argv[i] != NULL; i++) {
822
0
        p = nxt_sprintf(p, end, " %s", nxt_process_argv[i]);
823
0
    }
824
825
0
    if (p < end) {
826
0
        *p++ = ']';
827
0
    }
828
829
0
    *p = '\0';
830
831
0
    nxt_process_title(task, "%s", title);
832
0
}
833
834
835
static void
836
nxt_main_process_sigterm_handler(nxt_task_t *task, void *obj, void *data)
837
0
{
838
0
    nxt_debug(task, "sigterm handler signo:%d (%s)",
839
0
              (int) (uintptr_t) obj, data);
840
841
    /* TODO: fast exit. */
842
843
0
    nxt_exiting = 1;
844
845
0
    nxt_runtime_quit(task, 0);
846
0
}
847
848
849
static void
850
nxt_main_process_sigquit_handler(nxt_task_t *task, void *obj, void *data)
851
0
{
852
0
    nxt_debug(task, "sigquit handler signo:%d (%s)",
853
0
              (int) (uintptr_t) obj, data);
854
855
    /* TODO: graceful exit. */
856
857
0
    nxt_exiting = 1;
858
859
0
    nxt_runtime_quit(task, 0);
860
0
}
861
862
863
static void
864
nxt_main_process_sigusr1_handler(nxt_task_t *task, void *obj, void *data)
865
0
{
866
0
    nxt_mp_t        *mp;
867
0
    nxt_int_t       ret;
868
0
    nxt_uint_t      n;
869
0
    nxt_port_t      *port;
870
0
    nxt_file_t      *file, *new_file;
871
0
    nxt_array_t     *new_files;
872
0
    nxt_runtime_t   *rt;
873
874
0
    nxt_log(task, NXT_LOG_NOTICE, "signal %d (%s) received, %s",
875
0
            (int) (uintptr_t) obj, data, "log files rotation");
876
877
0
    rt = task->thread->runtime;
878
879
0
    port = rt->port_by_type[NXT_PROCESS_ROUTER];
880
881
0
    if (nxt_fast_path(port != NULL)) {
882
0
        (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_ACCESS_LOG,
883
0
                                     -1, 0, 0, NULL);
884
0
    }
885
886
0
    mp = nxt_mp_create(1024, 128, 256, 32);
887
0
    if (mp == NULL) {
888
0
        return;
889
0
    }
890
891
0
    n = nxt_list_nelts(rt->log_files);
892
893
0
    new_files = nxt_array_create(mp, n, sizeof(nxt_file_t));
894
0
    if (new_files == NULL) {
895
0
        nxt_mp_destroy(mp);
896
0
        return;
897
0
    }
898
899
0
    nxt_list_each(file, rt->log_files) {
900
901
        /* This allocation cannot fail. */
902
0
        new_file = nxt_array_add(new_files);
903
904
0
        new_file->name = file->name;
905
0
        new_file->fd = NXT_FILE_INVALID;
906
0
        new_file->log_level = NXT_LOG_ALERT;
907
908
0
        ret = nxt_file_open(task, new_file, O_WRONLY | O_APPEND, O_CREAT,
909
0
                            NXT_FILE_OWNER_ACCESS);
910
911
0
        if (ret != NXT_OK) {
912
0
            goto fail;
913
0
        }
914
915
0
    } nxt_list_loop;
916
917
0
    new_file = new_files->elts;
918
919
0
    ret = nxt_file_stderr(&new_file[0]);
920
921
0
    if (ret == NXT_OK) {
922
0
        n = 0;
923
924
0
        nxt_list_each(file, rt->log_files) {
925
926
0
            nxt_port_change_log_file(task, rt, n, new_file[n].fd);
927
            /*
928
             * The old log file descriptor must be closed at the moment
929
             * when no other threads use it.  dup2() allows to use the
930
             * old file descriptor for new log file.  This change is
931
             * performed atomically in the kernel.
932
             */
933
0
            (void) nxt_file_redirect(file, new_file[n].fd);
934
935
0
            n++;
936
937
0
        } nxt_list_loop;
938
939
0
        nxt_mp_destroy(mp);
940
0
        return;
941
0
    }
942
943
0
fail:
944
945
0
    new_file = new_files->elts;
946
0
    n = new_files->nelts;
947
948
0
    while (n != 0) {
949
0
        if (new_file->fd != NXT_FILE_INVALID) {
950
0
            nxt_file_close(task, new_file);
951
0
        }
952
953
0
        new_file++;
954
0
        n--;
955
0
    }
956
957
0
    nxt_mp_destroy(mp);
958
0
}
959
960
961
static void
962
nxt_main_process_sigchld_handler(nxt_task_t *task, void *obj, void *data)
963
0
{
964
0
    int                 status;
965
0
    nxt_int_t           ret;
966
0
    nxt_err_t           err;
967
0
    nxt_pid_t           pid;
968
0
    nxt_port_t          *port;
969
0
    nxt_queue_t         children;
970
0
    nxt_runtime_t       *rt;
971
0
    nxt_process_t       *process, *child;
972
0
    nxt_process_init_t  init;
973
974
0
    nxt_debug(task, "sigchld handler signo:%d (%s)",
975
0
              (int) (uintptr_t) obj, data);
976
977
0
    rt = task->thread->runtime;
978
979
0
    for ( ;; ) {
980
0
        pid = waitpid(-1, &status, WNOHANG);
981
982
0
        if (pid == -1) {
983
984
0
            switch (err = nxt_errno) {
985
986
0
            case NXT_ECHILD:
987
0
                return;
988
989
0
            case NXT_EINTR:
990
0
                continue;
991
992
0
            default:
993
0
                nxt_alert(task, "waitpid() failed: %E", err);
994
0
                return;
995
0
            }
996
0
        }
997
998
0
        nxt_debug(task, "waitpid(): %PI", pid);
999
1000
0
        if (pid == 0) {
1001
0
            return;
1002
0
        }
1003
1004
0
        if (WTERMSIG(status)) {
1005
0
#ifdef WCOREDUMP
1006
0
            nxt_alert(task, "process %PI exited on signal %d%s",
1007
0
                      pid, WTERMSIG(status),
1008
0
                      WCOREDUMP(status) ? " (core dumped)" : "");
1009
#else
1010
            nxt_alert(task, "process %PI exited on signal %d",
1011
                      pid, WTERMSIG(status));
1012
#endif
1013
1014
0
        } else {
1015
0
            nxt_trace(task, "process %PI exited with code %d",
1016
0
                      pid, WEXITSTATUS(status));
1017
0
        }
1018
1019
0
        process = nxt_runtime_process_find(rt, pid);
1020
1021
0
        if (process != NULL) {
1022
0
            nxt_main_process_cleanup(task, process);
1023
1024
0
            if (process->state == NXT_PROCESS_STATE_READY) {
1025
0
                process->stream = 0;
1026
0
            }
1027
1028
0
            nxt_queue_init(&children);
1029
1030
0
            if (!nxt_queue_is_empty(&process->children)) {
1031
0
                nxt_queue_add(&children, &process->children);
1032
1033
0
                nxt_queue_init(&process->children);
1034
1035
0
                nxt_queue_each(child, &children, nxt_process_t, link) {
1036
0
                    port = nxt_process_port_first(child);
1037
1038
0
                    (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT,
1039
0
                                                 -1, 0, 0, NULL);
1040
0
                } nxt_queue_loop;
1041
0
            }
1042
1043
0
            if (nxt_exiting) {
1044
0
                nxt_process_close_ports(task, process);
1045
1046
0
                nxt_queue_each(child, &children, nxt_process_t, link) {
1047
0
                    nxt_queue_remove(&child->link);
1048
0
                    child->link.next = NULL;
1049
1050
0
                    nxt_process_close_ports(task, child);
1051
0
                } nxt_queue_loop;
1052
1053
0
                if (rt->nprocesses <= 1) {
1054
0
                    nxt_runtime_quit(task, 0);
1055
1056
0
                    return;
1057
0
                }
1058
1059
0
                continue;
1060
0
            }
1061
1062
0
            nxt_port_remove_notify_others(task, process);
1063
1064
0
            nxt_queue_each(child, &children, nxt_process_t, link) {
1065
0
                nxt_port_remove_notify_others(task, child);
1066
1067
0
                nxt_queue_remove(&child->link);
1068
0
                child->link.next = NULL;
1069
1070
0
                nxt_process_close_ports(task, child);
1071
0
            } nxt_queue_loop;
1072
1073
0
            init = *(nxt_process_init_t *) nxt_process_init(process);
1074
1075
0
            nxt_process_close_ports(task, process);
1076
1077
0
            if (init.restart) {
1078
0
                ret = nxt_process_init_start(task, init);
1079
0
                if (nxt_slow_path(ret == NXT_ERROR)) {
1080
0
                    nxt_alert(task, "failed to restart %s", init.name);
1081
0
                }
1082
0
            }
1083
0
        }
1084
0
    }
1085
0
}
1086
1087
1088
static void
1089
nxt_main_process_signal_handler(nxt_task_t *task, void *obj, void *data)
1090
0
{
1091
0
    nxt_trace(task, "signal signo:%d (%s) received, ignored",
1092
0
              (int) (uintptr_t) obj, data);
1093
0
}
1094
1095
1096
static void
1097
nxt_main_process_cleanup(nxt_task_t *task, nxt_process_t *process)
1098
0
{
1099
0
    if (process->isolation.cleanup != NULL) {
1100
0
        process->isolation.cleanup(task, process);
1101
0
    }
1102
1103
0
    if (process->isolation.cgroup_cleanup != NULL) {
1104
0
        process->isolation.cgroup_cleanup(task, process);
1105
0
    }
1106
0
}
1107
1108
1109
static void
1110
nxt_main_port_socket_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
1111
0
{
1112
0
    size_t                  size;
1113
0
    nxt_int_t               ret;
1114
0
    nxt_buf_t               *b, *out;
1115
0
    nxt_port_t              *port;
1116
0
    nxt_sockaddr_t          *sa;
1117
0
    nxt_port_msg_type_t     type;
1118
0
    nxt_listening_socket_t  ls;
1119
0
    u_char                  message[2048];
1120
1121
0
    port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
1122
0
                                 msg->port_msg.reply_port);
1123
0
    if (nxt_slow_path(port == NULL)) {
1124
0
        return;
1125
0
    }
1126
1127
0
    if (nxt_slow_path(port->type != NXT_PROCESS_ROUTER)) {
1128
0
        nxt_alert(task, "process %PI cannot create listener sockets",
1129
0
                  msg->port_msg.pid);
1130
1131
0
        return;
1132
0
    }
1133
1134
0
    b = msg->buf;
1135
0
    sa = (nxt_sockaddr_t *) b->mem.pos;
1136
1137
    /* TODO check b size and make plain */
1138
1139
0
    ls.socket = -1;
1140
0
    ls.error = NXT_SOCKET_ERROR_SYSTEM;
1141
0
    ls.start = message;
1142
0
    ls.end = message + sizeof(message);
1143
1144
0
    nxt_debug(task, "listening socket \"%*s\"",
1145
0
              (size_t) sa->length, nxt_sockaddr_start(sa));
1146
1147
0
    ret = nxt_main_listening_socket(sa, &ls);
1148
1149
0
    if (ret == NXT_OK) {
1150
0
        nxt_debug(task, "socket(\"%*s\"): %d",
1151
0
                  (size_t) sa->length, nxt_sockaddr_start(sa), ls.socket);
1152
1153
0
        out = NULL;
1154
1155
0
        type = NXT_PORT_MSG_RPC_READY_LAST | NXT_PORT_MSG_CLOSE_FD;
1156
1157
0
    } else {
1158
0
        size = ls.end - ls.start;
1159
1160
0
        nxt_alert(task, "%*s", size, ls.start);
1161
1162
0
        out = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool,
1163
0
                                   size + 1);
1164
0
        if (nxt_fast_path(out != NULL)) {
1165
0
            *out->mem.free++ = (uint8_t) ls.error;
1166
1167
0
            out->mem.free = nxt_cpymem(out->mem.free, ls.start, size);
1168
0
        }
1169
1170
0
        type = NXT_PORT_MSG_RPC_ERROR;
1171
0
    }
1172
1173
0
    nxt_port_socket_write(task, port, type, ls.socket, msg->port_msg.stream,
1174
0
                          0, out);
1175
0
}
1176
1177
1178
static nxt_int_t
1179
nxt_main_listening_socket(nxt_sockaddr_t *sa, nxt_listening_socket_t *ls)
1180
0
{
1181
0
    nxt_err_t         err;
1182
0
    nxt_socket_t      s;
1183
1184
0
    const socklen_t   length = sizeof(int);
1185
0
    static const int  enable = 1;
1186
1187
0
    s = socket(sa->u.sockaddr.sa_family, sa->type, 0);
1188
1189
0
    if (nxt_slow_path(s == -1)) {
1190
0
        err = nxt_errno;
1191
1192
0
#if (NXT_INET6)
1193
1194
0
        if (err == EAFNOSUPPORT && sa->u.sockaddr.sa_family == AF_INET6) {
1195
0
            ls->error = NXT_SOCKET_ERROR_NOINET6;
1196
0
        }
1197
1198
0
#endif
1199
1200
0
        ls->end = nxt_sprintf(ls->start, ls->end,
1201
0
                              "socket(\\\"%*s\\\") failed %E",
1202
0
                              (size_t) sa->length, nxt_sockaddr_start(sa), err);
1203
1204
0
        return NXT_ERROR;
1205
0
    }
1206
1207
0
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &enable, length) != 0) {
1208
0
        ls->end = nxt_sprintf(ls->start, ls->end,
1209
0
                              "setsockopt(\\\"%*s\\\", SO_REUSEADDR) failed %E",
1210
0
                              (size_t) sa->length, nxt_sockaddr_start(sa),
1211
0
                              nxt_errno);
1212
0
        goto fail;
1213
0
    }
1214
1215
0
#if (NXT_INET6)
1216
1217
0
    if (sa->u.sockaddr.sa_family == AF_INET6) {
1218
1219
0
        if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &enable, length) != 0) {
1220
0
            ls->end = nxt_sprintf(ls->start, ls->end,
1221
0
                               "setsockopt(\\\"%*s\\\", IPV6_V6ONLY) failed %E",
1222
0
                               (size_t) sa->length, nxt_sockaddr_start(sa),
1223
0
                               nxt_errno);
1224
0
            goto fail;
1225
0
        }
1226
0
    }
1227
1228
0
#endif
1229
1230
0
    if (bind(s, &sa->u.sockaddr, sa->socklen) != 0) {
1231
0
        err = nxt_errno;
1232
1233
0
#if (NXT_HAVE_UNIX_DOMAIN)
1234
1235
0
        if (sa->u.sockaddr.sa_family == AF_UNIX) {
1236
0
            switch (err) {
1237
1238
0
            case EACCES:
1239
0
                ls->error = NXT_SOCKET_ERROR_ACCESS;
1240
0
                break;
1241
1242
0
            case ENOENT:
1243
0
            case ENOTDIR:
1244
0
                ls->error = NXT_SOCKET_ERROR_PATH;
1245
0
                break;
1246
0
            }
1247
1248
0
        } else
1249
0
#endif
1250
0
        {
1251
0
            switch (err) {
1252
1253
0
            case EACCES:
1254
0
                ls->error = NXT_SOCKET_ERROR_PORT;
1255
0
                break;
1256
1257
0
            case EADDRINUSE:
1258
0
                ls->error = NXT_SOCKET_ERROR_INUSE;
1259
0
                break;
1260
1261
0
            case EADDRNOTAVAIL:
1262
0
                ls->error = NXT_SOCKET_ERROR_NOADDR;
1263
0
                break;
1264
0
            }
1265
0
        }
1266
1267
0
        ls->end = nxt_sprintf(ls->start, ls->end, "bind(\\\"%*s\\\") failed %E",
1268
0
                              (size_t) sa->length, nxt_sockaddr_start(sa), err);
1269
0
        goto fail;
1270
0
    }
1271
1272
0
#if (NXT_HAVE_UNIX_DOMAIN)
1273
1274
0
    if (sa->u.sockaddr.sa_family == AF_UNIX
1275
0
        && sa->u.sockaddr_un.sun_path[0] != '\0')
1276
0
    {
1277
0
        char          *filename;
1278
0
        nxt_thread_t  *thr;
1279
1280
0
        filename = sa->u.sockaddr_un.sun_path;
1281
1282
0
        if (chmod(filename, 0666) != 0) {
1283
0
            ls->end = nxt_sprintf(ls->start, ls->end,
1284
0
                                  "chmod(\\\"%s\\\") failed %E",
1285
0
                                  filename, nxt_errno);
1286
0
            goto fail;
1287
0
        }
1288
1289
0
        thr = nxt_thread();
1290
0
        nxt_runtime_listen_socket_add(thr->runtime, sa);
1291
0
    }
1292
1293
0
#endif
1294
1295
0
    ls->socket = s;
1296
1297
0
    return NXT_OK;
1298
1299
0
fail:
1300
1301
0
    (void) close(s);
1302
1303
0
    return NXT_ERROR;
1304
0
}
1305
1306
1307
static void
1308
nxt_main_port_socket_unlink_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
1309
0
{
1310
0
#if (NXT_HAVE_UNIX_DOMAIN)
1311
0
    size_t               i;
1312
0
    nxt_buf_t            *b;
1313
0
    const char           *filename;
1314
0
    nxt_runtime_t        *rt;
1315
0
    nxt_sockaddr_t       *sa;
1316
0
    nxt_listen_socket_t  *ls;
1317
1318
0
    b = msg->buf;
1319
0
    sa = (nxt_sockaddr_t *) b->mem.pos;
1320
1321
0
    filename = sa->u.sockaddr_un.sun_path;
1322
0
    unlink(filename);
1323
1324
0
    rt = task->thread->runtime;
1325
1326
0
    for (i = 0; i < rt->listen_sockets->nelts; i++) {
1327
0
        const char  *name;
1328
1329
0
        ls = (nxt_listen_socket_t *) rt->listen_sockets->elts + i;
1330
0
        sa = ls->sockaddr;
1331
1332
0
        if (sa->u.sockaddr.sa_family != AF_UNIX
1333
0
            || sa->u.sockaddr_un.sun_path[0] == '\0')
1334
0
        {
1335
0
            continue;
1336
0
        }
1337
1338
0
        name = sa->u.sockaddr_un.sun_path;
1339
0
        if (strcmp(name, filename) != 0) {
1340
0
            continue;
1341
0
        }
1342
1343
0
        nxt_array_remove(rt->listen_sockets, ls);
1344
0
        break;
1345
0
    }
1346
0
#endif
1347
0
}
1348
1349
1350
static nxt_conf_map_t  nxt_app_lang_module_map[] = {
1351
    {
1352
        nxt_string("type"),
1353
        NXT_CONF_MAP_INT,
1354
        offsetof(nxt_app_lang_module_t, type),
1355
    },
1356
1357
    {
1358
        nxt_string("name"),
1359
        NXT_CONF_MAP_CSTRZ,
1360
        offsetof(nxt_app_lang_module_t, name),
1361
    },
1362
1363
    {
1364
        nxt_string("version"),
1365
        NXT_CONF_MAP_CSTRZ,
1366
        offsetof(nxt_app_lang_module_t, version),
1367
    },
1368
1369
    {
1370
        nxt_string("file"),
1371
        NXT_CONF_MAP_CSTRZ,
1372
        offsetof(nxt_app_lang_module_t, file),
1373
    },
1374
};
1375
1376
1377
static nxt_conf_map_t  nxt_app_lang_mounts_map[] = {
1378
    {
1379
        nxt_string("src"),
1380
        NXT_CONF_MAP_CSTRZ,
1381
        offsetof(nxt_fs_mount_t, src),
1382
    },
1383
    {
1384
        nxt_string("dst"),
1385
        NXT_CONF_MAP_CSTRZ,
1386
        offsetof(nxt_fs_mount_t, dst),
1387
    },
1388
    {
1389
        nxt_string("name"),
1390
        NXT_CONF_MAP_CSTRZ,
1391
        offsetof(nxt_fs_mount_t, name),
1392
    },
1393
    {
1394
        nxt_string("type"),
1395
        NXT_CONF_MAP_INT,
1396
        offsetof(nxt_fs_mount_t, type),
1397
    },
1398
    {
1399
        nxt_string("flags"),
1400
        NXT_CONF_MAP_INT,
1401
        offsetof(nxt_fs_mount_t, flags),
1402
    },
1403
    {
1404
        nxt_string("data"),
1405
        NXT_CONF_MAP_CSTRZ,
1406
        offsetof(nxt_fs_mount_t, data),
1407
    },
1408
};
1409
1410
1411
static void
1412
nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
1413
0
{
1414
0
    uint32_t               index, jindex, nmounts;
1415
0
    nxt_mp_t               *mp;
1416
0
    nxt_int_t              ret;
1417
0
    nxt_buf_t              *b;
1418
0
    nxt_port_t             *port;
1419
0
    nxt_runtime_t          *rt;
1420
0
    nxt_fs_mount_t         *mnt;
1421
0
    nxt_conf_value_t       *conf, *root, *value, *mounts;
1422
0
    nxt_app_lang_module_t  *lang;
1423
1424
0
    static const nxt_str_t root_path = nxt_string("/");
1425
0
    static const nxt_str_t mounts_name = nxt_string("mounts");
1426
1427
0
    rt = task->thread->runtime;
1428
1429
0
    if (msg->port_msg.pid != rt->port_by_type[NXT_PROCESS_DISCOVERY]->pid) {
1430
0
        nxt_alert(task, "process %PI cannot send modules", msg->port_msg.pid);
1431
0
        return;
1432
0
    }
1433
1434
0
    if (nxt_exiting) {
1435
0
        nxt_debug(task, "ignoring discovered modules, exiting");
1436
0
        return;
1437
0
    }
1438
1439
0
    port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
1440
0
                                 msg->port_msg.reply_port);
1441
1442
0
    if (nxt_fast_path(port != NULL)) {
1443
0
        (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, -1,
1444
0
                                     msg->port_msg.stream, 0, NULL);
1445
0
    }
1446
1447
0
    b = msg->buf;
1448
1449
0
    if (b == NULL) {
1450
0
        return;
1451
0
    }
1452
1453
0
    mp = nxt_mp_create(1024, 128, 256, 32);
1454
0
    if (mp == NULL) {
1455
0
        return;
1456
0
    }
1457
1458
0
    b = nxt_buf_chk_make_plain(mp, b, msg->size);
1459
1460
0
    if (b == NULL) {
1461
0
        return;
1462
0
    }
1463
1464
0
    nxt_debug(task, "application languages: \"%*s\"",
1465
0
              b->mem.free - b->mem.pos, b->mem.pos);
1466
1467
0
    conf = nxt_conf_json_parse(mp, b->mem.pos, b->mem.free, NULL);
1468
0
    if (conf == NULL) {
1469
0
        goto fail;
1470
0
    }
1471
1472
0
    root = nxt_conf_get_path(conf, &root_path);
1473
0
    if (root == NULL) {
1474
0
        goto fail;
1475
0
    }
1476
1477
0
    for (index = 0; /* void */ ; index++) {
1478
0
        value = nxt_conf_get_array_element(root, index);
1479
0
        if (value == NULL) {
1480
0
            break;
1481
0
        }
1482
1483
0
        lang = nxt_array_zero_add(rt->languages);
1484
0
        if (lang == NULL) {
1485
0
            goto fail;
1486
0
        }
1487
1488
0
        lang->module = NULL;
1489
1490
0
        ret = nxt_conf_map_object(rt->mem_pool, value, nxt_app_lang_module_map,
1491
0
                                  nxt_nitems(nxt_app_lang_module_map), lang);
1492
1493
0
        if (ret != NXT_OK) {
1494
0
            goto fail;
1495
0
        }
1496
1497
0
        mounts = nxt_conf_get_object_member(value, &mounts_name, NULL);
1498
0
        if (mounts == NULL) {
1499
0
            nxt_alert(task, "missing mounts from discovery message.");
1500
0
            goto fail;
1501
0
        }
1502
1503
0
        if (nxt_conf_type(mounts) != NXT_CONF_ARRAY) {
1504
0
            nxt_alert(task, "invalid mounts type from discovery message.");
1505
0
            goto fail;
1506
0
        }
1507
1508
0
        nmounts = nxt_conf_array_elements_count(mounts);
1509
1510
0
        lang->mounts = nxt_array_create(rt->mem_pool, nmounts,
1511
0
                                        sizeof(nxt_fs_mount_t));
1512
1513
0
        if (lang->mounts == NULL) {
1514
0
            goto fail;
1515
0
        }
1516
1517
0
        for (jindex = 0; /* */; jindex++) {
1518
0
            value = nxt_conf_get_array_element(mounts, jindex);
1519
0
            if (value == NULL) {
1520
0
                break;
1521
0
            }
1522
1523
0
            mnt = nxt_array_zero_add(lang->mounts);
1524
0
            if (mnt == NULL) {
1525
0
                goto fail;
1526
0
            }
1527
1528
0
            mnt->builtin = 1;
1529
0
            mnt->deps = 1;
1530
1531
0
            ret = nxt_conf_map_object(rt->mem_pool, value,
1532
0
                                      nxt_app_lang_mounts_map,
1533
0
                                      nxt_nitems(nxt_app_lang_mounts_map), mnt);
1534
1535
0
            if (ret != NXT_OK) {
1536
0
                goto fail;
1537
0
            }
1538
0
        }
1539
1540
0
        nxt_debug(task, "lang %d %s \"%s\" (%d mounts)",
1541
0
                  lang->type, lang->version, lang->file, lang->mounts->nelts);
1542
0
    }
1543
1544
0
    qsort(rt->languages->elts, rt->languages->nelts,
1545
0
          sizeof(nxt_app_lang_module_t), nxt_app_lang_compare);
1546
1547
0
fail:
1548
1549
0
    nxt_mp_destroy(mp);
1550
1551
0
    ret = nxt_process_init_start(task, nxt_controller_process);
1552
0
    if (ret == NXT_OK) {
1553
0
        ret = nxt_process_init_start(task, nxt_router_process);
1554
0
    }
1555
1556
0
    if (nxt_slow_path(ret == NXT_ERROR)) {
1557
0
        nxt_exiting = 1;
1558
1559
0
        nxt_runtime_quit(task, 1);
1560
0
    }
1561
0
}
1562
1563
1564
static int nxt_cdecl
1565
nxt_app_lang_compare(const void *v1, const void *v2)
1566
0
{
1567
0
    int                          n;
1568
0
    const nxt_app_lang_module_t  *lang1, *lang2;
1569
1570
0
    lang1 = v1;
1571
0
    lang2 = v2;
1572
1573
0
    n = lang1->type - lang2->type;
1574
1575
0
    if (n != 0) {
1576
0
        return n;
1577
0
    }
1578
1579
0
    n = nxt_strverscmp(lang1->version, lang2->version);
1580
1581
    /* Negate result to move higher versions to the beginning. */
1582
1583
0
    return -n;
1584
0
}
1585
1586
1587
static void
1588
nxt_main_port_conf_store_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
1589
0
{
1590
0
    void           *p;
1591
0
    size_t         n, size;
1592
0
    nxt_int_t      ret;
1593
0
    nxt_port_t     *ctl_port;
1594
0
    nxt_runtime_t  *rt;
1595
0
    u_char         ver[NXT_INT_T_LEN];
1596
1597
0
    rt = task->thread->runtime;
1598
1599
0
    ctl_port = rt->port_by_type[NXT_PROCESS_CONTROLLER];
1600
1601
0
    if (nxt_slow_path(msg->port_msg.pid != ctl_port->pid)) {
1602
0
        nxt_alert(task, "process %PI cannot store conf", msg->port_msg.pid);
1603
0
        return;
1604
0
    }
1605
1606
0
    p = MAP_FAILED;
1607
1608
    /*
1609
     * Ancient compilers like gcc 4.8.5 on CentOS 7 wants 'size' to be
1610
     * initialized in 'cleanup' section.
1611
     */
1612
0
    size = 0;
1613
1614
0
    if (nxt_slow_path(msg->fd[0] == -1)) {
1615
0
        nxt_alert(task, "conf_store_handler: invalid shm fd");
1616
0
        goto error;
1617
0
    }
1618
1619
0
    if (nxt_buf_mem_used_size(&msg->buf->mem) != sizeof(size_t)) {
1620
0
        nxt_alert(task, "conf_store_handler: unexpected buffer size (%d)",
1621
0
                  (int) nxt_buf_mem_used_size(&msg->buf->mem));
1622
0
        goto error;
1623
0
    }
1624
1625
0
    nxt_memcpy(&size, msg->buf->mem.pos, sizeof(size_t));
1626
1627
0
    p = nxt_mem_mmap(NULL, size, PROT_READ, MAP_SHARED, msg->fd[0], 0);
1628
1629
0
    nxt_fd_close(msg->fd[0]);
1630
0
    msg->fd[0] = -1;
1631
1632
0
    if (nxt_slow_path(p == MAP_FAILED)) {
1633
0
        goto error;
1634
0
    }
1635
1636
0
    nxt_debug(task, "conf_store_handler(%uz): %*s", size, size, p);
1637
1638
0
    if (nxt_conf_ver != NXT_VERNUM) {
1639
0
        n = nxt_sprintf(ver, ver + NXT_INT_T_LEN, "%d", NXT_VERNUM) - ver;
1640
1641
0
        ret = nxt_main_file_store(task, rt->ver_tmp, rt->ver, ver, n);
1642
0
        if (nxt_slow_path(ret != NXT_OK)) {
1643
0
            goto error;
1644
0
        }
1645
1646
0
        nxt_conf_ver = NXT_VERNUM;
1647
0
    }
1648
1649
0
    ret = nxt_main_file_store(task, rt->conf_tmp, rt->conf, p, size);
1650
1651
0
    if (nxt_fast_path(ret == NXT_OK)) {
1652
0
        goto cleanup;
1653
0
    }
1654
1655
0
error:
1656
1657
0
    nxt_alert(task, "failed to store current configuration");
1658
1659
0
cleanup:
1660
1661
0
    if (p != MAP_FAILED) {
1662
0
        nxt_mem_munmap(p, size);
1663
0
    }
1664
1665
0
    if (msg->fd[0] != -1) {
1666
0
        nxt_fd_close(msg->fd[0]);
1667
0
        msg->fd[0] = -1;
1668
0
    }
1669
0
}
1670
1671
1672
static nxt_int_t
1673
nxt_main_file_store(nxt_task_t *task, const char *tmp_name, const char *name,
1674
    u_char *buf, size_t size)
1675
0
{
1676
0
    ssize_t     n;
1677
0
    nxt_int_t   ret;
1678
0
    nxt_file_t  file;
1679
1680
0
    nxt_memzero(&file, sizeof(nxt_file_t));
1681
1682
0
    file.name = (nxt_file_name_t *) name;
1683
1684
0
    ret = nxt_file_open(task, &file, NXT_FILE_WRONLY, NXT_FILE_TRUNCATE,
1685
0
                        NXT_FILE_OWNER_ACCESS);
1686
0
    if (nxt_slow_path(ret != NXT_OK)) {
1687
0
        return NXT_ERROR;
1688
0
    }
1689
1690
0
    n = nxt_file_write(&file, buf, size, 0);
1691
1692
0
    nxt_file_close(task, &file);
1693
1694
0
    if (nxt_slow_path(n != (ssize_t) size)) {
1695
0
        (void) nxt_file_delete(file.name);
1696
0
        return NXT_ERROR;
1697
0
    }
1698
1699
0
    return nxt_file_rename(file.name, (nxt_file_name_t *) name);
1700
0
}
1701
1702
1703
static void
1704
nxt_main_port_access_log_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
1705
0
{
1706
0
    u_char               *path;
1707
0
    nxt_int_t            ret;
1708
0
    nxt_file_t           file;
1709
0
    nxt_port_t           *port;
1710
0
    nxt_port_msg_type_t  type;
1711
1712
0
    nxt_debug(task, "opening access log file");
1713
1714
0
    path = msg->buf->mem.pos;
1715
1716
0
    nxt_memzero(&file, sizeof(nxt_file_t));
1717
1718
0
    file.name = (nxt_file_name_t *) path;
1719
0
    file.log_level = NXT_LOG_ERR;
1720
1721
0
    ret = nxt_file_open(task, &file, O_WRONLY | O_APPEND, O_CREAT,
1722
0
                        NXT_FILE_OWNER_ACCESS);
1723
1724
0
    type = (ret == NXT_OK) ? NXT_PORT_MSG_RPC_READY_LAST | NXT_PORT_MSG_CLOSE_FD
1725
0
                           : NXT_PORT_MSG_RPC_ERROR;
1726
1727
0
    port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
1728
0
                                 msg->port_msg.reply_port);
1729
1730
0
    if (nxt_fast_path(port != NULL)) {
1731
0
        (void) nxt_port_socket_write(task, port, type, file.fd,
1732
0
                                     msg->port_msg.stream, 0, NULL);
1733
1734
0
    } else {
1735
0
        nxt_file_close(task, &file);
1736
0
    }
1737
0
}