varnish-cache/bin/varnishd/mgt/mgt_child.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2015 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * The mechanics of handling the child process
31
 */
32
33
#include "config.h"
34
35
#include <sys/types.h>
36
37
#include <poll.h>
38
#include <stdarg.h>
39
#include <stdio.h>
40
#include <string.h>
41
#include <syslog.h>
42
#include <unistd.h>
43
#include <sys/types.h>
44
#include <sys/socket.h>
45
#include <sys/time.h>
46
#include <sys/resource.h>
47
48
#include "mgt.h"
49
#include "acceptor/mgt_acceptor.h"
50
51
#include "vapi/vsig.h"
52
53
#include "vbm.h"
54
#include "vcli_serve.h"
55
#include "vev.h"
56
#include "vfil.h"
57
#include "vlu.h"
58
#include "vtim.h"
59
60
#include "common/heritage.h"
61
62
static pid_t            child_pid = -1;
63
64
static struct vbitmap   *fd_map;
65
66
static int              child_cli_fd = -1;
67
static int              child_output = -1;
68
69
static enum {
70
        CH_STOPPED = 0,
71
        CH_STARTING = 1,
72
        CH_RUNNING = 2,
73
        CH_STOPPING = 3,
74
        CH_DIED = 4
75
}                       child_state = CH_STOPPED;
76
77
static const char * const ch_state[] = {
78
        [CH_STOPPED] =  "stopped",
79
        [CH_STARTING] = "starting",
80
        [CH_RUNNING] =  "running",
81
        [CH_STOPPING] = "stopping",
82
        [CH_DIED] =     "died, (restarting)",
83
};
84
85
static struct vev       *ev_poker;
86
static struct vev       *ev_listen;
87
static struct vlu       *child_std_vlu;
88
89
static struct vsb *child_panic = NULL;
90
91
static void mgt_reap_child(void);
92
static int kill_child(void);
93
94
/*=====================================================================
95
 * Panic string evacuation and handling
96
 */
97
98
static void
99 13
mgt_panic_record(pid_t r)
100
{
101
        char time_str[30];
102
103 13
        if (child_panic != NULL)
104 0
                VSB_destroy(&child_panic);
105 13
        child_panic = VSB_new_auto();
106 13
        AN(child_panic);
107 13
        VTIM_format(VTIM_real(), time_str);
108 26
        VSB_printf(child_panic, "Child (%jd) Panic at: %s\n",
109 13
            (intmax_t)r, time_str);
110 26
        VSB_quote(child_panic, heritage.panic_str,
111 13
            strnlen(heritage.panic_str, heritage.panic_str_len),
112
            VSB_QUOTE_NONL);
113 13
        MGT_ComplainVSB(C_ERR, child_panic);
114 13
}
115
116
static void
117 11
mgt_panic_clear(void)
118
{
119 11
        VSB_destroy(&child_panic);
120 11
}
121
122
static void
123 954
cli_panic_show(struct cli *cli, const char * const *av, int json)
124
{
125 954
        if (!child_panic) {
126 943
                VCLI_SetResult(cli, CLIS_CANT);
127 943
                VCLI_Out(cli,
128
                    "Child has not panicked or panic has been cleared");
129 943
                return;
130
        }
131
132 11
        if (!json) {
133 7
                VCLI_Out(cli, "%s\n", VSB_data(child_panic));
134 7
                return;
135
        }
136
137 4
        VCLI_JSON_begin(cli, 2, av);
138 4
        VCLI_Out(cli, ",\n");
139 4
        VCLI_JSON_str(cli, VSB_data(child_panic));
140 4
        VCLI_JSON_end(cli);
141 954
}
142
143
static void v_matchproto_(cli_func_t)
144 950
mch_cli_panic_show(struct cli *cli, const char * const *av, void *priv)
145
{
146 950
        (void)priv;
147 950
        cli_panic_show(cli, av, 0);
148 950
}
149
150
static void v_matchproto_(cli_func_t)
151 4
mch_cli_panic_show_json(struct cli *cli, const char * const *av, void *priv)
152
{
153 4
        (void)priv;
154 4
        cli_panic_show(cli, av, 1);
155 4
}
156
157
static void v_matchproto_(cli_func_t)
158 14
mch_cli_panic_clear(struct cli *cli, const char * const *av, void *priv)
159
{
160 14
        (void)priv;
161
162 14
        if (av[2] != NULL && strcmp(av[2], "-z")) {
163 0
                VCLI_SetResult(cli, CLIS_PARAM);
164 0
                VCLI_Out(cli, "Unknown parameter \"%s\".", av[2]);
165 0
                return;
166 2
        } else if (av[2] != NULL) {
167 2
                VSC_C_mgt->child_panic = 0;
168 2
                if (child_panic == NULL)
169 1
                        return;
170 1
        }
171 13
        if (child_panic == NULL) {
172 2
                VCLI_SetResult(cli, CLIS_CANT);
173 2
                VCLI_Out(cli, "No panic to clear");
174 2
                return;
175
        }
176 11
        mgt_panic_clear();
177 14
}
178
179
/*=====================================================================
180
 * Track the highest file descriptor the parent knows is being used.
181
 *
182
 * This allows the child process to clean/close only a small fraction
183
 * of the possible file descriptors after exec(2).
184
 *
185
 * This is likely to a bit on the low side, as libc and other libraries
186
 * has a tendency to cache file descriptors (syslog, resolver, etc.)
187
 * so we add a margin of 10 fds.
188
 *
189
 * For added safety, we check that we see no file descriptor open for
190
 * another margin above the limit for which we close by design
191
 */
