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
#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 4
VCLS_func_close(struct cli *cli, const char *const *av, void *priv)
85
{
86
87
        (void)av;
88
        (void)priv;
89 4
        VCLI_Out(cli, "Closing CLI connection");
90 4
        VCLI_SetResult(cli, CLIS_CLOSE);
91 4
}
92
93
/*--------------------------------------------------------------------*/
94
95
void v_matchproto_(cli_func_t)
96 585
VCLS_func_ping(struct cli *cli, const char * const *av, void *priv)
97
{
98
        time_t t;
99
100
        (void)av;
101
        (void)priv;
102 585
        t = time(NULL);
103 585
        VCLI_Out(cli, "PONG %jd 1.0", (intmax_t)t);
104 585
}
105
106
void v_matchproto_(cli_func_t)
107 4
VCLS_func_ping_json(struct cli *cli, const char * const *av, void *priv)
108
{
109
        (void)av;
110
        (void)priv;
111 4
        VCLI_JSON_begin(cli, 2, av);
112 4
        VCLI_Out(cli, ", \"PONG\"\n");
113 4
        VCLI_JSON_end(cli);
114 4
}
115
116
/*--------------------------------------------------------------------*/
117
118
void v_matchproto_(cli_func_t)
119 30
VCLS_func_help(struct cli *cli, const char * const *av, void *priv)
120
{
121
        struct cli_proto *clp;
122
        unsigned all, debug, d;
123
        struct VCLS *cs;
124
125
        (void)priv;
126 30
        cs = cli->cls;
127 30
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
128
129 30
        if (av[2] == NULL) {
130 14
                all = debug = 0;
131 16
        } else if (!strcmp(av[2], "-a")) {
132 4
                all = 1;
133 4
                debug = 0;
134 12
        } else if (!strcmp(av[2], "-d")) {
135 4
                all = 0;
136 4
                debug = 1;
137
        } else {
138 164
                VTAILQ_FOREACH(clp, &cs->funcs, list) {
139 320
                        if (clp->auth <= cli->auth &&
140 160
                            !strcmp(clp->desc->request, av[2])) {
141 8
                                VCLI_Out(cli, "%s\n%s\n",
142 8
                                    clp->desc->syntax, clp->desc->help);
143 4
                                return;
144
                        }
145
                }
146 4
                VCLI_Out(cli, "Unknown request.\nType 'help' for more info.\n");
147 4
                VCLI_SetResult(cli, CLIS_UNKNOWN);
148 4
                return;
149
        }
150 550
        VTAILQ_FOREACH(clp, &cs->funcs, list) {
151 528
                if (clp->auth > cli->auth)
152 0
                        continue;
153 528
                d =  strchr(clp->flags, 'd') != NULL ? 1 : 0;
154 528
                if (d && (!all && !debug))
155 50
                        continue;
156 478
                if (debug && !d)
157 80
                        continue;
158 398
                if (clp->desc->syntax != NULL)
159 398
                        VCLI_Out(cli, "%s\n", clp->desc->syntax);
160
        }
161
}
162
163
void v_matchproto_(cli_func_t)
164 2
VCLS_func_help_json(struct cli *cli, const char * const *av, void *priv)
165
{
166
        struct cli_proto *clp;
167
        struct VCLS *cs;
168
169
        (void)priv;
170 2
        cs = cli->cls;
171 2
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
172
173 2
        VCLI_JSON_begin(cli, 2, av);
174 66
        VTAILQ_FOREACH(clp, &cs->funcs, list) {
175 64
                if (clp->auth > cli->auth)
176 0
                        continue;
177 64
                VCLI_Out(cli, ",\n  {\n");
178 64
                VSB_indent(cli->sb, 2);
179 64
                VCLI_Out(cli, "\"request\": ");
180 64
                VCLI_JSON_str(cli, clp->desc->request);
181 64
                VCLI_Out(cli, ",\n");
182 64
                VCLI_Out(cli, "\"syntax\": ");
183 64
                VCLI_JSON_str(cli, clp->desc->syntax);
184 64
                VCLI_Out(cli, ",\n");
185 64
                VCLI_Out(cli, "\"help\": ");
186 64
                VCLI_JSON_str(cli, clp->desc->help);
187 64
                VCLI_Out(cli, ",\n");
188 64
                VCLI_Out(cli, "\"minarg\": %d", clp->desc->minarg);
189 64
                VCLI_Out(cli, ",\n");
190 64
                VCLI_Out(cli, "\"maxarg\": %d", clp->desc->maxarg);
191 64
                VCLI_Out(cli, ",\n");
192 64
                VCLI_Out(cli, "\"flags\": ");
193 64
                VCLI_JSON_str(cli, clp->flags);
194 64
                VCLI_Out(cli, ",\n");
195 64
                VCLI_Out(cli, "\"json\": %s",
196 64
                    clp->jsonfunc == NULL ? "false" : "true");
197 64
                VCLI_Out(cli, "\n");
198 64
                VSB_indent(cli->sb, -2);
199 64
                VCLI_Out(cli, "}");
200
        }
201 2
        VCLI_JSON_end(cli);
202 2
}
203
204
/*--------------------------------------------------------------------
205
 * Look for a CLI command to execute
206
 */
