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