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 2862
mcf_banner(struct cli *cli, const char *const *av, void *priv)
77
{
78
79
        (void)av;
80
        (void)priv;
81 2862
        VCLI_Out(cli, "-----------------------------\n");
82 2862
        VCLI_Out(cli, "Varnish Cache CLI 1.0\n");
83 2862
        VCLI_Out(cli, "-----------------------------\n");
84 2862
        VCLI_Out(cli, "%s\n", VSB_data(vident) + 1);
85 2862
        VCLI_Out(cli, "%s\n", VCS_version);
86 2862
        VCLI_Out(cli, "\n");
87 2862
        VCLI_Out(cli, "Type 'help' for command list.\n");
88 2862
        VCLI_Out(cli, "Type 'quit' to close CLI session.\n");
89 2862
        if (!MCH_Running())
90 2842
                VCLI_Out(cli, "Type 'start' to launch worker process.\n");
91 2862
        VCLI_SetResult(cli, CLIS_OK);
92 2862
}
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 6030
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 6030
        if (cli_o <= 0) {
134 112
                VCLI_SetResult(cli, CLIS_UNKNOWN);
135 112
                VCLI_Out(cli,
136
                    "Unknown request in manager process "
137
                    "(child not running).\n"
138
                    "Type 'help' for more info.");
139 112
                return;
140
        }
141 5918
        vsb = VSB_new_auto();
142 13798
        for (i = 1; av[i] != NULL; i++) {
143 7880
                VSB_quote(vsb, av[i], strlen(av[i]), 0);
144 7880
                VSB_putc(vsb, ' ');
145
        }
146 5918
        VSB_putc(vsb, '\n');
147 5918
        AZ(VSB_finish(vsb));
148 5918
        i = write(cli_o, VSB_data(vsb), VSB_len(vsb));
149 5918
        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 5918
        VSB_destroy(&vsb);
157 5918
        if (VCLI_ReadResult(cli_i, &u, &q, mgt_param.cli_timeout))
158 2
                MCH_Cli_Fail();
159 5918
        VCLI_SetResult(cli, u);
160 5918
        VCLI_Out(cli, "%s", q);
161 5918
        free(q);
162
}
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 5523
mgt_cli_askchild(unsigned *status, char **resp, const char *fmt, ...)
179
{
180
        int i, j;
181
        va_list ap;
182
        unsigned u;
183
184 5523
        if (cli_buf == NULL) {
185 1354
                cli_buf = VSB_new_auto();
186 1354
                AN(cli_buf);
187
        } else {
188 4169
                VSB_clear(cli_buf);
189
        }
190
191 5523
        if (resp != NULL)
192 5523
                *resp = NULL;
193 5523
        if (status != NULL)
194 5523
                *status = 0;
195 5523
        if (cli_i < 0 || cli_o < 0) {
196 0
                if (status != NULL)
197 0
                        *status = CLIS_CANT;
198 0
                return (CLIS_CANT);
199
        }
200 5523
        va_start(ap, fmt);
201 5523
        AZ(VSB_vprintf(cli_buf, fmt, ap));
202 5523
        va_end(ap);
203 5523
        AZ(VSB_finish(cli_buf));
204 5523
        i = VSB_len(cli_buf);
205 5523
        assert(i > 0 && VSB_data(cli_buf)[i - 1] == '\n');
206 5523
        j = write(cli_o, VSB_data(cli_buf), i);
207 5523
        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 5523
        if (VCLI_ReadResult(cli_i, &u, resp, mgt_param.cli_timeout))
217 0
                MCH_Cli_Fail();
218 5523
        if (status != NULL)
219 5523
                *status = u;
220 5523
        return (u == CLIS_OK ? 0 : u);
221
}
222
223
/*--------------------------------------------------------------------*/
224
225
void
226 1404
mgt_cli_start_child(int fdi, int fdo)
227
{
228
229 1404
        cli_i = fdi;
230 1404
        cli_o = fdo;
231 1404
}
232
233
/*--------------------------------------------------------------------*/
234
235
void
236 1404
mgt_cli_stop_child(void)
237
{
238
239 1404
        cli_i = -1;
240 1404
        cli_o = -1;
241
        /* XXX: kick any users */
242 1404
}
243
244
/*--------------------------------------------------------------------
245
 * Generate a random challenge
246
 */
