varnish-cache/bin/varnishd/mgt/mgt_cli.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 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 management process' CLI handling
31
 */
32
33
#include "config.h"
34
35
#include <sys/types.h>
36
#include <sys/socket.h>
37
38
#include <fcntl.h>
39
#include <poll.h>
40
#include <signal.h>
41
#include <stdarg.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <unistd.h>
46
47
#include "mgt/mgt.h"
48
#include "common/heritage.h"
49
50
#include "vcli_serve.h"
51
#include "vev.h"
52
#include "vrnd.h"
53
#include "vsa.h"
54
#include "vss.h"
55
#include "vtcp.h"
56
57
#define CLI_CMD(U,l,s,h,d,m,M) \
58
const struct cli_cmd_desc CLICMD_##U[1] = {{ l, s, h, d, m, M }};
59
#include "tbl/cli_cmds.h"
60
61
static const struct cli_cmd_desc *cmds[] = {
62
#define CLI_CMD(U,l,s,h,d,m,M) CLICMD_##U,
63
#include "tbl/cli_cmds.h"
64
};
65
66
static const int ncmds = sizeof cmds / sizeof cmds[0];
67
68
static int              cli_i = -1, cli_o = -1;
69
struct VCLS             *mgt_cls;
70
static const char       *secret_file;
71
72
static struct vsb       *cli_buf = NULL;
73
74
/*--------------------------------------------------------------------*/
75
76
static void v_matchproto_(cli_func_t)
77 76360
mcf_banner(struct cli *cli, const char *const *av, void *priv)
78
{
79
80 76360
        (void)av;
81 76360
        (void)priv;
82 76360
        VCLI_Out(cli, "-----------------------------\n");
83 76360
        VCLI_Out(cli, "Varnish Cache CLI 1.0\n");
84 76360
        VCLI_Out(cli, "-----------------------------\n");
85 76360
        VCLI_Out(cli, "%s\n", VSB_data(vident) + 1);
86 76360
        VCLI_Out(cli, "%s\n", VCS_String("V"));
87 76360
        VCLI_Out(cli, "\n");
88 76360
        VCLI_Out(cli, "Type 'help' for command list.\n");
89 76360
        VCLI_Out(cli, "Type 'quit' to close CLI session.\n");
90 76360
        if (!MCH_Running())
91 75080
                VCLI_Out(cli, "Type 'start' to launch worker process.\n");
92 76360
        VCLI_SetResult(cli, CLIS_OK);
93 76360
}
94
95
/*--------------------------------------------------------------------*/
96
97
static struct cli_proto cli_proto[] = {
98
        { CLICMD_BANNER,                "", mcf_banner },
99
        { NULL }
100
};
101
102
/*--------------------------------------------------------------------*/
103
104
static void v_noreturn_ v_matchproto_(cli_func_t)
105 0
mcf_panic(struct cli *cli, const char * const *av, void *priv)
106
{
107
108 0
        (void)cli;
109 0
        (void)av;
110 0
        (void)priv;
111 0
        v_gcov_flush();
112 0
        AZ(strcmp("", "You asked for it"));
113
        /* NOTREACHED */
114 0
        abort();
115
}
116
117
static struct cli_proto cli_debug[] = {
118
        { CLICMD_DEBUG_PANIC_MASTER,            "d", mcf_panic },
119
        { NULL }
120
};
121
122
/*--------------------------------------------------------------------*/
123
124
static void v_matchproto_(cli_func_t)
125 160600
mcf_askchild(struct cli *cli, const char * const *av, void *priv)
126
{
127
        int i;
128
        char *q;
129
        unsigned u;
130
131 160600
        (void)priv;
132
        /*
133
         * Command not recognized in master, try cacher if it is
134
         * running.
135
         */
136 160600
        if (cli_o <= 0) {
137 2640
                VCLI_SetResult(cli, CLIS_UNKNOWN);
138 2640
                VCLI_Out(cli,
139
                    "Unknown request in manager process "
140
                    "(child not running).\n"
141
                    "Type 'help' for more info.");
142 2640
                return;
143
        }
144 157960
        VSB_clear(cli_buf);
145 371160
        for (i = 1; av[i] != NULL; i++) {
146 213200
                VSB_quote(cli_buf, av[i], strlen(av[i]), 0);
147 213200
                VSB_putc(cli_buf, ' ');
148 213200
        }
149 157960
        VSB_putc(cli_buf, '\n');
150 157960
        AZ(VSB_finish(cli_buf));
151 157960
        if (VSB_tofile(cli_buf, cli_o)) {
152 0
                VCLI_SetResult(cli, CLIS_COMMS);
153 0
                VCLI_Out(cli, "CLI communication error");
154 0
                MCH_Cli_Fail();
155 0
                return;
156
        }
157 157960
        if (VCLI_ReadResult(cli_i, &u, &q, mgt_param.cli_timeout))
158 40
                MCH_Cli_Fail();
159 157960
        VCLI_SetResult(cli, u);
160 157960
        VCLI_Out(cli, "%s", q);
161 157960
        free(q);
162 160600
}
163
164
static const struct cli_cmd_desc CLICMD_WILDCARD[1] =
165
    {{ "*", "<wild-card-entry>", "<fall through to cacher>", "", 0, 999 }};
