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