diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c index 63b9b9eacb0..27c3ceb9423 100644 --- a/bin/varnishd/cache/cache_vcl.c +++ b/bin/varnishd/cache/cache_vcl.c @@ -999,6 +999,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[] = { @@ -1009,6 +1079,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 9b3b3db1cc3..fc8386ebd9e 100644 --- a/bin/varnishd/cache/cache_vpi.c +++ b/bin/varnishd/cache/cache_vpi.c @@ -302,3 +302,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 b4b9c3194db..29f47e752e4 100644 --- a/include/tbl/cli_cmds.h +++ b/include/tbl/cli_cmds.h @@ -449,6 +449,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 c1f29fa98f9..594a5952c15 100644 --- a/include/tbl/symbol_kind.h +++ b/include/tbl/symbol_kind.h @@ -47,6 +47,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 529ab9ed17e..5d49f457d77 100644 --- a/include/vcc_interface.h +++ b/include/vcc_interface.h @@ -93,6 +93,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 */ @@ -118,3 +119,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.h b/lib/libvcc/vcc_vmod.h index a1084c2f5f4..8e5bcb07a4c 100644 --- a/lib/libvcc/vcc_vmod.h +++ b/lib/libvcc/vcc_vmod.h @@ -36,6 +36,7 @@ STANZA(METHOD, method, SYM_METHOD) \ STANZA(OBJ, obj, SYM_OBJECT) \ STANZA(VMOD, vmod, SYM_NONE) \ - STANZA(RESTRICT, restrict, SYM_NONE) + STANZA(RESTRICT, restrict, SYM_NONE) \ + STANZA(CLI, cli, SYM_CLI_METHOD) -void vcc_VmodSymbols(struct vcc *tl, const struct symbol *sym); +void vcc_VmodSymbols(struct vcc *tl, struct symbol *sym); diff --git a/lib/libvcc/vcc_vmod_sym.c b/lib/libvcc/vcc_vmod_sym.c index ad2631f5f63..93f7938bae7 100644 --- a/lib/libvcc/vcc_vmod_sym.c +++ b/lib/libvcc/vcc_vmod_sym.c @@ -148,9 +148,10 @@ func_restrict(struct vcc *tl, struct symbol *sym, vcc_kind_t kind, const struct } static void -func_sym(struct vcc *tl, vcc_kind_t kind, const struct symbol *psym, - const struct vjsn_val *v, const struct vjsn_val *vv) +func_sym(struct vcc *tl, vcc_kind_t kind, struct symbol *psym, + const struct vjsn_val *v, const struct vjsn_val *vr) { + const struct vjsn_val *vv; struct symbol *sym; struct vsb *buf; @@ -190,16 +191,23 @@ 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; - func_restrict(tl, sym, kind, vv); + func_restrict(tl, sym, kind, vr); + if (kind == SYM_CLI_METHOD) { + vv = VTAILQ_NEXT(vv, list); + assert(vjsn_is_string(vv)); + AZ(psym->extra); + psym->extra = vv->value; + } } 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; @@ -266,6 +274,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 035c42cfb9f..d9c0c3b3cc5 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 e8c4137938f..b4747958853 100755 --- a/lib/libvcc/vmodtool.py +++ b/lib/libvcc/vmodtool.py @@ -770,6 +770,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: @@ -803,11 +804,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") @@ -829,6 +834,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) @@ -931,6 +939,39 @@ def json(self, jl): jl.append(["$ALIAS", self.sym_alias, self.sym_name]) +####################################################################### + + +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 = { @@ -941,6 +982,7 @@ def json(self, jl): "Function": FunctionStanza, "Object": ObjectStanza, "Method": MethodStanza, + "Cli": CliStanza, "Synopsis": SynopsisStanza, "Alias": AliasStanza, "Restrict": RestrictStanza, diff --git a/vmod/tests/debug_c00001.vtc b/vmod/tests/debug_c00001.vtc new file mode 100644 index 00000000000..fdf751be36f --- /dev/null +++ b/vmod/tests/debug_c00001.vtc @@ -0,0 +1,32 @@ +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" + +varnish v1 -vcl { backend proforma none; } + +varnish v1 -cliok "vcl.use vcl2" +varnish v1 -cliok "vcl.state vcl1 cold" +varnish v1 -cliexpect "obj0: a b c" "tell vcl1.obj0 a b c" diff --git a/vmod/vmod_debug.vcc b/vmod/vmod_debug.vcc index f9427a62f7c..b6cdae29ce8 100644 --- a/vmod/vmod_debug.vcc +++ b/vmod/vmod_debug.vcc @@ -81,6 +81,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 28a6bfa0012..eb6ce6fdd9b 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" @@ -83,6 +84,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) {