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