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
#if !defined(__has_feature)
47
#define __has_feature(x)        0
48
#endif
49
50
struct plist {
51
        unsigned                        magic;
52
#define PLIST_MAGIC                     0xbfc3ea16
53
        VTAILQ_ENTRY(plist)             list;
54
        struct parspec                  *spec;
55
};
56
57
static VTAILQ_HEAD(, plist)             phead = VTAILQ_HEAD_INITIALIZER(phead);
58
59
struct params mgt_param;
60
static const int margin1 = 8;
61
static int margin2 = 0;
62
static const int wrap_at = 72;
63
static const int tab0 = 3;
64
65
/*--------------------------------------------------------------------*/
66
67
static const char OBJ_STICKY_TEXT[] =
68
        "\n\n"
69
        "NB: This parameter is evaluated only when objects are created. "
70
        "To change it for all objects, restart or ban everything.";
71
72
static const char DELAYED_EFFECT_TEXT[] =
73
        "\n\n"
74
        "NB: This parameter may take quite some time to take (full) effect.";
75
76
static const char MUST_RESTART_TEXT[] =
77
        "\n\n"
78
        "NB: This parameter will not take any effect until the "
79
        "child process has been restarted.";
80
81
static const char MUST_RELOAD_TEXT[] =
82
        "\n\n"
83
        "NB: This parameter will not take any effect until the "
84
        "VCL programs have been reloaded.";
85
86
static const char EXPERIMENTAL_TEXT[] =
87
        "\n\n"
88
        "NB: We do not know yet if it is a good idea to change "
89
        "this parameter, or if the default value is even sensible. "
90
        "Caution is advised, and feedback is most welcome.";
91
92
static const char WIZARD_TEXT[] =
93
        "\n\n"
94
        "NB: Do not change this parameter, unless a developer tell "
95
        "you to do so.";
96
97
static const char PROTECTED_TEXT[] =
98
        "\n\n"
99
        "NB: This parameter is protected and can not be changed.";
100
101
static const char ONLY_ROOT_TEXT[] =
102
        "\n\n"
103
        "NB: This parameter only works if varnishd is run as root.";
104
105
static const char NOT_IMPLEMENTED_TEXT[] =
106
        "This parameter depends on a feature which is not available"
107
        " on this platform.";
108
109
static const char NOT_IMPLEMENTED_DOC[] =
110
        "NB: This parameter depends on a feature which is not available"
111
        " on all platforms.";
112
113
/*--------------------------------------------------------------------*/
114
115
static struct parspec *
116 34494
mcf_findpar(const char *name)
117
{
118
        struct plist *pl;
119
120 34494
        AN(name);
121 2751573
        VTAILQ_FOREACH(pl, &phead, list)
122 2751569
                if (!strcmp(pl->spec->name, name))
123 34490
                        return (pl->spec);
124 4
        return (NULL);
125
}
126
127
static void
128 84448
mcf_addpar(struct parspec *ps)
129
{
130
        struct plist *pl, *pl2;
131
        int i;
132
133 84448
        ALLOC_OBJ(pl, PLIST_MAGIC);
134 84448
        AN(pl);
135 84448
        pl->spec = ps;
136 3594724
        VTAILQ_FOREACH(pl2, &phead, list) {
137 3555748
                i = strcmp(pl2->spec->name, pl->spec->name);
138 3555748
                if (i == 0) {
139 0
                        fprintf(stderr, "Duplicate param: %s\n", ps->name);
140 0
                        exit(4);
141 3555748
                } else if (i > 0) {
142 45472
                        VTAILQ_INSERT_BEFORE(pl2, pl, list);
143 45472
                        return;
144
                }
145
        }
146 38976
        VTAILQ_INSERT_TAIL(&phead, pl, list);
147
}
148
149
/*--------------------------------------------------------------------
150
 * Wrap the text nicely.
151
 * Lines are allowed to contain two TABS and we render that as a table
152
 * taking the width of the first column into account.
153
 */