166
167
static struct cli_proto cli_askchild[] = {
168
        { CLICMD_WILDCARD, "h*", mcf_askchild, mcf_askchild },
169
        { NULL }
170
};
171
172
/*--------------------------------------------------------------------
173
 * Ask the child something over CLI, return zero only if everything is
174
 * happy happy.
175
 */
176
177
int
178 146083
mgt_cli_askchild(unsigned *status, char **resp, const char *fmt, ...)
179
{
180
        int i;
181
        va_list ap;
182
        unsigned u;
183
184 146083
        AN(status);
185 146083
        VSB_clear(cli_buf);
186
187 146083
        if (resp != NULL)
188 146083
                *resp = NULL;
189 146083
        *status = 0;
190 146083
        if (cli_i < 0 || cli_o < 0) {
191 0
                *status = CLIS_CANT;
192 0
                return (CLIS_CANT);
193
        }
194 146083
        va_start(ap, fmt);
195 146083
        AZ(VSB_vprintf(cli_buf, fmt, ap));
196 146083
        va_end(ap);
197 146083
        AZ(VSB_finish(cli_buf));
198 146083
        i = VSB_len(cli_buf);
199 146083
        assert(i > 0 && VSB_data(cli_buf)[i - 1] == '\n');
200 146083
        if (VSB_tofile(cli_buf, cli_o)) {
201 0
                *status = CLIS_COMMS;
202 0
                if (resp != NULL)
203 0
                        *resp = strdup("CLI communication error");
204 0
                MCH_Cli_Fail();
205 0
                return (CLIS_COMMS);
206
        }
207
208 146083
        if (VCLI_ReadResult(cli_i, &u, resp, mgt_param.cli_timeout))
209 40
                MCH_Cli_Fail();
210 146083
        *status = u;
211 146083
        return (u == CLIS_OK || u == CLIS_TRUNCATED ? 0 : u);
212 146083
}
213
214
/*--------------------------------------------------------------------*/
215
216
unsigned
217 37600
mgt_cli_start_child(int fd, double tmo)
218
{
219
        unsigned u;
220
221 37600
        cli_i = fd;
222 37600
        cli_o = fd;
223 37600
        if (VCLI_ReadResult(cli_i, &u, NULL, tmo)) {
224 320
                return (CLIS_COMMS);
225
        }
226 37280
        return (u);
227 37600
}
228
229
/*--------------------------------------------------------------------*/
230
231
void
232 37600
mgt_cli_stop_child(void)
233
{
234
235 37600
        cli_i = -1;
236 37600
        cli_o = -1;
237
        /* XXX: kick any users */
238 37600
}
239
240
/*--------------------------------------------------------------------
241
 * Generate a random challenge
242
 */
