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 1848680
mcf_findpar(const char *name)
125
{
126
        struct plist *pl;
127
128 1848680
        AN(name);
129 175137400
        VTAILQ_FOREACH(pl, &phead, list)
130 175137240
                if (!strcmp(pl->spec->name, name))
131 1848520
                        return (pl->spec);
132 160
        return (NULL);
133 1848680
}
134
135
static void
136 5062680
mcf_addpar(struct parspec *ps)
137
{
138
        struct plist *pl, *pl2;
139
        int i;
140
141 5062680
        ALLOC_OBJ(pl, PLIST_MAGIC);
142 5062680
        AN(pl);
143 5062680
        pl->spec = ps;
144 241732680
        VTAILQ_FOREACH(pl2, &phead, list) {
145 240662520
                i = strcmp(pl2->spec->name, pl->spec->name);
146 240662520
                if (i == 0) {
147 0
                        fprintf(stderr, "Duplicate param: %s\n", ps->name);
148 0
                        exit(4);
149 240662520
                } else if (i > 0) {
150 3992520
                        VTAILQ_INSERT_BEFORE(pl2, pl, list);
151 3992520
                        return;
152
                }
153 236670000
        }
154 1070160
        VTAILQ_INSERT_TAIL(&phead, pl, list);
155 5062680
}
156
157
/*--------------------------------------------------------------------
158
 * Wrap the text nicely.
159
 * Lines are allowed to contain two TABS and we render that as a table
160
 * taking the width of the first column into account.
161
 */
