varnish-cache/bin/varnishd/mgt/mgt_vcl.c
0
/*-
1 325
 * Copyright (c) 2006 Verdens Gang AS
2 156
 * Copyright (c) 2006-2015 Varnish Software AS
3 52
 * 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 325
mcf_vcl_parse_state(struct cli *cli, const char *s)
82
{
83 325
        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 325
}
93
94
struct vclprog *
95 37075
mcf_vcl_byname(const char *name)
96
{
97
        struct vclprog *vp;
98
99 62256
        VTAILQ_FOREACH(vp, &vclhead, list)
100 41925
                if (!strcmp(name, vp->name))
101 16744
                        return (vp);
102 20331
        return (NULL);
103 37075
}
104
105
static int
106 36867
mcf_invalid_vclname(struct cli *cli, const char *name)
107
{
108
        const char *bad;
109
110 36867
        AN(name);
111 36867
        bad = VCT_invalid_name(name, NULL);
112
113 36867
        if (bad != NULL) {
114 65
                VCLI_SetResult(cli, CLIS_PARAM);
115 65
                VCLI_Out(cli, "Illegal character in VCL name ");
116 65
                if (*bad > 0x20 && *bad < 0x7f)
117 52
                        VCLI_Out(cli, "('%c')", *bad);
118
                else
119 13
                        VCLI_Out(cli, "(0x%02x)", *bad & 0xff);
120 65
                return (-1);
121
        }
122 36802
        return (0);
123 36867
}
124
125
static struct vclprog *
126 16406
mcf_find_vcl(struct cli *cli, const char *name)
127
{
128
        struct vclprog *vp;
129
130 16406
        if (mcf_invalid_vclname(cli, name))
131 0
                return (NULL);
132
133 16406
        vp = mcf_vcl_byname(name);
134 16406
        if (vp == NULL) {
135 39
                VCLI_SetResult(cli, CLIS_PARAM);
136 39
                VCLI_Out(cli, "No VCL named %s known\n", name);
137 39
        }
138 16406
        return (vp);
139 16406
}
140
141
static int
142 19967
mcf_find_no_vcl(struct cli *cli, const char *name)
143
{
144
145 19967
        if (mcf_invalid_vclname(cli, name))
146 52
                return (0);
147
148 19915
        if (mcf_vcl_byname(name) != NULL) {
149 13
                VCLI_SetResult(cli, CLIS_PARAM);
150 13
                VCLI_Out(cli, "Already a VCL named %s", name);
151 13
                return (0);
152
        }
153 19902
        return (1);
154 19967
}
155
156
int
157 51583
mcf_is_label(const struct vclprog *vp)
158
{
159 51583
        return (vp->state == VCL_STATE_LABEL);
160
}
161
162
/*--------------------------------------------------------------------*/
163
164
struct vcldep *
165 624
mgt_vcl_dep_add(struct vclprog *vp_from, struct vclprog *vp_to)
166
{
167
        struct vcldep *vd;
168
169 624
        CHECK_OBJ_NOTNULL(vp_from, VCLPROG_MAGIC);
170 624
        CHECK_OBJ_NOTNULL(vp_to, VCLPROG_MAGIC);
171 624
        assert(vp_to->state != VCL_STATE_COLD);
172
173 624
        ALLOC_OBJ(vd, VCLDEP_MAGIC);
174 624
        AN(vd);
175
176 624
        mgt_vcl_set_cooldown(vp_from, -1);
177 624
        mgt_vcl_set_cooldown(vp_to, -1);
178
179 624
        vd->from = vp_from;
180 624
        VTAILQ_INSERT_TAIL(&vp_from->dfrom, vd, lfrom);
181 624
        vd->to = vp_to;
182 624
        VTAILQ_INSERT_TAIL(&vp_to->dto, vd, lto);
183 624
        vp_to->nto++;
184 624
        return (vd);
185
}
186
187
static void
188 624
mgt_vcl_dep_del(struct vcldep *vd)
189
{
190
191 624
        CHECK_OBJ_NOTNULL(vd, VCLDEP_MAGIC);
192 624
        VTAILQ_REMOVE(&vd->from->dfrom, vd, lfrom);
193 624
        VTAILQ_REMOVE(&vd->to->dto, vd, lto);
194 624
        vd->to->nto--;
195 624
        if (vd->to->nto == 0)
196 507
                mgt_vcl_set_cooldown(vd->to, VTIM_mono());
197 624
        FREE_OBJ(vd);
198 624
}
199
200
/*--------------------------------------------------------------------*/
201
202
static struct vclprog *
203 20604
mgt_vcl_add(const char *name, const struct vclstate *state)
204
{
205
        struct vclprog *vp;
206
207 20604
        assert(state == VCL_STATE_WARM ||
208
               state == VCL_STATE_COLD ||
209
               state == VCL_STATE_AUTO ||
210
               state == VCL_STATE_LABEL);
211 20604
        ALLOC_OBJ(vp, VCLPROG_MAGIC);
212 20604
        XXXAN(vp);
213 20604
        REPLACE(vp->name, name);
214 20604
        VTAILQ_INIT(&vp->dfrom);
215 20604
        VTAILQ_INIT(&vp->dto);
216 20604
        VTAILQ_INIT(&vp->vmods);
217 20604
        vp->state = state;
218
219 20604
        if (vp->state != VCL_STATE_COLD)
220 20578
                vp->warm = 1;
221
222 20604
        VTAILQ_INSERT_TAIL(&vclhead, vp, list);
223 20604
        if (vp->state != VCL_STATE_LABEL)
224 20292
                vcl_count++;
225 20604
        return (vp);
226
}
227
228
static void
229 20604
mgt_vcl_del(struct vclprog *vp)
230
{
231
        char *p;
232
        struct vmoddep *vd;
233
        struct vmodfile *vf;
234
        struct vcldep *dep;
235
236 20604
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
237 20604
        assert(VTAILQ_EMPTY(&vp->dto));
238
239 20604
        mgt_vcl_symtab_clean(vp);
240
241 21189
        while ((dep = VTAILQ_FIRST(&vp->dfrom)) != NULL) {
242 585
                assert(dep->from == vp);
243 585
                mgt_vcl_dep_del(dep);
244
        }
245
246 20604
        VTAILQ_REMOVE(&vclhead, vp, list);
247 20604
        if (vp->state != VCL_STATE_LABEL)
248 20292
                vcl_count--;
249 20604
        if (vp->fname != NULL) {
250 16341
                if (!MGT_DO_DEBUG(DBG_VCL_KEEP))
251 16289
                        AZ(unlink(vp->fname));
252 16341
                p = strrchr(vp->fname, '/');
253 16341
                AN(p);
254 16341
                *p = '\0';
255 16341
                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 16341
                (void)rmdir(vp->fname);
263 16341
                VJ_master(JAIL_MASTER_LOW);
264 16341
                free(vp->fname);
265 16341
        }
266 27260
        while (!VTAILQ_EMPTY(&vp->vmods)) {
267 6656
                vd = VTAILQ_FIRST(&vp->vmods);
268 6656
                CHECK_OBJ(vd, VMODDEP_MAGIC);
269 6656
                vf = vd->to;
270 6656
                CHECK_OBJ(vf, VMODFILE_MAGIC);
271 6656
                VTAILQ_REMOVE(&vp->vmods, vd, lfrom);
272 6656
                VTAILQ_REMOVE(&vf->vcls, vd, lto);
273 6656
                FREE_OBJ(vd);
274
275 6656
                if (VTAILQ_EMPTY(&vf->vcls)) {
276 4784
                        if (!MGT_DO_DEBUG(DBG_VMOD_SO_KEEP))
277 4771
                                AZ(unlink(vf->fname));
278 4784
                        VTAILQ_REMOVE(&vmodhead, vf, list);
279 4784
                        free(vf->fname);
280 4784
                        FREE_OBJ(vf);
281 4784
                }
282
        }
283 20604
        free(vp->name);
284 20604
        FREE_OBJ(vp);
285 20604
}
286
287
const char *
288 25323
mgt_has_vcl(void)
289
{
290 25323
        if (VTAILQ_EMPTY(&vclhead))
291 12414
                return ("No VCL loaded");
292 12909
        if (mgt_vcl_active == NULL)
293 13
                return ("No active VCL");
294 12896
        CHECK_OBJ_NOTNULL(mgt_vcl_active, VCLPROG_MAGIC);
295 12896
        AN(mgt_vcl_active->warm);
296 12896
        return (NULL);
297 25323
}
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 24201
mgt_vcl_set_cooldown(struct vclprog *vp, vtim_mono now)
308
{
309 24201
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
310
311 30568
        if (vp == mgt_vcl_active ||
312 8268
            vp->state != VCL_STATE_AUTO ||
313 6770
            vp->warm == 0 ||
314 6627
            !VTAILQ_EMPTY(&vp->dto) ||
315 6367
            !VTAILQ_EMPTY(&vp->dfrom))
316 18172
                vp->go_cold = -1;
317
        else
318 6029
                vp->go_cold = now;
319 24201
}
320
321
static int
322 16354
mgt_vcl_settemp(struct cli *cli, struct vclprog *vp, unsigned warm)
323
{
324
        int i;
325
326 16354
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
327
328 16354
        if (warm == vp->warm)
329 16250
                return (0);
330
331 104
        if (vp->state == VCL_STATE_AUTO || vp->state == VCL_STATE_LABEL) {
332 104
                mgt_vcl_set_cooldown(vp, -1);
333 104
                i = mgt_vcl_askchild(cli, vp, warm);
334 104
                mgt_vcl_set_cooldown(vp, VTIM_mono());
335 104
        } else {
336 0
                i = mgt_vcl_setstate(cli, vp,
337 0
                    warm ? VCL_STATE_WARM : VCL_STATE_COLD);
338
        }
339
340 104
        return (i);
341 16354
}
342
343
static int
344 16328
mgt_vcl_requirewarm(struct cli *cli, struct vclprog *vp)
345
{
346 16328
        if (vp->state == VCL_STATE_COLD) {
347 26
                VCLI_SetResult(cli, CLIS_CANT);
348 52
                VCLI_Out(cli, "VCL '%s' is cold - set to auto or warm first",
349 26
                    vp->name);
350 26
                return (1);
351
        }
352 16302
        return (mgt_vcl_settemp(cli, vp, 1));
353 16328
}
354
355
static int
356 806
mgt_vcl_askchild(struct cli *cli, struct vclprog *vp, unsigned warm)
357
{
358
        unsigned status;
359
        char *p;
360
        int i;
361
362 806
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
363
364 806
        if (!MCH_Running()) {
365 52
                vp->warm = warm;
366 52
                return (0);
367
        }
368
369 754
        i = mgt_cli_askchild(&status, &p, "vcl.state %s %d%s\n",
370 754
            vp->name, warm, vp->state->name);
371 754
        if (i && cli != NULL) {
372 52
                VCLI_SetResult(cli, status);
373 52
                VCLI_Out(cli, "%s", p);
374 754
        } 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 702
                vp->warm = warm;
383
        }
