varnish-cache/bin/varnishd/mgt/mgt_param_tweak.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
 * Functions for tweaking parameters
31
 *
32
 */
33
34
#include "config.h"
35
36
#include <limits.h>
37
#include <math.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
42
#include "mgt/mgt.h"
43
44
#include "mgt/mgt_param.h"
45
#include "storage/storage.h"
46
#include "vav.h"
47
#include "vnum.h"
48
#include "vsl_priv.h"
49
50
const char * const JSON_FMT = (const char *)&JSON_FMT;
51
52
/*--------------------------------------------------------------------
53
 * Generic handling of double typed parameters
54
 */
55
56
typedef double parse_double_f(const char *, const char **);
57
58
static double
59 735600
parse_decimal(const char *p, const char **err)
60
{
61
        double v;
62
63 735600
        v = SF_Parse_Decimal(&p, 0, err);
64 735600
        if (errno == 0 && *p != '\0') {
65 40
                errno = EINVAL;
66 40
                *err = "Invalid number";
67 40
        }
68 735600
        return (v);
69
}
70
71
static int
72 7416440
tweak_generic_double(struct vsb *vsb, const char *arg, const struct parspec *pp,
73
    parse_double_f parse, const char *fmt)
74
{
75 7416440
        volatile double u, minv = VRT_DECIMAL_MIN, maxv = VRT_DECIMAL_MAX;
76 7416440
        volatile double *dest = pp->priv;
77
        const char *err;
78
79 7416440
        if (arg != NULL && arg != JSON_FMT) {
80 3759240
                if (pp->min != NULL) {
81 3759240
                        minv = parse(pp->min, &err);
82 3759240
                        if (errno) {
83 0
                                VSB_printf(vsb, "Min: %s (%s)\n", err, pp->min);
84 0
                                return (-1);
85
                        }
86 3759240
                }
87 3759240
                if (pp->max != NULL) {
88 1225160
                        maxv = parse(pp->max, &err);
89 1225160
                        if (errno) {
90 0
                                VSB_printf(vsb, "Max: %s (%s)\n", err, pp->max);
91 0
                                return (-1);
92
                        }
93 1225160
                }
94
95 3759240
                u = parse(arg, &err);
96 3759240
                if (errno) {
97 160
                        VSB_printf(vsb, "%s (%s)\n", err, arg);
98 160
                        return (-1);
99
                }
100 3759080
                if (u < minv) {
101 80
                        VSB_printf(vsb,
102 40
                            "Must be greater or equal to %s\n", pp->min);
103 40
                        return (-1);
104
                }
105 3759040
                if (u > maxv) {
106 80
                        VSB_printf(vsb,
107 40
                            "Must be less than or equal to %s\n", pp->max);
108 40
                        return (-1);
109
                }
110 3759000
                *dest = u;
111 3759000
        } else {
112 3657200
                VSB_printf(vsb, fmt, *dest);
113
        }
114 7416200
        return (0);
115 7416440
}
116
117
/*--------------------------------------------------------------------*/
118
119
static double
120 8008040
parse_duration(const char *p, const char **err)
121
{
122
        double v, r;
123
124 8008040
        v = SF_Parse_Decimal(&p, 0, err);
125 8008040
        if (*p == '\0')
126 7354320
                return (v);
127
128 653720
        r = VNUM_duration_unit(v, p, NULL);
129 653720
        if (isnan(r)) {
130 80
                errno = EINVAL;
131 80
                *err = "Invalid duration unit";
132 80
        }
133
134 653720
        return (r);
135 8008040
}
136
137
int v_matchproto_(tweak_t)
138 1886440
tweak_timeout(struct vsb *vsb, const struct parspec *par, const char *arg)
139
{
140 1886440
        volatile double *dest = par->priv;
141
142 1886440
        if (arg != NULL && !strcmp(arg, "never")) {
143 40
                *dest = INFINITY;
144 40
                return (0);
145
        }
146
147 1886400
        if (*dest == INFINITY && arg == NULL) {
148 0
                VSB_cat(vsb, "never");
149 0
                return (0);
150
        }
151
152 1886400
        if (*dest == INFINITY && arg == JSON_FMT) {
153 0
                VSB_cat(vsb, "\"never\"");
154 0
                return (0);
155
        }
156
157 1886400
        return (tweak_generic_double(vsb, arg, par, parse_duration, "%.3f"));
158 1886440
}
159
160
int v_matchproto_(tweak_t)
161 5161800
tweak_duration(struct vsb *vsb, const struct parspec *par, const char *arg)
162
{
163
164 5161800
        return (tweak_generic_double(vsb, arg, par, parse_duration, "%.3f"));
165
}
166
167
/*--------------------------------------------------------------------*/
168
169
int v_matchproto_(tweak_t)
170 245480
tweak_double(struct vsb *vsb, const struct parspec *par, const char *arg)
171
{
172
173 245480
        return (tweak_generic_double(vsb, arg, par, parse_decimal, "%g"));
174
}
175
176
/*--------------------------------------------------------------------*/
177
178
static int
179 483800
parse_boolean(struct vsb *vsb, const char *arg)
180
{
181
182 483800
        if (!strcasecmp(arg, "off"))
183 156400
                return (0);
184 327400
        if (!strcasecmp(arg, "disable"))
185 40
                return (0);
186 327360
        if (!strcasecmp(arg, "no"))
187 40
                return (0);
188 327320
        if (!strcasecmp(arg, "false"))
189 160
                return (0);
190 327160
        if (!strcasecmp(arg, "on"))
191 326600
                return (1);
192 560
        if (!strcasecmp(arg, "enable"))
193 40
                return (1);
194 520
        if (!strcasecmp(arg, "yes"))
195 40
                return (1);
196 480
        if (!strcasecmp(arg, "true"))
197 440
                return (1);
198
199 40
        VSB_cat(vsb, "use \"on\" or \"off\"\n");
200 40
        return (-1);
201 483800
}
202
203
int v_matchproto_(tweak_t)
204 898200
tweak_boolean(struct vsb *vsb, const struct parspec *par, const char *arg)
205
{
206
        volatile unsigned *dest;
207
        int val;
208
209 898200
        dest = par->priv;
210 898200
        if (arg != NULL && arg != JSON_FMT) {
211 483800
                val = parse_boolean(vsb, arg);
212 483800
                if (val < 0)
213 40
                        return (-1);
214 483760
                *dest = val;
215 898160
        } else if (arg == JSON_FMT) {
216 1200
                VSB_printf(vsb, "%s", *dest ? "true" : "false");
217 1200
        } else {
218 413200
                VSB_printf(vsb, "%s", *dest ? "on" : "off");
219
        }
220 898160
        return (0);
221 898200
}
222
223
/*--------------------------------------------------------------------*/
224
225
static int
226 5775240
tweak_generic_uint(struct vsb *vsb, volatile unsigned *dest, const char *arg,
227
    const char *min, const char *max,
228
    const char *min_reason, const char *max_reason)