207
208
static void
209 31139
cls_dispatch(struct cli *cli, const struct cli_proto *cp,
210
    char * const * av, unsigned ac)
211
{
212 31139
        int json = 0;
213
214 31139
        AN(av);
215
216 31139
        VSB_clear(cli->sb);
217
218 31139
        if (ac > 1 && !strcmp(av[2], "-j"))
219 104
                json = 1;
220
221 31139
        if (cp->func == NULL && !json) {
222 0
                VCLI_Out(cli, "Unimplemented\n");
223 0
                VCLI_SetResult(cli, CLIS_UNIMPL);
224 0
                return;
225
        }
226 31139
        if (cp->jsonfunc == NULL && json) {
227 2
                VCLI_Out(cli, "JSON unimplemented\n");
228 2
                VCLI_SetResult(cli, CLIS_UNIMPL);
229 2
                return;
230
        }
231
232 31137
        if (ac - 1 < cp->desc->minarg + json) {
233 10
                VCLI_Out(cli, "Too few parameters\n");
234 10
                VCLI_SetResult(cli, CLIS_TOOFEW);
235 10
                return;
236
        }
237
238 31127
        if (ac - 1 > cp->desc->maxarg + json) {
239 4
                VCLI_Out(cli, "Too many parameters\n");
240 4
                VCLI_SetResult(cli, CLIS_TOOMANY);
241 4
                return;
242
        }
243
244 31123
        cli->result = CLIS_OK;
245 31123
        if (json)
246 102
                cp->jsonfunc(cli, (const char * const *)av, cp->priv);
247
        else
248 31021
                cp->func(cli, (const char * const *)av, cp->priv);
249
}
250
251
/*--------------------------------------------------------------------
252
 * We have collected a full cli line, parse it and execute, if possible.
253
 */
