varnish-cache/bin/varnishd/mgt/mgt_param.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 */
30
31
#include "config.h"
32
33
#include <ctype.h>
34
#include <stdarg.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <unistd.h>
39
40
#include "mgt/mgt.h"
41
#include "common/heritage.h"
42
43
#include "mgt/mgt_param.h"
44
#include "vav.h"
45
#include "vcli_serve.h"
46
47
struct plist {
48
        unsigned                        magic;
49
#define PLIST_MAGIC                     0xbfc3ea16
50
        VTAILQ_ENTRY(plist)             list;
51
        struct parspec                  *spec;
52
};
53
54
static VTAILQ_HEAD(, plist)             phead = VTAILQ_HEAD_INITIALIZER(phead);
55
56
struct params mgt_param;
57
static const int margin1 = 8;
58
static int margin2 = 0;
59
static const int wrap_at = 72;
60
static const int tab0 = 3;
61
62
/*--------------------------------------------------------------------*/
63
64
static const char TYPE_TIMEOUT_TEXT[] =
65
        "\n\n"
66
        "NB: This parameter can be disabled with the value \"never\".";
67
68
static const char OBJ_STICKY_TEXT[] =
69
        "\n\n"
70
        "NB: This parameter is evaluated only when objects are created. "
71
        "To change it for all objects, restart or ban everything.";
72
73
static const char DELAYED_EFFECT_TEXT[] =
74
        "\n\n"
75
        "NB: This parameter may take quite some time to take (full) effect.";
76
77
static const char MUST_RESTART_TEXT[] =
78
        "\n\n"
79
        "NB: This parameter will not take any effect until the "
80
        "child process has been restarted.";
81
82
static const char MUST_RELOAD_TEXT[] =
83
        "\n\n"
84
        "NB: This parameter will not take any effect until the "
85
        "VCL programs have been reloaded.";
86
87
static const char EXPERIMENTAL_TEXT[] =
88
        "\n\n"
89
        "NB: We do not know yet if it is a good idea to change "
90
        "this parameter, or if the default value is even sensible. "
91
        "Caution is advised, and feedback is most welcome.";
92
93
static const char WIZARD_TEXT[] =
94
        "\n\n"
95
        "NB: Do not change this parameter, unless a developer tell "
96
        "you to do so.";
97
98
static const char PROTECTED_TEXT[] =
99
        "\n\n"
100
        "NB: This parameter is protected and cannot be changed.";
101
102
static const char ONLY_ROOT_TEXT[] =
103
        "\n\n"
104
        "NB: This parameter only works if varnishd is run as root.";
105
106
static const char NOT_IMPLEMENTED_TEXT[] =
107
        "\n\n"
108
        "NB: This parameter depends on a feature which is not available"
109
        " on this platform.";
110
111
static const char PLATFORM_DEPENDENT_TEXT[] =
112
        "\n\n"
113
        "NB: This parameter depends on a feature which is not available"
114
        " on all platforms.";
115
116
static const char BUILD_OPTIONS_TEXT[] =
117
        "\n\n"
118
        "NB: The actual default value for this parameter depends on the"
119
        " Varnish build environment and options.";
120
121
/*--------------------------------------------------------------------*/
122
123
static struct parspec *
124 617546
mcf_findpar(const char *name)
125
{
126
        struct plist *pl;
127
128 617546
        AN(name);
129 58723447
        VTAILQ_FOREACH(pl, &phead, list)
130 58723395
                if (!strcmp(pl->spec->name, name))
131 617494
                        return (pl->spec);
132 52
        return (NULL);
133 617546
}
134
135
static void
136 1705372
mcf_addpar(struct parspec *ps)
137
{
138
        struct plist *pl, *pl2;
139
        int i;
140
141 1705372
        ALLOC_OBJ(pl, PLIST_MAGIC);
142 1705372
        AN(pl);
143 1705372
        pl->spec = ps;
144 81404007
        VTAILQ_FOREACH(pl2, &phead, list) {
145 81142700
                i = strcmp(pl2->spec->name, pl->spec->name);
146 81142700
                if (i == 0) {
147 0
                        fprintf(stderr, "Duplicate param: %s\n", ps->name);
148 0
                        exit(4);
149 81142700
                } else if (i > 0) {
150 1444065
                        VTAILQ_INSERT_BEFORE(pl2, pl, list);
151 1444065
                        return;
152
                }
153 79698635
        }
154 261307
        VTAILQ_INSERT_TAIL(&phead, pl, list);
155 1705372
}
156
157
static const struct parspec *
158 78
mcf_alias(struct parspec *alias, const struct parspec *pp)
159
{
160
        const struct parspec *orig;
161
162 78
        orig = TRUST_ME(pp->priv);
163 78
        AN(orig);
164 78
        memcpy(alias, orig, sizeof *alias);
165 78
        alias->priv = TRUST_ME(orig);
166 78
        return (alias);
167
}
168
169
/*--------------------------------------------------------------------
170
 * Wrap the text nicely.
171
 * Lines are allowed to contain two TABS and we render that as a table
172
 * taking the width of the first column into account.
173
 */
