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