162
163
static void
164 102080
mcf_wrap_line(struct cli *cli, const char *b, const char *e, int tabs, int m0)
165
{
166 102080
        int n, hadtabs = 0;
167
        const char *w;
168
169 102080
        n = m0;
170 102080
        VCLI_Out(cli, "%*s", n, "");
171
172 1504240
        while (b < e) {
173 1453640
                if (!isspace(*b)) {
174 699440
                        VCLI_Out(cli, "%c", *b);
175 699440
                        b++;
176 699440
                        n++;
177 1453640
                } else if (*b == '\t') {
178 19760
                        assert(tabs);
179 19760
                        assert(hadtabs < 2);
180 19760
                        do {
181 156080
                                VCLI_Out(cli, " ");
182 156080
                                n++;
183 156080
                        } while ((n % tabs) != (m0 + tab0) % tabs);
184 19760
                        b++;
185 19760
                        hadtabs++;
186 19760
                } else {
187 734440
                        assert (*b == ' ');
188 4401560
                        for (w = b + 1; w < e; w++)
189 4350960
                                if (isspace(*w))
190 683840
                                        break;
191 734440
                        if (n + (w - b) < wrap_at) {
192 682960
                                VCLI_Out(cli, "%.*s", (int)(w - b), b);
193 682960
                                n += (w - b);
194 682960
                                b = w;
195 682960
                        } else {
196 51480
                                assert(hadtabs == 0 || hadtabs == 2);
197 51480
                                VCLI_Out(cli, "\n");
198 102960
                                mcf_wrap_line(cli, b + 1, e, 0,
199 51480
                                    hadtabs ? m0 + tab0 + tabs : m0);
200 51480
                                return;
201
                        }
202
                }
203
        }
204 50600
        assert(b == e);
205 102080
}
206
207
static void
208 25400
mcf_wrap(struct cli *cli, const char *text)
209
{
210
        const char *p, *q, *r;
211 25400
        int tw = 0;
212
213 25400
        if (strchr(text, '\t') != NULL) {
214 11160
                for (p = text; *p != '\0'; ) {
215 11160
                        q = strstr(p, "\n\t");
216 11160
                        if (q == NULL)
217 1280
                                break;
218 9880
                        q += 2;
219 9880
                        r = strchr(q, '\t');
220 9880
                        if (r == NULL) {
221 0
                                fprintf(stderr,
222 0
                                    "LINE with just one TAB: <%s>\n", text);
223 0
                                exit(4);
224
                        }
225 9880
                        if (r - q > tw)
226 3520
                                tw = r - q;
227 9880
                        p = q;
228
                }
229 1280
                tw += 2;
230 1280
                if (tw < 20)
231 1080
                        tw = 20;
232 1280
        }
233
234 129880
        for (p = text; *p != '\0'; ) {
235 104480
                if (*p == '\n') {
236 53880
                        VCLI_Out(cli, "\n");
237 53880
                        p++;
238 53880
                        continue;
239
                }
240 50600
                q = strchr(p, '\n');
241 50600
                if (q == NULL)
242 25400
                        q = strchr(p, '\0');
243 50600
                mcf_wrap_line(cli, p, q, tw, margin1);
244 50600
                p = q;
245
        }
246 25400
}
247
248
/*--------------------------------------------------------------------*/
249
250
static void v_matchproto_(cli_func_t)
251 1320
mcf_param_show(struct cli *cli, const char * const *av, void *priv)
252
{
253
        struct plist *pl;
254
        const struct parspec *pp, *pa;
255 1320
        int n, lfmt = 0, chg = 0;
256
        struct vsb *vsb;
257 1320
        const char *show = NULL;
258
259 1320
        (void)priv;
260
261 2400
        for (n = 2; av[n] != NULL; n++) {
262 1120
                if (strcmp(av[n], "-l") == 0) {
263 120
                        lfmt = 1;
264 120
                        continue;
265
                }
266 1000
                if (strcmp(av[n], "changed") == 0) {
267 40
                        chg = 1;
268 40
                        continue;
269
                }
270 960
                if (show != NULL) {
271 40
                        VCLI_SetResult(cli, CLIS_TOOMANY);
272 40
                        VCLI_Out(cli, "Too many parameters");
273 40
                        return;
274
                }
275 920
                show = av[n];
276 920
                lfmt = 1;
277 920
        }
278
279 1280
        vsb = VSB_new_auto();
280 1280
        AN(vsb);
281
282 1280
        n = 0;
283 158720
        VTAILQ_FOREACH(pl, &phead, list) {
284 157440
                pp = pl->spec;
285 157440
                if (lfmt && show != NULL && strcmp(pp->name, show))
286 107400
                        continue;
287 50200
                if (pp->func == tweak_alias &&
288 1760
                    (show == NULL || strcmp(pp->name, show)))
289 1600
                        continue;
290 48440
                n++;
291
292 48440
                VSB_clear(vsb);
293 48440
                if (pp->func(vsb, pp, NULL))
294 0
                        VCLI_SetResult(cli, CLIS_PARAM);
295 48440
                AZ(VSB_finish(vsb));
296 48440
                if (chg && pp->def != NULL && !strcmp(pp->def, VSB_data(vsb)))
297 4280
                        continue;
298
299 44160
                if (pp->flags & NOT_IMPLEMENTED) {
300 0
                        if (lfmt) {
301 0
                                VCLI_Out(cli, "%s\n", pp->name);
302 0
                                VCLI_Out(cli, "%-*sNot available", margin1, " ");
303 0
                        } else {
304 0
                                VCLI_Out(cli, "%-*s-", margin2, pp->name);
305
                        }
306 0
                } else {
307 44160
                        if (lfmt) {
308 15120
                                VCLI_Out(cli, "%s\n", pp->name);
309 15120
                                VCLI_Out(cli, "%-*sValue is: ", margin1, " ");
310 15120
                        } else {
311 29040
                                VCLI_Out(cli, "%-*s", margin2, pp->name);
312
                        }
313
314 44160
                        VCLI_Out(cli, "%s", VSB_data(vsb));
315 44160
                        if (pp->units != NULL && *pp->units != '\0')
316 35240
                                VCLI_Out(cli, " [%s]", pp->units);
317 44160
                        if (pp->def != NULL && !strcmp(pp->def, VSB_data(vsb)))
318 39480
                                VCLI_Out(cli, " (default)");
319
                }
320 44160
                VCLI_Out(cli, "\n");
321
322 44160
                if (lfmt && pp->func == tweak_alias) {
323 160
                        pa = TRUST_ME(pp->priv);
324 320
                        VCLI_Out(cli, "%-*sAlias of: %s\n",
325 160
                            margin1, " ", pa->name);
326 160
                }
327 44160
                if (lfmt && pp->flags & NOT_IMPLEMENTED) {
328 0
                        VCLI_Out(cli, "\n");
329 0
                        mcf_wrap(cli, NOT_IMPLEMENTED_TEXT);
330 0
                        VCLI_Out(cli, "\n\n");
331 44160
                } else if (lfmt) {
332 15120
                        if (pp->def != NULL && strcmp(pp->def, VSB_data(vsb)))
333 3360
                                VCLI_Out(cli, "%-*sDefault is: %s\n",
334 1680
                                    margin1, "", pp->def);
335 15120
                        if (pp->min != NULL)
336 23440
                                VCLI_Out(cli, "%-*sMinimum is: %s\n",
337 11720
                                    margin1, "", pp->min);
338 15120
                        if (pp->max != NULL)
339 6960
                                VCLI_Out(cli, "%-*sMaximum is: %s\n",
340 3480
                                    margin1, "", pp->max);
341 15120
                        VCLI_Out(cli, "\n");
342 15120
                        mcf_wrap(cli, pp->descr);
343 15120
                        if (pp->func == tweak_timeout)
344 1600
                                mcf_wrap(cli, TYPE_TIMEOUT_TEXT);
345 15120
                        if (pp->flags & OBJ_STICKY)
346 400
                                mcf_wrap(cli, OBJ_STICKY_TEXT);
347 15120
                        if (pp->flags & DELAYED_EFFECT)
348 2160
                                mcf_wrap(cli, DELAYED_EFFECT_TEXT);
349 15120
                        if (pp->flags & EXPERIMENTAL)
350 3600
                                mcf_wrap(cli, EXPERIMENTAL_TEXT);
351 15120
                        if (pp->flags & MUST_RELOAD)
352 280
                                mcf_wrap(cli, MUST_RELOAD_TEXT);
353 15120
                        if (pp->flags & MUST_RESTART)
354 960
                                mcf_wrap(cli, MUST_RESTART_TEXT);
355 15120
                        if (pp->flags & WIZARD)
356 720
                                mcf_wrap(cli, WIZARD_TEXT);
357 15120
                        if (pp->flags & PROTECTED)
358 40
                                mcf_wrap(cli, PROTECTED_TEXT);
359 15120
                        if (pp->flags & ONLY_ROOT)
360 0
                                mcf_wrap(cli, ONLY_ROOT_TEXT);
361 15120
                        if (pp->flags & BUILD_OPTIONS)
362 520
                                mcf_wrap(cli, BUILD_OPTIONS_TEXT);
363 15120
                        VCLI_Out(cli, "\n\n");
364 15120
                }
365 44160
        }
366 1280
        if (show != NULL && n == 0) {
367 40
                VCLI_SetResult(cli, CLIS_PARAM);
368 40
                VCLI_Out(cli, "Unknown parameter \"%s\".", show);
369 40
        }
370 1280
        VSB_destroy(&vsb);
371 1320
}
372
373
static inline void
374 49520
mcf_json_key_valstr(struct cli *cli, const char *key, const char *val)
375
{
376 49520
        VCLI_Out(cli, "\"%s\": ", key);
377 49520
        VCLI_JSON_str(cli, val);
378 49520
        VCLI_Out(cli, ",\n");
379 49520
}
380
381
static void v_matchproto_(cli_func_t)
382 600
mcf_param_show_json(struct cli *cli, const char * const *av, void *priv)
383
{
384 600
        int n, comma = 0, chg = 0;
385
        struct plist *pl;
386
        const struct parspec *pp, *pa;
387
        struct vsb *vsb, *def;
388 600
        const char *show = NULL, *sep;
389
390 600
        (void)priv;
391
392 1680
        for (int i = 2; av[i] != NULL; i++) {
393 1160
                if (strcmp(av[i], "-l") == 0) {
394 40
                        VCLI_SetResult(cli, CLIS_PARAM);
395 40
                        VCLI_Out(cli, "-l not permitted with param.show -j");
396 40
                        return;
397
                }
398 1120
                if (strcmp(av[i], "changed") == 0) {
399 40
                        chg = 1;
400 40
                        continue;
401
                }
402 1080
                if (strcmp(av[i], "-j") == 0)
403 600
                        continue;
404 480
                if (show != NULL) {
405 40
                        VCLI_SetResult(cli, CLIS_TOOMANY);
406 40
                        VCLI_Out(cli, "Too many parameters");
407 40
                        return;
408
                }
409 440
                show = av[i];
410 440
        }
411
412 520
        vsb = VSB_new_auto();
413 520
        AN(vsb);
414 520
        def = VSB_new_auto();
415 520
        AN(def);
416
417 520
        n = 0;
418 520
        VCLI_JSON_begin(cli, 2, av);
419 520
        VCLI_Out(cli, ",\n");
420 64480
        VTAILQ_FOREACH(pl, &phead, list) {
421 63960
                pp = pl->spec;
422 63960
                if (show != NULL && strcmp(pp->name, show) != 0)
423 48840
                        continue;
424 15160
                if (pp->func == tweak_alias &&
425 520
                    (show == NULL || strcmp(pp->name, show)))
426 480
                        continue;
427 14640
                n++;
428
429 14640
                VSB_clear(vsb);
430 14640
                if (pp->func(vsb, pp, JSON_FMT))
431 0
                        VCLI_SetResult(cli, CLIS_PARAM);
432 14640
                AZ(VSB_finish(vsb));
433 14640
                VSB_clear(def);
434 14640
                if (pp->func(def, pp, NULL))
435 0
                        VCLI_SetResult(cli, CLIS_PARAM);
436 14640
                AZ(VSB_finish(def));
437 14640
                if (chg && pp->def != NULL && !strcmp(pp->def, VSB_data(def)))
438 4280
                        continue;
439
440 10360
                VCLI_Out(cli, "%s", comma ? ",\n" : "");
441 10360
                comma++;
442 10360
                VCLI_Out(cli, "{\n");
443 10360
                VSB_indent(cli->sb, 2);
444 10360
                mcf_json_key_valstr(cli, "name", pp->name);
445 10360
                if (pp->func == tweak_alias) {
446 40
                        pa = TRUST_ME(pp->priv);
447 40
                        mcf_json_key_valstr(cli, "alias", pa->name);
448 40
                }
449 10360
                if (pp->flags & NOT_IMPLEMENTED) {
450 0
                        VCLI_Out(cli, "\"implemented\": false\n");
451 0
                        VSB_indent(cli->sb, -2);
452 0
                        VCLI_Out(cli, "}");
453 0
                        continue;
454
                }
455 10360
                VCLI_Out(cli, "\"implemented\": true,\n");
456 10360
                VCLI_Out(cli, "\"value\": %s,\n", VSB_data(vsb));
457 10360
                if (pp->units != NULL && *pp->units != '\0')
458 8080
                        mcf_json_key_valstr(cli, "units", pp->units);
459
460 10360
                if (pp->def != NULL)
461 10320
                        mcf_json_key_valstr(cli, "default", pp->def);
462 10360
                if (pp->min != NULL)
463 7880
                        mcf_json_key_valstr(cli, "minimum", pp->min);
464 10360
                if (pp->max != NULL)
465 2480
                        mcf_json_key_valstr(cli, "maximum", pp->max);
466 10360
                mcf_json_key_valstr(cli, "description", pp->descr);
467
468 10360
                VCLI_Out(cli, "\"flags\": [");
469 10360
                VSB_indent(cli->sb, 2);
470 10360
                sep = "";
471
472
#define flag_out(flag, string) do {                                     \
473
                        if (pp->flags & flag) {                         \
474
                                VCLI_Out(cli, "%s\n", sep);             \
475
                                VCLI_Out(cli, "\"%s\"", #string);       \
476
                                sep = ",";                              \
477
                        }                                               \
478
                } while(0)
479
480 10360
                flag_out(OBJ_STICKY, obj_sticky);
481 10360
                flag_out(DELAYED_EFFECT, delayed_effect);
482 10360
                flag_out(EXPERIMENTAL, experimental);
483 10360
                flag_out(MUST_RELOAD, must_reload);
484 10360
                flag_out(MUST_RESTART, must_restart);
485 10360
                flag_out(WIZARD, wizard);
486 10360
                flag_out(PROTECTED, protected);
487 10360
                flag_out(ONLY_ROOT, only_root);
488 10360
                flag_out(BUILD_OPTIONS, build_options);
489
490
#undef flag_out
491
492 10360
                if (pp->flags)
493 5240
                        VCLI_Out(cli, "\n");
494 10360
                VSB_indent(cli->sb, -2);
495 10360
                VCLI_Out(cli, "]\n");
496 10360
                VSB_indent(cli->sb, -2);
497 10360
                VCLI_Out(cli, "}");
498 10360
        }
499 520
        VCLI_JSON_end(cli);
500 520
        if (show != NULL && n == 0) {
501 40
                VSB_clear(cli->sb);
502 40
                VCLI_SetResult(cli, CLIS_PARAM);
503 40
                VCLI_Out(cli, "Unknown parameter \"%s\".", show);
504 40
        }
505 520
        VSB_destroy(&vsb);
506 520
        VSB_destroy(&def);
507 600
}
508
509
/*--------------------------------------------------------------------
510
 * Mark parameters as protected
511
 */
