varnish-cache/bin/varnishd/mgt/mgt_param.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2011 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29
30
#include "config.h"
31
32
#include <ctype.h>
33
#include <stdarg.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <unistd.h>
38
39
#include "mgt/mgt.h"
40
#include "common/heritage.h"
41
42
#include "mgt/mgt_param.h"
43
#include "vav.h"
44
#include "vcli_serve.h"
45
46
struct plist {
47
        unsigned                        magic;
48
#define PLIST_MAGIC                     0xbfc3ea16
49
        VTAILQ_ENTRY(plist)             list;
50
        struct parspec                  *spec;
51
};
52
53
static VTAILQ_HEAD(, plist)             phead = VTAILQ_HEAD_INITIALIZER(phead);
54
55
struct params mgt_param;
56
static const int margin1 = 8;
57
static int margin2 = 0;
58
static const int wrap_at = 72;
59
static const int tab0 = 3;
60
61
/*--------------------------------------------------------------------*/
62
63
static const char OBJ_STICKY_TEXT[] =
64
        "\n\n"
65
        "NB: This parameter is evaluated only when objects are created. "
66
        "To change it for all objects, restart or ban everything.";
67
68
static const char DELAYED_EFFECT_TEXT[] =
69
        "\n\n"
70
        "NB: This parameter may take quite some time to take (full) effect.";
71
72
static const char MUST_RESTART_TEXT[] =
73
        "\n\n"
74
        "NB: This parameter will not take any effect until the "
75
        "child process has been restarted.";
76
77
static const char MUST_RELOAD_TEXT[] =
78
        "\n\n"
79
        "NB: This parameter will not take any effect until the "
80
        "VCL programs have been reloaded.";
81
82
static const char EXPERIMENTAL_TEXT[] =
83
        "\n\n"
84
        "NB: We do not know yet if it is a good idea to change "
85
        "this parameter, or if the default value is even sensible. "
86
        "Caution is advised, and feedback is most welcome.";
87
88
static const char WIZARD_TEXT[] =
89
        "\n\n"
90
        "NB: Do not change this parameter, unless a developer tell "
91
        "you to do so.";
92
93
static const char PROTECTED_TEXT[] =
94
        "\n\n"
95
        "NB: This parameter is protected and can not be changed.";
96
97
static const char ONLY_ROOT_TEXT[] =
98
        "\n\n"
99
        "NB: This parameter only works if varnishd is run as root.";
100
101
static const char NOT_IMPLEMENTED_TEXT[] =
102
        "This parameter depends on a feature which is not available"
103
        " on this platform.";
104
105
static const char NOT_IMPLEMENTED_DOC[] =
106
        "NB: This parameter depends on a feature which is not available"
107
        " on all platforms.";
108
109
/*--------------------------------------------------------------------*/
110
111
static struct parspec *
112 59814
mcf_findpar(const char *name)
113
{
114
        struct plist *pl;
115
116 59814
        AN(name);
117 4368186
        VTAILQ_FOREACH(pl, &phead, list)
118 4368178
                if (!strcmp(pl->spec->name, name))
119 59806
                        return (pl->spec);
120 8
        return (NULL);
121
}
122
123
static void
124 136188
mcf_addpar(struct parspec *ps)
125
{
126
        struct plist *pl, *pl2;
127
        int i;
128
129 136188
        ALLOC_OBJ(pl, PLIST_MAGIC);
130 136188
        AN(pl);
131 136188
        pl->spec = ps;
132 5548608
        VTAILQ_FOREACH(pl2, &phead, list) {
133 5481216
                i = strcmp(pl2->spec->name, pl->spec->name);
134 5481216
                if (i == 0) {
135 0
                        fprintf(stderr, "Duplicate param: %s\n", ps->name);
136 0
                        exit(4);
137 5481216
                } else if (i > 0) {
138 68796
                        VTAILQ_INSERT_BEFORE(pl2, pl, list);
139 204984
                        return;
140
                }
141
        }
142 67392
        VTAILQ_INSERT_TAIL(&phead, pl, list);
143
}
144
145
/*--------------------------------------------------------------------
146
 * Wrap the text nicely.
147
 * Lines are allowed to contain two TABS and we render that as a table
148
 * taking the width of the first column into account.
149
 */