192
193
static int              mgt_max_fd;
194
195
#define CLOSE_FD_UP_TO  (mgt_max_fd + 10)
196
#define CHECK_FD_UP_TO  (CLOSE_FD_UP_TO + 10)
197
198
void
199 9855
MCH_TrackHighFd(int fd)
200
{
201
        /*
202
         * Assert > 0, to catch bogus opens, we know where stdin goes
203
         * in the master process.
204
         */
205 9855
        assert(fd > 0);
206 9855
        mgt_max_fd = vmax(mgt_max_fd, fd);
207 9855
}
208
209
/*--------------------------------------------------------------------
210
 * Keep track of which filedescriptors the child should inherit and
211
 * which should be closed after fork()
212
 */
213
214
void
215 5881
MCH_Fd_Inherit(int fd, const char *what)
216
{
217
218 5881
        assert(fd >= 0);
219
        // XXX why?
220 5881
        if (fd > 0)
221 5881
                MCH_TrackHighFd(fd);
222 10786
        if (fd_map == NULL)
223 976
                fd_map = vbit_new(128);
224 5881
        AN(fd_map);
225 5881
        if (what != NULL)
226 3946
                vbit_set(fd_map, fd);
227
        else
228 1935
                vbit_clr(fd_map, fd);
229 5881
}
230
231
/*=====================================================================
232
 * Listen to stdout+stderr from the child
233
 */
234
235
static const char *whining_child = C_ERR;
236
237
static int v_matchproto_(vlu_f)
238 2666
child_line(void *priv, const char *p)
239
{
240 2666
        (void)priv;
241
242 2666
        MGT_Complain(whining_child, "Child (%jd) said %s", (intmax_t)child_pid, p);
243 2666
        return (0);
244
}
245
246
/*--------------------------------------------------------------------
247
 * NB: Notice cleanup call from mgt_reap_child()
248
 */
249
250
static int v_matchproto_(vev_cb_f)
251 4221
child_listener(const struct vev *e, int what)
252
{
253
254 4221
        if ((what & ~VEV__RD) || VLU_Fd(child_std_vlu, child_output)) {
255 9
                ev_listen = NULL;
256 9
                if (e != NULL)
257 9
                        mgt_reap_child();
258 2145
                return (1);
259
        }
260 2076
        return (0);
261 4221
}
262
263
/*=====================================================================
264
 * Periodically poke the child, to see that it still lives
265
 */