512
513
void
514 40
MCF_ParamProtect(struct cli *cli, const char *args)
515
{
516
        char **av;
517
        struct parspec *pp;
518
        int i;
519
520 40
        av = VAV_Parse(args, NULL, ARGV_COMMA);
521 40
        if (av[0] != NULL) {
522 0
                VCLI_Out(cli, "Parse error: %s", av[0]);
523 0
                VCLI_SetResult(cli, CLIS_PARAM);
524 0
                VAV_Free(av);
525 0
                return;
526
        }
527 80
        for (i = 1; av[i] != NULL; i++) {
528 40
                pp = mcf_findpar(av[i]);
529 40
                if (pp == NULL) {
530 0
                        VCLI_Out(cli, "Unknown parameter %s", av[i]);
531 0
                        VCLI_SetResult(cli, CLIS_PARAM);
532 0
                        VAV_Free(av);
533 0
                        return;
534
                }
535 40
                pp->flags |= PROTECTED;
536 40
        }
537 40
        VAV_Free(av);
538 40
}
539
540
/*--------------------------------------------------------------------*/
541
542
void
543 368480
MCF_ParamSet(struct cli *cli, const char *param, const char *val)
544
{
545
        const struct parspec *pp;
546
547 368480
        pp = mcf_findpar(param);
548 368480
        if (pp == NULL) {
549 160
                VCLI_SetResult(cli, CLIS_PARAM);
550 160
                VCLI_Out(cli, "Unknown parameter \"%s\".", param);
551 160
                return;
552
        }
553 368320
        if (pp->flags & NOT_IMPLEMENTED) {
554 0
                VCLI_SetResult(cli, CLIS_CANT);
555 0
                VCLI_Out(cli,
556
                    "parameter \"%s\" is not available on this platform.",
557 0
                    param
558
                );
559 0
                return;
560
        }
561 368320
        if (pp->flags & PROTECTED) {
562 40
                VCLI_SetResult(cli, CLIS_AUTH);
563 40
                VCLI_Out(cli, "parameter \"%s\" is protected.", param);
564 40
                return;
565
        }
566 368280
        if (!val)
567 1400
                val = pp->def;
568 368280
        if (pp->func(cli->sb, pp, val))
569 1160
                VCLI_SetResult(cli, CLIS_PARAM);
570
571 368280
        if (cli->result == CLIS_OK && heritage.param != NULL)
572 13560
                *heritage.param = mgt_param;
573
574 368280
        if (cli->result != CLIS_OK) {
575 2320
                VCLI_Out(cli, "\n(attempting to set param '%s' to '%s')",
576 1160
                    pp->name, val);
577 368280
        } else if (MCH_Running() && pp->flags & MUST_RESTART) {
578 0
                VCLI_Out(cli,
579
                    "\nChange will take effect when child is restarted");
580 367120
        } else if (pp->flags & MUST_RELOAD) {
581 80
                VCLI_Out(cli,
582
                    "\nChange will take effect when VCL script is reloaded");
583 80
        }
584 368480
}
585
586
587
/*--------------------------------------------------------------------*/
588
589
static void v_matchproto_(cli_func_t)
590 21080
mcf_param_set(struct cli *cli, const char * const *av, void *priv)
591
{
592
593 21080
        (void)priv;
594 21080
        MCF_ParamSet(cli, av[2], av[3]);
595 21080
}
596
597
static void v_matchproto_(cli_func_t)
598 240
mcf_param_set_json(struct cli *cli, const char * const *av, void *priv)
599
{
600 240
        const char *const avs[] = { av[0], av[1], av[2], av[3], NULL };
601
602 240
        MCF_ParamSet(cli, av[3], av[4]);
603 240
        if (cli->result == CLIS_OK)
604 240
                mcf_param_show_json(cli, avs, priv);
605 240
}
606
607
/*--------------------------------------------------------------------*/
608
609
static void v_matchproto_(cli_func_t)
610 1360
mcf_param_reset(struct cli *cli, const char * const *av, void *priv)
611
{
612
613 1360
        (void)priv;
614 1360
        MCF_ParamSet(cli, av[2], NULL);
615 1360
}
616
617
/*--------------------------------------------------------------------
618
 * Initialize parameters and sort by name.
619
 */
