| | 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 |
} |