266
267
static int v_matchproto_(vev_cb_f)
268 186
child_poker(const struct vev *e, int what)
269
{
270 186
        char *r = NULL;
271
        unsigned status;
272
273 186
        (void)e;
274 186
        (void)what;
275 186
        if (child_state != CH_RUNNING)
276 0
                return (1);
277 186
        if (child_pid < 0)
278 0
                return (0);
279 186
        if (mgt_cli_askchild(&status, &r, "ping\n") || strncmp("PONG ", r, 5)) {
280 0
                MGT_Complain(C_ERR, "Unexpected reply from ping: %u %s",
281 0
                    status, r);
282 0
                if (status != CLIS_COMMS)
283 0
                        MCH_Cli_Fail();
284 0
        }
285 186
        free(r);
286 186
        return (0);
287 186
}
288
289
/*=====================================================================
290
 * Launch the child process
291
 */
292
293
#define mgt_launch_err(cli, status, ...) do {           \
294
                MGT_Complain(C_ERR, __VA_ARGS__);       \
295
                if (cli == NULL)                        \
296
                        break;                          \
297
                VCLI_Out(cli, __VA_ARGS__);             \
298
                VCLI_SetResult(cli, status);            \
299
        } while (0)
300
301
static void
302 961
mgt_launch_child(struct cli *cli)
303
{
304
        pid_t pid;
305
        unsigned u;
306
        char *p;
307
        struct vev *e;
308
        int i, cp[2];
309
        struct rlimit rl[1];
310
        vtim_dur dstart;
311
        int bstart;
312
        vtim_mono t0;
313
314 961
        if (child_state != CH_STOPPED && child_state != CH_DIED)
315 0
                return;
316
317 961
        child_state = CH_STARTING;
318
319
        /* Open pipe for mgt->child CLI */
320 961
        AZ(socketpair(AF_UNIX, SOCK_STREAM, 0, cp));
321 961
        heritage.cli_fd = cp[0];
322 961
        assert(cp[0] > STDERR_FILENO);  // See #2782
323 961
        assert(cp[1] > STDERR_FILENO);
324 961
        MCH_Fd_Inherit(heritage.cli_fd, "cli_fd");
325 961
        child_cli_fd = cp[1];
326
327
        /*
328
         * Open pipe for child stdout/err
329
         * NB: not inherited, because we dup2() it to stdout/stderr in child
330
         */
331 961
        AZ(pipe(cp));
332 961
        heritage.std_fd = cp[1];
333 961
        child_output = cp[0];
334
335 961
        mgt_SHM_ChildNew();
336
337 961
        AN(heritage.param);
338 961
        AN(heritage.panic_str);
339 961
        VJ_master(JAIL_MASTER_SYSTEM);
340 961
        if ((pid = fork()) < 0) {
341 0
                VJ_master(JAIL_MASTER_LOW);
342 0
                perror("Could not fork child");
343 0
                exit(1);                // XXX Harsh ?
344
        }
345 1903
        if (pid == 0) {
346
347 942
                if (MGT_FEATURE(FEATURE_NO_COREDUMP)) {
348 15
                        memset(rl, 0, sizeof *rl);
349 15
                        rl->rlim_cur = 0;
350 15
                        AZ(setrlimit(RLIMIT_CORE, rl));
351 15
                }
352
353
                /* Redirect stdin/out/err */
354 942
                VFIL_null_fd(STDIN_FILENO);
355 942
                assert(dup2(heritage.std_fd, STDOUT_FILENO) == STDOUT_FILENO);
356 942
                assert(dup2(heritage.std_fd, STDERR_FILENO) == STDERR_FILENO);
357
358 942
                setbuf(stdout, NULL);
359 942
                setbuf(stderr, NULL);
360 942
                printf("Child starts\n");
361
362
                /*
363
                 * Close all FDs the child shouldn't know about
364
                 *
365
                 * We cannot just close these filedescriptors, some random
366
                 * library routine might miss it later on and wantonly close
367
                 * a FD we use at that point in time. (See bug #1841).
368
                 * We close the FD and replace it with /dev/null instead,
369
                 * That prevents security leakage, and gives the library
370
                 * code a valid FD to close when it discovers the changed
371
                 * circumstances.
372
                 */
373 942
                closelog();
374
375 21677
                for (i = STDERR_FILENO + 1; i <= CLOSE_FD_UP_TO; i++) {
376 20735
                        if (vbit_test(fd_map, i))
377 2838
                                continue;
378 17897
                        if (close(i) == 0)
379 8468
                                VFIL_null_fd(i);
380 17897
                }
381 10362
                for (i = CLOSE_FD_UP_TO + 1; i <= CHECK_FD_UP_TO; i++) {
382 9420
                        assert(close(i) == -1);
383 9420
                        assert(errno == EBADF);
384 9420
                }
385
386 942
                mgt_ProcTitle("Child");
387
388 942
                heritage.cls = mgt_cls;
389 942
                heritage.ident = VSB_data(vident) + 1;
390
391 942
                vext_load();
392
393 942
                STV_Init();
394
395 942
                VJ_subproc(JAIL_SUBPROC_WORKER);
396
397
                /*
398
                 * We pass these two params because child_main needs them
399
                 * well before it has found its own param struct.
400
                 */
401 1884
                child_main(mgt_param.sigsegv_handler,
402 942
                    mgt_param.wthread_stacksize);
403
404
                /*
405
                 * It would be natural to clean VSMW up here, but it is apt
406
                 * to fail in some scenarios because of the fall-back
407
                 * "rm -rf" in mgt_SHM_ChildDestroy() which is there to
408
                 * catch the cases were we don't get here.
409
                 */
410
                // VSMW_Destroy(&heritage.proc_vsmw);
411
412 942
                exit(0);
413
        }
414 961
        VJ_master(JAIL_MASTER_LOW);
415 961
        assert(pid > 1);
416 961
        MGT_Complain(C_DEBUG, "Child (%jd) Started", (intmax_t)pid);
417 961
        VSC_C_mgt->child_start++;
418
419
        /* Close stuff the child got */
420 961
        closefd(&heritage.std_fd);
421
422 961
        MCH_Fd_Inherit(heritage.cli_fd, NULL);
423 961
        closefd(&heritage.cli_fd);
424
425 961
        child_std_vlu = VLU_New(child_line, NULL, 0);
426 961
        AN(child_std_vlu);
427
428
        /* Wait for cache/cache_cli.c::CLI_Run() to check in */
429 961
        bstart = mgt_param.startup_timeout >= mgt_param.cli_timeout;
430 961
        dstart = bstart ? mgt_param.startup_timeout : mgt_param.cli_timeout;
431 961
        t0 = VTIM_mono();
432 961
        u = mgt_cli_start_child(child_cli_fd, dstart);
433 961
        if (u != CLIS_OK) {
434 11
                assert(u == CLIS_COMMS);
435 11
                if (VTIM_mono() - t0 < dstart)
436 7
                        mgt_launch_err(cli, u, "Child failed on launch ");
437
                else
438 4
                        mgt_launch_err(cli, u, "Child failed on launch "
439
                            "within %s_timeout=%.2fs%s",
440
                            bstart ? "startup" : "cli", dstart,
441
                            bstart ? "" : " (tip: set startup_timeout)");
442 11
                child_pid = pid;
443 11
                (void)kill_child();
444 11
                mgt_reap_child();
445 11
                child_state = CH_STOPPED;
446 11
                return;
447
        } else {
448 950
                assert(u == CLIS_OK);
449 950
                fprintf(stderr, "Child launched OK\n");
450
        }
451 950
        whining_child = C_INFO;
452
453 950
        AZ(ev_listen);
454 950
        e = VEV_Alloc();
455 950
        XXXAN(e);
456 950
        e->fd = child_output;
457 950
        e->fd_flags = VEV__RD;
458 950
        e->name = "Child listener";
459 950
        e->callback = child_listener;
460 950
        AZ(VEV_Start(mgt_evb, e));
461 950
        ev_listen = e;
462 950
        AZ(ev_poker);
463 950
        if (mgt_param.ping_interval > 0) {
464 950
                e = VEV_Alloc();
465 950
                XXXAN(e);
466 950
                e->timeout = mgt_param.ping_interval;
467 950
                e->callback = child_poker;
468 950
                e->name = "child poker";
469 950
                AZ(VEV_Start(mgt_evb, e));
470 950
                ev_poker = e;
471 950
        }
472
473 950
        child_pid = pid;
474
475 950
        if (mgt_push_vcls(cli, &u, &p)) {
476 1
                mgt_launch_err(cli, u, "Child (%jd) Pushing vcls failed:\n%s",
477
                    (intmax_t)child_pid, p);
478 1
                free(p);
479 1
                MCH_Stop_Child();
480 1
                return;
481
        }
482
483 949
        if (mgt_cli_askchild(&u, &p, "start\n")) {
484 0
                mgt_launch_err(cli, u, "Child (%jd) Acceptor start failed:\n%s",
485
                    (intmax_t)child_pid, p);
486 0
                free(p);
487 0
                MCH_Stop_Child();
488 0
                return;
489
        }
490
491 949
        free(p);
492 949
        child_state = CH_RUNNING;
493 961
}
494
495
/*=====================================================================
496
 * Cleanup when child dies.
497
 */
