| | 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 |
9656 |
VCLS_func_ping(struct cli *cli, const char * const *av, void *priv) |
| 97 |
|
{ |
| 98 |
|
time_t t; |
| 99 |
|
|
| 100 |
9656 |
(void)av; |
| 101 |
9656 |
(void)priv; |
| 102 |
9656 |
t = time(NULL); |
| 103 |
9656 |
VCLI_Out(cli, "PONG %jd 1.0", (intmax_t)t); |
| 104 |
9656 |
} |
| 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 |
860696 |
cls_dispatch(struct cli *cli, struct VCLS *cs, char * const * av, int ac) |
| 215 |
|
{ |
| 216 |
860696 |
int json = 0; |
| 217 |
|
struct cli_proto *cp; |
| 218 |
|
|
| 219 |
860696 |
AN(av); |
| 220 |
860696 |
assert(ac >= 0); |
| 221 |
860696 |
AZ(av[0]); |
| 222 |
860696 |
AN(av[1]); |
| 223 |
|
|
| 224 |
15713208 |
VTAILQ_FOREACH(cp, &cs->funcs, list) { |
| 225 |
15547488 |
if (cp->auth > cli->auth) |
| 226 |
0 |
continue; |
| 227 |
15547488 |
if (!strcmp(cp->desc->request, av[1])) |
| 228 |
694976 |
break; |
| 229 |
14852512 |
} |
| 230 |
|
|
| 231 |
860696 |
if (cp == NULL && cs->wildcard && cs->wildcard->auth <= cli->auth) |
| 232 |
165680 |
cp = cs->wildcard; |
| 233 |
|
|
| 234 |
860696 |
if (cp == NULL) { |
| 235 |
40 |
VCLI_Out(cli, "Unknown request.\nType 'help' for more info.\n"); |
| 236 |
40 |
return; |
| 237 |
|
} |
| 238 |
|
|
| 239 |
860656 |
VSB_clear(cli->sb); |
| 240 |
|
|
| 241 |
860656 |
if (ac > 1 && !strcmp(av[2], "-j")) |
| 242 |
3200 |
json = 1; |
| 243 |
|
|
| 244 |
860656 |
if (cp->func == NULL && !json) { |
| 245 |
0 |
VCLI_Out(cli, "Unimplemented\n"); |
| 246 |
0 |
VCLI_SetResult(cli, CLIS_UNIMPL); |
| 247 |
0 |
return; |
| 248 |
|
} |
| 249 |
860656 |
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 |
860616 |
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 |
860456 |
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 |
860376 |
cli->result = CLIS_OK; |
| 268 |
860376 |
cli->cls = cs; |
| 269 |
860376 |
if (json) |
| 270 |
3160 |
cp->jsonfunc(cli, (const char * const *)av, cp->priv); |
| 271 |
|
else |
| 272 |
857216 |
cp->func(cli, (const char * const *)av, cp->priv); |
| 273 |
860376 |
cli->cls = NULL; |
| 274 |
860696 |
} |
| 275 |
|
|
| 276 |
|
/*-------------------------------------------------------------------- |
| 277 |
|
* We have collected a full cli line, parse it and execute, if possible. |
| 278 |
|
*/ |
| 279 |
|
|
| 280 |
|
static int |
| 281 |
860936 |
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 |
860936 |
int retval = 0; |
| 289 |
|
|
| 290 |
860936 |
CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC); |
| 291 |
860936 |
cs = cfd->cls; |
| 292 |
860936 |
CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC); |
| 293 |
|
|
| 294 |
860936 |
cli = cfd->cli; |
| 295 |
860936 |
CHECK_OBJ_NOTNULL(cli, CLI_MAGIC); |
| 296 |
860936 |
AN(cli->cmd); |
| 297 |
|
|
| 298 |
860936 |
cli->result = CLIS_UNKNOWN; |
| 299 |
860936 |
VSB_clear(cli->sb); |
| 300 |
|
|
| 301 |
860936 |
if (cs->before != NULL) |
| 302 |
860936 |
cs->before(cli); |
| 303 |
|
|
| 304 |
860936 |
do { |
| 305 |
860936 |
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 |
860856 |
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 |
860816 |
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 |
860696 |
cls_dispatch(cli, cs, av, ac); |
| 324 |
|
|
| 325 |
860696 |
} while (0); |
| 326 |
|
|
| 327 |
860936 |
AZ(VSB_finish(cli->sb)); |
| 328 |
|
|
| 329 |
860936 |
if (cs->after != NULL) |
| 330 |
860856 |
cs->after(cli); |
| 331 |
|
|
| 332 |
860936 |
s = VSB_data(cli->sb); |
| 333 |
860936 |
len = VSB_len(cli->sb); |
| 334 |
860936 |
lim = *cs->limit; |
| 335 |
860936 |
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 |
860936 |
if (VCLI_WriteResult(cfd->fdo, cli->result, s) || |
| 342 |
860816 |
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 |
860936 |
if (cli->auth == 0 && cli->result != CLIS_OK) |
| 350 |
0 |
retval = 1; |
| 351 |
|
|
| 352 |
860936 |
return (retval); |
| 353 |
|
} |
| 354 |
|
|
| 355 |
|
static int |
| 356 |
1367424 |
cls_feed(struct VCLS_fd *cfd, const char *p, const char *e) |
| 357 |
|
{ |
| 358 |
|
struct cli *cli; |
| 359 |
1367424 |
int i, retval = 0, ac; |
| 360 |
|
char **av, *q; |
| 361 |
|
|
| 362 |
1367424 |
CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC); |
| 363 |
1367424 |
AN(p); |
| 364 |
1367424 |
assert(e > p); |
| 365 |
|
|
| 366 |
1367424 |
cli = cfd->cli; |
| 367 |
1367424 |
CHECK_OBJ_NOTNULL(cli, CLI_MAGIC); |
| 368 |
|
|
| 369 |
38062712 |
for (;p < e; p++) { |
| 370 |
36695408 |
if (cli->cmd == NULL && isspace(*p)) { |
| 371 |
|
/* Ignore all leading space before cmd */ |
| 372 |
98600 |
continue; |
| 373 |
|
} |
| 374 |
36596808 |
if (cfd->argv == NULL) { |
| 375 |
|
|
| 376 |
|
/* Collect first line up to \n or \r */ |
| 377 |
17699797 |
if (cli->cmd == NULL) { |
| 378 |
861016 |
cli->cmd = VSB_new_auto(); |
| 379 |
861016 |
AN(cli->cmd); |
| 380 |
861016 |
} |
| 381 |
|
|
| 382 |
|
/* Until authenticated, limit length hard */ |
| 383 |
20465317 |
if (*p != '\n' && *p != '\r' && |
| 384 |
16838941 |
(cli->auth > 0 || VSB_len(cli->cmd) < 80)) { |
| 385 |
16838941 |
VSB_putc(cli->cmd, *p); |
| 386 |
16838941 |
continue; |
| 387 |
|
} |
| 388 |
|
|
| 389 |
860856 |
AZ(VSB_finish(cli->cmd)); |
| 390 |
|
|
| 391 |
|
/* Ignore leading '-' */ |
| 392 |
860856 |
q = VSB_data(cli->cmd); |
| 393 |
860856 |
if (*q == '-') |
| 394 |
160 |
q++; |
| 395 |
860856 |
av = VAV_Parse(q, &ac, 0); |
| 396 |
860856 |
AN(av); |
| 397 |
|
|
| 398 |
920816 |
if (cli->auth > 0 && |
| 399 |
820896 |
av[0] == NULL && |
| 400 |
820816 |
ac >= 3 && |
| 401 |
332720 |
!strcmp(av[ac-2], "<<") && |
| 402 |
59960 |
*av[ac - 1] != '\0') { |
| 403 |
|
/* Go to "<< nonce" mode */ |
| 404 |
59960 |
cfd->argv = av; |
| 405 |
59960 |
cfd->argc = ac; |
| 406 |
59960 |
cfd->match = av[ac - 1]; |
| 407 |
59960 |
cfd->last_arg = VSB_new_auto(); |
| 408 |
59960 |
AN(cfd->last_arg); |
| 409 |
59960 |
} else { |
| 410 |
|
/* Plain command */ |
| 411 |
800896 |
i = cls_exec(cfd, av, ac - 1); |
| 412 |
800896 |
VAV_Free(av); |
| 413 |
800896 |
VSB_destroy(&cli->cmd); |
| 414 |
800896 |
if (i) |
| 415 |
120 |
return (i); |
| 416 |
|
} |
| 417 |
860736 |
} else { |
| 418 |
|
/* "<< nonce" mode */ |
| 419 |
18897011 |
AN(cfd->argv); |
| 420 |
18897011 |
AN(cfd->argc); |
| 421 |
18897011 |
AN(cfd->match); |
| 422 |
18897011 |
AN(cfd->last_arg); |
| 423 |
18897011 |
if (*cfd->match == '\0' && (*p == '\r' || *p == '\n')) { |
| 424 |
59920 |
AZ(VSB_finish(cfd->last_arg)); |
| 425 |
|
// NB: VAV lib internals trusted |
| 426 |
59920 |
cfd->match = NULL; |
| 427 |
59920 |
REPLACE(cfd->argv[cfd->argc - 1], NULL); |
| 428 |
59920 |
REPLACE(cfd->argv[cfd->argc - 2], NULL); |
| 429 |
59920 |
cfd->argv[cfd->argc - 2] = |
| 430 |
59920 |
VSB_data(cfd->last_arg); |
| 431 |
59920 |
i = cls_exec(cfd, cfd->argv, cfd->argc - 2); |
| 432 |
59920 |
cfd->argv[cfd->argc - 2] = NULL; |
| 433 |
59920 |
VAV_Free(cfd->argv); |
| 434 |
59920 |
cfd->argv = NULL; |
| 435 |
59920 |
VSB_destroy(&cfd->last_arg); |
| 436 |
59920 |
VSB_destroy(&cli->cmd); |
| 437 |
59920 |
if (i) |
| 438 |
0 |
return (i); |
| 439 |
18897011 |
} else if (*p == *cfd->match) { |
| 440 |
1071400 |
cfd->match++; |
| 441 |
18837091 |
} 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 |
17651971 |
VSB_putc(cfd->last_arg, *p); |
| 448 |
|
} |
| 449 |
|
} |
| 450 |
19757747 |
} |
| 451 |
1367304 |
return (retval); |
| 452 |
1367424 |
} |
| 453 |
|
|
| 454 |
|
struct VCLS * |
| 455 |
80233 |
VCLS_New(struct VCLS *model) |
| 456 |
|
{ |
| 457 |
|
struct VCLS *cs; |
| 458 |
|
|
| 459 |
80233 |
CHECK_OBJ_ORNULL(model, VCLS_MAGIC); |
| 460 |
|
|
| 461 |
80233 |
ALLOC_OBJ(cs, VCLS_MAGIC); |
| 462 |
80233 |
AN(cs); |
| 463 |
80233 |
VTAILQ_INIT(&cs->fds); |
| 464 |
80233 |
VTAILQ_INIT(&cs->funcs); |
| 465 |
80233 |
if (model != NULL) |
| 466 |
38033 |
VTAILQ_CONCAT(&cs->funcs, &model->funcs, list); |
| 467 |
80233 |
return (cs); |
| 468 |
|
} |
| 469 |
|
|
| 470 |
|
void |
| 471 |
77513 |
VCLS_SetLimit(struct VCLS *cs, volatile unsigned *limit) |
| 472 |
|
{ |
| 473 |
77513 |
CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC); |
| 474 |
77513 |
cs->limit = limit; |
| 475 |
77513 |
} |
| 476 |
|
|
| 477 |
|
void |
| 478 |
80233 |
VCLS_SetHooks(struct VCLS *cs, cls_cbc_f *before, cls_cbc_f *after) |
| 479 |
|
{ |
| 480 |
|
|
| 481 |
80233 |
CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC); |
| 482 |
80233 |
cs->before = before; |
| 483 |
80233 |
cs->after = after; |
| 484 |
80233 |
} |
| 485 |
|
|
| 486 |
|
struct cli * |
| 487 |
116680 |
VCLS_AddFd(struct VCLS *cs, int fdi, int fdo, cls_cb_f *closefunc, void *priv) |
| 488 |
|
{ |
| 489 |
|
struct VCLS_fd *cfd; |
| 490 |
|
|
| 491 |
116680 |
CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC); |
| 492 |
116680 |
assert(fdi >= 0); |
| 493 |
116680 |
assert(fdo >= 0); |
| 494 |
116680 |
ALLOC_OBJ(cfd, VCLS_FD_MAGIC); |
| 495 |
116680 |
AN(cfd); |
| 496 |
116680 |
cfd->cls = cs; |
| 497 |
116680 |
cfd->fdi = fdi; |
| 498 |
116680 |
cfd->fdo = fdo; |
| 499 |
116680 |
cfd->cli = &cfd->clis; |
| 500 |
116680 |
cfd->cli->magic = CLI_MAGIC; |
| 501 |
116680 |
cfd->cli->sb = VSB_new_auto(); |
| 502 |
116680 |
AN(cfd->cli->sb); |
| 503 |
116680 |
cfd->cli->limit = cs->limit; |
| 504 |
116680 |
cfd->cli->priv = priv; |
| 505 |
116680 |
cfd->closefunc = closefunc; |
| 506 |
116680 |
cfd->priv = priv; |
| 507 |
116680 |
VTAILQ_INSERT_TAIL(&cs->fds, cfd, list); |
| 508 |
116680 |
cs->nfd++; |
| 509 |
116680 |
return (cfd->cli); |
| 510 |
|
} |
| 511 |
|
|
| 512 |
|
static int |
| 513 |
116280 |
cls_close_fd(struct VCLS *cs, struct VCLS_fd *cfd) |
| 514 |
|
{ |
| 515 |
116280 |
int retval = 0; |
| 516 |
|
|
| 517 |
116280 |
CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC); |
| 518 |
116280 |
CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC); |
| 519 |
|
|
| 520 |
116280 |
VTAILQ_REMOVE(&cs->fds, cfd, list); |
| 521 |
116280 |
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 |
116280 |
} 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 |
116120 |
cs->nfd--; |
| 534 |
116120 |
VSB_destroy(&cfd->cli->sb); |
| 535 |
116120 |
if (cfd->closefunc != NULL) |
| 536 |
76960 |
retval = cfd->closefunc(cfd->priv); |
| 537 |
116120 |
(void)close(cfd->fdi); |
| 538 |
116120 |
if (cfd->fdo != cfd->fdi) |
| 539 |
38520 |
(void)close(cfd->fdo); |
| 540 |
116120 |
if (cfd->cli->ident != NULL) |
| 541 |
78600 |
free(cfd->cli->ident); |
| 542 |
116120 |
FREE_OBJ(cfd); |
| 543 |
116120 |
return (retval); |
| 544 |
|
} |
| 545 |
|
|
| 546 |
|
void |
| 547 |
748173 |
VCLS_AddFunc(struct VCLS *cs, unsigned auth, struct cli_proto *clp) |
| 548 |
|
{ |
| 549 |
|
struct cli_proto *clp2; |
| 550 |
|
int i; |
| 551 |
|
|
| 552 |
748173 |
CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC); |
| 553 |
748173 |
AN(clp); |
| 554 |
|
|
| 555 |
2636251 |
for (;clp->desc != NULL; clp++) { |
| 556 |
1888078 |
clp->auth = auth; |
| 557 |
1888078 |
if (!strcmp(clp->desc->request, "*")) { |
| 558 |
42200 |
cs->wildcard = clp; |
| 559 |
42200 |
} else { |
| 560 |
1845878 |
i = 0; |
| 561 |
19487522 |
VTAILQ_FOREACH(clp2, &cs->funcs, list) { |
| 562 |
38485204 |
i = strcmp(clp->desc->request, |
| 563 |
19242602 |
clp2->desc->request); |
| 564 |
19242602 |
if (i <= 0) |
| 565 |
1600958 |
break; |
| 566 |
17641644 |
} |
| 567 |
1845878 |
if (clp2 != NULL && i == 0) { |
| 568 |
342211 |
VTAILQ_INSERT_BEFORE(clp2, clp, list); |
| 569 |
342211 |
VTAILQ_REMOVE(&cs->funcs, clp2, list); |
| 570 |
1845878 |
} else if (clp2 != NULL) |
| 571 |
1258747 |
VTAILQ_INSERT_BEFORE(clp2, clp, list); |
| 572 |
|
else |
| 573 |
244920 |
VTAILQ_INSERT_TAIL(&cs->funcs, clp, list); |
| 574 |
|
} |
| 575 |
1888078 |
} |
| 576 |
748173 |
} |
| 577 |
|
|
| 578 |
|
int |
| 579 |
1480472 |
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 |
1480472 |
CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC); |
| 587 |
1480472 |
if (cs->nfd == 0) { |
| 588 |
0 |
errno = 0; |
| 589 |
0 |
return (-1); |
| 590 |
|
} |
| 591 |
1480472 |
assert(cs->nfd > 0); |
| 592 |
|
|
| 593 |
1480472 |
i = 0; |
| 594 |
2569633 |
VTAILQ_FOREACH(cfd, &cs->fds, list) { |
| 595 |
2569353 |
if (cfd->cli != cli) |
| 596 |
1089161 |
continue; |
| 597 |
1480192 |
pfd[i].fd = cfd->fdi; |
| 598 |
1480192 |
pfd[i].events = POLLIN; |
| 599 |
1480192 |
pfd[i].revents = 0; |
| 600 |
1480192 |
i++; |
| 601 |
1480192 |
break; |
| 602 |
|
} |
| 603 |
1480472 |
assert(i == 1); |
| 604 |
1479912 |
CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC); |
| 605 |
|
|
| 606 |
1479912 |
j = poll(pfd, 1, timeout); |
| 607 |
1479912 |
if (j <= 0) |
| 608 |
0 |
return (j); |
| 609 |
1479912 |
if (pfd[0].revents & POLLHUP) |
| 610 |
75920 |
k = 1; |
| 611 |
|
else { |
| 612 |
1403992 |
i = read(cfd->fdi, buf, sizeof buf); |
| 613 |
1403992 |
if (i <= 0) |
| 614 |
36448 |
k = 1; |
| 615 |
|
else |
| 616 |
1367544 |
k = cls_feed(cfd, buf, buf + i); |
| 617 |
|
} |
| 618 |
1479912 |
if (k) { |
| 619 |
112408 |
i = cls_close_fd(cs, cfd); |
| 620 |
112408 |
if (i < 0) |
| 621 |
38440 |
k = i; |
| 622 |
112408 |
} |
| 623 |
1479912 |
return (k); |
| 624 |
1479912 |
} |
| 625 |
|
|
| 626 |
|
void |
| 627 |
38600 |
VCLS_Destroy(struct VCLS **csp) |
| 628 |
|
{ |
| 629 |
|
struct VCLS *cs; |
| 630 |
|
struct VCLS_fd *cfd, *cfd2; |
| 631 |
|
struct cli_proto *clp; |
| 632 |
|
|
| 633 |
38600 |
TAKE_OBJ_NOTNULL(cs, csp, VCLS_MAGIC); |
| 634 |
42312 |
VTAILQ_FOREACH_SAFE(cfd, &cs->fds, list, cfd2) |
| 635 |
3712 |
(void)cls_close_fd(cs, cfd); |
| 636 |
|
|
| 637 |
1003600 |
while (!VTAILQ_EMPTY(&cs->funcs)) { |
| 638 |
965000 |
clp = VTAILQ_FIRST(&cs->funcs); |
| 639 |
965000 |
VTAILQ_REMOVE(&cs->funcs, clp, list); |
| 640 |
|
} |
| 641 |
38600 |
FREE_OBJ(cs); |
| 642 |
38600 |
} |
| 643 |
|
|
| 644 |
|
/********************************************************************** |
| 645 |
|
* Utility functions for implementing CLI commands |
| 646 |
|
*/ |
| 647 |
|
|
| 648 |
|
static void |
| 649 |
4377926 |
vcli_outv(struct cli *cli, const char *fmt, va_list ap) |
| 650 |
|
{ |
| 651 |
|
|
| 652 |
4377926 |
if (VSB_len(cli->sb) < *cli->limit) |
| 653 |
4331206 |
(void)VSB_vprintf(cli->sb, fmt, ap); |
| 654 |
46720 |
else if (cli->result == CLIS_OK) |
| 655 |
200 |
cli->result = CLIS_TRUNCATED; |
| 656 |
4377926 |
} |
| 657 |
|
|
| 658 |
|
/*lint -e{818} cli could be const */ |
| 659 |
|
void |
| 660 |
3946734 |
VCLI_Out(struct cli *cli, const char *fmt, ...) |
| 661 |
|
{ |
| 662 |
|
va_list ap; |
| 663 |
|
|
| 664 |
3946734 |
CHECK_OBJ_NOTNULL(cli, CLI_MAGIC); |
| 665 |
3946734 |
AN(fmt); |
| 666 |
|
|
| 667 |
3946734 |
va_start(ap, fmt); |
| 668 |
3946734 |
vcli_outv(cli, fmt, ap); |
| 669 |
3946734 |
va_end(ap); |
| 670 |
3946734 |
} |
| 671 |
|
|
| 672 |
|
int v_matchproto_(VTE_format_f) |
| 673 |
431192 |
VCLI_VTE_format(void *priv, const char *fmt, ...) |
| 674 |
|
{ |
| 675 |
|
struct cli *cli; |
| 676 |
|
va_list ap; |
| 677 |
|
|
| 678 |
431192 |
CAST_OBJ_NOTNULL(cli, priv, CLI_MAGIC); |
| 679 |
431192 |
AN(fmt); |
| 680 |
|
|
| 681 |
431192 |
va_start(ap, fmt); |
| 682 |
431192 |
vcli_outv(cli, fmt, ap); |
| 683 |
431192 |
va_end(ap); |
| 684 |
|
|
| 685 |
431192 |
return (0); |
| 686 |
|
} |
| 687 |
|
|
| 688 |
|
/*lint -e{818} cli could be const */ |
| 689 |
|
int |
| 690 |
12200 |
VCLI_Overflow(struct cli *cli) |
| 691 |
|
{ |
| 692 |
12200 |
CHECK_OBJ_NOTNULL(cli, CLI_MAGIC); |
| 693 |
12200 |
if (cli->result == CLIS_TRUNCATED || |
| 694 |
12200 |
VSB_len(cli->sb) >= *cli->limit) |
| 695 |
0 |
return (1); |
| 696 |
12200 |
return (0); |
| 697 |
12200 |
} |
| 698 |
|
|
| 699 |
|
/*lint -e{818} cli could be const */ |
| 700 |
|
void |
| 701 |
93160 |
VCLI_JSON_str(struct cli *cli, const char *s) |
| 702 |
|
{ |
| 703 |
|
|
| 704 |
93160 |
CHECK_OBJ_NOTNULL(cli, CLI_MAGIC); |
| 705 |
93160 |
VSB_putc(cli->sb, '"'); |
| 706 |
93160 |
VSB_quote(cli->sb, s, -1, VSB_QUOTE_JSON); |
| 707 |
93160 |
VSB_putc(cli->sb, '"'); |
| 708 |
93160 |
} |
| 709 |
|
|
| 710 |
|
/*lint -e{818} cli could be const */ |
| 711 |
|
void |
| 712 |
2200 |
VCLI_JSON_begin(struct cli *cli, unsigned ver, const char * const * av) |
| 713 |
|
{ |
| 714 |
|
int i; |
| 715 |
|
|
| 716 |
2200 |
CHECK_OBJ_NOTNULL(cli, CLI_MAGIC); |
| 717 |
2200 |
VCLI_Out(cli, "[ %u, [", ver); |
| 718 |
7080 |
for (i = 1; av[i] != NULL; i++) { |
| 719 |
4880 |
VCLI_JSON_str(cli, av[i]); |
| 720 |
4880 |
if (av[i + 1] != NULL) |
| 721 |
2680 |
VCLI_Out(cli, ", "); |
| 722 |
4880 |
} |
| 723 |
2200 |
VCLI_Out(cli, "], %.3f", VTIM_real()); |
| 724 |
2200 |
VSB_indent(cli->sb, 2); |
| 725 |
2200 |
} |
| 726 |
|
|
| 727 |
|
void |
| 728 |
2200 |
VCLI_JSON_end(struct cli *cli) |
| 729 |
|
{ |
| 730 |
2200 |
VSB_indent(cli->sb, -2); |
| 731 |
2200 |
VCLI_Out(cli, "\n"); |
| 732 |
2200 |
VCLI_Out(cli, "]\n"); |
| 733 |
2200 |
} |
| 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 |
390280 |
VCLI_SetResult(struct cli *cli, unsigned res) |
| 746 |
|
{ |
| 747 |
|
|
| 748 |
390280 |
if (cli != NULL) { |
| 749 |
390280 |
CHECK_OBJ_NOTNULL(cli, CLI_MAGIC); |
| 750 |
390280 |
if (cli->result != CLIS_TRUNCATED || res != CLIS_OK) |
| 751 |
390240 |
cli->result = res; /*lint !e64 type mismatch */ |
| 752 |
390280 |
} else { |
| 753 |
0 |
printf("CLI result = %u\n", res); |
| 754 |
|
} |
| 755 |
390280 |
} |