620
621
static struct parspec mgt_parspec[] = {
622
#define PARAM_ALL
623
#define PARAM_PRE {
624
#define PARAM(typ, fld, nm, ...) #nm, __VA_ARGS__
625
#define PARAM_POST },
626
#include "tbl/params.h"
627
        { NULL, NULL, NULL }
628
};
629
630
static void
631 41160
mcf_init_params(void)
632
{
633
        struct parspec *pp;
634
        const char *s;
635
636 5103840
        for (pp = mgt_parspec; pp->name != NULL; pp++) {
637 5062680
                AN(pp->func);
638 5062680
                s = strchr(pp->descr, '\0');
639 5062680
                if (isspace(s[-1])) {
640 0
                        fprintf(stderr,
641 0
                            "Param->descr has trailing space: %s\n", pp->name);
642 0
                        exit(4);
643
                }
644 5062680
                mcf_addpar(pp);
645 5062680
                margin2 = vmax_t(int, margin2, strlen(pp->name) + 1);
646 5062680
        }
647 41160
}
648
649
/*--------------------------------------------------------------------
650
 * Wash a min/max/default value
651
 */
652
653
static void
654 3497000
mcf_dyn_vsb(enum mcf_which_e which, struct parspec *pp, struct vsb *vsb)
655
{
656
657 3497000
        switch (which) {
658
        case MCF_DEFAULT:
659 1111320
                REPLACE(pp->dyn_def, VSB_data(vsb));
660 1111320
                pp->def = pp->dyn_def;
661 1111320
                break;
662
        case MCF_MINIMUM:
663 1480240
                REPLACE(pp->dyn_min, VSB_data(vsb));
664 1480240
                pp->min = pp->dyn_min;
665 1480240
                break;
666
        case MCF_MAXIMUM:
667 905440
                REPLACE(pp->dyn_max, VSB_data(vsb));
668 905440
                pp->max = pp->dyn_max;
669 905440
                break;
670
        default:
671 0
                WRONG("bad 'which'");
672 0
        }
673 3497000
}
674
675
static void
676 10125360
mcf_wash_param(struct cli *cli, struct parspec *pp, enum mcf_which_e which,
677
    const char *name, struct vsb *vsb)