498
499
static int
500 15
kill_child(void)
501
{
502
        int i, error;
503
504 15
        VJ_master(JAIL_MASTER_KILL);
505 15
        i = kill(child_pid, SIGQUIT);
506 15
        error = errno;
507 15
        VJ_master(JAIL_MASTER_LOW);
508 15
        errno = error;
509 15
        return (i);
510
}
511
512
static void
513 961
mgt_reap_child(void)
514
{
515
        int i;
516 961
        int status = 0xffff;
517
        struct vsb *vsb;
518 961
        pid_t r = 0;
519
520 961
        assert(child_pid != -1);
521
522
        /*
523
         * Close the CLI connections
524
         * This signals orderly shut down to child
525
         */
526 961
        mgt_cli_stop_child();
527 961
        if (child_cli_fd >= 0)
528 961
                closefd(&child_cli_fd);
529
530
        /* Stop the poker */
531 972
        if (ev_poker != NULL) {
532 950
                VEV_Stop(mgt_evb, ev_poker);
533 950
                free(ev_poker);
534 950
                ev_poker = NULL;
535 950
        }
536
537
        /* Stop the listener */
538 959
        if (ev_listen != NULL) {
539 941
                VEV_Stop(mgt_evb, ev_listen);
540 941
                free(ev_listen);
541 941
                ev_listen = NULL;
542 941
        }
543
544
        /* Compose obituary */
545 961
        vsb = VSB_new_auto();
546 961
        XXXAN(vsb);
547
548 961
        (void)VFIL_nonblocking(child_output);
549
        /* Wait for child to die */
550 2147
        for (i = 0; i < mgt_param.cli_timeout * 10; i++) {
551 2145
                (void)child_listener(NULL, VEV__RD);
552 2145
                r = waitpid(child_pid, &status, WNOHANG);
553 2145
                if (r == child_pid)
554 959
                        break;
555 1186
                VTIM_sleep(0.1);
556 1186
        }
557 1916
        if (r == 0) {
558 4
                VSB_printf(vsb, "Child (%jd) not dying (waitpid = %jd),"
559 2
                    " killing\n", (intmax_t)child_pid, (intmax_t)r);
560
561
                /* Kick it Jim... */
562 2
                (void)kill_child();
563 2
                r = waitpid(child_pid, &status, 0);
564 2
        }
565 4
        if (r != child_pid)
566 0
                fprintf(stderr, "WAIT 0x%jd\n", (intmax_t)r);
567 961
        assert(r == child_pid);
568
569 1922
        VSB_printf(vsb, "Child (%jd) %s", (intmax_t)r,
570 961
            status ? "died" : "ended");
571 961
        if (WIFEXITED(status) && WEXITSTATUS(status)) {
572 7
                VSB_printf(vsb, " status=%d", WEXITSTATUS(status));
573 7
                exit_status |= 0x20;
574 7
                if (WEXITSTATUS(status) == 1)
575 0
                        VSC_C_mgt->child_exit++;
576
                else
577 7
                        VSC_C_mgt->child_stop++;
578 7
        }
579 961
        if (WIFSIGNALED(status)) {
580 15
                VSB_printf(vsb, " signal=%d", WTERMSIG(status));
581 15
                exit_status |= 0x40;
582 15
                VSC_C_mgt->child_died++;
583 15
        }
584
#ifdef WCOREDUMP
585 28
        if (WCOREDUMP(status)) {
586 2
                VSB_cat(vsb, " (core dumped)");
587 2
                if (!MGT_FEATURE(FEATURE_NO_COREDUMP))
588 2
                        exit_status |= 0x80;
589 2
                VSC_C_mgt->child_dump++;
590 2
        }
591
#endif
592 13
        MGT_ComplainVSB(status ? C_ERR : C_INFO, vsb);
593 13
        VSB_destroy(&vsb);
594
595
        /* Dispose of shared memory but evacuate panic messages first */
596 13
        if (heritage.panic_str[0] != '\0') {
597 13
                mgt_panic_record(r);
598 13
                VSC_C_mgt->child_panic++;
599 13
        }
600
601 17
        mgt_SHM_ChildDestroy();
602
603 17
        if (child_state == CH_RUNNING)
604 9
                child_state = CH_DIED;
605
606
        /* Pick up any stuff lingering on stdout/stderr */
607 961
        (void)child_listener(NULL, VEV__RD);
608 961
        closefd(&child_output);
609 961
        VLU_Destroy(&child_std_vlu);
610
611 961
        child_pid = -1;
612
613 961
        MGT_Complain(C_DEBUG, "Child cleanup complete");
614
615
        /* XXX number of retries? interval? */
616 961
        for (i = 0; i < 3; i++) {
617 961
                if (VCA_reopen_sockets() == 0)
618 961
                        break;
619
                /* error already logged */
620 0
                (void)sleep(1);
621 0
        }
622 961
        if (i == 3) {
623
                /* We failed to reopen our listening sockets. No choice
624
                 * but to exit. */
625 0
                MGT_Complain(C_ERR,
626
                    "Could not reopen listening sockets. Exiting.");
627 0
                exit(1);
628
        }
629
630 961
        if (child_state == CH_DIED && mgt_param.auto_restart)
631 0
                mgt_launch_child(NULL);
632 961
        else if (child_state == CH_DIED)
633 9
                child_state = CH_STOPPED;
634 952
        else if (child_state == CH_STOPPING)
635 941
                child_state = CH_STOPPED;
636 961
}
637
638
/*=====================================================================
639
 * If CLI communications with the child process fails, there is nothing
640
 * for us to do but to drag it behind the barn and get it over with.
641
 *
642
 * The typical case is where the child process fails to return a reply
643
 * before the cli_timeout expires.  This invalidates the CLI pipes for
644
 * all future use, as we don't know if the child was just slow and the
645
 * result gets piped later on, or if the child is catatonic.
646
 */
