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