384
385 754
        free(p);
386 754
        return (i);
387 806
}
388
389
static int
390 702
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 702
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
397
398 702
        assert(vs != VCL_STATE_LABEL);
399
400 702
        if (mcf_is_label(vp)) {
401 0
                AN(vp->warm);
402
                /* do not touch labels */
403 0
                return (0);
404
        }
405
406 702
        if (vp->state == vs)
407 0
                return (0);
408
409 702
        os = vp->state;
410 702
        vp->state = vs;
411
412 702
        if (vp == mgt_vcl_active) {
413 26
                assert (vs == VCL_STATE_WARM || vs == VCL_STATE_AUTO);
414 26
                AN(vp->warm);
415 26
                warm = 1;
416 702
        } else if (vs == VCL_STATE_AUTO) {
417 39
                warm = vp->warm;
418 39
        } else {
419 637
                warm = (vs == VCL_STATE_WARM ? 1 : 0);
420
        }
421
422 702
        i = mgt_vcl_askchild(cli, vp, warm);
423 702
        if (i == 0)
424 663
                mgt_vcl_set_cooldown(vp, VTIM_mono());
425
        else
426 39
                vp->state = os;
427 702
        return (i);
428 702
}
429
430
/*--------------------------------------------------------------------*/
431
432
static struct vclprog *
433 20305
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 20305
        AN(cli);