229
{
230 5775240
        unsigned u, minv = 0, maxv = 0;
231
        char *p;
232
233 5775240
        if (arg != NULL && arg != JSON_FMT) {
234 3022440
                if (min != NULL) {
235 2654040
                        p = NULL;
236 2654040
                        minv = strtoul(min, &p, 0);
237 2654040
                        if (*arg == '\0' || *p != '\0') {
238 0
                                VSB_printf(vsb, "Illegal Min: %s\n", min);
239 0
                                return (-1);
240
                        }
241 2654040
                }
242 3022440
                if (max != NULL) {
243 1101560
                        p = NULL;
244 1101560
                        maxv = strtoul(max, &p, 0);
245 1101560
                        if (*arg == '\0' || *p != '\0') {
246 0
                                VSB_printf(vsb, "Illegal Max: %s\n", max);
247 0
                                return (-1);
248
                        }
249 1101560
                }
250 3022440
                p = NULL;
251 3022440
                if (!strcasecmp(arg, "unlimited"))
252 40
                        u = UINT_MAX;
253
                else {
254 3022400
                        u = strtoul(arg, &p, 0);
255 3022400
                        if (*arg == '\0' || *p != '\0') {
256 80
                                VSB_printf(vsb, "Not a number (%s)\n", arg);
257 80
                                return (-1);
258
                        }
259
                }
260 3022360
                if (min != NULL && u < minv) {
261 120
                        VSB_printf(vsb, "Must be at least %s", min);
262 120
                        if (min_reason != NULL)
263 80
                                VSB_printf(vsb, " (%s)", min_reason);
264 120
                        VSB_putc(vsb, '\n');
265 120
                        return (-1);
266
                }
267 3022240
                if (max != NULL && u > maxv) {
268 240
                        VSB_printf(vsb, "Must be no more than %s", max);
269 240
                        if (max_reason != NULL)
270 200
                                VSB_printf(vsb, " (%s)", max_reason);
271 240
                        VSB_putc(vsb, '\n');
272 240
                        return (-1);
273
                }
274 3022000
                *dest = u;
275 5774800
        } else if (*dest == UINT_MAX && arg != JSON_FMT) {
276 200
                VSB_cat(vsb, "unlimited");
277 200
        } else {
278 2752600
                VSB_printf(vsb, "%u", *dest);
279
        }
280 5774800
        return (0);
281 5775240
}
282
283
/*--------------------------------------------------------------------*/
284
285
int v_matchproto_(tweak_t)
286 5529600
tweak_uint(struct vsb *vsb, const struct parspec *par, const char *arg)
287
{
288
        volatile unsigned *dest;
289
290 5529600
        dest = par->priv;
291 11059200
        return (tweak_generic_uint(vsb, dest, arg, par->min, par->max,
292 5529600
            par->dyn_min_reason, par->dyn_max_reason));
293
}
294
295
/*--------------------------------------------------------------------*/
296
297
static void
298 2378880
fmt_bytes(struct vsb *vsb, uintmax_t t)
299
{
300
        const char *p;
301
302 2378880
        if (t == 0 || t & 0xff) {
303 817640
                VSB_printf(vsb, "%jub", t);
304 817640
                return;
305
        }
306 2135040
        for (p = "kMGTPEZY"; *p; p++) {
307 2135040
                if (t & 0x300) {
308 205040
                        VSB_printf(vsb, "%.2f%c", t / 1024.0, *p);
309 205040
                        return;
310
                }
311 1930000
                t /= 1024;
312 1930000
                if (t & 0x0ff) {
313 1356200
                        VSB_printf(vsb, "%ju%c", t, *p);
314 1356200
                        return;
315
                }
316 573800
        }
317 0
        VSB_cat(vsb, "(bogus number)");
318 2378880
}
319
320
static int
321 4863360
tweak_generic_bytes(struct vsb *vsb, volatile ssize_t *dest, const char *arg,
322
    const char *min, const char *max)
