varnish-cache/lib/libvarnish/vcli_serve.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * Stuff for handling the CLI protocol
31
 */
32
33
#include "config.h"
34
35
#include <time.h>
36
#include <ctype.h>
37
#include <poll.h>
38
#include <stdarg.h>
39
#include <stdint.h>
40
#include <stdio.h>
41
#include <stdlib.h>
42
#include <string.h>
43
#include <unistd.h>
44
45
#include "vdef.h"
46
#include "vas.h"
47
#include "vqueue.h"
48
#include "miniobj.h"
49
50
#include "vav.h"
51
#include "vcli_serve.h"
52
#include "vsb.h"
53
#include "vtim.h"
54
55
struct VCLS_fd {
56
        unsigned                        magic;
57
#define VCLS_FD_MAGIC                   0x010dbd1e
58
        VTAILQ_ENTRY(VCLS_fd)           list;
59
        int                             fdi, fdo;
60
        struct VCLS                     *cls;
61
        struct cli                      *cli, clis;
62
        cls_cb_f                        *closefunc;
63
        void                            *priv;
64
        struct vsb                      *last_arg;
65
        char                            **argv;
66
        int                             argc;
67
        char                            *match;
68
};
69
70
struct VCLS {
71
        unsigned                        magic;
72
#define VCLS_MAGIC                      0x60f044a3
73
        VTAILQ_HEAD(,VCLS_fd)           fds;
74
        unsigned                        nfd;
75
        VTAILQ_HEAD(,cli_proto)         funcs;
76
        cls_cbc_f                       *before, *after;
77
        volatile unsigned               *limit;
78
        struct cli_proto                *wildcard;
79
};
80
81
/*--------------------------------------------------------------------*/
82
83
void v_matchproto_(cli_func_t)
84 54
VCLS_func_close(struct cli *cli, const char *const *av, void *priv)
85
{
86
87 54
        (void)av;
88 54
        (void)priv;
89 54
        VCLI_Out(cli, "Closing CLI connection");
90 54
        VCLI_SetResult(cli, CLIS_CLOSE);
91 54
}
92
93
/*--------------------------------------------------------------------*/
94
95
void v_matchproto_(cli_func_t)
96 5526
VCLS_func_ping(struct cli *cli, const char * const *av, void *priv)
97
{
98
        time_t t;
99
100 5526
        (void)av;
101 5526
        (void)priv;
102 5526
        t = time(NULL);
103 5526
        VCLI_Out(cli, "PONG %jd 1.0", (intmax_t)t);
104 5526
}
105
106
void v_matchproto_(cli_func_t)
107 54
VCLS_func_ping_json(struct cli *cli, const char * const *av, void *priv)
108
{
109 54
        (void)av;
110 54
        (void)priv;
111 54
        VCLI_JSON_begin(cli, 2, av);
112 54
        VCLI_Out(cli, ", \"PONG\"\n");
113 54
        VCLI_JSON_end(cli);
114 54
}
115
116
/*--------------------------------------------------------------------*/
117
118
static void
119 5508
help_helper(struct cli *cli, struct cli_proto *clp, const char * const *av)
120
{
121 5508
        AN(clp->desc->syntax);
122 5508
        if (av[0] != NULL)
123 54
                VCLI_Out(cli, "%s\n%s\n", clp->desc->syntax, clp->desc->help);
124
        else
125 5454
                VCLI_Out(cli, "%s\n", clp->desc->syntax);
126 5508
}
127
128
void v_matchproto_(cli_func_t)
129 432
VCLS_func_help(struct cli *cli, const char * const *av, void *priv)
130
{
131
        struct cli_proto *clp;
132 432
        unsigned filter = 1, d;
133
        struct VCLS *cs;
134
135 432
        (void)priv;
136 432
        cs = cli->cls;
137 432
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
138
139 540
        for (av += 2; av[0] != NULL && av[0][0] == '-'; av++) {
140 162
                if (!strcmp(av[0], "-a")) {
141 54
                        filter = 3;
142 162
                } else if (!strcmp(av[0], "-d")) {
143 54
                        filter = 2;
144 54
                } else {
145 54
                        VCLI_Out(cli, "Unknown flag\n");
146 54
                        VCLI_SetResult(cli, CLIS_UNKNOWN);
147 54
                        return;
148
                }
149 108
        }
150 10314
        VTAILQ_FOREACH(clp, &cs->funcs, list) {
151 9990
                if (clp->auth > cli->auth)
152 0
                        continue;
153 9990
                if (av[0] != NULL && !strcmp(clp->desc->request, av[0])) {
154 54
                        help_helper(cli, clp, av);
155 54
                        return;
156 9936
                } else if (av[0] == NULL) {
157 7452
                        d = strchr(clp->flags, 'd') != NULL ? 2 : 1;
158 7452
                        if (filter & d)
159 5454
                                help_helper(cli, clp, av);
160 7452
                }
161 9936
        }
162 324
        if (av[0] != NULL) {
163 54
                VCLI_Out(cli, "Unknown request.\nType 'help' for more info.\n");
164 54
                VCLI_SetResult(cli, CLIS_UNKNOWN);
165 54
        }
166 432
}
167
168
void v_matchproto_(cli_func_t)
169 162
VCLS_func_help_json(struct cli *cli, const char * const *av, void *priv)
170
{
171
        struct cli_proto *clp;
172
        struct VCLS *cs;
173
174 162
        (void)priv;
175 162
        cs = cli->cls;
176 162
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
177
178 162
        VCLI_JSON_begin(cli, 2, av);
179 6318
        VTAILQ_FOREACH(clp, &cs->funcs, list) {
180 6156
                if (clp->auth > cli->auth)
181 0
                        continue;
182 6156
                VCLI_Out(cli, ",\n  {\n");
183 6156
                VSB_indent(cli->sb, 2);
184 6156
                VCLI_Out(cli, "\"request\": ");
185 6156
                VCLI_JSON_str(cli, clp->desc->request);
186 6156
                VCLI_Out(cli, ",\n");
187 6156
                VCLI_Out(cli, "\"syntax\": ");
188 6156
                VCLI_JSON_str(cli, clp->desc->syntax);
189 6156
                VCLI_Out(cli, ",\n");
190 6156
                VCLI_Out(cli, "\"help\": ");
191 6156
                VCLI_JSON_str(cli, clp->desc->help);
192 6156
                VCLI_Out(cli, ",\n");
193 6156
                VCLI_Out(cli, "\"minarg\": %d", clp->desc->minarg);
194 6156
                VCLI_Out(cli, ",\n");
195 6156
                VCLI_Out(cli, "\"maxarg\": %d", clp->desc->maxarg);
196 6156
                VCLI_Out(cli, ",\n");
197 6156
                VCLI_Out(cli, "\"flags\": ");
198 6156
                VCLI_JSON_str(cli, clp->flags);
199 6156
                VCLI_Out(cli, ",\n");
200 12312
                VCLI_Out(cli, "\"json\": %s",
201 6156
                    clp->jsonfunc == NULL ? "false" : "true");
202 6156
                VCLI_Out(cli, "\n");
203 6156
                VSB_indent(cli->sb, -2);
204 6156
                VCLI_Out(cli, "}");
205 6156
        }
206 162
        VCLI_JSON_end(cli);
207 162
}
208
209
/*--------------------------------------------------------------------
210
 * Look for a CLI command to execute
211
 */
