varnish-cache/bin/varnishd/mgt/mgt_vcl.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2015 Varnish Software AS
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 <errno.h>
35
#include <fcntl.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <unistd.h>
40
#include <sys/stat.h>
41
42
#include "mgt/mgt.h"
43
#include "common/heritage.h"
44
45
#include "libvcc.h"
46
#include "vcli_serve.h"
47
#include "vct.h"
48
#include "vev.h"
49
#include "vtim.h"
50
51
static const char * const VCL_STATE_COLD = "cold";
52
static const char * const VCL_STATE_WARM = "warm";
53
static const char * const VCL_STATE_AUTO = "auto";
54
static const char * const VCL_STATE_LABEL = "label";
55
56
struct vclprog;
57
struct vmodfile;
58
59
struct vmoddep {
60
        unsigned                magic;
61
#define VMODDEP_MAGIC           0xc1490542
62
        VTAILQ_ENTRY(vmoddep)   lfrom;
63
        struct vmodfile         *to;
64
        VTAILQ_ENTRY(vmoddep)   lto;
65
};
66
67
struct vcldep {
68
        unsigned                magic;
69
#define VCLDEP_MAGIC            0xa9a17dc2
70
        struct vclprog          *from;
71
        VTAILQ_ENTRY(vcldep)    lfrom;
72
        struct vclprog          *to;
73
        VTAILQ_ENTRY(vcldep)    lto;
74
};
75
76
struct vclprog {
77
        unsigned                magic;
78
#define VCLPROG_MAGIC           0x9ac09fea
79
        VTAILQ_ENTRY(vclprog)   list;
80
        char                    *name;
81
        char                    *fname;
82
        unsigned                warm;
83
        const char *            state;
84
        double                  go_cold;
85
        VTAILQ_HEAD(, vcldep)   dfrom;
86
        VTAILQ_HEAD(, vcldep)   dto;
87
        int                     nto;
88
        int                     loaded;
89
        VTAILQ_HEAD(, vmoddep)  vmods;
90
};
91
92
struct vmodfile {
93
        unsigned                magic;
94
#define VMODFILE_MAGIC          0xffa1a0d5
95
        char                    *fname;
96
        VTAILQ_ENTRY(vmodfile)  list;
97
        VTAILQ_HEAD(, vmoddep)  vcls;
98
};
99
100
static VTAILQ_HEAD(, vclprog)   vclhead = VTAILQ_HEAD_INITIALIZER(vclhead);
101
static VTAILQ_HEAD(, vmodfile)  vmodhead = VTAILQ_HEAD_INITIALIZER(vmodhead);
102
static struct vclprog           *active_vcl;
103
static struct vev *e_poker;
104
105
static int mgt_vcl_setstate(struct cli *, struct vclprog *, const char *);
106
107
/*--------------------------------------------------------------------*/
108
109
static struct vclprog *
110 1778
mcf_vcl_byname(const char *name)
111
{
112
        struct vclprog *vp;
113
114 2867
        VTAILQ_FOREACH(vp, &vclhead, list)
115 1941
                if (!strcmp(name, vp->name))
116 852
                        return (vp);
117 926
        return (NULL);
118
}
119
120
static int
121 1788
mcf_invalid_vclname(struct cli *cli, const char *name)
122
{
123
        const char *bad;
124
125 1788
        AN(name);
126 1788
        bad = VCT_invalid_name(name, NULL);
127
128 1788
        if (bad != NULL) {
129 5
                VCLI_SetResult(cli, CLIS_PARAM);
130 5
                VCLI_Out(cli, "Illegal character in VCL name ");
131 5
                if (*bad > 0x20 && *bad < 0x7f)
132 4
                        VCLI_Out(cli, "('%c')", *bad);
133
                else
134 1
                        VCLI_Out(cli, "(0x%02x)", *bad & 0xff);
135 5
                return (-1);
136
        }
137 1783
        return (0);
138
}
139
140
static struct vclprog *
141 830
mcf_find_vcl(struct cli *cli, const char *name)
142
{
143
        struct vclprog *vp;
144
145 830
        if (mcf_invalid_vclname(cli, name))
146 0
                return (NULL);
147
148 830
        vp = mcf_vcl_byname(name);
149 830
        if (vp == NULL) {
150 8
                VCLI_SetResult(cli, CLIS_PARAM);
151 8
                VCLI_Out(cli, "No VCL named %s known.", name);
152
        }
153 830
        return (vp);
154
}
155
156
static int
157 899
mcf_find_no_vcl(struct cli *cli, const char *name)
158
{
159
160 899
        if (mcf_invalid_vclname(cli, name))
161 4
                return (0);
162
163 895
        if (mcf_vcl_byname(name) != NULL) {
164 1
                VCLI_SetResult(cli, CLIS_PARAM);
165 1
                VCLI_Out(cli, "Already a VCL named %s", name);
166 1
                return (0);
167
        }
168 894
        return (1);
169
}
170
171
static int
172 2554
mcf_is_label(const struct vclprog *vp)
173
{
174 2554
        return (!strcmp(vp->state, VCL_STATE_LABEL));
175
}
176
177
/*--------------------------------------------------------------------*/
178
179
static void
180 37
mgt_vcl_dep_add(struct vclprog *vp_from, struct vclprog *vp_to)
181
{
182
        struct vcldep *vd;
183
184 37
        CHECK_OBJ_NOTNULL(vp_from, VCLPROG_MAGIC);
185 37
        CHECK_OBJ_NOTNULL(vp_to, VCLPROG_MAGIC);
186 37
        ALLOC_OBJ(vd, VCLDEP_MAGIC);
187 37
        XXXAN(vd);
188 37
        vd->from = vp_from;
189 37
        VTAILQ_INSERT_TAIL(&vp_from->dfrom, vd, lfrom);
190 37
        vd->to = vp_to;
191 37
        VTAILQ_INSERT_TAIL(&vp_to->dto, vd, lto);
192 37
        vp_to->nto++;
193 37
        assert(vp_to->state == VCL_STATE_WARM ||        /* vcl.label ... */
194
            vp_to->state == VCL_STATE_LABEL);           /* return(vcl(...)) */
195 37
}
196
197
static void
198 37
mgt_vcl_dep_del(struct vcldep *vd)
199
{
200
201 37
        CHECK_OBJ_NOTNULL(vd, VCLDEP_MAGIC);
202 37
        VTAILQ_REMOVE(&vd->from->dfrom, vd, lfrom);
203 37
        VTAILQ_REMOVE(&vd->to->dto, vd, lto);
204 37
        vd->to->nto--;
205 37
        if (vd->to->nto == 0 && vd->to->state == VCL_STATE_WARM) {
206 16
                vd->to->state = VCL_STATE_AUTO;
207 16
                AZ(vd->to->go_cold);
208 16
                (void)mgt_vcl_setstate(NULL, vd->to, VCL_STATE_AUTO);
209 16
                AN(vd->to->go_cold);
210
        }
211 37
        FREE_OBJ(vd);
212 37
}
213
214
/*--------------------------------------------------------------------*/
215
216
static struct vclprog *
217 925
mgt_vcl_add(const char *name, const char *state)
218
{
219
        struct vclprog *vp;
220
221 925
        assert(state == VCL_STATE_WARM ||
222
               state == VCL_STATE_COLD ||
223
               state == VCL_STATE_AUTO ||
224
               state == VCL_STATE_LABEL);
225 925
        ALLOC_OBJ(vp, VCLPROG_MAGIC);
226 925
        XXXAN(vp);
227 925
        REPLACE(vp->name, name);
228 925
        VTAILQ_INIT(&vp->dfrom);
229 925
        VTAILQ_INIT(&vp->dto);
230 925
        VTAILQ_INIT(&vp->vmods);
231 925
        vp->state = state;
232
233 925
        if (vp->state != VCL_STATE_COLD)
234 924
                vp->warm = 1;
235
236 925
        VTAILQ_INSERT_TAIL(&vclhead, vp, list);
237 925
        return (vp);
238
}
239
240
static void
241 925
mgt_vcl_del(struct vclprog *vp)
242
{
243
        char *p;
244
        struct vmoddep *vd;
245
        struct vmodfile *vf;
246
247 925
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
248 1874
        while (!VTAILQ_EMPTY(&vp->dto))
249 24
                mgt_vcl_dep_del(VTAILQ_FIRST(&vp->dto));
250 1861
        while (!VTAILQ_EMPTY(&vp->dfrom))
251 11
                mgt_vcl_dep_del(VTAILQ_FIRST(&vp->dfrom));
252
253 925
        VTAILQ_REMOVE(&vclhead, vp, list);
254 925
        if (vp->fname != NULL) {
255 753
                AZ(unlink(vp->fname));
256 753
                p = strrchr(vp->fname, '/');
257 753
                AN(p);
258 753
                *p = '\0';
259 753
                VJ_master(JAIL_MASTER_FILE);
260
                /*
261
                 * This will fail if any files are dropped next to the library
262
                 * without us knowing.  This happens for instance with GCOV.
263
                 * Assume developers know how to clean up after themselves
264
                 * (or alternatively:  How to run out of disk space).
265
                 */
266 753
                (void)rmdir(vp->fname);
267 753
                VJ_master(JAIL_MASTER_LOW);
268 753
                free(vp->fname);
269
        }
270 2078
        while (!VTAILQ_EMPTY(&vp->vmods)) {
271 228
                vd = VTAILQ_FIRST(&vp->vmods);
272 228
                CHECK_OBJ(vd, VMODDEP_MAGIC);
273 228
                vf = vd->to;
274 228
                CHECK_OBJ(vf, VMODFILE_MAGIC);
275 228
                VTAILQ_REMOVE(&vp->vmods, vd, lfrom);
276 228
                VTAILQ_REMOVE(&vf->vcls, vd, lto);
277 228
                FREE_OBJ(vd);
278
279 228
                if (VTAILQ_EMPTY(&vf->vcls)) {
280 170
                        if (!MGT_DO_DEBUG(DBG_VMOD_SO_KEEP))
281 169
                                AZ(unlink(vf->fname));
282 170
                        VTAILQ_REMOVE(&vmodhead, vf, list);
283 170
                        free(vf->fname);
284 170
                        FREE_OBJ(vf);
285
                }
286
        }
287 925
        free(vp->name);
288 925
        FREE_OBJ(vp);
289 925
}
290
291
void
292 16
mgt_vcl_depends(struct vclprog *vp1, const char *name)
293
{
294
        struct vclprog *vp2;
295
296 16
        CHECK_OBJ_NOTNULL(vp1, VCLPROG_MAGIC);
297
298 16
        vp2 = mcf_vcl_byname(name);
299 16
        CHECK_OBJ_NOTNULL(vp2, VCLPROG_MAGIC);
300 16
        mgt_vcl_dep_add(vp1, vp2);
301 16
}
302
303
static int
304 170
mgt_vcl_cache_vmod(const char *nm, const char *fm, const char *to)
305
{
306
        int fi, fo;
307 170
        int ret = 0;
308
        ssize_t sz;
309
        char buf[BUFSIZ];
310
311 170
        fo = open(to, O_WRONLY | O_CREAT | O_EXCL, 0744);
312 170
        if (fo < 0 && errno == EEXIST)
313 0
                return (0);
314 170
        if (fo < 0) {
315 0
                fprintf(stderr, "Creating copy of vmod %s: %s\n",
316 0
                    nm, strerror(errno));
317 0
                return (1);
318
        }
319 170
        fi = open(fm, O_RDONLY);
320 170
        if (fi < 0) {
321 0
                fprintf(stderr, "Opening vmod %s from %s: %s\n",
322 0
                    nm, fm, strerror(errno));
323 0
                AZ(unlink(to));
324 0
                closefd(&fo);
325 0
                return (1);
326
        }
327
        while (1) {
328 30146
                sz = read(fi, buf, sizeof buf);
329 30146
                if (sz == 0)
330 170
                        break;
331 29976
                if (sz < 0 || sz != write(fo, buf, sz)) {
332 0
                        fprintf(stderr, "Copying vmod %s: %s\n",
333 0
                            nm, strerror(errno));
334 0
                        AZ(unlink(to));
335 0
                        ret = 1;
336 0
                        break;
337
                }
338 29976
        }
339 170
        closefd(&fi);
340 170
        AZ(fchmod(fo, 0444));
341 170
        closefd(&fo);
342 170
        return(ret);
343
}
344
345
void
346 228
mgt_vcl_vmod(struct vclprog *vp, const char *src, const char *dst)
347
{
348
        struct vmodfile *vf;
349
        struct vmoddep *vd;
350
351 228
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
352 228
        AN(src);
353 228
        AN(dst);
354 228
        assert(!strncmp(dst, "./vmod_cache/", 13));
355
356 267
        VTAILQ_FOREACH(vf, &vmodhead, list)
357 97
                if (!strcmp(vf->fname, dst))
358 58
                        break;
359 228
        if (vf == NULL) {
360 170
                ALLOC_OBJ(vf, VMODFILE_MAGIC);
361 170
                AN(vf);
362 170
                REPLACE(vf->fname, dst);
363 170
                AN(vf->fname);
364 170
                VTAILQ_INIT(&vf->vcls);
365 170
                AZ(mgt_vcl_cache_vmod(vp->name, src, dst));
366 170
                VTAILQ_INSERT_TAIL(&vmodhead, vf, list);
367
        }
368 228
        ALLOC_OBJ(vd, VMODDEP_MAGIC);
369 228
        AN(vd);
370 228
        vd->to = vf;
371 228
        VTAILQ_INSERT_TAIL(&vp->vmods, vd, lfrom);
372 228
        VTAILQ_INSERT_TAIL(&vf->vcls, vd, lto);
373 228
}
374
375
int
376 1238
mgt_has_vcl(void)
377
{
378
379 1238
        return (!VTAILQ_EMPTY(&vclhead));
380
}
381
382
static unsigned
383 1019
mgt_vcl_cooldown(struct vclprog *vp)
384
{
385
        double now;
386
387 1019
        if (vp->state != VCL_STATE_AUTO)
388 42
                return (0);
389
390 977
        now = VTIM_mono();
391 977
        if (vp->go_cold > 0 && vp->go_cold + mgt_param.vcl_cooldown < now)
392 23
                return (1);
393
394 954
        if (vp->go_cold == 0 && vp != active_vcl)
395 165
                vp->go_cold = now;
396
397 954
        return (0);
398
}
399
400
static int
401 988
mgt_vcl_setstate(struct cli *cli, struct vclprog *vp, const char *vs)
402
{
403
        unsigned status, warm;
404
        char *p;
405
        int i;
406
407 988
        assert(vs != VCL_STATE_LABEL);
408
409 988
        if (vp == active_vcl || mcf_is_label(vp)) {
410 639
                AN(vp->warm);
411
                /* Only the poker sends COLD indiscriminately, ignore it */
412 639
                if (vs == VCL_STATE_COLD)
413 0
                        AZ(cli);
414 639
                return (0);
415
        }
416
417 349
        if (vs == VCL_STATE_AUTO)
418 154
                vs = (mgt_vcl_cooldown(vp) ? VCL_STATE_COLD : VCL_STATE_WARM);
419
        else
420 195
                vp->go_cold = 0;
421
422 349
        warm = (vs == VCL_STATE_WARM ? 1 : 0);
423
424 349
        if (vp->warm == warm)
425 312
                return (0);
426
427 37
        if (!MCH_Running()) {
428 1
                vp->warm = warm;
429 1
                return (0);
430
        }
431
432 36
        i = mgt_cli_askchild(&status, &p, "vcl.state %s %d%s\n",
433
            vp->name, warm, vs);
434 36
        if (i && cli != NULL) {
435 3
                VCLI_SetResult(cli, status);
436 3
                VCLI_Out(cli, "%s", p);
437 33
        } else if (i) {
438 0
                MGT_Complain(C_ERR,
439
                    "Please file ticket: VCL poker problem: "
440
                    "'vcl.state %s %d%s' -> %03d '%s'",
441
                    vp->name, warm, vp->state, i, p);
442
        } else {
443
                /* Success, update mgt's VCL state to reflect child's
444
                   state */
445 33
                vp->warm = warm;
446
        }
447
448 36
        free(p);
449 36
        return (i);
450
}
451
452
/*--------------------------------------------------------------------*/
453
454
static void
455 906
mgt_new_vcl(struct cli *cli, const char *vclname, const char *vclsrc,
456
    const char *vclsrcfile, const char *state, int C_flag)