442
443 20305
        if (vcl_count >= mgt_param.max_vcl &&
444 26
            mgt_param.max_vcl_handling == 2) {
445 13
                VCLI_Out(cli, "Too many (%d) VCLs already loaded\n", vcl_count);
446 13
                VCLI_Out(cli, "(See max_vcl and max_vcl_handling parameters)");
447 13
                VCLI_SetResult(cli, CLIS_CANT);
448 13
                return (NULL);
449
        }
450
451 20292
        if (state == NULL)
452 20240
                vs = VCL_STATE_AUTO;
453
        else
454 52
                vs = mcf_vcl_parse_state(cli, state);
455
456 20292
        if (vs == NULL)
457 0
                return (NULL);
458
459 20292
        vp = mgt_vcl_add(vclname, vs);
460 20292
        lib = mgt_VccCompile(cli, vp, vclname, vclsrc, vclsrcfile, C_flag);
461 20292
        if (lib == NULL) {
462 3951
                mgt_vcl_del(vp);
463 3951
                return (NULL);
464
        }
465
466 16341
        AZ(C_flag);
467 16341
        vp->fname = lib;
468
469 16354
        if ((cli->result == CLIS_OK || cli->result == CLIS_TRUNCATED) &&
470 16341
            vcl_count > mgt_param.max_vcl &&
471 13
            mgt_param.max_vcl_handling == 1) {
472 13
                VCLI_Out(cli, "%d VCLs loaded\n", vcl_count);
473 13
                VCLI_Out(cli, "Remember to vcl.discard the old/unused VCLs.\n");
474 13
                VCLI_Out(cli, "(See max_vcl and max_vcl_handling parameters)");
475 13
        }