323
{
324 4863360
        uintmax_t r, rmin = 0, rmax = 0;
325
        const char *p;
326
327 4863360
        if (arg != NULL && arg != JSON_FMT) {
328 2481600
                if (min != NULL) {
329 2481600
                        p = VNUM_2bytes(min, &rmin, 0);
330 2481600
                        if (p != NULL) {
331 0
                                VSB_printf(vsb, "Invalid min-val: %s\n", min);
332 0
                                return (-1);
333
                        }
334 2481600
                }
335 2481600
                if (max != NULL) {
336 1336880
                        p = VNUM_2bytes(max, &rmax, 0);
337 1336880
                        if (p != NULL) {
338 0
                                VSB_printf(vsb, "Invalid max-val: %s\n", max);
339 0
                                return (-1);
340
                        }
341 1336880
                }
342 2481600
                p = VNUM_2bytes(arg, &r, 0);
343 2481600
                if (p != NULL) {
344 80
                        VSB_cat(vsb, "Could not convert to bytes.\n");
345 80
                        VSB_printf(vsb, "%s\n", p);
346 80
                        VSB_cat(vsb, "  Try something like '80k' or '120M'\n");
347 80
                        return (-1);
348
                }
349 2481520
                if ((uintmax_t)((ssize_t)r) != r) {
350 0
                        fmt_bytes(vsb, r);
351 0
                        VSB_cat(vsb, " is too large for this architecture.\n");
352 0
                        return (-1);
353
                }
354 2481520
                if (max != NULL && r > rmax) {
355 40
                        VSB_printf(vsb, "Must be no more than %s\n", max);
356 40
                        VSB_cat(vsb, "\n");
357 40
                        return (-1);
358
                }
359 2481480
                if (min != NULL && r < rmin) {
360 40
                        VSB_printf(vsb, "Must be at least %s\n", min);
361 40
                        return (-1);
362
                }
363 2481440
                *dest = r;
364 4863200
        } else if (arg == JSON_FMT) {
365 2880
                VSB_printf(vsb, "%zd", *dest);
366 2880
        } else {
367 2378880
                fmt_bytes(vsb, *dest);
368
        }
369 4863200
        return (0);
370 4863360
}
371
372
/*--------------------------------------------------------------------*/
373
374
int v_matchproto_(tweak_t)
375 939200
tweak_bytes(struct vsb *vsb, const struct parspec *par, const char *arg)
376
{
377
        volatile ssize_t *dest;
378
379 939200
        dest = par->priv;
380 939200
        return (tweak_generic_bytes(vsb, dest, arg, par->min, par->max));
381
}
382
383
/*--------------------------------------------------------------------*/
384
385
int v_matchproto_(tweak_t)
386 3514680
tweak_bytes_u(struct vsb *vsb, const struct parspec *par, const char *arg)
387
{
388
        volatile unsigned *d1;
389
        volatile ssize_t dest;
390
391 3514680
        d1 = par->priv;
392 3514680
        dest = *d1;
393 3514680
        if (tweak_generic_bytes(vsb, &dest, arg, par->min, par->max))
394 160
                return (-1);
395 3514520
        *d1 = dest;
396 3514520
        return (0);
397 3514680
}
398
399
/*--------------------------------------------------------------------
400
 * vsl_buffer and vsl_reclen have dependencies.
401
 */
