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 120
VCLS_func_close(struct cli *cli, const char *const *av, void *priv)
85
{
86
87 120
        (void)av;
88 120
        (void)priv;
89 120
        VCLI_Out(cli, "Closing CLI connection");
90 120
        VCLI_SetResult(cli, CLIS_CLOSE);
91 120
}
92
93
/*--------------------------------------------------------------------*/
94
95
void v_matchproto_(cli_func_t)
96 9513
VCLS_func_ping(struct cli *cli, const char * const *av, void *priv)
97
{
98
        time_t t;
99
100 9513
        (void)av;
101 9513
        (void)priv;
102 9513
        t = time(NULL);
103 9513
        VCLI_Out(cli, "PONG %jd 1.0", (intmax_t)t);
104 9513
}
105
106
void v_matchproto_(cli_func_t)
107 80
VCLS_func_ping_json(struct cli *cli, const char * const *av, void *priv)
108
{
109 80
        (void)av;
110 80
        (void)priv;
111 80
        VCLI_JSON_begin(cli, 2, av);
112 80
        VCLI_Out(cli, ", \"PONG\"\n");
113 80
        VCLI_JSON_end(cli);
114 80
}
115
116
/*--------------------------------------------------------------------*/
117
118
static void
119 9320
help_helper(struct cli *cli, struct cli_proto *clp, const char * const *av)
120
{
121 9320
        AN(clp->desc->syntax);
122 9320
        if (av[0] != NULL)
123 120
                VCLI_Out(cli, "%s\n%s\n", clp->desc->syntax, clp->desc->help);
124
        else
125 9200
                VCLI_Out(cli, "%s\n", clp->desc->syntax);
126 9320
}
127
128
void v_matchproto_(cli_func_t)
129 720
VCLS_func_help(struct cli *cli, const char * const *av, void *priv)
130
{
131
        struct cli_proto *clp;
132 720
        unsigned filter = 1, d;
133
        struct VCLS *cs;
134
135 720
        (void)priv;
136 720
        cs = cli->cls;
137 720
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
138
139 880
        for (av += 2; av[0] != NULL && av[0][0] == '-'; av++) {
140 240
                if (!strcmp(av[0], "-a")) {
141 80
                        filter = 3;
142 240
                } else if (!strcmp(av[0], "-d")) {
143 80
                        filter = 2;
144 80
                } else {
145 80
                        VCLI_Out(cli, "Unknown flag\n");
146 80
                        VCLI_SetResult(cli, CLIS_UNKNOWN);
147 80
                        return;
148
                }
149 160
        }
150 17480
        VTAILQ_FOREACH(clp, &cs->funcs, list) {
151 16960
                if (clp->auth > cli->auth)
152 0
                        continue;
153 16960
                if (av[0] != NULL && !strcmp(clp->desc->request, av[0])) {
154 120
                        help_helper(cli, clp, av);
155 120
                        return;
156 16840
                } else if (av[0] == NULL) {
157 12560
                        d = strchr(clp->flags, 'd') != NULL ? 2 : 1;
158 12560
                        if (filter & d)
159 9200
                                help_helper(cli, clp, av);
160 12560
                }
161 16840
        }
162 520
        if (av[0] != NULL) {
163 80
                VCLI_Out(cli, "Unknown request.\nType 'help' for more info.\n");
164 80
                VCLI_SetResult(cli, CLIS_UNKNOWN);
165 80
        }
166 720
}
167
168
void v_matchproto_(cli_func_t)
169 240
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 240
        (void)priv;
175 240
        cs = cli->cls;
176 240
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
177
178 240
        VCLI_JSON_begin(cli, 2, av);
