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 24492
mcf_vcl_byname(const char *name)
111
{
112
        struct vclprog *vp;
113
114 38964
        VTAILQ_FOREACH(vp, &vclhead, list)
115 25992
                if (!strcmp(name, vp->name))
116 11520
                        return (vp);
117 12972
        return (NULL);
118
}
119
120
static int
121 24576
mcf_invalid_vclname(struct cli *cli, const char *name)
122
{
123
        const char *bad;
124
125 24576
        AN(name);
126 24576
        bad = VCT_invalid_name(name, NULL);
127
128 24576
        if (bad != NULL) {
129 60
                VCLI_SetResult(cli, CLIS_PARAM);
130 60
                VCLI_Out(cli, "Illegal character in VCL name ");
131 60
                if (*bad > 0x20 && *bad < 0x7f)
132 48
                        VCLI_Out(cli, "('%c')", *bad);
133
                else
134 12
                        VCLI_Out(cli, "(0x%02x)", *bad & 0xff);
135 60
                return (-1);
136
        }
137 24516
        return (0);
138
}
139
140
static struct vclprog *
141 11220
mcf_find_vcl(struct cli *cli, const char *name)
142
{
143
        struct vclprog *vp;
144
145 11220
        if (mcf_invalid_vclname(cli, name))
146 0
                return (NULL);
147
148 11220
        vp = mcf_vcl_byname(name);
149 11220
        if (vp == NULL) {
150 96
                VCLI_SetResult(cli, CLIS_PARAM);
151 96
                VCLI_Out(cli, "No VCL named %s known.", name);
152
        }
153 11220
        return (vp);
154
}
155
156
static int
157 12648
mcf_find_no_vcl(struct cli *cli, const char *name)
158
{
159
160 12648
        if (mcf_invalid_vclname(cli, name))
161 48
                return (0);
162
163 12600
        if (mcf_vcl_byname(name) != NULL) {
164 12
                VCLI_SetResult(cli, CLIS_PARAM);
165 12
                VCLI_Out(cli, "Already a VCL named %s", name);
166 12
                return (0);
167
        }
168 12588
        return (1);
169
}
170
171
static int
172 35137
mcf_is_label(const struct vclprog *vp)
173
{
174 35137
        return (!strcmp(vp->state, VCL_STATE_LABEL));
175
}
176
177
/*--------------------------------------------------------------------*/
178
179
static void
180 444
mgt_vcl_dep_add(struct vclprog *vp_from, struct vclprog *vp_to)
181
{
182
        struct vcldep *vd;
183
184 444
        CHECK_OBJ_NOTNULL(vp_from, VCLPROG_MAGIC);
185 444
        CHECK_OBJ_NOTNULL(vp_to, VCLPROG_MAGIC);
186 444
        ALLOC_OBJ(vd, VCLDEP_MAGIC);
187 444
        XXXAN(vd);
188 444
        vd->from = vp_from;
189 444
        VTAILQ_INSERT_TAIL(&vp_from->dfrom, vd, lfrom);
190 444
        vd->to = vp_to;
191 444
        VTAILQ_INSERT_TAIL(&vp_to->dto, vd, lto);
192 444
        vp_to->nto++;
193 444
        assert(vp_to->state == VCL_STATE_WARM ||        /* vcl.label ... */
194
            vp_to->state == VCL_STATE_LABEL);           /* return(vcl(...)) */
195 444
}
196
197
static void
198 444
mgt_vcl_dep_del(struct vcldep *vd)
199
{
200
201 444
        CHECK_OBJ_NOTNULL(vd, VCLDEP_MAGIC);
202 444
        VTAILQ_REMOVE(&vd->from->dfrom, vd, lfrom);
203 444
        VTAILQ_REMOVE(&vd->to->dto, vd, lto);
204 444
        vd->to->nto--;
205 444
        if (vd->to->nto == 0 && vd->to->state == VCL_STATE_WARM) {
206 192
                vd->to->state = VCL_STATE_AUTO;
207 192
                AZ(vd->to->go_cold);
208 192
                (void)mgt_vcl_setstate(NULL, vd->to, VCL_STATE_AUTO);
209 192
                AN(vd->to->go_cold);
210
        }
211 444
        FREE_OBJ(vd);
212 444
}
213
214
/*--------------------------------------------------------------------*/
215
216
static struct vclprog *
217 12996
mgt_vcl_add(const char *name, const char *state)
218
{
219
        struct vclprog *vp;
220
221 12996
        assert(state == VCL_STATE_WARM ||
222
               state == VCL_STATE_COLD ||
223
               state == VCL_STATE_AUTO ||
224
               state == VCL_STATE_LABEL);
225 12996
        ALLOC_OBJ(vp, VCLPROG_MAGIC);
226 12996
        XXXAN(vp);
227 12996
        REPLACE(vp->name, name);
228 12996
        VTAILQ_INIT(&vp->dfrom);
229 12996
        VTAILQ_INIT(&vp->dto);
230 12996
        VTAILQ_INIT(&vp->vmods);
231 12996
        vp->state = state;
232
233 12996
        if (vp->state != VCL_STATE_COLD)
234 12984
                vp->warm = 1;
235
236 12996
        VTAILQ_INSERT_TAIL(&vclhead, vp, list);
237 12996
        return (vp);
238
}
239
240
static void
241 12996
mgt_vcl_del(struct vclprog *vp)
242
{
243
        char *p;
244
        struct vmoddep *vd;
245
        struct vmodfile *vf;
246
247 12996
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
248 26280
        while (!VTAILQ_EMPTY(&vp->dto))
249 288
                mgt_vcl_dep_del(VTAILQ_FIRST(&vp->dto));
250 26124
        while (!VTAILQ_EMPTY(&vp->dfrom))
251 132
                mgt_vcl_dep_del(VTAILQ_FIRST(&vp->dfrom));
252
253 12996
        VTAILQ_REMOVE(&vclhead, vp, list);
254 12996
        if (vp->fname != NULL) {
255 10512
                AZ(unlink(vp->fname));
256 10512
                p = strrchr(vp->fname, '/');
257 10512
                AN(p);
258 10512
                *p = '\0';
259 10512
                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 10512
                (void)rmdir(vp->fname);
267 10512
                VJ_master(JAIL_MASTER_LOW);
268 10512
                free(vp->fname);
269
        }
270 29796
        while (!VTAILQ_EMPTY(&vp->vmods)) {
271 3804
                vd = VTAILQ_FIRST(&vp->vmods);
272 3804
                CHECK_OBJ(vd, VMODDEP_MAGIC);
273 3804
                vf = vd->to;
274 3804
                CHECK_OBJ(vf, VMODFILE_MAGIC);
275 3804
                VTAILQ_REMOVE(&vp->vmods, vd, lfrom);
276 3804
                VTAILQ_REMOVE(&vf->vcls, vd, lto);
277 3804
                FREE_OBJ(vd);
278
279 3804
                if (VTAILQ_EMPTY(&vf->vcls)) {
280 2616
                        if (!MGT_DO_DEBUG(DBG_VMOD_SO_KEEP))
281 2604
                                AZ(unlink(vf->fname));
282 2616
                        VTAILQ_REMOVE(&vmodhead, vf, list);
283 2616
                        free(vf->fname);
284 2616
                        FREE_OBJ(vf);
285
                }
286
        }
287 12996
        free(vp->name);
288 12996
        FREE_OBJ(vp);
289 12996
}
290
291
void
292 192
mgt_vcl_depends(struct vclprog *vp1, const char *name)
293
{
294
        struct vclprog *vp2;
295
296 192
        CHECK_OBJ_NOTNULL(vp1, VCLPROG_MAGIC);
297
298 192
        vp2 = mcf_vcl_byname(name);
299 192
        CHECK_OBJ_NOTNULL(vp2, VCLPROG_MAGIC);
300 192
        mgt_vcl_dep_add(vp1, vp2);
301 192
}
302
303
static int
304 2616
mgt_vcl_cache_vmod(const char *nm, const char *fm, const char *to)
305
{
306
        int fi, fo;
307 2616
        int ret = 0;
308
        ssize_t sz;
309
        char buf[BUFSIZ];
310
311 2616
        fo = open(to, O_WRONLY | O_CREAT | O_EXCL, 0744);
312 2616
        if (fo < 0 && errno == EEXIST)
313 0
                return (0);
314 2616
        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 2616
        fi = open(fm, O_RDONLY);
320 2616
        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 1039608
                sz = read(fi, buf, sizeof buf);
329 521112
                if (sz == 0)
330 2616
                        break;
331 518496
                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
        }
