varnish-cache/bin/varnishd/mgt/mgt_vcl.c
0
/*-
1 625
 * Copyright (c) 2006 Verdens Gang AS
2 300
 * Copyright (c) 2006-2015 Varnish Software AS
3 100
 * 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
 * VCL management stuff
31
 */
32
33
#include "config.h"
34
35
#include <fnmatch.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <unistd.h>
40
41
#include "mgt/mgt.h"
42
#include "mgt/mgt_vcl.h"
43
#include "common/heritage.h"
44
45
#include "vcli_serve.h"
46
#include "vct.h"
47
#include "vev.h"
48
#include "vte.h"
49
#include "vtim.h"
50
51
struct vclstate {
52
        const char              *name;
53
};
54
55
#define VCL_STATE(sym, str)                                             \
56
        static const struct vclstate VCL_STATE_ ## sym[1] = {{ str }};
57
#include "tbl/vcl_states.h"
58
59
static const struct vclstate VCL_STATE_LABEL[1] = {{ "label" }};
60
61
static unsigned vcl_count;
62
63
struct vclproghead vclhead = VTAILQ_HEAD_INITIALIZER(vclhead);
64
static struct vclproghead discardhead = VTAILQ_HEAD_INITIALIZER(discardhead);
65
struct vmodfilehead vmodhead = VTAILQ_HEAD_INITIALIZER(vmodhead);
66
static struct vclprog *mgt_vcl_active;
67
static struct vev *e_poker;
68
69
static int mgt_vcl_setstate(struct cli *, struct vclprog *,
70
    const struct vclstate *);