179 9360
        VTAILQ_FOREACH(clp, &cs->funcs, list) {
180 9120
                if (clp->auth > cli->auth)
181 0
                        continue;
182 9120
                VCLI_Out(cli, ",\n  {\n");
183 9120
                VSB_indent(cli->sb, 2);
184 9120
                VCLI_Out(cli, "\"request\": ");
185 9120
                VCLI_JSON_str(cli, clp->desc->request);
186 9120
                VCLI_Out(cli, ",\n");
187 9120
                VCLI_Out(cli, "\"syntax\": ");
188 9120
                VCLI_JSON_str(cli, clp->desc->syntax);
189 9120
                VCLI_Out(cli, ",\n");
190 9120
                VCLI_Out(cli, "\"help\": ");
191 9120
                VCLI_JSON_str(cli, clp->desc->help);
192 9120
                VCLI_Out(cli, ",\n");
193 9120
                VCLI_Out(cli, "\"minarg\": %d", clp->desc->minarg);
194 9120
                VCLI_Out(cli, ",\n");
195 9120
                VCLI_Out(cli, "\"maxarg\": %d", clp->desc->maxarg);
196 9120
                VCLI_Out(cli, ",\n");
197 9120
                VCLI_Out(cli, "\"flags\": ");
198 9120
                VCLI_JSON_str(cli, clp->flags);
199 9120
                VCLI_Out(cli, ",\n");
200 18240
                VCLI_Out(cli, "\"json\": %s",
201 9120
                    clp->jsonfunc == NULL ? "false" : "true");
202 9120
                VCLI_Out(cli, "\n");
203 9120
                VSB_indent(cli->sb, -2);
204 9120
                VCLI_Out(cli, "}");
205 9120
        }
206 240
        VCLI_JSON_end(cli);
207 240
}
208
209
/*--------------------------------------------------------------------
210
 * Look for a CLI command to execute
211
 */
212
213
static void
214 831313
cls_dispatch(struct cli *cli, struct VCLS *cs, char * const * av, int ac)
215
{
216 831313
        int json = 0;
217
        struct cli_proto *cp;
218
219 831313
        AN(av);
220 831313
        assert(ac >= 0);
221 831313
        AZ(av[0]);
222 831313
        AN(av[1]);
223
224 15174519
        VTAILQ_FOREACH(cp, &cs->funcs, list) {
225 15014439
                if (cp->auth > cli->auth)
226 0
                        continue;
227 15014439
                if (!strcmp(cp->desc->request, av[1]))
228 671233
                        break;
229 14343206
        }
230
231 831313
        if (cp == NULL && cs->wildcard && cs->wildcard->auth <= cli->auth)
232 160040
                cp = cs->wildcard;
233
234 831313
        if (cp == NULL) {
235 40
                VCLI_Out(cli, "Unknown request.\nType 'help' for more info.\n");
236 40
                return;
237
        }
238
239 831273
        VSB_clear(cli->sb);
240
241 831273
        if (ac > 1 && !strcmp(av[2], "-j"))
242 3120
                json = 1;
243
244 831273
        if (cp->func == NULL && !json) {
245 0
                VCLI_Out(cli, "Unimplemented\n");
246 0
                VCLI_SetResult(cli, CLIS_UNIMPL);
247 0
                return;
248
        }
249 831273
        if (cp->jsonfunc == NULL && json) {
250 40
                VCLI_Out(cli, "JSON unimplemented\n");
251 40
                VCLI_SetResult(cli, CLIS_UNIMPL);
252 40
                return;
253
        }
254
255 831233
        if (ac - 1 < cp->desc->minarg + json) {
256 160
                VCLI_Out(cli, "Too few parameters\n");
257 160
                VCLI_SetResult(cli, CLIS_TOOFEW);
258 160
                return;
259
        }
260
261 831073
        if (cp->desc->maxarg >= 0 && ac - 1 > cp->desc->maxarg + json) {
262 80
                VCLI_Out(cli, "Too many parameters\n");
263 80
                VCLI_SetResult(cli, CLIS_TOOMANY);
264 80
                return;
265
        }
266
267 830993
        cli->result = CLIS_OK;
268 830993
        cli->cls = cs;
269 830993
        if (json)
270 3080
                cp->jsonfunc(cli, (const char * const *)av, cp->priv);
271
        else
272 827913
                cp->func(cli, (const char * const *)av, cp->priv);
273 830993
        cli->cls = NULL;
274 831313
}
275
276
/*--------------------------------------------------------------------
277
 * We have collected a full cli line, parse it and execute, if possible.
278
 */