339 2616
        closefd(&fi);
340 2616
        AZ(fchmod(fo, 0444));
341 2616
        closefd(&fo);
342 2616
        return(ret);
343
}
344
345
void
346 3804
mgt_vcl_vmod(struct vclprog *vp, const char *src, const char *dst)
347
{
348
        struct vmodfile *vf;
349
        struct vmoddep *vd;
350
351 3804
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
352 3804
        AN(src);
353 3804
        AN(dst);
354 3804
        assert(!strncmp(dst, "./vmod_cache/", 13));
355
356 4716
        VTAILQ_FOREACH(vf, &vmodhead, list)
357 2100
                if (!strcmp(vf->fname, dst))
358 1188
                        break;
359 3804
        if (vf == NULL) {
360 2616
                ALLOC_OBJ(vf, VMODFILE_MAGIC);
361 2616
                AN(vf);
362 2616
                REPLACE(vf->fname, dst);
363 2616
                AN(vf->fname);
364 2616
                VTAILQ_INIT(&vf->vcls);
365 2616
                AZ(mgt_vcl_cache_vmod(vp->name, src, dst));
366 2616
                VTAILQ_INSERT_TAIL(&vmodhead, vf, list);
367
        }
368 3804
        ALLOC_OBJ(vd, VMODDEP_MAGIC);
369 3804
        AN(vd);
370 3804
        vd->to = vf;
371 3804
        VTAILQ_INSERT_TAIL(&vp->vmods, vd, lfrom);
372 3804
        VTAILQ_INSERT_TAIL(&vf->vcls, vd, lto);
373 3804
}
374
375
int
376 16968
mgt_has_vcl(void)
377
{
378
379 16968
        return (!VTAILQ_EMPTY(&vclhead));
380
}
381
382
static unsigned
383 13537
mgt_vcl_cooldown(struct vclprog *vp)
384
{
385
        double now;
386
387 13537
        if (vp->state != VCL_STATE_AUTO)
388 525
                return (0);
389
390 13012
        now = VTIM_mono();
391 13012
        if (vp->go_cold > 0 && vp->go_cold + mgt_param.vcl_cooldown < now)
392 265
                return (1);
393
394 12747
        if (vp->go_cold == 0 && vp != active_vcl)
395 2221
                vp->go_cold = now;
396
397 12747
        return (0);
398
}
399
400
static int
401 13321
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 13321
        assert(vs != VCL_STATE_LABEL);