476
477 16341
        if (!MCH_Running())
478 13221
                return (vp);
479
480 3120
        if (mgt_cli_askchild(&status, &p, "vcl.load %s %s %d%s\n",
481 3120
            vp->name, vp->fname, vp->warm, vp->state->name)) {
482 546
                mgt_vcl_del(vp);
483 546
                VCLI_Out(cli, "%s", p);
484 546
                VCLI_SetResult(cli, status);
485 546
                free(p);
486 546
                return (NULL);
487
        }
488 2574
        free(p);
489
490 2574
        mgt_vcl_set_cooldown(vp, VTIM_mono());
491 2574
        return (vp);
492 20305
}
493
494
/*--------------------------------------------------------------------*/
495
496
void
497 403
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 403
        AZ(MCH_Running());
505
506 403
        AN(vclsrc);
507 403
        AN(origin);
508 403
        if (vclname == NULL) {
509 26
                bprintf(buf, "boot%d", n++);
510 26
                vclname = buf;
511 26
        }
512 403
        vp = mgt_new_vcl(cli, vclname, vclsrc, origin, NULL, C_flag);
513 403
        if (vp != NULL) {
514
                /* Last startup VCL becomes the automatically selected
515
                 * active VCL. */
516 338
                AN(vp->warm);
517 338
                mgt_vcl_active = vp;
518 338
        }
