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 76240
mcf_banner(struct cli *cli, const char *const *av, void *priv)
78
{
79
80 76240
        (void)av;
81 76240
        (void)priv;
82 76240
        VCLI_Out(cli, "-----------------------------\n");
83 76240
        VCLI_Out(cli, "Varnish Cache CLI 1.0\n");
84 76240
        VCLI_Out(cli, "-----------------------------\n");
85 76240
        VCLI_Out(cli, "%s\n", VSB_data(vident) + 1);
86 76240
        VCLI_Out(cli, "%s\n", VCS_String("V"));
87 76240
        VCLI_Out(cli, "\n");
88 76240
        VCLI_Out(cli, "Type 'help' for command list.\n");
89 76240
        VCLI_Out(cli, "Type 'quit' to close CLI session.\n");
90 76240
        if (!MCH_Running())
91 74960
                VCLI_Out(cli, "Type 'start' to launch worker process.\n");
92 76240
        VCLI_SetResult(cli, CLIS_OK);
93 76240
}
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 160440
mcf_askchild(struct cli *cli, const char * const *av, void *priv)
126
{
127
        int i;
128
        char *q;
129
        unsigned u;
130
131 160440
        (void)priv;
132
        /*
133
         * Command not recognized in master, try cacher if it is
134
         * running.
135
         */
136 160440
        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 157800
        VSB_clear(cli_buf);
145 370800
        for (i = 1; av[i] != NULL; i++) {
146 213000
                VSB_quote(cli_buf, av[i], strlen(av[i]), 0);
147 213000
                VSB_putc(cli_buf, ' ');
148 213000
        }
149 157800
        VSB_putc(cli_buf, '\n');
150 157800
        AZ(VSB_finish(cli_buf));
151 157800
        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 157800
        if (VCLI_ReadResult(cli_i, &u, &q, mgt_param.cli_timeout))
158 40
                MCH_Cli_Fail();
159 157800
        VCLI_SetResult(cli, u);
160 157800
        VCLI_Out(cli, "%s", q);
161 157800
        free(q);
162 160440
}
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 145943
mgt_cli_askchild(unsigned *status, char **resp, const char *fmt, ...)
179
{
180
        int i;
181
        va_list ap;
182
        unsigned u;
183
184 145943
        AN(status);
185 145943
        VSB_clear(cli_buf);
186
187 145943
        if (resp != NULL)
188 145943
                *resp = NULL;
189 145943
        *status = 0;
190 145943
        if (cli_i < 0 || cli_o < 0) {
191 0
                *status = CLIS_CANT;
192 0
                return (CLIS_CANT);
193
        }
194 145943
        va_start(ap, fmt);
195 145943
        AZ(VSB_vprintf(cli_buf, fmt, ap));
196 145943
        va_end(ap);
197 145943
        AZ(VSB_finish(cli_buf));
198 145943
        i = VSB_len(cli_buf);
199 145943
        assert(i > 0 && VSB_data(cli_buf)[i - 1] == '\n');
200 145943
        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 145943
        if (VCLI_ReadResult(cli_i, &u, resp, mgt_param.cli_timeout))
209 40
                MCH_Cli_Fail();
210 145943
        *status = u;
211 145943
        return (u == CLIS_OK || u == CLIS_TRUNCATED ? 0 : u);
212 145943
}
213
214
/*--------------------------------------------------------------------*/
215
216
unsigned
217 37560
mgt_cli_start_child(int fd, double tmo)
218
{
219
        unsigned u;
220
221 37560
        cli_i = fd;
222 37560
        cli_o = fd;
223 37560
        if (VCLI_ReadResult(cli_i, &u, NULL, tmo)) {
224 320
                return (CLIS_COMMS);
225
        }
226 37240
        return (u);
227 37560
}
228
229
/*--------------------------------------------------------------------*/
230
231
void
232 37560
mgt_cli_stop_child(void)
233
{
234
235 37560
        cli_i = -1;
236 37560
        cli_o = -1;
237
        /* XXX: kick any users */
238 37560
}
239
240
/*--------------------------------------------------------------------
241
 * Generate a random challenge
242
 */
