[master] e0b9077 Add support for JSON CLI responses.

Poul-Henning Kamp phk at FreeBSD.org
Thu Feb 4 02:55:26 CET 2016


commit e0b9077c87b683a12af45daaf8c08ab4e7ce4195
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Thu Feb 4 01:51:05 2016 +0000

    Add support for JSON CLI responses.
    
    To get JSON back, the first argument must be "-j".
    
    Currently only "help -j" is implemented, but that
    will helpfully tell you which commands support
    json output.
    
    All JSON output has the form:
    
    	[ version#, [<cli_command>],
    		stuff
    	]
    
    For instance:
    
    	[ 1, ["help", "-j"],
    	  {
    	  "request": "help",
    	  "syntax": "help [<command>]",
    	  "help": "\tShow command/protocol help.",
    	  "minarg": 0, "maxarg": 1, "flags": "", "json": true
    	  },
    	  {
    	  "request": "ping",
    	  "syntax": "ping [<timestamp>]",
    	  "help": "\tKeep connection alive.",
    	  "minarg": 0, "maxarg": 1, "flags": "", "json": false
    	  },
    	  ...
    	]
    
    The string quoting for weird characters in JSON is XXX incomplete.
    
    Prodded for by:	Kristian

diff --git a/bin/varnishd/cache/cache_cli.c b/bin/varnishd/cache/cache_cli.c
index 3ba8cdd..bc2302e 100644
--- a/bin/varnishd/cache/cache_cli.c
+++ b/bin/varnishd/cache/cache_cli.c
@@ -112,7 +112,7 @@ CLI_Run(void)
 
 static struct cli_proto cli_cmds[] = {
 	{ CLI_PING,		"i", VCLS_func_ping },
-	{ CLI_HELP,             "i", VCLS_func_help },
+	{ CLI_HELP,             "i", VCLS_func_help, VCLS_func_help_json, cli_cmds },
 	{ NULL }
 };
 
