[master] a502ab3 Renovate the parameter/cli code.

Poul-Henning Kamp phk at varnish-cache.org
Tue Nov 12 11:02:09 CET 2013


commit a502ab3cef61095c85a3495b78c80ce161999153
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Tue Nov 12 09:58:04 2013 +0000

    Renovate the parameter/cli code.
    
    Certain of our parameter names are so long that they have shoved the
    "long" param.show output all the way to the right.  Break the line
    after the param name and use most of the width again.
    
    Also add support for two column tables using TAB chars, for descriptions
    of params like "debug", "feature" etc.  The width autosizes, and lines
    wrap as expected in the second column.  Eliminate the 'pad' field in
    the tbl includes.
    
    Move all the "tweak" functions to a separate source file.
    
    Clean up white-space in param descriptions.

diff --git a/bin/varnishd/Makefile.am b/bin/varnishd/Makefile.am
index 680deb1..6980404 100644
--- a/bin/varnishd/Makefile.am
+++ b/bin/varnishd/Makefile.am
@@ -64,6 +64,7 @@ varnishd_SOURCES = \
 	mgt/mgt_param.c \
 	mgt/mgt_param_tbl.c \
 	mgt/mgt_param_bits.c \
+	mgt/mgt_param_tweak.c \
 	mgt/mgt_pool.c \
 	mgt/mgt_sandbox.c \
 	mgt/mgt_sandbox_solaris.c \