243
244
static void
245 38760
mgt_cli_challenge(struct cli *cli)
246
{
247
        size_t z;
248
        uint8_t u;
249
250 38760
        AZ(VRND_RandomCrypto(cli->challenge, sizeof cli->challenge - 2));
251 1279080
        for (z = 0; z < (sizeof cli->challenge) - 2; z++) {
252 1240320
                AZ(VRND_RandomCrypto(&u, sizeof u));
253 1240320
                cli->challenge[z] = (u % 26) + 'a';
254 1240320
        }
255 38760
        cli->challenge[z++] = '\n';
256 38760
        cli->challenge[z] = '\0';
257 38760
        VCLI_Out(cli, "%s", cli->challenge);
258 38760
        VCLI_Out(cli, "\nAuthentication required.\n");
259 38760
        VCLI_SetResult(cli, CLIS_AUTH);
260 38760
}
261
262
/*--------------------------------------------------------------------
263
 * Validate the authentication
264
 */
265
266
static void
267 38760
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 38760
        AN(av[2]);
273 38760
        (void)priv;
274 38760
        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 38760
        VJ_master(JAIL_MASTER_FILE);
280 38760
        fd = open(secret_file, O_RDONLY);
281 38760
        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 38760
        VJ_master(JAIL_MASTER_LOW);
289 38760
        MCH_TrackHighFd(fd);
290 38760
        VCLI_AuthResponse(fd, cli->challenge, buf);
291 38760
        closefd(&fd);
292 38760
        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 38760
        cli->auth = MCF_AUTH;
299 38760
        memset(cli->challenge, 0, sizeof cli->challenge);
300 38760
        VCLI_SetResult(cli, CLIS_OK);
301 38760
        mcf_banner(cli, av, priv);
302 38760
}
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 531920
mgt_cli_cb_before(const struct cli *cli)
336
{
337
338 531920
        if (cli->priv == stderr)
339 160
                fprintf(stderr, "> %s\n", VSB_data(cli->cmd));
340 531920
        MGT_Complain(C_CLI, "CLI %s Rd %s", cli->ident, VSB_data(cli->cmd));
341 531920
}
342
343
static void
344 532000
mgt_cli_cb_after(const struct cli *cli)
345
{
346
347 532000
        MGT_Complain(C_CLI, "CLI %s Wr %03u %s",
348 532000
            cli->ident, cli->result, VSB_data(cli->sb));
349 532000
        if (cli->priv != stderr)
350 531760
                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 531880
}
358
359
/*--------------------------------------------------------------------*/
360
361
void
362 40760
mgt_cli_init_cls(void)
363
{
364
365 40760
        mgt_cls = VCLS_New(NULL);
366 40760
        AN(mgt_cls);
367 40760
        VCLS_SetHooks(mgt_cls, mgt_cli_cb_before, mgt_cli_cb_after);
368 40760
        VCLS_AddFunc(mgt_cls, MCF_NOAUTH, cli_auth);
369 40760
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_proto);
370 40760
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_debug);
371 40760
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_askchild);
372 40760
        cli_buf = VSB_new_auto();
373 40760
        AN(cli_buf);
374 40760
}
375
376
/*--------------------------------------------------------------------
377
 * Get rid of all CLI sessions
378
 */
379
380
void
381 37320
mgt_cli_close_all(void)
382
{
383
384 37320
        VCLS_Destroy(&mgt_cls);
385 37320
}
386
387
/*--------------------------------------------------------------------
388
 * Callback whenever something happens to the input fd of the session.
389
 */
390
391
static int
392 1093043
mgt_cli_callback2(const struct vev *e, int what)
393
{
394
        int i;
395
396 1093043
        (void)what;
397 1093043
        i = VCLS_Poll(mgt_cls, e->priv, 0);
398 1093043
        return (i);
399
}
400
401
/*--------------------------------------------------------------------*/
402
403
void
404 76200
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 76200
        cli = VCLS_AddFd(mgt_cls, fdi, fdo, closefunc, priv);
411
412 76200
        REPLACE(cli->ident, ident);
413
414 76200
        if (!auth && secret_file != NULL) {
415 38760
                cli->auth = MCF_NOAUTH;
416 38760
                mgt_cli_challenge(cli);
417 38760
        } else {
418 37440
                cli->auth = MCF_AUTH;
419 37440
                mcf_banner(cli, NULL, NULL);
420
        }
421 76200
        AZ(VSB_finish(cli->sb));
422 76200
        (void)VCLI_WriteResult(fdo, cli->result, VSB_data(cli->sb));
423
424 76200
        ev = VEV_Alloc();
425 76200
        AN(ev);