243
244
static void
245 38840
mgt_cli_challenge(struct cli *cli)
246
{
247
        size_t z;
248
        uint8_t u;
249
250 38840
        AZ(VRND_RandomCrypto(cli->challenge, sizeof cli->challenge - 2));
251 1281720
        for (z = 0; z < (sizeof cli->challenge) - 2; z++) {
252 1242880
                AZ(VRND_RandomCrypto(&u, sizeof u));
253 1242880
                cli->challenge[z] = (u % 26) + 'a';
254 1242880
        }
255 38840
        cli->challenge[z++] = '\n';
256 38840
        cli->challenge[z] = '\0';
257 38840
        VCLI_Out(cli, "%s", cli->challenge);
258 38840
        VCLI_Out(cli, "\nAuthentication required.\n");
259 38840
        VCLI_SetResult(cli, CLIS_AUTH);
260 38840
}
261
262
/*--------------------------------------------------------------------
263
 * Validate the authentication
264
 */
265
266
static void
267 38840
mcf_auth(struct cli *cli, const char *const *av, void *priv)
268
{
269
        int fd;
270
        char buf[CLI_AUTH_RESPONSE_LEN + 1];
271
272 38840
        AN(av[2]);
273 38840
        (void)priv;
274 38840
        if (secret_file == NULL) {
275 0
                VCLI_Out(cli, "Secret file not configured\n");
276 0
                VCLI_SetResult(cli, CLIS_CANT);
277 0
                return;
278
        }
279 38840
        VJ_master(JAIL_MASTER_FILE);
280 38840
        fd = open(secret_file, O_RDONLY);
281 38840
        if (fd < 0) {
282 0
                VCLI_Out(cli, "Cannot open secret file (%s)\n",
283 0
                    VAS_errtxt(errno));
284 0
                VCLI_SetResult(cli, CLIS_CANT);
285 0
                VJ_master(JAIL_MASTER_LOW);
286 0
                return;
287
        }
288 38840
        VJ_master(JAIL_MASTER_LOW);
289 38840
        MCH_TrackHighFd(fd);
290 38840
        VCLI_AuthResponse(fd, cli->challenge, buf);
291 38840
        closefd(&fd);
292 38840
        if (strcasecmp(buf, av[2])) {
293 0
                MGT_Complain(C_SECURITY,
294 0
                    "CLI Authentication failure from %s", cli->ident);
295 0
                VCLI_SetResult(cli, CLIS_CLOSE);
296 0
                return;
297
        }
298 38840
        cli->auth = MCF_AUTH;
299 38840
        memset(cli->challenge, 0, sizeof cli->challenge);
300 38840
        VCLI_SetResult(cli, CLIS_OK);
301 38840
        mcf_banner(cli, av, priv);
302 38840
}
303
304
/*--------------------------------------------------------------------*/
305
306
static void v_matchproto_(cli_func_t)
307 840
mcf_help(struct cli *cli, const char * const *av, void *priv)
308
{
309 840
        if (cli_o <= 0)
310 560
                VCLS_func_help(cli, av, priv);
311
        else
312 280
                mcf_askchild(cli, av, priv);
313 840
}
314
315
static void v_matchproto_(cli_func_t)
316 280
mcf_help_json(struct cli *cli, const char * const *av, void *priv)
317
{
318 280
        if (cli_o <= 0)
319 0
                VCLS_func_help_json(cli, av, priv);
320
        else
321 280
                mcf_askchild(cli, av, priv);
322 280
}
323
324
static struct cli_proto cli_auth[] = {
325
        { CLICMD_HELP,          "", mcf_help, mcf_help_json },
326
        { CLICMD_PING,          "", VCLS_func_ping, VCLS_func_ping_json },
327
        { CLICMD_AUTH,          "", mcf_auth },
328
        { CLICMD_QUIT,          "", VCLS_func_close },
329
        { NULL }
330
};
331
332
/*--------------------------------------------------------------------*/
333
334
static void
335 532600
mgt_cli_cb_before(const struct cli *cli)
336
{
337
338 532600
        if (cli->priv == stderr)
339 160
                fprintf(stderr, "> %s\n", VSB_data(cli->cmd));
340 532600
        MGT_Complain(C_CLI, "CLI %s Rd %s", cli->ident, VSB_data(cli->cmd));
341 532600
}
342
343
static void
344 532680
mgt_cli_cb_after(const struct cli *cli)
345
{
346
347 532680
        MGT_Complain(C_CLI, "CLI %s Wr %03u %s",
348 532680
            cli->ident, cli->result, VSB_data(cli->sb));
349 532680
        if (cli->priv != stderr)
350 532440
                return;
351 240
        if (cli->result == CLIS_TRUNCATED)
352 80
                ARGV_ERR("-I file had incomplete CLI command at the end\n");
353 160
        if (cli->result != CLIS_OK && *VSB_data(cli->cmd) != '-') {
354 40
                ARGV_ERR("-I file CLI command failed (%d)\n%s\n",
355
                    cli->result, VSB_data(cli->sb));
356 0
        }
357 532560
}
358
359
/*--------------------------------------------------------------------*/
360
361
void
362 40800
mgt_cli_init_cls(void)
363
{
364
365 40800
        mgt_cls = VCLS_New(NULL);
366 40800
        AN(mgt_cls);
367 40800
        VCLS_SetHooks(mgt_cls, mgt_cli_cb_before, mgt_cli_cb_after);
368 40800
        VCLS_AddFunc(mgt_cls, MCF_NOAUTH, cli_auth);
369 40800
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_proto);
370 40800
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_debug);
371 40800
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_askchild);
372 40800
        cli_buf = VSB_new_auto();
