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