174
175
static void
176 34151
mcf_wrap_line(struct cli *cli, const char *b, const char *e, int tabs, int m0)
177
{
178 34151
        int n, hadtabs = 0;
179
        const char *w;
180
181 34151
        n = m0;
182 34151
        VCLI_Out(cli, "%*s", n, "");
183
184 504790
        while (b < e) {
185 487682
                if (!isspace(*b)) {
186 236600
                        VCLI_Out(cli, "%c", *b);
187 236600
                        b++;
188 236600
                        n++;
189 487682
                } else if (*b == '\t') {
190 7332
                        assert(tabs);
191 7332
                        assert(hadtabs < 2);
192 7332
                        do {
193 57291
                                VCLI_Out(cli, " ");
194 57291
                                n++;
195 57291
                        } while ((n % tabs) != (m0 + tab0) % tabs);
196 7332
                        b++;
197 7332
                        hadtabs++;
198 7332
                } else {
199 243750
                        assert (*b == ' ');
200 1461252
                        for (w = b + 1; w < e; w++)
201 1444144
                                if (isspace(*w))
202 226642
                                        break;
203 243750
                        if (n + (w - b) < wrap_at) {
204 226707
                                VCLI_Out(cli, "%.*s", (int)(w - b), b);
205 226707
                                n += (w - b);
206 226707
                                b = w;
207 226707
                        } else {
208 17043
                                assert(hadtabs == 0 || hadtabs == 2);
209 17043
                                VCLI_Out(cli, "\n");
210 34086
                                mcf_wrap_line(cli, b + 1, e, 0,
211 17043
                                    hadtabs ? m0 + tab0 + tabs : m0);
212 17043
                                return;
213
                        }
214
                }
215
        }
216 17108
        assert(b == e);
217 34151
}
218
219
static void
220 8333
mcf_wrap(struct cli *cli, const char *text)
221
{
222
        const char *p, *q, *r;
223 8333
        int tw = 0;
224
225 8333
        if (strchr(text, '\t') != NULL) {
226 4134
                for (p = text; *p != '\0'; ) {
227 4134
                        q = strstr(p, "\n\t");
228 4134
                        if (q == NULL)
229 468
                                break;
230 3666
                        q += 2;
231 3666
                        r = strchr(q, '\t');
232 3666
                        if (r == NULL) {
233 0
                                fprintf(stderr,
234 0
                                    "LINE with just one TAB: <%s>\n", text);
235 0
                                exit(4);
236
                        }
237 3666
                        if (r - q > tw)
238 1313
                                tw = r - q;
239 3666
                        p = q;
240
                }
241 468
                tw += 2;
242 468
                if (tw < 20)
243 403
                        tw = 20;
244 468
        }
245
246 43667
        for (p = text; *p != '\0'; ) {
247 35334
                if (*p == '\n') {
248 18226
                        VCLI_Out(cli, "\n");
249 18226
                        p++;
250 18226
                        continue;
251
                }
252 17108
                q = strchr(p, '\n');
253 17108
                if (q == NULL)
254 8333
                        q = strchr(p, '\0');
255 17108
                mcf_wrap_line(cli, p, q, tw, margin1);
256 17108
                p = q;
257
        }
258 8333
}
259
260
/*--------------------------------------------------------------------*/
261
262
static void v_matchproto_(cli_func_t)
263 429
mcf_param_show(struct cli *cli, const char * const *av, void *priv)
264
{
265
        struct plist *pl;
266
        const struct parspec *pp, *pa;
267
        struct parspec alias[1];
268 429
        int n, lfmt = 0, chg = 0;
269
        struct vsb *vsb;
270 429
        const char *show = NULL;
271
272 429
        (void)priv;
273
274 780
        for (n = 2; av[n] != NULL; n++) {
275 364
                if (strcmp(av[n], "-l") == 0) {
276 39
                        lfmt = 1;
277 39
                        continue;
278
                }
279 325
                if (strcmp(av[n], "changed") == 0) {
280 13
                        chg = 1;
281 13
                        continue;
282
                }
283 312
                if (show != NULL) {
284 13
                        VCLI_SetResult(cli, CLIS_TOOMANY);
285 13
                        VCLI_Out(cli, "Too many parameters");
286 13
                        return;
287
                }
288 299
                show = av[n];
289 299
                lfmt = 1;
290 299
        }
291
292 416
        vsb = VSB_new_auto();
293 416
        AN(vsb);
294
295 416
        n = 0;
296 52000
        VTAILQ_FOREACH(pl, &phead, list) {
297 51584
                pp = pl->spec;
298 51584
                if (lfmt && show != NULL && strcmp(pp->name, show))
299 35191
                        continue;
300 16393
                if (pp->func == tweak_alias) {
301 572
                        if (show == NULL)
302 520
                                continue;
303 52
                        if (strcmp(pp->name, show))
304 0
                                continue;
305 52
                        pp = mcf_alias(alias, pp);
306 52
                }
307 15873
                n++;
308
309 15873
                VSB_clear(vsb);
310 15873
                if (pp->func(vsb, pp, NULL))
311 0
                        VCLI_SetResult(cli, CLIS_PARAM);
312 15873
                AZ(VSB_finish(vsb));
313 15873
                if (chg && pp->def != NULL && !strcmp(pp->def, VSB_data(vsb)))
314 1404
                        continue;
315
316 14469
                if (pp->flags & NOT_IMPLEMENTED) {
317 0
                        if (lfmt) {
318 0
                                VCLI_Out(cli, "%s\n", pp->name);
319 0
                                VCLI_Out(cli, "%-*sNot available", margin1, " ");
320 0
                        } else {
321 0
                                VCLI_Out(cli, "%-*s-", margin2, pp->name);
322
                        }
323 0
                } else {
324 14469
                        if (lfmt) {
325 4953
                                VCLI_Out(cli, "%s\n", pp->name);
326 4953
                                VCLI_Out(cli, "%-*sValue is: ", margin1, " ");
327 4953
                        } else {
328 9516
                                VCLI_Out(cli, "%-*s", margin2, pp->name);
329
                        }
330
331 14469
                        VCLI_Out(cli, "%s", VSB_data(vsb));
332 14469
                        if (pp->units != NULL && *pp->units != '\0')
333 11570
                                VCLI_Out(cli, " [%s]", pp->units);
334 14469
                        if (pp->def != NULL && !strcmp(pp->def, VSB_data(vsb)))
335 12987
                                VCLI_Out(cli, " (default)");
336
                }
337 14469
                VCLI_Out(cli, "\n");
338
339 14469
                if (lfmt && pp->func == tweak_alias) {
340 0
                        pa = TRUST_ME(pp->priv);
341 0
                        VCLI_Out(cli, "%-*sAlias of: %s\n",
342 0
                            margin1, " ", pa->name);
343 0
                }
344 14469
                if (lfmt && pp->flags & NOT_IMPLEMENTED) {
345 0
                        VCLI_Out(cli, "\n");
346 0
                        mcf_wrap(cli, NOT_IMPLEMENTED_TEXT);
347 0
                        VCLI_Out(cli, "\n\n");
348 14469
                } else if (lfmt) {
349 4953
                        if (pp->def != NULL && strcmp(pp->def, VSB_data(vsb)))
350 1118
                                VCLI_Out(cli, "%-*sDefault is: %s\n",
351 559
                                    margin1, "", pp->def);
352 4953
                        if (pp->min != NULL)
353 7696
                                VCLI_Out(cli, "%-*sMinimum is: %s\n",
354 3848
                                    margin1, "", pp->min);
355 4953
                        if (pp->max != NULL)
356 2262
                                VCLI_Out(cli, "%-*sMaximum is: %s\n",
357 1131
                                    margin1, "", pp->max);
358 4953
                        VCLI_Out(cli, "\n");
359 4953
                        mcf_wrap(cli, pp->descr);
360 4953
                        if (pp->func == tweak_timeout)
361 520
                                mcf_wrap(cli, TYPE_TIMEOUT_TEXT);
362 4953
                        if (pp->flags & OBJ_STICKY)
363 169
                                mcf_wrap(cli, OBJ_STICKY_TEXT);
364 4953
                        if (pp->flags & DELAYED_EFFECT)
365 702
                                mcf_wrap(cli, DELAYED_EFFECT_TEXT);
366 4953
                        if (pp->flags & EXPERIMENTAL)
367 1170
                                mcf_wrap(cli, EXPERIMENTAL_TEXT);
368 4953
                        if (pp->flags & MUST_RELOAD)
369 91
                                mcf_wrap(cli, MUST_RELOAD_TEXT);
370 4953
                        if (pp->flags & MUST_RESTART)
371 312
                                mcf_wrap(cli, MUST_RESTART_TEXT);
372 4953
                        if (pp->flags & WIZARD)
373 234
                                mcf_wrap(cli, WIZARD_TEXT);
374 4953
                        if (pp->flags & PROTECTED)
375 13
                                mcf_wrap(cli, PROTECTED_TEXT);
376 4953
                        if (pp->flags & ONLY_ROOT)
377 0
                                mcf_wrap(cli, ONLY_ROOT_TEXT);
378 4953
                        if (pp->flags & BUILD_OPTIONS)
379 169
                                mcf_wrap(cli, BUILD_OPTIONS_TEXT);
380 4953
                        VCLI_Out(cli, "\n\n");
381 4953
                }
382 14469
        }
383 416
        if (show != NULL && n == 0) {
384 13
                VCLI_SetResult(cli, CLIS_PARAM);
385 13
                VCLI_Out(cli, "Unknown parameter \"%s\".", show);
386 13
        }
387 416
        VSB_destroy(&vsb);
388 429
}
389
390
static inline void
391 16224
mcf_json_key_valstr(struct cli *cli, const char *key, const char *val)
392
{
393 16224
        VCLI_Out(cli, "\"%s\": ", key);
394 16224
        VCLI_JSON_str(cli, val);
395 16224
        VCLI_Out(cli, ",\n");
396 16224
}
397
398
static void v_matchproto_(cli_func_t)
399 195
mcf_param_show_json(struct cli *cli, const char * const *av, void *priv)
400
{
401 195
        int n, comma = 0, chg = 0;
402
        struct plist *pl;
403
        const struct parspec *pp, *pa;
404
        struct parspec alias[1];
405
        struct vsb *vsb, *def;
406 195
        const char *show = NULL, *sep;
407
408 195
        (void)priv;
409
410 546
        for (int i = 2; av[i] != NULL; i++) {
411 377
                if (strcmp(av[i], "-l") == 0) {
412 13
                        VCLI_SetResult(cli, CLIS_PARAM);
413 13
                        VCLI_Out(cli, "-l not permitted with param.show -j");
414 13
                        return;
415
                }
416 364
                if (strcmp(av[i], "changed") == 0) {
417 13
                        chg = 1;
418 13
                        continue;
419
                }
420 351
                if (strcmp(av[i], "-j") == 0)
421 195
                        continue;
422 156
                if (show != NULL) {
423 13
                        VCLI_SetResult(cli, CLIS_TOOMANY);
424 13
                        VCLI_Out(cli, "Too many parameters");
425 13
                        return;
426
                }
427 143
                show = av[i];
428 143
        }
429
430 169
        vsb = VSB_new_auto();
431 169
        AN(vsb);
432 169
        def = VSB_new_auto();
433 169
        AN(def);
434
435 169
        n = 0;
436 169
        VCLI_JSON_begin(cli, 2, av);
437 169
        VCLI_Out(cli, ",\n");
438 21125
        VTAILQ_FOREACH(pl, &phead, list) {
439 20956
                pp = pl->spec;
440 20956
                if (show != NULL && strcmp(pp->name, show) != 0)
441 16003
                        continue;
442 4953
                if (pp->func == tweak_alias) {
443 169
                        if (show == NULL)
444 156
                                continue;
445 13
                        if (strcmp(pp->name, show))
446 0
                                continue;
447 13
                        pp = mcf_alias(alias, pp);
448 13
                }
449 4797
                n++;
450
451 4797
                VSB_clear(vsb);
452 4797
                if (pp->func(vsb, pp, JSON_FMT))
453 0
                        VCLI_SetResult(cli, CLIS_PARAM);
454 4797
                AZ(VSB_finish(vsb));
455 4797
                VSB_clear(def);
456 4797
                if (pp->func(def, pp, NULL))
457 0
                        VCLI_SetResult(cli, CLIS_PARAM);
458 4797
                AZ(VSB_finish(def));
459 4797
                if (chg && pp->def != NULL && !strcmp(pp->def, VSB_data(def)))
460 1404
                        continue;
461
462 3393
                VCLI_Out(cli, "%s", comma ? ",\n" : "");
463 3393
                comma++;
464 3393
                VCLI_Out(cli, "{\n");
465 3393
                VSB_indent(cli->sb, 2);
466 3393
                mcf_json_key_valstr(cli, "name", pp->name);
467 3393
                if (pp->func == tweak_alias) {
468 0
                        pa = TRUST_ME(pp->priv);
469 0
                        mcf_json_key_valstr(cli, "alias", pa->name);
470 0
                }
471 3393
                if (pp->flags & NOT_IMPLEMENTED) {
472 0
                        VCLI_Out(cli, "\"implemented\": false\n");
473 0
                        VSB_indent(cli->sb, -2);
474 0
                        VCLI_Out(cli, "}");
475 0
                        continue;
476
                }
477 3393
                VCLI_Out(cli, "\"implemented\": true,\n");
478 3393
                VCLI_Out(cli, "\"value\": %s,\n", VSB_data(vsb));
479 3393
                if (pp->units != NULL && *pp->units != '\0')
480 2652
                        mcf_json_key_valstr(cli, "units", pp->units);
481
482 3393
                if (pp->def != NULL)
483 3393
                        mcf_json_key_valstr(cli, "default", pp->def);
484 3393
                if (pp->min != NULL)
485 2587
                        mcf_json_key_valstr(cli, "minimum", pp->min);
486 3393
                if (pp->max != NULL)
487 806
                        mcf_json_key_valstr(cli, "maximum", pp->max);
488 3393
                mcf_json_key_valstr(cli, "description", pp->descr);
489
490 3393
                VCLI_Out(cli, "\"flags\": [");
491 3393
                VSB_indent(cli->sb, 2);
492 3393
                sep = "";
493
494
#define flag_out(flag, string) do {                                     \
495
                        if (pp->flags & flag) {                         \
496
                                VCLI_Out(cli, "%s\n", sep);             \
497
                                VCLI_Out(cli, "\"%s\"", #string);       \
498
                                sep = ",";                              \
499
                        }                                               \
500
                } while(0)
501
502 3393
                flag_out(OBJ_STICKY, obj_sticky);
503 3393
                flag_out(DELAYED_EFFECT, delayed_effect);
504 3393
                flag_out(EXPERIMENTAL, experimental);
505 3393
                flag_out(MUST_RELOAD, must_reload);
506 3393
                flag_out(MUST_RESTART, must_restart);
507 3393
                flag_out(WIZARD, wizard);
508 3393
                flag_out(PROTECTED, protected);
509 3393
                flag_out(ONLY_ROOT, only_root);
510 3393
                flag_out(BUILD_OPTIONS, build_options);
511
512
#undef flag_out
513
514 3393
                if (pp->flags)
515 1729
                        VCLI_Out(cli, "\n");
516 3393
                VSB_indent(cli->sb, -2);
517 3393
                VCLI_Out(cli, "]\n");
518 3393
                VSB_indent(cli->sb, -2);
519 3393
                VCLI_Out(cli, "}");
520 3393
        }
521 169
        VCLI_JSON_end(cli);
522 169
        if (show != NULL && n == 0) {
523 13
                VSB_clear(cli->sb);
524 13
                VCLI_SetResult(cli, CLIS_PARAM);
525 13
                VCLI_Out(cli, "Unknown parameter \"%s\".", show);
526 13
        }
527 169
        VSB_destroy(&vsb);
528 169
        VSB_destroy(&def);
529 195
}
530
531
/*--------------------------------------------------------------------
532
 * Mark parameters as protected
533
 */