254
255
static int
256 31153
cls_exec(struct VCLS_fd *cfd, char * const *av)
257
{
258
        struct VCLS *cs;
259
        struct cli_proto *clp;
260
        struct cli *cli;
261
        unsigned na;
262
        ssize_t len;
263
        char *s;
264
        unsigned lim;
265 31153
        int retval = 0;
266 31153
        const char *trunc = "!\n[response was truncated]\n";
267
268 31153
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
269 31153
        cs = cfd->cls;
270 31153
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
271
272 31153
        cli = cfd->cli;
273 31153
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
274 31153
        AN(cli->cmd);
275
276 31153
        cli->cls = cs;
277
278 31153
        cli->result = CLIS_UNKNOWN;
279 31153
        VSB_clear(cli->sb);
280 31153
        VCLI_Out(cli, "Unknown request.\nType 'help' for more info.\n");
281
282 31153
        if (cs->before != NULL)
283 31153
                cs->before(cli);
284
285
        do {
286 31153
                if (av[0] != NULL) {
287 4
                        VCLI_Out(cli, "Syntax Error: %s\n", av[0]);
288 4
                        VCLI_SetResult(cli, CLIS_SYNTAX);
289 4
                        break;
290
                }
291
292 31149
                if (av[1] == NULL) {
293 2
                        VCLI_Out(cli, "Empty CLI command.\n");
294 2
                        VCLI_SetResult(cli, CLIS_SYNTAX);
295 2
                        break;
296
                }
297
298 31147
                if (isupper(av[1][0])) {
299 4
                        VCLI_Out(cli, "all commands are in lower-case.\n");
300 4
                        VCLI_SetResult(cli, CLIS_UNKNOWN);
301 4
                        break;
302
                }
303
304 31143
                if (!islower(av[1][0]))
305 2
                        break;
306
307 82382
                for (na = 0; av[na + 1] != NULL; na++)
308 51241
                        continue;
309
310 487431
                VTAILQ_FOREACH(clp, &cs->funcs, list) {
311 481363
                        if (clp->auth > cli->auth)
312 0
                                continue;
313 481363
                        if (!strcmp(clp->desc->request, av[1])) {
314 25073
                                cls_dispatch(cli, clp, av, na);
315 25071
                                break;
316
                        }
317
                }
318 37207
                if (clp == NULL &&
319 12134
                    cs->wildcard && cs->wildcard->auth <= cli->auth)
320 6066
                        cls_dispatch(cli, cs->wildcard, av, na);
321
322
        } while (0);
323
324 31151
        AZ(VSB_finish(cli->sb));
325
326 31151
        if (cs->after != NULL)
327 31151
                cs->after(cli);
328
329 31147
        cli->cls = NULL;
330
331 31147
        s = VSB_data(cli->sb);
332 31147
        len = VSB_len(cli->sb);
333 31147
        lim = *cs->limit;
334 31147
        if (len > lim) {
335 8
                if (cli->result == CLIS_OK)
336 2
                        cli->result = CLIS_TRUNCATED;
337 8
                strcpy(s + (lim - strlen(trunc)), trunc);
338 8
                assert(strlen(s) <= lim);
339
        }
340 62294
        if (VCLI_WriteResult(cfd->fdo, cli->result, s) ||
341 31147
            cli->result == CLIS_CLOSE)
342 4
                retval = 1;
343
344
        /*
345
         * In unauthenticated mode we are very intolerant, and close the
346
         * connection at the least provocation.
347
         */
348 31147
        if (cli->auth == 0 && cli->result != CLIS_OK)
349 0
                retval = 1;
350
351 31147
        return (retval);
352
}
353
354
static int
355 49474
cls_feed(struct VCLS_fd *cfd, const char *p, const char *e)
356
{
357
        struct cli *cli;
358 49474
        int i, retval = 0, ac;
359
        char **av, *q;
360
361 49474
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
362 49474
        AN(p);
363 49474
        assert(e > p);
364
365 49474
        cli = cfd->cli;
366 49474
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
367
368 1346109
        for (;p < e; p++) {
369 1296645
                if (cli->cmd == NULL && isspace(*p)) {
370
                        /* Ignore all leading space before cmd */
371 3528
                        continue;
372
                }
373 1293117
                if (cfd->argv == NULL) {
374
375
                        /* Collect first line up to \n or \r */
376 627153
                        if (cli->cmd == NULL) {
377 31153
                                cli->cmd = VSB_new_auto();
378 31153
                                AN(cli->cmd);
379
                        }
380
381
                        /* Until authenticated, limit length hard */
382 1223153
                        if (*p != '\n' && *p != '\r' &&
383 696050
                            (cli->auth > 0 || VSB_len(cli->cmd) < 80)) {
384 596000
                                VSB_putc(cli->cmd, *p);
385 596000
                                continue;
386
                        }
387
388 31153
                        AZ(VSB_finish(cli->cmd));
389
390
                        /* Ignore leading '-' */
391 31153
                        q = VSB_data(cli->cmd);
392 31153
                        if (*q == '-')
393 10
                                q++;
394 31153
                        av = VAV_Parse(q, &ac, 0);
395 31153
                        AN(av);
396
397 60856
                        if (cli->auth > 0 &&
398 59402
                            av[0] == NULL &&
399 41305
                            ac >= 3 &&
400 13706
                            !strcmp(av[ac-2], "<<") &&
401 2100
                            *av[ac - 1] != '\0') {
402
                                /* Go to "<< nonce" mode */
403 2100
                                cfd->argv = av;
404 2100
                                cfd->argc = ac;
405 2100
                                cfd->match = av[ac - 1];
406 2100
                                cfd->last_arg = VSB_new_auto();
407 2100
                                AN(cfd->last_arg);
408
                        } else {
409
                                /* Plain command */
410 29053
                                i = cls_exec(cfd, av);
411 29047
                                VAV_Free(av);
412 29047
                                VSB_destroy(&cli->cmd);
413 29047
                                if (i)
414 4
                                        return(i);
415
                        }
416
                } else {
417
                        /* "<< nonce" mode */
418 665964
                        AN(cfd->argv);
419 665964
                        AN(cfd->argc);
420 665964
                        AN(cfd->match);
421 665964
                        AN(cfd->last_arg);
422 665964
                        if (*cfd->match == '\0' && (*p == '\r' || *p == '\n')) {
423 2100
                                AZ(VSB_finish(cfd->last_arg));
424
                                // NB: VAV lib internals trusted
425 2100
                                REPLACE(cfd->argv[cfd->argc - 1], NULL);
426 2100
                                REPLACE(cfd->argv[cfd->argc - 2], NULL);
427 4200
                                cfd->argv[cfd->argc - 2] =
428 2100
                                    VSB_data(cfd->last_arg);
429 2100
                                i = cls_exec(cfd, cfd->argv);
430 2100
                                cfd->argv[cfd->argc - 2] = NULL;
431 2100
                                VAV_Free(cfd->argv);
432 2100
                                cfd->argv = NULL;
433 2100
                                VSB_destroy(&cfd->last_arg);
434 2100
                                VSB_destroy(&cli->cmd);
435 4200
                                if (i)
436 0
                                        return (i);
437 663864
                        } else if (*p == *cfd->match) {
438 39244
                                cfd->match++;
439 624620
                        } else if (cfd->match != cfd->argv[cfd->argc - 1]) {
440 5670
                                q = cfd->argv[cfd->argc - 1];
441 5670
                                VSB_bcat(cfd->last_arg, q, cfd->match - q);
442 5670
                                cfd->match = q;
443 5670
                                VSB_putc(cfd->last_arg, *p);
444
                        } else {
445 618950
                                VSB_putc(cfd->last_arg, *p);
446
                        }
447
                }
448
        }
449 49464
        return (retval);
450
}
451
452
struct VCLS *
453 2954
VCLS_New(struct VCLS *model)
454
{
455
        struct VCLS *cs;
456
457 2954
        CHECK_OBJ_ORNULL(model, VCLS_MAGIC);
458
459 2954
        ALLOC_OBJ(cs, VCLS_MAGIC);
460 2954
        AN(cs);
461 2954
        VTAILQ_INIT(&cs->fds);
462 2954
        VTAILQ_INIT(&cs->funcs);
463 2954
        if (model != NULL)
464 1376
                VTAILQ_CONCAT(&cs->funcs, &model->funcs, list);
465 2954
        return (cs);
466
}
467
468
void
469 2954
VCLS_SetLimit(struct VCLS *cs, volatile unsigned *limit)
470
{
471 2954
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
472 2954
        cs->limit = limit;
473 2954
}
474
475
void
476 2954
VCLS_SetHooks(struct VCLS *cs, cls_cbc_f *before, cls_cbc_f *after)
477
{
478
479 2954
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
480 2954
        cs->before = before;
481 2954
        cs->after = after;
482 2954
}
483
484
struct cli *
485 4254
VCLS_AddFd(struct VCLS *cs, int fdi, int fdo, cls_cb_f *closefunc, void *priv)
486
{
487
        struct VCLS_fd *cfd;
488
489 4254
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
490 4254
        assert(fdi >= 0);
491 4254
        assert(fdo >= 0);
492 4254
        ALLOC_OBJ(cfd, VCLS_FD_MAGIC);
493 4254
        AN(cfd);
494 4254
        cfd->cls = cs;
495 4254
        cfd->fdi = fdi;
496 4254
        cfd->fdo = fdo;
497 4254
        cfd->cli = &cfd->clis;
498 4254
        cfd->cli->magic = CLI_MAGIC;
499 4254
        cfd->cli->sb = VSB_new_auto();
500 4254
        AN(cfd->cli->sb);
501 4254
        cfd->cli->limit = cs->limit;
502 4254
        cfd->cli->priv = priv;
503 4254
        cfd->closefunc = closefunc;
504 4254
        cfd->priv = priv;
505 4254
        VTAILQ_INSERT_TAIL(&cs->fds, cfd, list);
506 4254
        cs->nfd++;
507 4254
        return (cfd->cli);
508
}
509
510
static int
511 4236
cls_close_fd(struct VCLS *cs, struct VCLS_fd *cfd)
512
{
513 4236
        int retval = 0;
514
515 4236
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
516 4236
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
517
518 4236
        VTAILQ_REMOVE(&cs->fds, cfd, list);
519 4236
        cs->nfd--;
520 4236
        VSB_destroy(&cfd->cli->sb);
521 4236
        if (cfd->closefunc != NULL)
522 2848
                retval = cfd->closefunc(cfd->priv);
523 4236
        (void)close(cfd->fdi);
524 4236
        if (cfd->fdo != cfd->fdi)
525 2786
                (void)close(cfd->fdo);
526 4236
        if (cfd->cli->ident != NULL)
527 2874
                free(cfd->cli->ident);
528 4236
        FREE_OBJ(cfd);
529 4236
        return (retval);
530
}
531
532
void
533 24630
VCLS_AddFunc(struct VCLS *cs, unsigned auth, struct cli_proto *clp)
534
{
535
        struct cli_proto *clp2;
536
        int i;
537
538 24630
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
539 24630
        AN(clp);
540
541 85032
        for (;clp->desc != NULL; clp++) {
542 60402
                clp->auth = auth;
543 60402
                if (!strcmp(clp->desc->request, "*")) {
544 1578
                        cs->wildcard = clp;
545
                } else {
546 58824
                        i = 0;
547 543746
                        VTAILQ_FOREACH(clp2, &cs->funcs, list) {
548 534710
                                i = strcmp(clp->desc->request,
549 534710
                                    clp2->desc->request);
550 534710
                                if (i <= 0)
551 49788
                                        break;
552
                        }
553 58824
                        if (clp2 != NULL && i == 0) {
554 12384
                                VTAILQ_INSERT_BEFORE(clp2, clp, list);
555 12384
                                VTAILQ_REMOVE(&cs->funcs, clp2, list);
556 46440
                        } else if (clp2 != NULL)
557 37404
                                VTAILQ_INSERT_BEFORE(clp2, clp, list);
558
                        else
559 9036
                                VTAILQ_INSERT_TAIL(&cs->funcs, clp, list);
560
                }
561
        }
562 24630
}
563
564
int
565 52817
VCLS_Poll(struct VCLS *cs, const struct cli *cli, int timeout)
566
{
567
        struct VCLS_fd *cfd;
568
        struct pollfd pfd[1];
569
        int i, j, k;
570
        char buf[BUFSIZ];
571
572 52817
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
573 52817
        if (cs->nfd == 0) {
574 0
                errno = 0;
575 0
                return (-1);
576
        }
577 52817
        assert(cs->nfd > 0);
578
579 52817
        i = 0;
580 181926
        VTAILQ_FOREACH(cfd, &cs->fds, list) {
581 90963
                if (cfd->cli != cli)
582 38146
                        continue;
583 52817
                pfd[i].fd = cfd->fdi;
584 52817
                pfd[i].events = POLLIN;
585 52817
                pfd[i].revents = 0;
586 52817
                i++;
587 52817
                break;
588
        }
589 52817
        assert(i == 1);
590 52817
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
591
592 52817
        j = poll(pfd, 1, timeout);
593 52805
        if (j <= 0)
594 0
                return (j);
595 52805
        if (pfd[0].revents & POLLHUP)
596 2784
                k = 1;
597
        else {
598 50021
                i = read(cfd->fdi, buf, sizeof buf);
599 50021
                if (i <= 0)
600 547
                        k = 1;
601
                else
602 49474
                        k = cls_feed(cfd, buf, buf + i);
603
        }
604 52799
        if (k) {
605 3335
                i = cls_close_fd(cs, cfd);
606 3335
                if (i < 0)
607 1422
                        k = i;
608
        }
609 52799
        return (k);
610
}
611
612
void
613 1426
VCLS_Destroy(struct VCLS **csp)
614
{
615
        struct VCLS *cs;
616
        struct VCLS_fd *cfd, *cfd2;
617
        struct cli_proto *clp;
618
619 1426
        TAKE_OBJ_NOTNULL(cs, csp, VCLS_MAGIC);
620 2327
        VTAILQ_FOREACH_SAFE(cfd, &cs->fds, list, cfd2)
621 901
                (void)cls_close_fd(cs, cfd);
622
623 32798
        while (!VTAILQ_EMPTY(&cs->funcs)) {
624 29946
                clp = VTAILQ_FIRST(&cs->funcs);
625 29946
                VTAILQ_REMOVE(&cs->funcs, clp, list);
626
        }
627 1426
        FREE_OBJ(cs);
628 1426
}
629
630
/**********************************************************************
631
 * Utility functions for implementing CLI commands
632
 */
