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