534
535
void
536 39
MCF_ParamProtect(struct cli *cli, const char *args)
537
{
538
        const struct parspec *orig;
539
        struct parspec *pp;
540
        char **av;
541
        int i;
542
543 39
        av = VAV_Parse(args, NULL, ARGV_COMMA);
544 39
        if (av[0] != NULL) {
545 0
                VCLI_Out(cli, "Parse error: %s", av[0]);
546 0
                VCLI_SetResult(cli, CLIS_PARAM);
547 0
                VAV_Free(av);
548 0
                return;
549
        }
550 65
        for (i = 1; av[i] != NULL; i++) {
551 39
                pp = mcf_findpar(av[i]);
552 39
                if (pp == NULL) {
553 0
                        VCLI_Out(cli, "Unknown parameter %s", av[i]);
554 0
                        VCLI_SetResult(cli, CLIS_PARAM);
555 0
                        break;
556
                }
557 39
                if (pp->func == tweak_alias) {
558 13
                        orig = TRUST_ME(pp->priv);
559 13
                        AN(orig);
560 26
                        VCLI_Out(cli,
561
                            "Cannot mark alias %s read only.\n"
562
                            "Did you mean to mark %s read only?",
563 13
                            pp->name, orig->name);
564 13
                        VCLI_SetResult(cli, CLIS_PARAM);
565 13
                        break;
566
                }
567 26
                pp->flags |= PROTECTED;
568 26
        }
569 39
        VAV_Free(av);
570 39
}
571
572
/*--------------------------------------------------------------------*/
573
574
void
575 123023
MCF_ParamSet(struct cli *cli, const char *param, const char *val)
576
{
577
        const struct parspec *pp;
578
        struct parspec alias[1];
579
580 123023
        pp = mcf_findpar(param);
581 123023
        if (pp == NULL) {
582 52
                VCLI_SetResult(cli, CLIS_PARAM);
583 52
                VCLI_Out(cli, "Unknown parameter \"%s\".", param);
584 52
                return;
585
        }
586 122971
        if (pp->flags & NOT_IMPLEMENTED) {
587 0
                VCLI_SetResult(cli, CLIS_CANT);
588 0
                VCLI_Out(cli,
589
                    "parameter \"%s\" is not available on this platform.",
590 0
                    param
591
                );
592 0
                return;
593
        }
594 122971
        if (!val)
595 455
                val = pp->def;
596 122971
        if (pp->func == tweak_alias) {
597 13
                pp = mcf_alias(alias, pp);
598 13
                alias->name = param;
599 13
        }
600 122971
        if (pp->flags & PROTECTED) {
601 39
                VCLI_SetResult(cli, CLIS_AUTH);
602 39
                VCLI_Out(cli, "parameter \"%s\" is protected.", param);
603 39
                return;
604
        }
605 122932
        if (pp->func(cli->sb, pp, val))
606 377
                VCLI_SetResult(cli, CLIS_PARAM);
607
608 122932
        if (cli->result == CLIS_OK && heritage.param != NULL)
609 4524
                *heritage.param = mgt_param;
610
611 122932
        if (cli->result != CLIS_OK) {
612 754
                VCLI_Out(cli, "\n(attempting to set param '%s' to '%s')",
613 377
                    pp->name, val);
614 122932
        } else if (MCH_Running() && pp->flags & MUST_RESTART) {
615 0
                VCLI_Out(cli,
616
                    "\nChange will take effect when child is restarted");
617 122555
        } else if (pp->flags & MUST_RELOAD) {
618 26
                VCLI_Out(cli,
619
                    "\nChange will take effect when VCL script is reloaded");
620 26
        }
621 123023
}
622
623
624
/*--------------------------------------------------------------------*/
625
626
static void v_matchproto_(cli_func_t)
627 7215
mcf_param_set(struct cli *cli, const char * const *av, void *priv)
628
{
629
630 7215
        (void)priv;
631 7215
        MCF_ParamSet(cli, av[2], av[3]);
632 7215
}
633
634
static void v_matchproto_(cli_func_t)
635 78
mcf_param_set_json(struct cli *cli, const char * const *av, void *priv)
636
{
637 78
        const char *const avs[] = { av[0], av[1], av[2], av[3], NULL };
638
639 78
        MCF_ParamSet(cli, av[3], av[4]);
640 78
        if (cli->result == CLIS_OK)
641 78
                mcf_param_show_json(cli, avs, priv);
642 78
}
643
644
/*--------------------------------------------------------------------*/
645
646
static void v_matchproto_(cli_func_t)
647 442
mcf_param_reset(struct cli *cli, const char * const *av, void *priv)
648
{
649
650 442
        (void)priv;
651 442
        MCF_ParamSet(cli, av[2], NULL);
652 442
}
653
654
/*--------------------------------------------------------------------
655
 * Initialize parameters and sort by name.
656
 */