150
151
static void
152 2346
mcf_wrap_line(struct cli *cli, const char *b, const char *e, int tabs, int m0)
153
{
154 2346
        int n, hadtabs = 0;
155
        const char *w;
156
157 2346
        n = m0;
158 2346
        VCLI_Out(cli, "%*s", n, "");
159
160 2346
        while (b < e) {
161 33526
                if (!isspace(*b)) {
162 16064
                        VCLI_Out(cli, "%c", *b);
163 16064
                        b++;
164 16064
                        n++;
165 17462
                } else if (*b == '\t') {
166 576
                        assert(tabs);
167 576
                        assert(hadtabs < 2);
168
                        do {
169 4440
                                VCLI_Out(cli, " ");
170 4440
                                n++;
171 4440
                        } while ((n % tabs) != (m0 + tab0) % tabs);
172 576
                        b++;
173 576
                        hadtabs++;
174
                } else {
175 16886
                        assert (*b == ' ');
176 99302
                        for (w = b + 1; w < e; w++)
177 98058
                                if (isspace(*w))
178 15642
                                        break;
179 16886
                        if (n + (w - b) < wrap_at) {
180 15784
                                VCLI_Out(cli, "%.*s", (int)(w - b), b);
181 15784
                                n += (w - b);
182 15784
                                b = w;
183
                        } else {
184 1102
                                assert(hadtabs == 0 || hadtabs == 2);
185 1102
                                VCLI_Out(cli, "\n");
186 1118
                                mcf_wrap_line(cli, b + 1, e, 0,
187 16
                                    hadtabs ? m0 + tab0 + tabs : m0);
188 1102
                                return;
189
                        }
190
                }
191
        }
192 1244
        assert(b == e);
193
}
194
195
static void
196 606
mcf_wrap(struct cli *cli, const char *text)
197
{
198
        const char *p, *q, *r;
199 606
        int tw = 0;
200
201 606
        if (strchr(text, '\t') != NULL) {
202 368
                for (p = text; *p != '\0'; ) {
203 328
                        q = strstr(p, "\n\t");
204 328
                        if (q == NULL)
205 40
                                break;
206 288
                        q += 2;
207 288
                        r = strchr(q, '\t');
208 288
                        if (r == NULL) {
209 0
                                fprintf(stderr,
210
                                    "LINE with just one TAB: <%s>\n", text);
211 0
                                exit(4);
212
                        }
213 288
                        if (r - q > tw)
214 96
                                tw = r - q;
215 288
                        p = q;
216
                }
217 40
                tw += 2;
218 40
                if (tw < 20)
219 32
                        tw = 20;
220
        }
221
222 3680
        for (p = text; *p != '\0'; ) {
223 2468
                if (*p == '\n') {
224 1224
                        VCLI_Out(cli, "\n");
225 1224
                        p++;
226 1224
                        continue;
227
                }
228 1244
                q = strchr(p, '\n');
229 1244
                if (q == NULL)
230 606
                        q = strchr(p, '\0');
231 1244
                mcf_wrap_line(cli, p, q, tw, margin1);
232 1244
                p = q;
233
        }
234 606
}
235
236
/*--------------------------------------------------------------------*/
237
238
static void v_matchproto_(cli_func_t)
239 40
mcf_param_show(struct cli *cli, const char * const *av, void *priv)
240
{
241
        int n;
242
        struct plist *pl;
243
        const struct parspec *pp;
244 40
        int lfmt = 0, chg = 0;
245
        struct vsb *vsb;
246
247 40
        vsb = VSB_new_auto();
248
        (void)priv;
249
250 40
        if (av[2] != NULL && !strcmp(av[2], "changed"))
251 2
                chg = 1;
252 38
        else if (av[2] != NULL)
253 28
                lfmt = 1;
254
255 40
        n = 0;
256 3920
        VTAILQ_FOREACH(pl, &phead, list) {
257 3880
                pp = pl->spec;
258 3880
                if (lfmt && strcmp(pp->name, av[2]) && strcmp("-l", av[2]))
259 2306
                        continue;
260 1574
                n++;
261
262 1574
                VSB_clear(vsb);
263 1574
                if (pp->func(vsb, pp, NULL))
264 0
                        VCLI_SetResult(cli, CLIS_PARAM);
265 1574
                AZ(VSB_finish(vsb));
266 1574
                if (chg && pp->def != NULL && !strcmp(pp->def, VSB_data(vsb)))
267 176
                        continue;
268
269 1398
                if (pp->flags & NOT_IMPLEMENTED) {
270 14
                        if (lfmt) {
271 4
                                VCLI_Out(cli, "%s\n", pp->name);
272 4
                                VCLI_Out(cli, "%-*sNot available", margin1, " ");
273
                        } else {
274 10
                                VCLI_Out(cli, "%-*s-", margin2, pp->name);
275
                        }
276
                } else {
277 1384
                        if (lfmt) {
278 406
                                VCLI_Out(cli, "%s\n", pp->name);
279 406
                                VCLI_Out(cli, "%-*sValue is: ", margin1, " ");
280
                        } else {
281 978
                                VCLI_Out(cli, "%-*s", margin2, pp->name);
282
                        }
283
284 1384
                        VCLI_Out(cli, "%s", VSB_data(vsb));
285 1384
                        if (pp->units != NULL && *pp->units != '\0')
286 1110
                                VCLI_Out(cli, " [%s]", pp->units);
287 1384
                        if (pp->def != NULL && !strcmp(pp->def, VSB_data(vsb)))
288 1240
                                VCLI_Out(cli, " (default)");
289
                }
290 1398
                VCLI_Out(cli, "\n");
291
292 1398
                if (lfmt && pp->flags & NOT_IMPLEMENTED) {
293 4
                        VCLI_Out(cli, "\n");
294 4
                        mcf_wrap(cli, NOT_IMPLEMENTED_TEXT);
295 4
                        VCLI_Out(cli, "\n\n");
296 1394
                } else if (lfmt) {
297 406
                        if (pp->def != NULL && strcmp(pp->def, VSB_data(vsb)))
298 48
                                VCLI_Out(cli, "%-*sDefault is: %s\n",
299
                                    margin1, "", pp->def);
300 406
                        if (pp->min != NULL)
301 294
                                VCLI_Out(cli, "%-*sMinimum is: %s\n",
302
                                    margin1, "", pp->min);
303 406
                        if (pp->max != NULL)
304 94
                                VCLI_Out(cli, "%-*sMaximum is: %s\n",
305
                                    margin1, "", pp->max);
306 406
                        VCLI_Out(cli, "\n");
307 406
                        mcf_wrap(cli, pp->descr);
308 406
                        if (pp->flags & OBJ_STICKY)
309 14
                                mcf_wrap(cli, OBJ_STICKY_TEXT);
310 406
                        if (pp->flags & DELAYED_EFFECT)
311 52
                                mcf_wrap(cli, DELAYED_EFFECT_TEXT);
312 406
                        if (pp->flags & EXPERIMENTAL)
313 88
                                mcf_wrap(cli, EXPERIMENTAL_TEXT);
314 406
                        if (pp->flags & MUST_RELOAD)
315 4
                                mcf_wrap(cli, MUST_RELOAD_TEXT);
316 406
                        if (pp->flags & MUST_RESTART)
317 20
                                mcf_wrap(cli, MUST_RESTART_TEXT);
318 406
                        if (pp->flags & WIZARD)
319 16
                                mcf_wrap(cli, WIZARD_TEXT);
320 406
                        if (pp->flags & PROTECTED)
321 2
                                mcf_wrap(cli, PROTECTED_TEXT);
322 406
                        if (pp->flags & ONLY_ROOT)
323 0
                                mcf_wrap(cli, ONLY_ROOT_TEXT);
324 406
                        VCLI_Out(cli, "\n\n");
325
                }
326
        }
327 40
        if (av[2] != NULL && lfmt && strcmp(av[2], "-l") && n == 0) {
328 2
                VCLI_SetResult(cli, CLIS_PARAM);
329 2
                VCLI_Out(cli, "Unknown parameter \"%s\".", av[2]);
330
        }
331 40
        VSB_destroy(&vsb);
332 40
}
333
334
/*--------------------------------------------------------------------
335
 * Mark parameters as protected
336
 */