279
280
static int
281 831553
cls_exec(struct VCLS_fd *cfd, char * const *av, int ac)
282
{
283
        struct VCLS *cs;
284
        struct cli *cli;
285
        ssize_t len;
286
        char *s;
287
        unsigned lim;
288 831553
        int retval = 0;
289
290 831553
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
291 831553
        cs = cfd->cls;
292 831553
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
293
294 831553
        cli = cfd->cli;
295 831553
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
296 831553
        AN(cli->cmd);
297
298 831553
        cli->result = CLIS_UNKNOWN;
299 831553
        VSB_clear(cli->sb);
300
301 831553
        if (cs->before != NULL)
302 831553
                cs->before(cli);
303
304 831553
        do {
305 831553
                if (av[0] != NULL) {
306 80
                        VCLI_Out(cli, "Syntax Error: %s\n", av[0]);
307 80
                        VCLI_SetResult(cli, CLIS_SYNTAX);
308 80
                        break;
309
                }
310
311 831473
                if (av[1] == NULL) {
312 40
                        VCLI_Out(cli, "Empty CLI command.\n");
313 40
                        VCLI_SetResult(cli, CLIS_SYNTAX);
314 40
                        break;
315
                }
316
317 831433
                if (!islower(av[1][0])) {
318 120
                        VCLI_Out(cli, "All commands are in lower-case.\n");
319 120
                        VCLI_SetResult(cli, CLIS_UNKNOWN);
320 120
                        break;
321
                }
322
323 831313
                cls_dispatch(cli, cs, av, ac);
324
325 831313
        } while (0);
326
327 831553
        AZ(VSB_finish(cli->sb));
328
329 831553
        if (cs->after != NULL)
330 831473
                cs->after(cli);
331
332 831553
        s = VSB_data(cli->sb);
333 831553
        len = VSB_len(cli->sb);
334 831553
        lim = *cs->limit;
335 831553
        if (len > lim) {
336 200
                if (cli->result == CLIS_OK)
337 0
                        cli->result = CLIS_TRUNCATED;
338 200
                s[lim - 1] = '\0';
339 200
                assert(strlen(s) <= lim);
340 200
        }
341 831553
        if (VCLI_WriteResult(cfd->fdo, cli->result, s) ||
342 831433
            cli->result == CLIS_CLOSE)
343 120
                retval = 1;
344
345
        /*
346
         * In unauthenticated mode we are very intolerant, and close the
347
         * connection at the least provocation.
348
         */
349 831553
        if (cli->auth == 0 && cli->result != CLIS_OK)
350 0
                retval = 1;
351
352 831553
        return (retval);
353
}
354
355
static int
356 1320847
cls_feed(struct VCLS_fd *cfd, const char *p, const char *e)
357
{
358
        struct cli *cli;
359 1320847
        int i, retval = 0, ac;
360
        char **av, *q;
361
362 1320847
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
363 1320847
        AN(p);
364 1320847
        assert(e > p);
365
366 1320847
        cli = cfd->cli;
367 1320847
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
368
369 36730402
        for (;p < e; p++) {
370 35409675
                if (cli->cmd == NULL && isspace(*p)) {
371
                        /* Ignore all leading space before cmd */
372 95520
                        continue;
373
                }
374 35314155
                if (cfd->argv == NULL) {
375
376
                        /* Collect first line up to \n or \r */
377 17078470
                        if (cli->cmd == NULL) {
378 831633
                                cli->cmd = VSB_new_auto();
379 831633
                                AN(cli->cmd);
380 831633
                        }
381
382
                        /* Until authenticated, limit length hard */
383 19758430
                        if (*p != '\n' && *p != '\r' &&
384 16246997
                            (cli->auth > 0 || VSB_len(cli->cmd) < 80)) {
385 16246997
                                VSB_putc(cli->cmd, *p);
386 16246997
                                continue;
387
                        }
388
389 831473
                        AZ(VSB_finish(cli->cmd));
390
391
                        /* Ignore leading '-' */
392 831473
                        q = VSB_data(cli->cmd);
393 831473
                        if (*q == '-')
394 160
                                q++;
395 831473
                        av = VAV_Parse(q, &ac, 0);
396 831473
                        AN(av);
397
398 889593
                        if (cli->auth > 0 &&
399 792753
                            av[0] == NULL &&
400 792673
                            ac >= 3 &&
401 320840
                            !strcmp(av[ac-2], "<<") &&
402 58120
                            *av[ac - 1] != '\0') {
403
                                /* Go to "<< nonce" mode */
404 58120
                                cfd->argv = av;
405 58120
                                cfd->argc = ac;
406 58120
                                cfd->match = av[ac - 1];
407 58120
                                cfd->last_arg = VSB_new_auto();
408 58120
                                AN(cfd->last_arg);
409 58120
                        } else {
410
                                /* Plain command */
411 773353
                                i = cls_exec(cfd, av, ac - 1);
412 773353
                                VAV_Free(av);
413 773353
                                VSB_destroy(&cli->cmd);
414 773353
                                if (i)
415 120
                                        return (i);
416
                        }
417 831353
                } else {
418
                        /* "<< nonce" mode */
419 18235685
                        AN(cfd->argv);
420 18235685
                        AN(cfd->argc);
421 18235685
                        AN(cfd->match);
422 18235685
                        AN(cfd->last_arg);
423 18235685
                        if (*cfd->match == '\0' && (*p == '\r' || *p == '\n')) {
424 58080
                                AZ(VSB_finish(cfd->last_arg));
425
                                // NB: VAV lib internals trusted
426 58080
                                cfd->match = NULL;
427 58080
                                REPLACE(cfd->argv[cfd->argc - 1], NULL);
428 58080
                                REPLACE(cfd->argv[cfd->argc - 2], NULL);
429 58080
                                cfd->argv[cfd->argc - 2] =
430 58080
                                    VSB_data(cfd->last_arg);
431 58080
                                i = cls_exec(cfd, cfd->argv, cfd->argc - 2);
432 58080
                                cfd->argv[cfd->argc - 2] = NULL;
433 58080
                                VAV_Free(cfd->argv);
434 58080
                                cfd->argv = NULL;
435 58080
                                VSB_destroy(&cfd->last_arg);
436 58080
                                VSB_destroy(&cli->cmd);
437 58080
                                if (i)
438 0
                                        return (i);
439 18235685
                        } else if (*p == *cfd->match) {
440 1041960
                                cfd->match++;
441 18177605
                        } else if (cfd->match != cfd->argv[cfd->argc - 1]) {
442 113720
                                q = cfd->argv[cfd->argc - 1];
443 113720
                                VSB_bcat(cfd->last_arg, q, cfd->match - q);
444 113720
                                cfd->match = q;
445 113720
                                VSB_putc(cfd->last_arg, *p);
446 113720
                        } else {
447 17021925
                                VSB_putc(cfd->last_arg, *p);
448
                        }
449
                }
450 19067038
        }
451 1320727
        return (retval);
452 1320847
}
453
454
struct VCLS *
455 77476
VCLS_New(struct VCLS *model)
456
{
457
        struct VCLS *cs;
458
459 77476
        CHECK_OBJ_ORNULL(model, VCLS_MAGIC);
460
461 77476
        ALLOC_OBJ(cs, VCLS_MAGIC);
462 77476
        AN(cs);
463 77476
        VTAILQ_INIT(&cs->fds);
464 77476
        VTAILQ_INIT(&cs->funcs);
465 77476
        if (model != NULL)
466 36676
                VTAILQ_CONCAT(&cs->funcs, &model->funcs, list);
467 77476
        return (cs);
468
}
469
470
void
471 74796
VCLS_SetLimit(struct VCLS *cs, volatile unsigned *limit)
472
{
473 74796
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
474 74796
        cs->limit = limit;
475 74796
}
476
477
void
478 77476
VCLS_SetHooks(struct VCLS *cs, cls_cbc_f *before, cls_cbc_f *after)
479
{
480
481 77476
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
482 77476
        cs->before = before;
483 77476
        cs->after = after;
484 77476
}
485
486
struct cli *
487 112840
VCLS_AddFd(struct VCLS *cs, int fdi, int fdo, cls_cb_f *closefunc, void *priv)
488
{
489
        struct VCLS_fd *cfd;
490
491 112840
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
492 112840
        assert(fdi >= 0);
493 112840
        assert(fdo >= 0);
494 112840
        ALLOC_OBJ(cfd, VCLS_FD_MAGIC);
495 112840
        AN(cfd);
496 112840
        cfd->cls = cs;
497 112840
        cfd->fdi = fdi;
498 112840
        cfd->fdo = fdo;
499 112840
        cfd->cli = &cfd->clis;
500 112840
        cfd->cli->magic = CLI_MAGIC;
501 112840
        cfd->cli->sb = VSB_new_auto();
502 112840
        AN(cfd->cli->sb);
503 112840
        cfd->cli->limit = cs->limit;
504 112840
        cfd->cli->priv = priv;
505 112840
        cfd->closefunc = closefunc;
506 112840
        cfd->priv = priv;
507 112840
        VTAILQ_INSERT_TAIL(&cs->fds, cfd, list);
508 112840
        cs->nfd++;
509 112840
        return (cfd->cli);
510
}
511
512
static int
513 112440
cls_close_fd(struct VCLS *cs, struct VCLS_fd *cfd)
514
{
515 112440
        int retval = 0;
516
517 112440
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
518 112440
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
519
520 112440
        VTAILQ_REMOVE(&cs->fds, cfd, list);
521 112440
        if (cfd->match != NULL) {
522 80
                cfd->cli->result = CLIS_TRUNCATED;
523 80
                if (cs->after != NULL)
524 40
                        cs->after(cfd->cli);
525 80
                VSB_destroy(&cfd->last_arg);
526 112440
        } else if (cfd->cli->cmd != NULL) {
527 80
                (void)VSB_finish(cfd->cli->cmd);
528 80
                cfd->cli->result = CLIS_TRUNCATED;
529 80
                if (cs->after != NULL)
530 40
                        cs->after(cfd->cli);
531 80
                VSB_destroy(&cfd->cli->cmd);
532 80
        }
533 112280
        cs->nfd--;
534 112280
        VSB_destroy(&cfd->cli->sb);
535 112280
        if (cfd->closefunc != NULL)
536 74480
                retval = cfd->closefunc(cfd->priv);
537 112280
        (void)close(cfd->fdi);
538 112280
        if (cfd->fdo != cfd->fdi)
539 37280
                (void)close(cfd->fdo);
540 112280
        if (cfd->cli->ident != NULL)
541 76120
                free(cfd->cli->ident);
542 112280
        FREE_OBJ(cfd);
543 112280
        return (retval);
544
}
545
546
void
547 722171
VCLS_AddFunc(struct VCLS *cs, unsigned auth, struct cli_proto *clp)
548
{
549
        struct cli_proto *clp2;
550
        int i;
551
552 722171
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
553 722171
        AN(clp);
554
555 2544643
        for (;clp->desc != NULL; clp++) {
556 1822472
                clp->auth = auth;
557 1822472
                if (!strcmp(clp->desc->request, "*")) {
558 40800
                        cs->wildcard = clp;
559 40800
                } else {
560 1781672
                        i = 0;
561 18803308
                        VTAILQ_FOREACH(clp2, &cs->funcs, list) {
562 37133336
                                i = strcmp(clp->desc->request,
563 18566668
                                    clp2->desc->request);
564 18566668
                                if (i <= 0)
565 1545032
                                        break;
566 17021636
                        }
567 1781672
                        if (clp2 != NULL && i == 0) {
568 329999
                                VTAILQ_INSERT_BEFORE(clp2, clp, list);
569 329999
                                VTAILQ_REMOVE(&cs->funcs, clp2, list);
570 1781672
                        } else if (clp2 != NULL)
571 1215033
                                VTAILQ_INSERT_BEFORE(clp2, clp, list);
572
                        else
573 236640
                                VTAILQ_INSERT_TAIL(&cs->funcs, clp, list);
574
                }
575 1822472
        }
576 722171
}
577
578
int
579 1430249
VCLS_Poll(struct VCLS *cs, const struct cli *cli, int timeout)
580
{
581
        struct VCLS_fd *cfd;
582
        struct pollfd pfd[1];
583
        int i, j, k;
584
        char buf[BUFSIZ];
585
586 1430249
        CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
587 1430249
        if (cs->nfd == 0) {
588 0
                errno = 0;
589 0
                return (-1);
590
        }
591 1430249
        assert(cs->nfd > 0);
592
593 1430249
        i = 0;
594 2482365
        VTAILQ_FOREACH(cfd, &cs->fds, list) {
595 2482085
                if (cfd->cli != cli)
596 1052116
                        continue;
597 1429969
                pfd[i].fd = cfd->fdi;
598 1429969
                pfd[i].events = POLLIN;
599 1429969
                pfd[i].revents = 0;
600 1429969
                i++;
601 1429969
                break;
602
        }
603 1430249
        assert(i == 1);
604 1429689
        CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC);