457
{
458
        unsigned status;
459
        char *lib, *p;
460
        struct vclprog *vp;
461
        char buf[32];
462
463 906
        AN(cli);
464
465 906
        if (C_flag) {
466 2
                bprintf(buf, ".CflagTest.%d", (int)getpid());
467 2
                vclname = buf;
468
        }
469
470 906
        if (state == NULL)
471 905
                state = VCL_STATE_AUTO;
472 1
        else if (!strcmp(state, VCL_STATE_AUTO))
473 0
                state = VCL_STATE_AUTO;
474 1
        else if (!strcmp(state, VCL_STATE_COLD))
475 1
                state = VCL_STATE_COLD;
476 0
        else if (!strcmp(state, VCL_STATE_WARM))
477 0
                state = VCL_STATE_WARM;
478
        else {
479 0
                VCLI_Out(cli, "State must be one of auto, cold or warm.");
480 0
                VCLI_SetResult(cli, CLIS_PARAM);
481 0
                return;
482
        }
483
484 906
        vp = mgt_vcl_add(vclname, state);
485 906
        lib = mgt_VccCompile(cli, vp, vclname, vclsrc, vclsrcfile, C_flag);
486 906
        if (lib == NULL) {
487 153
                mgt_vcl_del(vp);
488 153
                return;
489
        }
490
491 753
        AZ(C_flag);
492 753
        vp->fname = lib;
493
494 753
        if (active_vcl == NULL)
495 609
                active_vcl = vp;
496
497 753
        if (!MCH_Running())
498 643
                return;
499
500 110
        if (mgt_cli_askchild(&status, &p, "vcl.load %s %s %d%s\n",
501
            vp->name, vp->fname, vp->warm, vp->state)) {
502 16
                mgt_vcl_del(vp);
503 16
                VCLI_Out(cli, "%s", p);
504 16
                VCLI_SetResult(cli, status);
505 16
                free(p);
506 16
                return;
507
        }
508 94
        free(p);
509
510 94
        if (vp->warm && !strcmp(vp->state, "auto"))
511 93
                vp->go_cold = VTIM_mono();
512
}
513
514
/*--------------------------------------------------------------------*/
515
516
void
517 12
mgt_vcl_startup(struct cli *cli, const char *vclsrc, const char *vclname,
518
    const char *origin, int C_flag)