408
409 13321
        if (vp == active_vcl || mcf_is_label(vp)) {
410 8724
                AN(vp->warm);
411
                /* Only the poker sends COLD indiscriminately, ignore it */
412 8724
                if (vs == VCL_STATE_COLD)
413 0
                        AZ(cli);
414 8724
                return (0);
415
        }
416
417 4597
        if (vs == VCL_STATE_AUTO)
418 2040
                vs = (mgt_vcl_cooldown(vp) ? VCL_STATE_COLD : VCL_STATE_WARM);
419
        else
420 2557
                vp->go_cold = 0;
421
422 4597
        warm = (vs == VCL_STATE_WARM ? 1 : 0);
423
424 4597
        if (vp->warm == warm)
425 4117
                return (0);
426
427 480
        if (!MCH_Running()) {
428 12
                vp->warm = warm;
429 12
                return (0);
430
        }
431
432 468
        i = mgt_cli_askchild(&status, &p, "vcl.state %s %d%s\n",
433
            vp->name, warm, vs);
434 468
        if (i && cli != NULL) {
435 36
                VCLI_SetResult(cli, status);
436 36
                VCLI_Out(cli, "%s", p);
437 432
        } 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 432
                vp->warm = warm;
446
        }
447
448 468
        free(p);
449 468
        return (i);