519 403
}
520
521
/*--------------------------------------------------------------------*/
522
523
int
524 12597
mgt_push_vcls(struct cli *cli, unsigned *status, char **p)
525
{
526
        struct vclprog *vp;
527
        struct vcldep *vd;
528
        int done;
529
530 12597
        AN(mgt_vcl_active);
531
532
        /* The VCL has not been loaded yet, it cannot fail */
533 12597
        (void)cli;
534
535 26000
        VTAILQ_FOREACH(vp, &vclhead, list)
536 13403
                vp->loaded = 0;
537
538 12597
        do {
539 12597
                done = 1;
540 25974
                VTAILQ_FOREACH(vp, &vclhead, list) {
541 13390
                        if (vp->loaded)
542 0
                                continue;
543 13559
                        VTAILQ_FOREACH(vd, &vp->dfrom, lfrom)
544 169
                                if (!vd->to->loaded)
545 0
                                        break;
546 13390
                        if (vd != NULL) {
547 0
                                done = 0;
548 0
                                continue;
549
                        }
550 13390
                        if (mcf_is_label(vp)) {
551 104
                                vd = VTAILQ_FIRST(&vp->dfrom);
552 104
                                AN(vd);
553 208
                                if (mgt_cli_askchild(status, p,
554
                                    "vcl.label %s %s\n",
555 104
                                    vp->name, vd->to->name))
556 0
                                        return (1);
557 104
                        } else {
558 26572
                                if (mgt_cli_askchild(status, p,
559
                                    "vcl.load \"%s\" %s %d%s\n",
560 13286
                                    vp->name, vp->fname, vp->warm,
561 13286
                                    vp->state->name))
562 13
                                        return (1);
563
                        }
564 13377
                        vp->loaded = 1;
565 13377
                        free(*p);
566 13377
                        *p = NULL;
567 13377
                }
568 12584
        } while (!done);
569
570 25168
        if (mgt_cli_askchild(status, p, "vcl.use \"%s\"\n",
571 12584
              mgt_vcl_active->name)) {
572 0
                return (1);
573
        }
574 12584
        free(*p);
575 12584
        *p = NULL;
576 12584
        return (0);
577 12597
}
578
579
/*--------------------------------------------------------------------*/
580
581
static void v_matchproto_(cli_func_t)
582 19785
mcf_vcl_inline(struct cli *cli, const char * const *av, void *priv)
583
{
584
        struct vclprog *vp;
585
586 19785
        (void)priv;
587
588 19785
        if (!mcf_find_no_vcl(cli, av[2]))
589 52
                return;
590
591 19733
        vp = mgt_new_vcl(cli, av[2], av[3], "<vcl.inline>", av[4], 0);
592 19733
        if (vp != NULL && !MCH_Running())
593 12831
                VCLI_Out(cli, "VCL compiled.\n");
594 19785
}
595
596
static void v_matchproto_(cli_func_t)
597 182
mcf_vcl_load(struct cli *cli, const char * const *av, void *priv)
598
{
599
        struct vclprog *vp;
600
601 182
        (void)priv;
602 182
        if (!mcf_find_no_vcl(cli, av[2]))
603 13
                return;
604
605 169
        vp = mgt_new_vcl(cli, av[2], NULL, av[3], av[4], 0);
606 169
        if (vp != NULL && !MCH_Running())
607 52
                VCLI_Out(cli, "VCL compiled.\n");
608 182
}
609
610
611
static void v_matchproto_(cli_func_t)
612 286
mcf_vcl_state(struct cli *cli, const char * const *av, void *priv)
613
{
614
        const struct vclstate *state;
615
        struct vclprog *vp;
616
617 286
        (void)priv;
618 286
        vp = mcf_find_vcl(cli, av[2]);
619 286
        if (vp == NULL)
620 0
                return;
621
622 286
        if (mcf_is_label(vp)) {
623 13
                VCLI_Out(cli, "Labels are always warm");
624 13
                VCLI_SetResult(cli, CLIS_PARAM);
625 13
                return;
626
        }
627
628 273
        state = mcf_vcl_parse_state(cli, av[3]);
629 273
        if (state == NULL)
630 0
                return;
631
632 273
        if (state == VCL_STATE_COLD) {
633 143
                if (!VTAILQ_EMPTY(&vp->dto)) {
634 13
                        assert(vp->state != VCL_STATE_COLD);
635 13
                        VCLI_Out(cli, "A labeled VCL cannot be set cold");
636 13
                        VCLI_SetResult(cli, CLIS_CANT);
637 13
                        return;
638
                }
639 130
                if (vp == mgt_vcl_active) {
640 13
                        VCLI_Out(cli, "Cannot set the active VCL cold.");
641 13
                        VCLI_SetResult(cli, CLIS_CANT);
642 13
                        return;
643
                }
644 117
        }
645
646 247
        (void)mgt_vcl_setstate(cli, vp, state);
647 286
}
648
649
static void v_matchproto_(cli_func_t)
650 15652
mcf_vcl_use(struct cli *cli, const char * const *av, void *priv)
651
{
652
        unsigned status;
653 15652
        char *p = NULL;
654
        struct vclprog *vp, *vp2;
655
        vtim_mono now;
656
657 15652
        (void)priv;
658 15652
        vp = mcf_find_vcl(cli, av[2]);
659 15652
        if (vp == NULL)
660 13
                return;
661 15639
        if (vp == mgt_vcl_active)
662 78
                return;
663
664 15561
        if (mgt_vcl_requirewarm(cli, vp))
665 13
                return;
666
667 15548
        if (MCH_Running() &&
668 2665
            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 15548
                VCLI_Out(cli, "VCL '%s' now active", av[2]);
673 15548
                vp2 = mgt_vcl_active;
674 15548
                mgt_vcl_active = vp;
675 15548
                now = VTIM_mono();
676 15548
                mgt_vcl_set_cooldown(vp, now);
677 15548
                if (vp2 != NULL)
678 3393
                        mgt_vcl_set_cooldown(vp2, now);
679
        }
680 15548
        free(p);
681 15652
}
682
683
static void
684 520
mgt_vcl_discard(struct cli *cli, struct vclprog *vp)
685
{
686 520
        char *p = NULL;
687
        unsigned status;
688
689 520
        AN(vp);
690 520
        AN(vp->discard);
691 520
        assert(vp != mgt_vcl_active);
692
693 624
        while (!VTAILQ_EMPTY(&vp->dto))
694 104
                mgt_vcl_discard(cli, VTAILQ_FIRST(&vp->dto)->from);
695
696 520
        if (mcf_is_label(vp)) {
697 65
                AN(vp->warm);
698 65
                vp->warm = 0;
699 65
        } else {
700 455
                (void)mgt_vcl_setstate(cli, vp, VCL_STATE_COLD);
701
        }
702 520
        if (MCH_Running()) {
703 507
                AZ(vp->warm);
704 507
                if (mgt_cli_askchild(&status, &p, "vcl.discard %s\n", vp->name))
705 0
                        assert(status == CLIS_OK || status == CLIS_COMMS);
706 507
                free(p);
707 507
        }
708 520
        VTAILQ_REMOVE(&discardhead, vp, discard_list);
709 520
        mgt_vcl_del(vp);
710 520
}
711
712
static int
713 624
mgt_vcl_discard_mark(struct cli *cli, const char *glob)
714
{
715
        struct vclprog *vp;
716 624
        unsigned marked = 0;
717
718 2600
        VTAILQ_FOREACH(vp, &vclhead, list) {
719 2067
                if (fnmatch(glob, vp->name, 0))
720 1209
                        continue;
721 858
                if (vp == mgt_vcl_active) {
722 91
                        VCLI_SetResult(cli, CLIS_CANT);
723 182
                        VCLI_Out(cli, "Cannot discard active VCL program %s\n",
724 91
                            vp->name);
725 91
                        return (-1);
726
                }
727 767
                if (!vp->discard)
728 767
                        VTAILQ_INSERT_TAIL(&discardhead, vp, discard_list);
729 767
                vp->discard = 1;
730 767
                marked++;
731 767
        }
732
733 533
        if (marked == 0) {
734 52
                VCLI_SetResult(cli, CLIS_PARAM);
735 52
                VCLI_Out(cli, "No VCL name matches %s\n", glob);
736 52
        }
737
738 533
        return (marked);
739 624
}
740
741
static void
742 52
mgt_vcl_discard_depfail(struct cli *cli, struct vclprog *vp)
743
{
744
        struct vcldep *vd;
745
        int n;
746
747 52
        AN(cli);
748 52
        AN(vp);
749 52
        assert(!VTAILQ_EMPTY(&vp->dto));
750
751 52
        VCLI_SetResult(cli, CLIS_CANT);
752 52
        AN(vp->warm);
753 52
        if (!mcf_is_label(vp))
754 78
                VCLI_Out(cli, "Cannot discard labeled VCL program %s:\n",
755 39
                    vp->name);
756
        else
757 26
                VCLI_Out(cli,
758
                    "Cannot discard VCL label %s, other VCLs depend on it:\n",
759 13
                    vp->name);
760 52
        n = 0;
761 169
        VTAILQ_FOREACH(vd, &vp->dto, lto) {
762 117
                if (n++ == 5) {
763 0
                        VCLI_Out(cli, "\t[...]");
764 0
                        break;
765
                }
766 117
                VCLI_Out(cli, "\t%s\n", vd->from->name);
767 117
        }
768 52
}
769
770
static int
771 598
mgt_vcl_discard_depcheck(struct cli *cli)
772
{
773
        struct vclprog *vp;
774
        struct vcldep *vd;
775
776 1118
        VTAILQ_FOREACH(vp, &discardhead, discard_list) {
777 676
                VTAILQ_FOREACH(vd, &vp->dto, lto)
778 156
                        if (!vd->from->discard) {
779 52
                                mgt_vcl_discard_depfail(cli, vp);
780 52
                                return (-1);
781
                        }
782 520
        }
783
784 546
        return (0);
785 598
}
786
787
static void
788 195
mgt_vcl_discard_clear(void)
789
{
790
        struct vclprog *vp, *vp2;
791
792 442
        VTAILQ_FOREACH_SAFE(vp, &discardhead, discard_list, vp2) {
793 247
                AN(vp->discard);
794 247
                vp->discard = 0;
795 247
                VTAILQ_REMOVE(&discardhead, vp, discard_list);
796 247
        }
797 195
}
798
799
static void v_matchproto_(cli_func_t)
800 598
mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv)
801
{
802
        const struct vclprog *vp;
803
804 598
        (void)priv;
805
806 598
        assert(VTAILQ_EMPTY(&discardhead));
807 2561
        VTAILQ_FOREACH(vp, &vclhead, list)
808 1963
                AZ(vp->discard);
809
810 1079
        for (av += 2; *av != NULL; av++) {
811 624
                if (mgt_vcl_discard_mark(cli, *av) <= 0) {
812 143
                        mgt_vcl_discard_clear();
813 143
                        break;
814
                }
815 481
        }
816
817 598
        if (mgt_vcl_discard_depcheck(cli) != 0)
818 52
                mgt_vcl_discard_clear();
819
820 1014
        while (!VTAILQ_EMPTY(&discardhead))
821 416
                mgt_vcl_discard(cli, VTAILQ_FIRST(&discardhead));
822 598
}
823
824
static void v_matchproto_(cli_func_t)
825 1170
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 1170
        (void)av;