212
213
static void
214 521451
cls_dispatch(struct cli *cli, const struct cli_proto *cp,
215
    char * const * av, int ac)
216
{
217 521451
        int json = 0;
218
219 521451
        AN(av);
220 521451
        assert(ac >= 0);
221
222 521451
        VSB_clear(cli->sb);
223
224 521451
        if (ac > 1 && !strcmp(av[2], "-j"))
225 2025
                json = 1;
226
227 521451
        if (cp->func == NULL && !json) {
228 0
                VCLI_Out(cli, "Unimplemented\n");
229 0
                VCLI_SetResult(cli, CLIS_UNIMPL);
230 0
                return;
231
        }
232 521451
        if (cp->jsonfunc == NULL && json) {
233 27
                VCLI_Out(cli, "JSON unimplemented\n");
234 27
                VCLI_SetResult(cli, CLIS_UNIMPL);
235 27
                return;
236
        }
237
238 521424
        if (ac - 1 < cp->desc->minarg + json) {
239 108
                VCLI_Out(cli, "Too few parameters\n");
240 108
                VCLI_SetResult(cli, CLIS_TOOFEW);
241 108
                return;
242
        }
243
244 521316
        if (cp->desc->maxarg >= 0 && ac - 1 > cp->desc->maxarg + json) {
245 54
                VCLI_Out(cli, "Too many parameters\n");
246 54
                VCLI_SetResult(cli, CLIS_TOOMANY);
247 54
                return;
248
        }
249
250 521262
        cli->result = CLIS_OK;
251 521262
        if (json)
252 1998
                cp->jsonfunc(cli, (const char * const *)av, cp->priv);
253
        else
254 519264
                cp->func(cli, (const char * const *)av, cp->priv);
255 521451
}
256
257
/*--------------------------------------------------------------------
258
 * We have collected a full cli line, parse it and execute, if possible.
259
 */