402
403
int v_matchproto_(tweak_t)
404 164040
tweak_vsl_buffer(struct vsb *vsb, const struct parspec *par, const char *arg)
405
{
406
        volatile unsigned *d1;
407
        volatile ssize_t dest;
408
409 164040
        d1 = par->priv;
410 164040
        dest = *d1;
411 164040
        if (tweak_generic_bytes(vsb, &dest, arg, par->min, par->max))
412 0
                return (-1);
413 164040
        *d1 = dest;
414 164040
        MCF_ParamConf(MCF_MAXIMUM, "vsl_reclen", "%u", *d1 - 12);
415 164040
        return (0);
416 164040
}
417
418
int v_matchproto_(tweak_t)
419 245440
tweak_vsl_reclen(struct vsb *vsb, const struct parspec *par, const char *arg)
420
{
421
        volatile unsigned *d1;
422
        volatile ssize_t dest;
423
424 245440
        d1 = par->priv;
425 245440
        dest = *d1;
426 245440
        if (tweak_generic_bytes(vsb, &dest, arg, par->min, par->max))
427 0
                return (-1);
428 245440
        *d1 = dest;
429 245440
        MCF_ParamConf(MCF_MINIMUM, "vsl_buffer", "%u", *d1 + 12);
430 245440
        return (0);
431 245440
}
432
433
/*--------------------------------------------------------------------*/
434
435
int v_matchproto_(tweak_t)
436 449120
tweak_string(struct vsb *vsb, const struct parspec *par, const char *arg)
437
{
438 449120
        char **p = TRUST_ME(par->priv);
439
440 449120
        AN(p);
441 449120
        if (arg == NULL) {
442 206640
                VSB_quote(vsb, *p, -1, 0);
443 449120
        } else if (arg == JSON_FMT) {
444 600
                VSB_putc(vsb, '"');
445 600
                VSB_quote(vsb, *p, -1, VSB_QUOTE_JSON);
446 600
                VSB_putc(vsb, '"');
447 600
        } else {
448 241880
                REPLACE(*p, arg);
449
        }
450 449120
        return (0);
451
}
452
453
/*--------------------------------------------------------------------*/
454
455
int v_matchproto_(tweak_t)
456 247400
tweak_poolparam(struct vsb *vsb, const struct parspec *par, const char *arg)
457
{
458
        volatile struct poolparam *pp, px;
459
        struct parspec pt;
460
        char **av;
461 247400
        int retval = 0;
462
463 247400
        pp = par->priv;
464 247400
        if (arg == JSON_FMT) {
465 440
                VSB_cat(vsb, "{\n");
466 440
                VSB_indent(vsb, 8);
467 440
                VSB_printf(vsb, "\"min_pool\": %u,\n", pp->min_pool);
468 440
                VSB_printf(vsb, "\"max_pool\": %u,\n", pp->max_pool);
469 440
                VSB_printf(vsb, "\"max_age\": %g\n", pp->max_age);
470 440
                VSB_indent(vsb, -4);
471 440
                VSB_cat(vsb, "}");
472 247400
        } else if (arg == NULL) {
473 248080
                VSB_printf(vsb, "%u,%u,%g",
474 124040
                    pp->min_pool, pp->max_pool, pp->max_age);
475 124040
        } else {
476 122920
                av = VAV_Parse(arg, NULL, ARGV_COMMA);
477 122920
                do {
478 122920
                        if (av[0] != NULL) {
479 40
                                VSB_printf(vsb, "Parse error: %s", av[0]);
480 40
                                retval = -1;
481 40
                                break;
482
                        }
483 122880
                        if (av[1] == NULL || av[2] == NULL || av[3] == NULL) {
484 40
                                VSB_cat(vsb,
485
                                    "Three fields required:"
486
                                    " min_pool, max_pool and max_age\n");
487 40
                                retval = -1;
488 40
                                break;
489
                        }
490 122840
                        px = *pp;
491 245680
                        retval = tweak_generic_uint(vsb, &px.min_pool, av[1],
492 122840
                            par->min, par->max, par->dyn_min_reason,
493 122840
                            par->dyn_max_reason);
494 122840
                        if (retval)
495 40
                                break;
496 245600
                        retval = tweak_generic_uint(vsb, &px.max_pool, av[2],
497 122800
                            par->min, par->max, par->dyn_min_reason,
498 122800
                            par->dyn_max_reason);
499 122800
                        if (retval)
500 40
                                break;
501 122760
                        pt.priv = &px.max_age;
502 122760
                        pt.min = "0";
503 122760
                        pt.max = "1000000";
504 122760
                        retval = tweak_generic_double(vsb, av[3], &pt,
505
                            parse_decimal, "%.0f");
506 122760
                        if (retval)
507 40
                                break;
508 122720
                        if (px.min_pool > px.max_pool) {
509 40
                                VSB_cat(vsb,
510
                                    "min_pool cannot be larger"
511
                                    " than max_pool\n");
512 40
                                retval = -1;
513 40
                                break;
514
                        }
515 122680
                        *pp = px;
516 122680
                } while (0);
517 122920
                VAV_Free(av);
518
        }
519 247400
        return (retval);
520
}
521
522
/*--------------------------------------------------------------------
523
 * Thread pool tweaks.
524
 *
525
 * The min/max values automatically update the opposites appropriate
526
 * limit, so they don't end up crossing.
527
 */
