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
 * 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
 * Stuff for handling the CLI protocol
30
 */
31
32
#include "config.h"
33
34
#include <time.h>
35
#include <ctype.h>
36
#include <errno.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
54
struct VCLS_fd {
55
        unsigned                        magic;
56
#define VCLS_FD_MAGIC                   0x010dbd1e
57
        VTAILQ_ENTRY(VCLS_fd)           list;
58
        int                             fdi, fdo;
59
        struct VCLS                     *cls;
60
        struct cli                      *cli, clis;
61
        cls_cb_f                        *closefunc;
62
        void                            *priv;
63
        struct vsb                      *last_arg;
64
        char                            **argv;
65
        int                             argc;
66
        char                            *match;
67
};
68
69
struct VCLS {
70
        unsigned                        magic;
71
#define VCLS_MAGIC                      0x60f044a3
72
        VTAILQ_HEAD(,VCLS_fd)           fds;
73
        unsigned                        nfd;
74
        VTAILQ_HEAD(,cli_proto)         funcs;
75
        cls_cbc_f                       *before, *after;
76
        volatile unsigned               *limit;
77
        struct cli_proto                *wildcard;
78
};
79
80
/*--------------------------------------------------------------------*/
81
82
void v_matchproto_(cli_func_t)
83 2
VCLS_func_close(struct cli *cli, const char *const *av, void *priv)
84
{
85
86
        (void)av;
87
        (void)priv;
88 2
        VCLI_Out(cli, "Closing CLI connection");
89 2
        VCLI_SetResult(cli, CLIS_CLOSE);
90 2
}
91
92
/*--------------------------------------------------------------------*/
93
94
void v_matchproto_(cli_func_t)
95 444
VCLS_func_ping(struct cli *cli, const char * const *av, void *priv)
96
{
97
        time_t t;
98
99
        (void)av;
100
        (void)priv;
101 444
        t = time(NULL);
102 444
        VCLI_Out(cli, "PONG %jd 1.0", (intmax_t)t);
103 444
}
104
105
/*--------------------------------------------------------------------*/
106
107
void v_matchproto_(cli_func_t)
108 30
VCLS_func_help(struct cli *cli, const char * const *av, void *priv)
109
{
110
        struct cli_proto *clp;
111
        unsigned all, debug, d;
112
        struct VCLS *cs;
113
114
        (void)priv;
115 30
        cs = cli->cls;
116 30
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
117
118 30
        if (av[2] == NULL) {
119 14
                all = debug = 0;
120 16
        } else if (!strcmp(av[2], "-a")) {
121 4
                all = 1;
122 4
                debug = 0;
123 12
        } else if (!strcmp(av[2], "-d")) {
124 4
                all = 0;
125 4
                debug = 1;
126
        } else {
127 164
                VTAILQ_FOREACH(clp, &cs->funcs, list) {
128 320
                        if (clp->auth <= cli->auth &&
129 160
                            !strcmp(clp->desc->request, av[2])) {
130 8
                                VCLI_Out(cli, "%s\n%s\n",
131 8
                                    clp->desc->syntax, clp->desc->help);
132 4
                                return;
133
                        }
134
                }
135 4
                VCLI_Out(cli, "Unknown request.\nType 'help' for more info.\n");
136 4
                VCLI_SetResult(cli, CLIS_UNKNOWN);
137 4
                return;
138
        }
139 550
        VTAILQ_FOREACH(clp, &cs->funcs, list) {
140 528
                if (clp->auth > cli->auth)
141 60
                        continue;
142 468
                d =  strchr(clp->flags, 'd') != NULL ? 1 : 0;
143 468
                if (d && (!all && !debug))
144 44
                        continue;
145 424
                if (debug && !d)
146 80
                        continue;
147 344
                if (clp->desc->syntax != NULL)
148 344
                        VCLI_Out(cli, "%s\n", clp->desc->syntax);
149
        }
150
}
151
152
void v_matchproto_(cli_func_t)
153 4
VCLS_func_help_json(struct cli *cli, const char * const *av, void *priv)
154
{
155
        struct cli_proto *clp;
156
        struct VCLS *cs;
157
158
        (void)priv;
159 4
        cs = cli->cls;
160 4
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
161
162 4
        VCLI_JSON_ver(cli, 1, av);
163 132
        VTAILQ_FOREACH(clp, &cs->funcs, list) {
164 128
                if (clp->auth > cli->auth)
165 40
                        continue;
166 88
                VCLI_Out(cli, ",\n  {");
167 88
                VCLI_Out(cli, "\n  \"request\": ");
168 88
                VCLI_JSON_str(cli, clp->desc->request);
169 88
                VCLI_Out(cli, ",\n  \"syntax\": ");
170 88
                VCLI_JSON_str(cli, clp->desc->syntax);
171 88
                VCLI_Out(cli, ",\n  \"help\": ");
172 88
                VCLI_JSON_str(cli, clp->desc->help);
173 88
                VCLI_Out(cli, ",\n  \"minarg\": %d", clp->desc->minarg);
174 88
                VCLI_Out(cli, ", \"maxarg\": %d", clp->desc->maxarg);
175 88
                VCLI_Out(cli, ", \"flags\": ");
176 88
                VCLI_JSON_str(cli, clp->flags);
177 88
                VCLI_Out(cli, ", \"json\": %s",
178 88
                    clp->jsonfunc == NULL ? "false" : "true");
179 88
                VCLI_Out(cli, "\n  }");
180
        }
181 4
        VCLI_Out(cli, "\n]\n");
182 4
}
183
184
/*--------------------------------------------------------------------
185
 * Look for a CLI command to execute
186
 */