337
338
void
339 2
MCF_ParamProtect(struct cli *cli, const char *args)
340
{
341
        char **av;
342
        struct parspec *pp;
343
        int i;
344
345 2
        av = VAV_Parse(args, NULL, ARGV_COMMA);
346 2
        if (av[0] != NULL) {
347 0
                VCLI_Out(cli, "Parse error: %s", av[0]);
348 0
                VCLI_SetResult(cli, CLIS_PARAM);
349 0
                VAV_Free(av);
350 0
                return;
351
        }
352 4
        for (i = 1; av[i] != NULL; i++) {
353 2
                pp = mcf_findpar(av[i]);
354 2
                if (pp == NULL) {
355 0
                        VCLI_Out(cli, "Unknown parameter %s", av[i]);
356 0
                        VCLI_SetResult(cli, CLIS_PARAM);
357 0
                        VAV_Free(av);
358 0
                        return;
359
                }
360 2
                pp->flags |= PROTECTED;
361
        }
362 2
        VAV_Free(av);
363
}
364
365
/*--------------------------------------------------------------------*/
366
367
void
368 9424
MCF_ParamSet(struct cli *cli, const char *param, const char *val)
369
{
370
        const struct parspec *pp;
371
372 9424
        pp = mcf_findpar(param);
373 9424
        if (pp == NULL) {
374 8
                VCLI_SetResult(cli, CLIS_PARAM);
375 8
                VCLI_Out(cli, "Unknown parameter \"%s\".", param);
376 8
                return;
377
        }
378 9416
        if (pp->flags & PROTECTED) {
379 2
                VCLI_SetResult(cli, CLIS_AUTH);
380 2
                VCLI_Out(cli, "parameter \"%s\" is protected.", param);
381 2
                return;
382
        }
383 9414
        if (pp->func(cli->sb, pp, val))
384 44
                VCLI_SetResult(cli, CLIS_PARAM);
385
386 9414
        if (cli->result == CLIS_OK && heritage.param != NULL)
387 356
                *heritage.param = mgt_param;
388
389 9414
        if (cli->result != CLIS_OK) {
390 44
                VCLI_Out(cli, "\n(attempting to set param '%s' to '%s')",
391
                    pp->name, val);
392 9370
        } else if (MCH_Running() && pp->flags & MUST_RESTART) {
393 0
                VCLI_Out(cli,
394
                    "\nChange will take effect when child is restarted");
395 9370
        } else if (pp->flags & MUST_RELOAD) {
396 0
                VCLI_Out(cli,
397
                    "\nChange will take effect when VCL script is reloaded");
398
        }
399
}
400
401
402
/*--------------------------------------------------------------------*/
403
404
static void v_matchproto_(cli_func_t)
405 518
mcf_param_set(struct cli *cli, const char * const *av, void *priv)
406
{
407
408
        (void)priv;
409 518
        MCF_ParamSet(cli, av[2], av[3]);
410 518
}
411
412
/*--------------------------------------------------------------------
413
 * Add a group of parameters to the global set and sort by name.
414
 */