528
529
int v_matchproto_(tweak_t)
530 283520
tweak_thread_pool_min(struct vsb *vsb, const struct parspec *par,
531
    const char *arg)
532
{
533 283520
        if (tweak_uint(vsb, par, arg))
534 80
                return (-1);
535
536 283440
        MCF_ParamConf(MCF_MINIMUM, "thread_pool_max",
537 283440
            "%u", mgt_param.wthread_min);
538 283440
        MCF_ParamConf(MCF_MAXIMUM, "thread_pool_reserve",
539 283440
            "%u", mgt_param.wthread_min * 950 / 1000);
540 283440
        return (0);
541 283520
}
542
543
int v_matchproto_(tweak_t)
544 82920
tweak_thread_pool_max(struct vsb *vsb, const struct parspec *par,
545
    const char *arg)
546
{
547
548 82920
        if (tweak_uint(vsb, par, arg))
549 80
                return (-1);
550
551 82840
        MCF_ParamConf(MCF_MAXIMUM, "thread_pool_min",
552 82840
            "%u", mgt_param.wthread_max);
553 82840
        return (0);
554 82920
}
555
556
/*--------------------------------------------------------------------
557
 * Tweak storage
558
 */
559
560
int v_matchproto_(tweak_t)
561 82280
tweak_storage(struct vsb *vsb, const struct parspec *par, const char *arg)
562
{
563
        struct stevedore *stv;
564
565
        /* XXX: If we want to remove the MUST_RESTART flag from the
566
         * h2_rxbuf_storage parameter, we could have a mechanism here
567
         * that when the child is running calls out through CLI to change
568
         * the stevedore being used. */
569
570 82280
        if (arg == NULL || arg == JSON_FMT)
571 41440
                return (tweak_string(vsb, par, arg));
572
573 40840
        if (!strcmp(arg, "Transient")) {
574
                /* Always allow setting to the special name
575
                 * "Transient". There will always be a stevedore with this
576
                 * name, but it may not have been configured at the time
577
                 * this is called. */
578 40800
        } else {
579
                /* Only allow setting the value to a known configured
580
                 * stevedore */
581 40
                STV_Foreach(stv) {
582 40
                        CHECK_OBJ_NOTNULL(stv, STEVEDORE_MAGIC);
583 40
                        if (!strcmp(stv->ident, arg))
584 40
                                break;
585
                }
586 40
                if (stv == NULL) {
587 0
                        VSB_printf(vsb, "unknown storage backend '%s'", arg);
588 0
                        return (-1);
589
                }
590
        }
591 40840
        return (tweak_string(vsb, par, arg));
592 82280
}
593
594
/*--------------------------------------------------------------------
595
 * Tweak alias
596
 */