657
658
static struct parspec mgt_parspec[] = {
659
#define PARAM_ALL
660
#define PARAM_PRE {
661
#define PARAM(typ, fld, nm, ...) #nm, __VA_ARGS__
662
#define PARAM_POST },
663
#include "tbl/params.h"
664
        { NULL, NULL, NULL }
665
};
666
667
static void
668 13753
mcf_init_params(void)
669
{
670
        struct parspec *pp;
671
        const char *s;
672
673 1719125
        for (pp = mgt_parspec; pp->name != NULL; pp++) {
674 1705372
                AN(pp->func);
675 1705372
                s = strchr(pp->descr, '\0');
676 1705372
                if (isspace(s[-1])) {
677 0
                        fprintf(stderr,
678 0
                            "Param->descr has trailing space: %s\n", pp->name);
679 0
                        exit(4);
680
                }
681 1705372
                mcf_addpar(pp);
682 1705372
                margin2 = vmax_t(int, margin2, strlen(pp->name) + 1);
683 1705372
        }
684 13753
}
685
686
/*--------------------------------------------------------------------
687
 * Wash a min/max/default value
688
 */
689
690
static void
691 1182134
mcf_dyn_vsb(enum mcf_which_e which, struct parspec *pp, struct vsb *vsb)
692
{
693
694 1182134
        switch (which) {
695
        case MCF_DEFAULT:
696 385084
                REPLACE(pp->dyn_def, VSB_data(vsb));
697 385084
                pp->def = pp->dyn_def;
698 385084
                break;
699
        case MCF_MINIMUM:
700 494562
                REPLACE(pp->dyn_min, VSB_data(vsb));
701 494562
                pp->min = pp->dyn_min;
702 494562
                break;
703
        case MCF_MAXIMUM:
704 302488
                REPLACE(pp->dyn_max, VSB_data(vsb));
705 302488
                pp->max = pp->dyn_max;
706 302488
                break;
707
        default:
708 0
                WRONG("bad 'which'");
709 0
        }
710 1182134
}
711
712
static void
713 3410744
mcf_wash_param(struct cli *cli, struct parspec *pp, enum mcf_which_e which,
714
    const char *name, struct vsb *vsb)