373 40800
        AN(cli_buf);
374 40800
}
375
376
/*--------------------------------------------------------------------
377
 * Get rid of all CLI sessions
378
 */
379
380
void
381 37360
mgt_cli_close_all(void)
382
{
383
384 37360
        VCLS_Destroy(&mgt_cls);
385 37360
}
386
387
/*--------------------------------------------------------------------
388
 * Callback whenever something happens to the input fd of the session.
389
 */
390
391
static int
392 1094551
mgt_cli_callback2(const struct vev *e, int what)
393
{
394
        int i;
395
396 1094551
        (void)what;
397 1094551
        i = VCLS_Poll(mgt_cls, e->priv, 0);
398 1094551
        return (i);
399
}
400
401
/*--------------------------------------------------------------------*/
402
403
void
404 76320
mgt_cli_setup(int fdi, int fdo, int auth, const char *ident,
405
    mgt_cli_close_f *closefunc, void *priv)
406
{
407
        struct cli *cli;
408
        struct vev *ev;
409
410 76320
        cli = VCLS_AddFd(mgt_cls, fdi, fdo, closefunc, priv);
411
412 76320
        REPLACE(cli->ident, ident);
413
414 76320
        if (!auth && secret_file != NULL) {
415 38840
                cli->auth = MCF_NOAUTH;
416 38840
                mgt_cli_challenge(cli);
417 38840
        } else {
418 37480
                cli->auth = MCF_AUTH;
419 37480
                mcf_banner(cli, NULL, NULL);
420
        }
421 76320
        AZ(VSB_finish(cli->sb));
422 76320
        (void)VCLI_WriteResult(fdo, cli->result, VSB_data(cli->sb));
423
424 76320
        ev = VEV_Alloc();
425 76320
        AN(ev);
426 76320
        ev->name = cli->ident;
427 76320
        ev->fd = fdi;
428 76320
        ev->fd_flags = VEV__RD;
429 76320
        ev->callback = mgt_cli_callback2;
430 76320
        ev->priv = cli;
431 76320
        AZ(VEV_Start(mgt_evb, ev));
432 76320
}
433
434
/*--------------------------------------------------------------------*/
435
436
static struct vsb *
437 38840
sock_id(const char *pfx, int fd)
438
{
439
        struct vsb *vsb;
440
441
        char abuf1[VTCP_ADDRBUFSIZE], abuf2[VTCP_ADDRBUFSIZE];
442
        char pbuf1[VTCP_PORTBUFSIZE], pbuf2[VTCP_PORTBUFSIZE];
443
444 38840
        vsb = VSB_new_auto();
445 38840
        AN(vsb);
446 38840
        VTCP_myname(fd, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1);
447 38840
        VTCP_hisname(fd, abuf2, sizeof abuf2, pbuf2, sizeof pbuf2);
448 38840
        VSB_printf(vsb, "%s %s %s %s %s", pfx, abuf2, pbuf2, abuf1, pbuf1);
449 38840
        AZ(VSB_finish(vsb));
450 38840
        return (vsb);
451
}
452
453
/*--------------------------------------------------------------------*/
454
455
static int
456 1640
telnet_accept(const struct vev *ev, int what)
457
{
458
        struct vsb *vsb;
459
        int i;
460
461 1640
        (void)what;
462 1640
        i = accept(ev->fd, NULL, NULL);
463 1640
        if (i < 0 && errno == EBADF)
464 0
                return (1);
465 1640
        if (i < 0)
466 0
                return (0);
467
468 1640
        MCH_TrackHighFd(i);
469 1640
        vsb = sock_id("telnet", i);
470 1640
        mgt_cli_setup(i, i, 0, VSB_data(vsb), NULL, NULL);
471 1640
        VSB_destroy(&vsb);
472 1640
        return (0);
473 1640
}
474
475
void
476 37680
mgt_cli_secret(const char *S_arg)
477
{
478
        int i, fd;
479
        char buf[BUFSIZ];
480
481
        /* Save in shmem */
482 37680
        mgt_SHM_static_alloc(S_arg, strlen(S_arg) + 1L, "Arg", "-S");
483
484 37680
        VJ_master(JAIL_MASTER_FILE);
485 37680
        fd = open(S_arg, O_RDONLY);
486 37680
        if (fd < 0) {
487 0
                fprintf(stderr, "Cannot open secret-file \"%s\"\n", S_arg);
488 0
                exit(2);
489
        }
490 37680
        VJ_master(JAIL_MASTER_LOW);
491 37680
        MCH_TrackHighFd(fd);
492 37680
        i = read(fd, buf, sizeof buf);
493 37680
        if (i == 0) {
494 0
                fprintf(stderr, "Empty secret-file \"%s\"\n", S_arg);
495 0
                exit(2);
496
        }
497 37680
        if (i < 0) {
498 0
                fprintf(stderr, "Cannot read secret-file \"%s\"\n", S_arg);
499 0
                exit(2);
500
        }
501 37680
        closefd(&fd);
502 37680
        secret_file = S_arg;
503 37680
}
504
505
static int v_matchproto_(vss_resolved_f)
506 75440
mct_callback(void *priv, const struct suckaddr *sa)
507
{
508
        int sock;
509 75440
        struct vsb *vsb = priv;
510
        const char *err;
511
        char abuf[VTCP_ADDRBUFSIZE];
512
        char pbuf[VTCP_PORTBUFSIZE];
513
        struct vev *ev;
514
515 75440
        VJ_master(JAIL_MASTER_PRIVPORT);
516 75440
        sock = VTCP_listen(sa, 10, &err);
517 75440
        VJ_master(JAIL_MASTER_LOW);
518 75440
        assert(sock != 0);              // We know where stdin is
519 75440
        if (sock > 0) {
520 75440
                VTCP_myname(sock, abuf, sizeof abuf, pbuf, sizeof pbuf);
521 75440
                VSB_printf(vsb, "%s %s\n", abuf, pbuf);
522 75440
                ev = VEV_Alloc();
523 75440
                AN(ev);
524 75440
                ev->fd = sock;
525 75440
                ev->fd_flags = POLLIN;
526 75440
                ev->callback = telnet_accept;
527 75440
                AZ(VEV_Start(mgt_evb, ev));
528 75440
        }
529 75440
        return (0);
530
}
531
532
void
533 37720
mgt_cli_telnet(const char *T_arg)
534
{
535
        int error;
536
        const char *err;
537
        struct vsb *vsb;
538
539 37720
        AN(T_arg);
540 37720
        vsb = VSB_new_auto();
541 37720
        AN(vsb);
542 37720
        error = VSS_resolver(T_arg, NULL, mct_callback, vsb, &err);
543 37720
        if (err != NULL)
544 0
                ARGV_ERR("Could not resolve -T argument to address\n\t%s\n",
545
                    err);
546 37720
        AZ(error);
547 37720
        AZ(VSB_finish(vsb));
548 37720
        if (VSB_len(vsb) == 0)
549 0
                ARGV_ERR("-T %s could not be listened on.\n", T_arg);
550
        /* Save in shmem */
551 37720
        mgt_SHM_static_alloc(VSB_data(vsb), VSB_len(vsb) + 1, "Arg", "-T");
552 37720
        VSB_destroy(&vsb);
553 37720
}
554
555
/* Reverse CLI ("Master") connections --------------------------------*/
556
557
struct m_addr {
558
        unsigned                magic;
559
#define M_ADDR_MAGIC            0xbc6217ed
560
        const struct suckaddr   *sa;
561
        VTAILQ_ENTRY(m_addr)    list;
562
};
563
564
static int M_fd = -1;
565
static struct vev *M_poker, *M_conn;
566
static double M_poll = 0.1;
567
568
static VTAILQ_HEAD(,m_addr)     m_addr_list =
569
    VTAILQ_HEAD_INITIALIZER(m_addr_list);