597
598
int v_matchproto_(tweak_t)
599 240
tweak_alias(struct vsb *vsb, const struct parspec *par, const char *arg)
600
{
601
        const struct parspec *orig;
602
        struct parspec alias[1];
603
604 240
        orig = TRUST_ME(par->priv);
605 240
        AN(orig);
606 240
        memcpy(alias, orig, sizeof *orig);
607 240
        alias->name = par->name;
608 240
        alias->priv = TRUST_ME(orig);
609 240
        return (alias->func(vsb, alias, arg));
610
}
611
612
/*--------------------------------------------------------------------
613
 * Tweak bits
614
 */
615
616
enum bit_do {BSET, BCLR, BTST};
617
618
static int
619 13084720
bit(uint8_t *p, unsigned no, enum bit_do act)
620
{
621
        uint8_t b;
622
623 13084720
        p += (no >> 3);
624 13084720
        b = (0x80 >> (no & 7));
625 13084720
        if (act == BSET)
626 783000
                *p |= b;
627 12301720
        else if (act == BCLR)
628 113400
                *p &= ~b;
629 13084720
        return (*p & b);
630
}
631
632
static inline void
633 204120
bit_clear(uint8_t *p, unsigned l)
634
{
635
636 204120
        memset(p, 0, ((size_t)l + 7) >> 3);
637 204120
}
638
639
/*--------------------------------------------------------------------
640
 */