154
155
static void
156 1249
mcf_wrap_line(struct cli *cli, const char *b, const char *e, int tabs, int m0)
157
{
158 1249
        int n, hadtabs = 0;
159
        const char *w;
160
161 1249
        n = m0;
162 1249
        VCLI_Out(cli, "%*s", n, "");
163
164 1249
        while (b < e) {
165 17885
                if (!isspace(*b)) {
166 8632
                        VCLI_Out(cli, "%c", *b);
167 8632
                        b++;
168 8632
                        n++;
169 9253
                } else if (*b == '\t') {
170 304
                        assert(tabs);
171 304
                        assert(hadtabs < 2);
172
                        do {
173 2324
                                VCLI_Out(cli, " ");
174 2324
                                n++;
175 2324
                        } while ((n % tabs) != (m0 + tab0) % tabs);
176 304
                        b++;
177 304
                        hadtabs++;
178
                } else {
179 8949
                        assert (*b == ' ');
180 52893
                        for (w = b + 1; w < e; w++)
181 52231
                                if (isspace(*w))
182 8287
                                        break;
183 8949
                        if (n + (w - b) < wrap_at) {
184 8362
                                VCLI_Out(cli, "%.*s", (int)(w - b), b);
185 8362
                                n += (w - b);
186 8362
                                b = w;
187
                        } else {
188 587
                                assert(hadtabs == 0 || hadtabs == 2);
189 587
                                VCLI_Out(cli, "\n");
190 599
                                mcf_wrap_line(cli, b + 1, e, 0,
191 12
                                    hadtabs ? m0 + tab0 + tabs : m0);
192 587
                                return;
193
                        }
194
                }
195
        }
196 662
        assert(b == e);
197
}
198
199
static void
200 321
mcf_wrap(struct cli *cli, const char *text)
201
{
202
        const char *p, *q, *r;
203 321
        int tw = 0;
204
205 321
        if (strchr(text, '\t') != NULL) {
206 192
                for (p = text; *p != '\0'; ) {
207 172
                        q = strstr(p, "\n\t");
208 172
                        if (q == NULL)
209 20
                                break;
210 152
                        q += 2;
211 152
                        r = strchr(q, '\t');
212 152
                        if (r == NULL) {
213 0
                                fprintf(stderr,
214
                                    "LINE with just one TAB: <%s>\n", text);
215 0
                                exit(4);
216
                        }
217 152
                        if (r - q > tw)
218 48
                                tw = r - q;
219 152
                        p = q;
220
                }
221 20
                tw += 2;
222 20
                if (tw < 20)
223 16
                        tw = 20;
224
        }
225
226 1948
        for (p = text; *p != '\0'; ) {
227 1306
                if (*p == '\n') {
228 644
                        VCLI_Out(cli, "\n");
229 644
                        p++;
230 644
                        continue;
231
                }
232 662
                q = strchr(p, '\n');
233 662
                if (q == NULL)
234 321
                        q = strchr(p, '\0');
235 662
                mcf_wrap_line(cli, p, q, tw, margin1);
236 662
                p = q;
237
        }
238 321
}
239
240
/*--------------------------------------------------------------------*/
241
242
static void v_matchproto_(cli_func_t)
243 20
mcf_param_show(struct cli *cli, const char * const *av, void *priv)
244
{
245
        int n;
246
        struct plist *pl;
247
        const struct parspec *pp;
248 20
        int lfmt = 0, chg = 0;
249
        struct vsb *vsb;
250
251 20
        vsb = VSB_new_auto();
252
        (void)priv;
253
254 20
        if (av[2] != NULL && !strcmp(av[2], "changed"))
255 1
                chg = 1;
256 19
        else if (av[2] != NULL)
257 14
                lfmt = 1;
258
259 20
        n = 0;
260 2100
        VTAILQ_FOREACH(pl, &phead, list) {
261 2080
                pp = pl->spec;
262 2080
                if (lfmt && strcmp(pp->name, av[2]) && strcmp("-l", av[2]))
263 1237
                        continue;
264 843
                n++;
265
266 843
                VSB_clear(vsb);
267 843
                if (pp->func(vsb, pp, NULL))
268 0
                        VCLI_SetResult(cli, CLIS_PARAM);
269 843
                AZ(VSB_finish(vsb));
270 843
                if (chg && pp->def != NULL && !strcmp(pp->def, VSB_data(vsb)))
271 95
                        continue;
272
273 748
                if (pp->flags & NOT_IMPLEMENTED) {
274 7
                        if (lfmt) {
275 2
                                VCLI_Out(cli, "%s\n", pp->name);
276 2
                                VCLI_Out(cli, "%-*sNot available", margin1, " ");
277
                        } else {
278 5
                                VCLI_Out(cli, "%-*s-", margin2, pp->name);
279
                        }
280
                } else {
281 741
                        if (lfmt) {
282 217
                                VCLI_Out(cli, "%s\n", pp->name);
283 217
                                VCLI_Out(cli, "%-*sValue is: ", margin1, " ");
284
                        } else {
285 524
                                VCLI_Out(cli, "%-*s", margin2, pp->name);
286
                        }
287
288 741
                        VCLI_Out(cli, "%s", VSB_data(vsb));
289 741
                        if (pp->units != NULL && *pp->units != '\0')
290 604
                                VCLI_Out(cli, " [%s]", pp->units);
291 741
                        if (pp->def != NULL && !strcmp(pp->def, VSB_data(vsb)))
292 669
                                VCLI_Out(cli, " (default)");
293
                }
294 748
                VCLI_Out(cli, "\n");
295
296 748
                if (lfmt && pp->flags & NOT_IMPLEMENTED) {
297 2
                        VCLI_Out(cli, "\n");
298 2
                        mcf_wrap(cli, NOT_IMPLEMENTED_TEXT);
299 2
                        VCLI_Out(cli, "\n\n");
300 746
                } else if (lfmt) {
301 217
                        if (pp->def != NULL && strcmp(pp->def, VSB_data(vsb)))
302 24
                                VCLI_Out(cli, "%-*sDefault is: %s\n",
303
                                    margin1, "", pp->def);
304 217
                        if (pp->min != NULL)
305 161
                                VCLI_Out(cli, "%-*sMinimum is: %s\n",
306
                                    margin1, "", pp->min);
307 217
                        if (pp->max != NULL)
308 51
                                VCLI_Out(cli, "%-*sMaximum is: %s\n",
309
                                    margin1, "", pp->max);
310 217
                        VCLI_Out(cli, "\n");
311 217
                        mcf_wrap(cli, pp->descr);
312 217
                        if (pp->flags & OBJ_STICKY)
313 7
                                mcf_wrap(cli, OBJ_STICKY_TEXT);
314 217
                        if (pp->flags & DELAYED_EFFECT)
315 26
                                mcf_wrap(cli, DELAYED_EFFECT_TEXT);
316 217
                        if (pp->flags & EXPERIMENTAL)
317 48
                                mcf_wrap(cli, EXPERIMENTAL_TEXT);
318 217
                        if (pp->flags & MUST_RELOAD)
319 2
                                mcf_wrap(cli, MUST_RELOAD_TEXT);
320 217
                        if (pp->flags & MUST_RESTART)
321 10
                                mcf_wrap(cli, MUST_RESTART_TEXT);
322 217
                        if (pp->flags & WIZARD)
323 8
                                mcf_wrap(cli, WIZARD_TEXT);
324 217
                        if (pp->flags & PROTECTED)
325 1
                                mcf_wrap(cli, PROTECTED_TEXT);
326 217
                        if (pp->flags & ONLY_ROOT)
327 0
                                mcf_wrap(cli, ONLY_ROOT_TEXT);
328 217
                        VCLI_Out(cli, "\n\n");
329
                }
330
        }
331 20
        if (av[2] != NULL && lfmt && strcmp(av[2], "-l") && n == 0) {
332 1
                VCLI_SetResult(cli, CLIS_PARAM);
333 1
                VCLI_Out(cli, "Unknown parameter \"%s\".", av[2]);
334
        }
335 20
        VSB_destroy(&vsb);
336 20
}
337
338
/*--------------------------------------------------------------------
339
 * Mark parameters as protected
340
 */