450
}
451
452
/*--------------------------------------------------------------------*/
453
454
static void
455 12768
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 12768
        AN(cli);
464
465 12768
        if (C_flag) {
466 24
                bprintf(buf, ".CflagTest.%d", (int)getpid());
467 24
                vclname = buf;
468
        }
469
470 12768
        if (state == NULL)
471 12756
                state = VCL_STATE_AUTO;
472 12
        else if (!strcmp(state, VCL_STATE_AUTO))
473 0
                state = VCL_STATE_AUTO;
474 12
        else if (!strcmp(state, VCL_STATE_COLD))
475 12
                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 12768
        vp = mgt_vcl_add(vclname, state);
485 12768
        lib = mgt_VccCompile(cli, vp, vclname, vclsrc, vclsrcfile, C_flag);
486 12768
        if (lib == NULL) {
487 2256
                mgt_vcl_del(vp);
488 2256
                return;
489
        }
490
491 10512
        AZ(C_flag);
492 10512
        vp->fname = lib;
493
494 10512
        if (active_vcl == NULL)
495 8364
                active_vcl = vp;
496
497 10512
        if (!MCH_Running())
498 8784
                return;
499
500 1728
        if (mgt_cli_askchild(&status, &p, "vcl.load %s %s %d%s\n",
501
            vp->name, vp->fname, vp->warm, vp->state)) {
502 420
                mgt_vcl_del(vp);
503 420
                VCLI_Out(cli, "%s", p);
504 420
                VCLI_SetResult(cli, status);
505 420
                free(p);
506 420
                return;
507
        }
508 1308
        free(p);
509
510 1308
        if (vp->warm && !strcmp(vp->state, "auto"))
511 1296
                vp->go_cold = VTIM_mono();
512
}
513
514
/*--------------------------------------------------------------------*/
515
516
void
517 180
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 180
        AN(vclsrc);
524 180
        AN(origin);
525 180
        if (vclname == NULL) {
526 12
                bprintf(buf, "boot%d", n++);
527 12
                vclname = buf;
528
        }
529 180
        mgt_new_vcl(cli, vclname, vclsrc, origin, NULL, C_flag);
530 180
        active_vcl = mcf_vcl_byname(vclname);
