varnish-cache/bin/varnishd/mgt/mgt_vcl.c
1
/*-
2 19
 * Copyright (c) 2006 Verdens Gang AS
3 10
 * Copyright (c) 2006-2015 Varnish Software AS
4 4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 * VCL management stuff
30
 */
31
32
#include "config.h"
33
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <unistd.h>
38
39
#include "mgt/mgt.h"
40
#include "mgt/mgt_vcl.h"
41
#include "common/heritage.h"
42
43
#include "vcli_serve.h"
44
#include "vct.h"
45
#include "vev.h"
46
#include "vtim.h"
47
48
struct vclstate {
49
        const char              *name;
50
};
51
52
#define VCL_STATE(sym, str)                                             \
53
        static const struct vclstate VCL_STATE_ ## sym[1] = {{ str }};
54
#include "tbl/vcl_states.h"
55
56
static const struct vclstate VCL_STATE_LABEL[1] = {{ "label" }};
57
58
static int vcl_count;
59
60
struct vclproghead vclhead = VTAILQ_HEAD_INITIALIZER(vclhead);
61
struct vmodfilehead vmodhead = VTAILQ_HEAD_INITIALIZER(vmodhead);
62
static struct vclprog           *active_vcl;
63
static struct vev *e_poker;
64
65
static int mgt_vcl_setstate(struct cli *, struct vclprog *,
66
    const struct vclstate *);