247
248
static void
249 1440
mgt_cli_challenge(struct cli *cli)
250
{
251
        int i;
252
        uint8_t u;
253
254 1440
        AZ(VRND_RandomCrypto(cli->challenge, sizeof cli->challenge - 2));
255 47520
        for (i = 0; i < (sizeof cli->challenge) - 2; i++) {
256 46080
                AZ(VRND_RandomCrypto(&u, sizeof u));
257 46080
                cli->challenge[i] = (u % 26) + 'a';
258
        }
259 1440
        cli->challenge[i++] = '\n';
260 1440
        cli->challenge[i] = '\0';
261 1440
        VCLI_Out(cli, "%s", cli->challenge);
262 1440
        VCLI_Out(cli, "\nAuthentication required.\n");
263 1440
        VCLI_SetResult(cli, CLIS_AUTH);
264 1440
}
265
266
/*--------------------------------------------------------------------
267
 * Validate the authentication
268
 */
269
270
static void
271 1440
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 1440
        AN(av[2]);
277
        (void)priv;
278 1440
        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 1440
        VJ_master(JAIL_MASTER_FILE);
284 1440
        fd = open(secret_file, O_RDONLY);
285 1440
        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 1440
        VJ_master(JAIL_MASTER_LOW);
293 1440
        MCH_TrackHighFd(fd);
294 1440
        VCLI_AuthResponse(fd, cli->challenge, buf);
295 1440
        closefd(&fd);
296 1440
        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 1440
        cli->auth = MCF_AUTH;
303 1440
        memset(cli->challenge, 0, sizeof cli->challenge);
304 1440
        VCLI_SetResult(cli, CLIS_OK);
305 1440
        mcf_banner(cli, av, priv);
306
}
307
308
/*--------------------------------------------------------------------*/
309
310
static void v_matchproto_(cli_func_t)
311 34
mcf_help(struct cli *cli, const char * const *av, void *priv)
312
{
313 34
        if (cli_o <= 0)
314 24
                VCLS_func_help(cli, av, priv);
315
        else
316 10
                mcf_askchild(cli, av, priv);
317 34
}
318
319
static void v_matchproto_(cli_func_t)
320 4
mcf_help_json(struct cli *cli, const char * const *av, void *priv)
321
{
322 4
        if (cli_o <= 0)
323 0
                VCLS_func_help_json(cli, av, priv);
324
        else
325 4
                mcf_askchild(cli, av, priv);
326 4
}
327
328
static struct cli_proto cli_auth[] = {
329
        { CLICMD_HELP,          "", mcf_help, mcf_help_json },
330
        { CLICMD_PING,          "", VCLS_func_ping, VCLS_func_ping_json },
331
        { CLICMD_AUTH,          "", mcf_auth },
332
        { CLICMD_QUIT,          "", VCLS_func_close },
333
        { NULL }
334
};
335
336
/*--------------------------------------------------------------------*/
337
338
static void
339 19534
mgt_cli_cb_before(const struct cli *cli)
340
{
341
342 19534
        if (cli->priv == stderr)
343 12
                fprintf(stderr, "> %s\n", VSB_data(cli->cmd));
344 19534
        MGT_Complain(C_CLI, "CLI %s Rd %s", cli->ident, VSB_data(cli->cmd));
345 19534
}
346
347
static void
348 19534
mgt_cli_cb_after(const struct cli *cli)
349
{
350
351 39068
        MGT_Complain(C_CLI, "CLI %s Wr %03u %s",
352 39068
            cli->ident, cli->result, VSB_data(cli->sb));
353 19546
        if (cli->priv == stderr &&
354 20
            cli->result != CLIS_OK && *VSB_data(cli->cmd) != '-') {
355 8
                MGT_Complain(C_ERR, "-I file CLI command failed (%d)\n%s\n",
356 8
                    cli->result, VSB_data(cli->sb));
357 4
                exit(2);
358
        }
359 19530
}
360
361
/*--------------------------------------------------------------------*/
362
363
void
364 1622
mgt_cli_init_cls(void)
365
{
366
367 1622
        mgt_cls = VCLS_New(NULL);
368 1622
        AN(mgt_cls);
369 1622
        VCLS_SetLimit(mgt_cls, &mgt_param.cli_limit);
370 1622
        VCLS_SetHooks(mgt_cls, mgt_cli_cb_before, mgt_cli_cb_after);
371 1622
        VCLS_AddFunc(mgt_cls, MCF_NOAUTH, cli_auth);
372 1622
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_proto);
373 1622
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_debug);
374 1622
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_askchild);
375 1622
}
376
377
/*--------------------------------------------------------------------
378
 * Get rid of all CLI sessions
379
 */
