[master] 12cd341e3 varnishtest: Allow macros to be backed by functions

Dridi Boukelmoune dridi.boukelmoune at gmail.com
Mon Jul 5 15:48:05 UTC 2021


commit 12cd341e3cd8d588af0811106b3ba8e91b5c6b17
Author: Dridi Boukelmoune <dridi.boukelmoune at gmail.com>
Date:   Thu Jun 17 15:49:45 2021 +0200

    varnishtest: Allow macros to be backed by functions
    
    Instead of having a mere value, these would be able to compute a macro
    expansion. We parse the contents inside the ${...} delimiters as a VAV,
    but there can't be (yet?) nested curly {braces}, even quoted.
    
    The first argument inside the delimiters is the macro name, and other
    VAV arguments are treated as arguments to the macro's function.
    
    For example ${foo,bar,baz} would call the a macro "foo"'s function with
    arguments "bar" and "baz". Simple macros don't take arguments and work
    as usual.

diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c
index 7a9eb6144..2eb2e5d43 100644
--- a/bin/varnishtest/vtc.c
+++ b/bin/varnishtest/vtc.c
@@ -80,6 +80,7 @@ struct macro {
 	VTAILQ_ENTRY(macro)	list;
 	char			*name;
 	char			*val;
+	macro_f			*func;
 };
 
 static VTAILQ_HEAD(,macro) macro_list = VTAILQ_HEAD_INITIALIZER(macro_list);
@@ -99,7 +100,7 @@ static const struct cmds top_cmds[] = {
 /**********************************************************************/
 
 static struct macro *
-macro_def_int(const char *name, const char *fmt, va_list ap)
+macro_def_int(const char *name, macro_f *func, const char *fmt, va_list ap)
 {
 	struct macro *m;
 	char buf[512];
@@ -115,9 +116,15 @@ macro_def_int(const char *name, const char *fmt, va_list ap)
 		VTAILQ_INSERT_TAIL(&macro_list, m, list);
 	}
 	AN(m);
-	vbprintf(buf, fmt, ap);
-	REPLACE(m->val, buf);
-	AN(m->val);
+	if (func != NULL) {
+		AZ(fmt);
+		m->func = func;
+	} else {
+		AN(fmt);
+		vbprintf(buf, fmt, ap);
+		REPLACE(m->val, buf);
+		AN(m->val);
+	}
 	return (m);
 }
 
@@ -128,12 +135,12 @@ macro_def_int(const char *name, const char *fmt, va_list ap)
  */
 
 void
-extmacro_def(const char *name, const char *fmt, ...)
+extmacro_def(const char *name, macro_f *func, const char *fmt, ...)
 {
 	va_list ap;
 
 	va_start(ap, fmt);
-	(void)macro_def_int(name, fmt, ap);
+	(void)macro_def_int(name, func, fmt, ap);
 	va_end(ap);
 }
 
@@ -149,8 +156,13 @@ init_macro(void)
 	struct macro *m;
 
 	/* Dump the extmacros for completeness */
-	VTAILQ_FOREACH(m, &macro_list, list)
-		vtc_log(vltop, 4, "extmacro def %s=%s", m->name, m->val);
+	VTAILQ_FOREACH(m, &macro_list, list) {
+		if (m->val != NULL)
+			vtc_log(vltop, 4,
+			    "extmacro def %s=%s", m->name, m->val);
+		else
+			vtc_log(vltop, 4, "extmacro def %s(...)", m->name);
+	}
 
 	AZ(pthread_mutex_init(&macro_mtx, NULL));
 }