67
static int mgt_vcl_settemp(struct cli *, struct vclprog *, unsigned);
68
static int mgt_vcl_askchild(struct cli *, struct vclprog *, unsigned);
69
static void mgt_vcl_set_cooldown(struct vclprog *, vtim_mono);
70
71
/*--------------------------------------------------------------------*/
72
73
static const struct vclstate *
74 19
mcf_vcl_parse_state(struct cli *cli, const char *s)
75
{
76 19
        if (s != NULL) {
77
#define VCL_STATE(sym, str)                             \
78
                if (!strcmp(s, str))                    \
79
                        return (VCL_STATE_ ## sym);
80
#include "tbl/vcl_states.h"
81 0
        }
82 0
        VCLI_Out(cli, "State must be one of auto, cold or warm.");
83 0
        VCLI_SetResult(cli, CLIS_PARAM);
84 0
        return (NULL);
85 19
}
86
87
struct vclprog *
88 2233
mcf_vcl_byname(const char *name)
89
{
90
        struct vclprog *vp;
91
92 3693
        VTAILQ_FOREACH(vp, &vclhead, list)
93 2511
                if (!strcmp(name, vp->name))
94 1051
                        return (vp);
95 1182
        return (NULL);
96 2233
}
97
98
static int
99 2219
mcf_invalid_vclname(struct cli *cli, const char *name)
100
{
101
        const char *bad;
102
103 2219
        AN(name);
104 2219
        bad = VCT_invalid_name(name, NULL);
105
106 2219
        if (bad != NULL) {
107 5
                VCLI_SetResult(cli, CLIS_PARAM);
108 5
                VCLI_Out(cli, "Illegal character in VCL name ");
109 5
                if (*bad > 0x20 && *bad < 0x7f)
110 4
                        VCLI_Out(cli, "('%c')", *bad);
111
                else
112 1
                        VCLI_Out(cli, "(0x%02x)", *bad & 0xff);
113 5
                return (-1);
114
        }
115 2214
        return (0);
116 2219
}
117
118
static struct vclprog *
119 1031
mcf_find_vcl(struct cli *cli, const char *name)
120
{
121
        struct vclprog *vp;
122
123 1031
        if (mcf_invalid_vclname(cli, name))
124 0
                return (NULL);
125
126 1031
        vp = mcf_vcl_byname(name);
127 1031
        if (vp == NULL) {
128 7
                VCLI_SetResult(cli, CLIS_PARAM);
129 7
                VCLI_Out(cli, "No VCL named %s known.", name);
130 7
        }
131 1031
        return (vp);
132 1031
}
133
134
static int
135 1152
mcf_find_no_vcl(struct cli *cli, const char *name)
136
{
137
138 1152
        if (mcf_invalid_vclname(cli, name))
139 4
                return (0);
140
141 1148
        if (mcf_vcl_byname(name) != NULL) {
142 1
                VCLI_SetResult(cli, CLIS_PARAM);
143 1
                VCLI_Out(cli, "Already a VCL named %s", name);
144 1
                return (0);
145
        }
146 1147
        return (1);
147 1152
}
148
149
int
150 2910
mcf_is_label(const struct vclprog *vp)
151
{
152 2910
        return (vp->state == VCL_STATE_LABEL);
153
}
154
155
/*--------------------------------------------------------------------*/
156
157
struct vcldep *
158 44
mgt_vcl_dep_add(struct vclprog *vp_from, struct vclprog *vp_to)
159
{
160
        struct vcldep *vd;
161
162 44
        CHECK_OBJ_NOTNULL(vp_from, VCLPROG_MAGIC);
163 44
        CHECK_OBJ_NOTNULL(vp_to, VCLPROG_MAGIC);
164 44
        assert(vp_to->state != VCL_STATE_COLD);
165
166 44
        ALLOC_OBJ(vd, VCLDEP_MAGIC);
167 44
        AN(vd);
168
169 44
        mgt_vcl_set_cooldown(vp_from, -1);
170 44
        mgt_vcl_set_cooldown(vp_to, -1);
171
172 44
        vd->from = vp_from;
173 44
        VTAILQ_INSERT_TAIL(&vp_from->dfrom, vd, lfrom);
174 44
        vd->to = vp_to;
175 44
        VTAILQ_INSERT_TAIL(&vp_to->dto, vd, lto);
176 44
        vp_to->nto++;
177 44
        return (vd);
178
}
179
180
static void
181 44
mgt_vcl_dep_del(struct vcldep *vd)
182
{
183
184 44
        CHECK_OBJ_NOTNULL(vd, VCLDEP_MAGIC);
185 44
        VTAILQ_REMOVE(&vd->from->dfrom, vd, lfrom);
186 44
        VTAILQ_REMOVE(&vd->to->dto, vd, lto);
187 44
        vd->to->nto--;
188 44
        if (vd->to->nto == 0)
189 35
                mgt_vcl_set_cooldown(vd->to, VTIM_mono());
190 44
        FREE_OBJ(vd);
191 44
}
192
193
/*--------------------------------------------------------------------*/
194
195
static struct vclprog *
196 1181
mgt_vcl_add(const char *name, const struct vclstate *state)
197
{
198
        struct vclprog *vp;
199
200 1181
        assert(state == VCL_STATE_WARM ||
201
               state == VCL_STATE_COLD ||
202
               state == VCL_STATE_AUTO ||
203
               state == VCL_STATE_LABEL);
204 1181
        ALLOC_OBJ(vp, VCLPROG_MAGIC);
205 1181
        XXXAN(vp);
206 1181
        REPLACE(vp->name, name);
207 1181
        VTAILQ_INIT(&vp->dfrom);
208 1181
        VTAILQ_INIT(&vp->dto);
209 1181
        VTAILQ_INIT(&vp->vmods);
210 1181
        vp->state = state;
211
212 1181
        if (vp->state != VCL_STATE_COLD)
213 1180
                vp->warm = 1;
214
215 1181
        VTAILQ_INSERT_TAIL(&vclhead, vp, list);
216 1181
        if (vp->state != VCL_STATE_LABEL)
217 1159
                vcl_count++;
218 1181
        return (vp);
219
}
220
221
static void
222 1181
mgt_vcl_del(struct vclprog *vp)
223
{
224
        char *p;
225
        struct vmoddep *vd;
226
        struct vmodfile *vf;
227
228 1181
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
229 1181
        assert(VTAILQ_EMPTY(&vp->dto));
230
231 1181
        mgt_vcl_symtab_clean(vp);
232
233 1222
        while (!VTAILQ_EMPTY(&vp->dfrom))
234 41
                mgt_vcl_dep_del(VTAILQ_FIRST(&vp->dfrom));
235
236 1181
        VTAILQ_REMOVE(&vclhead, vp, list);
237 1181
        if (vp->state != VCL_STATE_LABEL)
238 1159
                vcl_count--;
239 1181
        if (vp->fname != NULL) {
240 961
                if (!MGT_DO_DEBUG(DBG_VCL_KEEP))
241 957
                        AZ(unlink(vp->fname));
242 961
                p = strrchr(vp->fname, '/');
243 961
                AN(p);
244 961
                *p = '\0';
245 961
                VJ_master(JAIL_MASTER_FILE);
246
                /*
247
                 * This will fail if any files are dropped next to the library
248
                 * without us knowing.  This happens for instance with GCOV.
249
                 * Assume developers know how to clean up after themselves
250
                 * (or alternatively:  How to run out of disk space).
251
                 */
252 961
                (void)rmdir(vp->fname);
253 961
                VJ_master(JAIL_MASTER_LOW);
254 961
                free(vp->fname);
255 961
        }
256 1565
        while (!VTAILQ_EMPTY(&vp->vmods)) {
257 384
                vd = VTAILQ_FIRST(&vp->vmods);
258 384
                CHECK_OBJ(vd, VMODDEP_MAGIC);
259 384
                vf = vd->to;
260 384
                CHECK_OBJ(vf, VMODFILE_MAGIC);
261 384
                VTAILQ_REMOVE(&vp->vmods, vd, lfrom);
262 384
                VTAILQ_REMOVE(&vf->vcls, vd, lto);
263 384
                FREE_OBJ(vd);
264
265 384
                if (VTAILQ_EMPTY(&vf->vcls)) {
266 258
                        if (!MGT_DO_DEBUG(DBG_VMOD_SO_KEEP))
267 257
                                AZ(unlink(vf->fname));
268 258
                        VTAILQ_REMOVE(&vmodhead, vf, list);
269 258
                        free(vf->fname);
270 258
                        FREE_OBJ(vf);
271 258
                }
272
        }
273 1181
        free(vp->name);
274 1181
        FREE_OBJ(vp);
275 1181
}
276
277
int
278 1508
mgt_has_vcl(void)
279
{
280
281 1508
        return (!VTAILQ_EMPTY(&vclhead));
282
}
283
284
/*
285
 * go_cold
286
 *
287
 * -1: leave alone
288
 *  0: timer not started - not currently used
289
 * >0: when timer started
290
 */
291
static void
292 1117
mgt_vcl_set_cooldown(struct vclprog *vp, vtim_mono now)
293
{
294 1117
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
295
296 1486
        if (vp == active_vcl ||
297 496
            vp->state != VCL_STATE_AUTO ||
298 398
            vp->warm == 0 ||
299 387
            !VTAILQ_EMPTY(&vp->dto) ||
300 369
            !VTAILQ_EMPTY(&vp->dfrom))
301 772
                vp->go_cold = -1;
302
        else
303 345
                vp->go_cold = now;
304 1117
}
305
306
static int
307 251
mgt_vcl_settemp(struct cli *cli, struct vclprog *vp, unsigned warm)
308
{
309
        int i;
310
311 251
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
312
313 251
        if (warm == vp->warm)
314 243
                return (0);
315
316 8
        if (vp->state == VCL_STATE_AUTO || vp->state == VCL_STATE_LABEL) {
317 8
                mgt_vcl_set_cooldown(vp, -1);
318 8
                i = mgt_vcl_askchild(cli, vp, warm);
319 8
                mgt_vcl_set_cooldown(vp, VTIM_mono());
320 8
        } else {
321 0
                i = mgt_vcl_setstate(cli, vp,
322 0
                    warm ? VCL_STATE_WARM : VCL_STATE_COLD);
323
        }
324
325 8
        return (i);
326 251
}
327
328
static int
329 249
mgt_vcl_requirewarm(struct cli *cli, struct vclprog *vp)
330
{
331 249
        if (vp->state == VCL_STATE_COLD) {
332 2
                VCLI_SetResult(cli, CLIS_CANT);
333 4
                VCLI_Out(cli, "VCL '%s' is cold - set to auto or warm first",
334 2
                    vp->name);
335 2
                return (1);
336
        }
337 247
        return (mgt_vcl_settemp(cli, vp, 1));
338 249
}
339
340
static int
341 52
mgt_vcl_askchild(struct cli *cli, struct vclprog *vp, unsigned warm)
342
{
343
        unsigned status;
344
        char *p;
345
        int i;
346
347 52
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
348
349 52
        if (!MCH_Running()) {
350 3
                vp->warm = warm;
351 3
                return (0);
352
        }
353
354 49
        i = mgt_cli_askchild(&status, &p, "vcl.state %s %d%s\n",
355 49
            vp->name, warm, vp->state->name);
356 49
        if (i && cli != NULL) {
357 3
                VCLI_SetResult(cli, status);
358 3
                VCLI_Out(cli, "%s", p);
359 49
        } else if (i) {
360 0
                MGT_Complain(C_ERR,
361
                    "Please file ticket: VCL poker problem: "
362
                    "'vcl.state %s %d%s' -> %03d '%s'",
363 0
                    vp->name, warm, vp->state->name, i, p);
364 0
        } else {
365
                /* Success, update mgt's VCL state to reflect child's
366
                   state */
367 46
                vp->warm = warm;
368
        }
369
370 49
        free(p);
371 49
        return (i);
372 52
}
373
374
static int
375 44
mgt_vcl_setstate(struct cli *cli, struct vclprog *vp, const struct vclstate *vs)
376
{
377
        unsigned warm;
378
        int i;
379
        const struct vclstate *os;
380
381 44
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
382
383 44
        assert(vs != VCL_STATE_LABEL);
384
385 44
        if (mcf_is_label(vp)) {
386 0
                AN(vp->warm);
387
                /* do not touch labels */
388 0
                return (0);
389
        }
390
391 44
        if (vp->state == vs)
392 0
                return (0);
393
394 44
        os = vp->state;
395 44
        vp->state = vs;
396
397 44
        if (vp == active_vcl) {
398 2
                assert (vs == VCL_STATE_WARM || vs == VCL_STATE_AUTO);
399 2
                AN(vp->warm);
400 2
                warm = 1;
401 44
        } else if (vs == VCL_STATE_AUTO) {
402 3
                warm = vp->warm;
403 3
        } else {
404 39
                warm = (vs == VCL_STATE_WARM ? 1 : 0);
405
        }
406
407 44
        i = mgt_vcl_askchild(cli, vp, warm);
408 44
        if (i == 0)
409 42
                mgt_vcl_set_cooldown(vp, VTIM_mono());
410
        else
411 2
                vp->state = os;
412 44
        return (i);
413 44
}
414
415
/*--------------------------------------------------------------------*/
416
417
static void
418 1160
mgt_new_vcl(struct cli *cli, const char *vclname, const char *vclsrc,
419
    const char *vclsrcfile, const char *state, int C_flag)
420
{
421
        unsigned status;
422
        char *lib, *p;
423
        struct vclprog *vp;
424
        const struct vclstate *vs;
425
        char buf[32];
426
427 1160
        AN(cli);
428
429 1160
        if (C_flag) {
430 1
                bprintf(buf, ".CflagTest.%d", (int)getpid());
431 1
                vclname = buf;
432 1160
        } else if (vcl_count >= mgt_param.max_vcl &&
433 2
            mgt_param.max_vcl_handling == 2) {
434 1
                VCLI_Out(cli, "Too many (%d) VCLs already loaded\n", vcl_count);
435 1
                VCLI_Out(cli, "(See max_vcl and max_vcl_handling parameters)");
436 1
                VCLI_SetResult(cli, CLIS_CANT);
437 1
                return;
438
        }
439
440 1159
        if (state == NULL)
441 1158
                vs = VCL_STATE_AUTO;
442
        else
443 1
                vs = mcf_vcl_parse_state(cli, state);
444
445 1159
        if (vs == NULL)
446 0
                return;
447
448 1159
        vp = mgt_vcl_add(vclname, vs);
449 1159
        lib = mgt_VccCompile(cli, vp, vclname, vclsrc, vclsrcfile, C_flag);
450 1159
        if (lib == NULL) {
451 198
                mgt_vcl_del(vp);
452 198
                return;
453
        }
454
455 961
        AZ(C_flag);
456 961
        vp->fname = lib;
457
458 961
        if (active_vcl == NULL)
459 745
                active_vcl = vp;
460
461 962
        if (cli->result == CLIS_OK &&
462 961
            vcl_count > mgt_param.max_vcl &&
463 1
            mgt_param.max_vcl_handling == 1) {
464 1
                VCLI_Out(cli, "%d VCLs loaded\n", vcl_count);
465 1
                VCLI_Out(cli, "Remember to vcl.discard the old/unused VCLs.\n");
466 1
                VCLI_Out(cli, "(See max_vcl and max_vcl_handling parameters)");
467 1
        }
468
469 961
        if (!MCH_Running())
470 782
                return;
471
472 179
        if (mgt_cli_askchild(&status, &p, "vcl.load %s %s %d%s\n",
473 179
            vp->name, vp->fname, vp->warm, vp->state->name)) {
474 38
                mgt_vcl_del(vp);
475 38
                VCLI_Out(cli, "%s", p);
476 38
                VCLI_SetResult(cli, status);
477 38
                free(p);
478 38
                return;
479
        }
480 141
        free(p);
481
482 141
        mgt_vcl_set_cooldown(vp, VTIM_mono());
483 1160
}
484
485
/*--------------------------------------------------------------------*/
486
487
void
488 13
mgt_vcl_startup(struct cli *cli, const char *vclsrc, const char *vclname,
489
    const char *origin, int C_flag)
490
{
491
        char buf[20];
492
        static int n = 0;
493
494 13
        AN(vclsrc);
495 13
        AN(origin);
496 13
        if (vclname == NULL) {
497 1
                bprintf(buf, "boot%d", n++);
498 1
                vclname = buf;
499 1
        }
500 13
        active_vcl = NULL;
501 13
        mgt_new_vcl(cli, vclname, vclsrc, origin, NULL, C_flag);
502 13
}
503
504
/*--------------------------------------------------------------------*/
505
506
int
507 751
mgt_push_vcls(struct cli *cli, unsigned *status, char **p)
508
{
509
        struct vclprog *vp;
510
        struct vcldep *vd;
511
        int done;
512
513 751
        AN(active_vcl);
514
515
        /* The VCL has not been loaded yet, it cannot fail */
516 751
        (void)cli;
517
518 1530
        VTAILQ_FOREACH(vp, &vclhead, list)
519 779
                vp->loaded = 0;
520
521 751
        do {
522 751
                done = 1;
523 1528
                VTAILQ_FOREACH(vp, &vclhead, list) {
524 778
                        if (vp->loaded)
525 0
                                continue;
526 791
                        VTAILQ_FOREACH(vd, &vp->dfrom, lfrom)
527 13
                                if (!vd->to->loaded)
528 0
                                        break;
529 778
                        if (vd != NULL) {
530 0
                                done = 0;
531 0
                                continue;
532
                        }
533 778
                        if (mcf_is_label(vp)) {
534 8
                                vd = VTAILQ_FIRST(&vp->dfrom);
535 8
                                AN(vd);
536 16
                                if (mgt_cli_askchild(status, p,
537
                                    "vcl.label %s %s\n",
538 8
                                    vp->name, vd->to->name))
539 0
                                        return (1);
540 8
                        } else {
541 1540
                                if (mgt_cli_askchild(status, p,
542
                                    "vcl.load \"%s\" %s %d%s\n",
543 770
                                    vp->name, vp->fname, vp->warm,
544 770
                                    vp->state->name))
545 1
                                        return (1);
546
                        }
547 777
                        vp->loaded = 1;
548 777
                        free(*p);
549 777
                        *p = NULL;
550 777
                }
551 750
        } while (!done);
552
553 750
        if (mgt_cli_askchild(status, p, "vcl.use \"%s\"\n", active_vcl->name))
554 0
                return (1);
555 750
        free(*p);
556 750
        *p = NULL;
557 750
        return (0);
558 751
}
559
560
/*--------------------------------------------------------------------*/
561
562
static void v_matchproto_(cli_func_t)
563 1147
mcf_vcl_inline(struct cli *cli, const char * const *av, void *priv)
564
{
565
566 1147
        (void)priv;
567
568 1147
        if (!mcf_find_no_vcl(cli, av[2]))
569 4
                return;
570
571 1143
        mgt_new_vcl(cli, av[2], av[3], "<vcl.inline>", av[4], 0);
572 1147
}
573
574
static void v_matchproto_(cli_func_t)
575 5
mcf_vcl_load(struct cli *cli, const char * const *av, void *priv)
576
{
577
578 5
        (void)priv;
579 5
        if (!mcf_find_no_vcl(cli, av[2]))
580 1
                return;
581
582 4
        mgt_new_vcl(cli, av[2], NULL, av[3], av[4], 0);
583 5
}
584
585
586
static void v_matchproto_(cli_func_t)
587 19
mcf_vcl_state(struct cli *cli, const char * const *av, void *priv)
588
{
589
        const struct vclstate *state;
590
        struct vclprog *vp;
591
592 19
        (void)priv;
593 19
        vp = mcf_find_vcl(cli, av[2]);
594 19
        if (vp == NULL)
595 0
                return;
596
597 19
        if (mcf_is_label(vp)) {
598 1
                VCLI_Out(cli, "Labels are always warm");
599 1
                VCLI_SetResult(cli, CLIS_PARAM);
600 1
                return;
601
        }
602
603 18
        state = mcf_vcl_parse_state(cli, av[3]);
604 18
        if (state == NULL)
605 0
                return;
606
607 18
        if (state == VCL_STATE_COLD) {
608 8
                if (!VTAILQ_EMPTY(&vp->dto)) {
609 1
                        assert(vp->state != VCL_STATE_COLD);
610 1
                        VCLI_Out(cli, "A labeled VCL cannot be set cold");
611 1
                        VCLI_SetResult(cli, CLIS_CANT);
612 1
                        return;
613
                }
614 7
                if (vp == active_vcl) {
615 1
                        VCLI_Out(cli, "Cannot set the active VCL cold.");
616 1
                        VCLI_SetResult(cli, CLIS_CANT);
617 1
                        return;
618
                }
619 6
        }
620
621 16
        (void)mgt_vcl_setstate(cli, vp, state);
622 19
}
623
624
static void v_matchproto_(cli_func_t)
625 932
mcf_vcl_use(struct cli *cli, const char * const *av, void *priv)
626
{
627
        unsigned status;
628 932
        char *p = NULL;
629
        struct vclprog *vp, *vp2;
630
        vtim_mono now;
631
632 932
        (void)priv;
633 932
        vp = mcf_find_vcl(cli, av[2]);
634 932
        if (vp == NULL)
635 1
                return;
636 931
        if (vp == active_vcl)
637 737
                return;
638
639 194
        if (mgt_vcl_requirewarm(cli, vp))
640 1
                return;
641
642 193
        if (MCH_Running() &&
643 155
            mgt_cli_askchild(&status, &p, "vcl.use %s\n", av[2])) {
644 0
                VCLI_SetResult(cli, status);
645 0
                VCLI_Out(cli, "%s", p);
646 0
        } else {
647 193
                VCLI_Out(cli, "VCL '%s' now active", av[2]);
648 193
                vp2 = active_vcl;
649 193
                active_vcl = vp;
650 193
                now = VTIM_mono();
651 193
                mgt_vcl_set_cooldown(vp, now);
652 193
                if (vp2 != NULL)
653 193
                        mgt_vcl_set_cooldown(vp2, now);
654
        }
655 193
        free(p);
656 932
}
657
658
static void v_matchproto_(cli_func_t)
659 46
mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv)
660
{
661
        unsigned status;
662 46
        char *p = NULL;
663
        struct vclprog *vp;
664
        struct vcldep *vd;
665
        int n;
666
667 46
        (void)priv;
668 46
        vp = mcf_find_vcl(cli, av[2]);
669 46
        if (vp == NULL)
670 4
                return;
671 42
        if (vp == active_vcl) {
672 6
                VCLI_SetResult(cli, CLIS_CANT);
673 6
                VCLI_Out(cli, "Cannot discard active VCL program\n");
674 6
                return;
675
        }
676 36
        if (!VTAILQ_EMPTY(&vp->dto)) {
677 3
                VCLI_SetResult(cli, CLIS_CANT);
678 3
                AN(vp->warm);
679 3
                if (!mcf_is_label(vp))
680 2
                        VCLI_Out(cli, "Cannot discard labeled VCL program.\n");
681
                else
682 1
                        VCLI_Out(cli,
683
                            "Cannot discard this VCL label, "
684
                            "other VCLs depend on it.\n");
685 3
                n = 0;
686 11
                VTAILQ_FOREACH(vd, &vp->dto, lto) {
687 8
                        if (n++ == 5) {
688 0
                                VCLI_Out(cli, "\t[...]");
689 0
                                break;
690
                        }
691 8
                        VCLI_Out(cli, "\t%s\n", vd->from->name);
692 8
                }
693 3
                return;
694
        }
695 33
        if (mcf_is_label(vp)) {
696 5
                AN(vp->warm);
697 5
                vp->warm = 0;
698 5
        } else {
699 28
                (void)mgt_vcl_setstate(cli, vp, VCL_STATE_COLD);
700
        }
701 33
        if (MCH_Running()) {
702 32
                AZ(vp->warm);
703 32
                if (mgt_cli_askchild(&status, &p, "vcl.discard %s\n", av[2]))
704 0
                        assert(status == CLIS_OK || status == CLIS_COMMS);
705 32
                free(p);
706 32
        }
707 33
        mgt_vcl_del(vp);
708 46
}
709
710
static void v_matchproto_(cli_func_t)
711 75
mcf_vcl_list(struct cli *cli, const char * const *av, void *priv)
712
{
713
        unsigned status;
714
        char *p;
715
        struct vclprog *vp;
716
        struct vcldep *vd;
717
        const struct vclstate *vs;
718
        struct vsb *vsb;
719
720
        /* NB: Shall generate same output as vcl_cli_list() */
721
722 75
        (void)av;
723 75
        (void)priv;
724
725 75
        if (MCH_Running()) {
726 65
                if (!mgt_cli_askchild(&status, &p, "vcl.list\n")) {
727 65
                        VCLI_SetResult(cli, status);
728 65
                        VCLI_Out(cli, "%s", p);
729 65
                }
730 65
                free(p);
731 65
        } else {
732 10
                vsb = VSB_new_auto();
733 10
                AN(vsb);
734
735 33
                VTAILQ_FOREACH(vp, &vclhead, list) {
736 46
                        VSB_printf(vsb, "%s",
737 23
                            vp == active_vcl ? "active" : "available");
738 23
                        vs = vp->warm ?  VCL_STATE_WARM : VCL_STATE_COLD;
739 23
                        VSB_printf(vsb, "\t%s\t%s", vp->state->name, vs->name);
740 23
                        VSB_printf(vsb, "\t%6s\t%s", "-", vp->name);
741 23
                        if (mcf_is_label(vp)) {
742 7
                                vd = VTAILQ_FIRST(&vp->dfrom);
743 7
                                AN(vd);
744 7
                                VSB_printf(vsb, "\t->\t%s", vd->to->name);
745 7
                                if (vp->nto > 0)
746 4
                                        VSB_printf(vsb, " (%d return(vcl)%s)",
747 2
                                            vp->nto, vp->nto > 1 ? "'s" : "");
748 23
                        } else if (vp->nto > 0) {
749 10
                                VSB_printf(vsb, "\t<-\t(%d label%s)",
750 5
                                    vp->nto, vp->nto > 1 ? "s" : "");
751 5
                        }
752 23
                        VSB_printf(vsb, "\n");
753 23
                }
754 10
                VCLI_VTE(cli, &vsb, 80);
755
        }
756 75
}
757
758
static void v_matchproto_(cli_func_t)
759 7
mcf_vcl_list_json(struct cli *cli, const char * const *av, void *priv)
760
{
761
        unsigned status;
762
        char *p;
763
        struct vclprog *vp;
764
        struct vcldep *vd;
765
        const struct vclstate *vs;
766
767
        /* NB: Shall generate same output as vcl_cli_list() */
768
769 7
        (void)priv;
770 7
        if (MCH_Running()) {
771 5
                if (!mgt_cli_askchild(&status, &p, "vcl.list -j\n")) {
772 5
                        VCLI_SetResult(cli, status);
773 5
                        VCLI_Out(cli, "%s", p);
774 5
                }
775 5
                free(p);
776 5
        } else {
777 2
                VCLI_JSON_begin(cli, 2, av);
778 2
                VCLI_Out(cli, ",\n");
779 9
                VTAILQ_FOREACH(vp, &vclhead, list) {
780 7
                        VCLI_Out(cli, "{\n");
781 7
                        VSB_indent(cli->sb, 2);
782 14
                        VCLI_Out(cli, "\"status\": \"%s\",\n",
783 7
                            vp == active_vcl ? "active" : "available");
784 7
                        VCLI_Out(cli, "\"state\": \"%s\",\n", vp->state->name);
785 7
                        vs = vp->warm ?  VCL_STATE_WARM : VCL_STATE_COLD;
786 7
                        VCLI_Out(cli, "\"temperature\": \"%s\",\n", vs->name);
787 7
                        VCLI_Out(cli, "\"name\": \"%s\"", vp->name);
788 7
                        if (mcf_is_label(vp)) {
789 3
                                vd = VTAILQ_FIRST(&vp->dfrom);
790 3
                                AN(vd);
791 3
                                VCLI_Out(cli, ",\n");
792 3
                                VCLI_Out(cli, "\"label\": {\n");
793 3
                                VSB_indent(cli->sb, 2);
794 3
                                VCLI_Out(cli, "\"name\": \"%s\"", vd->to->name);
795 3
                                if (vp->nto > 0)
796 0
                                        VCLI_Out(cli, ",\n\"refs\": %d",
797 0
                                                 vp->nto);
798 3
                                VSB_indent(cli->sb, -2);
799 3
                                VCLI_Out(cli, "\n");
800 3
                                VCLI_Out(cli, "}");
801 7
                        } else if (vp->nto > 0) {
802 1
                                VCLI_Out(cli, ",\n");
803 1
                                VCLI_Out(cli, "\"labels\": %d", vp->nto);
804 1
                        }
805 7
                        VSB_indent(cli->sb, -2);
806 7
                        VCLI_Out(cli, "\n}");
807 7
                        if (VTAILQ_NEXT(vp, list) != NULL)
808 5
                                VCLI_Out(cli, ",\n");
809 7
                }
810 2
                VCLI_JSON_end(cli);
811
        }
812 7
}
813
814
static int
815 14
mcf_vcl_check_dag(struct cli *cli, struct vclprog *tree, struct vclprog *target)
816
{
817
        struct vcldep *vd;
818
819 14
        if (target == tree)
820 3
                return (1);
821 11
        VTAILQ_FOREACH(vd, &tree->dfrom, lfrom) {
822 9
                if (!mcf_vcl_check_dag(cli, vd->to, target))
823 0
                        continue;
824 9
                if (mcf_is_label(tree))
825 6
                        VCLI_Out(cli, "Label %s points to vcl %s\n",
826 3
                            tree->name, vd->to->name);
827
                else
828 12
                        VCLI_Out(cli, "Vcl %s uses Label %s\n",
829 6
                            tree->name, vd->to->name);
830 9
                return (1);
831
        }
832 2
        return(0);
833 14
}
834
835
static void v_matchproto_(cli_func_t)
836 36
mcf_vcl_label(struct cli *cli, const char * const *av, void *priv)
837
{
838
        struct vclprog *vpl;
839
        struct vclprog *vpt;
840
        unsigned status;
841
        char *p;
842
        int i;
843
844 36
        (void)priv;
845 36
        if (mcf_invalid_vclname(cli, av[2]))
846 1
                return;
847 35
        vpl = mcf_vcl_byname(av[2]);
848 35
        if (vpl != NULL && !mcf_is_label(vpl)) {
849 1
                VCLI_SetResult(cli, CLIS_PARAM);
850 1
                VCLI_Out(cli, "%s is not a label", vpl->name);
851 1
                return;
852
        }
853 34
        vpt = mcf_find_vcl(cli, av[3]);
854 34
        if (vpt == NULL)
855 2
                return;
856 32
        if (mcf_is_label(vpt)) {
857 2
                VCLI_SetResult(cli, CLIS_CANT);
858 2
                VCLI_Out(cli, "VCL labels cannot point to labels");
859 2
                return;
860
        }
861 30
        if (vpl != NULL) {
862 6
                if (VTAILQ_FIRST(&vpl->dfrom)->to != vpt &&
863 5
                    mcf_vcl_check_dag(cli, vpt, vpl)) {
864 6
                        VCLI_Out(cli,
865
                            "Pointing label %s at %s would create a loop",
866 3
                            vpl->name, vpt->name);
867 3
                        VCLI_SetResult(cli, CLIS_PARAM);
868 3
                        return;
869
                }
870 3
        }
871
872 27
        if (mgt_vcl_requirewarm(cli, vpt))
873 2
                return;
874
875 25
        if (vpl != NULL) {
876
                /* potentially fail before we delete the dependency */
877 3
                if (mgt_vcl_requirewarm(cli, vpl))
878 0
                        return;
879 3
                mgt_vcl_dep_del(VTAILQ_FIRST(&vpl->dfrom));
880 3
                AN(VTAILQ_EMPTY(&vpl->dfrom));
881 3
        } else {
882 22
                vpl = mgt_vcl_add(av[2], VCL_STATE_LABEL);
883
        }
884
885 25
        AN(vpl);
886 25
        if (mgt_vcl_requirewarm(cli, vpl))
887 0
                return;
888 25
        (void)mgt_vcl_dep_add(vpl, vpt);
889
890 25
        if (!MCH_Running())
891 8
                return;
892
893 17
        i = mgt_cli_askchild(&status, &p, "vcl.label %s %s\n", av[2], av[3]);
894 17
        if (i) {
895 0
                VCLI_SetResult(cli, status);
896 0
                VCLI_Out(cli, "%s", p);
897 0
        }
898 17
        free(p);
899 36
}
900
901
/*--------------------------------------------------------------------*/
902
903
static int v_matchproto_(vev_cb_f)
904 589
mgt_vcl_poker(const struct vev *e, int what)
905
{
906
        struct vclprog *vp;
907
        vtim_mono now;
908
909 589
        (void)e;
910 589
        (void)what;
911 589
        e_poker->timeout = mgt_param.vcl_cooldown * .45;
912 589
        now = VTIM_mono();
913 1429
        VTAILQ_FOREACH(vp, &vclhead, list) {
914 840
                if (vp->go_cold == 0)
915 409
                        mgt_vcl_set_cooldown(vp, now);
916 431
                else if (vp->go_cold > 0 &&
917 109
                    vp->go_cold + mgt_param.vcl_cooldown < now)
918 4
                        (void)mgt_vcl_settemp(NULL, vp, 0);
919 840
        }
920 589
        return (0);
921
}
922
923
/*--------------------------------------------------------------------*/
924
925
static struct cli_proto cli_vcl[] = {
926
        { CLICMD_VCL_LOAD,              "", mcf_vcl_load },
927
        { CLICMD_VCL_INLINE,            "", mcf_vcl_inline },
928
        { CLICMD_VCL_USE,               "", mcf_vcl_use },
929
        { CLICMD_VCL_STATE,             "", mcf_vcl_state },
930
        { CLICMD_VCL_DISCARD,           "", mcf_vcl_discard },
931
        { CLICMD_VCL_LIST,              "", mcf_vcl_list, mcf_vcl_list_json },
932
        { CLICMD_VCL_LABEL,             "", mcf_vcl_label },
933
        { CLICMD_DEBUG_VCL_SYMTAB,      "d", mcf_vcl_symtab },
934
        { NULL }
935
};
936
937
/*--------------------------------------------------------------------*/
938
939
static void
940 3601
mgt_vcl_atexit(void)
941
{
942
        struct vclprog *vp, *vp2;
943
944 3601
        if (getpid() != heritage.mgt_pid)
945 2841
                return;
946 760
        active_vcl = NULL;
947 1527
        while (!VTAILQ_EMPTY(&vclhead))
948 1727
                VTAILQ_FOREACH_SAFE(vp, &vclhead, list, vp2)
949 1872
                        if (VTAILQ_EMPTY(&vp->dto))
950 912
                                mgt_vcl_del(vp);
951 3601
}
952
953
void
954 760
mgt_vcl_init(void)
955
{
956
957 760
        e_poker = VEV_Alloc();
958 760
        AN(e_poker);
959 760
        e_poker->timeout = 3;           // random, prime
960
961 760
        e_poker->callback = mgt_vcl_poker;
962 760
        e_poker->name = "vcl poker";
963 760
        AZ(VEV_Start(mgt_evb, e_poker));
964
965 760
        AZ(atexit(mgt_vcl_atexit));
966
967 760
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_vcl);
968 760
}