633
634
/*lint -e{818} cli could be const */
635
void
636 147952
VCLI_Out(struct cli *cli, const char *fmt, ...)
637
{
638
        va_list ap;
639
640 147952
        va_start(ap, fmt);
641 147952
        if (cli != NULL) {
642 147952
                CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
643 147952
                if (VSB_len(cli->sb) < *cli->limit)
644 145972
                        (void)VSB_vprintf(cli->sb, fmt, ap);
645 1980
                else if (cli->result == CLIS_OK)
646 6
                        cli->result = CLIS_TRUNCATED;
647
        } else {
648 0
                (void)vfprintf(stdout, fmt, ap);
649
        }
650 147952
        va_end(ap);
651 147952
}
652
653
/*lint -e{818} cli could be const */
654
int
655 282
VCLI_Overflow(struct cli *cli)
656
{
657 282
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
658 564
        if (cli->result == CLIS_TRUNCATED ||
659 282
            VSB_len(cli->sb) >= *cli->limit)
660 0
                return (1);
661 282
        return (0);
662
}
663
664
/*lint -e{818} cli could be const */
665
void
666 1552
VCLI_JSON_str(struct cli *cli, const char *s)
667
{
668
669 1552
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
670 1552
        VSB_putc(cli->sb, '"');
671 1552
        VSB_quote(cli->sb, s, -1, VSB_QUOTE_JSON);
672 1552
        VSB_putc(cli->sb, '"');
673 1552
}
674
675
/*lint -e{818} cli could be const */
676
void
677 66
VCLI_JSON_begin(struct cli *cli, unsigned ver, const char * const * av)
678
{
679
        int i;
680
681 66
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
682 66
        VCLI_Out(cli, "[ %u, [", ver);
683 208
        for (i = 1; av[i] != NULL; i++) {
684 142
                VCLI_JSON_str(cli, av[i]);
685 142
                if (av[i + 1] != NULL)
686 76
                        VCLI_Out(cli, ", ");
687
        }
688 66
        VCLI_Out(cli, "], %.3f", VTIM_real());
689 66
        VSB_indent(cli->sb, 2);
690 66
}
691
692
void
693 66
VCLI_JSON_end(struct cli *cli)
694
{
695 66
        VSB_indent(cli->sb, -2);
696 66
        VCLI_Out(cli, "\n");
697 66
        VCLI_Out(cli, "]\n");
698 66
}
699
700
/*lint -e{818} cli could be const */
701
void
702 26
VCLI_Quote(struct cli *cli, const char *s)
703
{
704
705 26
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
706 26
        VSB_quote(cli->sb, s, -1, 0);
707 26
}
708
709
void
710 14230
VCLI_SetResult(struct cli *cli, unsigned res)
711
{
712
713 14230
        if (cli != NULL) {
714 14230
                CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
715 14230
                if (cli->result != CLIS_TRUNCATED || res != CLIS_OK)
716 14228
                        cli->result = res;      /*lint !e64 type mismatch */
717
        } else {
718 0
                printf("CLI result = %u\n", res);
719
        }
720 14230
}