678
{
679
        const char *val;
680
        int err;
681
682 10125360
        switch (which) {
683 5062680
        case MCF_DEFAULT: val = pp->def; break;
684 3869040
        case MCF_MINIMUM: val = pp->min; break;
685 1193640
        case MCF_MAXIMUM: val = pp->max; break;
686
        default:
687 0
                WRONG("bad 'which'");
688 0
        }
689 10125360
        AN(val);
690
691 10125360
        if (pp->func == tweak_alias) {
692 164640
                assert(which == MCF_DEFAULT);
693 164640
                pp->priv = mcf_findpar(pp->def);
694 164640
                pp->def = NULL;
695 164640
                return;
696
        }
697
698 9960720
        VSB_clear(vsb);
699 19921440
        VSB_printf(vsb, "FAILED to set %s for param %s: %s\n",
700 9960720
            name, pp->name, val);
701 9960720
        err = pp->func(vsb, pp, val);
702 9960720
        AZ(VSB_finish(vsb));
703 9960720
        if (err) {
704 0
                VCLI_Out(cli, "%s\n", VSB_data(vsb));
705 0
                VCLI_SetResult(cli, CLIS_CANT);
706 0
                return;
707
        }
708 9960720
        VSB_clear(vsb);
709 9960720
        err = pp->func(vsb, pp, NULL);
710 9960720
        AZ(err);
711 9960720
        AZ(VSB_finish(vsb));
712 9960720
        if (strcmp(val, VSB_data(vsb)))
713 2181480
                mcf_dyn_vsb(which, pp, vsb);
714 10125360
}
715
716
/*--------------------------------------------------------------------*/
717
718
static struct cli_proto cli_params[] = {
719
        { CLICMD_PARAM_SHOW,    "", mcf_param_show, mcf_param_show_json },
720
        { CLICMD_PARAM_SET,     "", mcf_param_set, mcf_param_set_json },
721
        { CLICMD_PARAM_RESET,   "", mcf_param_reset, mcf_param_set_json },
722
        { NULL }
723
};
724
725
/*--------------------------------------------------------------------
726
 * Configure the parameters
727
 */