837 1170
        (void)priv;
838
839 1170
        if (MCH_Running()) {
840 1001
                if (!mgt_cli_askchild(&status, &p, "vcl.list\n")) {
841 1001
                        VCLI_SetResult(cli, status);
842 1001
                        VCLI_Out(cli, "%s", p);
843 1001
                }
844 1001
                free(p);
845 1001
        } else {
846 169
                vte = VTE_new(7, 80);
847 169
                AN(vte);
848
849 637
                VTAILQ_FOREACH(vp, &vclhead, list) {
850 936
                        VTE_printf(vte, "%s",
851 468
                            vp == mgt_vcl_active ? "active" : "available");
852 468
                        vs = vp->warm ?  VCL_STATE_WARM : VCL_STATE_COLD;
853 468
                        VTE_printf(vte, "\t%s\t%s", vp->state->name, vs->name);
854 468
                        VTE_printf(vte, "\t-\t%s", vp->name);
855 468
                        if (mcf_is_label(vp)) {
856 91
                                vd = VTAILQ_FIRST(&vp->dfrom);
857 91
                                AN(vd);
858 91
                                VTE_printf(vte, "\t->\t%s", vd->to->name);
859 91
                                if (vp->nto > 0)
860 52
                                        VTE_printf(vte, " (%d return(vcl)%s)",
861 26
                                            vp->nto, vp->nto > 1 ? "'s" : "");
862 468
                        } else if (vp->nto > 0) {
863 130
                                VTE_printf(vte, "\t<-\t(%d label%s)",
864 65
                                    vp->nto, vp->nto > 1 ? "s" : "");
865 65
                        }
866 468
                        VTE_cat(vte, "\n");
867 468
                }
868 169
                AZ(VTE_finish(vte));
869 169
                AZ(VTE_format(vte, VCLI_VTE_format, cli));
870 169
                VTE_destroy(&vte);
871
        }