415
416
void
417 4212
MCF_AddParams(struct parspec *ps)
418
{
419
        struct parspec *pp;
420
        const char *s;
421
422 140400
        for (pp = ps; pp->name != NULL; pp++) {
423 136188
                AN(pp->func);
424 136188
                s = strchr(pp->descr, '\0');
425 136188
                if (isspace(s[-1])) {
426 0
                        fprintf(stderr,
427
                            "Param->descr has trailing space: %s\n", pp->name);
428 0
                        exit(4);
429
                }
430 136188
                mcf_addpar(pp);
431 136188
                if (strlen(pp->name) + 1 > margin2)
432 7020
                        margin2 = strlen(pp->name) + 1;
433
        }
434 4212
}
435
436
/*--------------------------------------------------------------------
437
 * Wash a min/max/default value
438
 */
439
440
static void
441 265356
mcf_wash_param(struct cli *cli, const struct parspec *pp, const char **val,
442
    const char *name, struct vsb *vsb)
443
{
444
        int err;
445
446 265356
        AN(*val);
447 265356
        VSB_clear(vsb);
448 265356
        VSB_printf(vsb, "FAILED to set %s for param %s: %s\n",
449
            name, pp->name, *val);
450 265356
        err = pp->func(vsb, pp, *val);
451 265356
        AZ(VSB_finish(vsb));
452 265356
        if (err) {
453 0
                VCLI_Out(cli, "%s\n", VSB_data(vsb));
454 0
                VCLI_SetResult(cli, CLIS_CANT);
455 0
                return;
456
        }
457 265356
        VSB_clear(vsb);
458 265356
        err = pp->func(vsb, pp, NULL);
459 265356
        AZ(err);
460 265356
        AZ(VSB_finish(vsb));
461 265356
        if (strcmp(*val, VSB_data(vsb))) {
462 51948
                *val = strdup(VSB_data(vsb));
463 51948
                AN(*val);
464
        }
465
}
466
467
/*--------------------------------------------------------------------*/
468
469
static struct cli_proto cli_params[] = {
470
        { CLICMD_PARAM_SHOW,            "", mcf_param_show },
471
        { CLICMD_PARAM_SET,             "", mcf_param_set },
472
        { NULL }
473
};
474
475
/*--------------------------------------------------------------------
476
 * Configure the parameters
477
 */