380
381
void
382 1418
mgt_cli_close_all(void)
383
{
384
385 1418
        VCLS_Destroy(&mgt_cls);
386 1418
}
387
388
/*--------------------------------------------------------------------
389
 * Callback whenever something happens to the input fd of the session.
390
 */
391
392
static int
393 39389
mgt_cli_callback2(const struct vev *e, int what)
394
{
395
        int i;
396
397
        (void)what;
398 39389
        i = VCLS_Poll(mgt_cls, e->priv, 0);
399 39385
        return (i);
400
}
401
402
/*--------------------------------------------------------------------*/
403
404
void
405 2860
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 2860
        cli = VCLS_AddFd(mgt_cls, fdi, fdo, closefunc, priv);
412
413 2860
        REPLACE(cli->ident, ident);
414
415 2860
        if (!auth && secret_file != NULL) {
416 1440
                cli->auth = MCF_NOAUTH;
417 1440
                mgt_cli_challenge(cli);
418
        } else {
419 1420
                cli->auth = MCF_AUTH;
420 1420
                mcf_banner(cli, NULL, NULL);
421
        }
422 2860
        AZ(VSB_finish(cli->sb));
423 2860
        (void)VCLI_WriteResult(fdo, cli->result, VSB_data(cli->sb));
424
425 2860
        ev = VEV_Alloc();
426 2860
        AN(ev);
427 2860
        ev->name = cli->ident;
428 2860
        ev->fd = fdi;
429 2860
        ev->fd_flags = VEV__RD;
430 2860
        ev->callback = mgt_cli_callback2;
431 2860
        ev->priv = cli;
432 2860
        AZ(VEV_Start(mgt_evb, ev));
433 2860
}
434
435
/*--------------------------------------------------------------------*/
436
437
static struct vsb *
438 1440
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 1440
        vsb = VSB_new_auto();
446 1440
        AN(vsb);
447 1440
        VTCP_myname(fd, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1);
448 1440
        VTCP_hisname(fd, abuf2, sizeof abuf2, pbuf2, sizeof pbuf2);
449 1440
        VSB_printf(vsb, "%s %s %s %s %s", pfx, abuf2, pbuf2, abuf1, pbuf1);
450 1440
        AZ(VSB_finish(vsb));
451 1440
        return (vsb);
452
}
453
454
/*--------------------------------------------------------------------*/
455
456
static int
457 24
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 24
        addrlen = sizeof addr;
466 24
        i = accept(ev->fd, (void *)&addr, &addrlen);
467 24
        if (i < 0 && errno == EBADF)
468 0
                return (1);
469 24
        if (i < 0)
470 0
                return (0);
471
472 24
        MCH_TrackHighFd(i);
473 24
        vsb = sock_id("telnet", i);
474 24
        mgt_cli_setup(i, i, 0, VSB_data(vsb), NULL, NULL);
475 24
        VSB_destroy(&vsb);
476 24
        return (0);
