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