478
479
void
480 1404
MCF_InitParams(struct cli *cli)
481
{
482
        struct plist *pl;
483
        struct parspec *pp;
484
        struct vsb *vsb;
485
        ssize_t def, low;
486
487 1404
        MCF_AddParams(mgt_parspec);
488 1404
        MCF_AddParams(WRK_parspec);
489 1404
        MCF_AddParams(VSL_parspec);
490
491 1404
        MCF_TcpParams();
492
493
        if (sizeof(void *) < 8) {               /*lint !e506 !e774  */
494
                /*
495
                 * Adjust default parameters for 32 bit systems to conserve
496
                 * VM space.
497
                 */
498
                MCF_ParamConf(MCF_DEFAULT, "workspace_client", "24k");
499
                MCF_ParamConf(MCF_DEFAULT, "workspace_backend", "16k");
500
                MCF_ParamConf(MCF_DEFAULT, "http_resp_size", "8k");
501
                MCF_ParamConf(MCF_DEFAULT, "http_req_size", "12k");
502
                MCF_ParamConf(MCF_DEFAULT, "gzip_buffer", "4k");
503
                MCF_ParamConf(MCF_MAXIMUM, "vsl_space", "1G");
504
        }
505
506
#if !defined(HAVE_ACCEPT_FILTERS) || defined(__linux)
507
        MCF_ParamConf(MCF_DEFAULT, "accept_filter", "off");
508
#endif
509
510 1404
        low = sysconf(_SC_THREAD_STACK_MIN);
511 1404
        MCF_ParamConf(MCF_MINIMUM, "thread_pool_stack", "%jdb", (intmax_t)low);
512
513
#if defined(__SANITIZER)
514
        def = 92 * 1024;
515
#else
516 1404
        def = 48 * 1024;
517
#endif
518 1404
        if (def < low)
519 0
                def = low;
520 1404
        MCF_ParamConf(MCF_DEFAULT, "thread_pool_stack", "%jdb", (intmax_t)def);
521
522
#if !defined(MAX_THREAD_POOLS)
523
#  define MAX_THREAD_POOLS 32
524
#endif
525
526 1404
        MCF_ParamConf(MCF_MAXIMUM, "thread_pools", "%d", MAX_THREAD_POOLS);
527
528 1404
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_params);
529
530 1404
        vsb = VSB_new_auto();
531 1404
        AN(vsb);
532
533 137592
        VTAILQ_FOREACH(pl, &phead, list) {
534 136188
                pp = pl->spec;
535
536 136188
                if (pp->flags & NOT_IMPLEMENTED)
537 1404
                        continue;
538 134784
                if (pp->min != NULL)
539 99684
                        mcf_wash_param(cli, pp, &pp->min, "minimum", vsb);
540 134784
                if (pp->max != NULL)
541 30888
                        mcf_wash_param(cli, pp, &pp->max, "maximum", vsb);
542 134784
                AN(pp->def);
543 134784
                mcf_wash_param(cli, pp, &pp->def, "default", vsb);
544
        }
545 1404
        VSB_destroy(&vsb);