341
342
void
343 1
MCF_ParamProtect(struct cli *cli, const char *args)
344
{
345
        char **av;
346
        struct parspec *pp;
347
        int i;
348
349 1
        av = VAV_Parse(args, NULL, ARGV_COMMA);
350 1
        if (av[0] != NULL) {
351 0
                VCLI_Out(cli, "Parse error: %s", av[0]);
352 0
                VCLI_SetResult(cli, CLIS_PARAM);
353 0
                VAV_Free(av);
354 0
                return;
355
        }
356 2
        for (i = 1; av[i] != NULL; i++) {
357 1
                pp = mcf_findpar(av[i]);
358 1
                if (pp == NULL) {
359 0
                        VCLI_Out(cli, "Unknown parameter %s", av[i]);
360 0
                        VCLI_SetResult(cli, CLIS_PARAM);
361 0
                        VAV_Free(av);
362 0
                        return;
363
                }
364 1
                pp->flags |= PROTECTED;
365
        }
366 1
        VAV_Free(av);
367
}
368
369
/*--------------------------------------------------------------------*/
370
371
void
372 5383
MCF_ParamSet(struct cli *cli, const char *param, const char *val)
373
{
374
        const struct parspec *pp;
375
376 5383
        pp = mcf_findpar(param);
377 5383
        if (pp == NULL) {
378 4
                VCLI_SetResult(cli, CLIS_PARAM);
379 4
                VCLI_Out(cli, "Unknown parameter \"%s\".", param);
380 4
                return;
381
        }
382 5379
        if (pp->flags & PROTECTED) {
383 1
                VCLI_SetResult(cli, CLIS_AUTH);
384 1
                VCLI_Out(cli, "parameter \"%s\" is protected.", param);
385 1
                return;
386
        }
387 5378
        if (pp->func(cli->sb, pp, val))
388 22
                VCLI_SetResult(cli, CLIS_PARAM);
389
390 5378
        if (cli->result == CLIS_OK && heritage.param != NULL)
391 217
                *heritage.param = mgt_param;
392
393 5378
        if (cli->result != CLIS_OK) {
394 22
                VCLI_Out(cli, "\n(attempting to set param '%s' to '%s')",
395
                    pp->name, val);
396 5356
        } else if (MCH_Running() && pp->flags & MUST_RESTART) {
397 0
                VCLI_Out(cli,
398
                    "\nChange will take effect when child is restarted");
399 5356
        } else if (pp->flags & MUST_RELOAD) {
400 0
                VCLI_Out(cli,
401
                    "\nChange will take effect when VCL script is reloaded");
402
        }
403
}
404
405
406
/*--------------------------------------------------------------------*/
407
408
static void v_matchproto_(cli_func_t)
409 305
mcf_param_set(struct cli *cli, const char * const *av, void *priv)
410
{
411
412
        (void)priv;
413 305
        MCF_ParamSet(cli, av[2], av[3]);
414 305
}
415
416
/*--------------------------------------------------------------------
417
 * Add a group of parameters to the global set and sort by name.
418
 */