531 180
}
532
533
/*--------------------------------------------------------------------*/
534
535
void
536 12768
mgt_vcl_export_labels(struct vcc *vcc)
537
{
538
        struct vclprog *vp;
539 33336
        VTAILQ_FOREACH(vp, &vclhead, list) {
540 20568
                if (mcf_is_label(vp))
541 432
                        VCC_Predef(vcc, "VCL_VCL", vp->name);
542
        }
543 12768
}
544
545
/*--------------------------------------------------------------------*/
546
547
int
548 8436
mgt_push_vcls(struct cli *cli, unsigned *status, char **p)
549
{
550
        struct vclprog *vp;
551
        struct vcldep *vd;
552
        int done;
553
554 8436
        AN(active_vcl);
555
556
        /* The VCL has not been loaded yet, it cannot fail */
557 8436
        AZ(mgt_vcl_setstate(cli, active_vcl, VCL_STATE_WARM));
558
559 17160
        VTAILQ_FOREACH(vp, &vclhead, list)
560 8724
                vp->loaded = 0;
561
562
        do {
563 8436
                done = 1;
564 17136
                VTAILQ_FOREACH(vp, &vclhead, list) {
565 8712
                        if (vp->loaded)
566 0
                                continue;
567 8844
                        VTAILQ_FOREACH(vd, &vp->dfrom, lfrom)
568 132
                                if (!vd->to->loaded)
569 0
                                        break;
570 8712
                        if (vd != NULL) {
571 0
                                done = 0;
572 0
                                continue;
573
                        }
574 8712
                        if (mcf_is_label(vp)) {
575 84
                                vd = VTAILQ_FIRST(&vp->dfrom);
576 84
                                AN(vd);
577 84
                                if (mgt_cli_askchild(status, p,
578
                                    "vcl.label %s %s\n",
579 84
                                    vp->name, vd->to->name))
580 0
                                        return (1);
581
                        } else {
582 8628
                                if (mgt_cli_askchild(status, p,
583
                                    "vcl.load \"%s\" %s %d%s\n",
584
                                    vp->name, vp->fname, vp->warm, vp->state))
585 12
                                        return (1);
586
                        }
587 8700
                        vp->loaded = 1;
588 8700
                        free(*p);
589 8700
                        *p = NULL;
590
                }
591 8424
        } while (!done);
592
593 8424
        if (mgt_cli_askchild(status, p, "vcl.use \"%s\"\n", active_vcl->name))
594 0
                return (1);
595 8424
        free(*p);
596 8424
        *p = NULL;
597 8424
        return (0);
598
}
599
600
/*--------------------------------------------------------------------*/
601
602
static void v_matchproto_(cli_func_t)
603 12588
mcf_vcl_inline(struct cli *cli, const char * const *av, void *priv)
604
{
605
606
        (void)priv;
607
608 12588
        if (!mcf_find_no_vcl(cli, av[2]))
609 48
                return;
610
611 12540
        mgt_new_vcl(cli, av[2], av[3], "<vcl.inline>", av[4], 0);
612
}
613
614
static void v_matchproto_(cli_func_t)
615 60
mcf_vcl_load(struct cli *cli, const char * const *av, void *priv)
616
{
617
618
        (void)priv;
619 60
        if (!mcf_find_no_vcl(cli, av[2]))
620 12
                return;
621
622 48
        mgt_new_vcl(cli, av[2], NULL, av[3], av[4], 0);
623
}
624
625
626
static void v_matchproto_(cli_func_t)
627 180
mcf_vcl_state(struct cli *cli, const char * const *av, void *priv)
628
{
629
        struct vclprog *vp;
630
631
        (void)priv;
632 180
        vp = mcf_find_vcl(cli, av[2]);
633 180
        if (vp == NULL)
634 0
                return;
635
636 180
        if (mcf_is_label(vp)) {
637 12
                VCLI_Out(cli, "Labels are always warm");
638 12
                VCLI_SetResult(cli, CLIS_PARAM);
639 12
                return;
640
        }
641 168
        if (!VTAILQ_EMPTY(&vp->dto)) {
642 36
                AN(strcmp(vp->state, "cold"));
643 36
                if (!strcmp(av[3], "cold")) {
644 12
                        VCLI_Out(cli, "A labeled VCL cannot be set cold");
645 12
                        VCLI_SetResult(cli, CLIS_CANT);
646 12
                        return;
647
                }
648
        }
649
650 156
        if (!strcmp(vp->state, av[3]))
651 24
                return;
652
653 132
        if (!strcmp(av[3], VCL_STATE_AUTO)) {
654 12
                vp->state = VCL_STATE_AUTO;
655 12
                (void)mgt_vcl_setstate(cli, vp, VCL_STATE_AUTO);
656 120
        } else if (!strcmp(av[3], VCL_STATE_COLD)) {
657 60
                if (vp == active_vcl) {
658 12
                        VCLI_Out(cli, "Cannot set the active VCL cold.");
659 12
                        VCLI_SetResult(cli, CLIS_CANT);
660 12
                        return;
661
                }
662 48
                vp->state = VCL_STATE_AUTO;
663 48
                (void)mgt_vcl_setstate(cli, vp, VCL_STATE_COLD);
664 60
        } else if (!strcmp(av[3], VCL_STATE_WARM)) {
665 60
                if (mgt_vcl_setstate(cli, vp, VCL_STATE_WARM) == 0)
666 36
                        vp->state = VCL_STATE_WARM;
667
        } else {
668 0
                VCLI_Out(cli, "State must be one of auto, cold or warm.");
669 0
                VCLI_SetResult(cli, CLIS_PARAM);
670
        }
671
}
672
673
static void v_matchproto_(cli_func_t)
674 10164
mcf_vcl_use(struct cli *cli, const char * const *av, void *priv)
675
{
676
        unsigned status;
677 10164
        char *p = NULL;
678
        struct vclprog *vp, *vp2;
679
680
        (void)priv;
681 10164
        vp = mcf_find_vcl(cli, av[2]);
682 10164
        if (vp == NULL)
683 8304
                return;
684 10152
        if (vp == active_vcl)
685 8280
                return;
686 1872
        if (mgt_vcl_setstate(cli, vp, VCL_STATE_WARM))
687 0
                return;
688 3312
        if (MCH_Running() &&
689 1440
            mgt_cli_askchild(&status, &p, "vcl.use %s\n", av[2])) {
690 0
                VCLI_SetResult(cli, status);
691 0
                VCLI_Out(cli, "%s", p);
692 0
                AZ(vp->go_cold);
693 0
                (void)mgt_vcl_setstate(cli, vp, VCL_STATE_AUTO);
694
        } else {
695 1872
                VCLI_Out(cli, "VCL '%s' now active", av[2]);
696 1872
                vp2 = active_vcl;
697 1872
                active_vcl = vp;
698 1872
                if (vp2 != NULL) {
699 1872
                        AZ(vp2->go_cold);
700 1872
                        (void)mgt_vcl_setstate(cli, vp2, VCL_STATE_AUTO);
701
                }
702
        }
703 1872
        free(p);
704
}
705
706
static void v_matchproto_(cli_func_t)
707 528
mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv)
708
{
709
        unsigned status;
710 528
        char *p = NULL;
711
        struct vclprog *vp;
712
        struct vcldep *vd;
713
        int n;
714
715
        (void)priv;
716 528
        vp = mcf_find_vcl(cli, av[2]);
717 528
        if (vp == NULL)
718 228
                return;
719 468
        if (vp == active_vcl) {
720 72
                VCLI_SetResult(cli, CLIS_CANT);
721 72
                VCLI_Out(cli, "Cannot discard active VCL program\n");
722 72
                return;
723
        }
724 396
        if (!VTAILQ_EMPTY(&vp->dto)) {
725 36
                VCLI_SetResult(cli, CLIS_CANT);
726 36
                AN(vp->warm);
727 36
                if (!mcf_is_label(vp))
728 24
                        VCLI_Out(cli, "Cannot discard labeled VCL program.\n");
729
                else
730 12
                        VCLI_Out(cli,
731
                            "Cannot discard this VCL label, "
732
                            "other VCLs depend on it.\n");
733 36
                n = 0;
734 132
                VTAILQ_FOREACH(vd, &vp->dto, lto) {
735 96
                        if (n++ == 5) {
736 0
                                VCLI_Out(cli, "\t[...]");
737 0
                                break;
738
                        }
739 96
                        VCLI_Out(cli, "\t%s\n", vd->from->name);
740
                }
741 36
                return;
742
        }
743 360
        if (mcf_is_label(vp))
744 60
                AN(vp->warm);
745
        else
746 300
                (void)mgt_vcl_setstate(cli, vp, VCL_STATE_COLD);
747 360
        if (MCH_Running()) {
748
                /* XXX If this fails the child is crashing, figure that later */
749 348
                assert(vp->state != VCL_STATE_WARM);
750 348
                (void)mgt_cli_askchild(&status, &p, "vcl.discard %s\n", av[2]);
751 348
                free(p);
752
        }
753 360
        mgt_vcl_del(vp);
754
}
755
756
static void v_matchproto_(cli_func_t)
757 648
mcf_vcl_list(struct cli *cli, const char * const *av, void *priv)
758
{
759
        unsigned status;
760
        char *p;
761
        struct vclprog *vp;
762
        struct vcldep *vd;
763
764
        /* NB: Shall generate same output as vcl_cli_list() */
765
766
        (void)av;
767
        (void)priv;
768 648
        if (MCH_Running()) {
769 540
                if (!mgt_cli_askchild(&status, &p, "vcl.list\n")) {
770 540
                        VCLI_SetResult(cli, status);
771 540
                        VCLI_Out(cli, "%s", p);
772
                }
773 540
                free(p);
774
        } else {
775 360
                VTAILQ_FOREACH(vp, &vclhead, list) {
776 504
                        VCLI_Out(cli, "%-10s %5s",
777 252
                            vp == active_vcl ? "active" : "available",
778
                            vp->state);
779 252
                        VCLI_Out(cli, "/%-8s", vp->warm ?
780
                            VCL_STATE_WARM : VCL_STATE_COLD);
781 252
                        VCLI_Out(cli, " %6s %s", "-", vp->name);
782 252
                        if (mcf_is_label(vp)) {
783 84
                                vd = VTAILQ_FIRST(&vp->dfrom);
784 84
                                AN(vd);
785 84
                                VCLI_Out(cli, " -> %s", vd->to->name);
786 84
                                if (vp->nto > 0)
787 24
                                        VCLI_Out(cli, " (%d return(vcl)%s)",
788 24
                                            vp->nto, vp->nto > 1 ? "'s" : "");
789 168
                        } else if (vp->nto > 0) {
790 60
                                VCLI_Out(cli, " (%d label%s)",
791 60
                                    vp->nto, vp->nto > 1 ? "s" : "");
792
                        }
793 252
                        VCLI_Out(cli, "\n");
794
                }
795
        }
796 648
}
797
798
static void v_matchproto_(cli_func_t)
799 360
mcf_vcl_label(struct cli *cli, const char * const *av, void *priv)
800
{
801
        struct vclprog *vpl;
802
        struct vclprog *vpt;
803
        unsigned status;
804
        char *p;
805
        int i;
806
807
        (void)priv;
808 360
        if (mcf_invalid_vclname(cli, av[2]))
809 204
                return;
810 348
        if (mcf_invalid_vclname(cli, av[3]))
811 0
                return;
812 348
        vpt = mcf_find_vcl(cli, av[3]);
813 348
        if (vpt == NULL)
814 24
                return;
815 324
        if (mcf_is_label(vpt)) {
816 24
                VCLI_SetResult(cli, CLIS_CANT);
817 24
                VCLI_Out(cli, "VCL labels cannot point to labels");
818 24
                return;
819
        }
820 300
        vpl = mcf_vcl_byname(av[2]);
821 300
        if (vpl != NULL) {
822 60
                if (!mcf_is_label(vpl)) {
823 12
                        VCLI_SetResult(cli, CLIS_PARAM);
824 12
                        VCLI_Out(cli, "%s is not a label", vpl->name);
825 12
                        return;
826
                }
827 72
                if (!VTAILQ_EMPTY(&vpt->dfrom) &&
828 24
                    !VTAILQ_EMPTY(&vpl->dto)) {
829 24
                        VCLI_SetResult(cli, CLIS_PARAM);
830 24
                        VCLI_Out(cli, "return(vcl) can only be used from"
831
                            " the active VCL.\n\n");
832 24
                        VCLI_Out(cli,
833
                            "Label %s is used in return(vcl) from VCL %s\n",
834 24
                            vpl->name, VTAILQ_FIRST(&vpl->dto)->from->name);
835 24
                        VCLI_Out(cli, "and VCL %s also has return(vcl)",
836
                            vpt->name);
837 24
                        return;
838
                }
839
        }
840
841 264
        if (mgt_vcl_setstate(cli, vpt, VCL_STATE_WARM))
842 12
                return;
843 252
        vpt->state = VCL_STATE_WARM; /* XXX: race with the poker? */
844
845 252
        if (vpl != NULL) {
846 24
                mgt_vcl_dep_del(VTAILQ_FIRST(&vpl->dfrom));
847 24
                AN(VTAILQ_EMPTY(&vpl->dfrom));
848
        } else {
849 228
                vpl = mgt_vcl_add(av[2], VCL_STATE_LABEL);
850
        }
851
852 252
        AN(vpl);
853 252
        vpl->warm = 1;
854 252
        mgt_vcl_dep_add(vpl, vpt);
855
856 252
        if (!MCH_Running())
857 84
                return;
858
859 168
        i = mgt_cli_askchild(&status, &p, "vcl.label %s %s\n", av[2], av[3]);
860 168
        if (i) {
861 0
                VCLI_SetResult(cli, status);
862 0
                VCLI_Out(cli, "%s", p);
863
        }
864 168
        free(p);
865
}
866
867
/*--------------------------------------------------------------------*/
868
869
static int v_matchproto_(vev_cb_f)
870 9952
mgt_vcl_poker(const struct vev *e, int what)
871
{
872
        struct vclprog *vp;
873
874
        (void)e;
875
        (void)what;
876 9952
        e_poker->timeout = mgt_param.vcl_cooldown * .45;
877 21449
        VTAILQ_FOREACH(vp, &vclhead, list)
878 11497
                if (mgt_vcl_cooldown(vp))
879 265
                        (void)mgt_vcl_setstate(NULL, vp, VCL_STATE_COLD);
880 9952
        return (0);
881
}
882
883
/*--------------------------------------------------------------------*/
884
885
static struct cli_proto cli_vcl[] = {
886
        { CLICMD_VCL_LOAD,              "", mcf_vcl_load },
887
        { CLICMD_VCL_INLINE,            "", mcf_vcl_inline },
888
        { CLICMD_VCL_USE,               "", mcf_vcl_use },
889
        { CLICMD_VCL_STATE,             "", mcf_vcl_state },
890
        { CLICMD_VCL_DISCARD,           "", mcf_vcl_discard },
891
        { CLICMD_VCL_LIST,              "", mcf_vcl_list },
892
        { CLICMD_VCL_LABEL,             "", mcf_vcl_label },
893
        { NULL }
894
};
895
896
/*--------------------------------------------------------------------*/
897
898
static void
899 39948
mgt_vcl_atexit(void)
900
{
901
        struct vclprog *vp;
902
903 39948
        if (getpid() != heritage.mgt_pid)
904 31356
                return;
905 8592
        active_vcl = NULL;
906
        do {
907 18552
                vp = VTAILQ_FIRST(&vclhead);
908 18552
                if (vp != NULL)
909 9960
                        mgt_vcl_del(vp);
910 18552
        } while (vp != NULL);
911
}
912
913
void
914 8592
mgt_vcl_init(void)
915
{
916
917 8592
        e_poker = VEV_Alloc();
918 8592
        AN(e_poker);
919 8592
        e_poker->timeout = 3;           // random, prime
920
921 8592
        e_poker->callback = mgt_vcl_poker;
922 8592
        e_poker->name = "vcl poker";
923 8592
        AZ(VEV_Start(mgt_evb, e_poker));
924
925 8592
        AZ(atexit(mgt_vcl_atexit));
926
927 8592
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_vcl);
928 8592
}