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