419
420
void
421 2436
MCF_AddParams(struct parspec *ps)
422
{
423
        struct parspec *pp;
424
        const char *s;
425
426 86884
        for (pp = ps; pp->name != NULL; pp++) {
427 84448
                AN(pp->func);
428 84448
                s = strchr(pp->descr, '\0');
429 84448
                if (isspace(s[-1])) {
430 0
                        fprintf(stderr,
431
                            "Param->descr has trailing space: %s\n", pp->name);
432 0
                        exit(4);
433
                }
434 84448
                mcf_addpar(pp);
435 84448
                if (strlen(pp->name) + 1 > margin2)
436 4060
                        margin2 = strlen(pp->name) + 1;
437
        }
438 2436
}
439
440
/*--------------------------------------------------------------------
441
 * Wash a min/max/default value
442
 */
443
444
static void
445 166460
mcf_wash_param(struct cli *cli, const struct parspec *pp, const char **val,
446
    const char *name, struct vsb *vsb)
447
{
448
        int err;
449
450 166460
        AN(*val);
451 166460
        VSB_clear(vsb);
452 166460
        VSB_printf(vsb, "FAILED to set %s for param %s: %s\n",
453
            name, pp->name, *val);
454 166460
        err = pp->func(vsb, pp, *val);
455 166460
        AZ(VSB_finish(vsb));
456 166460
        if (err) {
457 0
                VCLI_Out(cli, "%s\n", VSB_data(vsb));
458 0
                VCLI_SetResult(cli, CLIS_CANT);
459 0
                return;
460
        }
461 166460
        VSB_clear(vsb);
462 166460
        err = pp->func(vsb, pp, NULL);
463 166460
        AZ(err);
464 166460
        AZ(VSB_finish(vsb));
465 166460
        if (strcmp(*val, VSB_data(vsb))) {
466 30856
                *val = strdup(VSB_data(vsb));
467 30856
                AN(*val);
468
        }
469
}
470
471
/*--------------------------------------------------------------------*/
472
473
static struct cli_proto cli_params[] = {
474
        { CLICMD_PARAM_SHOW,            "", mcf_param_show },
475
        { CLICMD_PARAM_SET,             "", mcf_param_set },
476
        { NULL }
477
};
478
479
/*--------------------------------------------------------------------
480
 * Configure the parameters
481
 */