260
261
static int
262 521721
cls_exec(struct VCLS_fd *cfd, char * const *av)
263
{
264
        struct VCLS *cs;
265
        struct cli_proto *clp;
266
        struct cli *cli;
267
        int na;
268
        ssize_t len;
269
        char *s;
270
        unsigned lim;
271 521721
        int retval = 0;
272
273 521721
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
274 521721
        cs = cfd->cls;
275 521721
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
276
277 521721
        cli = cfd->cli;
278 521721
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
279 521721
        AN(cli->cmd);
280
281 521721
        cli->cls = cs;
282
283 521721
        cli->result = CLIS_UNKNOWN;
284 521721
        VSB_clear(cli->sb);
285 521721
        VCLI_Out(cli, "Unknown request.\nType 'help' for more info.\n");
286
287 521721
        if (cs->before != NULL)
288 521640
                cs->before(cli);
289
290 521721
        do {
291 521559
                if (av[0] != NULL) {
292 54
                        VCLI_Out(cli, "Syntax Error: %s\n", av[0]);
293 54
                        VCLI_SetResult(cli, CLIS_SYNTAX);
294 54
                        break;
295
                }
296
297 521505
                if (av[1] == NULL) {
298 27
                        VCLI_Out(cli, "Empty CLI command.\n");
299 27
                        VCLI_SetResult(cli, CLIS_SYNTAX);
300 27
                        break;
301
                }
302
303 521478
                if (isupper(av[1][0])) {
304 54
                        VCLI_Out(cli, "all commands are in lower-case.\n");
305 54
                        VCLI_SetResult(cli, CLIS_UNKNOWN);
306 54
                        break;
307
                }
308
309 521424
                if (!islower(av[1][0]))
310 27
                        break;
311
312 1392726
                for (na = 0; av[na + 1] != NULL; na++)
313 871329
                        continue;
314
315 9527331
                VTAILQ_FOREACH(clp, &cs->funcs, list) {
316 9426432
                        if (clp->auth > cli->auth)
317 0
                                continue;
318 9426432
                        if (!strcmp(clp->desc->request, av[1])) {
319 420498
                                cls_dispatch(cli, clp, av, na);
320 420498
                                break;
321
                        }
322 9005934
                }
323 622350
                if (clp == NULL &&
324 100980
                    cs->wildcard && cs->wildcard->auth <= cli->auth)
325 100953
                        cls_dispatch(cli, cs->wildcard, av, na);
326
327 521397
        } while (0);
328
329 521559
        AZ(VSB_finish(cli->sb));
330
331 521559
        if (cs->after != NULL)
332 521559
                cs->after(cli);
333
334 521559
        cli->cls = NULL;
335
336 521559
        s = VSB_data(cli->sb);
337 521559
        len = VSB_len(cli->sb);
338 521559
        lim = *cs->limit;
339 521559
        if (len > lim) {
340 81
                if (cli->result == CLIS_OK)
341 0
                        cli->result = CLIS_TRUNCATED;
342 81
                s[lim - 1] = '\0';
343 81
                assert(strlen(s) <= lim);
344 81
        }
345 521559
        if (VCLI_WriteResult(cfd->fdo, cli->result, s) ||
346 521532
            cli->result == CLIS_CLOSE)
347 54
                retval = 1;
348
349
        /*
350
         * In unauthenticated mode we are very intolerant, and close the
351
         * connection at the least provocation.
352
         */
353 521559
        if (cli->auth == 0 && cli->result != CLIS_OK)
354 0
                retval = 1;
355
356 521559
        return (retval);
357
}
358
359
static int
360 828560
cls_feed(struct VCLS_fd *cfd, const char *p, const char *e)
361
{
362
        struct cli *cli;
363 828560
        int i, retval = 0, ac;
364
        char **av, *q;
365
366 828560
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
367 828560
        AN(p);
368 828560
        assert(e > p);
369
370 828560
        cli = cfd->cli;
371 828560
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
372
373 23103087
        for (;p < e; p++) {
374 22274581
                if (cli->cmd == NULL && isspace(*p)) {
375
                        /* Ignore all leading space before cmd */
376 60318
                        continue;
377
                }
378 22214263
                if (cfd->argv == NULL) {
379
380
                        /* Collect first line up to \n or \r */
381 10641506
                        if (cli->cmd == NULL) {
382 521640
                                cli->cmd = VSB_new_auto();
383 521640
                                AN(cli->cmd);
384 521640
                        }
385
386
                        /* Until authenticated, limit length hard */
387 12334973
                        if (*p != '\n' && *p != '\r' &&
388 10119974
                            (cli->auth > 0 || VSB_len(cli->cmd) < 80)) {
389 10119974
                                VSB_putc(cli->cmd, *p);
390 10119974
                                continue;
391
                        }
392
393 521532
                        AZ(VSB_finish(cli->cmd));
394
395
                        /* Ignore leading '-' */
396 521532
                        q = VSB_data(cli->cmd);
397 521532
                        if (*q == '-')
398 108
                                q++;
399 521532
                        av = VAV_Parse(q, &ac, 0);
400 521532
                        AN(av);
401
402 558090
                        if (cli->auth > 0 &&
403 497097
                            av[0] == NULL &&
404 497043
                            ac >= 3 &&
405 200142
                            !strcmp(av[ac-2], "<<") &&
406 36558
                            *av[ac - 1] != '\0') {
407
                                /* Go to "<< nonce" mode */
408 36558
                                cfd->argv = av;
409 36558
                                cfd->argc = ac;
410 36558
                                cfd->match = av[ac - 1];
411 36558
                                cfd->last_arg = VSB_new_auto();
412 36558
                                AN(cfd->last_arg);
413 36558
                        } else {
414
                                /* Plain command */
415 484974
                                i = cls_exec(cfd, av);
416 484974
                                VAV_Free(av);
417 484974
                                VSB_destroy(&cli->cmd);
418 484974
                                if (i)
419 54
                                        return (i);
420
                        }
421 521478
                } else {
422
                        /* "<< nonce" mode */
423 11572757
                        AN(cfd->argv);
424 11572757
                        AN(cfd->argc);
425 11572757
                        AN(cfd->match);
426 11572757
                        AN(cfd->last_arg);
427 11572757
                        if (*cfd->match == '\0' && (*p == '\r' || *p == '\n')) {
428 36558
                                AZ(VSB_finish(cfd->last_arg));
429
                                // NB: VAV lib internals trusted
430 36558
                                REPLACE(cfd->argv[cfd->argc - 1], NULL);
431 36558
                                REPLACE(cfd->argv[cfd->argc - 2], NULL);
432 36558
                                cfd->argv[cfd->argc - 2] =
433 36558
                                    VSB_data(cfd->last_arg);
434 36558
                                i = cls_exec(cfd, cfd->argv);
435 36558
                                cfd->argv[cfd->argc - 2] = NULL;
436 36558
                                VAV_Free(cfd->argv);
437 36558
                                cfd->argv = NULL;
438 36558
                                VSB_destroy(&cfd->last_arg);
439 36558
                                VSB_destroy(&cli->cmd);
440 36558
                                if (i)
441 0
                                        return (i);
442 11572757
                        } else if (*p == *cfd->match) {
443 660987
                                cfd->match++;
444 11536199
                        } else if (cfd->match != cfd->argv[cfd->argc - 1]) {
445 76761
                                q = cfd->argv[cfd->argc - 1];
446 76761
                                VSB_bcat(cfd->last_arg, q, cfd->match - q);
447 76761
                                cfd->match = q;
448 76761
                                VSB_putc(cfd->last_arg, *p);
449 76761
                        } else {
450 10798451
                                VSB_putc(cfd->last_arg, *p);
451
                        }
452
                }
453 12094235
        }
454 828506
        return (retval);
455 828560
}
456
457
struct VCLS *
458 48678
VCLS_New(struct VCLS *model)
459
{
460
        struct VCLS *cs;
461
462 48678
        CHECK_OBJ_ORNULL(model, VCLS_MAGIC);
463
464 48678
        ALLOC_OBJ(cs, VCLS_MAGIC);
465 48678
        AN(cs);
466 48678
        VTAILQ_INIT(&cs->fds);
467 48678
        VTAILQ_INIT(&cs->funcs);
468 48678
        if (model != NULL)
469 22920
                VTAILQ_CONCAT(&cs->funcs, &model->funcs, list);
470 48678
        return (cs);
471
}
472
473
void
474 46896
VCLS_SetLimit(struct VCLS *cs, volatile unsigned *limit)
475
{
476 46896
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
477 46896
        cs->limit = limit;
478 46896
}
479
480
void
481 48678
VCLS_SetHooks(struct VCLS *cs, cls_cbc_f *before, cls_cbc_f *after)
482
{
483
484 48678
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
485 48678
        cs->before = before;
486 48678
        cs->after = after;
487 48678
}
488
489
struct cli *
490 71142
VCLS_AddFd(struct VCLS *cs, int fdi, int fdo, cls_cb_f *closefunc, void *priv)
491
{
492
        struct VCLS_fd *cfd;
493
494 71142
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
495 71142
        assert(fdi >= 0);
496 71142
        assert(fdo >= 0);
497 71142
        ALLOC_OBJ(cfd, VCLS_FD_MAGIC);
498 71142
        AN(cfd);
499 71142
        cfd->cls = cs;
500 71142
        cfd->fdi = fdi;
501 71142
        cfd->fdo = fdo;
502 71142
        cfd->cli = &cfd->clis;
503 71142
        cfd->cli->magic = CLI_MAGIC;
504 71142
        cfd->cli->sb = VSB_new_auto();
505 71142
        AN(cfd->cli->sb);
506 71142
        cfd->cli->limit = cs->limit;
507 71142
        cfd->cli->priv = priv;
508 71142
        cfd->closefunc = closefunc;
509 71142
        cfd->priv = priv;
510 71142
        VTAILQ_INSERT_TAIL(&cs->fds, cfd, list);
511 71142
        cs->nfd++;
512 71142
        return (cfd->cli);
513
}
514
515
static int
516 70875
cls_close_fd(struct VCLS *cs, struct VCLS_fd *cfd)
517
{
518 70875
        int retval = 0;
519
520 70875
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
521 70875
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
522
523 70875
        VTAILQ_REMOVE(&cs->fds, cfd, list);
524 70875
        cs->nfd--;
525 70875
        VSB_destroy(&cfd->cli->sb);
526 70875
        if (cfd->closefunc != NULL)
527 47223
                retval = cfd->closefunc(cfd->priv);
528 70875
        (void)close(cfd->fdi);
529 70875
        if (cfd->fdo != cfd->fdi)
530 46332
                (void)close(cfd->fdo);
531 70875
        if (cfd->cli->ident != NULL)
532 48168
                free(cfd->cli->ident);
533 70875
        FREE_OBJ(cfd);
534 70875
        return (retval);
535
}
536
537
void
538 430377
VCLS_AddFunc(struct VCLS *cs, unsigned auth, struct cli_proto *clp)
539
{
540
        struct cli_proto *clp2;
541
        int i;
542
543 430377
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
544 430377
        AN(clp);
545
546 1574706
        for (;clp->desc != NULL; clp++) {
547 1144329
                clp->auth = auth;
548 1144329
                if (!strcmp(clp->desc->request, "*")) {
549 25758
                        cs->wildcard = clp;
550 25758
                } else {
551 1118571
                        i = 0;
552 11674866
                        VTAILQ_FOREACH(clp2, &cs->funcs, list) {
553 23051490
                                i = strcmp(clp->desc->request,
554 11525745
                                    clp2->desc->request);
555 11525745
                                if (i <= 0)
556 969450
                                        break;
557 10556295
                        }
558 1118571
                        if (clp2 != NULL && i == 0) {
559 206280
                                VTAILQ_INSERT_BEFORE(clp2, clp, list);
560 206280
                                VTAILQ_REMOVE(&cs->funcs, clp2, list);
561 1118571
                        } else if (clp2 != NULL)
562 763170
                                VTAILQ_INSERT_BEFORE(clp2, clp, list);
563
                        else
564 149121
                                VTAILQ_INSERT_TAIL(&cs->funcs, clp, list);
565
                }
566 1144329
        }
567 430377
}
568
569
int
570 898138
VCLS_Poll(struct VCLS *cs, const struct cli *cli, int timeout)
571
{
572
        struct VCLS_fd *cfd;
573
        struct pollfd pfd[1];
574
        int i, j, k;
575
        char buf[BUFSIZ];
576
577 898138
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
578 898138
        if (cs->nfd == 0) {
579 0
                errno = 0;
580 0
                return (-1);
581
        }
582 898138
        assert(cs->nfd > 0);
583
584 898138
        i = 0;
585 1550473
        VTAILQ_FOREACH(cfd, &cs->fds, list) {
586 1550314
                if (cfd->cli != cli)
587 652335
                        continue;
588 897979
                pfd[i].fd = cfd->fdi;
589 897979
                pfd[i].events = POLLIN;
590 897979
                pfd[i].revents = 0;
591 897979
                i++;
592 897979
                break;
593
        }
594 898138
        assert(i == 1);
595 897820
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
596
597 897820
        j = poll(pfd, 1, timeout);
598 897820
        if (j <= 0)
599 0
                return (j);
600 897820
        if (pfd[0].revents & POLLHUP)
601 46278
                k = 1;
602
        else {
603 851542
                i = read(cfd->fdi, buf, sizeof buf);
604 851542
                if (i <= 0)
605 22874
                        k = 1;
606
                else
607 828668
                        k = cls_feed(cfd, buf, buf + i);
608
        }
609 897820
        if (k) {
610 69206
                i = cls_close_fd(cs, cfd);
611 69206
                if (i < 0)
612 23571
                        k = i;
613 69206
        }
614 897820
        return (k);
615 897820
}
616
617
void
618 23652
VCLS_Destroy(struct VCLS **csp)
619
{
620
        struct VCLS *cs;
621
        struct VCLS_fd *cfd, *cfd2;
622
        struct cli_proto *clp;
623
624 23652
        TAKE_OBJ_NOTNULL(cs, csp, VCLS_MAGIC);
625 25321
        VTAILQ_FOREACH_SAFE(cfd, &cs->fds, list, cfd2)
626 1669
                (void)cls_close_fd(cs, cfd);
627
628 614952
        while (!VTAILQ_EMPTY(&cs->funcs)) {
629 591300
                clp = VTAILQ_FIRST(&cs->funcs);
630 591300
                VTAILQ_REMOVE(&cs->funcs, clp, list);
631
        }
632 23652
        FREE_OBJ(cs);
633 23652
}
634
635
/**********************************************************************
636
 * Utility functions for implementing CLI commands
637
 */