187
188
static void
189 27032
cls_dispatch(struct cli *cli, const struct cli_proto *cp,
190
    char * const * av, unsigned ac)
191
{
192 27032
        int json = 0;
193
194 27032
        AN(av);
195
196 27032
        VSB_clear(cli->sb);
197
198 27032
        if (ac > 1 && !strcmp(av[2], "-j"))
199 10
                json = 1;
200
201 27032
        if (cp->func == NULL && !json) {
202 0
                VCLI_Out(cli, "Unimplemented\n");
203 0
                VCLI_SetResult(cli, CLIS_UNIMPL);
204 0
                return;
205
        }
206 27032
        if (cp->jsonfunc == NULL && json) {
207 2
                VCLI_Out(cli, "JSON unimplemented\n");
208 2
                VCLI_SetResult(cli, CLIS_UNIMPL);
209 2
                return;
210
        }
211
212 27030
        if (ac - 1 < cp->desc->minarg + json) {
213 10
                VCLI_Out(cli, "Too few parameters\n");
214 10
                VCLI_SetResult(cli, CLIS_TOOFEW);
215 10
                return;
216
        }
217
218 27020
        if (ac - 1> cp->desc->maxarg + json) {
219 4
                VCLI_Out(cli, "Too many parameters\n");
220 4
                VCLI_SetResult(cli, CLIS_TOOMANY);
221 4
                return;
222
        }
223
224 27016
        cli->result = CLIS_OK;
225 27016
        if (json)
226 8
                cp->jsonfunc(cli, (const char * const *)av, cp->priv);
227
        else
228 27008
                cp->func(cli, (const char * const *)av, cp->priv);
229
}
230
231
/*--------------------------------------------------------------------
232
 * We have collected a full cli line, parse it and execute, if possible.
233
 */