715
{
716
        const char *val;
717
        int err;
718
719 3410744
        switch (which) {
720 1705372
        case MCF_DEFAULT: val = pp->def; break;
721 1306535
        case MCF_MINIMUM: val = pp->min; break;
722 398837
        case MCF_MAXIMUM: val = pp->max; break;
723
        default:
724 0
                WRONG("bad 'which'");
725 0
        }
726 3410744
        AN(val);
727
728 3410744
        if (pp->func == tweak_alias) {
729 55012
                assert(which == MCF_DEFAULT);
730 55012
                pp->priv = mcf_findpar(pp->def);
731 55012
                pp->def = NULL;
732 55012
                return;
733
        }
734
735 3355732
        VSB_clear(vsb);
736 6711464
        VSB_printf(vsb, "FAILED to set %s for param %s: %s\n",
737 3355732
            name, pp->name, val);
738 3355732
        err = pp->func(vsb, pp, val);
739 3355732
        AZ(VSB_finish(vsb));
740 3355732
        if (err) {
741 0
                VCLI_Out(cli, "%s\n", VSB_data(vsb));
742 0
                VCLI_SetResult(cli, CLIS_CANT);
743 0
                return;
744
        }
745 3355732
        VSB_clear(vsb);
746 3355732
        err = pp->func(vsb, pp, NULL);
747 3355732
        AZ(err);
748 3355732
        AZ(VSB_finish(vsb));
749 3355732
        if (strcmp(val, VSB_data(vsb)))
750 742662
                mcf_dyn_vsb(which, pp, vsb);
751 3410744
}
752
753
/*--------------------------------------------------------------------*/
754
755
static struct cli_proto cli_params[] = {
756
        { CLICMD_PARAM_SHOW,    "", mcf_param_show, mcf_param_show_json },
757
        { CLICMD_PARAM_SET,     "", mcf_param_set, mcf_param_set_json },
758
        { CLICMD_PARAM_RESET,   "", mcf_param_reset, mcf_param_set_json },
759
        { NULL }
760
};
761
762
/*--------------------------------------------------------------------
763
 * Configure the parameters
764
 */
