diff --git a/bin/varnishadm/varnishadm.c b/bin/varnishadm/varnishadm.c index d08da8278f..d890f50ff9 100644 --- a/bin/varnishadm/varnishadm.c +++ b/bin/varnishadm/varnishadm.c @@ -66,7 +66,8 @@ #include "vapi/vsig.h" #include "vapi/vsm.h" #include "vas.h" -#include "vcli.h" +#include "vcli_serve.h" +#include "vsb.h" #include "vjsn.h" #include "vtcp.h" @@ -83,8 +84,14 @@ enum pass_mode_e { static double timeout = 5; static int p_arg = 0; +static int e_arg = 0; static int line_sock; +static void v_noreturn_ +adm_exit(enum VCLI_status_e status) { + RL_EXIT(status / 100); +} + static void cli_write(int sock, const char *s) { @@ -95,7 +102,7 @@ cli_write(int sock, const char *s) if (i == l) return; perror("Write error CLI socket"); - RL_EXIT(1); + adm_exit(CLIS_COMMS); } /* @@ -166,6 +173,24 @@ cli_sock(const char *T_arg, const char *S_arg) return (sock); } +static void +pass_print_answer(int status, char *answer, enum pass_mode_e mode) +{ + if (p_arg && answer != NULL) { + printf("%-3u %-8zu\n%s", status, strlen(answer), answer); + } else if (p_arg) { + printf("%-3u %-8u\n", status, 0U); + } else { + if (mode == pass_interactive) + printf("%u\n", status); + if (answer != NULL) + printf("%s\n", answer); + if (status == CLIS_TRUNCATED) + printf("[response was truncated]\n"); + } + fflush(stdout); +} + static unsigned pass_answer(int fd, enum pass_mode_e mode) { @@ -176,27 +201,15 @@ pass_answer(int fd, enum pass_mode_e mode) if (u) { if (status == CLIS_COMMS) { fprintf(stderr, "%s\n", answer); - RL_EXIT(2); + adm_exit(CLIS_COMMS); } if (answer) fprintf(stderr, "%s\n", answer); - RL_EXIT(1); + adm_exit(status); } - if (p_arg && answer != NULL) { - printf("%-3u %-8zu\n%s", status, strlen(answer), answer); - } else if (p_arg) { - printf("%-3u %-8u\n", status, 0U); - } else { - if (mode == pass_interactive) - printf("%u\n", status); - if (answer != NULL) - printf("%s\n", answer); - if (status == CLIS_TRUNCATED) - printf("[response was truncated]\n"); - } + pass_print_answer(status, answer, mode); free(answer); - (void)fflush(stdout); return (status); } @@ -219,7 +232,7 @@ do_args(int sock, int argc, char * const *argv) exit(0); if (!p_arg) fprintf(stderr, "Command failed with error code %u\n", status); - exit(1); + adm_exit(status); } /* Callback for readline, doesn't take a private pointer, so we need @@ -338,57 +351,54 @@ interactive(int sock) } } +/*--------------------------------------------------------------------*/ + +static void +vadm_cli_cb_after(const struct cli *cli) +{ + const char *cmd; + + if (!e_arg) + return; + + if (cli->result == CLIS_OK || cli->result == CLIS_TRUNCATED) + return; + + cmd = VSB_data(cli->cmd); + if (*cmd == '-') + return; + + pass_print_answer(cli->result, VSB_data(cli->sb), pass_script); + fprintf(stderr, "\nCommand \"%s\" failed with error code %u\n", + cmd, cli->result); + adm_exit(cli->result); +} + /* * No arguments given, simply pass bytes on stdin/stdout and CLI socket */ + static void v_noreturn_ pass(int sock) { - struct pollfd fds[2]; - char buf[1024]; + struct VCLP *vclp; int i; - ssize_t n; - int busy = 0; - fds[0].fd = sock; - fds[0].events = POLLIN; - fds[1].fd = 0; - fds[1].events = POLLIN; - while (1) { - i = poll(fds, 2, -1); - if (i == -1 && errno == EINTR) { - continue; - } - assert(i > 0); - if (fds[0].revents & POLLIN) { - (void)pass_answer(fds[0].fd, pass_script); - busy = 0; - if (fds[1].fd < 0) - RL_EXIT(0); - } - if (fds[1].revents & POLLIN || fds[1].revents & POLLHUP) { - n = read(fds[1].fd, buf, sizeof buf - 1); - if (n == 0) { - if (!busy) - RL_EXIT(0); - fds[1].fd = -1; - } else if (n < 0) { - RL_EXIT(0); - } else { - busy = 1; - buf[n] = '\0'; - cli_write(sock, buf); - } - } - } + vclp = VCLP_New(STDIN_FILENO, STDOUT_FILENO, + sock, p_arg ? PROTO_FULL : PROTO_HEADLESS, timeout); + AN(vclp); + VCLP_SetHooks(vclp, NULL, vadm_cli_cb_after); + do { + i = VCLP_Poll(vclp, -1); + } while (i == 0); + RL_EXIT(0); } - static void v_noreturn_ usage(int status) { fprintf(stderr, - "Usage: varnishadm [-h] [-n ident] [-p] [-S secretfile] " + "Usage: varnishadm [-e] [-h] [-n ident] [-p] [-S secretfile] " "[-T [address]:port] [-t timeout] [command [...]]\n"); fprintf(stderr, "\t-n is mutually exclusive with -S and -T\n"); exit(status); @@ -452,7 +462,7 @@ t_arg_timeout(const char *t_arg) return (1); } -#define OPTARG "hn:pS:T:t:" +#define OPTARG "ehn:pS:T:t:" int main(int argc, char * const *argv) @@ -477,6 +487,9 @@ main(int argc, char * const *argv) */ while ((opt = getopt(argc, argv, "+" OPTARG)) != -1) { switch (opt) { + case 'e': + e_arg = 1; + break; case 'h': /* Usage help */ usage(0); @@ -496,7 +509,7 @@ main(int argc, char * const *argv) t_arg = optarg; break; default: - usage(1); + usage(2); } } @@ -505,11 +518,11 @@ main(int argc, char * const *argv) if (T_arg != NULL) { if (n_arg != NULL) - usage(1); + usage(2); sock = cli_sock(T_arg, S_arg); } else { if (S_arg != NULL) - usage(1); + usage(2); sock = n_arg_sock(n_arg, t_arg); } if (sock < 0) diff --git a/bin/varnishd/mgt/mgt_cli.c b/bin/varnishd/mgt/mgt_cli.c index c3cada3d99..fa2a1cab0a 100644 --- a/bin/varnishd/mgt/mgt_cli.c +++ b/bin/varnishd/mgt/mgt_cli.c @@ -143,7 +143,7 @@ mcf_askchild(struct cli *cli, const char * const *av, void *priv) } VSB_clear(cli_buf); for (i = 1; av[i] != NULL; i++) { - VSB_quote(cli_buf, av[i], strlen(av[i]), 0); + VSB_quote(cli_buf, av[i], strlen(av[i]), VSB_QUOTE_CLI); VSB_putc(cli_buf, ' '); } VSB_putc(cli_buf, '\n'); diff --git a/bin/varnishtest/tests/m00000.vtc b/bin/varnishtest/tests/m00000.vtc index 715f5fa7b7..ab9bd2455f 100644 --- a/bin/varnishtest/tests/m00000.vtc +++ b/bin/varnishtest/tests/m00000.vtc @@ -186,7 +186,7 @@ shell { varnish v1 -clierr 300 "vcl.load f1 ${tmpdir}/f1" -shell -exit 1 -expect {failing as requested} { +shell -exit 3 -expect {failing as requested} { varnishadm -t 10 -n ${tmpdir}/v1 vcl.load f1 ${tmpdir}/f1 } diff --git a/bin/varnishtest/tests/u00000.vtc b/bin/varnishtest/tests/u00000.vtc index d3baa09d69..7b19aaa2e4 100644 --- a/bin/varnishtest/tests/u00000.vtc +++ b/bin/varnishtest/tests/u00000.vtc @@ -116,7 +116,7 @@ shell {varnishadm -n ${tmpdir}/v1 start} shell {varnishadm -n ${tmpdir}/v1 debug.listen_address} -shell -exit 1 -expect "Command failed with error code 500" { +shell -exit 5 -expect "Command failed with error code 500" { varnishadm -n ${tmpdir}/v1 quit } diff --git a/bin/varnishtest/tests/u00001.vtc b/bin/varnishtest/tests/u00001.vtc index 0b5bf125bc..2d700e67d5 100644 --- a/bin/varnishtest/tests/u00001.vtc +++ b/bin/varnishtest/tests/u00001.vtc @@ -34,7 +34,6 @@ shell { # same command via stdin shell { echo status | varnishadm -n ${v1_name} -p | tr ' ' _ >actual.txt - printf '\n' >>actual.txt diff -u expected.txt actual.txt } @@ -61,3 +60,71 @@ shell -err { varnishadm -n ${v1_name} -p | python3 -c 'import sys, json; print(json.load(sys.stdin))' } + +shell { + cat > cli_file.txt <<-EOF + status + foo + vcl.list + EOF +} + +shell { + cat > expected_success.txt <<-EOF + Child in state running + Unknown request. + Type 'help' for more info. + + active auto warm 0 vcl1 + + EOF +} + +shell { + cat > expected_abort.txt <<-EOF + Child in state running + Unknown request. + Type 'help' for more info. + + EOF +} + +shell { varnishadm -n ${v1_name} < cli_file.txt } +shell { varnishadm -n ${v1_name} < cli_file.txt | diff -u expected_success.txt - } + +shell -exit 1 { varnishadm -e -n ${v1_name} < cli_file.txt } +shell { varnishadm -e -n ${v1_name} < cli_file.txt | diff -u expected_abort.txt - } + +shell { cat cli_file.txt | varnishadm -n ${v1_name} } +shell { cat cli_file.txt | varnishadm -n ${v1_name} | diff -u expected_success.txt - } + +shell -exit 1 { cat cli_file.txt | varnishadm -e -n ${v1_name} } +shell { cat cli_file.txt | varnishadm -e -n ${v1_name} | diff -u expected_abort.txt - } + +shell { + cat > cli_file.txt <<-EOF + status + -foo + vcl.list + EOF +} + +shell { varnishadm -e -n ${v1_name} < cli_file.txt } +shell { varnishadm -e -n ${v1_name} < cli_file.txt | diff -u expected_success.txt - } + +shell { cat cli_file.txt | varnishadm -e -n ${v1_name} } +shell { cat cli_file.txt | varnishadm -e -n ${v1_name} | diff -u expected_success.txt - } + +shell { + cat > cli_heredoc.txt <<-EOF1 + vcl.inline bar << EOF2 + vcl 4.1; + + backend default none; + EOF2 + EOF1 +} + +shell { varnishadm -e -p -n ${v1_name} < cli_heredoc.txt } +shell { varnishadm -n ${v1_name} vcl.discard bar } +shell { cat cli_heredoc.txt | varnishadm -e -p -n ${v1_name} } diff --git a/bin/varnishtest/tests/u00012.vtc b/bin/varnishtest/tests/u00012.vtc index 3f4a19ec6d..10dc81332b 100644 --- a/bin/varnishtest/tests/u00012.vtc +++ b/bin/varnishtest/tests/u00012.vtc @@ -32,7 +32,7 @@ process p1 -screen_dump -write "\x04" -wait process p1 -log {varnishadm -t foobar 2>&1} -expect-exit 2 -run process p1 -expect-text 0 0 "-t: Invalid argument:" -process p1 -log {varnishadm -Q 2>&1} -expect-exit 1 -run +process p1 -log {varnishadm -Q 2>&1} -expect-exit 2 -run process p1 -expect-text 0 0 "Usage: varnishadm" process p2 -log {varnishadm -h 2>&1} -expect-exit 0 -run diff --git a/doc/sphinx/reference/varnishadm.rst b/doc/sphinx/reference/varnishadm.rst index 8cfdc69838..a0bc50bb01 100644 --- a/doc/sphinx/reference/varnishadm.rst +++ b/doc/sphinx/reference/varnishadm.rst @@ -19,7 +19,7 @@ Control a running Varnish instance SYNOPSIS ======== -varnishadm [-h] [-n ident] [-p] [-S secretfile] [-T [address]:port] [-t timeout] [command [...]] +varnishadm [-e] [-h] [-n ident] [-p] [-S secretfile] [-T [address]:port] [-t timeout] [command [...]] DESCRIPTION @@ -40,6 +40,14 @@ replies between the CLI socket and stdin/stdout. OPTIONS ======= +-e + Exit immediately if a command fails in `pass` mode with an exit + status indicating the nature of the error (See EXIT STATUS for details). + This has no effect on `interactive` mode (except when `-p` is used). + Similarly to `CLI Command File` (see :ref:`varnishd(1)`), if a command + is prefixed with '-', its failure will be ignored and will not cause + varnishadm to exit. + -h Print program usage and exit. @@ -75,7 +83,13 @@ EXIT STATUS =========== If a command is given, the exit status of the `varnishadm` utility is -zero if the command succeeded, and non-zero otherwise. +zero if the command succeeded, and one of the following otherwise: + +1: could not execute the command +2: bad usage +3: the command failed +4: the connection was lost +5: the CLI session was terminated EXAMPLES ======== diff --git a/include/vcli.h b/include/vcli.h index 16705d71d9..85fb9f0f1d 100644 --- a/include/vcli.h +++ b/include/vcli.h @@ -56,12 +56,19 @@ enum VCLI_status_e { CLIS_CLOSE = 500 }; +typedef enum { + PROTO_FULL = 0, + PROTO_STATUS, + PROTO_HEADLESS +} vcls_proto_e; + /* Length of first line of response */ #define CLI_LINE0_LEN 13 #define CLI_AUTH_RESPONSE_LEN 64 /* 64 hex + NUL */ #if !defined(VCLI_PROTOCOL_ONLY) /* Convenience functions exported in libvarnishapi */ +int VCLI_Write(int fd, vcls_proto_e proto, unsigned status, const char *result); int VCLI_WriteResult(int fd, unsigned status, const char *result); int VCLI_ReadResult(int fd, unsigned *status, char **ptr, double tmo); void VCLI_AuthResponse(int S_fd, const char *challenge, diff --git a/include/vcli_serve.h b/include/vcli_serve.h index e1b721ea4e..a23fbd7577 100644 --- a/include/vcli_serve.h +++ b/include/vcli_serve.h @@ -37,6 +37,7 @@ struct cli; /* NB: struct cli is opaque at this level. */ struct VCLS; +struct VCLP; typedef void cli_func_t(struct cli*, const char * const *av, void *priv); @@ -80,6 +81,7 @@ struct cli { char *ident; struct VCLS *cls; volatile unsigned *limit; + char *hdoc; }; /* The implementation must provide these functions */ @@ -91,6 +93,7 @@ void VCLI_JSON_begin(struct cli *cli, unsigned ver, const char * const * av); void VCLI_JSON_end(struct cli *cli); void VCLI_SetResult(struct cli *cli, unsigned r); +/* CLI server */ typedef int cls_cb_f(void *priv); typedef void cls_cbc_f(const struct cli*); struct VCLS *VCLS_New(struct VCLS *); @@ -102,6 +105,12 @@ void VCLS_AddFunc(struct VCLS *cs, unsigned auth, struct cli_proto *clp); int VCLS_Poll(struct VCLS *cs, const struct cli*, int timeout); void VCLS_Destroy(struct VCLS **); +/* CLI proxy */ +struct VCLP *VCLP_New(int fdi, int fdo, int sock, vcls_proto_e proto, double timeout); +int VCLP_Poll(struct VCLP *cp, int timeout); +void VCLP_Destroy(struct VCLP **); +void VCLP_SetHooks(struct VCLP *, cls_cbc_f *, cls_cbc_f *); + /* From libvarnish/cli.c */ cli_func_t VCLS_func_close; cli_func_t VCLS_func_help; diff --git a/include/vsb.h b/include/vsb.h index 37fc1c0271..9b3be76644 100644 --- a/include/vsb.h +++ b/include/vsb.h @@ -123,6 +123,11 @@ void VSB_destroy(struct vsb **); * Not valid with VSB_QUOTE_JSON and VSB_QUOTE_HEX */ +#define VSB_QUOTE_CLI 64 + /* + * Add quotes ".." around arguments when needed + */ + void VSB_quote_pfx(struct vsb *, const char*, const void *, int len, int how); void VSB_quote(struct vsb *, const void *, int len, int how); diff --git a/lib/libvarnish/vcli_proto.c b/lib/libvarnish/vcli_proto.c index e0fca5cecc..7ca0b93d55 100644 --- a/lib/libvarnish/vcli_proto.c +++ b/lib/libvarnish/vcli_proto.c @@ -72,9 +72,9 @@ VCLI_AuthResponse(int S_fd, const char *challenge, } int -VCLI_WriteResult(int fd, unsigned status, const char *result) +VCLI_Write(int fd, vcls_proto_e proto, unsigned status, const char *result) { - int i, l; + int i, l, k = 0; struct iovec iov[3]; char nl[2] = "\n"; size_t len; @@ -87,26 +87,45 @@ VCLI_WriteResult(int fd, unsigned status, const char *result) assert(status <= 999); /*lint !e650 const out of range */ len = strlen(result); + switch (proto) { + case PROTO_FULL: + i = snprintf(res, sizeof res, "%-3d %-8zd\n", status, len); + assert(i == CLI_LINE0_LEN); + assert(strtoul(res + 3, NULL, 10) == len); + break; + case PROTO_STATUS: + i = snprintf(res, sizeof res, "%-12u\n", status); + assert(i == CLI_LINE0_LEN); + break; + case PROTO_HEADLESS: + break; + default: + WRONG("Unknown cli output format\n"); + } - i = snprintf(res, sizeof res, "%-3d %-8zd\n", status, len); - assert(i == CLI_LINE0_LEN); - assert(strtoul(res + 3, NULL, 10) == len); - - iov[0].iov_base = res; - iov[0].iov_len = CLI_LINE0_LEN; + if (proto != 2) { + iov[k].iov_base = res; + iov[k++].iov_len = CLI_LINE0_LEN; + } - iov[1].iov_base = (void*)(uintptr_t)result; /* TRUST ME */ - iov[1].iov_len = len; + iov[k].iov_base = (void*)(uintptr_t)result; /* TRUST ME */ + iov[k++].iov_len = len; - iov[2].iov_base = nl; - iov[2].iov_len = 1; + iov[k].iov_base = nl; + iov[k++].iov_len = 1; - for (l = i = 0; i < 3; i++) + for (l = i = 0; i < k; i++) l += iov[i].iov_len; - i = writev(fd, iov, 3); + i = writev(fd, iov, k); return (i != l); } +int +VCLI_WriteResult(int fd, unsigned status, const char *result) +{ + return (VCLI_Write(fd, PROTO_FULL, status, result)); +} + static int read_tmo(int fd, char *ptr, unsigned len, double tmo) { diff --git a/lib/libvarnish/vcli_serve.c b/lib/libvarnish/vcli_serve.c index dbb384a0ff..d04df20dac 100644 --- a/lib/libvarnish/vcli_serve.c +++ b/lib/libvarnish/vcli_serve.c @@ -33,6 +33,7 @@ #include "config.h" +#include #include #include #include @@ -73,12 +74,27 @@ struct VCLS { #define VCLS_MAGIC 0x60f044a3 VTAILQ_HEAD(,VCLS_fd) fds; unsigned nfd; + vcls_proto_e proto; VTAILQ_HEAD(,cli_proto) funcs; cls_cbc_f *before, *after; volatile unsigned *limit; struct cli_proto *wildcard; }; +struct vclp_priv { + unsigned magic; +#define VCLP_PRIV_MAGIC 0xa154f8be + int proxy_sock; + double timeout; +}; + +struct VCLP { + unsigned magic; +#define VCLP_MAGIC 0x78695378 + struct VCLS *vcls; + struct vclp_priv priv[1]; +}; + /*--------------------------------------------------------------------*/ void v_matchproto_(cli_func_t) @@ -343,7 +359,7 @@ cls_exec(struct VCLS_fd *cfd, char * const *av) s[lim - 1] = '\0'; assert(strlen(s) <= lim); } - if (VCLI_WriteResult(cfd->fdo, cli->result, s) || + if (VCLI_Write(cfd->fdo, cs->proto, cli->result, s) || cli->result == CLIS_CLOSE) retval = 1; @@ -409,6 +425,7 @@ cls_feed(struct VCLS_fd *cfd, const char *p, const char *e) cfd->argv = av; cfd->argc = ac; cfd->match = av[ac - 1]; + cli->hdoc = strdup(av[ac - 1]); cfd->last_arg = VSB_new_auto(); AN(cfd->last_arg); } else { @@ -434,6 +451,7 @@ cls_feed(struct VCLS_fd *cfd, const char *p, const char *e) cfd->argv[cfd->argc - 2] = VSB_data(cfd->last_arg); i = cls_exec(cfd, cfd->argv); + REPLACE(cli->hdoc, NULL); cfd->argv[cfd->argc - 2] = NULL; VAV_Free(cfd->argv); cfd->argv = NULL; @@ -611,15 +629,11 @@ VCLS_Poll(struct VCLS *cs, const struct cli *cli, int timeout) j = poll(pfd, 1, timeout); if (j <= 0) return (j); - if (pfd[0].revents & POLLHUP) + i = read(cfd->fdi, buf, sizeof buf); + if (i <= 0) k = 1; - else { - i = read(cfd->fdi, buf, sizeof buf); - if (i <= 0) - k = 1; - else - k = cls_feed(cfd, buf, buf + i); - } + else + k = cls_feed(cfd, buf, buf + i); if (k) { i = cls_close_fd(cs, cfd); if (i < 0) @@ -646,6 +660,155 @@ VCLS_Destroy(struct VCLS **csp) FREE_OBJ(cs); } +/********************************************************************** + * CLI Proxy +*/ + +static void v_matchproto_(cli_func_t) +vclp_forward(struct cli *cli, const char * const *av, void *priv) +{ + int i, sock; + unsigned u; + char *q; + struct vclp_priv *vp; + struct vsb *cli_buf; + + CAST_OBJ_NOTNULL(vp, priv, VCLP_PRIV_MAGIC); + sock = vp->proxy_sock; + cli_buf = VSB_new_auto(); + AN(cli_buf); + VSB_clear(cli_buf); + for (i = 1; av[i] != NULL && av[i+1] != NULL; i++) { + VSB_quote(cli_buf, av[i], strlen(av[i]), VSB_QUOTE_CLI); + VSB_putc(cli_buf, ' '); + } + if (av[i] != NULL && cli->hdoc != NULL) { + VSB_printf(cli_buf, "<< %s\n%s\n%s", cli->hdoc, av[i], cli->hdoc); + } else if (av[i] != NULL) { + VSB_quote(cli_buf, av[i], strlen(av[i]), VSB_QUOTE_CLI); + VSB_putc(cli_buf, ' '); + } + VSB_putc(cli_buf, '\n'); + AZ(VSB_finish(cli_buf)); + if (VSB_tofile(cli_buf, sock)) { + VCLI_SetResult(cli, CLIS_COMMS); + VCLI_Out(cli, "CLI communication error"); + return; + } + if (VCLI_ReadResult(sock, &u, &q, vp->timeout)) { + VCLI_SetResult(cli, CLIS_COMMS); + VCLI_Out(cli, "CLI communication error"); + return; + } + + VCLI_SetResult(cli, u); + VCLI_Out(cli, "%s", q); + free(q); +} + +static const struct cli_cmd_desc CLICMD_WILDCARD[1] = + {{ "*", "", "", "", 0, 999 }}; + +struct cli_proto cli_proxy[] = { + { CLICMD_WILDCARD, "", vclp_forward, vclp_forward}, + { NULL } +}; + +struct VCLP * +VCLP_New(int fdi, int fdo, int sock, vcls_proto_e proto, double timeout) +{ + struct VCLP *cp; + struct cli *cli; + unsigned clilim = UINT_MAX; + + ALLOC_OBJ(cp, VCLP_MAGIC); + AN(cp); + INIT_OBJ(cp->priv, VCLP_PRIV_MAGIC); + cp->priv->proxy_sock = sock; + cp->priv->timeout = timeout; + cli_proxy->priv = &cp->priv; + cp->vcls = VCLS_New(NULL); + AN(cp->vcls); + VCLS_SetLimit(cp->vcls, &clilim); + cp->vcls->proto = proto; + cli = VCLS_AddFd(cp->vcls, fdi, fdo, NULL, NULL); + AN(cli); + cli->auth = 1; + VCLS_AddFunc(cp->vcls, 1, cli_proxy); + return cp; +} + +void +VCLP_SetHooks(struct VCLP *cp, cls_cbc_f *before, cls_cbc_f *after) +{ + CHECK_OBJ_NOTNULL(cp, VCLP_MAGIC); + CHECK_OBJ_NOTNULL(cp->vcls, VCLS_MAGIC); + + cp->vcls->before = before; + cp->vcls->after = after; +} + +int +VCLP_Poll(struct VCLP *cp, int timeout) +{ + struct VCLS_fd *cfd; + struct VCLS *cs; + struct pollfd pfd[2]; + int i, j, k; + char buf[BUFSIZ]; + + CHECK_OBJ_NOTNULL(cp, VCLP_MAGIC); + cs = cp->vcls; + CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC); + if (cs->nfd == 0) { + errno = 0; + return (-1); + } + assert(cs->nfd > 0); + + cfd = VTAILQ_FIRST(&cs->fds); + CHECK_OBJ_NOTNULL(cfd, VCLS_FD_MAGIC); + + memset(pfd, 0, sizeof(pfd)); + pfd[0].fd = cfd->fdi; + pfd[0].events = POLLIN; + + pfd[1].fd = cp->priv->proxy_sock; + pfd[1].events = POLLIN; + + j = poll(pfd, 2, timeout); + + if (j <= 0) + return (j); + + if (j > 0 && (pfd[1].revents & POLLIN)) { + return (-1); + } + + i = read(cfd->fdi, buf, sizeof buf); + if (i <= 0) + k = 1; + else + k = cls_feed(cfd, buf, buf + i); + if (k) { + i = cls_close_fd(cs, cfd); + if (i < 0) + k = i; + } + return (k); +} + +void +VCLP_Destroy(struct VCLP **cpp) +{ + struct VCLP *cp; + + TAKE_OBJ_NOTNULL(cp, cpp, VCLP_MAGIC); + VCLS_Destroy(&cp->vcls); + ZERO_OBJ(cp->priv, sizeof(cp->priv)); + FREE_OBJ(cp); +} + /********************************************************************** * Utility functions for implementing CLI commands */ diff --git a/lib/libvarnish/vsb.c b/lib/libvarnish/vsb.c index 67a4efec84..696a49820b 100644 --- a/lib/libvarnish/vsb.c +++ b/lib/libvarnish/vsb.c @@ -529,7 +529,7 @@ VSB_quote_pfx(struct vsb *s, const char *pfx, const void *v, int len, int how) { const uint8_t *p = v; const uint8_t *q; - int quote = 0; + int quote = 0, dq; int nl; nl = how & @@ -564,22 +564,24 @@ VSB_quote_pfx(struct vsb *s, const char *pfx, const void *v, int len, int how) return; } - if (how & VSB_QUOTE_CSTR) - VSB_putc(s, '"'); - for (q = p; q < p + len; q++) { if ( *q < 0x20 || *q == '"' || *q == '\\' || (*q == '?' && (how & VSB_QUOTE_CSTR)) || - (*q > 0x7e && !(how & VSB_QUOTE_JSON)) + (*q > 0x7e && !(how & VSB_QUOTE_JSON)) || + (isspace(*q) && (how & VSB_QUOTE_CLI)) ) { quote++; break; } } + dq = (how & VSB_QUOTE_CSTR || (how & VSB_QUOTE_CLI && quote)); + if (dq) + VSB_putc(s, '"'); + if (!quote) { VSB_bcat(s, p, len); if ((how & VSB_QUOTE_NONL) && @@ -640,7 +642,7 @@ VSB_quote_pfx(struct vsb *s, const char *pfx, const void *v, int len, int how) break; } } - if (how & VSB_QUOTE_CSTR) + if (dq) VSB_putc(s, '"'); if ((how & VSB_QUOTE_NONL) && !nl) VSB_putc(s, '\n');