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