234
235
static int
236 27044
cls_exec(struct VCLS_fd *cfd, char * const *av)
237
{
238
        struct VCLS *cs;
239
        struct cli_proto *clp;
240
        struct cli *cli;
241
        unsigned na;
242
        ssize_t len;
243
        char *s;
244
        unsigned lim;
245 27044
        int retval = 0;
246 27044
        const char *trunc = "!\n[response was truncated]\n";
247
248 27044
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
249 27044
        cs = cfd->cls;
250 27044
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
251
252 27044
        cli = cfd->cli;
253 27044
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
254 27044
        AN(cli->cmd);
255
256 27044
        cli->cls = cs;
257
258 27044
        cli->result = CLIS_UNKNOWN;
259 27044
        VSB_clear(cli->sb);
260 27044
        VCLI_Out(cli, "Unknown request.\nType 'help' for more info.\n");
261
262 27044
        if (cs->before != NULL)
263 27044
                cs->before(cli);
264
265
        do {
266 27044
                if (av[0] != NULL) {
267 4
                        VCLI_Out(cli, "Syntax Error: %s\n", av[0]);
268 4
                        VCLI_SetResult(cli, CLIS_SYNTAX);
269 4
                        break;
270
                }
271
272 27040
                if (isupper(av[1][0])) {
273 4
                        VCLI_Out(cli, "all commands are in lower-case.\n");
274 4
                        VCLI_SetResult(cli, CLIS_UNKNOWN);
275 4
                        break;
276
                }
277
278 27036
                if (!islower(av[1][0]))
279 2
                        break;
280
281 71394
                for (na = 0; av[na + 1] != NULL; na++)
282 44360
                        continue;
283
284 423194
                VTAILQ_FOREACH(clp, &cs->funcs, list) {
285 417936
                        if (clp->auth > cli->auth)
286 46578
                                continue;
287 371358
                        if (!strcmp(clp->desc->request, av[1])) {
288 21776
                                cls_dispatch(cli, clp, av, na);
289 21774
                                break;
290
                        }
291
                }
292 32290
                if (clp == NULL &&
293 10514
                    cs->wildcard && cs->wildcard->auth <= cli->auth)
294 5256
                        cls_dispatch(cli, cs->wildcard, av, na);
295
296
        } while (0);
297
298 27042
        AZ(VSB_finish(cli->sb));
299
300 27042
        if (cs->after != NULL)
301 27042
                cs->after(cli);
302
303 27038
        cli->cls = NULL;
304
305 27038
        s = VSB_data(cli->sb);
306 27038
        len = VSB_len(cli->sb);
307 27038
        lim = *cs->limit;
308 27038
        if (len > lim) {
309 10
                if (cli->result == CLIS_OK)
310 0
                        cli->result = CLIS_TRUNCATED;
311 10
                strcpy(s + (lim - strlen(trunc)), trunc);
312 10
                assert(strlen(s) <= lim);
313
        }
314 54076
        if (VCLI_WriteResult(cfd->fdo, cli->result, s) ||
315 27038
            cli->result == CLIS_CLOSE)
316 2
                retval = 1;
317
318
        /*
319
         * In unauthenticated mode we are very intolerant, and close the
320
         * connection at the least provocation.
321
         */
322 27038
        if (cli->auth == 0 && cli->result != CLIS_OK)
323 0
                retval = 1;
324
325 27038
        return (retval);
326
}
327
328
static int
329 42927
cls_feed(struct VCLS_fd *cfd, const char *p, const char *e)
330
{
331
        struct cli *cli;
332 42927
        int i, retval = 0, ac;
333
        char **av, *q;
334
335 42927
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
336 42927
        AN(p);
337 42927
        assert(e > p);
338
339 42927
        cli = cfd->cli;
340 42927
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
341
342 1151297
        for (;p < e; p++) {
343 1108378
                if (cli->cmd == NULL && isspace(*p)) {
344
                        /* Ignore all leading space before cmd */
345 3020
                        continue;
346
                }
347 1105358
                if (cfd->argv == NULL) {
348
349
                        /* Collect first line up to \n or \r */
350 548272
                        if (cli->cmd == NULL) {
351 27044
                                cli->cmd = VSB_new_auto();
352 27044
                                AN(cli->cmd);
353
                        }
354
355
                        /* Until authenticated, limit length hard */
356 1069500
                        if (*p != '\n' && *p != '\r' &&
357 608306
                            (cli->auth > 0 || VSB_len(cli->cmd) < 80)) {
358 521228
                                VSB_putc(cli->cmd, *p);
359 521228
                                continue;
360
                        }
361
362 27044
                        AZ(VSB_finish(cli->cmd));
363
364
                        /* Ignore leading '-' */
365 27044
                        q = VSB_data(cli->cmd);
366 27044
                        if (*q == '-')
367 8
                                q++;
368 27044
                        av = VAV_Parse(q, &ac, 0);
369 27044
                        AN(av);
370
371 52826
                        if (cli->auth > 0 &&
372 51560
                            av[0] == NULL &&
373 35772
                            ac >= 3 &&
374 11768
                            !strcmp(av[ac-2], "<<") &&
375 1774
                            *av[ac - 1] != '\0') {
376
                                /* Go to "<< nonce" mode */
377 1774
                                cfd->argv = av;
378 1774
                                cfd->argc = ac;
379 1774
                                cfd->match = av[ac - 1];
380 1774
                                cfd->last_arg = VSB_new_auto();
381 1774
                                AN(cfd->last_arg);
382
                        } else {
383
                                /* Plain command */
384 25270
                                i = cls_exec(cfd, av);
385 25264
                                VAV_Free(av);
386 25264
                                VSB_destroy(&cli->cmd);
387 25264
                                if (i)
388 2
                                        return(i);
389
                        }
390
                } else {
391
                        /* "<< nonce" mode */
392 557086
                        AN(cfd->argv);
393 557086
                        AN(cfd->argc);
394 557086
                        AN(cfd->match);
395 557086
                        AN(cfd->last_arg);
396 557086
                        if (*cfd->match == '\0' && (*p == '\r' || *p == '\n')) {
397 1774
                                AZ(VSB_finish(cfd->last_arg));
398
                                // NB: VAV lib internals trusted
399 1774
                                REPLACE(cfd->argv[cfd->argc - 1], NULL);
400 1774
                                REPLACE(cfd->argv[cfd->argc - 2], NULL);
401 3548
                                cfd->argv[cfd->argc - 2] =
402 1774
                                    VSB_data(cfd->last_arg);
403 1774
                                i = cls_exec(cfd, cfd->argv);
404 1774
                                cfd->argv[cfd->argc - 2] = NULL;
405 1774
                                VAV_Free(cfd->argv);
406 1774
                                cfd->argv = NULL;
407 1774
                                VSB_destroy(&cfd->last_arg);
408 1774
                                VSB_destroy(&cli->cmd);
409 3548
                                if (i)
410 0
                                        return (i);
411 555312
                        } else if (*p == *cfd->match) {
412 34028
                                cfd->match++;
413 521284
                        } else if (cfd->match != cfd->argv[cfd->argc - 1]) {
414 5670
                                q = cfd->argv[cfd->argc - 1];
415 5670
                                VSB_bcat(cfd->last_arg, q, cfd->match - q);
416 5670
                                cfd->match = q;
417 5670
                                VSB_putc(cfd->last_arg, *p);
418
                        } else {
419 515614
                                VSB_putc(cfd->last_arg, *p);
420
                        }
421
                }
422
        }
423 42919
        return (retval);
424
}
425
426
struct VCLS *
427 2632
VCLS_New(struct VCLS *model)
428
{
429
        struct VCLS *cs;
430
431 2632
        CHECK_OBJ_ORNULL(model, VCLS_MAGIC);
432
433 2632
        ALLOC_OBJ(cs, VCLS_MAGIC);
434 2632
        AN(cs);
435 2632
        VTAILQ_INIT(&cs->fds);
436 2632
        VTAILQ_INIT(&cs->funcs);
437 2632
        if (model != NULL)
438 1228
                VTAILQ_CONCAT(&cs->funcs, &model->funcs, list);
439 2632
        return (cs);
440
}
441
442
void
443 2632
VCLS_SetLimit(struct VCLS *cs, volatile unsigned *limit)
444
{
445 2632
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
446 2632
        cs->limit = limit;
447 2632
}
448
449
void
450 2632
VCLS_SetHooks(struct VCLS *cs, cls_cbc_f *before, cls_cbc_f *after)
451
{
452
453 2632
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
454 2632
        cs->before = before;
455 2632
        cs->after = after;
456 2632
}
457
458
struct cli *
459 3736
VCLS_AddFd(struct VCLS *cs, int fdi, int fdo, cls_cb_f *closefunc, void *priv)
460
{
461
        struct VCLS_fd *cfd;
462
463 3736
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
464 3736
        assert(fdi >= 0);
465 3736
        assert(fdo >= 0);
466 3736
        ALLOC_OBJ(cfd, VCLS_FD_MAGIC);
467 3736
        AN(cfd);
468 3736
        cfd->cls = cs;
469 3736
        cfd->fdi = fdi;
470 3736
        cfd->fdo = fdo;
471 3736
        cfd->cli = &cfd->clis;
472 3736
        cfd->cli->magic = CLI_MAGIC;
473 3736
        cfd->cli->sb = VSB_new_auto();
474 3736
        AN(cfd->cli->sb);
475 3736
        cfd->cli->limit = cs->limit;
476 3736
        cfd->cli->priv = priv;
477 3736
        cfd->closefunc = closefunc;
478 3736
        cfd->priv = priv;
479 3736
        VTAILQ_INSERT_TAIL(&cs->fds, cfd, list);
480 3736
        cs->nfd++;
481 3736
        return (cfd->cli);
482
}
483
484
static int
485 3722
cls_close_fd(struct VCLS *cs, struct VCLS_fd *cfd)
486
{
487 3722
        int retval = 0;
488
489 3722
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
490 3722
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
491
492 3722
        VTAILQ_REMOVE(&cs->fds, cfd, list);
493 3722
        cs->nfd--;
494 3722
        VSB_destroy(&cfd->cli->sb);
495 3722
        if (cfd->closefunc != NULL)
496 2484
                retval = cfd->closefunc(cfd->priv);
497 3722
        (void)close(cfd->fdi);
498 3722
        if (cfd->fdo != cfd->fdi)
499 2460
                (void)close(cfd->fdo);
500 3722
        if (cfd->cli->ident != NULL)
501 2504
                free(cfd->cli->ident);
502 3722
        FREE_OBJ(cfd);
503 3722
        return (retval);
504
}
505
506
void
507 21890
VCLS_AddFunc(struct VCLS *cs, unsigned auth, struct cli_proto *clp)
508
{
509
        struct cli_proto *clp2;
510
        int i;
511
512 21890
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
513 21890
        AN(clp);
514
515 75432
        for (;clp->desc != NULL; clp++) {
516 53542
                clp->auth = auth;
517 53542
                if (!strcmp(clp->desc->request, "*")) {
518 1404
                        cs->wildcard = clp;
519
                } else {
520 52138
                        i = 0;
521 482084
                        VTAILQ_FOREACH(clp2, &cs->funcs, list) {
522 474104
                                i = strcmp(clp->desc->request,
523 474104
                                    clp2->desc->request);
524 474104
                                if (i <= 0)
525 44158
                                        break;
526
                        }
527 52138
                        if (clp2 != NULL && i == 0) {
528 11052
                                VTAILQ_INSERT_BEFORE(clp2, clp, list);
529 11052
                                VTAILQ_REMOVE(&cs->funcs, clp2, list);
530 41086
                        } else if (clp2 != NULL)
531 33106
                                VTAILQ_INSERT_BEFORE(clp2, clp, list);
532
                        else
533 7980
                                VTAILQ_INSERT_TAIL(&cs->funcs, clp, list);
534
                }
535
        }
536 21890
}
537
538
int
539 46589
VCLS_Poll(struct VCLS *cs, const struct cli *cli, int timeout)
540
{
541
        struct VCLS_fd *cfd;
542
        struct pollfd pfd[1];
543
        int i, j, k;
544
        char buf[BUFSIZ];
545
546 46589
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
547 46589
        if (cs->nfd == 0) {
548 0
                errno = 0;
549 0
                return (-1);
550
        }
551 46589
        assert(cs->nfd > 0);
552
553 46589
        i = 0;
554 159014
        VTAILQ_FOREACH(cfd, &cs->fds, list) {
555 79507
                if (cfd->cli != cli)
556 32918
                        continue;
557 46589
                pfd[i].fd = cfd->fdi;
558 46589
                pfd[i].events = POLLIN;
559 46589
                pfd[i].revents = 0;
560 46589
                i++;
561 46589
                break;
562
        }
563 46589
        assert(i == 1);
564 46589
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
565
566 46589
        j = poll(pfd, 1, timeout);
567 46581
        if (j <= 0)
568 0
                return (j);
569 46581
        if (pfd[0].revents & POLLHUP)
570 2458
                k = 1;
571
        else {
572 44123
                i = read(cfd->fdi, buf, sizeof buf);
573 44123
                if (i <= 0)
574 1196
                        k = 1;
575
                else
576 42927
                        k = cls_feed(cfd, buf, buf + i);
577
        }
578 46575
        if (k) {
579 3656
                i = cls_close_fd(cs, cfd);
580 3656
                if (i < 0)
581 1240
                        k = i;
582
        }
583 46575
        return (k);
584
}
585
586
void
587 1244
VCLS_Destroy(struct VCLS **csp)
588
{
589
        struct VCLS *cs;
590
        struct VCLS_fd *cfd, *cfd2;
591
        struct cli_proto *clp;
592
593 1244
        TAKE_OBJ_NOTNULL(cs, csp, VCLS_MAGIC);
594 1310
        VTAILQ_FOREACH_SAFE(cfd, &cs->fds, list, cfd2)
595 66
                (void)cls_close_fd(cs, cfd);
596
597 28612
        while (!VTAILQ_EMPTY(&cs->funcs)) {
598 26124
                clp = VTAILQ_FIRST(&cs->funcs);
599 26124
                VTAILQ_REMOVE(&cs->funcs, clp, list);
600
        }
601 1244
        FREE_OBJ(cs);
602 1244
}
603
604
/**********************************************************************
605
 * Utility functions for implementing CLI commands
606
 */