519
{
520
        char buf[20];
521
        static int n = 0;
522
523 12
        AN(vclsrc);
524 12
        AN(origin);
525 12
        if (vclname == NULL) {
526 1
                bprintf(buf, "boot%d", n++);
527 1
                vclname = buf;
528
        }
529 12
        mgt_new_vcl(cli, vclname, vclsrc, origin, NULL, C_flag);
530 12
        active_vcl = mcf_vcl_byname(vclname);
531 12
}
532
533
/*--------------------------------------------------------------------*/
534
535
void
536 906
mgt_vcl_export_labels(struct vcc *vcc)
537
{
538
        struct vclprog *vp;
539 2372
        VTAILQ_FOREACH(vp, &vclhead, list) {
540 1466
                if (mcf_is_label(vp))
541 36
                        VCC_Predef(vcc, "VCL_VCL", vp->name);
542
        }
543 906
}
544
545
/*--------------------------------------------------------------------*/
546
547
int
548 615
mgt_push_vcls_and_start(struct cli *cli, unsigned *status, char **p)
549
{
550
        struct vclprog *vp;
551
        struct vcldep *vd;
552
        int done;
553
554 615
        AN(active_vcl);
555
556
        /* The VCL has not been loaded yet, it cannot fail */
557 615
        AZ(mgt_vcl_setstate(cli, active_vcl, VCL_STATE_WARM));
558
559 1253
        VTAILQ_FOREACH(vp, &vclhead, list)
560 638
                vp->loaded = 0;
561
562
        do {
563 615
                done = 1;
564 1251
                VTAILQ_FOREACH(vp, &vclhead, list) {
565 637
                        if (vp->loaded)
566 0
                                continue;
567 648
                        VTAILQ_FOREACH(vd, &vp->dfrom, lfrom)
568 11
                                if (!vd->to->loaded)
569 0
                                        break;
570 637
                        if (vd != NULL) {
571 0
                                done = 0;
572 0
                                continue;
573
                        }
574 637
                        if (mcf_is_label(vp)) {
575 7
                                vd = VTAILQ_FIRST(&vp->dfrom);
576 7
                                AN(vd);
577 7
                                if (mgt_cli_askchild(status, p,
578
                                    "vcl.label %s %s\n",
579 7
                                    vp->name, vd->to->name))
580 0
                                        return (1);
581
                        } else {
582 630
                                if (mgt_cli_askchild(status, p,
583
                                    "vcl.load \"%s\" %s %d%s\n",
584
                                    vp->name, vp->fname, vp->warm, vp->state))
585 1
                                        return (1);
586
                        }
587 636
                        vp->loaded = 1;
588 636
                        free(*p);
589 636
                        *p = NULL;
590
                }
591 614
        } while (!done);
592
593 614
        if (mgt_cli_askchild(status, p, "vcl.use \"%s\"\n", active_vcl->name))
594 0
                return (1);
595 614
        free(*p);
596 614
        *p = NULL;
597 614
        if (mgt_cli_askchild(status, p, "start\n"))
598 0
                return (1);
599 614
        free(*p);
600 614
        *p = NULL;
601 614
        return (0);
602
}
603
604
/*--------------------------------------------------------------------*/
605
606
static void v_matchproto_(cli_func_t)
607 894
mcf_vcl_inline(struct cli *cli, const char * const *av, void *priv)
608
{
609
610
        (void)priv;
611
612 894
        if (!mcf_find_no_vcl(cli, av[2]))
613 898
                return;
614
615 890
        mgt_new_vcl(cli, av[2], av[3], "<vcl.inline>", av[4], 0);
616
}
617
618
static void v_matchproto_(cli_func_t)
619 5
mcf_vcl_load(struct cli *cli, const char * const *av, void *priv)
620
{
621
622
        (void)priv;
623 5
        if (!mcf_find_no_vcl(cli, av[2]))
624 6
                return;
625
626 4
        mgt_new_vcl(cli, av[2], NULL, av[3], av[4], 0);
627
}
628
629
630
static void v_matchproto_(cli_func_t)
631 15
mcf_vcl_state(struct cli *cli, const char * const *av, void *priv)
632
{
633
        struct vclprog *vp;
634
635
        (void)priv;
636 15
        vp = mcf_find_vcl(cli, av[2]);
637 15
        if (vp == NULL)
638 0
                return;
639
640 15
        if (mcf_is_label(vp)) {
641 1
                VCLI_Out(cli, "Labels are always warm");
642 1
                VCLI_SetResult(cli, CLIS_PARAM);
643 1
                return;
644
        }
645 14
        if (!VTAILQ_EMPTY(&vp->dto)) {
646 3
                AN(strcmp(vp->state, "cold"));
647 3
                if (!strcmp(av[3], "cold")) {
648 1
                        VCLI_Out(cli, "A labeled VCL cannot be set cold");
649 1
                        VCLI_SetResult(cli, CLIS_CANT);
650 1
                        return;
651
                }
652
        }
653
654 13
        if (!strcmp(vp->state, av[3]))
655 2
                return;
656
657 11
        if (!strcmp(av[3], VCL_STATE_AUTO)) {
658 1
                vp->state = VCL_STATE_AUTO;
659 1
                (void)mgt_vcl_setstate(cli, vp, VCL_STATE_AUTO);
660 10
        } else if (!strcmp(av[3], VCL_STATE_COLD)) {
661 5
                if (vp == active_vcl) {
662 1
                        VCLI_Out(cli, "Cannot set the active VCL cold.");
663 1
                        VCLI_SetResult(cli, CLIS_CANT);
664 1
                        return;
665
                }
666 4
                vp->state = VCL_STATE_AUTO;
667 4
                (void)mgt_vcl_setstate(cli, vp, VCL_STATE_COLD);
668 5
        } else if (!strcmp(av[3], VCL_STATE_WARM)) {
669 5
                if (mgt_vcl_setstate(cli, vp, VCL_STATE_WARM) == 0)
670 3
                        vp->state = VCL_STATE_WARM;
671
        } else {
672 0
                VCLI_Out(cli, "State must be one of auto, cold or warm.");
673 0
                VCLI_SetResult(cli, CLIS_PARAM);
674
        }
675
}
676
677
static void v_matchproto_(cli_func_t)
678 745
mcf_vcl_use(struct cli *cli, const char * const *av, void *priv)
679
{
680
        unsigned status;
681 745
        char *p = NULL;
682
        struct vclprog *vp, *vp2;
683
684
        (void)priv;
685 745
        vp = mcf_find_vcl(cli, av[2]);
686 745
        if (vp == NULL)
687 606
                return;
688 744
        if (vp == active_vcl)
689 604
                return;
690 140
        if (mgt_vcl_setstate(cli, vp, VCL_STATE_WARM))
691 0
                return;
692 245
        if (MCH_Running() &&
693 105
            mgt_cli_askchild(&status, &p, "vcl.use %s\n", av[2])) {
694 0
                VCLI_SetResult(cli, status);
695 0
                VCLI_Out(cli, "%s", p);
696 0
                AZ(vp->go_cold);
697 0
                (void)mgt_vcl_setstate(cli, vp, VCL_STATE_AUTO);
698
        } else {
699 140
                VCLI_Out(cli, "VCL '%s' now active", av[2]);
700 140
                vp2 = active_vcl;
701 140
                active_vcl = vp;
702 140
                if (vp2 != NULL) {
703 140
                        AZ(vp2->go_cold);
704 140
                        (void)mgt_vcl_setstate(cli, vp2, VCL_STATE_AUTO);
705
                }
706
        }
707 140
        free(p);
708
}
709
710
static void v_matchproto_(cli_func_t)
711 41
mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv)
712
{
713
        unsigned status;
714 41
        char *p = NULL;
715
        struct vclprog *vp;
716
        struct vcldep *vd;
717
        int n;
718
719
        (void)priv;
720 41
        vp = mcf_find_vcl(cli, av[2]);
721 41
        if (vp == NULL)
722 19
                return;
723 36
        if (vp == active_vcl) {
724 6
                VCLI_SetResult(cli, CLIS_CANT);
725 6
                VCLI_Out(cli, "Cannot discard active VCL program\n");
726 6
                return;
727
        }
728 30
        if (!VTAILQ_EMPTY(&vp->dto)) {
729 3
                VCLI_SetResult(cli, CLIS_CANT);
730 3
                AN(vp->warm);
731 3
                if (!mcf_is_label(vp))
732 2
                        VCLI_Out(cli, "Cannot discard labeled VCL program.\n");
733
                else
734 1
                        VCLI_Out(cli,
735
                            "Cannot discard this VCL label, "
736
                            "other VCLs depend on it.\n");
737 3
                n = 0;
738 11
                VTAILQ_FOREACH(vd, &vp->dto, lto) {
739 8
                        if (n++ == 5) {
740 0
                                VCLI_Out(cli, "\t[...]");
741 0
                                break;
742
                        }
743 8
                        VCLI_Out(cli, "\t%s\n", vd->from->name);
744
                }
745 3
                return;
746
        }
747 27
        if (mcf_is_label(vp))
748 5
                AN(vp->warm);
749
        else
750 22
                (void)mgt_vcl_setstate(cli, vp, VCL_STATE_COLD);
751 27
        if (MCH_Running()) {
752
                /* XXX If this fails the child is crashing, figure that later */
753 26
                assert(vp->state != VCL_STATE_WARM);
754 26
                (void)mgt_cli_askchild(&status, &p, "vcl.discard %s\n", av[2]);
755 26
                free(p);
756
        }
757 27
        mgt_vcl_del(vp);
758
}
759
760
static void v_matchproto_(cli_func_t)
761 48
mcf_vcl_list(struct cli *cli, const char * const *av, void *priv)
762
{
763
        unsigned status;
764
        char *p;
765
        struct vclprog *vp;
766
        struct vcldep *vd;
767
768
        /* NB: Shall generate same output as vcl_cli_list() */
769
770
        (void)av;
771
        (void)priv;
772 48
        if (MCH_Running()) {
773 39
                if (!mgt_cli_askchild(&status, &p, "vcl.list\n")) {
774 39
                        VCLI_SetResult(cli, status);
775 39
                        VCLI_Out(cli, "%s", p);
776
                }
777 39
                free(p);
778
        } else {
779 30
                VTAILQ_FOREACH(vp, &vclhead, list) {
780 42
                        VCLI_Out(cli, "%-10s %5s",
781 21
                            vp == active_vcl ? "active" : "available",
782
                            vp->state);
783 21
                        VCLI_Out(cli, "/%-8s", vp->warm ?
784
                            VCL_STATE_WARM : VCL_STATE_COLD);
785 21
                        VCLI_Out(cli, " %6s %s", "-", vp->name);
786 21
                        if (mcf_is_label(vp)) {
787 7
                                vd = VTAILQ_FIRST(&vp->dfrom);
788 7
                                AN(vd);
789 7
                                VCLI_Out(cli, " -> %s", vd->to->name);
790 7
                                if (vp->nto > 0)
791 2
                                        VCLI_Out(cli, " (%d return(vcl)%s)",
792 2
                                            vp->nto, vp->nto > 1 ? "'s" : "");
793 14
                        } else if (vp->nto > 0) {
794 5
                                VCLI_Out(cli, " (%d label%s)",
795 5
                                    vp->nto, vp->nto > 1 ? "s" : "");
796
                        }
797 21
                        VCLI_Out(cli, "\n");
798
                }
799
        }
800 48
}
801
802
static void v_matchproto_(cli_func_t)
803 30
mcf_vcl_label(struct cli *cli, const char * const *av, void *priv)
804
{
805
        struct vclprog *vpl;
806
        struct vclprog *vpt;
807
        unsigned status;
808
        char *p;
809
        int i;
810
811
        (void)priv;
812 30
        if (mcf_invalid_vclname(cli, av[2]))
813 17
                return;
814 29
        if (mcf_invalid_vclname(cli, av[3]))
815 0
                return;
816 29
        vpt = mcf_find_vcl(cli, av[3]);
817 29
        if (vpt == NULL)
818 2
                return;
819 27
        if (mcf_is_label(vpt)) {
820 2
                VCLI_SetResult(cli, CLIS_CANT);
821 2
                VCLI_Out(cli, "VCL labels cannot point to labels");
822 2
                return;
823
        }
824 25
        vpl = mcf_vcl_byname(av[2]);
825 25
        if (vpl != NULL) {
826 5
                if (!mcf_is_label(vpl)) {
827 1
                        VCLI_SetResult(cli, CLIS_PARAM);
828 1
                        VCLI_Out(cli, "%s is not a label", vpl->name);
829 1
                        return;
830
                }
831 6
                if (!VTAILQ_EMPTY(&vpt->dfrom) &&
832 2
                    !VTAILQ_EMPTY(&vpl->dto)) {
833 2
                        VCLI_SetResult(cli, CLIS_PARAM);
834 2
                        VCLI_Out(cli, "return(vcl) can only be used from"
835
                            " the active VCL.\n\n");
836 2
                        VCLI_Out(cli,
837
                            "Label %s is used in return(vcl) from VCL %s\n",
838 2
                            vpl->name, VTAILQ_FIRST(&vpl->dto)->from->name);
839 2
                        VCLI_Out(cli, "and VCL %s also has return(vcl)",
840
                            vpt->name);
841 2
                        return;
842
                }
843
        }
844
845 22
        if (mgt_vcl_setstate(cli, vpt, VCL_STATE_WARM))
846 1
                return;
847 21
        vpt->state = VCL_STATE_WARM; /* XXX: race with the poker? */
848
849 21
        if (vpl != NULL) {
850 2
                mgt_vcl_dep_del(VTAILQ_FIRST(&vpl->dfrom));
851 2
                AN(VTAILQ_EMPTY(&vpl->dfrom));
852
        } else {
853 19
                vpl = mgt_vcl_add(av[2], VCL_STATE_LABEL);
854
        }
855
856 21
        AN(vpl);
857 21
        vpl->warm = 1;
858 21
        mgt_vcl_dep_add(vpl, vpt);
859
860 21
        if (!MCH_Running())
861 7
                return;
862
863 14
        i = mgt_cli_askchild(&status, &p, "vcl.label %s %s\n", av[2], av[3]);
864 14
        if (i) {
865 0
                VCLI_SetResult(cli, status);
866 0
                VCLI_Out(cli, "%s", p);
867
        }
868 14
        free(p);
869
}
870
871
/*--------------------------------------------------------------------*/
872
873
static int v_matchproto_(vev_cb_f)
874 742
mgt_vcl_poker(const struct vev *e, int what)
875
{
876
        struct vclprog *vp;
877
878
        (void)e;
879
        (void)what;
880 742
        e_poker->timeout = mgt_param.vcl_cooldown * .45;
881 1607
        VTAILQ_FOREACH(vp, &vclhead, list)
882 865
                if (mgt_vcl_cooldown(vp))
883 23
                        (void)mgt_vcl_setstate(NULL, vp, VCL_STATE_COLD);
884 742
        return (0);
885
}
886
887
/*--------------------------------------------------------------------*/
888
889
static struct cli_proto cli_vcl[] = {
890
        { CLICMD_VCL_LOAD,              "", mcf_vcl_load },
891
        { CLICMD_VCL_INLINE,            "", mcf_vcl_inline },
892
        { CLICMD_VCL_USE,               "", mcf_vcl_use },
893
        { CLICMD_VCL_STATE,             "", mcf_vcl_state },
894
        { CLICMD_VCL_DISCARD,           "", mcf_vcl_discard },
895
        { CLICMD_VCL_LIST,              "", mcf_vcl_list },
896
        { CLICMD_VCL_LABEL,             "", mcf_vcl_label },
897
        { NULL }
898
};
899
900
/*--------------------------------------------------------------------*/
901
902
static void
903 2903
mgt_vcl_atexit(void)
904
{
905
        struct vclprog *vp;
906
907 2903
        if (getpid() != heritage.mgt_pid)
908 5178
                return;
909 628
        active_vcl = NULL;
910
        do {
911 1357
                vp = VTAILQ_FIRST(&vclhead);
912 1357
                if (vp != NULL)
913 729
                        mgt_vcl_del(vp);
914 1357
        } while (vp != NULL);
915
}
916
917
void
918 628
mgt_vcl_init(void)
919
{
920
921 628
        e_poker = VEV_Alloc();
922 628
        AN(e_poker);
923 628
        e_poker->timeout = 3;           // random, prime
924
925 628
        e_poker->callback = mgt_vcl_poker;
926 628
        e_poker->name = "vcl poker";
927 628
        AZ(VEV_Start(mgt_evb, e_poker));
928
929 628
        AZ(atexit(mgt_vcl_atexit));
930
931 628
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_vcl);
932 628
}