diff --git a/bin/varnishd/common/params.h b/bin/varnishd/common/params.h
index 4a16304..28c4500 100644
--- a/bin/varnishd/common/params.h
+++ b/bin/varnishd/common/params.h
@@ -36,14 +36,14 @@
 #define VSM_CLASS_PARAM		"Params"
 
 enum debug_bits {
-#define DEBUG_BIT(U, l, p, d) DBG_##U,
+#define DEBUG_BIT(U, l, d) DBG_##U,
 #include "tbl/debug_bits.h"
 #undef DEBUG_BIT
        DBG_Reserved
 };
 
 enum feature_bits {
-#define FEATURE_BIT(U, l, p, d, ld) FEATURE_##U,
+#define FEATURE_BIT(U, l, d, ld) FEATURE_##U,
 #include "tbl/feature_bits.h"
 #undef FEATURE_BIT
        FEATURE_Reserved
diff --git a/bin/varnishd/mgt/mgt_param.c b/bin/varnishd/mgt/mgt_param.c
index 8284a14..bd858ad 100644
--- a/bin/varnishd/mgt/mgt_param.c
+++ b/bin/varnishd/mgt/mgt_param.c
@@ -29,6 +29,7 @@
 
 #include "config.h"
 
+#include <ctype.h>
 #include <grp.h>
 #include <limits.h>
 #include <math.h>
@@ -48,630 +49,159 @@
 #include "vcli.h"
 #include "vcli_common.h"
 #include "vcli_priv.h"
-#include "vnum.h"
-#include "vss.h"
 
 #include "mgt_cli.h"
 
 struct params mgt_param;
 static int nparspec;
 static struct parspec const ** parspecs;
-static int margin;
+static const int margin1 = 8;
+static int margin2 = 0;
+static const int wrap_at = 72;
+static const int tab0 = 3;
 
 /*--------------------------------------------------------------------*/
 
-static const struct parspec *
-mcf_findpar(const char *name)
-{
-	int i;
-
-	for (i = 0; i < nparspec; i++)
-		if (!strcmp(parspecs[i]->name, name))
-			return (parspecs[i]);
-	return (NULL);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-tweak_generic_timeout(struct cli *cli, volatile unsigned *dst, const char *arg)
-{
-	unsigned u;
-
-	if (arg != NULL) {
-		u = strtoul(arg, NULL, 0);
-		if (u == 0) {
-			VCLI_Out(cli, "Timeout must be greater than zero\n");
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return;
-		}
-		*dst = u;
-	} else
-		VCLI_Out(cli, "%u", *dst);
-}
-
-/*--------------------------------------------------------------------*/
+static const char OBJ_STICKY_TEXT[] =
+	"\n\nNB: This parameter is evaluated only when objects are created."
+	"To change it for all objects, restart or ban everything.";
 
-void
-tweak_timeout(struct cli *cli, const struct parspec *par, const char *arg)
-{
-	volatile unsigned *dest;
-
-	dest = par->priv;
-	tweak_generic_timeout(cli, dest, arg);
-}
+static const char DELAYED_EFFECT_TEXT[] =
+	"\n\nNB: This parameter may take quite some time to take (full) effect.";
 
-/*--------------------------------------------------------------------*/
+static const char MUST_RESTART_TEXT[] =
+	"\n\nNB: This parameter will not take any effect until the "
+	"child process has been restarted.";
 
-static int
-tweak_generic_timeout_double(struct cli *cli, volatile double *dest,
-    const char *arg, double min, double max)
-{
-	double u;
-	char *p;
-
-	if (arg != NULL) {
-		p = NULL;
-		u = strtod(arg, &p);
-		if (*arg == '\0' || *p != '\0') {
-			VCLI_Out(cli, "Not a number(%s)\n", arg);
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return (-1);
-		}
-		if (u < min) {
-			VCLI_Out(cli,
-			    "Timeout must be greater or equal to %.g\n", min);
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return (-1);
-		}
-		if (u > max) {
-			VCLI_Out(cli,
-			    "Timeout must be less than or equal to %.g\n", max);
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return (-1);
-		}
-		*dest = u;
-	} else
-		VCLI_Out(cli, "%.6f", *dest);
-	return (0);
-}
+static const char MUST_RELOAD_TEXT[] =
+	"\n\nNB: This parameter will not take any effect until the "
+	"VCL programs have been reloaded.";
 
-void
-tweak_timeout_double(struct cli *cli, const struct parspec *par,
-    const char *arg)
-{
-	volatile double *dest;
+static const char EXPERIMENTAL_TEXT[] =
+	"\n\nNB: We do not know yet if it is a good idea to change "
+	"this parameter, or if the default value is even sensible.  "
+	"Caution is advised, and feedback is most welcome.";
 
-	dest = par->priv;
-	(void)tweak_generic_timeout_double(cli, dest, arg, par->min, par->max);
-}
+static const char WIZARD_TEXT[] =
+	"\n\nNB: Do not change this parameter, unless a developer tell "
+	"you to do so.";
 
-/*--------------------------------------------------------------------*/
+static const char PROTECTED_TEXT[] =
+	"\n\nNB: This parameter is protected and can not be changed.";
 
-void
-tweak_generic_double(struct cli *cli, const struct parspec *par,
-    const char *arg)
-{
-	volatile double *dest;
-	char *p;
-	double u;
-
-	dest = par->priv;
-	if (arg != NULL) {
-		p = NULL;
-		u = strtod(arg, &p);
-		if (*p != '\0') {
-			VCLI_Out(cli,
-			    "Not a number (%s)\n", arg);
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return;
-		}
-		if (u < par->min) {
-			VCLI_Out(cli,
-			    "Must be greater or equal to %.g\n",
-				 par->min);
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return;
-		}
-		if (u > par->max) {
-			VCLI_Out(cli,
-			    "Must be less than or equal to %.g\n",
-				 par->max);
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return;
-		}
-		*dest = u;
-	} else
-		VCLI_Out(cli, "%f", *dest);
-}
 
 /*--------------------------------------------------------------------*/
 
-void
-tweak_bool(struct cli *cli, const struct parspec *par, const char *arg)
-{
-	volatile unsigned *dest;
-	int mode = 0;
-
-	if (!strcmp(par->def, "off") || !strcmp(par->def, "on"))
-		mode = 1;
-
-	dest = par->priv;
-	if (arg != NULL) {
-		if (!strcasecmp(arg, "off"))
-			*dest = 0;
-		else if (!strcasecmp(arg, "disable"))
-			*dest = 0;
-		else if (!strcasecmp(arg, "no"))
-			*dest = 0;
-		else if (!strcasecmp(arg, "false"))
-			*dest = 0;
-		else if (!strcasecmp(arg, "on"))
-			*dest = 1;
-		else if (!strcasecmp(arg, "enable"))
-			*dest = 1;
-		else if (!strcasecmp(arg, "yes"))
-			*dest = 1;
-		else if (!strcasecmp(arg, "true"))
-			*dest = 1;
-		else {
-			VCLI_Out(cli,
-			    mode ?
-				"use \"on\" or \"off\"\n" :
-				"use \"true\" or \"false\"\n");
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return;
-		}
-	} else if (mode) {
-		VCLI_Out(cli, *dest ? "on" : "off");
-	} else {
-		VCLI_Out(cli, *dest ? "true" : "false");
-	}
-}
-
-/*--------------------------------------------------------------------*/
-
-int
-tweak_generic_uint(struct cli *cli, volatile unsigned *dest, const char *arg,
-    unsigned min, unsigned max)
-{
-	unsigned u;
-	char *p;
-
-	if (arg != NULL) {
-		p = NULL;
-		if (!strcasecmp(arg, "unlimited"))
-			u = UINT_MAX;
-		else {
-			u = strtoul(arg, &p, 0);
-			if (*arg == '\0' || *p != '\0') {
-				VCLI_Out(cli, "Not a number (%s)\n", arg);
-				VCLI_SetResult(cli, CLIS_PARAM);
-				return (-1);
-			}
-		}
-		if (u < min) {
-			VCLI_Out(cli, "Must be at least %u\n", min);
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return (-1);
-		}
-		if (u > max) {
-			VCLI_Out(cli, "Must be no more than %u\n", max);
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return (-1);
-		}
-		*dest = u;
-	} else if (*dest == UINT_MAX) {
-		VCLI_Out(cli, "unlimited");
-	} else {
-		VCLI_Out(cli, "%u", *dest);
-	}
-	return (0);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-tweak_uint(struct cli *cli, const struct parspec *par, const char *arg)
-{
-	volatile unsigned *dest;
-
-	dest = par->priv;
-	(void)tweak_generic_uint(cli, dest, arg,
-	    (uint)par->min, (uint)par->max);
-}
-
-/*--------------------------------------------------------------------*/
-
-static void
-fmt_bytes(struct cli *cli, uintmax_t t)
+static const struct parspec *
+mcf_findpar(const char *name, int *idx)
 {
-	const char *p;
-
-	if (t & 0xff) {
-		VCLI_Out(cli, "%jub", t);
-		return;
-	}
-	for (p = "kMGTPEZY"; *p; p++) {
-		if (t & 0x300) {
-			VCLI_Out(cli, "%.2f%c", t / 1024.0, *p);
-			return;
-		}
-		t /= 1024;
-		if (t & 0x0ff) {
-			VCLI_Out(cli, "%ju%c", t, *p);
-			return;
-		}
-	}
-	VCLI_Out(cli, "(bogus number)");
-}
+	int i;
 
-static void
-tweak_generic_bytes(struct cli *cli, volatile ssize_t *dest, const char *arg,
-    double min, double max)
-{
-	uintmax_t r;
-	const char *p;
-
-	if (arg != NULL) {
-		p = VNUM_2bytes(arg, &r, 0);
-		if (p != NULL) {
-			VCLI_Out(cli, "Could not convert to bytes.\n");
-			VCLI_Out(cli, "%s\n", p);
-			VCLI_Out(cli,
-			    "  Try something like '80k' or '120M'\n");
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return;
-		}
-		if ((uintmax_t)((ssize_t)r) != r) {
-			fmt_bytes(cli, r);
-			VCLI_Out(cli, " is too large for this architecture.\n");
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return;
-		}
-		if (max != 0. && r > max) {
-			VCLI_Out(cli, "Must be no more than ");
-			fmt_bytes(cli, (uintmax_t)max);
-			VCLI_Out(cli, "\n");
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return;
-		}
-		if (r < min) {
-			VCLI_Out(cli, "Must be at least ");
-			fmt_bytes(cli, (uintmax_t)min);
-			VCLI_Out(cli, "\n");
-			VCLI_SetResult(cli, CLIS_PARAM);
-			return;
+	AN(name);
+	for (i = 0; i < nparspec; i++)
+		if (!strcmp(parspecs[i]->name, name)) {
+			if (idx != NULL)
+				*idx = i;
+			return (parspecs[i]);
 		}
-		*dest = r;
-	} else {
-		fmt_bytes(cli, *dest);
-	}
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-tweak_bytes(struct cli *cli, const struct parspec *par, const char *arg)
-{
-	volatile ssize_t *dest;
-
-	assert(par->min >= 0);
-	dest = par->priv;
-	tweak_generic_bytes(cli, dest, arg, par->min, par->max);
-}
-
-
-/*--------------------------------------------------------------------*/
-
-void
-tweak_bytes_u(struct cli *cli, const struct parspec *par, const char *arg)
-{
-	volatile unsigned *d1;
-	volatile ssize_t dest;
-
-	assert(par->max <= UINT_MAX);
-	assert(par->min >= 0);
-	d1 = par->priv;
-	dest = *d1;
-	tweak_generic_bytes(cli, &dest, arg, par->min, par->max);
-	*d1 = dest;
+	if (idx != NULL)
+		*idx = -1;
+	return (NULL);
 }
 
 /*--------------------------------------------------------------------
- * XXX: slightly magic.  We want to initialize to "nobody" (XXX: shouldn't
- * XXX: that be something autocrap found for us ?) but we don't want to
- * XXX: fail initialization if that user doesn't exists, even though we
- * XXX: do want to fail it, in subsequent sets.
- * XXX: The magic init string is a hack for this.
+ * Wrap the text nicely.
+ * Lines are allowed to contain to TABS and we render that as a table
+ * taking the width of the first column into account.
  */
 
-void
-tweak_user(struct cli *cli, const struct parspec *par, const char *arg)
-{
-	struct passwd *pw;
-
-	(void)par;
-	if (arg != NULL) {
-		if (*arg != '\0') {
-			pw = getpwnam(arg);
-			if (pw == NULL) {
-				VCLI_Out(cli, "Unknown user");
-				VCLI_SetResult(cli, CLIS_PARAM);
-				return;
-			}
-			REPLACE(mgt_param.user, pw->pw_name);
-			mgt_param.uid = pw->pw_uid;
+static void
+mcf_wrap_line(struct cli *cli, const char *b, const char *e, int tabs, int m0)
+{
+	int n, hadtabs = 0;
+	const char *w;
+
+	n = m0;
+	VCLI_Out(cli, "%*s", n, "");
+
+	while (b < e) {
+		if (!isspace(*b)) {
+			VCLI_Out(cli, "%c", *b);
+			b++;
+			n++;
+		} else if (*b == '\t') {
+			assert(tabs);
+			assert(hadtabs < 2);
+			do {
+				VCLI_Out(cli, " ");
+				n++;
+			} while ((n % tabs) != (m0 + tab0) % tabs);
+			b++;
+			hadtabs++;
 		} else {
-			mgt_param.uid = getuid();
-		}
-	} else if (mgt_param.user) {
-		VCLI_Out(cli, "%s (%d)", mgt_param.user, (int)mgt_param.uid);
-	} else {
-		VCLI_Out(cli, "UID %d", (int)mgt_param.uid);
-	}
-}
-
-/*--------------------------------------------------------------------
- * XXX: see comment for tweak_user, same thing here.
- */
-
-void
-tweak_group(struct cli *cli, const struct parspec *par, const char *arg)
-{
-	struct group *gr;
-
-	(void)par;
-	if (arg != NULL) {
-		if (*arg != '\0') {
-			gr = getgrnam(arg);
-			if (gr == NULL) {
-				VCLI_Out(cli, "Unknown group");
-				VCLI_SetResult(cli, CLIS_PARAM);
+			assert (*b == ' ');
+			for (w = b + 1; w < e; w++)
+				if (isspace(*w))
+					break;
+			if (n + (w - b) < wrap_at) {
+				VCLI_Out(cli, "%.*s", (int)(w - b), b);
+				n += (w - b);
+				b = w;
+			} else {
+				assert(hadtabs == 0 || hadtabs == 2);
+				VCLI_Out(cli, "\n");
+				mcf_wrap_line(cli, b + 1, e, 0,
+				    hadtabs ? m0 + tab0 + tabs : m0);
 				return;
 			}
-			REPLACE(mgt_param.group, gr->gr_name);
-			mgt_param.gid = gr->gr_gid;
-		} else {
-			mgt_param.gid = getgid();
 		}
-	} else if (mgt_param.group) {
-		VCLI_Out(cli, "%s (%d)", mgt_param.group, (int)mgt_param.gid);
-	} else {
-		VCLI_Out(cli, "GID %d", (int)mgt_param.gid);
 	}
+	assert(b == e);
 }
 
-/*--------------------------------------------------------------------*/
-
 static void
-clean_listen_sock_head(struct listen_sock_head *lsh)
-{
-	struct listen_sock *ls, *ls2;
-
-	VTAILQ_FOREACH_SAFE(ls, lsh, list, ls2) {
-		CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC);
-		VTAILQ_REMOVE(lsh, ls, list);
-		free(ls->name);
-		free(ls->addr);
-		FREE_OBJ(ls);
-	}
-}
-
-void
-tweak_listen_address(struct cli *cli, const struct parspec *par,
-    const char *arg)
-{
-	char **av;
-	int i;
-	struct listen_sock		*ls;
-	struct listen_sock_head		lsh;
-
-	(void)par;
-	if (arg == NULL) {
-		VCLI_Quote(cli, mgt_param.listen_address);
-		return;
-	}
-
-	av = VAV_Parse(arg, NULL, ARGV_COMMA);
-	if (av == NULL) {
-		VCLI_Out(cli, "Parse error: out of memory");
-		VCLI_SetResult(cli, CLIS_PARAM);
-		return;
-	}
-	if (av[0] != NULL) {
-		VCLI_Out(cli, "Parse error: %s", av[0]);
-		VCLI_SetResult(cli, CLIS_PARAM);
-		VAV_Free(av);
-		return;
-	}
-	if (av[1] == NULL) {
-		VCLI_Out(cli, "Empty listen address");
-		VCLI_SetResult(cli, CLIS_PARAM);
-		VAV_Free(av);
-		return;
-	}
-	VTAILQ_INIT(&lsh);
-	for (i = 1; av[i] != NULL; i++) {
-		struct vss_addr **ta;
-		int j, n;
-
-		n = VSS_resolve(av[i], "http", &ta);
-		if (n == 0) {
-			VCLI_Out(cli, "Invalid listen address ");
-			VCLI_Quote(cli, av[i]);
-			VCLI_SetResult(cli, CLIS_PARAM);
-			break;
-		}
-		for (j = 0; j < n; ++j) {
-			ALLOC_OBJ(ls, LISTEN_SOCK_MAGIC);
-			AN(ls);
-			ls->sock = -1;
-			ls->addr = ta[j];
-			ls->name = strdup(av[i]);
-			AN(ls->name);
-			VTAILQ_INSERT_TAIL(&lsh, ls, list);
-		}
-		free(ta);
-	}
-	VAV_Free(av);
-	if (cli != NULL && cli->result != CLIS_OK) {
-		clean_listen_sock_head(&lsh);
-		return;
-	}
-
-	REPLACE(mgt_param.listen_address, arg);
-
-	clean_listen_sock_head(&heritage.socks);
-	heritage.nsocks = 0;
-
-	while (!VTAILQ_EMPTY(&lsh)) {
-		ls = VTAILQ_FIRST(&lsh);
-		VTAILQ_REMOVE(&lsh, ls, list);
-		CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC);
-		VTAILQ_INSERT_TAIL(&heritage.socks, ls, list);
-		heritage.nsocks++;
-	}
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-tweak_string(struct cli *cli, const struct parspec *par, const char *arg)
-{
-	char **p = TRUST_ME(par->priv);
-
-	AN(p);
-	/* XXX should have tweak_generic_string */
-	if (arg == NULL) {
-		VCLI_Quote(cli, *p);
-	} else {
-		REPLACE(*p, arg);
-	}
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-tweak_waiter(struct cli *cli, const struct parspec *par, const char *arg)
-{
-
-	/* XXX should have tweak_generic_string */
-	(void)par;
-	WAIT_tweak_waiter(cli, arg);
-}
-
-/*--------------------------------------------------------------------*/
-
-void
-tweak_poolparam(struct cli *cli, const struct parspec *par, const char *arg)
+mcf_wrap(struct cli *cli, const char *text)
 {
-	volatile struct poolparam *pp, px;
-	char **av;
+	const char *p, *q, *r;
+	int tw = 0;
 
-	pp = par->priv;
-	if (arg == NULL) {
-		VCLI_Out(cli, "%u,%u,%g",
-		    pp->min_pool, pp->max_pool, pp->max_age);
-	} else {
-		av = VAV_Parse(arg, NULL, ARGV_COMMA);
-		do {
-			if (av[0] != NULL) {
-				VCLI_Out(cli, "Parse error: %s", av[0]);
-				VCLI_SetResult(cli, CLIS_PARAM);
-				break;
-			}
-			if (av[1] == NULL || av[2] == NULL || av[3] == NULL) {
-				VCLI_Out(cli,
-				    "Three fields required:"
-				    " min_pool, max_pool and max_age\n");
-				VCLI_SetResult(cli, CLIS_PARAM);
-				break;
-			}
-			px = *pp;
-			if (tweak_generic_uint(cli, &px.min_pool, av[1],
-			    (uint)par->min, (uint)par->max))
-				break;
-			if (tweak_generic_uint(cli, &px.max_pool, av[2],
-			    (uint)par->min, (uint)par->max))
-				break;
-			if (tweak_generic_timeout_double(cli, &px.max_age,
-			    av[3], 0, 1e6))
-				break;
-			if (px.min_pool > px.max_pool) {
-				VCLI_Out(cli,
-				    "min_pool cannot be larger"
-				    " than max_pool\n");
-				VCLI_SetResult(cli, CLIS_PARAM);
+	if (strchr(text, '\t') != NULL) {
+		for (p = text; *p != '\0'; ) {
+			q = strstr(p, "\n\t");
+			if (q == NULL)
 				break;
+			q += 2;
+			r = strchr(q, '\t');
+			if (r == NULL) {
+				fprintf(stderr,
+				    "LINE with just one TAB: <%s>\n", text);
+				exit(2);
 			}
-			*pp = px;
-		} while(0);
-		VAV_Free(av);
+			if (r - q > tw)
+				tw = r - q;
+			p = q;
+		}
+		tw += 2;
+		if (tw < 20)
+			tw = 20;
 	}
-}
-
-/*--------------------------------------------------------------------*/
 
-/*
- * Make sure to end all lines with either a space or newline of the
- * formatting will go haywire.
- */
-
-#define OBJ_STICKY_TEXT \
-	"\nNB: This parameter is evaluated only when objects are created." \
-	"To change it for all objects, restart or ban everything."
-
-#define DELAYED_EFFECT_TEXT \
-	"\nNB: This parameter may take quite some time to take (full) effect."
-
-#define MUST_RESTART_TEXT \
-	"\nNB: This parameter will not take any effect until the " \
-	"child process has been restarted."
-
-#define MUST_RELOAD_TEXT \
-	"\nNB: This parameter will not take any effect until the " \
-	"VCL programs have been reloaded."
-
-#define EXPERIMENTAL_TEXT \
-	"\nNB: We do not know yet if it is a good idea to change " \
-	"this parameter, or if the default value is even sensible.  " \
-	"Caution is advised, and feedback is most welcome."
-
-#define WIZARD_TEXT \
-	"\nNB: Do not change this parameter, unless a developer tell " \
-	"you to do so."
-
-#define PROTECTED_TEXT \
-	"\nNB: This parameter is protected and can not be changed."
-
-/*--------------------------------------------------------------------*/
-
-#define WIDTH 76
-
-static void
-mcf_wrap(struct cli *cli, const char *text)
-{
-	const char *p, *q;
-
-	/* Format text to COLUMNS width */
 	for (p = text; *p != '\0'; ) {
+		if (*p == '\n') {
+			VCLI_Out(cli, "\n");
+			p++;
+			continue;
+		}
 		q = strchr(p, '\n');
 		if (q == NULL)
 			q = strchr(p, '\0');
-		if (q > p + WIDTH - margin) {
-			q = p + WIDTH - margin;
-			while (q > p && *q != ' ')
-				q--;
-			AN(q);
-		}
-		VCLI_Out(cli, "%*s %.*s\n", margin, "", (int)(q - p), p);
+		mcf_wrap_line(cli, p, q, tw, margin1);
 		p = q;
-		if (*p == ' ' || *p == '\n')
-			p++;
 	}
 }
 
+/*--------------------------------------------------------------------*/
+
 void
 mcf_param_show(struct cli *cli, const char * const *av, void *priv)
 {
@@ -680,7 +210,7 @@ mcf_param_show(struct cli *cli, const char * const *av, void *priv)
 	int lfmt;
 
 	(void)priv;
-	if (av[2] == NULL || strcmp(av[2], "-l"))
+	if (av[2] == NULL)
 		lfmt = 0;
 	else
 		lfmt = 1;
@@ -688,13 +218,11 @@ mcf_param_show(struct cli *cli, const char * const *av, void *priv)
 		pp = parspecs[i];
 		if (av[2] != NULL && !lfmt && strcmp(pp->name, av[2]))
 			continue;
-		VCLI_Out(cli, "%-*s ", margin, pp->name);
-		if (pp->func == NULL) {
-			VCLI_Out(cli, "Not implemented.\n");
-			if (av[2] != NULL && !lfmt)
-				return;
-			else
-				continue;
+		if (lfmt) {
+			VCLI_Out(cli, "%s\n", pp->name);
+			VCLI_Out(cli, "%-*sValue is: ", margin1, " ");
+		} else {
+			VCLI_Out(cli, "%-*s", margin2, pp->name);
 		}
 		pp->func(cli, pp, NULL);
 		if (pp->units != NULL && *pp->units != '\0')
@@ -702,8 +230,8 @@ mcf_param_show(struct cli *cli, const char * const *av, void *priv)
 		else
 			VCLI_Out(cli, "\n");
 		if (av[2] != NULL) {
-			VCLI_Out(cli, "%-*s Default is %s\n",
-			    margin, "", pp->def);
+			VCLI_Out(cli, "%-*sDefault is: %s\n\n",
+			    margin1, "", pp->def);
 			mcf_wrap(cli, pp->descr);
 			if (pp->flags & OBJ_STICKY)
 				mcf_wrap(cli, OBJ_STICKY_TEXT);
@@ -722,7 +250,7 @@ mcf_param_show(struct cli *cli, const char * const *av, void *priv)
 			if (!lfmt)
 				return;
 			else
-				VCLI_Out(cli, "\n");
+				VCLI_Out(cli, "\n\n");
 		}
 	}
 	if (av[2] != NULL && !lfmt) {
@@ -750,17 +278,14 @@ MCF_ParamProtect(struct cli *cli, const char *args)
 		return;
 	}
 	for (i = 1; av[i] != NULL; i++) {
-		for (j = 0; j < nparspec; j++)
-			if (!strcmp(parspecs[j]->name, av[i]))
-				break;
-		if (j == nparspec) {
+		if (mcf_findpar(av[i], &j) == NULL) {
 			VCLI_Out(cli, "Unknown parameter %s", av[i]);
 			VCLI_SetResult(cli, CLIS_PARAM);
 			VAV_Free(av);
 			return;
 		}
 		pp = calloc(sizeof *pp, 1L);
-		XXXAN(pp);
+		AN(pp);
 		memcpy(pp, parspecs[j], sizeof *pp);
 		pp->flags |= PROTECTED;
 		parspecs[j] = pp;
@@ -775,7 +300,7 @@ MCF_ParamSet(struct cli *cli, const char *param, const char *val)
 {
 	const struct parspec *pp;
 
-	pp = mcf_findpar(param);
+	pp = mcf_findpar(param, NULL);
 	if (pp == NULL) {
 		VCLI_SetResult(cli, CLIS_PARAM);
 		VCLI_Out(cli, "Unknown parameter \"%s\".", param);
@@ -819,7 +344,7 @@ mcf_param_set(struct cli *cli, const char * const *av, void *priv)
  */
 
 static int
-parspec_cmp(const void *a, const void *b)
+mcf_parspec_cmp(const void *a, const void *b)
 {
 	struct parspec * const * pa = a;
 	struct parspec * const * pb = b;
@@ -830,14 +355,24 @@ static void
 MCF_AddParams(const struct parspec *ps)
 {
 	const struct parspec *pp;
+	const char *s;
 	int n;
 
 	n = 0;
 	for (pp = ps; pp->name != NULL; pp++) {
-		if (mcf_findpar(pp->name) != NULL)
+		AN(pp->func);
+		s = strchr(pp->descr, '\0');
+		if (isspace(s[-1])) {
+			fprintf(stderr,
+			    "Param->descr has trailing space: %s\n", pp->name);
+			exit(2);
+		}
+		if (mcf_findpar(pp->name, NULL) != NULL) {
 			fprintf(stderr, "Duplicate param: %s\n", pp->name);
-		if (strlen(pp->name) + 1 > margin)
-			margin = strlen(pp->name) + 1;
+			exit(2);
+		}
+		if (strlen(pp->name) + 1 > margin2)
+			margin2 = strlen(pp->name) + 1;
 		n++;
 	}
 	parspecs = realloc(parspecs, (1L + nparspec + n) * sizeof *parspecs);
@@ -845,7 +380,7 @@ MCF_AddParams(const struct parspec *ps)
 	for (pp = ps; pp->name != NULL; pp++)
 		parspecs[nparspec++] = pp;
 	parspecs[nparspec] = NULL;
-	qsort (parspecs, nparspec, sizeof parspecs[0], parspec_cmp);
+	qsort (parspecs, nparspec, sizeof parspecs[0], mcf_parspec_cmp);
 }
 
 /*--------------------------------------------------------------------
diff --git a/bin/varnishd/mgt/mgt_param.h b/bin/varnishd/mgt/mgt_param.h
index 64fccf1..61c4e0e 100644
--- a/bin/varnishd/mgt/mgt_param.h
+++ b/bin/varnishd/mgt/mgt_param.h
@@ -82,5 +82,5 @@ extern const struct parspec WRK_parspec[];
 	"The three numbers are:\n"					\
 	"   min_pool -- minimum size of free pool.\n"			\
 	"   max_pool -- maximum size of free pool.\n"			\
-	"   max_age -- max age of free element.\n"
+	"   max_age -- max age of free element."
 
diff --git a/bin/varnishd/mgt/mgt_param_bits.c b/bin/varnishd/mgt/mgt_param_bits.c
index 247eeea..5d8e7cf 100644
--- a/bin/varnishd/mgt/mgt_param_bits.c
+++ b/bin/varnishd/mgt/mgt_param_bits.c
@@ -158,7 +158,7 @@ tweak_vsl_mask(struct cli *cli, const struct parspec *par, const char *arg)
  */
 
 static const char * const debug_tags[] = {
-#  define DEBUG_BIT(U,l,p,d) [DBG_##U] = #l,
+#  define DEBUG_BIT(U, l, d) [DBG_##U] = #l,
 #  include "tbl/debug_bits.h"
 #  undef DEBUG_BIT
        NULL
@@ -197,7 +197,7 @@ tweak_debug(struct cli *cli, const struct parspec *par, const char *arg)
  */
 
 static const char * const feature_tags[] = {
-#  define FEATURE_BIT(U,l,p,d, ld) [FEATURE_##U] = #l,
+#  define FEATURE_BIT(U, l, d, ld) [FEATURE_##U] = #l,
 #  include "tbl/feature_bits.h"
 #  undef FEATURE_BIT
        NULL
@@ -239,23 +239,23 @@ tweak_feature(struct cli *cli, const struct parspec *par, const char *arg)
 const struct parspec VSL_parspec[] = {
 	{ "vsl_mask", tweak_vsl_mask, NULL, 0, 0,
 		"Mask individual VSL messages from being logged.\n"
-		"\tdefault\tSet default value\n\n"
+		"\tdefault\tSet default value\n"
 		"Use +/- prefixe in front of VSL tag name, to mask/unmask "
 		"individual VSL messages.",
 		0, "default", "" },
 	{ "debug", tweak_debug, NULL, 0, 0,
 		"Enable/Disable various kinds of debugging.\n"
-		"\tnone\t\tDisable all debugging\n\n"
-		"Use +/- prefix to set/reset individual bits:\n"
-#define DEBUG_BIT(U, l, p, d) "\t" #l "\t" p d "\n"
+		"\tnone\tDisable all debugging\n\n"
+		"Use +/- prefix to set/reset individual bits:"
+#define DEBUG_BIT(U, l, d) "\n\t" #l "\t" d
 #include "tbl/debug_bits.h"
 #undef DEBUG_BIT
 		, 0, "none", "" },
 	{ "feature", tweak_feature, NULL, 0, 0,
 		"Enable/Disable various minor features.\n"
-		"\tnone\t\tDisable all features.\n\n"
-		"Use +/- prefix to enable/disable individual feature:\n"
-#define FEATURE_BIT(U, l, p, d, ld) "\t" #l "\t" p d "\n"
+		"\tnone\tDisable all features.\n\n"
+		"Use +/- prefix to enable/disable individual feature:"
+#define FEATURE_BIT(U, l, d, ld) "\n\t" #l "\t" d
 #include "tbl/feature_bits.h"
 #undef FEATURE_BIT
 		, 0, "none", "" },
diff --git a/bin/varnishd/mgt/mgt_param_tbl.c b/bin/varnishd/mgt/mgt_param_tbl.c
index f123661..12bc84e 100644
--- a/bin/varnishd/mgt/mgt_param_tbl.c
+++ b/bin/varnishd/mgt/mgt_param_tbl.c
@@ -56,14 +56,14 @@ const struct parspec mgt_parspec[] = {
 	{ "default_ttl", tweak_timeout_double, &mgt_param.default_ttl,
 		0, UINT_MAX,
 		"The TTL assigned to objects if neither the backend nor "
-		"the VCL code assigns one.\n",
+		"the VCL code assigns one.",
 		OBJ_STICKY,
 		"120", "seconds" },
 	{ "default_grace", tweak_timeout_double, &mgt_param.default_grace,
 		0, UINT_MAX,
 		"Default grace period.  We will deliver an object "
 		"this long after it has expired, provided another thread "
-		"is attempting to get a new copy.\n",
+		"is attempting to get a new copy.",
 		OBJ_STICKY,
 		"10", "seconds" },
 	{ "default_keep", tweak_timeout_double, &mgt_param.default_keep,
@@ -95,14 +95,14 @@ const struct parspec mgt_parspec[] = {
 		"One use is for the io-vectors for writing requests and"
 		" responses to sockets, having too little space will"
 		" result in more writev(2) system calls, having too much"
-		" just wastes the space.\n",
+		" just wastes the space.",
 		DELAYED_EFFECT,
 		"2048", "bytes" },
 	{ "http_req_hdr_len",
 		tweak_bytes_u, &mgt_param.http_req_hdr_len,
 		40, UINT_MAX,
 		"Maximum length of any HTTP client request header we will "
-		"allow.  The limit is inclusive its continuation lines.\n",
+		"allow.  The limit is inclusive its continuation lines.",
 		0,
 		"8k", "bytes" },
 	{ "http_req_size",
@@ -120,7 +120,7 @@ const struct parspec mgt_parspec[] = {
 		tweak_bytes_u, &mgt_param.http_resp_hdr_len,
 		40, UINT_MAX,
 		"Maximum length of any HTTP backend response header we will "
-		"allow.  The limit is inclusive its continuation lines.\n",
+		"allow.  The limit is inclusive its continuation lines.",
 		0,
 		"8k", "bytes" },
 	{ "http_resp_size",
@@ -139,7 +139,7 @@ const struct parspec mgt_parspec[] = {
 		"{req|resp|bereq|beresp}.http "
 		"(obj.http is autosized to the exact number of headers).\n"
 		"Cheap, ~20 bytes, in terms of workspace memory.\n"
-		"Note that the first line occupies five header lines.\n",
+		"Note that the first line occupies five header lines.",
 		0,
 		"64", "header lines" },
 	{ "vsl_buffer",
@@ -176,7 +176,7 @@ const struct parspec mgt_parspec[] = {
 	{ "pipe_timeout", tweak_timeout, &mgt_param.pipe_timeout, 0, 0,
 		"Idle timeout for PIPE sessions. "
 		"If nothing have been received in either direction for "
-		"this many seconds, the session is closed.\n",
+		"this many seconds, the session is closed.",
 		0,
 		"60", "seconds" },
 	{ "send_timeout", tweak_timeout, &mgt_param.send_timeout, 0, 0,
@@ -220,7 +220,7 @@ const struct parspec mgt_parspec[] = {
 		"5", "seconds" },
 #endif
 	{ "auto_restart", tweak_bool, &mgt_param.auto_restart, 0, 0,
-		"Restart child process automatically if it dies.\n",
+		"Restart child process automatically if it dies.",
 		0,
 		"on", "bool" },
 	{ "nuke_limit",
@@ -244,7 +244,7 @@ const struct parspec mgt_parspec[] = {
 		    &mgt_param.fetch_maxchunksize, 64 * 1024, UINT_MAX,
 		"The maximum chunksize we attempt to allocate from storage. "
 		"Making this too large may cause delays and storage "
-		"fragmentation.\n",
+		"fragmentation.",
 		EXPERIMENTAL,
 		"256m", "bytes" },
 	{ "accept_filter", tweak_bool, &mgt_param.accept_filter, 0, 0,
@@ -266,7 +266,7 @@ const struct parspec mgt_parspec[] = {
 		"Size of buffer for CLI command input."
 		"\nYou may need to increase this if you have big VCL files "
 		"and use the vcl.inline CLI command.\n"
-		"NB: Must be specified with -p to have effect.\n",
+		"NB: Must be specified with -p to have effect.",
 		0,
 		"8k", "bytes" },
 	{ "cli_limit",
@@ -305,16 +305,16 @@ const struct parspec mgt_parspec[] = {
 	{ "max_restarts", tweak_uint, &mgt_param.max_restarts, 0, UINT_MAX,
 		"Upper limit on how many times a request can restart."
 		"\nBe aware that restarts are likely to cause a hit against "
-		"the backend, so don't increase thoughtlessly.\n",
+		"the backend, so don't increase thoughtlessly.",
 		0,
 		"4", "restarts" },
 	{ "max_retries", tweak_uint, &mgt_param.max_retries, 0, UINT_MAX,
-		"Upper limit on how many times a backend fetch can retry.\n",
+		"Upper limit on how many times a backend fetch can retry.",
 		0,
 		"4", "retries" },
 	{ "max_esi_depth",
 		tweak_uint, &mgt_param.max_esi_depth, 0, UINT_MAX,
-		"Maximum depth of esi:include processing.\n",
+		"Maximum depth of esi:include processing.",
 		0,
 		"5", "levels" },
 	{ "connect_timeout", tweak_timeout_double,
@@ -386,7 +386,7 @@ const struct parspec mgt_parspec[] = {
 		"before just dropping connections.\n"
 		"This is mostly an anti-DoS measure, and setting it plenty "
 		"high should not hurt, as long as you have the memory for "
-		"it.\n",
+		"it.",
 		0,
 		"100000", "sessions" },
 	{ "timeout_linger", tweak_timeout_double, &mgt_param.timeout_linger,
@@ -405,19 +405,19 @@ const struct parspec mgt_parspec[] = {
 		"Log the local address on the TCP connection in the "
 		"SessionOpen VSL record.\n"
 		"Disabling this saves a getsockname(2) system call "
-		"per TCP connection.\n",
+		"per TCP connection.",
 		0,
 		"on", "bool" },
 	{ "waiter", tweak_waiter, NULL, 0, 0,
-		"Select the waiter kernel interface.\n",
+		"Select the waiter kernel interface.",
 		WIZARD | MUST_RESTART,
 		WAITER_DEFAULT, NULL },
 	{ "ban_dups", tweak_bool, &mgt_param.ban_dups, 0, 0,
-		"Detect and eliminate duplicate bans.\n",
+		"Detect and eliminate duplicate bans.",
 		0,
 		"on", "bool" },
 	{ "syslog_cli_traffic", tweak_bool, &mgt_param.syslog_cli_traffic, 0, 0,
-		"Log all CLI traffic to syslog(LOG_INFO).\n",
+		"Log all CLI traffic to syslog(LOG_INFO).",
 		0,
 		"on", "bool" },
 	{ "ban_lurker_sleep", tweak_timeout_double,
@@ -429,7 +429,7 @@ const struct parspec mgt_parspec[] = {
 		0,
 		"0.01", "s" },
 	{ "http_range_support", tweak_bool, &mgt_param.http_range_support, 0, 0,
-		"Enable support for HTTP Range headers.\n",
+		"Enable support for HTTP Range headers.",
 		0,
 		"on", "bool" },
 	{ "http_gzip_support", tweak_bool, &mgt_param.http_gzip_support, 0, 0,
@@ -469,18 +469,18 @@ const struct parspec mgt_parspec[] = {
 	{ "shortlived", tweak_timeout_double,
 		&mgt_param.shortlived, 0, UINT_MAX,
 		"Objects created with TTL shorter than this are always "
-		"put in transient storage.\n",
+		"put in transient storage.",
 		0,
 		"10.0", "s" },
 	{ "critbit_cooloff", tweak_timeout_double,
 		&mgt_param.critbit_cooloff, 60, 254,
 		"How long time the critbit hasher keeps deleted objheads "
-		"on the cooloff list.\n",
+		"on the cooloff list.",
 		WIZARD,
 		"180.0", "s" },
 	{ "sigsegv_handler", tweak_bool, &mgt_param.sigsegv_handler, 0, 0,
 		"Install a signal handler which tries to dump debug information "
-		"on segmentation faults.\n",
+		"on segmentation faults.",
 		MUST_RESTART,
 		"off", "bool" },
 	{ "vcl_dir", tweak_string, &mgt_vcl_dir, 0, 0,
@@ -504,18 +504,18 @@ const struct parspec mgt_parspec[] = {
 		NULL },
 
 	{ "vcc_err_unref", tweak_bool, &mgt_vcc_err_unref, 0, 0,
-		"Unreferenced VCL objects result in error.\n",
+		"Unreferenced VCL objects result in error.",
 		0,
 		"on", "bool" },
 
 	{ "vcc_allow_inline_c", tweak_bool, &mgt_vcc_allow_inline_c, 0, 0,
-		"Allow inline C code in VCL.\n",
+		"Allow inline C code in VCL.",
 		0,
 		"off", "bool" },
 
 	{ "vcc_unsafe_path", tweak_bool, &mgt_vcc_unsafe_path, 0, 0,
 		"Allow '/' in vmod & include paths.\n"
-		"Allow 'import ... from ...'.\n",
+		"Allow 'import ... from ...'.",
 		0,
 		"on", "bool" },
 
@@ -559,7 +559,7 @@ const struct parspec mgt_parspec[] = {
 		&mgt_param.bo_cache, 0, 0,
 		"Cache free busyobj per worker thread. "
 		"Disable this if you have very high hitrates and want "
-		"to save the memory of one busyobj per worker thread. ",
+		"to save the memory of one busyobj per worker thread.",
 		0,
 		"off", "bool"},
 
diff --git a/bin/varnishd/mgt/mgt_param_tweak.c b/bin/varnishd/mgt/mgt_param_tweak.c
new file mode 100644
index 0000000..ec5ef05
--- /dev/null
+++ b/bin/varnishd/mgt/mgt_param_tweak.c
@@ -0,0 +1,597 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006-2011 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Functions for tweaking parameters
+ *
+ */
+
+#include "config.h"
+
+#include <grp.h>
+#include <limits.h>
+#include <math.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mgt/mgt.h"
+#include "common/heritage.h"
+#include "common/params.h"
+
+#include "mgt/mgt_param.h"
+#include "waiter/waiter.h"
+#include "vav.h"
+#include "vcli.h"
+#include "vcli_common.h"
+#include "vcli_priv.h"
+#include "vnum.h"
+#include "vss.h"
+
+#include "mgt_cli.h"
+
+/*--------------------------------------------------------------------*/
+
+static void
+tweak_generic_timeout(struct cli *cli, volatile unsigned *dst, const char *arg)
+{
+	unsigned u;
+
+	if (arg != NULL) {
+		u = strtoul(arg, NULL, 0);
+		if (u == 0) {
+			VCLI_Out(cli, "Timeout must be greater than zero\n");
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return;
+		}
+		*dst = u;
+	} else
+		VCLI_Out(cli, "%u", *dst);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+tweak_timeout(struct cli *cli, const struct parspec *par, const char *arg)
+{
+	volatile unsigned *dest;
+
+	dest = par->priv;
+	tweak_generic_timeout(cli, dest, arg);
+}
+
+/*--------------------------------------------------------------------*/
+
+static int
+tweak_generic_timeout_double(struct cli *cli, volatile double *dest,
+    const char *arg, double min, double max)
+{
+	double u;
+	char *p;
+
+	if (arg != NULL) {
+		p = NULL;
+		u = strtod(arg, &p);
+		if (*arg == '\0' || *p != '\0') {
+			VCLI_Out(cli, "Not a number(%s)\n", arg);
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return (-1);
+		}
+		if (u < min) {
+			VCLI_Out(cli,
+			    "Timeout must be greater or equal to %.g\n", min);
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return (-1);
+		}
+		if (u > max) {
+			VCLI_Out(cli,
+			    "Timeout must be less than or equal to %.g\n", max);
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return (-1);
+		}
+		*dest = u;
+	} else
+		VCLI_Out(cli, "%.6f", *dest);
+	return (0);
+}
+
+void
+tweak_timeout_double(struct cli *cli, const struct parspec *par,
+    const char *arg)
+{
+	volatile double *dest;
+
+	dest = par->priv;
+	(void)tweak_generic_timeout_double(cli, dest, arg, par->min, par->max);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+tweak_generic_double(struct cli *cli, const struct parspec *par,
+    const char *arg)
+{
+	volatile double *dest;
+	char *p;
+	double u;
+
+	dest = par->priv;
+	if (arg != NULL) {
+		p = NULL;
+		u = strtod(arg, &p);
+		if (*p != '\0') {
+			VCLI_Out(cli,
+			    "Not a number (%s)\n", arg);
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return;
+		}
+		if (u < par->min) {
+			VCLI_Out(cli,
+			    "Must be greater or equal to %.g\n",
+				 par->min);
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return;
+		}
+		if (u > par->max) {
+			VCLI_Out(cli,
+			    "Must be less than or equal to %.g\n",
+				 par->max);
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return;
+		}
+		*dest = u;
+	} else
+		VCLI_Out(cli, "%f", *dest);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+tweak_bool(struct cli *cli, const struct parspec *par, const char *arg)
+{
+	volatile unsigned *dest;
+	int mode = 0;
+
+	if (!strcmp(par->def, "off") || !strcmp(par->def, "on"))
+		mode = 1;
+
+	dest = par->priv;
+	if (arg != NULL) {
+		if (!strcasecmp(arg, "off"))
+			*dest = 0;
+		else if (!strcasecmp(arg, "disable"))
+			*dest = 0;
+		else if (!strcasecmp(arg, "no"))
+			*dest = 0;
+		else if (!strcasecmp(arg, "false"))
+			*dest = 0;
+		else if (!strcasecmp(arg, "on"))
+			*dest = 1;
+		else if (!strcasecmp(arg, "enable"))
+			*dest = 1;
+		else if (!strcasecmp(arg, "yes"))
+			*dest = 1;
+		else if (!strcasecmp(arg, "true"))
+			*dest = 1;
+		else {
+			VCLI_Out(cli,
+			    mode ?
+				"use \"on\" or \"off\"\n" :
+				"use \"true\" or \"false\"\n");
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return;
+		}
+	} else if (mode) {
+		VCLI_Out(cli, *dest ? "on" : "off");
+	} else {
+		VCLI_Out(cli, *dest ? "true" : "false");
+	}
+}
+
+/*--------------------------------------------------------------------*/
+
+int
+tweak_generic_uint(struct cli *cli, volatile unsigned *dest, const char *arg,
+    unsigned min, unsigned max)
+{
+	unsigned u;
+	char *p;
+
+	if (arg != NULL) {
+		p = NULL;
+		if (!strcasecmp(arg, "unlimited"))
+			u = UINT_MAX;
+		else {
+			u = strtoul(arg, &p, 0);
+			if (*arg == '\0' || *p != '\0') {
+				VCLI_Out(cli, "Not a number (%s)\n", arg);
+				VCLI_SetResult(cli, CLIS_PARAM);
+				return (-1);
+			}
+		}
+		if (u < min) {
+			VCLI_Out(cli, "Must be at least %u\n", min);
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return (-1);
+		}
+		if (u > max) {
+			VCLI_Out(cli, "Must be no more than %u\n", max);
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return (-1);
+		}
+		*dest = u;
+	} else if (*dest == UINT_MAX) {
+		VCLI_Out(cli, "unlimited");
+	} else {
+		VCLI_Out(cli, "%u", *dest);
+	}
+	return (0);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+tweak_uint(struct cli *cli, const struct parspec *par, const char *arg)
+{
+	volatile unsigned *dest;
+
+	dest = par->priv;
+	(void)tweak_generic_uint(cli, dest, arg,
+	    (uint)par->min, (uint)par->max);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+fmt_bytes(struct cli *cli, uintmax_t t)
+{
+	const char *p;
+
+	if (t & 0xff) {
+		VCLI_Out(cli, "%jub", t);
+		return;
+	}
+	for (p = "kMGTPEZY"; *p; p++) {
+		if (t & 0x300) {
+			VCLI_Out(cli, "%.2f%c", t / 1024.0, *p);
+			return;
+		}
+		t /= 1024;
+		if (t & 0x0ff) {
+			VCLI_Out(cli, "%ju%c", t, *p);
+			return;
+		}
+	}
+	VCLI_Out(cli, "(bogus number)");
+}
+
+static void
+tweak_generic_bytes(struct cli *cli, volatile ssize_t *dest, const char *arg,
+    double min, double max)
+{
+	uintmax_t r;
+	const char *p;
+
+	if (arg != NULL) {
+		p = VNUM_2bytes(arg, &r, 0);
+		if (p != NULL) {
+			VCLI_Out(cli, "Could not convert to bytes.\n");
+			VCLI_Out(cli, "%s\n", p);
+			VCLI_Out(cli,
+			    "  Try something like '80k' or '120M'\n");
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return;
+		}
+		if ((uintmax_t)((ssize_t)r) != r) {
+			fmt_bytes(cli, r);
+			VCLI_Out(cli, " is too large for this architecture.\n");
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return;
+		}
+		if (max != 0. && r > max) {
+			VCLI_Out(cli, "Must be no more than ");
+			fmt_bytes(cli, (uintmax_t)max);
+			VCLI_Out(cli, "\n");
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return;
+		}
+		if (r < min) {
+			VCLI_Out(cli, "Must be at least ");
+			fmt_bytes(cli, (uintmax_t)min);
+			VCLI_Out(cli, "\n");
+			VCLI_SetResult(cli, CLIS_PARAM);
+			return;
+		}
+		*dest = r;
+	} else {
+		fmt_bytes(cli, *dest);
+	}
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+tweak_bytes(struct cli *cli, const struct parspec *par, const char *arg)
+{
+	volatile ssize_t *dest;
+
+	assert(par->min >= 0);
+	dest = par->priv;
+	tweak_generic_bytes(cli, dest, arg, par->min, par->max);
+}
+
+
+/*--------------------------------------------------------------------*/
+
+void
+tweak_bytes_u(struct cli *cli, const struct parspec *par, const char *arg)
+{
+	volatile unsigned *d1;
+	volatile ssize_t dest;
+
+	assert(par->max <= UINT_MAX);
+	assert(par->min >= 0);
+	d1 = par->priv;
+	dest = *d1;
+	tweak_generic_bytes(cli, &dest, arg, par->min, par->max);
+	*d1 = dest;
+}
+
+/*--------------------------------------------------------------------
+ * XXX: slightly magic.  We want to initialize to "nobody" (XXX: shouldn't
+ * XXX: that be something autocrap found for us ?) but we don't want to
+ * XXX: fail initialization if that user doesn't exists, even though we
+ * XXX: do want to fail it, in subsequent sets.
+ * XXX: The magic init string is a hack for this.
+ */
+
+void
+tweak_user(struct cli *cli, const struct parspec *par, const char *arg)
+{
+	struct passwd *pw;
+
+	(void)par;
+	if (arg != NULL) {
+		if (*arg != '\0') {
+			pw = getpwnam(arg);
+			if (pw == NULL) {
+				VCLI_Out(cli, "Unknown user");
+				VCLI_SetResult(cli, CLIS_PARAM);
+				return;
+			}
+			REPLACE(mgt_param.user, pw->pw_name);
+			mgt_param.uid = pw->pw_uid;
+		} else {
+			mgt_param.uid = getuid();
+		}
+	} else if (mgt_param.user) {
+		VCLI_Out(cli, "%s (%d)", mgt_param.user, (int)mgt_param.uid);
+	} else {
+		VCLI_Out(cli, "UID %d", (int)mgt_param.uid);
+	}
+}
+
+/*--------------------------------------------------------------------
+ * XXX: see comment for tweak_user, same thing here.
+ */
+
+void
+tweak_group(struct cli *cli, const struct parspec *par, const char *arg)
+{
+	struct group *gr;
+
+	(void)par;
+	if (arg != NULL) {
+		if (*arg != '\0') {
+			gr = getgrnam(arg);
+			if (gr == NULL) {
+				VCLI_Out(cli, "Unknown group");
+				VCLI_SetResult(cli, CLIS_PARAM);
+				return;
+			}
+			REPLACE(mgt_param.group, gr->gr_name);
+			mgt_param.gid = gr->gr_gid;
+		} else {
+			mgt_param.gid = getgid();
+		}
+	} else if (mgt_param.group) {
+		VCLI_Out(cli, "%s (%d)", mgt_param.group, (int)mgt_param.gid);
+	} else {
+		VCLI_Out(cli, "GID %d", (int)mgt_param.gid);
+	}
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+clean_listen_sock_head(struct listen_sock_head *lsh)
+{
+	struct listen_sock *ls, *ls2;
+
+	VTAILQ_FOREACH_SAFE(ls, lsh, list, ls2) {
+		CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC);
+		VTAILQ_REMOVE(lsh, ls, list);
+		free(ls->name);
+		free(ls->addr);
+		FREE_OBJ(ls);
+	}
+}
+
+void
+tweak_listen_address(struct cli *cli, const struct parspec *par,
+    const char *arg)
+{
+	char **av;
+	int i;
+	struct listen_sock		*ls;
+	struct listen_sock_head		lsh;
+
+	(void)par;
+	if (arg == NULL) {
+		VCLI_Quote(cli, mgt_param.listen_address);
+		return;
+	}
+
+	av = VAV_Parse(arg, NULL, ARGV_COMMA);
+	if (av == NULL) {
+		VCLI_Out(cli, "Parse error: out of memory");
+		VCLI_SetResult(cli, CLIS_PARAM);
+		return;
+	}
+	if (av[0] != NULL) {
+		VCLI_Out(cli, "Parse error: %s", av[0]);
+		VCLI_SetResult(cli, CLIS_PARAM);
+		VAV_Free(av);
+		return;
+	}
+	if (av[1] == NULL) {
+		VCLI_Out(cli, "Empty listen address");
+		VCLI_SetResult(cli, CLIS_PARAM);
+		VAV_Free(av);
+		return;
+	}
+	VTAILQ_INIT(&lsh);
+	for (i = 1; av[i] != NULL; i++) {
+		struct vss_addr **ta;
+		int j, n;
+
+		n = VSS_resolve(av[i], "http", &ta);
+		if (n == 0) {
+			VCLI_Out(cli, "Invalid listen address ");
+			VCLI_Quote(cli, av[i]);
+			VCLI_SetResult(cli, CLIS_PARAM);
+			break;
+		}
+		for (j = 0; j < n; ++j) {
+			ALLOC_OBJ(ls, LISTEN_SOCK_MAGIC);
+			AN(ls);
+			ls->sock = -1;
+			ls->addr = ta[j];
+			ls->name = strdup(av[i]);
+			AN(ls->name);
+			VTAILQ_INSERT_TAIL(&lsh, ls, list);
+		}
+		free(ta);
+	}
+	VAV_Free(av);
+	if (cli != NULL && cli->result != CLIS_OK) {
+		clean_listen_sock_head(&lsh);
+		return;
+	}
+
+	REPLACE(mgt_param.listen_address, arg);
+
+	clean_listen_sock_head(&heritage.socks);
+	heritage.nsocks = 0;
+
+	while (!VTAILQ_EMPTY(&lsh)) {
+		ls = VTAILQ_FIRST(&lsh);
+		VTAILQ_REMOVE(&lsh, ls, list);
+		CHECK_OBJ_NOTNULL(ls, LISTEN_SOCK_MAGIC);
+		VTAILQ_INSERT_TAIL(&heritage.socks, ls, list);
+		heritage.nsocks++;
+	}
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+tweak_string(struct cli *cli, const struct parspec *par, const char *arg)
+{
+	char **p = TRUST_ME(par->priv);
+
+	AN(p);
+	/* XXX should have tweak_generic_string */
+	if (arg == NULL) {
+		VCLI_Quote(cli, *p);
+	} else {
+		REPLACE(*p, arg);
+	}
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+tweak_waiter(struct cli *cli, const struct parspec *par, const char *arg)
+{
+
+	/* XXX should have tweak_generic_string */
+	(void)par;
+	WAIT_tweak_waiter(cli, arg);
+}
+
+/*--------------------------------------------------------------------*/
+
+void
+tweak_poolparam(struct cli *cli, const struct parspec *par, const char *arg)
+{
+	volatile struct poolparam *pp, px;
+	char **av;
+
+	pp = par->priv;
+	if (arg == NULL) {
+		VCLI_Out(cli, "%u,%u,%g",
+		    pp->min_pool, pp->max_pool, pp->max_age);
+	} else {
+		av = VAV_Parse(arg, NULL, ARGV_COMMA);
+		do {
+			if (av[0] != NULL) {
+				VCLI_Out(cli, "Parse error: %s", av[0]);
+				VCLI_SetResult(cli, CLIS_PARAM);
+				break;
+			}
+			if (av[1] == NULL || av[2] == NULL || av[3] == NULL) {
+				VCLI_Out(cli,
+				    "Three fields required:"
+				    " min_pool, max_pool and max_age\n");
+				VCLI_SetResult(cli, CLIS_PARAM);
+				break;
+			}
+			px = *pp;
+			if (tweak_generic_uint(cli, &px.min_pool, av[1],
+			    (uint)par->min, (uint)par->max))
+				break;
+			if (tweak_generic_uint(cli, &px.max_pool, av[2],
+			    (uint)par->min, (uint)par->max))
+				break;
+			if (tweak_generic_timeout_double(cli, &px.max_age,
+			    av[3], 0, 1e6))
+				break;
+			if (px.min_pool > px.max_pool) {
+				VCLI_Out(cli,
+				    "min_pool cannot be larger"
+				    " than max_pool\n");
+				VCLI_SetResult(cli, CLIS_PARAM);
+				break;
+			}
+			*pp = px;
+		} while(0);
+		VAV_Free(av);
+	}
+}
diff --git a/bin/varnishd/mgt/mgt_pool.c b/bin/varnishd/mgt/mgt_pool.c
index 385a500..1bfa994 100644
--- a/bin/varnishd/mgt/mgt_pool.c
+++ b/bin/varnishd/mgt/mgt_pool.c
@@ -161,7 +161,7 @@ const struct parspec WRK_parspec[] = {
 		"Set this to a few milliseconds if you see the "
 		"'threads_failed' counter grow too much.\n"
 		"\n"
-		"Setting this too high results in insuffient worker threads.\n",
+		"Setting this too high results in insuffient worker threads.",
 		EXPERIMENTAL,
 		"0", "seconds" },
 	{ "thread_pool_fail_delay",
@@ -180,7 +180,7 @@ const struct parspec WRK_parspec[] = {
 		"\n"
 		"It may also help to increase thread_pool_timeout and "
 		"thread_pool_min, to reduce the rate at which treads are "
-		"destroyed and later recreated.\n",
+		"destroyed and later recreated.",
 		EXPERIMENTAL,
 		"0.2", "seconds" },
 	{ "thread_stats_rate",
@@ -190,7 +190,7 @@ const struct parspec WRK_parspec[] = {
 		"finish a request.\n"
 		"This parameters defines the maximum number of requests "
 		"a worker thread may handle, before it is forced to dump "
-		"its accumulated stats into the global counters.\n",
+		"its accumulated stats into the global counters.",
 		EXPERIMENTAL,
 		"10", "requests" },
 	{ "thread_queue_limit", tweak_uint, &mgt_param.wthread_queue_limit,
@@ -199,7 +199,7 @@ const struct parspec WRK_parspec[] = {
 		"\n"
 		"This sets the number of requests we will queue, waiting "
 		"for an available thread.  Above this limit sessions will "
-		"be dropped instead of queued.\n",
+		"be dropped instead of queued.",
 		EXPERIMENTAL,
 		"20", "" },
 	{ "rush_exponent", tweak_uint, &mgt_param.rush_exponent, 2, UINT_MAX,
@@ -214,7 +214,7 @@ const struct parspec WRK_parspec[] = {
 		tweak_stack_size, &mgt_param.wthread_stacksize, 0, UINT_MAX,
 		"Worker thread stack size.\n"
 		"This is likely rounded up to a multiple of 4k by the kernel.\n"
-		"The kernel/OS has a lower limit which will be enforced.\n",
+		"The kernel/OS has a lower limit which will be enforced.",
 		EXPERIMENTAL,
 		"48k", "bytes" },
 	{ NULL, NULL, NULL }
diff --git a/include/tbl/debug_bits.h b/include/tbl/debug_bits.h
index 7c6a655..59bc64e 100644
--- a/include/tbl/debug_bits.h
+++ b/include/tbl/debug_bits.h
@@ -29,12 +29,12 @@
  *
  */
 
-DEBUG_BIT(REQ_STATE,		req_state,	"",  "VSL Request state engine")
-DEBUG_BIT(WORKSPACE,		workspace,	"",  "VSL Workspace operations")
-DEBUG_BIT(WAITER,		waiter,		"\t","VSL Waiter internals")
-DEBUG_BIT(WAITINGLIST,		waitinglist,	"",  "VSL Waitinglist events")
-DEBUG_BIT(SYNCVSL,		syncvsl,	"\t","Make VSL synchronous")
-DEBUG_BIT(HASHEDGE,		hashedge,	"",  "Edge cases in Hash")
-DEBUG_BIT(VCLREL,		vclrel,		"\t","Rapid VCL release")
-DEBUG_BIT(LURKER,		lurker,		"\t","VSL Ban lurker")
-DEBUG_BIT(ESI_CHOP,		esi_chop,	"",  "Chop ESI fetch to bits")
+DEBUG_BIT(REQ_STATE,		req_state,	"VSL Request state engine")
+DEBUG_BIT(WORKSPACE,		workspace,	"VSL Workspace operations")
+DEBUG_BIT(WAITER,		waiter,		"VSL Waiter internals")
+DEBUG_BIT(WAITINGLIST,		waitinglist,	"VSL Waitinglist events")
+DEBUG_BIT(SYNCVSL,		syncvsl,	"Make VSL synchronous")
+DEBUG_BIT(HASHEDGE,		hashedge,	"Edge cases in Hash")
+DEBUG_BIT(VCLREL,		vclrel,		"Rapid VCL release")
+DEBUG_BIT(LURKER,		lurker,		"VSL Ban lurker")
+DEBUG_BIT(ESI_CHOP,		esi_chop,	"Chop ESI fetch to bits")
diff --git a/include/tbl/feature_bits.h b/include/tbl/feature_bits.h
index 2f30e58..388f9da 100644
--- a/include/tbl/feature_bits.h
+++ b/include/tbl/feature_bits.h
@@ -29,31 +29,31 @@
  *
  */
 
-FEATURE_BIT(SHORT_PANIC,		short_panic, "",
+FEATURE_BIT(SHORT_PANIC,		short_panic,
     "Short panic message.",
     "Reduce level of detail for panic messages."
 )
-FEATURE_BIT(WAIT_SILO,			wait_silo, "",
+FEATURE_BIT(WAIT_SILO,			wait_silo,
     "Wait for persistent silo.",
     "Wait for persistent silos to load completely before serving requests."
 )
-FEATURE_BIT(NO_COREDUMP,		no_coredump, "",
+FEATURE_BIT(NO_COREDUMP,		no_coredump,
     "No coredumps.",
     "Don't attempt to coredump child process on panics."
 )
-FEATURE_BIT(ESI_IGNORE_HTTPS,		esi_ignore_https, "",
+FEATURE_BIT(ESI_IGNORE_HTTPS,		esi_ignore_https,
     "Treat HTTPS as HTTP in ESI:includes",
     "Convert <esi:include src\"https://... to http://..."
 )
-FEATURE_BIT(ESI_DISABLE_XML_CHECK,	esi_disable_xml_check, "",
+FEATURE_BIT(ESI_DISABLE_XML_CHECK,	esi_disable_xml_check,
     "Don't check of body looks like XML",
     "Allow ESI processing on any kind of object"
 )
-FEATURE_BIT(ESI_IGNORE_OTHER_ELEMENTS,	esi_ignore_other_elements, "",
+FEATURE_BIT(ESI_IGNORE_OTHER_ELEMENTS,	esi_ignore_other_elements,
     "Ignore non-esi XML-elements",
     "Allows syntax errors in the XML"
 )
-FEATURE_BIT(ESI_REMOVE_BOM,		esi_remove_bom, "",
+FEATURE_BIT(ESI_REMOVE_BOM,		esi_remove_bom,
     "Remove UTF-8 BOM",
     "Remove UTF-8 BOM from front of object."
     "Ignore and remove the UTF-8 BOM (0xeb 0xbb 0xbf) from front of object."



More information about the varnish-commit mailing list