607
608
/*lint -e{818} cli could be const */
609
void
610 131010
VCLI_Out(struct cli *cli, const char *fmt, ...)
611
{
612
        va_list ap;
613
614 131010
        va_start(ap, fmt);
615 131010
        if (cli != NULL) {
616 131010
                CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
617 131010
                if (VSB_len(cli->sb) < *cli->limit)
618 129222
                        (void)VSB_vprintf(cli->sb, fmt, ap);
619 1788
                else if (cli->result == CLIS_OK)
620 10
                        cli->result = CLIS_TRUNCATED;
621
        } else {
622 0
                (void)vfprintf(stdout, fmt, ap);
623
        }
624 131010
        va_end(ap);
625 131010
}
626
627
/*lint -e{818} cli could be const */
628
int
629 266
VCLI_Overflow(struct cli *cli)
630
{
631 266
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
632 532
        if (cli->result == CLIS_TRUNCATED ||
633 266
            VSB_len(cli->sb) >= *cli->limit)
634 0
                return (1);
635 266
        return (0);
636
}
637
638
/*lint -e{818} cli could be const */
639
void
640 360
VCLI_JSON_str(struct cli *cli, const char *s)
641
{
642
643 360
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
644 360
        VSB_quote(cli->sb, s, -1, VSB_QUOTE_JSON);
645 360
}
646
647
/*lint -e{818} cli could be const */
648
void
649 4
VCLI_JSON_ver(struct cli *cli, unsigned ver, const char * const * av)
650
{
651
        int i;
652
653 4
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
654 4
        VCLI_Out(cli, "[ %u, [", ver);
655 12
        for (i = 1; av[i] != NULL; i++) {
656 8
                VCLI_JSON_str(cli, av[i]);
657 8
                if (av[i + 1] != NULL)
658 4
                        VCLI_Out(cli, ", ");
659
        }
660 4
        VCLI_Out(cli, "]");
661 4
}
662
663
/*lint -e{818} cli could be const */
664
void
665 0
VCLI_Quote(struct cli *cli, const char *s)
666
{
667
668 0
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
669 0
        VSB_quote(cli->sb, s, -1, 0);
670 0
}
671
672
void
673 12286
VCLI_SetResult(struct cli *cli, unsigned res)
674
{
675
676 12286
        if (cli != NULL) {
677 12286
                CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
678 12286
                if (cli->result != CLIS_TRUNCATED || res != CLIS_OK)
679 12284
                        cli->result = res;      /*lint !e64 type mismatch */
680
        } else {
681 0
                printf("CLI result = %u\n", res);
682
        }
683 12286
}