477
}
478
479
void
480 1422
mgt_cli_secret(const char *S_arg)
481
{
482
        int i, fd;
483
        char buf[BUFSIZ];
484
485
        /* Save in shmem */
486 1422
        mgt_SHM_static_alloc(S_arg, strlen(S_arg) + 1L, "Arg", "-S");
487
488 1422
        VJ_master(JAIL_MASTER_FILE);
489 1422
        fd = open(S_arg, O_RDONLY);
490 1422
        if (fd < 0) {
491 0
                fprintf(stderr, "Can not open secret-file \"%s\"\n", S_arg);
492 0
                exit(2);
493
        }
494 1422
        VJ_master(JAIL_MASTER_LOW);
495 1422
        MCH_TrackHighFd(fd);
496 1422
        i = read(fd, buf, sizeof buf);
497 1422
        if (i == 0) {
498 0
                fprintf(stderr, "Empty secret-file \"%s\"\n", S_arg);
499 0
                exit(2);
500
        }
501 1422
        if (i < 0) {
502 0
                fprintf(stderr, "Can not read secret-file \"%s\"\n", S_arg);
503 0
                exit(2);
504
        }
505 1422
        closefd(&fd);
506 1422
        secret_file = S_arg;
507 1422
}
508
509
static int v_matchproto_(vss_resolved_f)
510 2844
mct_callback(void *priv, const struct suckaddr *sa)
511
{
512
        int sock;
513 2844
        struct vsb *vsb = priv;
514
        const char *err;
515
        char abuf[VTCP_ADDRBUFSIZE];
516
        char pbuf[VTCP_PORTBUFSIZE];
517
        struct vev *ev;
518
519 2844
        VJ_master(JAIL_MASTER_PRIVPORT);
520 2844
        sock = VTCP_listen(sa, 10, &err);
521 2844
        VJ_master(JAIL_MASTER_LOW);
522 2844
        assert(sock != 0);              // We know where stdin is
523 2844
        if (sock > 0) {
524 2844
                VTCP_myname(sock, abuf, sizeof abuf, pbuf, sizeof pbuf);
525 2844
                VSB_printf(vsb, "%s %s\n", abuf, pbuf);
526 2844
                ev = VEV_Alloc();
527 2844
                AN(ev);
528 2844
                ev->fd = sock;
529 2844
                ev->fd_flags = POLLIN;
530 2844
                ev->callback = telnet_accept;
531 2844
                AZ(VEV_Start(mgt_evb, ev));
532
        }
533 2844
        return (0);
534
}
535
536
void
537 1422
mgt_cli_telnet(const char *T_arg)
538
{
539
        int error;
540
        const char *err;
541
        struct vsb *vsb;
542
543 1422
        AN(T_arg);
544 1422
        vsb = VSB_new_auto();
545 1422
        AN(vsb);
546 1422
        error = VSS_resolver(T_arg, NULL, mct_callback, vsb, &err);
547 1422
        if (err != NULL)
548 0
                ARGV_ERR("Could not resolve -T argument to address\n\t%s\n",
549
                    err);
550 1422
        AZ(error);
551 1422
        AZ(VSB_finish(vsb));
552 1422
        if (VSB_len(vsb) == 0)
553 0
                ARGV_ERR("-T %s could not be listened on.\n", T_arg);
554
        /* Save in shmem */
555 1422
        mgt_SHM_static_alloc(VSB_data(vsb), VSB_len(vsb) + 1, "Arg", "-T");
556 1422
        VSB_destroy(&vsb);
557 1422
}
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 1416
Marg_closer(void *priv)
577
{
578
579
        (void)priv;
580 1416
        M_fd = -1;
581 1416
        return(0);
582
}
583
584
static int v_matchproto_(vev_cb_f)
585 1416
Marg_connect(const struct vev *e, int what)
586
{
587
        struct vsb *vsb;
588
        struct m_addr *ma;
589
590 1416
        assert(e == M_conn);
591
        (void)what;
592
593 1416
        M_fd = VTCP_connected(M_fd);
594 1416
        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 1416
        vsb = sock_id("master", M_fd);
606 1416
        mgt_cli_setup(M_fd, M_fd, 0, VSB_data(vsb), Marg_closer, NULL);
607 1416
        VSB_destroy(&vsb);
608 1416
        M_poll = 1;
609 1416
        return (1);
610
}
611
612
static int v_matchproto_(vev_cb_f)
613 8831
Marg_poker(const struct vev *e, int what)
614
{
615
        int s;
616
        struct m_addr *ma;
617
618 8831
        assert(e == M_poker);
619
        (void)what;
620
621 8831
        M_poker->timeout = M_poll;      /* XXX nasty ? */
622 8831
        if (M_fd > 0)
623 7415
                return (0);
624
625 1416
        ma = VTAILQ_FIRST(&m_addr_list);
626 1416
        AN(ma);
627
628
        /* Try to connect asynchronously */
629 1416
        s = VTCP_connect(ma->sa, -1);
630 1416
        if (s < 0)
631 0
                return (0);
632
633 1416
        MCH_TrackHighFd(s);
634
635 1416
        M_conn = VEV_Alloc();
636 1416
        AN(M_conn);
637 1416
        M_conn->callback = Marg_connect;
638 1416
        M_conn->name = "-M connector";
639 1416
        M_conn->fd_flags = VEV__WR;
640 1416
        M_conn->fd = s;
641 1416
        M_fd = s;
642 1416
        AZ(VEV_Start(mgt_evb, M_conn));
643 1416
        return (0);
644
}
645
646
static int v_matchproto_(vss_resolved_f)
647 1416
marg_cb(void *priv, const struct suckaddr *sa)
648
{
649
        struct m_addr *ma;
650
651
        (void)priv;
652 1416
        ALLOC_OBJ(ma, M_ADDR_MAGIC);
653 1416
        AN(ma);
654 1416
        ma->sa = VSA_Clone(sa);
655 1416
        VTAILQ_INSERT_TAIL(&m_addr_list, ma, list);
656 1416
        return(0);
657
}
658
659
void
660 1416
mgt_cli_master(const char *M_arg)
661
{
662
        const char *err;
663
        int error;
664
665 1416
        AN(M_arg);
666
667 1416
        error = VSS_resolver(M_arg, NULL, marg_cb, NULL, &err);
668 1416
        if (err != NULL)
669 0
                ARGV_ERR("Could not resolve -M argument to address\n\t%s\n",
670
                    err);
671 1416
        AZ(error);
672 1416
        if (VTAILQ_EMPTY(&m_addr_list))
673 0
                ARGV_ERR("Could not resolve -M argument to address\n");
674 1416
        AZ(M_poker);
675 1416
        M_poker = VEV_Alloc();
676 1416
        AN(M_poker);
677 1416
        M_poker->timeout = M_poll;
678 1416
        M_poker->callback = Marg_poker;
679 1416
        M_poker->name = "-M poker";
680 1416
        AZ(VEV_Start(mgt_evb, M_poker));
681 1416
}
682
683
static int
684 978
cli_cmp(const void *a, const void *b)
685
{
686 978
        struct cli_cmd_desc * const * const aa = a;
687 978
        struct cli_cmd_desc * const * const bb = b;
688
689 978
        return (strcmp((*aa)->request, (*bb)->request));
690
}
691
692
void
693 6
mgt_DumpRstCli(void)
694
{
695
        const struct cli_cmd_desc *cp;
696
        int i, j;
697
698 6
        qsort(cmds, ncmds, sizeof cmds[0], cli_cmp);
699 204
        for (i = 0; i < ncmds; i++, cp++) {
700 198
                cp = cmds[i];
701 198
                if (!strncmp(cp->request, "debug.", 6))
702 48
                        continue;
703 150
                printf(".. _ref_cli_%s:\n\n", cp->syntax);
704 150
                printf("%s\n", cp->syntax);
705 3678
                for (j = 0; j < strlen(cp->syntax); j++)
706 3528
                        printf("~");
707 150
                printf("\n");
708 150
                printf("  %s\n", cp->help);
709 150
                if (*cp->doc != '\0')
710 18
                        printf("\n%s\n", cp->doc);
711
712 150
                printf("\n");
713
        }
714 6
}