728
729
void
730 41160
MCF_InitParams(struct cli *cli)
731
{
732
        struct plist *pl;
733
        struct parspec *pp;
734
        struct vsb *vsb;
735
        ssize_t def, low;
736
737 41160
        mcf_init_params();
738 41160
        MCF_TcpParams();
739
740 41160
        def = 80 * 1024;
741
742
        if (sizeof(void *) < 8) {               /*lint !e506 !e774  */
743
                /*
744
                 * Adjust default parameters for 32 bit systems to conserve
745
                 * VM space.
746
                 *
747
                 * Reflect changes in doc/sphinx/reference/varnishd.rst !
748
                 */
749
                MCF_ParamConf(MCF_DEFAULT, "workspace_client", "24k");
750
                MCF_ParamConf(MCF_DEFAULT, "workspace_backend", "20k");
751
                MCF_ParamConf(MCF_DEFAULT, "http_resp_size", "8k");
752
                MCF_ParamConf(MCF_DEFAULT, "http_req_size", "12k");
753
                MCF_ParamConf(MCF_DEFAULT, "gzip_buffer", "4k");
754
                MCF_ParamConf(MCF_DEFAULT, "vsl_buffer", "4k");
755
                MCF_ParamConf(MCF_MAXIMUM, "vsl_space", "1G");
756
                def = 64 * 1024;
757
        }
758
759 41160
        low = sysconf(_SC_THREAD_STACK_MIN);
760 41160
        MCF_ParamConf(MCF_MINIMUM, "thread_pool_stack", "%jdb", (intmax_t)low);
761
762
#if defined(ENABLE_SANITIZER) || defined(ENABLE_COVERAGE)
763 41160
        def = 192 * 1024;
764
#endif
765
766 41160
        if (def < low)
767 0
                def = low;
768 41160
        MCF_ParamConf(MCF_DEFAULT, "thread_pool_stack", "%jdb", (intmax_t)def);
769
770
#if !defined(MAX_THREAD_POOLS)
771
#  define MAX_THREAD_POOLS 32
772
#endif
773
774 41160
        MCF_ParamConf(MCF_MAXIMUM, "thread_pools", "%d", MAX_THREAD_POOLS);
775
776
#if !defined(HAVE_ACCEPT_FILTERS) || defined(__linux)
777
        MCF_ParamConf(MCF_DEFAULT, "accept_filter", "off");
778
#endif
779
780 41160
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_params);
781
782 41160
        vsb = VSB_new_auto();