diff --git a/bin/varnishd/mgt/mgt_cli.c b/bin/varnishd/mgt/mgt_cli.c
index baae752..6df8f9e 100644
--- a/bin/varnishd/mgt/mgt_cli.c
+++ b/bin/varnishd/mgt/mgt_cli.c
@@ -142,7 +142,9 @@ mcf_askchild(struct cli *cli, const char * const *av, void *priv)
 	 */
 	if (cli_o <= 0) {
 		if (!strcmp(av[1], "help")) {
-			VCLI_Out(cli, "No help from child, (not running).\n");
+			if (av[2] == NULL || strcmp(av[2], "-j"))
+				VCLI_Out(cli,
+				   "No help from child, (not running).\n");
 			return;
 		}
 		VCLI_SetResult(cli, CLIS_UNKNOWN);
@@ -308,7 +310,7 @@ mcf_auth(struct cli *cli, const char *const *av, void *priv)
 }
 
 static struct cli_proto cli_auth[] = {
-	{ CLI_HELP,		"", VCLS_func_help },
+	{ CLI_HELP,		"", VCLS_func_help, VCLS_func_help_json },
 	{ CLI_PING,		"", VCLS_func_ping },
 	{ CLI_AUTH,		"", mcf_auth },
 	{ CLI_QUIT,		"", VCLS_func_close },
diff --git a/bin/varnishtest/tests/b00008.vtc b/bin/varnishtest/tests/b00008.vtc
index 2448aa4..ac18701 100644
--- a/bin/varnishtest/tests/b00008.vtc
+++ b/bin/varnishtest/tests/b00008.vtc
@@ -24,6 +24,8 @@ varnish v1 -start
 
 varnish v1 -cliok "help"
 
+varnish v1 -cliok "help -j"
+
 varnish v1 -clierr 106 "param.set waiter HASH(0x8839c4c)"
 
 varnish v1 -cliok "param.set cli_limit 128"
diff --git a/include/vcli_priv.h b/include/vcli_priv.h
index 2f98d7d..61f5720 100644
--- a/include/vcli_priv.h
+++ b/include/vcli_priv.h
@@ -48,12 +48,14 @@ struct cli_proto {
 
 	/* Dispatch information */
 	cli_func_t		*func;
+	cli_func_t		*jsonfunc;
 	void			*priv;
 };
 
 /* The implementation must provide these functions */
 int VCLI_Overflow(struct cli *cli);
-void VCLI_Out(struct cli *cli, const char *fmt, ...)
-    __v_printflike(2, 3);
+void VCLI_Out(struct cli *cli, const char *fmt, ...) __v_printflike(2, 3);
 void VCLI_Quote(struct cli *cli, const char *str);
+void VCLI_JSON_str(struct cli *cli, const char *str);
+void VCLI_JSON_ver(struct cli *cli, unsigned ver, const char * const * av);
 void VCLI_SetResult(struct cli *cli, unsigned r);
diff --git a/include/vcli_serve.h b/include/vcli_serve.h
index fe240c1..0ff65d0 100644
--- a/include/vcli_serve.h
+++ b/include/vcli_serve.h
@@ -42,4 +42,5 @@ void VCLS_Destroy(struct VCLS **);
 /* From libvarnish/cli.c */
 cli_func_t	VCLS_func_close;
 cli_func_t	VCLS_func_help;
+cli_func_t	VCLS_func_help_json;
 cli_func_t	VCLS_func_ping;
diff --git a/include/vsb.h b/include/vsb.h
index 4b34314..a3bdd3d 100644
--- a/include/vsb.h
+++ b/include/vsb.h
@@ -76,6 +76,7 @@ char		*VSB_data(const struct vsb *);
 ssize_t		 VSB_len(const struct vsb *);
 void		 VSB_delete(struct vsb *);
 #define VSB_QUOTE_NONL	1
+#define VSB_QUOTE_JSON	2
 void		 VSB_quote(struct vsb *s, const char *p, int len, int how);
 void		 VSB_indent(struct vsb *, int);
 #ifdef __cplusplus
diff --git a/lib/libvarnish/cli_common.c b/lib/libvarnish/cli_common.c
index 44a6869..b735d1f 100644
--- a/lib/libvarnish/cli_common.c
+++ b/lib/libvarnish/cli_common.c
@@ -84,6 +84,31 @@ VCLI_Overflow(struct cli *cli)
 
 /*lint -e{818} cli could be const */
 void
+VCLI_JSON_str(struct cli *cli, const char *s)
+{
+
+	CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
+	VSB_quote(cli->sb, s, -1, VSB_QUOTE_JSON);
+}
+
+/*lint -e{818} cli could be const */
+void
+VCLI_JSON_ver(struct cli *cli, unsigned ver, const char * const * av)
+{
+	int i;
+
+	CHECK_OBJ_NOTNULL(cli, CLI_MAGIC);
+	VCLI_Out(cli, "[ %u, [", ver);
+	for (i = 1; av[i] != NULL; i++) {
+		VCLI_JSON_str(cli, av[i]);
+		if (av[i + 1] != NULL)
+			VCLI_Out(cli, ", ");
+	}
+	VCLI_Out(cli, "]");
+}
+
+/*lint -e{818} cli could be const */
+void
 VCLI_Quote(struct cli *cli, const char *s)
 {
 
diff --git a/lib/libvarnish/cli_serve.c b/lib/libvarnish/cli_serve.c
index 64fb7dd..9861e0c 100644
--- a/lib/libvarnish/cli_serve.c
+++ b/lib/libvarnish/cli_serve.c
@@ -146,7 +146,7 @@ VCLS_func_help(struct cli *cli, const char * const *av, void *priv)
 				}
 				for (u = 0; u < sizeof cp->flags; u++) {
 					if (cp->flags[u] == '*') {
-						cp->func(cli,av,priv);
+						cp->func(cli, av, NULL);
 						return;
 					}
 				}
@@ -179,7 +179,7 @@ VCLS_func_help(struct cli *cli, const char * const *av, void *priv)
 			if (i)
 				continue;
 			if (wc) {
-				cp->func(cli, av, priv);
+				cp->func(cli, av, NULL);
 				continue;
 			}
 			if (debug != d)
@@ -192,6 +192,57 @@ VCLS_func_help(struct cli *cli, const char * const *av, void *priv)
 	}
 }
 
