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