546 1404
}
547
548
/*--------------------------------------------------------------------*/
549
550
void
551 50388
MCF_ParamConf(enum mcf_which_e which, const char *param, const char *fmt, ...)
552
{
553
        struct parspec *pp;
554
        struct vsb *vsb;
555
        va_list ap;
556
557 50388
        pp = mcf_findpar(param);
558 50388
        AN(pp);
559 50388
        vsb = VSB_new_auto();
560 50388
        AN(vsb);
561 50388
        va_start(ap, fmt);
562 50388
        VSB_vprintf(vsb, fmt, ap);
563 50388
        va_end(ap);
564 50388
        AZ(VSB_finish(vsb));
565 50388
        switch (which) {
566
        case MCF_DEFAULT:
567 5616
                pp->def = strdup(VSB_data(vsb));
568 5616
                AN(pp->def);
569 5616
                break;
570
        case MCF_MINIMUM:
571 22372
                pp->min = strdup(VSB_data(vsb));
572 22372
                AN(pp->min);
573 22372
                break;
574
        case MCF_MAXIMUM:
575 22400
                pp->max = strdup(VSB_data(vsb));
576 22400
                AN(pp->max);
577 22400
                break;
578
        }
579 50388
        VSB_delete(vsb);
580 50388
}
581
582
/*--------------------------------------------------------------------*/
583
584
void
585 6
MCF_DumpRstParam(void)
586
{
587
        struct plist *pl;
588
        const struct parspec *pp;
589
        const char *p, *q, *t1, *t2;
590
        int j;
591
592 6
        printf("\n.. The following is the autogenerated "
593
            "output from varnishd -x parameter\n\n");
594 588
        VTAILQ_FOREACH(pl, &phead, list) {
595 582
                pp = pl->spec;
596 582
                printf(".. _ref_param_%s:\n\n", pp->name);
597 582
                printf("%s\n", pp->name);
598 8736
                for (j = 0; j < strlen(pp->name); j++)
599 8154
                        printf("~");
600 582
                printf("\n");
601
602 582
                if (pp->flags && pp->flags & NOT_IMPLEMENTED)
603 6
                        printf("\n%s\n\n", NOT_IMPLEMENTED_DOC);
604
605 582
                if (pp->units != NULL && *pp->units != '\0')
606 474
                        printf("\t* Units: %s\n", pp->units);
607 582
                printf("\t* Default: %s\n", pp->def);
608 582
                if (pp->min != NULL)
609 432
                        printf("\t* Minimum: %s\n", pp->min);
610 582
                if (pp->max != NULL)
611 138
                        printf("\t* Maximum: %s\n", pp->max);
612
                /*
613
                 * XXX: we should mark the params with one/two flags
614
                 * XXX: that say if ->min/->max are valid, so we
615
                 * XXX: can emit those also in help texts.
616
                 */
617 582
                if (pp->flags) {
618 276
                        printf("\t* Flags: ");
619 276
                        q = "";
620
621 276
                        if (pp->flags & DELAYED_EFFECT) {
622 78
                                printf("%sdelayed", q);
623 78
                                q = ", ";
624
                        }
625 276
                        if (pp->flags & MUST_RESTART) {
626 30
                                printf("%smust_restart", q);
627 30
                                q = ", ";
628
                        }
629 276
                        if (pp->flags & MUST_RELOAD) {
630 6
                                printf("%smust_reload", q);
631 6
                                q = ", ";
632
                        }
633 276
                        if (pp->flags & EXPERIMENTAL) {
634 132
                                printf("%sexperimental", q);
635 132
                                q = ", ";
636
                        }
637 276
                        if (pp->flags & WIZARD) {
638 24
                                printf("%swizard", q);
639 24
                                q = ", ";
640
                        }
641 276
                        if (pp->flags & ONLY_ROOT) {
642 0
                                printf("%sonly_root", q);
643 0
                                q = ", ";
644
                        }
645 276
                        if (pp->flags & OBJ_STICKY) {
646 18
                                printf("%sobj_sticky", q);
647 18
                                q = ", ";
648
                        }
649 276
                        printf("\n");
650
                }
651 582
                printf("\n");
652 582
                p = pp->descr;
653 2748
                while (*p != '\0') {
654 1584
                        q = strchr(p, '\n');
655 1584
                        if (q == NULL)
656 582
                                q = strchr(p, '\0');
657 1584
                        t1 = strchr(p, '\t');
658 1584
                        if (t1 != NULL && t1 < q) {
659 240
                                t2 = strchr(t1 + 1, '\t');
660 240
                                AN(t2);
661 240
                                printf("\n\t*");
662 240
                                (void)fwrite(t1 + 1, (t2 - 1) - t1, 1, stdout);
663 240
                                printf("*\n\t\t");
664 240
                                p = t2 + 1;
665
                        }
666 1584
                        (void)fwrite(p, q - p, 1, stdout);
667 1584
                        p = q;
668 1584
                        if (*p == '\n') {
669 1002
                                printf("\n");
670 1002
                                p++;
671
                        }
672 1584
                        continue;
673
                }
674 582
                printf("\n\n");
675
        }
676 6
}