647
648
void
649 2
MCH_Cli_Fail(void)
650
{
651
652 2
        if (child_state != CH_RUNNING && child_state != CH_STARTING)
653 0
                return;
654 2
        if (child_pid < 0)
655 0
                return;
656 2
        if (kill_child() == 0)
657 2
                MGT_Complain(C_ERR, "Child (%jd) not responding to CLI,"
658 2
                    " killed it.", (intmax_t)child_pid);
659
        else
660 0
                MGT_Complain(C_ERR, "Failed to kill child with PID %jd: %s",
661 0
                    (intmax_t)child_pid, VAS_errtxt(errno));
662 2
}
663
664
/*=====================================================================
665
 * Controlled stop of child process
666
 *
667
 * Reaping the child asks for orderly shutdown
668
 */
669
670
void
671 1891
MCH_Stop_Child(void)
672
{
673
674 1891
        if (child_state != CH_RUNNING && child_state != CH_STARTING)
675 950
                return;
676
677 941
        child_state = CH_STOPPING;
678
679 941
        MGT_Complain(C_DEBUG, "Stopping Child");
680
681 941
        mgt_reap_child();
682 1891
}
683
684
/*=====================================================================
685
 */
686
687
int
688 9
MCH_Start_Child(void)
689
{
690 9
        mgt_launch_child(NULL);
691 9
        if (child_state != CH_RUNNING)
692 8
                return (2);
693 1
        return (0);
694 9
}
695
696
/*====================================================================
697
 * Query if the child is running
698
 */