641
642
static int
643 290680
bit_tweak(struct vsb *vsb, uint8_t *p, unsigned l, const char *arg,
644
    const char * const *tags, const char *desc, char sign)
645
{
646
        int i, n;
647
        unsigned j;
648
        char **av;
649
        const char *s;
650
651 290680
        av = VAV_Parse(arg, &n, ARGV_COMMA);
652 290680
        if (av[0] != NULL) {
653 40
                VSB_printf(vsb, "Cannot parse: %s\n", av[0]);
654 40
                VAV_Free(av);
655 40
                return (-1);
656
        }
657 1391160
        for (i = 1; av[i] != NULL; i++) {
658 1100600
                s = av[i];
659 1100600
                if (sign == '+' && !strcmp(s, "none")) {
660 163240
                        bit_clear(p, l);
661 163240
                        continue;
662
                }
663 937360
                if (sign == '-' && !strcmp(s, "all")) {
664 40880
                        bit_clear(p, l);
665 40880
                        continue;
666
                }
667 896480
                if (*s != '-' && *s != '+') {
668 40
                        VSB_printf(vsb, "Missing '+' or '-' (%s)\n", s);
669 40
                        VAV_Free(av);
670 40
                        return (-1);
671
                }
672 46013840
                for (j = 0; j < l; j++) {
673 46013800
                        if (tags[j] != NULL && !strcasecmp(s + 1, tags[j]))
674 896400
                                break;
675 45117400
                }
676 896440
                if (tags[j] == NULL) {
677 40
                        VSB_printf(vsb, "Unknown %s (%s)\n", desc, s);
678 40
                        VAV_Free(av);
679 40
                        return (-1);
680
                }
681 896400
                assert(j < l);
682 896400
                if (s[0] == sign)
683 783000
                        (void)bit(p, j, BSET);
684
                else
685 113400
                        (void)bit(p, j, BCLR);
686 896400
        }
687 290560
        VAV_Free(av);
688 290560
        return (0);
689 290680
}
690
691
692
/*--------------------------------------------------------------------
693
 */
694
695
static int
696 498680
tweak_generic_bits(struct vsb *vsb, const struct parspec *par, const char *arg,
697
    uint8_t *p, unsigned l, const char * const *tags, const char *desc,
698
    char sign)
699
{
700
        unsigned j;
701
702 498680
        if (arg != NULL && !strcmp(arg, "default")) {
703
                /* XXX: deprecated in favor of param.reset */
704 0
                return (tweak_generic_bits(vsb, par, par->def, p, l, tags,
705 0
                    desc, sign));
706
        }
707
708 498680
        if (arg != NULL && arg != JSON_FMT)
709 290680
                return (bit_tweak(vsb, p, l, arg, tags, desc, sign));
710
711 208000
        if (arg == JSON_FMT)
712 760
                VSB_putc(vsb, '"');
713 208000
        VSB_cat(vsb, sign == '+' ? "none" : "all");
714 12396320
        for (j = 0; j < l; j++) {
715 12188320
                if (bit(p, j, BTST))
716 748480
                        VSB_printf(vsb, ",%c%s", sign, tags[j]);
717 12188320
        }
718 208000
        if (arg == JSON_FMT)
719 760
                VSB_putc(vsb, '"');
720 208000
        return (0);
721 498680
}
722
723
/*--------------------------------------------------------------------
724
 * The vsl_mask parameter
725
 */