638
639
/*lint -e{818} cli could be const */
640
void
641 6983285
VCLI_Out(struct cli *cli, const char *fmt, ...)
642
{
643
        va_list ap;
644
645 6983285
        AN(cli);
646 6983285
        va_start(ap, fmt);
647 6983285
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
648 6983285
        if (VSB_len(cli->sb) < *cli->limit)
649 6939761
                (void)VSB_vprintf(cli->sb, fmt, ap);
650 43524
        else if (cli->result == CLIS_OK)
651 162
                cli->result = CLIS_TRUNCATED;
652 6983285
        va_end(ap);
653 6983285
}
654
655
/*lint -e{818} cli could be const */
656
int
657 7452
VCLI_Overflow(struct cli *cli)
658
{
659 7452
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
660 7452
        if (cli->result == CLIS_TRUNCATED ||
661 7452
            VSB_len(cli->sb) >= *cli->limit)
662 0
                return (1);
663 7452
        return (0);
664 7452
}
665
666
/*lint -e{818} cli could be const */
667
void
668 58671
VCLI_JSON_str(struct cli *cli, const char *s)
669
{
670
671 58671
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
672 58671
        VSB_putc(cli->sb, '"');
673 58671
        VSB_quote(cli->sb, s, -1, VSB_QUOTE_JSON);
674 58671
        VSB_putc(cli->sb, '"');
675 58671
}
676
677
/*lint -e{818} cli could be const */
678
void
679 1350
VCLI_JSON_begin(struct cli *cli, unsigned ver, const char * const * av)
680
{
681
        int i;
682
683 1350
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
684 1350
        VCLI_Out(cli, "[ %u, [", ver);
685 4239
        for (i = 1; av[i] != NULL; i++) {
686 2889
                VCLI_JSON_str(cli, av[i]);
687 2889
                if (av[i + 1] != NULL)
688 1539
                        VCLI_Out(cli, ", ");
689 2889
        }
690 1350
        VCLI_Out(cli, "], %.3f", VTIM_real());
691 1350
        VSB_indent(cli->sb, 2);
692 1350
}
693
694
void
695 1350
VCLI_JSON_end(struct cli *cli)
696
{
697 1350
        VSB_indent(cli->sb, -2);
698 1350
        VCLI_Out(cli, "\n");
699 1350
        VCLI_Out(cli, "]\n");
700 1350
}
701
702
/*lint -e{818} cli could be const */
703
void
704 351
VCLI_Quote(struct cli *cli, const char *s)
705
{
706
707 351
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
708 351
        VSB_quote(cli->sb, s, -1, 0);
709 351
}
710
711
void
712 238896
VCLI_SetResult(struct cli *cli, unsigned res)
713
{
714
715 238896
        if (cli != NULL) {
716 238896
                CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
717 238896
                if (cli->result != CLIS_TRUNCATED || res != CLIS_OK)
718 238869
                        cli->result = res;      /*lint !e64 type mismatch */
719 238896
        } else {
720 0
                printf("CLI result = %u\n", res);
721
        }
722 238896
}