765
766
void
767 13753
MCF_InitParams(struct cli *cli)
768
{
769
        struct plist *pl;
770
        struct parspec *pp;
771
        struct vsb *vsb;
772
        ssize_t def, low;
773
774 13753
        mcf_init_params();
775 13753
        MCF_TcpParams();
776
777 13753
        def = 80 * 1024;
778
779
        if (sizeof(void *) < 8) {               /*lint !e506 !e774  */
780
                /*
781
                 * Adjust default parameters for 32 bit systems to conserve
782
                 * VM space.
783
                 *
784
                 * Reflect changes in doc/sphinx/reference/varnishd.rst !
785
                 */
786
                MCF_ParamConf(MCF_DEFAULT, "workspace_client", "24k");
787
                MCF_ParamConf(MCF_DEFAULT, "workspace_backend", "20k");
788
                MCF_ParamConf(MCF_DEFAULT, "http_resp_size", "8k");
789
                MCF_ParamConf(MCF_DEFAULT, "http_req_size", "12k");
790
                MCF_ParamConf(MCF_DEFAULT, "gzip_buffer", "4k");
791
                MCF_ParamConf(MCF_DEFAULT, "vsl_buffer", "4k");
792
                MCF_ParamConf(MCF_MAXIMUM, "vsl_space", "1G");
793
                def = 64 * 1024;
794
        }
795
796 13753
        low = sysconf(_SC_THREAD_STACK_MIN);
797 13753
        MCF_ParamConf(MCF_MINIMUM, "thread_pool_stack", "%jdb", (intmax_t)low);
798
799
#if defined(ENABLE_SANITIZER) || defined(ENABLE_COVERAGE)
800 13753
        def = 192 * 1024;
801
#endif
802
803 13753
        if (def < low)
804 0
                def = low;
805 13753
        MCF_ParamConf(MCF_DEFAULT, "thread_pool_stack", "%jdb", (intmax_t)def);
806
807
#if !defined(MAX_THREAD_POOLS)
808
#  define MAX_THREAD_POOLS 32
809
#endif
810
811 13753
        MCF_ParamConf(MCF_MAXIMUM, "thread_pools", "%d", MAX_THREAD_POOLS);
812
813
#if !defined(HAVE_ACCEPT_FILTERS) || defined(__linux)
814
        MCF_ParamConf(MCF_DEFAULT, "accept_filter", "off");
815
#endif
816
817 13753
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_params);
818
819 13753
        vsb = VSB_new_auto();