726
727
static const char * const VSL_tags[256] = {
728
#  define SLTM(foo,flags,sdesc,ldesc) [SLT_##foo] = #foo,
729
#  include "tbl/vsl_tags.h"
730
};
731
732
int v_matchproto_(tweak_t)
733 121120
tweak_vsl_mask(struct vsb *vsb, const struct parspec *par, const char *arg)
734
{
735
736 121120
        return (tweak_generic_bits(vsb, par, arg, mgt_param.vsl_mask,
737
            SLT__Reserved, VSL_tags, "VSL tag", '-'));
738
}
739
740
/*--------------------------------------------------------------------
741
 * The debug parameter
742
 */
743
744
static const char * const debug_tags[] = {
745
#  define DEBUG_BIT(U, l, d) [DBG_##U] = #l,
746
#  include "tbl/debug_bits.h"
747
       NULL
748
};
749
750
int v_matchproto_(tweak_t)
751 124760
tweak_debug(struct vsb *vsb, const struct parspec *par, const char *arg)
752
{
753
754 124760
        return (tweak_generic_bits(vsb, par, arg, mgt_param.debug_bits,
755
            DBG_Reserved, debug_tags, "debug bit", '+'));
756
}
757
758
/*--------------------------------------------------------------------
759
 * The experimental parameter
760
 */
761
762
static const char * const experimental_tags[] = {
763
#  define EXPERIMENTAL_BIT(U, l, d) [EXPERIMENT_##U] = #l,
764
#  include "tbl/experimental_bits.h"
765
       NULL
766
};
767
768
int v_matchproto_(tweak_t)
769 82320
tweak_experimental(struct vsb *vsb, const struct parspec *par, const char *arg)
770
{
771
772 82320
        return (tweak_generic_bits(vsb, par, arg, mgt_param.experimental_bits,
773
            EXPERIMENT_Reserved, experimental_tags, "experimental bit", '+'));
774
}
775
776
/*--------------------------------------------------------------------
777
 * The feature parameter
778
 */
779
780
static const char * const feature_tags[] = {
781
#  define FEATURE_BIT(U, l, d) [FEATURE_##U] = #l,
782
#  include "tbl/feature_bits.h"
783
       NULL
784
};
785
786
int v_matchproto_(tweak_t)
787 87040
tweak_feature(struct vsb *vsb, const struct parspec *par, const char *arg)
788
{
789
790 87040
        return (tweak_generic_bits(vsb, par, arg, mgt_param.feature_bits,
791
            FEATURE_Reserved, feature_tags, "feature bit", '+'));
792
}
793
794
/*--------------------------------------------------------------------
795
 * The vcc_feature parameter
796
 */
797
798
static const char * const vcc_feature_tags[] = {
799
#  define VCC_FEATURE_BIT(U, l, d) [VCC_FEATURE_##U] = #l,
800
#  include "tbl/vcc_feature_bits.h"
801
       NULL
802
};
803
804
int v_matchproto_(tweak_t)
805 83440
tweak_vcc_feature(struct vsb *vsb, const struct parspec *par, const char *arg)
806
{
807
        const struct parspec *orig;
808
        char buf[32];
809
        int val;
810
811 83440
        if (arg != NULL && arg != JSON_FMT &&
812 41880
            strcmp(par->name, "vcc_feature")) {
813 0
                orig = TRUST_ME(par->priv);
814 0
                val = parse_boolean(vsb, arg);
815 0
                if (val < 0)
816 0
                        return (-1);
817 0
                bprintf(buf, "%c%s", val ? '+' : '-',
818
                    par->name + strlen("vcc_"));
819 0
                return (tweak_vcc_feature(vsb, orig, buf));
820
        }
821 83440
        return (tweak_generic_bits(vsb, par, arg, mgt_param.vcc_feature_bits,
822
            VCC_FEATURE_Reserved, vcc_feature_tags, "vcc_feature bit", '+'));
823 83440
}