605
606 1429689
        j = poll(pfd, 1, timeout);
607 1429689
        if (j <= 0)
608 0
                return (j);
609 1429689
        if (pfd[0].revents & POLLHUP)
610 73320
                k = 1;
611
        else {
612 1356369
                i = read(cfd->fdi, buf, sizeof buf);
613 1356369
                if (i <= 0)
614 35402
                        k = 1;
615
                else
616 1320967
                        k = cls_feed(cfd, buf, buf + i);
617
        }
618 1429689
        if (k) {
619 108762
                i = cls_close_fd(cs, cfd);
620 108762
                if (i < 0)
621 37200
                        k = i;
622 108762
        }
623 1429689
        return (k);
624 1429689
}
625
626
void
627 37360
VCLS_Destroy(struct VCLS **csp)
628
{
629
        struct VCLS *cs;
630
        struct VCLS_fd *cfd, *cfd2;
631
        struct cli_proto *clp;
632
633 37360
        TAKE_OBJ_NOTNULL(cs, csp, VCLS_MAGIC);
634 40878
        VTAILQ_FOREACH_SAFE(cfd, &cs->fds, list, cfd2)
635 3518
                (void)cls_close_fd(cs, cfd);
636
637 971360
        while (!VTAILQ_EMPTY(&cs->funcs)) {
638 934000
                clp = VTAILQ_FIRST(&cs->funcs);
639 934000
                VTAILQ_REMOVE(&cs->funcs, clp, list);
640
        }
641 37360
        FREE_OBJ(cs);
642 37360
}
643
644
/**********************************************************************
645
 * Utility functions for implementing CLI commands
646
 */
