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