426 76200
        ev->name = cli->ident;
427 76200
        ev->fd = fdi;
428 76200
        ev->fd_flags = VEV__RD;
429 76200
        ev->callback = mgt_cli_callback2;
430 76200
        ev->priv = cli;
431 76200
        AZ(VEV_Start(mgt_evb, ev));
432 76200
}
433
434
/*--------------------------------------------------------------------*/
435
436
static struct vsb *
437 38760
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 38760
        vsb = VSB_new_auto();
445 38760
        AN(vsb);
446 38760
        VTCP_myname(fd, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1);
447 38760
        VTCP_hisname(fd, abuf2, sizeof abuf2, pbuf2, sizeof pbuf2);
448 38760
        VSB_printf(vsb, "%s %s %s %s %s", pfx, abuf2, pbuf2, abuf1, pbuf1);
449 38760
        AZ(VSB_finish(vsb));
450 38760
        return (vsb);
451
}
452
453
/*--------------------------------------------------------------------*/
454
455
static int
456 1600
telnet_accept(const struct vev *ev, int what)
457
{
458
        struct vsb *vsb;
459
        int i;
460
461 1600
        (void)what;
462 1600
        i = accept(ev->fd, NULL, NULL);
463 1600
        if (i < 0 && errno == EBADF)
464 0
                return (1);
465 1600
        if (i < 0)
466 0
                return (0);
467
468 1600
        MCH_TrackHighFd(i);
469 1600
        vsb = sock_id("telnet", i);
470 1600
        mgt_cli_setup(i, i, 0, VSB_data(vsb), NULL, NULL);
471 1600
        VSB_destroy(&vsb);
472 1600
        return (0);
473 1600
}
474
475
void
476 37640
mgt_cli_secret(const char *S_arg)
477
{
478
        int i, fd;
479
        char buf[BUFSIZ];
480
481
        /* Save in shmem */
482 37640
        mgt_SHM_static_alloc(S_arg, strlen(S_arg) + 1L, "Arg", "-S");
483
484 37640
        VJ_master(JAIL_MASTER_FILE);
485 37640
        fd = open(S_arg, O_RDONLY);
486 37640
        if (fd < 0) {
487 0
                fprintf(stderr, "Cannot open secret-file \"%s\"\n", S_arg);
488 0
                exit(2);
489
        }
490 37640
        VJ_master(JAIL_MASTER_LOW);
491 37640
        MCH_TrackHighFd(fd);
492 37640
        i = read(fd, buf, sizeof buf);
493 37640
        if (i == 0) {
494 0
                fprintf(stderr, "Empty secret-file \"%s\"\n", S_arg);
495 0
                exit(2);
496
        }
497 37640
        if (i < 0) {
498 0
                fprintf(stderr, "Cannot read secret-file \"%s\"\n", S_arg);
499 0
                exit(2);
500
        }
501 37640
        closefd(&fd);
502 37640
        secret_file = S_arg;
503 37640
}
504
505
static int v_matchproto_(vss_resolved_f)
506 75360
mct_callback(void *priv, const struct suckaddr *sa)
507
{
508
        int sock;
509 75360
        struct vsb *vsb = priv;
510
        const char *err;
511
        char abuf[VTCP_ADDRBUFSIZE];
512
        char pbuf[VTCP_PORTBUFSIZE];
513
        struct vev *ev;
514
515 75360
        VJ_master(JAIL_MASTER_PRIVPORT);
516 75360
        sock = VTCP_listen(sa, 10, &err);
517 75360
        VJ_master(JAIL_MASTER_LOW);
518 75360
        assert(sock != 0);              // We know where stdin is
519 75360
        if (sock > 0) {
520 75360
                VTCP_myname(sock, abuf, sizeof abuf, pbuf, sizeof pbuf);
521 75360
                VSB_printf(vsb, "%s %s\n", abuf, pbuf);
522 75360
                ev = VEV_Alloc();
523 75360
                AN(ev);
524 75360
                ev->fd = sock;
525 75360
                ev->fd_flags = POLLIN;
526 75360
                ev->callback = telnet_accept;
527 75360
                AZ(VEV_Start(mgt_evb, ev));
528 75360
        }
529 75360
        return (0);
530
}
531
532
void
533 37680
mgt_cli_telnet(const char *T_arg)
534
{
535
        int error;
536
        const char *err;
537
        struct vsb *vsb;
538
539 37680
        AN(T_arg);
540 37680
        vsb = VSB_new_auto();
541 37680
        AN(vsb);
542 37680
        error = VSS_resolver(T_arg, NULL, mct_callback, vsb, &err);
543 37680
        if (err != NULL)
544 0
                ARGV_ERR("Could not resolve -T argument to address\n\t%s\n",
545
                    err);
546 37680
        AZ(error);
547 37680
        AZ(VSB_finish(vsb));
548 37680
        if (VSB_len(vsb) == 0)
549 0
                ARGV_ERR("-T %s could not be listened on.\n", T_arg);
550
        /* Save in shmem */
551 37680
        mgt_SHM_static_alloc(VSB_data(vsb), VSB_len(vsb) + 1, "Arg", "-T");
552 37680
        VSB_destroy(&vsb);
553 37680
}
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 37160
Marg_closer(void *priv)
573
{
574
575 37160
        (void)priv;
576 37160
        M_fd = -1;
577 37160
        return (0);
578
}
579
580
static int v_matchproto_(vev_cb_f)
581 37160
Marg_connect(const struct vev *e, int what)
582
{
583
        struct vsb *vsb;
584
        struct m_addr *ma;
585
586 37160
        assert(e == M_conn);
587 37160
        (void)what;
588
589 37160
        M_fd = VTCP_connected(M_fd);
590 37160
        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 37160
        vsb = sock_id("master", M_fd);
602 37160
        mgt_cli_setup(M_fd, M_fd, 0, VSB_data(vsb), Marg_closer, NULL);
603 37160
        VSB_destroy(&vsb);
604 37160
        M_poll = 1;
605 37160
        return (1);
606 37160
}
607
608
static int v_matchproto_(vev_cb_f)
609 125411
Marg_poker(const struct vev *e, int what)
610
{
611
        int s;
612
        struct m_addr *ma;
613
614 125411
        assert(e == M_poker);
615 125411
        (void)what;
616
617 125411
        M_poker->timeout = M_poll;      /* XXX nasty ? */
618 125411
        if (M_fd > 0)
619 88251
                return (0);
620
621 37160
        ma = VTAILQ_FIRST(&m_addr_list);
622 37160
        AN(ma);
623
624
        /* Try to connect asynchronously */
625 37160
        s = VTCP_connect(ma->sa, -1);
626 37160
        if (s < 0)
627 0
                return (0);
628
629 37160
        MCH_TrackHighFd(s);
630
631 37160
        M_conn = VEV_Alloc();
632 37160
        AN(M_conn);
633 37160
        M_conn->callback = Marg_connect;
634 37160
        M_conn->name = "-M connector";
635 37160
        M_conn->fd_flags = VEV__WR;
636 37160
        M_conn->fd = s;
637 37160
        M_fd = s;
638 37160
        AZ(VEV_Start(mgt_evb, M_conn));
639 37160
        return (0);
640 125411
}
641
642
static int v_matchproto_(vss_resolved_f)
643 37160
marg_cb(void *priv, const struct suckaddr *sa)
644
{
645
        struct m_addr *ma;
646
647 37160
        (void)priv;
648 37160
        ALLOC_OBJ(ma, M_ADDR_MAGIC);
649 37160
        AN(ma);
650 37160
        ma->sa = VSA_Clone(sa);
651 37160
        VTAILQ_INSERT_TAIL(&m_addr_list, ma, list);
652 37160
        return (0);
653
}
654
655
void
656 37160
mgt_cli_master(const char *M_arg)
657
{
658
        const char *err;
659
        int error;
660
661 37160
        AN(M_arg);
662
663 37160
        error = VSS_resolver(M_arg, NULL, marg_cb, NULL, &err);
664 37160
        if (err != NULL)
665 0
                ARGV_ERR("Could not resolve -M argument to address\n\t%s\n",
666
                    err);
667 37160
        AZ(error);
668 37160
        if (VTAILQ_EMPTY(&m_addr_list))
669 0
                ARGV_ERR("Could not resolve -M argument to address\n");
670 37160
        AZ(M_poker);
671 37160
        M_poker = VEV_Alloc();
672 37160
        AN(M_poker);
673 37160
        M_poker->timeout = M_poll;
674 37160
        M_poker->callback = Marg_poker;
675 37160
        M_poker->name = "-M poker";
676 37160
        AZ(VEV_Start(mgt_evb, M_poker));
677 37160
}
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
}