71
static int mgt_vcl_settemp(struct cli *, struct vclprog *, unsigned);
72
static int mgt_vcl_askchild(struct cli *, struct vclprog *, unsigned);
73
static void mgt_vcl_set_cooldown(struct vclprog *, vtim_mono);
74
75
/*--------------------------------------------------------------------*/
76
77
static const struct vclstate *
78 625
mcf_vcl_parse_state(struct cli *cli, const char *s)
79
{
80 625
        if (s != NULL) {
81
#define VCL_STATE(sym, str)                             \
82
                if (!strcmp(s, str))                    \
83
                        return (VCL_STATE_ ## sym);
84
#include "tbl/vcl_states.h"
85 0
        }
86 0
        VCLI_Out(cli, "State must be one of auto, cold or warm.");
87 0
        VCLI_SetResult(cli, CLIS_PARAM);
88 0
        return (NULL);
89 625
}
90
91
struct vclprog *
92 66700
mcf_vcl_byname(const char *name)
93
{
94
        struct vclprog *vp;
95
96 110850
        VTAILQ_FOREACH(vp, &vclhead, list)
97 74175
                if (!strcmp(name, vp->name))
98 30025
                        return (vp);
99 36675
        return (NULL);
100 66700
}
101
102
static int
103 66300
mcf_invalid_vclname(struct cli *cli, const char *name)
104
{
105
        const char *bad;
106
107 66300
        AN(name);
108 66300
        bad = VCT_invalid_name(name, NULL);
109
110 66300
        if (bad != NULL) {
111 125
                VCLI_SetResult(cli, CLIS_PARAM);
112 125
                VCLI_Out(cli, "Illegal character in VCL name ");
113 125
                if (*bad > 0x20 && *bad < 0x7f)
114 100
                        VCLI_Out(cli, "('%c')", *bad);
115
                else
116 25
                        VCLI_Out(cli, "(0x%02x)", *bad & 0xff);
117 125
                return (-1);
118
        }
119 66175
        return (0);
120 66300
}
121
122
static struct vclprog *
123 29375
mcf_find_vcl(struct cli *cli, const char *name)
124
{
125
        struct vclprog *vp;
126
127 29375
        if (mcf_invalid_vclname(cli, name))
128 0
                return (NULL);
129
130 29375
        vp = mcf_vcl_byname(name);
131 29375
        if (vp == NULL) {
132 75
                VCLI_SetResult(cli, CLIS_PARAM);
133 75
                VCLI_Out(cli, "No VCL named %s known\n", name);
134 75
        }
135 29375
        return (vp);
136 29375
}
137
138
static int
139 35975
mcf_find_no_vcl(struct cli *cli, const char *name)
140
{
141
142 35975
        if (mcf_invalid_vclname(cli, name))
143 100
                return (0);
144
145 35875
        if (mcf_vcl_byname(name) != NULL) {
146 25
                VCLI_SetResult(cli, CLIS_PARAM);
147 25
                VCLI_Out(cli, "Already a VCL named %s", name);
148 25
                return (0);
149
        }
150 35850
        return (1);
151 35975
}
152
153
int
154 92050
mcf_is_label(const struct vclprog *vp)
155
{
156 92050
        return (vp->state == VCL_STATE_LABEL);
157
}
158
159
/*--------------------------------------------------------------------*/
160
161
struct vcldep *
162 1200
mgt_vcl_dep_add(struct vclprog *vp_from, struct vclprog *vp_to)
163
{
164
        struct vcldep *vd;
165
166 1200
        CHECK_OBJ_NOTNULL(vp_from, VCLPROG_MAGIC);
167 1200
        CHECK_OBJ_NOTNULL(vp_to, VCLPROG_MAGIC);
168 1200
        assert(vp_to->state != VCL_STATE_COLD);
169
170 1200
        ALLOC_OBJ(vd, VCLDEP_MAGIC);
171 1200
        AN(vd);
172
173 1200
        mgt_vcl_set_cooldown(vp_from, -1);
174 1200
        mgt_vcl_set_cooldown(vp_to, -1);
175
176 1200
        vd->from = vp_from;
177 1200
        VTAILQ_INSERT_TAIL(&vp_from->dfrom, vd, lfrom);
178 1200
        vd->to = vp_to;
179 1200
        VTAILQ_INSERT_TAIL(&vp_to->dto, vd, lto);
180 1200
        vp_to->nto++;
181 1200
        return (vd);
182
}
183
184
static void
185 1200
mgt_vcl_dep_del(struct vcldep *vd)
186
{
187
188 1200
        CHECK_OBJ_NOTNULL(vd, VCLDEP_MAGIC);
189 1200
        VTAILQ_REMOVE(&vd->from->dfrom, vd, lfrom);
190 1200
        VTAILQ_REMOVE(&vd->to->dto, vd, lto);
191 1200
        vd->to->nto--;
192 1200
        if (vd->to->nto == 0)
193 975
                mgt_vcl_set_cooldown(vd->to, VTIM_mono());
194 1200
        FREE_OBJ(vd);
195 1200
}
196
197
/*--------------------------------------------------------------------*/
198
199
static struct vclprog *
200 37100
mgt_vcl_add(const char *name, const struct vclstate *state)
201
{
202
        struct vclprog *vp;
203
204 37100
        assert(state == VCL_STATE_WARM ||
205
               state == VCL_STATE_COLD ||
206
               state == VCL_STATE_AUTO ||
207
               state == VCL_STATE_LABEL);
208 37100
        ALLOC_OBJ(vp, VCLPROG_MAGIC);
209 37100
        XXXAN(vp);
210 37100
        REPLACE(vp->name, name);
211 37100
        VTAILQ_INIT(&vp->dfrom);
212 37100
        VTAILQ_INIT(&vp->dto);
213 37100
        VTAILQ_INIT(&vp->vmods);
214 37100
        vp->state = state;
215
216 37100
        if (vp->state != VCL_STATE_COLD)
217 37050
                vp->warm = 1;
218
219 37100
        VTAILQ_INSERT_TAIL(&vclhead, vp, list);
220 37100
        if (vp->state != VCL_STATE_LABEL)
221 36500
                vcl_count++;
222 37100
        return (vp);
223
}
224
225
static void
226 37100
mgt_vcl_del(struct vclprog *vp)
227
{
228
        char *p;
229
        struct vmoddep *vd;
230
        struct vmodfile *vf;
231
        struct vcldep *dep;
232
233 37100
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
234 37100
        assert(VTAILQ_EMPTY(&vp->dto));
235
236 37100
        mgt_vcl_symtab_clean(vp);
237
238 38225
        while ((dep = VTAILQ_FIRST(&vp->dfrom)) != NULL) {
239 1125
                assert(dep->from == vp);
240 1125
                mgt_vcl_dep_del(dep);
241
        }
242
243 37100
        VTAILQ_REMOVE(&vclhead, vp, list);
244 37100
        if (vp->state != VCL_STATE_LABEL)
245 36500
                vcl_count--;
246 37100
        if (vp->fname != NULL) {
247 29125
                if (!MGT_DO_DEBUG(DBG_VCL_KEEP))
248 29025
                        AZ(unlink(vp->fname));
249 29125
                p = strrchr(vp->fname, '/');
250 29125
                AN(p);
251 29125
                *p = '\0';
252 29125
                VJ_master(JAIL_MASTER_FILE);
253
                /*
254
                 * This will fail if any files are dropped next to the library
255
                 * without us knowing.  This happens for instance with GCOV.
256
                 * Assume developers know how to clean up after themselves
257
                 * (or alternatively:  How to run out of disk space).
258
                 */
259 29125
                (void)rmdir(vp->fname);
260 29125
                VJ_master(JAIL_MASTER_LOW);
261 29125
                free(vp->fname);
262 29125
        }
263 49025
        while (!VTAILQ_EMPTY(&vp->vmods)) {
264 11925
                vd = VTAILQ_FIRST(&vp->vmods);
265 11925
                CHECK_OBJ(vd, VMODDEP_MAGIC);
266 11925
                vf = vd->to;
267 11925
                CHECK_OBJ(vf, VMODFILE_MAGIC);
268 11925
                VTAILQ_REMOVE(&vp->vmods, vd, lfrom);
269 11925
                VTAILQ_REMOVE(&vf->vcls, vd, lto);
270 11925
                FREE_OBJ(vd);
271
272 11925
                if (VTAILQ_EMPTY(&vf->vcls)) {
273 8475
                        if (!MGT_DO_DEBUG(DBG_VMOD_SO_KEEP))
274 8450
                                AZ(unlink(vf->fname));
275 8475
                        VTAILQ_REMOVE(&vmodhead, vf, list);
276 8475
                        free(vf->fname);
277 8475
                        FREE_OBJ(vf);
278 8475
                }
279
        }
280 37100
        free(vp->name);
281 37100
        FREE_OBJ(vp);
282 37100
}
283
284
const char *
285 45475
mgt_has_vcl(void)
286
{
287 45475
        if (VTAILQ_EMPTY(&vclhead))
288 22375
                return ("No VCL loaded");
289 23100
        if (mgt_vcl_active == NULL)
290 25
                return ("No active VCL");
291 23075
        CHECK_OBJ_NOTNULL(mgt_vcl_active, VCLPROG_MAGIC);
292 23075
        AN(mgt_vcl_active->warm);
293 23075
        return (NULL);
294 45475
}
295
296
/*
297
 * go_cold
298
 *
299
 * -1: leave alone
300
 *  0: timer not started - not currently used
301
 * >0: when timer started
302
 */
303
static void
304 43068
mgt_vcl_set_cooldown(struct vclprog *vp, vtim_mono now)
305
{
306 43068
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
307
308 54030
        if (vp == mgt_vcl_active ||
309 14590
            vp->state != VCL_STATE_AUTO ||
310 11737
            vp->warm == 0 ||
311 11462
            !VTAILQ_EMPTY(&vp->dto) ||
312 10962
            !VTAILQ_EMPTY(&vp->dfrom))
313 32756
                vp->go_cold = -1;
314
        else
315 10312
                vp->go_cold = now;
316 43068
}
317
318
static int
319 29300
mgt_vcl_settemp(struct cli *cli, struct vclprog *vp, unsigned warm)
320
{
321
        int i;
322
323 29300
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
324
325 29300
        if (warm == vp->warm)
326 29100
                return (0);
327
328 200
        if (vp->state == VCL_STATE_AUTO || vp->state == VCL_STATE_LABEL) {
329 200
                mgt_vcl_set_cooldown(vp, -1);
330 200
                i = mgt_vcl_askchild(cli, vp, warm);
331 200
                mgt_vcl_set_cooldown(vp, VTIM_mono());
332 200
        } else {
333 0
                i = mgt_vcl_setstate(cli, vp,
334 0
                    warm ? VCL_STATE_WARM : VCL_STATE_COLD);
335
        }
336
337 200
        return (i);
338 29300
}
339
340
static int
341 29250
mgt_vcl_requirewarm(struct cli *cli, struct vclprog *vp)
342
{
343 29250
        if (vp->state == VCL_STATE_COLD) {
344 50
                VCLI_SetResult(cli, CLIS_CANT);
345 100
                VCLI_Out(cli, "VCL '%s' is cold - set to auto or warm first",
346 50
                    vp->name);
347 50
                return (1);
348
        }
349 29200
        return (mgt_vcl_settemp(cli, vp, 1));
350 29250
}
351
352
static int
353 1525
mgt_vcl_askchild(struct cli *cli, struct vclprog *vp, unsigned warm)
354
{
355
        unsigned status;
356
        char *p;
357
        int i;
358
359 1525
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
360
361 1525
        if (!MCH_Running()) {
362 100
                vp->warm = warm;
363 100
                return (0);
364
        }
365
366 1425
        i = mgt_cli_askchild(&status, &p, "vcl.state %s %d%s\n",
367 1425
            vp->name, warm, vp->state->name);
368 1425
        if (i && cli != NULL) {
369 100
                VCLI_SetResult(cli, status);
370 100
                VCLI_Out(cli, "%s", p);
371 1425
        } else if (i) {
372 0
                MGT_Complain(C_ERR,
373
                    "Please file ticket: VCL poker problem: "
374
                    "'vcl.state %s %d%s' -> %03d '%s'",
375 0
                    vp->name, warm, vp->state->name, i, p);
376 0
        } else {
377
                /* Success, update mgt's VCL state to reflect child's
378
                   state */
379 1325
                vp->warm = warm;
380
        }
381
382 1425
        free(p);
383 1425
        return (i);
384 1525
}
385
386
static int
387 1325
mgt_vcl_setstate(struct cli *cli, struct vclprog *vp, const struct vclstate *vs)
388
{
389
        unsigned warm;
390
        int i;
391
        const struct vclstate *os;
392
393 1325
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
394
395 1325
        assert(vs != VCL_STATE_LABEL);
396
397 1325
        if (mcf_is_label(vp)) {
398 0
                AN(vp->warm);
399
                /* do not touch labels */
400 0
                return (0);
401
        }
402
403 1325
        if (vp->state == vs)
404 0
                return (0);
405
406 1325
        os = vp->state;
407 1325
        vp->state = vs;
408
409 1325
        if (vp == mgt_vcl_active) {
410 50
                assert (vs == VCL_STATE_WARM || vs == VCL_STATE_AUTO);
411 50
                AN(vp->warm);
412 50
                warm = 1;
413 1325
        } else if (vs == VCL_STATE_AUTO) {
414 75
                warm = vp->warm;
415 75
        } else {
416 1200
                warm = (vs == VCL_STATE_WARM ? 1 : 0);
417
        }
418
419 1325
        i = mgt_vcl_askchild(cli, vp, warm);
420 1325
        if (i == 0)
421 1250
                mgt_vcl_set_cooldown(vp, VTIM_mono());
422
        else
423 75
                vp->state = os;
424 1325
        return (i);
425 1325
}
426
427
/*--------------------------------------------------------------------*/
428
429
static struct vclprog *
430 36525
mgt_new_vcl(struct cli *cli, const char *vclname, const char *vclsrc,
431
    const char *vclsrcfile, const char *state, int C_flag)
432
{
433
        unsigned status;
434
        char *lib, *p;
435
        struct vclprog *vp;
436
        const struct vclstate *vs;
437
438 36525
        AN(cli);
439
440 36525
        if (vcl_count >= mgt_param.max_vcl &&
441 50
            mgt_param.max_vcl_handling == 2) {
442 25
                VCLI_Out(cli, "Too many (%d) VCLs already loaded\n", vcl_count);
443 25
                VCLI_Out(cli, "(See max_vcl and max_vcl_handling parameters)");
444 25
                VCLI_SetResult(cli, CLIS_CANT);
445 25
                return (NULL);
446
        }
447
448 36500
        if (state == NULL)
449 36400
                vs = VCL_STATE_AUTO;
450
        else
451 100
                vs = mcf_vcl_parse_state(cli, state);
452
453 36500
        if (vs == NULL)
454 0
                return (NULL);
455
456 36500
        vp = mgt_vcl_add(vclname, vs);
457 36500
        lib = mgt_VccCompile(cli, vp, vclname, vclsrc, vclsrcfile, C_flag);
458 36500
        if (lib == NULL) {
459 7375
                mgt_vcl_del(vp);
460 7375
                return (NULL);
461
        }
462
463 29125
        AZ(C_flag);
464 29125
        vp->fname = lib;
465
466 29150
        if ((cli->result == CLIS_OK || cli->result == CLIS_TRUNCATED) &&
467 29125
            vcl_count > mgt_param.max_vcl &&
468 25
            mgt_param.max_vcl_handling == 1) {
469 25
                VCLI_Out(cli, "%d VCLs loaded\n", vcl_count);
470 25
                VCLI_Out(cli, "Remember to vcl.discard the old/unused VCLs.\n");
471 25
                VCLI_Out(cli, "(See max_vcl and max_vcl_handling parameters)");
472 25
        }
473
474 29125
        if (!MCH_Running())
475 23675
                return (vp);
476
477 5450
        if (mgt_cli_askchild(&status, &p, "vcl.load %s %s %d%s\n",
478 5450
            vp->name, vp->fname, vp->warm, vp->state->name)) {
479 1050
                mgt_vcl_del(vp);
480 1050
                VCLI_Out(cli, "%s", p);
481 1050
                VCLI_SetResult(cli, status);
482 1050
                free(p);
483 1050
                return (NULL);
484
        }
485 4400
        free(p);
486
487 4400
        mgt_vcl_set_cooldown(vp, VTIM_mono());
488 4400
        return (vp);
489 36525
}
490
491
/*--------------------------------------------------------------------*/
492
493
void
494 675
mgt_vcl_startup(struct cli *cli, const char *vclsrc, const char *vclname,
495
    const char *origin, int C_flag)
496
{
497
        char buf[20];
498
        static int n = 0;
499
        struct vclprog *vp;
500
501 675
        AZ(MCH_Running());
502
503 675
        AN(vclsrc);
504 675
        AN(origin);
505 675
        if (vclname == NULL) {
506 50
                bprintf(buf, "boot%d", n++);
507 50
                vclname = buf;
508 50
        }
509 675
        vp = mgt_new_vcl(cli, vclname, vclsrc, origin, NULL, C_flag);
510 675
        if (vp != NULL) {
511
                /* Last startup VCL becomes the automatically selected
512
                 * active VCL. */
513 550
                AN(vp->warm);
514 550
                mgt_vcl_active = vp;
515 550
        }
516 675
}
517
518
/*--------------------------------------------------------------------*/
519
520
int
521 22575
mgt_push_vcls(struct cli *cli, unsigned *status, char **p)
522
{
523
        struct vclprog *vp;
524
        struct vcldep *vd;
525
        int done;
526
527 22575
        AN(mgt_vcl_active);
528
529
        /* The VCL has not been loaded yet, it cannot fail */
530 22575
        (void)cli;
531
532 46375
        VTAILQ_FOREACH(vp, &vclhead, list)
533 23800
                vp->loaded = 0;
534
535 22575
        do {
536 22575
                done = 1;
537 46325
                VTAILQ_FOREACH(vp, &vclhead, list) {
538 23775
                        if (vp->loaded)
539 0
                                continue;
540 24100
                        VTAILQ_FOREACH(vd, &vp->dfrom, lfrom)
541 325
                                if (!vd->to->loaded)
542 0
                                        break;
543 23775
                        if (vd != NULL) {
544 0
                                done = 0;
545 0
                                continue;
546
                        }
547 23775
                        if (mcf_is_label(vp)) {
548 200
                                vd = VTAILQ_FIRST(&vp->dfrom);
549 200
                                AN(vd);
550 400
                                if (mgt_cli_askchild(status, p,
551
                                    "vcl.label %s %s\n",
552 200
                                    vp->name, vd->to->name))
553 0
                                        return (1);
554 200
                        } else {
555 47150
                                if (mgt_cli_askchild(status, p,
556
                                    "vcl.load \"%s\" %s %d%s\n",
557 23575
                                    vp->name, vp->fname, vp->warm,
558 23575
                                    vp->state->name))
559 25
                                        return (1);
560
                        }
561 23750
                        vp->loaded = 1;
562 23750
                        free(*p);
563 23750
                        *p = NULL;
564 23750
                }
565 22550
        } while (!done);
566
567 45100
        if (mgt_cli_askchild(status, p, "vcl.use \"%s\"\n",
568 22550
              mgt_vcl_active->name)) {
569 0
                return (1);
570
        }
571 22550
        free(*p);
572 22550
        *p = NULL;
573 22550
        return (0);
574 22575
}
575
576
/*--------------------------------------------------------------------*/
577
578
static void v_matchproto_(cli_func_t)
579 35650
mcf_vcl_inline(struct cli *cli, const char * const *av, void *priv)
580
{
581
        struct vclprog *vp;
582
583 35650
        (void)priv;
584
585 35650
        if (!mcf_find_no_vcl(cli, av[2]))
586 100
                return;
587
588 35550
        vp = mgt_new_vcl(cli, av[2], av[3], "<vcl.inline>", av[4], 0);
589 35550
        if (vp != NULL && !MCH_Running())
590 23025
                VCLI_Out(cli, "VCL compiled.\n");
591 35650
}
592
593
static void v_matchproto_(cli_func_t)
594 325
mcf_vcl_load(struct cli *cli, const char * const *av, void *priv)
595
{
596
        struct vclprog *vp;
597
598 325
        (void)priv;
599 325
        if (!mcf_find_no_vcl(cli, av[2]))
600 25
                return;
601
602 300
        vp = mgt_new_vcl(cli, av[2], NULL, av[3], av[4], 0);
603 300
        if (vp != NULL && !MCH_Running())
604 100
                VCLI_Out(cli, "VCL compiled.\n");
605 325
}
606
607
608
static void v_matchproto_(cli_func_t)
609 550
mcf_vcl_state(struct cli *cli, const char * const *av, void *priv)
610
{
611
        const struct vclstate *state;
612
        struct vclprog *vp;
613
614 550
        (void)priv;
615 550
        vp = mcf_find_vcl(cli, av[2]);
616 550
        if (vp == NULL)
617 0
                return;
618
619 550
        if (mcf_is_label(vp)) {
620 25
                VCLI_Out(cli, "Labels are always warm");
621 25
                VCLI_SetResult(cli, CLIS_PARAM);
622 25
                return;
623
        }
624
625 525
        state = mcf_vcl_parse_state(cli, av[3]);
626 525
        if (state == NULL)
627 0
                return;
628
629 525
        if (state == VCL_STATE_COLD) {
630 275
                if (!VTAILQ_EMPTY(&vp->dto)) {
631 25
                        assert(vp->state != VCL_STATE_COLD);
632 25
                        VCLI_Out(cli, "A labeled VCL cannot be set cold");
633 25
                        VCLI_SetResult(cli, CLIS_CANT);
634 25
                        return;
635
                }
636 250
                if (vp == mgt_vcl_active) {
637 25
                        VCLI_Out(cli, "Cannot set the active VCL cold.");
638 25
                        VCLI_SetResult(cli, CLIS_CANT);
639 25
                        return;
640
                }
641 225
        }
642
643 475
        (void)mgt_vcl_setstate(cli, vp, state);
644 550
}
645
646
static void v_matchproto_(cli_func_t)
647 27925
mcf_vcl_use(struct cli *cli, const char * const *av, void *priv)
648
{
649
        unsigned status;
650 27925
        char *p = NULL;
651
        struct vclprog *vp, *vp2;
652
        vtim_mono now;
653
654 27925
        (void)priv;
655 27925
        vp = mcf_find_vcl(cli, av[2]);
656 27925
        if (vp == NULL)
657 25
                return;
658 27900
        if (vp == mgt_vcl_active)
659 125
                return;
660
661 27775
        if (mgt_vcl_requirewarm(cli, vp))
662 25
                return;
663
664 27750
        if (MCH_Running() &&
665 4625
            mgt_cli_askchild(&status, &p, "vcl.use %s\n", av[2])) {
666 0
                VCLI_SetResult(cli, status);
667 0
                VCLI_Out(cli, "%s", p);
668 0
        } else {
669 27750
                VCLI_Out(cli, "VCL '%s' now active", av[2]);
670 27750
                vp2 = mgt_vcl_active;
671 27750
                mgt_vcl_active = vp;
672 27750
                now = VTIM_mono();
673 27750
                mgt_vcl_set_cooldown(vp, now);
674 27750
                if (vp2 != NULL)
675 5825
                        mgt_vcl_set_cooldown(vp2, now);
676
        }
677 27750
        free(p);
678 27925
}
679
680
static void
681 975
mgt_vcl_discard(struct cli *cli, struct vclprog *vp)
682
{
683 975
        char *p = NULL;
684
        unsigned status;
685
686 975
        AN(vp);
687 975
        AN(vp->discard);
688 975
        assert(vp != mgt_vcl_active);
689
690 1175
        while (!VTAILQ_EMPTY(&vp->dto))
691 200
                mgt_vcl_discard(cli, VTAILQ_FIRST(&vp->dto)->from);
692
693 975
        if (mcf_is_label(vp)) {
694 125
                AN(vp->warm);
695 125
                vp->warm = 0;
696 125
        } else {
697 850
                (void)mgt_vcl_setstate(cli, vp, VCL_STATE_COLD);
698
        }
699 975
        if (MCH_Running()) {
700 950
                AZ(vp->warm);
701 950
                if (mgt_cli_askchild(&status, &p, "vcl.discard %s\n", vp->name))
702 0
                        assert(status == CLIS_OK || status == CLIS_COMMS);
703 950
                free(p);
704 950
        }
705 975
        VTAILQ_REMOVE(&discardhead, vp, discard_list);
706 975
        mgt_vcl_del(vp);
707 975
}
708
709
static int
710 1175
mgt_vcl_discard_mark(struct cli *cli, const char *glob)
711
{
712
        struct vclprog *vp;
713 1175
        unsigned marked = 0;
714
715 4925
        VTAILQ_FOREACH(vp, &vclhead, list) {
716 3925
                if (fnmatch(glob, vp->name, 0))
717 2300
                        continue;
718 1625
                if (vp == mgt_vcl_active) {
719 175
                        VCLI_SetResult(cli, CLIS_CANT);
720 350
                        VCLI_Out(cli, "Cannot discard active VCL program %s\n",
721 175
                            vp->name);
722 175
                        return (-1);
723
                }
724 1450
                if (!vp->discard)
725 1450
                        VTAILQ_INSERT_TAIL(&discardhead, vp, discard_list);
726 1450
                vp->discard = 1;
727 1450
                marked++;
728 1450
        }
729
730 1000
        if (marked == 0) {
731 100
                VCLI_SetResult(cli, CLIS_PARAM);
732 100
                VCLI_Out(cli, "No VCL name matches %s\n", glob);
733 100
        }
734
735 1000
        return (marked);
736 1175
}
737
738
static void
739 100
mgt_vcl_discard_depfail(struct cli *cli, struct vclprog *vp)
740
{
741
        struct vcldep *vd;
742
        int n;
743
744 100
        AN(cli);
745 100
        AN(vp);
746 100
        assert(!VTAILQ_EMPTY(&vp->dto));
747
748 100
        VCLI_SetResult(cli, CLIS_CANT);
749 100
        AN(vp->warm);
750 100
        if (!mcf_is_label(vp))
751 150
                VCLI_Out(cli, "Cannot discard labeled VCL program %s:\n",
752 75
                    vp->name);
753
        else
754 50
                VCLI_Out(cli,
755
                    "Cannot discard VCL label %s, other VCLs depend on it:\n",
756 25
                    vp->name);
757 100
        n = 0;
758 325
        VTAILQ_FOREACH(vd, &vp->dto, lto) {
759 225
                if (n++ == 5) {
760 0
                        VCLI_Out(cli, "\t[...]");
761 0
                        break;
762
                }
763 225
                VCLI_Out(cli, "\t%s\n", vd->from->name);
764 225
        }
765 100
}
766
767
static int
768 1125
mgt_vcl_discard_depcheck(struct cli *cli)
769
{
770
        struct vclprog *vp;
771
        struct vcldep *vd;
772
773 2100
        VTAILQ_FOREACH(vp, &discardhead, discard_list) {
774 1275
                VTAILQ_FOREACH(vd, &vp->dto, lto)
775 300
                        if (!vd->from->discard) {
776 100
                                mgt_vcl_discard_depfail(cli, vp);
777 100
                                return (-1);
778
                        }
779 975
        }
780
781 1025
        return (0);
782 1125
}
783
784
static void
785 375
mgt_vcl_discard_clear(void)
786
{
787
        struct vclprog *vp, *vp2;
788
789 850
        VTAILQ_FOREACH_SAFE(vp, &discardhead, discard_list, vp2) {
790 475
                AN(vp->discard);
791 475
                vp->discard = 0;
792 475
                VTAILQ_REMOVE(&discardhead, vp, discard_list);
793 475
        }
794 375
}
795
796
static void v_matchproto_(cli_func_t)
797 1125
mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv)
798
{
799
        const struct vclprog *vp;
800
801 1125
        (void)priv;
802
803 1125
        assert(VTAILQ_EMPTY(&discardhead));
804 4850
        VTAILQ_FOREACH(vp, &vclhead, list)
805 3725
                AZ(vp->discard);
806
807 2025
        for (av += 2; *av != NULL; av++) {
808 1175
                if (mgt_vcl_discard_mark(cli, *av) <= 0) {
809 275
                        mgt_vcl_discard_clear();
810 275
                        break;
811
                }
812 900
        }
813
814 1125
        if (mgt_vcl_discard_depcheck(cli) != 0)
815 100
                mgt_vcl_discard_clear();
816
817 1900
        while (!VTAILQ_EMPTY(&discardhead))
818 775
                mgt_vcl_discard(cli, VTAILQ_FIRST(&discardhead));
819 1125
}
820
821
static void v_matchproto_(cli_func_t)
822 2250
mcf_vcl_list(struct cli *cli, const char * const *av, void *priv)
823
{
824
        unsigned status;
825
        char *p;
826
        struct vclprog *vp;
827
        struct vcldep *vd;
828
        const struct vclstate *vs;
829
        struct vte *vte;
830
831
        /* NB: Shall generate same output as vcl_cli_list() */
832
833 2250
        (void)av;
834 2250
        (void)priv;
835
836 2250
        if (MCH_Running()) {
837 1925
                if (!mgt_cli_askchild(&status, &p, "vcl.list\n")) {
838 1925
                        VCLI_SetResult(cli, status);
839 1925
                        VCLI_Out(cli, "%s", p);
840 1925
                }
841 1925
                free(p);
842 1925
        } else {
843 325
                vte = VTE_new(7, 80);
844 325
                AN(vte);
845
846 1225
                VTAILQ_FOREACH(vp, &vclhead, list) {
847 1800
                        VTE_printf(vte, "%s",
848 900
                            vp == mgt_vcl_active ? "active" : "available");
849 900
                        vs = vp->warm ?  VCL_STATE_WARM : VCL_STATE_COLD;
850 900
                        VTE_printf(vte, "\t%s\t%s", vp->state->name, vs->name);
851 900
                        VTE_printf(vte, "\t-\t%s", vp->name);
852 900
                        if (mcf_is_label(vp)) {
853 175
                                vd = VTAILQ_FIRST(&vp->dfrom);
854 175
                                AN(vd);
855 175
                                VTE_printf(vte, "\t->\t%s", vd->to->name);
856 175
                                if (vp->nto > 0)
857 100
                                        VTE_printf(vte, " (%d return(vcl)%s)",
858 50
                                            vp->nto, vp->nto > 1 ? "'s" : "");
859 900
                        } else if (vp->nto > 0) {
860 250
                                VTE_printf(vte, "\t<-\t(%d label%s)",
861 125
                                    vp->nto, vp->nto > 1 ? "s" : "");
862 125
                        }
863 900
                        VTE_cat(vte, "\n");
864 900
                }
865 325
                AZ(VTE_finish(vte));
866 325
                AZ(VTE_format(vte, VCLI_VTE_format, cli));
867 325
                VTE_destroy(&vte);
868
        }
869 2250
}
870
871
static void v_matchproto_(cli_func_t)
872 200
mcf_vcl_list_json(struct cli *cli, const char * const *av, void *priv)
873
{
874
        unsigned status;
875
        char *p;
876
        struct vclprog *vp;
877
        struct vcldep *vd;
878
        const struct vclstate *vs;
879
880
        /* NB: Shall generate same output as vcl_cli_list() */
881
882 200
        (void)priv;
883 200
        if (MCH_Running()) {
884 125
                if (!mgt_cli_askchild(&status, &p, "vcl.list -j\n")) {
885 125
                        VCLI_SetResult(cli, status);
886 125
                        VCLI_Out(cli, "%s", p);
887 125
                }
888 125
                free(p);
889 125
        } else {
890 75
                VCLI_JSON_begin(cli, 2, av);
891 250
                VTAILQ_FOREACH(vp, &vclhead, list) {
892 175
                        VCLI_Out(cli, ",\n");
893 175
                        VCLI_Out(cli, "{\n");
894 175
                        VSB_indent(cli->sb, 2);
895 350
                        VCLI_Out(cli, "\"status\": \"%s\",\n",
896 175
                            vp == mgt_vcl_active ? "active" : "available");
897 175
                        VCLI_Out(cli, "\"state\": \"%s\",\n", vp->state->name);
898 175
                        vs = vp->warm ?  VCL_STATE_WARM : VCL_STATE_COLD;
899 175
                        VCLI_Out(cli, "\"temperature\": \"%s\",\n", vs->name);
900 175
                        VCLI_Out(cli, "\"name\": \"%s\"", vp->name);
901 175
                        if (mcf_is_label(vp)) {
902 75
                                vd = VTAILQ_FIRST(&vp->dfrom);
903 75
                                AN(vd);
904 75
                                VCLI_Out(cli, ",\n");
905 75
                                VCLI_Out(cli, "\"label\": {\n");
906 75
                                VSB_indent(cli->sb, 2);
907 75
                                VCLI_Out(cli, "\"name\": \"%s\"", vd->to->name);
908 75
                                if (vp->nto > 0)
909 0
                                        VCLI_Out(cli, ",\n\"refs\": %d",
910 0
                                                 vp->nto);
911 75
                                VSB_indent(cli->sb, -2);
912 75
                                VCLI_Out(cli, "\n");
913 75
                                VCLI_Out(cli, "}");
914 175
                        } else if (vp->nto > 0) {
915 25
                                VCLI_Out(cli, ",\n");
916 25
                                VCLI_Out(cli, "\"labels\": %d", vp->nto);
917 25
                        }
918 175
                        VSB_indent(cli->sb, -2);
919 175
                        VCLI_Out(cli, "\n}");
920 175
                }
921 75
                VCLI_JSON_end(cli);
922
        }
923 200
}
924
925
static int
926 350
mcf_vcl_check_dag(struct cli *cli, struct vclprog *tree, struct vclprog *target)
927
{
928
        struct vcldep *vd;
929
930 350
        if (target == tree)
931 75
                return (1);
932 275
        VTAILQ_FOREACH(vd, &tree->dfrom, lfrom) {
933 225
                if (!mcf_vcl_check_dag(cli, vd->to, target))
934 0
                        continue;
935 225
                if (mcf_is_label(tree))
936 150
                        VCLI_Out(cli, "Label %s points to vcl %s\n",
937 75
                            tree->name, vd->to->name);
938
                else
939 300
                        VCLI_Out(cli, "Vcl %s uses Label %s\n",
940 150
                            tree->name, vd->to->name);
941 225
                return (1);
942
        }
943 50
        return (0);
944 350
}
945
946
static void v_matchproto_(cli_func_t)
947 950
mcf_vcl_label(struct cli *cli, const char * const *av, void *priv)
948
{
949
        struct vclprog *vpl;
950
        struct vclprog *vpt;
951
        unsigned status;
952
        char *p;
953
        int i;
954
955 950
        (void)priv;
956 950
        if (mcf_invalid_vclname(cli, av[2]))
957 25
                return;
958 925
        vpl = mcf_vcl_byname(av[2]);
959 925
        if (vpl != NULL && !mcf_is_label(vpl)) {
960 25
                VCLI_SetResult(cli, CLIS_PARAM);
961 25
                VCLI_Out(cli, "%s is not a label", vpl->name);
962 25
                return;
963
        }
964 900
        vpt = mcf_find_vcl(cli, av[3]);
965 900
        if (vpt == NULL)
966 50
                return;
967 850
        if (mcf_is_label(vpt)) {
968 50
                VCLI_SetResult(cli, CLIS_CANT);
969 50
                VCLI_Out(cli, "VCL labels cannot point to labels");
970 50
                return;
971
        }
972 800
        if (vpl != NULL) {
973 150
                if (VTAILQ_FIRST(&vpl->dfrom)->to != vpt &&
974 125
                    mcf_vcl_check_dag(cli, vpt, vpl)) {
975 150
                        VCLI_Out(cli,
976
                            "Pointing label %s at %s would create a loop",
977 75
                            vpl->name, vpt->name);
978 75
                        VCLI_SetResult(cli, CLIS_PARAM);
979 75
                        return;
980
                }
981 75
        }
982
983 725
        if (mgt_vcl_requirewarm(cli, vpt))
984 50
                return;
985
986 675
        if (vpl != NULL) {
987
                /* potentially fail before we delete the dependency */
988 75
                if (mgt_vcl_requirewarm(cli, vpl))
989 0
                        return;
990 75
                mgt_vcl_dep_del(VTAILQ_FIRST(&vpl->dfrom));
991 75
                AN(VTAILQ_EMPTY(&vpl->dfrom));
992 75
        } else {
993 600
                vpl = mgt_vcl_add(av[2], VCL_STATE_LABEL);
994
        }
995
996 675
        AN(vpl);
997 675
        if (mgt_vcl_requirewarm(cli, vpl))
998 0
                return;
999 675
        (void)mgt_vcl_dep_add(vpl, vpt);
1000
1001 675
        if (!MCH_Running())
1002 200
                return;
1003
1004 475
        i = mgt_cli_askchild(&status, &p, "vcl.label %s %s\n", av[2], av[3]);
1005 475
        if (i) {
1006 0
                VCLI_SetResult(cli, status);
1007 0
                VCLI_Out(cli, "%s", p);
1008 0
        }
1009 475
        free(p);
1010 950
}
1011
1012
static void v_matchproto_(cli_func_t)
1013 25
mcf_vcl_deps(struct cli *cli, const char * const *av, void *priv)
1014
{
1015
        struct vclprog *vp;
1016
        struct vcldep *vd;
1017
        struct vte *vte;
1018
1019 25
        (void)av;
1020 25
        (void)priv;
1021
1022 25
        vte = VTE_new(2, 80);
1023 25
        AN(vte);
1024
1025 300
        VTAILQ_FOREACH(vp, &vclhead, list) {
1026 275
                if (VTAILQ_EMPTY(&vp->dfrom)) {
1027 75
                        VTE_printf(vte, "%s\n", vp->name);
1028 75
                        continue;
1029
                }
1030 400
                VTAILQ_FOREACH(vd, &vp->dfrom, lfrom)
1031 200
                        VTE_printf(vte, "%s\t%s\n", vp->name, vd->to->name);
1032 200
        }
1033 25
        AZ(VTE_finish(vte));
1034 25
        AZ(VTE_format(vte, VCLI_VTE_format, cli));
1035 25
        VTE_destroy(&vte);
1036 25
}
1037
1038
static void v_matchproto_(cli_func_t)
1039 0
mcf_vcl_deps_json(struct cli *cli, const char * const *av, void *priv)
1040
{
1041
        struct vclprog *vp;
1042
        struct vcldep *vd;
1043
        const char *sepd, *sepa;
1044
1045 0
        (void)priv;
1046
1047 0
        VCLI_JSON_begin(cli, 1, av);
1048
1049 0
        VTAILQ_FOREACH(vp, &vclhead, list) {
1050 0
                VCLI_Out(cli, ",\n");
1051 0
                VCLI_Out(cli, "{\n");
1052 0
                VSB_indent(cli->sb, 2);
1053 0
                VCLI_Out(cli, "\"name\": \"%s\",\n", vp->name);
1054 0
                VCLI_Out(cli, "\"deps\": [");
1055 0
                VSB_indent(cli->sb, 2);
1056 0
                sepd = "";
1057 0
                sepa = "";
1058 0
                VTAILQ_FOREACH(vd, &vp->dfrom, lfrom) {
1059 0
                        VCLI_Out(cli, "%s\n", sepd);
1060 0
                        VCLI_Out(cli, "\"%s\"", vd->to->name);
1061 0
                        sepd = ",";
1062 0
                        sepa = "\n";
1063 0
                }
1064 0
                VSB_indent(cli->sb, -2);
1065 0
                VCLI_Out(cli, "%s", sepa);
1066 0
                VCLI_Out(cli, "]\n");
1067 0
                VSB_indent(cli->sb, -2);
1068 0
                VCLI_Out(cli, "}");
1069 0
        }
1070
1071 0
        VCLI_JSON_end(cli);
1072 0
}
1073
1074
/*--------------------------------------------------------------------*/
1075
1076
static int v_matchproto_(vev_cb_f)
1077 6951
mgt_vcl_poker(const struct vev *e, int what)
1078
{
1079
        struct vclprog *vp;
1080
        vtim_mono now;
1081
1082 6951
        (void)e;
1083 6951
        (void)what;
1084 6951
        e_poker->timeout = mgt_param.vcl_cooldown * .45;
1085 6951
        now = VTIM_mono();
1086 18178
        VTAILQ_FOREACH(vp, &vclhead, list) {
1087 11227
                if (vp->go_cold == 0)
1088 68
                        mgt_vcl_set_cooldown(vp, now);
1089 11159
                else if (vp->go_cold > 0 &&
1090 1612
                    vp->go_cold + mgt_param.vcl_cooldown < now)
1091 100
                        (void)mgt_vcl_settemp(NULL, vp, 0);
1092 11227
        }
1093 6951
        return (0);
1094
}
1095
1096
/*--------------------------------------------------------------------*/
1097
1098
static struct cli_proto cli_vcl[] = {
1099
        { CLICMD_VCL_LOAD,              "", mcf_vcl_load },
1100
        { CLICMD_VCL_INLINE,            "", mcf_vcl_inline },
1101
        { CLICMD_VCL_USE,               "", mcf_vcl_use },
1102
        { CLICMD_VCL_STATE,             "", mcf_vcl_state },
1103
        { CLICMD_VCL_DISCARD,           "", mcf_vcl_discard },
1104
        { CLICMD_VCL_LIST,              "", mcf_vcl_list, mcf_vcl_list_json },
1105
        { CLICMD_VCL_DEPS,              "", mcf_vcl_deps, mcf_vcl_deps_json },
1106
        { CLICMD_VCL_LABEL,             "", mcf_vcl_label },
1107
        { CLICMD_DEBUG_VCL_SYMTAB,      "d", mcf_vcl_symtab },
1108
        { NULL }
1109
};
1110
1111
/*--------------------------------------------------------------------*/
1112
1113
static void
1114 110550
mgt_vcl_atexit(void)
1115
{
1116
        struct vclprog *vp, *vp2;
1117
1118 110550
        if (getpid() != heritage.mgt_pid)
1119 87425
                return;
1120 23125
        mgt_vcl_active = NULL;
1121 46250
        while (!VTAILQ_EMPTY(&vclhead))
1122 52175
                VTAILQ_FOREACH_SAFE(vp, &vclhead, list, vp2)
1123 56750
                        if (VTAILQ_EMPTY(&vp->dto))
1124 27700
                                mgt_vcl_del(vp);
1125 110550
}
1126
1127
void
1128 23125
mgt_vcl_init(void)
1129
{
1130
1131 23125
        e_poker = VEV_Alloc();
1132 23125
        AN(e_poker);
1133 23125
        e_poker->timeout = 3;           // random, prime
1134
1135 23125
        e_poker->callback = mgt_vcl_poker;
1136 23125
        e_poker->name = "vcl poker";
1137 23125
        AZ(VEV_Start(mgt_evb, e_poker));
1138
1139 23125
        AZ(atexit(mgt_vcl_atexit));
1140
1141 23125
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_vcl);
1142 23125
}