@@ -172,7 +184,7 @@ macro_def(struct vtclog *vl, const char *instance, const char *name,
 
 	AZ(pthread_mutex_lock(&macro_mtx));
 	va_start(ap, fmt);
-	m = macro_def_int(name, fmt, ap);
+	m = macro_def_int(name, NULL, fmt, ap);
 	va_end(ap);
 	vtc_log(vl, 4, "macro def %s=%s", name, m->val);
 	AZ(pthread_mutex_unlock(&macro_mtx));
@@ -208,35 +220,61 @@ void
 macro_cat(struct vtclog *vl, struct vsb *vsb, const char *b, const char *e)
 {
 	struct macro *m;
-	int l;
-	char *retval = NULL;
+	char **argv, *retval = NULL;
+	const char *err = NULL;
+	int argc;
 
 	AN(b);
 	if (e == NULL)
 		e = strchr(b, '\0');
 	AN(e);
-	l = e - b;
 
-	if (l == 4 && !memcmp(b, "date", l)) {
+	argv = VAV_ParseTxt(b, e, &argc, ARGV_COMMA);
+	AN(argv);
+
+	if (*argv != NULL)
+		vtc_fatal(vl, "Macro ${%.*s} parsing failed: %s",
+		    (int)(e - b), b, *argv);
+
+	assert(argc >= 2);
+
+	if (!strcmp(argv[1], "date")) {
 		double t = VTIM_real();
 		retval = malloc(VTIM_FORMAT_SIZE);
 		AN(retval);
 		VTIM_format(t, retval);
 		VSB_cat(vsb, retval);
 		free(retval);
+		VAV_Free(argv);
 		return;
 	}
 
 	AZ(pthread_mutex_lock(&macro_mtx));
 	VTAILQ_FOREACH(m, &macro_list, list) {
 		CHECK_OBJ_NOTNULL(m, MACRO_MAGIC);
-		if (!strncmp(b, m->name, l) && m->name[l] == '\0')
+		if (!strcmp(argv[1], m->name))
 			break;
 	}
-	if (m != NULL)
-		REPLACE(retval, m->val);
+	if (m != NULL) {
+		if (m->func != NULL) {
+			AZ(m->val);
+			retval = m->func(argc, argv, &err);
+			if (err == NULL)
+				AN(retval);
+		} else {
+			AN(m->val);
+			if (argc == 2)
+				REPLACE(retval, m->val);
+			else
+				err = "macro does not take arguments";
+		}
+	}
 	AZ(pthread_mutex_unlock(&macro_mtx));
 
+	if (err != NULL)
+		vtc_fatal(vl, "Macro ${%.*s} failed: %s",
+		    (int)(e - b), b, err);
+
 	if (retval == NULL) {
 		if (!ign_unknown_macro)
 			vtc_fatal(vl, "Macro ${%.*s} not found",
@@ -247,6 +285,7 @@ macro_cat(struct vtclog *vl, struct vsb *vsb, const char *b, const char *e)
 
 	VSB_cat(vsb, retval);
 	free(retval);
+	VAV_Free(argv);
 }
 
 struct vsb *
diff --git a/bin/varnishtest/vtc.h b/bin/varnishtest/vtc.h
index 6e6978018..55d28b351 100644
--- a/bin/varnishtest/vtc.h
+++ b/bin/varnishtest/vtc.h
@@ -139,8 +139,9 @@ struct vsb *macro_expand(struct vtclog *vl, const char *text);
 struct vsb *macro_expandf(struct vtclog *vl, const char *, ...)
     v_printflike_(2, 3);
 
-void extmacro_def(const char *name, const char *fmt, ...)
-    v_printflike_(2, 3);
+typedef char* macro_f(int, char *const *, const char **);
+void extmacro_def(const char *name, macro_f *func, const char *fmt, ...)
+    v_printflike_(3, 4);
 
 struct http;
 void cmd_stream(CMD_ARGS);
diff --git a/bin/varnishtest/vtc_main.c b/bin/varnishtest/vtc_main.c
index 309bd876e..141dac1e5 100644
--- a/bin/varnishtest/vtc_main.c
+++ b/bin/varnishtest/vtc_main.c
@@ -167,7 +167,7 @@ parse_D_opt(char *arg)
 	if (!q)
 		return (0);
 	*q++ = '\0';
-	extmacro_def(p, "%s", q);
+	extmacro_def(p, NULL, "%s", q);
 
 	return (1);
 }
@@ -544,7 +544,7 @@ i_mode(void)
 
 	}
 	AN(topbuild);
-	extmacro_def("topbuild", "%s", topbuild);
+	extmacro_def("topbuild", NULL, "%s", topbuild);
 
 	/*
 	 * Build $PATH which can find all programs in the build tree
@@ -605,7 +605,7 @@ ip_magic(void)
 	}
 	assert(bad_backend_fd >= 0);
 	VTCP_myname(bad_backend_fd, abuf, sizeof abuf, pbuf, sizeof(pbuf));
-	extmacro_def("localhost", "%s", abuf);
+	extmacro_def("localhost", NULL, "%s", abuf);
 	s = strdup(abuf);
 	AN(s);
 
@@ -620,9 +620,9 @@ ip_magic(void)
 
 	/* Expose a backend that is forever down. */
 	if (VSA_Get_Proto(sa) == AF_INET)
-		extmacro_def("bad_backend", "%s:%s", abuf, pbuf);
+		extmacro_def("bad_backend", NULL, "%s:%s", abuf, pbuf);
 	else
-		extmacro_def("bad_backend", "[%s]:%s", abuf, pbuf);
+		extmacro_def("bad_backend", NULL, "[%s]:%s", abuf, pbuf);
 
 	/* our default bind/listen address */
 	if (VSA_Get_Proto(sa) == AF_INET)
@@ -631,7 +631,7 @@ ip_magic(void)
 		bprintf(abuf, "[%s]:0", s);
 	free(s);
 
-	extmacro_def("listen_addr", "%s", abuf);
+	extmacro_def("listen_addr", NULL, "%s", abuf);
 	default_listen_addr = strdup(abuf);
 	AN(default_listen_addr);
 	free(sa);
@@ -644,7 +644,7 @@ ip_magic(void)
 	 * check your /proc/sys/net/ipv4/ip_nonlocal_bind setting.
 	 */
 
-	extmacro_def("bad_ip", "%s", "192.0.2.255");
+	extmacro_def("bad_ip", NULL, "%s", "192.0.2.255");
 }
 
 /**********************************************************************
@@ -725,7 +725,7 @@ main(int argc, char * const *argv)
 		tmppath = strdup("/tmp");
 
 	cwd = getcwd(buf, sizeof buf);
-	extmacro_def("pwd", "%s", cwd);
+	extmacro_def("pwd", NULL, "%s", cwd);
 
 	vmod_path = NULL;
 


More information about the varnish-commit mailing list