872 1170
}
873
874
static void v_matchproto_(cli_func_t)
875 104
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 104
        (void)priv;
886 104
        if (MCH_Running()) {
887 65
                if (!mgt_cli_askchild(&status, &p, "vcl.list -j\n")) {
888 65
                        VCLI_SetResult(cli, status);
889 65
                        VCLI_Out(cli, "%s", p);
890 65
                }
891 65
                free(p);
892 65
        } else {
893 39
                VCLI_JSON_begin(cli, 2, av);
894 130
                VTAILQ_FOREACH(vp, &vclhead, list) {
895 91
                        VCLI_Out(cli, ",\n");
896 91
                        VCLI_Out(cli, "{\n");
897 91
                        VSB_indent(cli->sb, 2);
898 182
                        VCLI_Out(cli, "\"status\": \"%s\",\n",
899 91
                            vp == mgt_vcl_active ? "active" : "available");
900 91
                        VCLI_Out(cli, "\"state\": \"%s\",\n", vp->state->name);
901 91
                        vs = vp->warm ?  VCL_STATE_WARM : VCL_STATE_COLD;
902 91
                        VCLI_Out(cli, "\"temperature\": \"%s\",\n", vs->name);
903 91
                        VCLI_Out(cli, "\"name\": \"%s\"", vp->name);
904 91
                        if (mcf_is_label(vp)) {
905 39
                                vd = VTAILQ_FIRST(&vp->dfrom);
906 39
                                AN(vd);
907 39
                                VCLI_Out(cli, ",\n");
908 39
                                VCLI_Out(cli, "\"label\": {\n");
909 39
                                VSB_indent(cli->sb, 2);
910 39
                                VCLI_Out(cli, "\"name\": \"%s\"", vd->to->name);
911 39
                                if (vp->nto > 0)
912 0
                                        VCLI_Out(cli, ",\n\"refs\": %d",
913 0
                                                 vp->nto);
914 39
                                VSB_indent(cli->sb, -2);
915 39
                                VCLI_Out(cli, "\n");
916 39
                                VCLI_Out(cli, "}");
917 91
                        } else if (vp->nto > 0) {
918 13
                                VCLI_Out(cli, ",\n");
919 13
                                VCLI_Out(cli, "\"labels\": %d", vp->nto);
920 13
                        }
921 91
                        VSB_indent(cli->sb, -2);
922 91
                        VCLI_Out(cli, "\n}");
923 91
                }
924 39
                VCLI_JSON_end(cli);
925
        }
926 104
}
927
928
static int
929 182
mcf_vcl_check_dag(struct cli *cli, struct vclprog *tree, struct vclprog *target)
930
{
931
        struct vcldep *vd;
932
933 182
        if (target == tree)
934 39
                return (1);
935 143
        VTAILQ_FOREACH(vd, &tree->dfrom, lfrom) {
936 117
                if (!mcf_vcl_check_dag(cli, vd->to, target))
937 0
                        continue;
938 117
                if (mcf_is_label(tree))
939 78
                        VCLI_Out(cli, "Label %s points to vcl %s\n",
940 39
                            tree->name, vd->to->name);
941
                else
942 156
                        VCLI_Out(cli, "Vcl %s uses Label %s\n",
943 78
                            tree->name, vd->to->name);
944 117
                return (1);
945
        }
946 26
        return (0);
947 182
}
948
949
static void v_matchproto_(cli_func_t)
950 494
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 494
        (void)priv;
959 494
        if (mcf_invalid_vclname(cli, av[2]))
960 13
                return;
961 481
        vpl = mcf_vcl_byname(av[2]);
962 481
        if (vpl != NULL && !mcf_is_label(vpl)) {
963 13
                VCLI_SetResult(cli, CLIS_PARAM);
964 13
                VCLI_Out(cli, "%s is not a label", vpl->name);
965 13
                return;
966
        }
967 468
        vpt = mcf_find_vcl(cli, av[3]);
968 468
        if (vpt == NULL)
969 26
                return;
970 442
        if (mcf_is_label(vpt)) {
971 26
                VCLI_SetResult(cli, CLIS_CANT);
972 26
                VCLI_Out(cli, "VCL labels cannot point to labels");
973 26
                return;
974
        }