783 41160
        AN(vsb);
784
785 5103840
        VTAILQ_FOREACH(pl, &phead, list) {
786 5062680
                pp = pl->spec;
787
788 5062680
                if (pp->flags & NOT_IMPLEMENTED)
789 0
                        continue;
790 5062680
                if (pp->min != NULL)
791 3869040
                        mcf_wash_param(cli, pp, MCF_MINIMUM, "minimum", vsb);
792 5062680
                if (pp->max != NULL)
793 1193640
                        mcf_wash_param(cli, pp, MCF_MAXIMUM, "maximum", vsb);
794 5062680
                AN(pp->def);
795 5062680
                mcf_wash_param(cli, pp, MCF_DEFAULT, "default", vsb);
796 5062680
        }
797 41160
        VSB_destroy(&vsb);
798
799 41160
        AN(mgt_cc_cmd);
800 41160
        REPLACE(mgt_cc_cmd_def, mgt_cc_cmd);
801 41160
}
802
803
/*--------------------------------------------------------------------*/
804
805
void
806 1315520
MCF_ParamConf(enum mcf_which_e which, const char * const param,
807
    const char *fmt, ...)
808
{
809
        struct parspec *pp;
810
        struct vsb *vsb;
811
        va_list ap;
812
813 1315520
        pp = mcf_findpar(param);
814 1315520
        AN(pp);
815 1315520
        vsb = VSB_new_auto();
816 1315520
        AN(vsb);
817 1315520
        va_start(ap, fmt);
818 1315520
        VSB_vprintf(vsb, fmt, ap);
819 1315520
        va_end(ap);
820 1315520
        AZ(VSB_finish(vsb));
821 1315520
        mcf_dyn_vsb(which, pp, vsb);
822 1315520
        VSB_destroy(&vsb);
823 1315520
}
824
825
/*--------------------------------------------------------------------*/
826
827
void
828 80
MCF_DumpRstParam(void)
829
{
830
        struct plist *pl;
831
        const struct parspec *pp;
832
        const char *p, *q, *t1, *t2;
833
        unsigned flags;
834
        size_t z;
835
836 80
        printf("\n.. The following is the autogenerated "
837
            "output from varnishd -x parameter\n\n");
838 9920
        VTAILQ_FOREACH(pl, &phead, list) {
839 9840
                pp = pl->spec;
840 9840
                if (!strcmp("deprecated_dummy", pp->name))
841 80
                    continue;
842 9760
                printf(".. _ref_param_%s:\n\n", pp->name);
843 9760
                printf("%s\n", pp->name);
844 156080
                for (z = 0; z < strlen(pp->name); z++)
845 146320
                        printf("~");
846 9760
                printf("\n");
847
848 9760
                if (pp->flags && pp->flags & PLATFORM_DEPENDENT)
849 400
                        printf("\n%s\n\n", PLATFORM_DEPENDENT_TEXT);
850
851 9760
                if (pp->flags && pp->flags & BUILD_OPTIONS)
852 320
                        printf("\n%s\n\n", BUILD_OPTIONS_TEXT);
853
854 9760
                if (pp->units != NULL && *pp->units != '\0')
855 7680
                        printf("\t* Units: %s\n", pp->units);
856
#define MCF_DYN_REASON(lbl, nm)                                 \
857
                if (pp->dyn_ ## nm ## _reason != NULL)          \
858
                        printf("\t* " #lbl ": %s\n",            \
859
                            pp->dyn_ ## nm ## _reason);         \
860
                else if (pp->nm != NULL)                        \
861
                        printf("\t* " #lbl ": %s\n", pp->nm);
862 9760
                MCF_DYN_REASON(Default, def);
863 9760
                MCF_DYN_REASON(Minimum, min);
864 9760
                MCF_DYN_REASON(Maximum, max);
865
#undef MCF_DYN_REASON
866 9760
                flags = pp->flags & ~DOCS_FLAGS;
867 9760
                if (pp->func == tweak_timeout)
868 880
                        flags |= TYPE_TIMEOUT;
869 9760
                if (flags) {
870 5360
                        printf("\t* Flags: ");
871 5360
                        q = "";
872
873 5360
                        if (flags & TYPE_TIMEOUT) {
874 880
                                printf("%stimeout", q);
875 880
                                q = ", ";
876 880
                        }
877 5360
                        if (flags & DELAYED_EFFECT) {
878 1440
                                printf("%sdelayed", q);
879 1440
                                q = ", ";
880 1440
                        }
881 5360
                        if (flags & MUST_RESTART) {
882 640
                                printf("%smust_restart", q);
883 640
                                q = ", ";
884 640
                        }
885 5360
                        if (flags & MUST_RELOAD) {
886 160
                                printf("%smust_reload", q);
887 160
                                q = ", ";
888 160
                        }
889 5360
                        if (flags & EXPERIMENTAL) {
890 2400
                                printf("%sexperimental", q);
891 2400
                                q = ", ";
892 2400
                        }
893 5360
                        if (flags & WIZARD) {
894 480
                                printf("%swizard", q);
895 480
                                q = ", ";
896 480
                        }
897 5360
                        if (flags & ONLY_ROOT) {
898 0
                                printf("%sonly_root", q);
899 0
                                q = ", ";
900 0
                        }
901 5360
                        if (flags & OBJ_STICKY) {
902 240
                                printf("%sobj_sticky", q);
903 240
                                q = ", ";
904 240
                        }
905 5360
                        printf("\n");
906 5360
                }
907 9760
                printf("\n");
908 9760
                p = pp->descr;
909 38800
                while (*p != '\0') {
910 29040
                        q = strchr(p, '\n');
911 29040
                        if (q == NULL)
912 9760
                                q = strchr(p, '\0');
913 29040
                        t1 = strchr(p, '\t');
914 29040
                        if (t1 != NULL && t1 < q) {
915 4400
                                t2 = strchr(t1 + 1, '\t');
916 4400
                                AN(t2);
917 4400
                                printf("\n\t*");
918 4400
                                (void)fwrite(t1 + 1, (t2 - 1) - t1, 1, stdout);
919 4400
                                printf("*\n\t\t");
920 4400
                                p = t2 + 1;
921 4400
                        }
922 29040
                        (void)fwrite(p, q - p, 1, stdout);
923 29040
                        p = q;
924 29040
                        if (*p == '\n') {
925 19280
                                printf("\n");
926 19280
                                p++;
927 19280
                        }
928 29040
                        continue;
929
                }
930 9760
                printf("\n\n");
931 9760
        }
932 80
}