482
483
void
484 812
MCF_InitParams(struct cli *cli)
485
{
486
        struct plist *pl;
487
        struct parspec *pp;
488
        struct vsb *vsb;
489
        ssize_t def, low;
490
491 812
        MCF_AddParams(mgt_parspec);
492 812
        MCF_AddParams(WRK_parspec);
493 812
        MCF_AddParams(VSL_parspec);
494
495 812
        MCF_TcpParams();
496
497
        if (sizeof(void *) < 8) {               /*lint !e506 !e774  */
498
                /*
499
                 * Adjust default parameters for 32 bit systems to conserve
500
                 * VM space.
501
                 */
502
                MCF_ParamConf(MCF_DEFAULT, "workspace_client", "24k");
503
                MCF_ParamConf(MCF_DEFAULT, "workspace_backend", "16k");
504
                MCF_ParamConf(MCF_DEFAULT, "http_resp_size", "8k");
505
                MCF_ParamConf(MCF_DEFAULT, "http_req_size", "12k");
506
                MCF_ParamConf(MCF_DEFAULT, "gzip_buffer", "4k");
507
                MCF_ParamConf(MCF_MAXIMUM, "vsl_space", "1G");
508
        }
509
510
#if !defined(HAVE_ACCEPT_FILTERS) || defined(__linux)
511
        MCF_ParamConf(MCF_DEFAULT, "accept_filter", "off");
512
#endif
513
514 812
        low = sysconf(_SC_THREAD_STACK_MIN);
515 812
        MCF_ParamConf(MCF_MINIMUM, "thread_pool_stack", "%jdb", (intmax_t)low);
516
517
#if defined(__SANITIZER) || __has_feature(address_sanitizer)
518
        def = 92 * 1024;
519
#else
520 812
        def = 48 * 1024;
521
#endif
522 812
        if (def < low)
523 0
                def = low;
524 812
        MCF_ParamConf(MCF_DEFAULT, "thread_pool_stack", "%jdb", (intmax_t)def);
525
526
#if !defined(MAX_THREAD_POOLS)
527
#  define MAX_THREAD_POOLS 32
528
#endif
529
530 812
        MCF_ParamConf(MCF_MAXIMUM, "thread_pools", "%d", MAX_THREAD_POOLS);
531
532 812
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_params);
533
534 812
        vsb = VSB_new_auto();
535 812
        AN(vsb);
536
537 85260
        VTAILQ_FOREACH(pl, &phead, list) {
538 84448
                pp = pl->spec;
539
540 84448
                if (pp->flags & NOT_IMPLEMENTED)
541 812
                        continue;
542 83636
                if (pp->min != NULL)
543 63336
                        mcf_wash_param(cli, pp, &pp->min, "minimum", vsb);
544 83636
                if (pp->max != NULL)
545 19488
                        mcf_wash_param(cli, pp, &pp->max, "maximum", vsb);
546 83636
                AN(pp->def);
547 83636
                mcf_wash_param(cli, pp, &pp->def, "default", vsb);
548
        }
549 812
        VSB_destroy(&vsb);