820 13753
        AN(vsb);
821
822 1719125
        VTAILQ_FOREACH(pl, &phead, list) {
823 1705372
                pp = pl->spec;
824
825 1705372
                if (pp->flags & NOT_IMPLEMENTED)
826 0
                        continue;
827 1705372
                if (pp->min != NULL)
828 1306535
                        mcf_wash_param(cli, pp, MCF_MINIMUM, "minimum", vsb);
829 1705372
                if (pp->max != NULL)
830 398837
                        mcf_wash_param(cli, pp, MCF_MAXIMUM, "maximum", vsb);
831 1705372
                AN(pp->def);
832 1705372
                mcf_wash_param(cli, pp, MCF_DEFAULT, "default", vsb);
833 1705372
        }
834 13753
        VSB_destroy(&vsb);
835
836 13753
        AN(mgt_cc_cmd);
837 13753
        REPLACE(mgt_cc_cmd_def, mgt_cc_cmd);
838 13753
}
839
840
/*--------------------------------------------------------------------*/
841
842
void
843 439472
MCF_ParamConf(enum mcf_which_e which, const char * const param,
844
    const char *fmt, ...)
845
{
846
        struct parspec *pp;
847
        struct vsb *vsb;
848
        va_list ap;
849
850 439472
        pp = mcf_findpar(param);
851 439472
        AN(pp);
852 439472
        vsb = VSB_new_auto();
853 439472
        AN(vsb);
854 439472
        va_start(ap, fmt);
855 439472
        VSB_vprintf(vsb, fmt, ap);
856 439472
        va_end(ap);
857 439472
        AZ(VSB_finish(vsb));
858 439472
        mcf_dyn_vsb(which, pp, vsb);
859 439472
        VSB_destroy(&vsb);
860 439472
}
861
862
/*--------------------------------------------------------------------*/
863
864
void
865 26
MCF_DumpRstParam(void)
866
{
867
        struct plist *pl;
868
        const struct parspec *pp;
869
        const char *p, *q, *t1, *t2;
870
        unsigned flags;
871
        size_t z;
872
873 26
        printf("\n.. The following is the autogenerated "
874
            "output from varnishd -x parameter\n\n");
875 3250
        VTAILQ_FOREACH(pl, &phead, list) {
876 3224
                pp = pl->spec;
877 3224
                if (!strcmp("deprecated_dummy", pp->name))
878 26
                    continue;
879 3198
                printf(".. _ref_param_%s:\n\n", pp->name);
880 3198
                printf("%s\n", pp->name);
881 51142
                for (z = 0; z < strlen(pp->name); z++)
882 47944
                        printf("~");
883 3198
                printf("\n");
884
885 3198
                if (pp->flags && pp->flags & PLATFORM_DEPENDENT)
886 130
                        printf("\n%s\n\n", PLATFORM_DEPENDENT_TEXT);
887
888 3198
                if (pp->flags && pp->flags & BUILD_OPTIONS)
889 104
                        printf("\n%s\n\n", BUILD_OPTIONS_TEXT);
890
891 3198
                if (pp->units != NULL && *pp->units != '\0')
892 2522
                        printf("\t* Units: %s\n", pp->units);
893
#define MCF_DYN_REASON(lbl, nm)                                 \
894
                if (pp->dyn_ ## nm ## _reason != NULL)          \
895
                        printf("\t* " #lbl ": %s\n",            \
896
                            pp->dyn_ ## nm ## _reason);         \
897
                else if (pp->nm != NULL)                        \
898
                        printf("\t* " #lbl ": %s\n", pp->nm);
899 3198
                MCF_DYN_REASON(Default, def);
900 3198
                MCF_DYN_REASON(Minimum, min);
901 3198
                MCF_DYN_REASON(Maximum, max);
902
#undef MCF_DYN_REASON
903 3198
                flags = pp->flags & ~DOCS_FLAGS;
904 3198
                if (pp->func == tweak_timeout)
905 286
                        flags |= TYPE_TIMEOUT;
906 3198
                if (flags) {
907 1768
                        printf("\t* Flags: ");
908 1768
                        q = "";
909
910 1768
                        if (flags & TYPE_TIMEOUT) {
911 286
                                printf("%stimeout", q);
912 286
                                q = ", ";
913 286
                        }
914 1768
                        if (flags & DELAYED_EFFECT) {
915 468
                                printf("%sdelayed", q);
916 468
                                q = ", ";
917 468
                        }
918 1768
                        if (flags & MUST_RESTART) {
919 208
                                printf("%smust_restart", q);
920 208
                                q = ", ";
921 208
                        }
922 1768
                        if (flags & MUST_RELOAD) {
923 52
                                printf("%smust_reload", q);
924 52
                                q = ", ";
925 52
                        }
926 1768
                        if (flags & EXPERIMENTAL) {
927 780
                                printf("%sexperimental", q);
928 780
                                q = ", ";
929 780
                        }
930 1768
                        if (flags & WIZARD) {
931 156
                                printf("%swizard", q);
932 156
                                q = ", ";
933 156
                        }
934 1768
                        if (flags & ONLY_ROOT) {
935 0
                                printf("%sonly_root", q);
936 0
                                q = ", ";
937 0
                        }
938 1768
                        if (flags & OBJ_STICKY) {
939 104
                                printf("%sobj_sticky", q);
940 104
                                q = ", ";
941 104
                        }
942 1768
                        printf("\n");
943 1768
                }
944 3198
                printf("\n");
945 3198
                p = pp->descr;
946 12714
                while (*p != '\0') {
947 9516
                        q = strchr(p, '\n');
948 9516
                        if (q == NULL)
949 3198
                                q = strchr(p, '\0');
950 9516
                        t1 = strchr(p, '\t');
951 9516
                        if (t1 != NULL && t1 < q) {
952 1430
                                t2 = strchr(t1 + 1, '\t');
953 1430
                                AN(t2);
954 1430
                                printf("\n\t*");
955 1430
                                (void)fwrite(t1 + 1, (t2 - 1) - t1, 1, stdout);
956 1430
                                printf("*\n\t\t");
957 1430
                                p = t2 + 1;
958 1430
                        }
959 9516
                        (void)fwrite(p, q - p, 1, stdout);
960 9516
                        p = q;
961 9516
                        if (*p == '\n') {
962 6318
                                printf("\n");
963 6318
                                p++;
964 6318
                        }
965 9516
                        continue;
966
                }
967 3198
                printf("\n\n");
968 3198
        }
969 26
}