+void
+VCLS_func_help_json(struct cli *cli, const char * const *av, void *priv)
+{
+	struct cli_proto *cp;
+	struct VCLS_func *cfn;
+	struct VCLS *cs;
+	unsigned u, f_wc, f_i;
+
+	(void)priv;
+	cs = cli->cls;
+	CHECK_OBJ_NOTNULL(cs, VCLS_MAGIC);
+
+	if (priv == NULL)
+		VCLI_JSON_ver(cli, 1, av);
+	VTAILQ_FOREACH(cfn, &cs->funcs, list) {
+		if (cfn->auth > cli->auth)
+			continue;
+		for (cp = cfn->clp; cp->request != NULL; cp++) {
+			f_wc = f_i = 0;
+			for (u = 0; u < sizeof cp->flags; u++) {
+				if (cp->flags[u] == '*')
+					f_wc = 1;
+				if (cp->flags[u] == 'i')
+					f_i = 1;
+			}
+			if (f_wc) {
+				cp->func(cli, av, priv);
+				continue;
+			}
+			if (f_i)
+				continue;
+			VCLI_Out(cli, ",\n  {");
+			VCLI_Out(cli, "\n  \"request\": ");
+			VCLI_JSON_str(cli, cp->request);
+			VCLI_Out(cli, ",\n  \"syntax\": ");
+			VCLI_JSON_str(cli, cp->syntax);
+			VCLI_Out(cli, ",\n  \"help\": ");
+			VCLI_JSON_str(cli, cp->help);
+			VCLI_Out(cli, ",\n  \"minarg\": %d", cp->minarg);
+			VCLI_Out(cli, ", \"maxarg\": %d", cp->maxarg);
+			VCLI_Out(cli, ", \"flags\": ");
+			VCLI_JSON_str(cli, cp->flags);
+			VCLI_Out(cli, ", \"json\": %s",
+			    cp->jsonfunc == NULL ? "false" : "true");
+			VCLI_Out(cli, "\n  }");
+		}
+	}
+	if (priv == NULL)
+		VCLI_Out(cli, "\n]\n");
+}
+
 /*--------------------------------------------------------------------
  * Look for a CLI command to execute
  */
@@ -201,6 +252,7 @@ cls_dispatch(struct cli *cli, struct cli_proto *clp, char * const * av,
     unsigned ac)
 {
 	struct cli_proto *cp;
+	int json = 0;
 
 	AN(av);
 	for (cp = clp; cp->request != NULL; cp++) {
@@ -212,19 +264,27 @@ cls_dispatch(struct cli *cli, struct cli_proto *clp, char * const * av,
 	if (cp->request == NULL)
 		return (0);
 
-	if (cp->func == NULL) {
+	if (ac > 1 && !strcmp(av[2], "-j"))
+		json = 1;
+
+	if (cp->func == NULL && !json) {
 		VCLI_Out(cli, "Unimplemented\n");
 		VCLI_SetResult(cli, CLIS_UNIMPL);
 		return(1);
 	}
+	if (cp->jsonfunc == NULL && json) {
+		VCLI_Out(cli, "JSON unimplemented\n");
+		VCLI_SetResult(cli, CLIS_UNIMPL);
+		return(1);
+	}
 
-	if (ac - 1 < cp->minarg) {
+	if (ac - 1 < cp->minarg + json) {
 		VCLI_Out(cli, "Too few parameters\n");
 		VCLI_SetResult(cli, CLIS_TOOFEW);
 		return(1);
 	}
 
-	if (ac - 1> cp->maxarg) {
+	if (ac - 1> cp->maxarg + json) {
 		VCLI_Out(cli, "Too many parameters\n");
 		VCLI_SetResult(cli, CLIS_TOOMANY);
 		return(1);
@@ -232,7 +292,10 @@ cls_dispatch(struct cli *cli, struct cli_proto *clp, char * const * av,
 
 	cli->result = CLIS_OK;
 	VSB_clear(cli->sb);
-	cp->func(cli, (const char * const *)av, cp->priv);
+	if (json)
+		cp->jsonfunc(cli, (const char * const *)av, cp->priv);
+	else
+		cp->func(cli, (const char * const *)av, cp->priv);
 	return (1);
 }
 
diff --git a/lib/libvarnish/vsb.c b/lib/libvarnish/vsb.c
index 4d80b8a..d737ef1 100644
--- a/lib/libvarnish/vsb.c
+++ b/lib/libvarnish/vsb.c
@@ -498,7 +498,6 @@ VSB_quote(struct vsb *s, const char *p, int len, int how)
 	const char *q;
 	int quote = 0;
 
-	(void)how;	/* For future enhancements */
 	if (len == -1)
 		len = strlen(p);
 
@@ -508,7 +507,7 @@ VSB_quote(struct vsb *s, const char *p, int len, int how)
 			break;
 		}
 	}
-	if (!quote) {
+	if (!quote && !(how & VSB_QUOTE_JSON)) {
 		(void)VSB_bcat(s, p, len);
 		return;
 	}
@@ -536,6 +535,7 @@ VSB_quote(struct vsb *s, const char *p, int len, int how)
 			(void)VSB_cat(s, "\\t");
 			break;
 		default:
+			/* XXX: Implement VSB_QUOTE_JSON */
 			if (isgraph(*q))
 				(void)VSB_putc(s, *q);
 			else



More information about the varnish-commit mailing list