diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c index 0efded1314..b568598609 100644 --- a/bin/varnishd/cache/cache_vcl.c +++ b/bin/varnishd/cache/cache_vcl.c @@ -988,6 +988,76 @@ vcl_cli_show(struct cli *cli, const char * const *av, void *priv) } } +// "tell [.] ...", + +static void v_matchproto_(cli_func_t) +vcl_cli_tell(struct cli *cli, const char * const *av, void *priv) +{ + struct strands args; + const char *objname; + struct vcl *vcl; + struct vrt_ctx *ctx; + struct vsb *msg; + char *n; + int i; + + AZ(priv); + ASSERT_CLI(); + AN(av[2]); + AN(av[3]); + + objname = strchr(av[2], '.'); + if (objname) { + n = strndup(av[2], objname - av[2]); + objname++; + vcl = vcl_find(n); + if (vcl == NULL) { + VCLI_SetResult(cli, CLIS_CANT); + VCLI_Out(cli, "VCL %s not found", n); + REPLACE(n, NULL); + return; + } + REPLACE(n, NULL); + } else { + vcl = vcl_active; + objname = av[2]; + } + + AN(vcl); + AN(objname); + + if (vcl->label) + vcl = vcl->label; + AN(vcl); + + i = 0; + while (av[3 + i] != NULL) + i++; + + const char *p[i]; + args.n = i; + args.p = p; + + i = 0; + while (av[3 + i] != NULL) { + args.p[i] = av[3 + i]; + i++; + } + + ctx = VCL_Get_CliCtx(1); + ctx->vcl = vcl; + ctx->syntax = ctx->vcl->conf->syntax; + + i = VPI_Tell(ctx, objname, &args); + + msg = VCL_Rel_CliCtx(&ctx); + + VCLI_SetResult(cli, i); + // could have VCLI_Cat or VCLI_Vsb + VCLI_Out(cli, "%s", VSB_data(msg)); + VSB_destroy(&msg); +} + /*--------------------------------------------------------------------*/ static struct cli_proto vcl_cmds[] = { @@ -998,6 +1068,7 @@ static struct cli_proto vcl_cmds[] = { { CLICMD_VCL_USE, "", vcl_cli_use }, { CLICMD_VCL_SHOW, "", vcl_cli_show }, { CLICMD_VCL_LABEL, "", vcl_cli_label }, + { CLICMD_TELL, "", vcl_cli_tell}, { NULL } }; diff --git a/bin/varnishd/cache/cache_vpi.c b/bin/varnishd/cache/cache_vpi.c index f6e09dd869..38e313cff7 100644 --- a/bin/varnishd/cache/cache_vpi.c +++ b/bin/varnishd/cache/cache_vpi.c @@ -186,3 +186,56 @@ VPI_Call_End(VRT_CTX, unsigned n) AN(vbm); vbit_clr(vbm, n); } + +/*-------------------------------------------------------------------- + * tell interface. + * + * we all agree it does not quite fit the purpose of VPI, but it fits here + * better than anywhere else + * + * XXX: Replace instance info with a tree indexed by name + */ + +int +VPI_Tell(VRT_CTX, VCL_STRING objname, VCL_STRANDS msg) +{ + struct vcl *vcl; + const struct VCL_conf *conf; + const struct vpi_ii *ii; + vmod_cli_f *cli; + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + AN(objname); + + ASSERT_CLI(); + AZ(ctx->method); + AN(ctx->msg); + + vcl = ctx->vcl; + CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC); + + conf = vcl->conf; + CHECK_OBJ_NOTNULL(conf, VCL_CONF_MAGIC); + + ii = conf->instance_info; + AN(ii); + while (ii->p != NULL) { + if (! strcmp(ii->name, objname)) + break; + ii++; + } + + if (ii->p == NULL) { + VSB_printf(ctx->msg, "No object named %s found\n", objname); + return (300); + } + if (ii->clip == NULL) { + VSB_printf(ctx->msg, "Object %s has no cli method\n", objname); + return (300); + } + + cli = (void *)*ii->clip; + AN(cli); + AN(*ii->p); + return(cli(ctx, (void *)*ii->p, msg)); +} diff --git a/include/tbl/cli_cmds.h b/include/tbl/cli_cmds.h index 9537833f6b..db05b25dc5 100644 --- a/include/tbl/cli_cmds.h +++ b/include/tbl/cli_cmds.h @@ -435,6 +435,16 @@ CLI_CMD(PID, 0, 0 ) +CLI_CMD(TELL, + "tell", + "tell [.] ...", + "Tell to from the given or the active vcl", + " It is entirely up to the cli method implementation of the" + " respective vmod to interpret , implement any actions" + " and generate a response", + 2, -1 +) + #undef CLI_CMD /*lint -restore */ diff --git a/include/tbl/symbol_kind.h b/include/tbl/symbol_kind.h index 40dfbfdd83..bc4cfda4f3 100644 --- a/include/tbl/symbol_kind.h +++ b/include/tbl/symbol_kind.h @@ -46,6 +46,7 @@ VCC_KIND(SUB, sub) VCC_KIND(VAR, var) VCC_KIND(VCL, vcl) VCC_KIND(VMOD, vmod) +VCC_KIND(CLI_METHOD, cli) #undef VCC_KIND /*lint -restore */ diff --git a/include/vcc_interface.h b/include/vcc_interface.h index c1a74fc37f..5720954954 100644 --- a/include/vcc_interface.h +++ b/include/vcc_interface.h @@ -77,6 +77,7 @@ void VPI_acl_log(VRT_CTX, const char *); struct vpi_ii { uintptr_t * p; const char * const name; + uintptr_t * clip; }; /* Compile time regexp */ @@ -102,3 +103,7 @@ enum vcl_func_fail_e VPI_Call_Check(VRT_CTX, const struct VCL_conf *conf, unsigned methods, unsigned n); void VPI_Call_Begin(VRT_CTX, unsigned n); void VPI_Call_End(VRT_CTX, unsigned n); + +// return value should be a VCLI_status_e +typedef VCL_INT vmod_cli_f(VRT_CTX, void *, VCL_STRANDS); +int VPI_Tell(VRT_CTX, VCL_STRING, VCL_STRANDS); diff --git a/lib/libvcc/vcc_vmod.c b/lib/libvcc/vcc_vmod.c index 2b5ec59596..146f84e6a1 100644 --- a/lib/libvcc/vcc_vmod.c +++ b/lib/libvcc/vcc_vmod.c @@ -74,12 +74,13 @@ vcc_path_dlopen(void *priv, const char *fn) } static void vcc_VmodObject(struct vcc *tl, struct symbol *sym); -static void vcc_VmodSymbols(struct vcc *tl, const struct symbol *sym); +static void vcc_VmodSymbols(struct vcc *tl, struct symbol *sym); static void -func_sym(struct vcc *tl, vcc_kind_t kind, const struct symbol *psym, +func_sym(struct vcc *tl, vcc_kind_t kind, struct symbol *psym, const struct vjsn_val *v) { + const struct vjsn_val *vv; struct symbol *sym; struct vsb *buf; @@ -115,11 +116,18 @@ func_sym(struct vcc *tl, vcc_kind_t kind, const struct symbol *psym, sym->eval_priv = v; v = VTAILQ_FIRST(&v->children); assert(vjsn_is_array(v)); + vv = v; v = VTAILQ_FIRST(&v->children); assert(vjsn_is_string(v)); sym->type = VCC_Type(v->value); AN(sym->type); sym->r_methods = VCL_MET_TASK_ALL; + if (kind == SYM_CLI_METHOD) { + vv = VTAILQ_NEXT(vv, list); + assert(vjsn_is_string(vv)); + AZ(psym->extra); + psym->extra = vv->value; + } } static void @@ -237,6 +245,7 @@ vcc_vmod_kind(const char *type) VMOD_KIND("$OBJ", SYM_OBJECT); VMOD_KIND("$METHOD", SYM_METHOD); VMOD_KIND("$FUNC", SYM_FUNC); + VMOD_KIND("$CLI", SYM_CLI_METHOD); #undef VMOD_KIND return (SYM_NONE); } @@ -265,7 +274,7 @@ vcc_VmodObject(struct vcc *tl, struct symbol *sym) } static void -vcc_VmodSymbols(struct vcc *tl, const struct symbol *sym) +vcc_VmodSymbols(struct vcc *tl, struct symbol *sym) { const struct vjsn *vj; const struct vjsn_val *vv, *vv1, *vv2; @@ -490,6 +499,7 @@ vcc_Act_New(struct vcc *tl, struct token *t, struct symbol *sym) /* Scratch the generic INSTANCE type */ isym->type = osym->type; + isym->extra = osym->extra; CAST_OBJ_NOTNULL(vv, osym->eval_priv, VJSN_VAL_MAGIC); // vv = object name diff --git a/lib/libvcc/vcc_xref.c b/lib/libvcc/vcc_xref.c index 035c42cfb9..d9c0c3b3cc 100644 --- a/lib/libvcc/vcc_xref.c +++ b/lib/libvcc/vcc_xref.c @@ -410,7 +410,12 @@ vcc_instance_info(struct vcc *tl, const struct symbol *sym) AN(sym->rname); Fc(tl, 0, "\t{ .p = (uintptr_t *)&%s, .name = \"", sym->rname); VCC_SymName(tl->fc, sym); - Fc(tl, 0, "\" },\n"); + Fc(tl, 0, "\", .clip = "); + if (sym->extra) + Fc(tl, 0, "(uintptr_t *)&%s", sym->extra); + else + Fc(tl, 0, "NULL"); + Fc(tl, 0, "},\n"); } void @@ -418,7 +423,7 @@ VCC_InstanceInfo(struct vcc *tl) { Fc(tl, 0, "\nstatic const struct vpi_ii VGC_instance_info[] = {\n"); VCC_WalkSymbols(tl, vcc_instance_info, SYM_MAIN, SYM_INSTANCE); - Fc(tl, 0, "\t{ .p = NULL, .name = \"\" }\n"); + Fc(tl, 0, "\t{ .p = NULL, .name = \"\", .clip = NULL }\n"); Fc(tl, 0, "};\n"); } diff --git a/lib/libvcc/vmodtool.py b/lib/libvcc/vmodtool.py index 0eb77c5272..a6330cc1c6 100755 --- a/lib/libvcc/vmodtool.py +++ b/lib/libvcc/vmodtool.py @@ -744,6 +744,7 @@ def parse(self): self.rstlbl = '%s.%s()' % (self.vcc.modname, self.proto.name) self.vcc.contents.append(self) self.methods = [] + self.cli = None def rsthead(self, fo, man): if self.rstlbl: @@ -777,11 +778,15 @@ def cstuff(self, fo, w): fo.write(self.fini.cproto(['struct %s **' % sn], w)) for i in self.methods: fo.write(i.proto.cproto(['VRT_CTX', 'struct %s *' % sn], w)) + if self.cli is not None: + fo.write(self.cli.proto.cproto(['VRT_CTX', 'struct %s *' % sn], w)) fo.write("\n") def cstruct(self, fo, define): self.fmt_cstruct_proto(fo, self.init, define) self.fmt_cstruct_proto(fo, self.fini, define) + if self.cli is not None: + self.cli.cstruct(fo, define) for i in self.methods: i.cstruct(fo, define) fo.write("\n") @@ -803,6 +808,9 @@ def json(self, jl): ll.append(l2) self.fini.jsonproto(l2, self.fini.name) + if self.cli is not None: + self.cli.json(ll) + for i in self.methods: i.json(ll) @@ -835,6 +843,39 @@ def json(self, jl): self.proto.jsonproto(jl[-1], self.proto.cname()) +####################################################################### + + +class CliStanza(Stanza): + + ''' $Cli INT name (STRANDS) ''' + + def parse(self): + p = self.vcc.contents[-1] + assert isinstance(p, ObjectStanza) + self.pfx = p.proto.name + self.proto = ProtoType(self, prefix=self.pfx) + if p.cli is not None: + err("$Cli %s.%s: Cli method already defined for this class" + % (self.pfx, self.proto.bname), warn=False) + if self.proto.retval.vt != "INT": + err("$Cli %s.%s: Return value needs to be INT" + % (self.pfx, self.proto.bname), warn=False) + if len(self.proto.args) != 1 or self.proto.args[0].vt != 'STRANDS': + err("$Cli %s.%s: Need single argument of type STRANDS" + % (self.pfx, self.proto.bname), warn=False) +# self.proto.obj = "x" + self.pfx +# self.rstlbl = 'x%s()' % self.proto.name + p.cli = self + + def cstruct(self, fo, define): + self.fmt_cstruct_proto(fo, self.proto, define) + + def json(self, jl): + jl.append(["$CLI", self.proto.name]) + self.proto.jsonproto(jl[-1], self.proto.cname()) + + ####################################################################### DISPATCH = { @@ -845,6 +886,7 @@ def json(self, jl): "Function": FunctionStanza, "Object": ObjectStanza, "Method": MethodStanza, + "Cli": CliStanza, "Synopsis": SynopsisStanza, } diff --git a/vmod/tests/debug_c00001.vtc b/vmod/tests/debug_c00001.vtc new file mode 100644 index 0000000000..d432800cb3 --- /dev/null +++ b/vmod/tests/debug_c00001.vtc @@ -0,0 +1,26 @@ +varnishtest "Test vmod cli methods / vcl tell" + +varnish v1 -vcl+backend { + import debug; + + backend proforma none; + + sub vcl_init { + new obj0 = debug.obj(); + new obj1 = debug.obj("only_argument"); + new oo0 = debug.obj_opt(); + } +} -start + +# vcl2 not found +varnish v1 -clierr "300" "tell vcl2.obj0 a b c" +# No object named objX found +varnish v1 -clierr "300" "tell objX a b c" +# Object oo0 has no cli method +varnish v1 -clierr "300" "tell oo0 a b c" +# Too few parameters +varnish v1 -clierr "104" "tell obj0" + +varnish v1 -cliexpect "obj0: a b c" "tell obj0 a b c" +varnish v1 -cliexpect "obj0: a b c" "tell vcl1.obj0 a b c" +varnish v1 -cliexpect "obj1: a b c" "tell obj1 a b c" diff --git a/vmod/vmod_debug.vcc b/vmod/vmod_debug.vcc index b3ff9ac4d0..4220249636 100644 --- a/vmod/vmod_debug.vcc +++ b/vmod/vmod_debug.vcc @@ -78,6 +78,8 @@ $Method VOID .enum(ENUM { phk, des, kristian, mithrandir, martin }) Testing that enums work as part of object and that the parser isn't (too) buggy. +$Cli INT cli(STRANDS) + $Method VOID .obj() Covering the fact that a method can be named like the constructor. diff --git a/vmod/vmod_debug_obj.c b/vmod/vmod_debug_obj.c index 94331f047b..446595c5ff 100644 --- a/vmod/vmod_debug_obj.c +++ b/vmod/vmod_debug_obj.c @@ -35,6 +35,7 @@ #include "cache/cache.h" #include "vcl.h" +#include "vsb.h" #include "vcc_debug_if.h" @@ -79,6 +80,29 @@ xyzzy_obj__fini(struct xyzzy_debug_obj **op) FREE_OBJ(o); } +VCL_INT v_matchproto_(td_xyzzy_objcli) +xyzzy_objcli(VRT_CTX, struct xyzzy_debug_obj *o, VCL_STRANDS s) +{ + int i; + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(o, VMOD_DEBUG_OBJ_MAGIC); + + if (s->n > 0 && ! strcmp(s->p[0], "fail")) { + VSB_cat(ctx->msg, "You asked me to fail"); + return (300); + } + VSB_cat(ctx->msg, o->vcl_name); + VSB_putc(ctx->msg, ':'); + + for (i = 0; i < s->n; i++) { + VSB_putc(ctx->msg, ' '); + VSB_cat(ctx->msg, s->p[i]); + } + + return (200); +} + VCL_VOID v_matchproto_(td_xyzzy_obj_enum) xyzzy_obj_enum(VRT_CTX, struct xyzzy_debug_obj *o, VCL_ENUM e) {