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