647
648
static void
649 4201001
vcli_outv(struct cli *cli, const char *fmt, va_list ap)
650
{
651
652 4201001
        if (VSB_len(cli->sb) < *cli->limit)
653 4155481
                (void)VSB_vprintf(cli->sb, fmt, ap);
654 45520
        else if (cli->result == CLIS_OK)
655 200
                cli->result = CLIS_TRUNCATED;
656 4201001
}
657
658
/*lint -e{818} cli could be const */
659
void
660 3780273
VCLI_Out(struct cli *cli, const char *fmt, ...)
661
{
662
        va_list ap;
663
664 3780273
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
665 3780273
        AN(fmt);
666
667 3780273
        va_start(ap, fmt);
668 3780273
        vcli_outv(cli, fmt, ap);
669 3780273
        va_end(ap);
670 3780273
}
671
672
int v_matchproto_(VTE_format_f)
673 420728
VCLI_VTE_format(void *priv, const char *fmt, ...)
674
{
675
        struct cli *cli;
676
        va_list ap;
677
678 420728
        CAST_OBJ_NOTNULL(cli, priv, CLI_MAGIC);
679 420728
        AN(fmt);
680
681 420728
        va_start(ap, fmt);
682 420728
        vcli_outv(cli, fmt, ap);
683 420728
        va_end(ap);
684
685 420728
        return (0);
686
}
687
688
/*lint -e{818} cli could be const */
689
int
690 11040
VCLI_Overflow(struct cli *cli)
691
{
692 11040
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
693 11040
        if (cli->result == CLIS_TRUNCATED ||
694 11040
            VSB_len(cli->sb) >= *cli->limit)
695 0
                return (1);
696 11040
        return (0);
697 11040
}
698
699
/*lint -e{818} cli could be const */
700
void
701 91400
VCLI_JSON_str(struct cli *cli, const char *s)
702
{
703
704 91400
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
705 91400
        VSB_putc(cli->sb, '"');
706 91400
        VSB_quote(cli->sb, s, -1, VSB_QUOTE_JSON);
707 91400
        VSB_putc(cli->sb, '"');
708 91400
}
709
710
/*lint -e{818} cli could be const */
711
void
712 2120
VCLI_JSON_begin(struct cli *cli, unsigned ver, const char * const * av)
713
{
714
        int i;
715
716 2120
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
717 2120
        VCLI_Out(cli, "[ %u, [", ver);
718 6760
        for (i = 1; av[i] != NULL; i++) {
719 4640
                VCLI_JSON_str(cli, av[i]);
720 4640
                if (av[i + 1] != NULL)
721 2520
                        VCLI_Out(cli, ", ");
722 4640
        }
723 2120
        VCLI_Out(cli, "], %.3f", VTIM_real());
724 2120
        VSB_indent(cli->sb, 2);
725 2120
}
726
727
void
728 2120
VCLI_JSON_end(struct cli *cli)
729
{
730 2120
        VSB_indent(cli->sb, -2);
731 2120
        VCLI_Out(cli, "\n");
732 2120
        VCLI_Out(cli, "]\n");
733 2120
}
734
735
/*lint -e{818} cli could be const */
736
void
737 520
VCLI_Quote(struct cli *cli, const char *s)
738
{
739
740 520
        CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
741 520
        VSB_quote(cli->sb, s, -1, 0);
742 520
}
743
744
void
745 378040
VCLI_SetResult(struct cli *cli, unsigned res)
746
{
747
748 378040
        if (cli != NULL) {
749 378040
                CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
750 378040
                if (cli->result != CLIS_TRUNCATED || res != CLIS_OK)
751 378000
                        cli->result = res;      /*lint !e64 type mismatch */
752 378040
        } else {
753 0
                printf("CLI result = %u\n", res);
754
        }
755 378040
}