550 812
}
551
552
/*--------------------------------------------------------------------*/
553
554
void
555 29110
MCF_ParamConf(enum mcf_which_e which, const char *param, const char *fmt, ...)
556
{
557
        struct parspec *pp;
558
        struct vsb *vsb;
559
        va_list ap;
560
561 29110
        pp = mcf_findpar(param);
562 29110
        AN(pp);
563 29110
        vsb = VSB_new_auto();
564 29110
        AN(vsb);
565 29110
        va_start(ap, fmt);
566 29110
        VSB_vprintf(vsb, fmt, ap);
567 29110
        va_end(ap);
568 29110
        AZ(VSB_finish(vsb));
569 29110
        switch (which) {
570
        case MCF_DEFAULT:
571 3248
                pp->def = strdup(VSB_data(vsb));
572 3248
                AN(pp->def);
573 3248
                break;
574
        case MCF_MINIMUM:
575 12924
                pp->min = strdup(VSB_data(vsb));
576 12924
                AN(pp->min);
577 12924
                break;
578
        case MCF_MAXIMUM:
579 12938
                pp->max = strdup(VSB_data(vsb));
580 12938
                AN(pp->max);
581 12938
                break;
582
        }
583 29110
        VSB_delete(vsb);
584 29110
}
585
586
/*--------------------------------------------------------------------*/
587
588
void
589 3
MCF_DumpRstParam(void)
590
{
591
        struct plist *pl;
592
        const struct parspec *pp;
593
        const char *p, *q, *t1, *t2;
594
        int j;
595
596 3
        printf("\n.. The following is the autogenerated "
597
            "output from varnishd -x parameter\n\n");
598 315
        VTAILQ_FOREACH(pl, &phead, list) {
599 312
                pp = pl->spec;
600 312
                printf(".. _ref_param_%s:\n\n", pp->name);
601 312
                printf("%s\n", pp->name);
602 4881
                for (j = 0; j < strlen(pp->name); j++)
603 4569
                        printf("~");
604 312
                printf("\n");
605
606 312
                if (pp->flags && pp->flags & NOT_IMPLEMENTED)
607 3
                        printf("\n%s\n\n", NOT_IMPLEMENTED_DOC);
608
609 312
                if (pp->units != NULL && *pp->units != '\0')
610 258
                        printf("\t* Units: %s\n", pp->units);
611 312
                printf("\t* Default: %s\n", pp->def);
612 312
                if (pp->min != NULL)
613 237
                        printf("\t* Minimum: %s\n", pp->min);
614 312
                if (pp->max != NULL)
615 75
                        printf("\t* Maximum: %s\n", pp->max);
616
                /*
617
                 * XXX: we should mark the params with one/two flags
618
                 * XXX: that say if ->min/->max are valid, so we
619
                 * XXX: can emit those also in help texts.
620
                 */
621 312
                if (pp->flags) {
622 144
                        printf("\t* Flags: ");
623 144
                        q = "";
624
625 144
                        if (pp->flags & DELAYED_EFFECT) {
626 39
                                printf("%sdelayed", q);
627 39
                                q = ", ";
628
                        }
629 144
                        if (pp->flags & MUST_RESTART) {
630 15
                                printf("%smust_restart", q);
631 15
                                q = ", ";
632
                        }
633 144
                        if (pp->flags & MUST_RELOAD) {
634 3
                                printf("%smust_reload", q);
635 3
                                q = ", ";
636
                        }
637 144
                        if (pp->flags & EXPERIMENTAL) {
638 72
                                printf("%sexperimental", q);
639 72
                                q = ", ";
640
                        }
641 144
                        if (pp->flags & WIZARD) {
642 12
                                printf("%swizard", q);
643 12
                                q = ", ";
644
                        }
645 144
                        if (pp->flags & ONLY_ROOT) {
646 0
                                printf("%sonly_root", q);
647 0
                                q = ", ";
648
                        }
649 144
                        if (pp->flags & OBJ_STICKY) {
650 9
                                printf("%sobj_sticky", q);
651 9
                                q = ", ";
652
                        }
653 144
                        printf("\n");
654
                }
655 312
                printf("\n");
656 312
                p = pp->descr;
657 1467
                while (*p != '\0') {
658 843
                        q = strchr(p, '\n');
659 843
                        if (q == NULL)
660 312
                                q = strchr(p, '\0');
661 843
                        t1 = strchr(p, '\t');
662 843
                        if (t1 != NULL && t1 < q) {
663 126
                                t2 = strchr(t1 + 1, '\t');
664 126
                                AN(t2);
665 126
                                printf("\n\t*");
666 126
                                (void)fwrite(t1 + 1, (t2 - 1) - t1, 1, stdout);
667 126
                                printf("*\n\t\t");
668 126
                                p = t2 + 1;
669
                        }
670 843
                        (void)fwrite(p, q - p, 1, stdout);
671 843
                        p = q;
672 843
                        if (*p == '\n') {
673 531
                                printf("\n");
674 531
                                p++;
675
                        }
676 843
                        continue;
677
                }
678 312
                printf("\n\n");
679
        }
680 3
}