975 416
        if (vpl != NULL) {
976 78
                if (VTAILQ_FIRST(&vpl->dfrom)->to != vpt &&
977 65
                    mcf_vcl_check_dag(cli, vpt, vpl)) {
978 78
                        VCLI_Out(cli,
979
                            "Pointing label %s at %s would create a loop",
980 39
                            vpl->name, vpt->name);
981 39
                        VCLI_SetResult(cli, CLIS_PARAM);
982 39
                        return;
983
                }
984 39
        }
985
986 377
        if (mgt_vcl_requirewarm(cli, vpt))
987 26
                return;
988
989 351
        if (vpl != NULL) {
990
                /* potentially fail before we delete the dependency */
991 39
                if (mgt_vcl_requirewarm(cli, vpl))
992 0
                        return;
993 39
                mgt_vcl_dep_del(VTAILQ_FIRST(&vpl->dfrom));
994 39
                AN(VTAILQ_EMPTY(&vpl->dfrom));
995 39
        } else {
996 312
                vpl = mgt_vcl_add(av[2], VCL_STATE_LABEL);
997
        }
998
999 351
        AN(vpl);
1000 351
        if (mgt_vcl_requirewarm(cli, vpl))
1001 0
                return;
1002 351
        (void)mgt_vcl_dep_add(vpl, vpt);
1003
1004 351
        if (!MCH_Running())
1005 104
                return;
1006
1007 247
        i = mgt_cli_askchild(&status, &p, "vcl.label %s %s\n", av[2], av[3]);
1008 247
        if (i) {
1009 0
                VCLI_SetResult(cli, status);
1010 0
                VCLI_Out(cli, "%s", p);
1011 0
        }
1012 247
        free(p);
1013 494
}
1014
1015
static void v_matchproto_(cli_func_t)
1016 13
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 13
        (void)av;
1023 13
        (void)priv;
1024
1025 13
        vte = VTE_new(2, 80);
1026 13
        AN(vte);
1027
1028 156
        VTAILQ_FOREACH(vp, &vclhead, list) {
1029 143
                if (VTAILQ_EMPTY(&vp->dfrom)) {
1030 39
                        VTE_printf(vte, "%s\n", vp->name);
1031 39
                        continue;
1032
                }
1033 208
                VTAILQ_FOREACH(vd, &vp->dfrom, lfrom)
1034 104
                        VTE_printf(vte, "%s\t%s\n", vp->name, vd->to->name);
1035 104
        }
1036 13
        AZ(VTE_finish(vte));
1037 13
        AZ(VTE_format(vte, VCLI_VTE_format, cli));
1038 13
        VTE_destroy(&vte);
1039 13
}
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 4701
mgt_vcl_poker(const struct vev *e, int what)
1081
{
1082
        struct vclprog *vp;
1083
        vtim_mono now;
1084
1085 4701
        (void)e;
1086 4701
        (void)what;
1087 4701
        e_poker->timeout = mgt_param.vcl_cooldown * .45;
1088 4701
        now = VTIM_mono();
1089 12480
        VTAILQ_FOREACH(vp, &vclhead, list) {
1090 7779
                if (vp->go_cold == 0)
1091 60
                        mgt_vcl_set_cooldown(vp, now);
1092 7719
                else if (vp->go_cold > 0 &&
1093 1497
                    vp->go_cold + mgt_param.vcl_cooldown < now)
1094 52
                        (void)mgt_vcl_settemp(NULL, vp, 0);
1095 7779
        }
1096 4701
        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 61709
mgt_vcl_atexit(void)
1118
{
1119
        struct vclprog *vp, *vp2;
1120
1121 61709
        if (getpid() != heritage.mgt_pid)
1122 48853
                return;
1123 12856
        mgt_vcl_active = NULL;
1124 25687
        while (!VTAILQ_EMPTY(&vclhead))
1125 29120
                VTAILQ_FOREACH_SAFE(vp, &vclhead, list, vp2)
1126 31876
                        if (VTAILQ_EMPTY(&vp->dto))
1127 15587
                                mgt_vcl_del(vp);
1128 61709
}
1129
1130
void
1131 12856
mgt_vcl_init(void)
1132
{
1133
1134 12856
        e_poker = VEV_Alloc();
1135 12856
        AN(e_poker);
1136 12856
        e_poker->timeout = 3;           // random, prime
1137
1138 12856
        e_poker->callback = mgt_vcl_poker;
1139 12856
        e_poker->name = "vcl poker";
1140 12856
        AZ(VEV_Start(mgt_evb, e_poker));
1141
1142 12856
        AZ(atexit(mgt_vcl_atexit));
1143
1144 12856
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_vcl);
1145 12856
}