699
700
int
701 15008
MCH_Running(void)
702
{
703
704 15008
        return (child_pid > 0);
705
}
706
707
/*=====================================================================
708
 * CLI commands
709
 */
710
711
static void v_matchproto_(cli_func_t)
712 2
mch_pid(struct cli *cli, const char * const *av, void *priv)
713
{
714
715 2
        (void)av;
716 2
        (void)priv;
717 2
        VCLI_Out(cli, "Master: %10jd\n", (intmax_t)getpid());
718 2
        if (!MCH_Running())
719 1
                return;
720 1
        VCLI_Out(cli, "Worker: %10jd\n", (intmax_t)child_pid);
721 2
}
722
723
static void v_matchproto_(cli_func_t)
724 2
mch_pid_json(struct cli *cli, const char * const *av, void *priv)
725
{
726
727 2
        (void)priv;
728 2
        VCLI_JSON_begin(cli, 2, av);
729 2
        VCLI_Out(cli, ",\n  {\"master\": %jd", (intmax_t)getpid());
730 2
        if (MCH_Running())
731 1
                VCLI_Out(cli, ", \"worker\": %jd", (intmax_t)child_pid);
732 2
        VCLI_Out(cli, "}");
733 2
        VCLI_JSON_end(cli);
734 2
}
735
736
static void v_matchproto_(cli_func_t)
737 957
mch_cli_server_start(struct cli *cli, const char * const *av, void *priv)
738
{
739
        const char *err;
740
741 957
        (void)av;
742 957
        (void)priv;
743 957
        if (child_state == CH_STOPPED) {
744 953
                err = mgt_has_vcl();
745 953
                if (err == NULL) {
746 952
                        mgt_launch_child(cli);
747 952
                } else {
748 1
                        VCLI_SetResult(cli, CLIS_CANT);
749 1
                        VCLI_Out(cli, "%s", err);
750
                }
751 953
        } else {
752 4
                VCLI_SetResult(cli, CLIS_CANT);
753 4
                VCLI_Out(cli, "Child in state %s", ch_state[child_state]);
754
        }
755 957
}
756
757
static void v_matchproto_(cli_func_t)
758 1007
mch_cli_server_stop(struct cli *cli, const char * const *av, void *priv)
759
{
760
761 1007
        (void)av;
762 1007
        (void)priv;
763 1007
        if (child_state == CH_RUNNING) {
764 937
                MCH_Stop_Child();
765 937
        } else {
766 70
                VCLI_SetResult(cli, CLIS_CANT);
767 70
                VCLI_Out(cli, "Child in state %s", ch_state[child_state]);
768
        }
769 1007
}
770
771
static void v_matchproto_(cli_func_t)
772 1948
mch_cli_server_status(struct cli *cli, const char * const *av, void *priv)
773
{
774 1948
        (void)av;
775 1948
        (void)priv;
776 1948
        VCLI_Out(cli, "Child in state %s", ch_state[child_state]);
777 1948
}
778
779
static void v_matchproto_(cli_func_t)
780 8
mch_cli_server_status_json(struct cli *cli, const char * const *av, void *priv)
781
{
782 8
        (void)priv;
783 8
        VCLI_JSON_begin(cli, 2, av);
784 8
        VCLI_Out(cli, ", ");
785 8
        VCLI_JSON_str(cli, ch_state[child_state]);
786 8
        VCLI_JSON_end(cli);
787 8
}
788
789
static struct cli_proto cli_mch[] = {
790
        { CLICMD_SERVER_STATUS,         "", mch_cli_server_status,
791
          mch_cli_server_status_json },
792
        { CLICMD_SERVER_START,          "", mch_cli_server_start },
793
        { CLICMD_SERVER_STOP,           "", mch_cli_server_stop },
794
        { CLICMD_PANIC_SHOW,            "", mch_cli_panic_show,
795
          mch_cli_panic_show_json },
796
        { CLICMD_PANIC_CLEAR,           "", mch_cli_panic_clear },
797
        { CLICMD_PID,                   "", mch_pid, mch_pid_json },
798
        { NULL }
799
};
800
801
/*=====================================================================
802
 * This thread is the master thread in the management process.
803
 * The relatively simple task is to start and stop the child process
804
 * and to reincarnate it in case of trouble.
805
 */
806
807
void
808 961
MCH_Init(void)
809
{
810
811 961
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_mch);
812 961
}