From f29e4f1f3524a2362fecf20c54e3dcb2b0babf3e Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Wed, 15 Oct 2025 12:09:58 +0200 Subject: [PATCH 1/3] Add dynamic configuration of top level commands --- src/vtc.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++---- src/vtc.h | 10 +++++++++ src/vtc_misc.c | 8 +++---- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/vtc.c b/src/vtc.c index 2c8a7036..1b308081 100644 --- a/src/vtc.c +++ b/src/vtc.c @@ -91,12 +91,64 @@ static const struct cmds global_cmds[] = { { NULL, NULL } }; -static const struct cmds top_cmds[] = { +static const struct cmds builtin_top_cmds[] = { #define CMD_TOP(n) { #n, cmd_##n }, #include "cmds.h" - { NULL, NULL } }; +static struct dyncmds *top = NULL; + +/* + * Register commands, to be called from a constructor/init function + * + * the cmds array always has one additional NULL entry. + * + * Because we always search commands in order, the first registration of a + * command wins. We do not check for duplicates. + * + * NB: We never free the dyncmds deliberately as most of the allocations in + */ +static void +register_cmds(struct dyncmds **dynp, const struct cmds *cmds, unsigned n) +{ + struct dyncmds *dyn; + + AN(dynp); + dyn = *dynp; + if (dyn == NULL) { + ALLOC_FLEX_OBJ(dyn, cmds, n + 1, VTC_DYNCMDS_MAGIC); + AN(dyn); + dyn->n = 1; + } else { + CHECK_OBJ(dyn, VTC_DYNCMDS_MAGIC); + dyn = realloc(dyn, SIZEOF_FLEX_OBJ(dyn, cmds, dyn->n + n)); + AN(dyn); + } + assert(dyn->n >= 1); + memcpy(&dyn->cmds[dyn->n - 1], cmds, n * sizeof *cmds); + dyn->n += n; + dyn->cmds[dyn->n - 1] = (struct cmds){0}; + *dynp = dyn; +} + +void +register_top_cmds(const struct cmds *cmds, unsigned n) +{ + register_cmds(&top, cmds, n); +} + +const struct cmds * +top_cmds(void) +{ + return (top->cmds); +} + +static __attribute__((constructor)) void +register_builtin_top_cmds(void) +{ + register_top_cmds(builtin_top_cmds, vcountof(builtin_top_cmds)); +} + /**********************************************************************/ static struct macro * @@ -545,7 +597,7 @@ fail_out(void) vtc_stop = 1; vtc_log(vltop, 1, "RESETTING after %s", tfn); reset_cmds(global_cmds); - reset_cmds(top_cmds); + reset_cmds(top->cmds); vtc_error |= old_err; if (vtc_error) @@ -579,7 +631,7 @@ exec_file(const char *fn, const char *script, const char *tmpdir, vtc_loginit(logbuf, loglen); vltop = vtc_logopen("top"); AN(vltop); - vtc_log_set_cmd(vltop, top_cmds); + vtc_log_set_cmd(vltop, top->cmds); vtc_log(vltop, 1, "TEST %s starting", fn); diff --git a/src/vtc.h b/src/vtc.h index 7a9f3464..fb9526bd 100644 --- a/src/vtc.h +++ b/src/vtc.h @@ -65,6 +65,16 @@ struct cmds { cmd_f *cmd; }; +// to extend cmds with shared objects during startup +struct dyncmds { + unsigned magic; +#define VTC_DYNCMDS_MAGIC 0xc298428f + unsigned n; + struct cmds cmds[] v_counted_by_(n); +}; +void register_top_cmds(const struct cmds *cmds, unsigned n); +const struct cmds * top_cmds(void); + void parse_string(struct vtclog *vl, void *priv, const char *spec); int fail_out(void); diff --git a/src/vtc_misc.c b/src/vtc_misc.c index 24462d8c..3307530f 100644 --- a/src/vtc_misc.c +++ b/src/vtc_misc.c @@ -652,11 +652,9 @@ cmd_feature(CMD_ARGS) av++; if (*av == NULL) vtc_fatal(vl, "vtest_cmd needs the command name"); -#define CMD_TOP(n) \ - if (!strcmp(*av, #n)) \ - skip = neg; -#define CMD_GLOBAL(n) CMD_TOP(n) -#include "cmds.h" + for (const struct cmds *cmd = top_cmds(); cmd->name != NULL; cmd++) + if (!strcmp(*av, cmd->name)) + skip = neg; } if (!good) vtc_fatal(vl, "FAIL test, unknown feature: %s", feat); From 5f1297c48375c1048ac6413ecc44a7cbf0be59cd Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Wed, 15 Oct 2025 13:58:35 +0200 Subject: [PATCH 2/3] Add extension support --- .gitignore | 2 ++ Makefile | 23 +++++++++++++++++++++-- src/debug/ext_foo.c | 34 ++++++++++++++++++++++++++++++++++ src/vtc_main.c | 11 ++++++++++- tests/a00029.vtc | 5 +++++ 5 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/debug/ext_foo.c create mode 100644 tests/a00029.vtc diff --git a/.gitignore b/.gitignore index 39c424ab..1e150698 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ lib/*.o src/*.o +src/debug/*.o src/teken_state.h src/vtc_h2_dectbl.h vtest +ext_foo.so # droppings from submodule use tests/*.log diff --git a/Makefile b/Makefile index c1a921ee..fe413ee3 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ DEPS= lib/*.h \ FLAGS= -O2 -Wall -Werror CFLAGS= ${FLAGS} -LDFLAGS= ${FLAGS} -s +LDFLAGS= ${FLAGS} -s -rdynamic -ldl DEFINES= INCS= -I. \ @@ -52,6 +52,7 @@ vtest: ${DEPS} ${SRCS} test: vtest env PATH=`pwd`:${PATH} vtest tests/*.vtc + ./vtest -E `pwd`/ext_foo.so tests/a00029.vtc ####################################################################### # ... other point to varnish source tree and use this part: @@ -70,6 +71,24 @@ varnishtest: ${DEPS} ${SRCS} -Wl,--rpath,${VARNISH_SRC}/lib/libvarnishapi/.libs \ -lvarnishapi +####################################################################### +# demo extension + +src/debug/ext_foo.o: src/debug/ext_foo.c + ${CC} \ + ${CFLAGS} \ + ${DEFINES} \ + ${INCS} \ + -fPIC -DPIC \ + -o $@ -c $< + +ext_foo.so: src/debug/ext_foo.o + ${CC} \ + -shared \ + ${LDFLAGS} \ + -o $@ $< + + ####################################################################### # Implicit rule used in a sub-process by the rules above, and makes use # of ${DEFINES} for extra arguments : @@ -101,7 +120,7 @@ clean: rm -f vtest varnishtest rm -f src/teken_state.h rm -f src/vtc_h2_dectbl.h - rm -f ${OBJS} + rm -f ${OBJS} src/debug/ext_foo.o ext_foo.so ####################################################################### # Housekeeping diff --git a/src/debug/ext_foo.c b/src/debug/ext_foo.c new file mode 100644 index 00000000..b1d3c77f --- /dev/null +++ b/src/debug/ext_foo.c @@ -0,0 +1,34 @@ +/* + * example vtc extension to add the 'foo' command + */ + +#include "config.h" + +#include + +#include "vtc.h" + +static void +cmd_foo(CMD_ARGS) +{ + (void)priv; + + // fini + if (av == NULL) + return; + + AZ(strcmp(av[0], "foo")); + av++; + + AN(vl); + + for (; *av != NULL; av++) + vtc_log(vl, 1, "foo: %s", *av); +} + +static struct cmds foo_cmds[1] = {{"foo", cmd_foo}}; + +static __attribute__((constructor)) void +foo_init(void) { + register_top_cmds(foo_cmds, vcountof(foo_cmds)); +} diff --git a/src/vtc_main.c b/src/vtc_main.c index c9deb10d..61fde141 100644 --- a/src/vtc_main.c +++ b/src/vtc_main.c @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -185,6 +186,7 @@ usage(void) "Set internal buffer size (default: 1M)"); fprintf(stderr, FMT, "-C", "Use cleaner subprocess"); fprintf(stderr, FMT, "-D name=val", "Define macro"); + fprintf(stderr, FMT, "-E extension.so", "Load extenstion"); fprintf(stderr, FMT, "-i", "Find varnish binaries in build tree"); fprintf(stderr, FMT, "-j jobs", "Run this many tests in parallel"); fprintf(stderr, FMT, "-k", "Continue on test failure"); @@ -865,7 +867,7 @@ main(int argc, char * const *argv) AN(cbvsb); setbuf(stdout, NULL); setbuf(stderr, NULL); - while ((ch = getopt(argc, argv, "b:CD:hij:kLln:p:qt:vW")) != -1) { + while ((ch = getopt(argc, argv, "b:CD:E:hij:kLln:p:qt:vW")) != -1) { switch (ch) { case 'b': if (VNUM_2bytes(optarg, &bufsiz, 0)) { @@ -890,6 +892,13 @@ main(int argc, char * const *argv) exit(2); } break; + case 'E': + if (! dlopen(optarg, RTLD_NOW)) { + fprintf(stderr, "Failed to load extension %s: %s\n", + optarg, dlerror()); + exit(2); + } + break; case 'i': iflg = 1; break; diff --git a/tests/a00029.vtc b/tests/a00029.vtc new file mode 100644 index 00000000..591c0d55 --- /dev/null +++ b/tests/a00029.vtc @@ -0,0 +1,5 @@ +vtest "extension use" + +feature vtest_cmd foo + +foo 1 2 3 From 185391a6308b3407a3e7fb39358c984a8029b53b Mon Sep 17 00:00:00 2001 From: Nils Goroll Date: Wed, 15 Oct 2025 21:44:06 +0200 Subject: [PATCH 3/3] build: add install target and pkg-config file FTR: I am not convinced that a life without automake is a better life --- .gitignore | 1 + Makefile | 37 +++++++++++++++++++++++++++++++++++++ vtest.pc.in | 10 ++++++++++ 3 files changed, 48 insertions(+) create mode 100644 vtest.pc.in diff --git a/.gitignore b/.gitignore index 1e150698..808cc524 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ src/debug/*.o src/teken_state.h src/vtc_h2_dectbl.h vtest +vtest.pc ext_foo.so # droppings from submodule use diff --git a/Makefile b/Makefile index fe413ee3..ae5f48a1 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,8 @@ VARNISH_SRC ?= /home/phk/Varnish/trunk/varnish-cache AWK ?= awk +SED ?= sed + SRCS= src/*.c \ lib/*.c @@ -36,6 +38,38 @@ LIBS= -L/usr/local/lib \ -lpcre2-8 \ -lz +PREFIX ?= /usr/local + +PACKAGE_VERSION = 0.9 + +####################################################################### + +all: vtest + +####################################################################### + +vtest.pc: vtest.pc.in + ${SED} <$< >$@.tmp "s:@prefix@:$(PREFIX):; s:@PACKAGE_VERSION@:$(PACKAGE_VERSION):;" + mv $@.tmp $@ + +####################################################################### + +HEADERS= \ + src/vtc.h \ + src/cmds.h \ + lib/vdef.h \ + lib/miniobj.h \ + lib/vas.h \ + lib/vsb.h + +install: vtest vtest.pc + umask 022 && \ + mkdir -p $(PREFIX)/bin $(PREFIX)/include/vtest $(PREFIX)/lib/pkgconfig && \ + cp vtest $(PREFIX)/bin && chmod 755 $(PREFIX)/bin/vtest && \ + cp $(HEADERS) $(PREFIX)/include/vtest && \ + cp vtest.pc $(PREFIX)/lib/pkgconfig && \ + chmod 644 $(PREFIX)/include/vtest/*.h $(PREFIX)/lib/pkgconfig/vtest.pc + ####################################################################### # If you want to build vtest without varnish support, use this part: @@ -88,6 +122,8 @@ ext_foo.so: src/debug/ext_foo.o ${LDFLAGS} \ -o $@ $< +####################################################################### +# pkg-config ####################################################################### # Implicit rule used in a sub-process by the rules above, and makes use @@ -121,6 +157,7 @@ clean: rm -f src/teken_state.h rm -f src/vtc_h2_dectbl.h rm -f ${OBJS} src/debug/ext_foo.o ext_foo.so + rm -f vtest.pc ####################################################################### # Housekeeping diff --git a/vtest.pc.in b/vtest.pc.in new file mode 100644 index 00000000..491cbb3c --- /dev/null +++ b/vtest.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +bindir=${prefix}/bin +includedir=${prefix}/include +pkgincludedir=${includedir}/vtest + +Name: VTest2 +Description: The HTTP test-program formerly known as Varnishtest (reiterated) +URL: https://github.com/vtest/VTest2 +Version: @PACKAGE_VERSION@ +Cflags: -I${pkgincludedir}