570
571
static int v_matchproto_(mgt_cli_close_f)
572 37200
Marg_closer(void *priv)
573
{
574
575 37200
        (void)priv;
576 37200
        M_fd = -1;
577 37200
        return (0);
578
}
579
580
static int v_matchproto_(vev_cb_f)
581 37200
Marg_connect(const struct vev *e, int what)
582
{
583
        struct vsb *vsb;
584
        struct m_addr *ma;
585
586 37200
        assert(e == M_conn);
587 37200
        (void)what;
588
589 37200
        M_fd = VTCP_connected(M_fd);
590 37200
        if (M_fd < 0) {
591 0
                MGT_Complain(C_INFO, "Could not connect to CLI-master: %s",
592 0
                        VAS_errtxt(errno));
593 0
                ma = VTAILQ_FIRST(&m_addr_list);
594 0
                AN(ma);
595 0
                VTAILQ_REMOVE(&m_addr_list, ma, list);
596 0
                VTAILQ_INSERT_TAIL(&m_addr_list, ma, list);
597 0
                if (M_poll < 10)
598 0
                        M_poll++;
599 0
                return (1);
600
        }
601 37200
        vsb = sock_id("master", M_fd);
602 37200
        mgt_cli_setup(M_fd, M_fd, 0, VSB_data(vsb), Marg_closer, NULL);
603 37200
        VSB_destroy(&vsb);
604 37200
        M_poll = 1;
605 37200
        return (1);
606 37200
}
607
608
static int v_matchproto_(vev_cb_f)
609 125649
Marg_poker(const struct vev *e, int what)
610
{
611
        int s;
612
        struct m_addr *ma;
613
614 125649
        assert(e == M_poker);
615 125649
        (void)what;
616
617 125649
        M_poker->timeout = M_poll;      /* XXX nasty ? */
618 125649
        if (M_fd > 0)
619 88449
                return (0);
620
621 37200
        ma = VTAILQ_FIRST(&m_addr_list);
622 37200
        AN(ma);
623
624
        /* Try to connect asynchronously */
625 37200
        s = VTCP_connect(ma->sa, -1);
626 37200
        if (s < 0)
627 0
                return (0);
628
629 37200
        MCH_TrackHighFd(s);
630
631 37200
        M_conn = VEV_Alloc();
632 37200
        AN(M_conn);
633 37200
        M_conn->callback = Marg_connect;
634 37200
        M_conn->name = "-M connector";
635 37200
        M_conn->fd_flags = VEV__WR;
636 37200
        M_conn->fd = s;
637 37200
        M_fd = s;
638 37200
        AZ(VEV_Start(mgt_evb, M_conn));
639 37200
        return (0);
640 125649
}
641
642
static int v_matchproto_(vss_resolved_f)
643 37200
marg_cb(void *priv, const struct suckaddr *sa)
644
{
645
        struct m_addr *ma;
646
647 37200
        (void)priv;
648 37200
        ALLOC_OBJ(ma, M_ADDR_MAGIC);
649 37200
        AN(ma);
650 37200
        ma->sa = VSA_Clone(sa);
651 37200
        VTAILQ_INSERT_TAIL(&m_addr_list, ma, list);
652 37200
        return (0);
653
}
654
655
void
656 37200
mgt_cli_master(const char *M_arg)
657
{
658
        const char *err;
659
        int error;
660
661 37200
        AN(M_arg);
662
663 37200
        error = VSS_resolver(M_arg, NULL, marg_cb, NULL, &err);
664 37200
        if (err != NULL)
665 0
                ARGV_ERR("Could not resolve -M argument to address\n\t%s\n",
666
                    err);
667 37200
        AZ(error);
668 37200
        if (VTAILQ_EMPTY(&m_addr_list))
669 0
                ARGV_ERR("Could not resolve -M argument to address\n");
670 37200
        AZ(M_poker);
671 37200
        M_poker = VEV_Alloc();
672 37200
        AN(M_poker);
673 37200
        M_poker->timeout = M_poll;
674 37200
        M_poker->callback = Marg_poker;
675 37200
        M_poker->name = "-M poker";
676 37200
        AZ(VEV_Start(mgt_evb, M_poker));
677 37200
}
678
679
static int
680 14160
cli_cmp(const void *a, const void *b)
681
{
682 14160
        struct cli_cmd_desc * const * const aa = a;
683 14160
        struct cli_cmd_desc * const * const bb = b;
684
685 14160
        return (strcmp((*aa)->request, (*bb)->request));
686
}
687
688
void
689 80
mgt_DumpRstCli(void)
690
{
691
        const struct cli_cmd_desc *cp;
692
        const char *p;
693
        int z;
694
        size_t j;
695
696 80
        qsort(cmds, ncmds, sizeof cmds[0], cli_cmp);
697 3200
        for (z = 0; z < ncmds; z++, cp++) {
698 3120
                cp = cmds[z];
699 3120
                if (!strncmp(cp->request, "debug.", 6))
700 800
                        continue;
701 2320
                printf(".. _ref_cli_");
702 20880
                for (p = cp->request; *p; p++)
703 18560
                        fputc(*p == '.' ? '_' : *p, stdout);
704 2320
                printf(":\n\n");
705 2320
                printf("%s\n", cp->syntax);
706 58000
                for (j = 0; j < strlen(cp->syntax); j++)
707 55680
                        printf("~");
708 2320
                printf("\n");
709 2320
                printf("  %s\n", cp->help);
710 2320
                if (*cp->doc != '\0')
711 1440
                        printf("\n%s\n", cp->doc);
712
713 2320
                printf("\n");
714 2320
        }
715 80
}