vinyl-cache/bin/vinyld/mgt/mgt_vcl.c
0
/*-
1 567
 * Copyright (c) 2006 Verdens Gang AS
2 294
 * Copyright (c) 2006-2015 Varnish Software AS
3 84
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * VCL management stuff
31
 */
32
33
#include "config.h"
34
35
#include <errno.h>
36
#include <fcntl.h>
37
#include <fnmatch.h>
38
#include <signal.h>
39
#include <stdio.h>
40
#include <stdlib.h>
41
#include <string.h>
42
#include <unistd.h>
43
44
#include "mgt/mgt.h"
45
#include "mgt/mgt_vcl.h"
46
#include "common/heritage.h"
47
48
#include "vcli_serve.h"
49
#include "vct.h"
50
#include "vev.h"
51
#include "vte.h"
52
#include "vtim.h"
53
54
struct vclstate {
55
        const char              *name;
56
};
57
58
#define VCL_STATE(sym, str)                                             \
59
        static const struct vclstate VCL_STATE_ ## sym[1] = {{ str }};
60
#include "tbl/vcl_states.h"
61
62
static const struct vclstate VCL_STATE_LABEL[1] = {{ "label" }};
63
64
static unsigned vcl_count;
65
66
struct vclproghead vclhead = VTAILQ_HEAD_INITIALIZER(vclhead);
67
static struct vclproghead discardhead = VTAILQ_HEAD_INITIALIZER(discardhead);
68
struct vmodfilehead vmodhead = VTAILQ_HEAD_INITIALIZER(vmodhead);
69
static struct vclprog *mgt_vcl_active;
70
static struct vev *e_poker;
71
72
static int mgt_vcl_setstate(struct cli *, struct vclprog *,
73
    const struct vclstate *);
74
static int mgt_vcl_settemp(struct cli *, struct vclprog *, unsigned);
75
static int mgt_vcl_askchild(struct cli *, struct vclprog *, unsigned);
76
static void mgt_vcl_set_cooldown(struct vclprog *, vtim_mono);
77
78
/*--------------------------------------------------------------------*/
79
80
static const struct vclstate *
81 567
mcf_vcl_parse_state(struct cli *cli, const char *s)
82
{
83 567
        if (s != NULL) {
84
#define VCL_STATE(sym, str)                             \
85
                if (!vstrcmp(s, str))                   \
86
                        return (VCL_STATE_ ## sym);
87
#include "tbl/vcl_states.h"
88
        }
89 0
        VCLI_Out(cli, "State must be one of auto, cold or warm.");
90 0
        VCLI_SetResult(cli, CLIS_PARAM);
91 0
        return (NULL);
92 567
}
93
94
struct vclprog *
95 62516
mcf_vcl_byname(const char *name)
96
{
97
        struct vclprog *vp;
98
99 103445
        VTAILQ_FOREACH(vp, &vclhead, list)
100 69090
                if (!vstrcmp(name, vp->name))
101 28161
                        return (vp);
102 34355
        return (NULL);
103 62516
}
104
105
static int
106 62180
mcf_invalid_vclname(struct cli *cli, const char *name)
107
{
108
        const char *bad;
109
110 62180
        AN(name);
111 62180
        bad = VCT_invalid_name(name, NULL);
112
113 62180
        if (bad != NULL) {
114 105
                VCLI_SetResult(cli, CLIS_PARAM);
115 105
                VCLI_Out(cli, "Illegal character in VCL name ");
116 105
                if (*bad > 0x20 && *bad < 0x7f)
117 84
                        VCLI_Out(cli, "('%c')", *bad);
118
                else
119 21
                        VCLI_Out(cli, "(0x%02x)", *bad & 0xff);
120 105
                return (-1);
121
        }
122 62075
        return (0);
123 62180
}
124
125
static struct vclprog *
126 27615
mcf_find_vcl(struct cli *cli, const char *name)
127
{
128
        struct vclprog *vp;
129
130 27615
        if (mcf_invalid_vclname(cli, name))
131 0
                return (NULL);
132
133 27615
        vp = mcf_vcl_byname(name);
134 27615
        if (vp == NULL) {
135 63
                VCLI_SetResult(cli, CLIS_PARAM);
136 63
                VCLI_Out(cli, "No VCL named %s known\n", name);
137 63
        }
138 27615
        return (vp);
139 27615
}
140
141
static int
142 33767
mcf_find_no_vcl(struct cli *cli, const char *name)
143
{
144
145 33767
        if (mcf_invalid_vclname(cli, name))
146 84
                return (0);
147
148 33683
        if (mcf_vcl_byname(name) != NULL) {
149 21
                VCLI_SetResult(cli, CLIS_PARAM);
150 21
                VCLI_Out(cli, "Already a VCL named %s", name);
151 21
                return (0);
152
        }
153 33662
        return (1);
154 33767
}
155
156
int
157 85952
mcf_is_label(const struct vclprog *vp)
158
{
159 85952
        return (vp->state == VCL_STATE_LABEL);
160
}
161
162
/*--------------------------------------------------------------------*/
163
164
struct vcldep *
165 1008
mgt_vcl_dep_add(struct vclprog *vp_from, struct vclprog *vp_to)
166
{
167
        struct vcldep *vd;
168
169 1008
        CHECK_OBJ_NOTNULL(vp_from, VCLPROG_MAGIC);
170 1008
        CHECK_OBJ_NOTNULL(vp_to, VCLPROG_MAGIC);
171 1008
        assert(vp_to->state != VCL_STATE_COLD);
172
173 1008
        ALLOC_OBJ(vd, VCLDEP_MAGIC);
174 1008
        AN(vd);
175
176 1008
        mgt_vcl_set_cooldown(vp_from, -1);
177 1008
        mgt_vcl_set_cooldown(vp_to, -1);
178
179 1008
        vd->from = vp_from;
180 1008
        VTAILQ_INSERT_TAIL(&vp_from->dfrom, vd, lfrom);
181 1008
        vd->to = vp_to;
182 1008
        VTAILQ_INSERT_TAIL(&vp_to->dto, vd, lto);
183 1008
        vp_to->nto++;
184 1008
        return (vd);
185
}
186
187
static void
188 1008
mgt_vcl_dep_del(struct vcldep *vd)
189
{
190
191 1008
        CHECK_OBJ_NOTNULL(vd, VCLDEP_MAGIC);
192 1008
        VTAILQ_REMOVE(&vd->from->dfrom, vd, lfrom);
193 1008
        VTAILQ_REMOVE(&vd->to->dto, vd, lto);
194 1008
        vd->to->nto--;
195 1008
        if (vd->to->nto == 0)
196 819
                mgt_vcl_set_cooldown(vd->to, VTIM_mono());
197 1008
        FREE_OBJ(vd);
198 1008
}
199
200
/*--------------------------------------------------------------------*/
201
202
static struct vclprog *
203 34712
mgt_vcl_add(const char *name, const struct vclstate *state)
204
{
205
        struct vclprog *vp;
206
207 34712
        assert(state == VCL_STATE_WARM ||
208
               state == VCL_STATE_COLD ||
209
               state == VCL_STATE_AUTO ||
210
               state == VCL_STATE_LABEL);
211 34712
        ALLOC_OBJ(vp, VCLPROG_MAGIC);
212 34712
        XXXAN(vp);
213 34712
        REPLACE(vp->name, name);
214 34712
        VTAILQ_INIT(&vp->dfrom);
215 34712
        VTAILQ_INIT(&vp->dto);
216 34712
        VTAILQ_INIT(&vp->vmods);
217 34712
        vp->state = state;
218
219 34712
        if (vp->state != VCL_STATE_COLD)
220 34670
                vp->warm = 1;
221
222 34712
        VTAILQ_INSERT_TAIL(&vclhead, vp, list);
223 34712
        if (vp->state != VCL_STATE_LABEL)
224 34208
                vcl_count++;
225 34712
        return (vp);
226
}
227
228
static void
229 34712
mgt_vcl_del(struct vclprog *vp)
230
{
231
        char *p;
232
        struct vmoddep *vd;
233
        struct vmodfile *vf;
234
        struct vcldep *dep;
235
236 34712
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
237 34712
        assert(VTAILQ_EMPTY(&vp->dto));
238
239 34712
        mgt_vcl_symtab_clean(vp);
240
241 35657
        while ((dep = VTAILQ_FIRST(&vp->dfrom)) != NULL) {
242 945
                assert(dep->from == vp);
243 945
                mgt_vcl_dep_del(dep);
244
        }
245
246 34712
        VTAILQ_REMOVE(&vclhead, vp, list);
247 34712
        if (vp->state != VCL_STATE_LABEL)
248 34208
                vcl_count--;
249 34712
        if (vp->fname != NULL) {
250 27489
                if (!MGT_DO_DEBUG(DBG_VCL_KEEP))
251 27405
                        AZ(unlink(vp->fname));
252 27489
                p = strrchr(vp->fname, '/');
253 27489
                AN(p);
254 27489
                *p = '\0';
255 27489
                VJ_master(JAIL_MASTER_FILE);
256
                /*
257
                 * This will fail if any files are dropped next to the library
258
                 * without us knowing.  This happens for instance with GCOV.
259
                 * Assume developers know how to clean up after themselves
260
                 * (or alternatively:  How to run out of disk space).
261
                 */
262 27489
                (void)rmdir(vp->fname);
263 27489
                VJ_master(JAIL_MASTER_LOW);
264 27489
                free(vp->fname);
265 27489
        }
266 46031
        while (!VTAILQ_EMPTY(&vp->vmods)) {
267 11319
                vd = VTAILQ_FIRST(&vp->vmods);
268 11319
                CHECK_OBJ(vd, VMODDEP_MAGIC);
269 11319
                vf = vd->to;
270 11319
                CHECK_OBJ(vf, VMODFILE_MAGIC);
271 11319
                VTAILQ_REMOVE(&vp->vmods, vd, lfrom);
272 11319
                VTAILQ_REMOVE(&vf->vcls, vd, lto);
273 11319
                FREE_OBJ(vd);
274
275 11319
                if (VTAILQ_EMPTY(&vf->vcls)) {
276 8463
                        if (!MGT_DO_DEBUG(DBG_VMOD_SO_KEEP))
277 8442
                                AZ(unlink(vf->fname));
278 8463
                        VTAILQ_REMOVE(&vmodhead, vf, list);
279 8463
                        free(vf->fname);
280 8463
                        FREE_OBJ(vf);
281 8463
                }
282
        }
283 34712
        free(vp->name);
284 34712
        FREE_OBJ(vp);
285 34712
}
286
287
const char *
288 42755
mgt_has_vcl(void)
289
{
290 42755
        if (VTAILQ_EMPTY(&vclhead))
291 21062
                return ("No VCL loaded");
292 21693
        if (mgt_vcl_active == NULL)
293 21
                return ("No active VCL");
294 21672
        CHECK_OBJ(mgt_vcl_active, VCLPROG_MAGIC);
295 21672
        AN(mgt_vcl_active->warm);
296 21672
        return (NULL);
297 42755
}
298
299
/*
300
 * go_cold
301
 *
302
 * -1: leave alone
303
 *  0: timer not started - not currently used
304
 * >0: when timer started
305
 */
306
static void
307 40632
mgt_vcl_set_cooldown(struct vclprog *vp, vtim_mono now)
308
{
309 40632
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
310
311 51165
        if (vp == mgt_vcl_active ||
312 13683
            vp->state != VCL_STATE_AUTO ||
313 11184
            vp->warm == 0 ||
314 10953
            !VTAILQ_EMPTY(&vp->dto) ||
315 10533
            !VTAILQ_EMPTY(&vp->dfrom))
316 30645
                vp->go_cold = -1;
317
        else
318 9987
                vp->go_cold = now;
319 40632
}
320
321
static int
322 27531
mgt_vcl_settemp(struct cli *cli, struct vclprog *vp, unsigned warm)
323
{
324
        int i;
325
326 27531
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
327
328 27531
        if (warm == vp->warm)
329 27363
                return (0);
330
331 168
        if (vp->state == VCL_STATE_AUTO || vp->state == VCL_STATE_LABEL) {
332 168
                mgt_vcl_set_cooldown(vp, -1);
333 168
                i = mgt_vcl_askchild(cli, vp, warm);
334 168
                mgt_vcl_set_cooldown(vp, VTIM_mono());
335 168
        } else {
336 0
                i = mgt_vcl_setstate(cli, vp,
337 0
                    warm ? VCL_STATE_WARM : VCL_STATE_COLD);
338
        }
339
340 168
        return (i);
341 27531
}
342
343
static int
344 27489
mgt_vcl_requirewarm(struct cli *cli, struct vclprog *vp)
345
{
346 27489
        if (vp->state == VCL_STATE_COLD) {
347 42
                VCLI_SetResult(cli, CLIS_CANT);
348 84
                VCLI_Out(cli, "VCL '%s' is cold - set to auto or warm first",
349 42
                    vp->name);
350 42
                return (1);
351
        }
352 27447
        return (mgt_vcl_settemp(cli, vp, 1));
353 27489
}
354
355
static int
356 1344
mgt_vcl_askchild(struct cli *cli, struct vclprog *vp, unsigned warm)
357
{
358
        unsigned status;
359
        char *p;
360
        int i;
361
362 1344
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
363
364 1344
        if (!MCH_Running()) {
365 105
                vp->warm = warm;
366 105
                return (0);
367
        }
368
369 1239
        i = mgt_cli_askchild(&status, &p, "vcl.state %s %d%s\n",
370 1239
            vp->name, warm, vp->state->name);
371 1239
        if (i && cli != NULL) {
372 105
                VCLI_SetResult(cli, status);
373 105
                VCLI_Out(cli, "%s", p);
374 1239
        } else if (i) {
375 0
                MGT_Complain(C_ERR,
376
                    "Please file ticket: VCL poker problem: "
377
                    "'vcl.state %s %d%s' -> %03d '%s'",
378 0
                    vp->name, warm, vp->state->name, i, p);
379 0
        } else {
380
                /* Success, update mgt's VCL state to reflect child's
381
                   state */
382 1134
                vp->warm = warm;
383
        }
384
385 1239
        free(p);
386 1239
        return (i);
387 1344
}
388
389
static int
390 1176
mgt_vcl_setstate(struct cli *cli, struct vclprog *vp, const struct vclstate *vs)
391
{
392
        unsigned warm;
393
        int i;
394
        const struct vclstate *os;
395
396 1176
        CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC);
397
398 1176
        assert(vs != VCL_STATE_LABEL);
399
400 1176
        if (mcf_is_label(vp)) {
401 0
                AN(vp->warm);
402
                /* do not touch labels */
403 0
                return (0);
404
        }
405
406 1176
        if (vp->state == vs)
407 0
                return (0);
408
409 1176
        os = vp->state;
410 1176
        vp->state = vs;
411
412 1176
        if (vp == mgt_vcl_active) {
413 42
                assert (vs == VCL_STATE_WARM || vs == VCL_STATE_AUTO);
414 42
                AN(vp->warm);
415 42
                warm = 1;
416 1176
        } else if (vs == VCL_STATE_AUTO) {
417 63
                warm = vp->warm;
418 63
        } else {
419 1071
                warm = (vs == VCL_STATE_WARM ? 1 : 0);
420
        }
421
422 1176
        i = mgt_vcl_askchild(cli, vp, warm);
423 1176
        if (i == 0)
424 1092
                mgt_vcl_set_cooldown(vp, VTIM_mono());
425
        else
426 84
                vp->state = os;
427 1176
        return (i);
428 1176
}
429
430
/*--------------------------------------------------------------------*/
431
432
static struct vclprog *
433 34229
mgt_new_vcl(struct cli *cli, const char *vclname, const char *vclsrc,
434
    const char *vclsrcfile, const char *state, int C_flag)
435
{
436
        unsigned status;
437
        char *lib, *p;
438
        struct vclprog *vp;
439
        const struct vclstate *vs;
440
441 34229
        AN(cli);
442
443 34229
        if (vcl_count >= mgt_param.max_vcl &&
444 42
            mgt_param.max_vcl_handling == 2) {
445 21
                VCLI_Out(cli, "Too many (%d) VCLs already loaded\n", vcl_count);
446 21
                VCLI_Out(cli, "(See max_vcl and max_vcl_handling parameters)");
447 21
                VCLI_SetResult(cli, CLIS_CANT);
448 21
                return (NULL);
449
        }
450
451 34208
        if (state == NULL)
452 34082
                vs = VCL_STATE_AUTO;
453
        else
454 126
                vs = mcf_vcl_parse_state(cli, state);
455
456 34208
        if (vs == NULL)
457 0
                return (NULL);
458
459 34208
        vp = mgt_vcl_add(vclname, vs);
460 34208
        lib = mgt_VccCompile(cli, vp, vclname, vclsrc, vclsrcfile, C_flag);
461 34208
        if (lib == NULL) {
462 6719
                mgt_vcl_del(vp);
463 6719
                return (NULL);
464
        }
465
466 27489
        AZ(C_flag);
467 27489
        vp->fname = lib;
468
469 27510
        if ((cli->result == CLIS_OK || cli->result == CLIS_TRUNCATED) &&
470 27489
            vcl_count > mgt_param.max_vcl &&
471 21
            mgt_param.max_vcl_handling == 1) {
472 21
                VCLI_Out(cli, "%d VCLs loaded\n", vcl_count);
473 21
                VCLI_Out(cli, "Remember to vcl.discard the old/unused VCLs.\n");
474 21
                VCLI_Out(cli, "(See max_vcl and max_vcl_handling parameters)");
475 21
        }
476
477 27489
        if (!MCH_Running())
478 22281
                return (vp);
479
480 5208
        if (mgt_cli_askchild(&status, &p, "vcl.load %s %s %d%s\n",
481 5208
            vp->name, vp->fname, vp->warm, vp->state->name)) {
482 924
                mgt_vcl_del(vp);
483 924
                VCLI_Out(cli, "%s", p);
484 924
                VCLI_SetResult(cli, status);
485 924
                free(p);
486 924
                return (NULL);
487
        }
488 4284
        free(p);
489
490 4284
        mgt_vcl_set_cooldown(vp, VTIM_mono());
491 4284
        return (vp);
492 34229
}
493
494
/*--------------------------------------------------------------------*/
495
496
void
497 567
mgt_vcl_startup(struct cli *cli, const char *vclsrc, const char *vclname,
498
    const char *origin, int C_flag)
499
{
500
        char buf[20];
501
        static int n = 0;
502
        struct vclprog *vp;
503
504 567
        AZ(MCH_Running());
505
506 567
        AN(vclsrc);
507 567
        AN(origin);
508 567
        if (vclname == NULL) {
509 42
                bprintf(buf, "boot%d", n++);
510 42
                vclname = buf;
511 42
        }
512 567
        vp = mgt_new_vcl(cli, vclname, vclsrc, origin, NULL, C_flag);
513 567
        if (vp != NULL) {
514
                /* Last startup VCL becomes the automatically selected
515
                 * active VCL. */
516 420
                AN(vp->warm);
517 420
                mgt_vcl_active = vp;
518 420
        }
519 567
}
520
521
/*--------------------------------------------------------------------*/
522
523
int
524 21252
mgt_push_vcls(struct cli *cli, unsigned *status, char **p)
525
{
526
        struct vclprog *vp;
527
        struct vcldep *vd;
528
        int done;
529
530 21252
        AN(mgt_vcl_active);
531
532
        /* The VCL has not been loaded yet, it cannot fail */
533 21252
        (void)cli;
534
535 43869
        VTAILQ_FOREACH(vp, &vclhead, list)
536 22617
                vp->loaded = 0;
537
538 21252
        do {
539 21252
                done = 1;
540 43827
                VTAILQ_FOREACH(vp, &vclhead, list) {
541 22596
                        if (vp->loaded)
542 0
                                continue;
543 22869
                        VTAILQ_FOREACH(vd, &vp->dfrom, lfrom)
544 273
                                if (!vd->to->loaded)
545 0
                                        break;
546 22596
                        if (vd != NULL) {
547 0
                                done = 0;
548 0
                                continue;
549
                        }
550 22596
                        if (mcf_is_label(vp)) {
551 168
                                vd = VTAILQ_FIRST(&vp->dfrom);
552 168
                                AN(vd);
553 336
                                if (mgt_cli_askchild(status, p,
554
                                    "vcl.label %s %s\n",
555 168
                                    vp->name, vd->to->name))
556 0
                                        return (1);
557 168
                        } else {
558 44856
                                if (mgt_cli_askchild(status, p,
559
                                    "vcl.load \"%s\" %s %d%s\n",
560 22428
                                    vp->name, vp->fname, vp->warm,
561 22428
                                    vp->state->name))
562 21
                                        return (1);
563
                        }
564 22575
                        vp->loaded = 1;
565 22575
                        free(*p);
566 22575
                        *p = NULL;
567 22575
                }
568 21231
        } while (!done);
569
570 42462
        if (mgt_cli_askchild(status, p, "vcl.use \"%s\"\n",
571 21231
              mgt_vcl_active->name)) {
572 0
                return (1);
573
        }
574 21231
        free(*p);
575 21231
        *p = NULL;
576 21231
        return (0);
577 21252
}
578
579
/*--------------------------------------------------------------------*/
580
581
static void v_matchproto_(cli_func_t)
582 33431
mcf_vcl_inline(struct cli *cli, const char * const *av, void *priv)
583
{
584
        struct vclprog *vp;
585
586 33431
        (void)priv;
587
588 33431
        if (!mcf_find_no_vcl(cli, av[2]))
589 84
                return;
590
591 33347
        vp = mgt_new_vcl(cli, av[2], av[3], "<vcl.inline>", av[4], 0);
592 33347
        if (vp != NULL && !MCH_Running())
593 21777
                VCLI_Out(cli, "VCL compiled.\n");
594 33431
}
595
596
static void v_matchproto_(cli_func_t)
597 336
mcf_vcl_load(struct cli *cli, const char * const *av, void *priv)
598
{
599
        struct vclprog *vp;
600
601 336
        (void)priv;
602 336
        if (!mcf_find_no_vcl(cli, av[2]))
603 21
                return;
604
605 315
        vp = mgt_new_vcl(cli, av[2], NULL, av[3], av[4], 0);
606 315
        if (vp != NULL && !MCH_Running())
607 84
                VCLI_Out(cli, "VCL compiled.\n");
608 336
}
609
610
611
static void v_matchproto_(cli_func_t)
612 462
mcf_vcl_state(struct cli *cli, const char * const *av, void *priv)
613
{
614
        const struct vclstate *state;
615
        struct vclprog *vp;
616
617 462
        (void)priv;
618 462
        vp = mcf_find_vcl(cli, av[2]);
619 462
        if (vp == NULL)
620 0
                return;
621
622 462
        if (mcf_is_label(vp)) {
623 21
                VCLI_Out(cli, "Labels are always warm");
624 21
                VCLI_SetResult(cli, CLIS_PARAM);
625 21
                return;
626
        }
627
628 441
        state = mcf_vcl_parse_state(cli, av[3]);
629 441
        if (state == NULL)
630 0
                return;
631
632 441
        if (state == VCL_STATE_COLD) {
633 231
                if (!VTAILQ_EMPTY(&vp->dto)) {
634 21
                        assert(vp->state != VCL_STATE_COLD);
635 21
                        VCLI_Out(cli, "A labeled VCL cannot be set cold");
636 21
                        VCLI_SetResult(cli, CLIS_CANT);
637 21
                        return;
638
                }
639 210
                if (vp == mgt_vcl_active) {
640 21
                        VCLI_Out(cli, "Cannot set the active VCL cold.");
641 21
                        VCLI_SetResult(cli, CLIS_CANT);
642 21
                        return;
643
                }
644 189
        }
645
646 399
        (void)mgt_vcl_setstate(cli, vp, state);
647 462
}
648
649
static void v_matchproto_(cli_func_t)
650 26397
mcf_vcl_use(struct cli *cli, const char * const *av, void *priv)
651
{
652
        unsigned status;
653 26397
        char *p = NULL;
654
        struct vclprog *vp, *vp2;
655
        vtim_mono now;
656
657 26397
        (void)priv;
658 26397
        vp = mcf_find_vcl(cli, av[2]);
659 26397
        if (vp == NULL)
660 21
                return;
661 26376
        if (vp == mgt_vcl_active)
662 126
                return;
663
664 26250
        if (mgt_vcl_requirewarm(cli, vp))
665 21
                return;
666
667 26229
        if (MCH_Running() &&
668 4389
            mgt_cli_askchild(&status, &p, "vcl.use %s\n", av[2])) {
669 0
                VCLI_SetResult(cli, status);
670 0
                VCLI_Out(cli, "%s", p);
671 0
        } else {
672 26229
                VCLI_Out(cli, "VCL '%s' now active", av[2]);
673 26229
                vp2 = mgt_vcl_active;
674 26229
                mgt_vcl_active = vp;
675 26229
                now = VTIM_mono();
676 26229
                mgt_vcl_set_cooldown(vp, now);
677 26229
                if (vp2 != NULL)
678 5586
                        mgt_vcl_set_cooldown(vp2, now);
679
        }
680 26229
        free(p);
681 26397
}
682
683
static void
684 882
mgt_vcl_discard(struct cli *cli, struct vclprog *vp)
685
{
686 882
        char *p = NULL;
687
        unsigned status;
688
689 882
        AN(vp);
690 882
        AN(vp->discard);
691 882
        assert(vp != mgt_vcl_active);
692
693 1050
        while (!VTAILQ_EMPTY(&vp->dto))
694 168
                mgt_vcl_discard(cli, VTAILQ_FIRST(&vp->dto)->from);
695
696 882
        if (mcf_is_label(vp)) {
697 105
                AN(vp->warm);
698 105
                vp->warm = 0;
699 882
        } else if (mgt_vcl_setstate(cli, vp, VCL_STATE_COLD)) {
700
                /*
701
                 * Cooling cannot fail in the child, VCL_Poll() asserts as
702
                 * much, so this is a CLI failure and MCH_Cli_Fail() has
703
                 * signalled the child. The CLI being unusable is what matters
704
                 * here: nothing can be told to the child, so finish the
705
                 * discard as with none running.
706
                 */
707 21
                MGT_Complain(C_ERR,
708
                    "VCL %s discarded after the child failed to cool it",
709 21
                    vp->name);
710 21
                VTAILQ_REMOVE(&discardhead, vp, discard_list);
711 21
                mgt_vcl_del(vp);
712 21
                return;
713
        }
714 861
        if (MCH_Running()) {
715 819
                AZ(vp->warm);
716 819
                if (mgt_cli_askchild(&status, &p, "vcl.discard %s\n", vp->name))
717 0
                        assert(status == CLIS_OK || status == CLIS_COMMS);
718 819
                free(p);
719 819
        }
720 861
        VTAILQ_REMOVE(&discardhead, vp, discard_list);
721 861
        mgt_vcl_del(vp);
722 882
}
723
724
static int
725 1092
mgt_vcl_discard_mark(struct cli *cli, const char *glob)
726
{
727
        struct vclprog *vp;
728 1092
        unsigned marked = 0;
729
730 4368
        VTAILQ_FOREACH(vp, &vclhead, list) {
731 3423
                if (fnmatch(glob, vp->name, 0))
732 1995
                        continue;
733 1428
                if (vp == mgt_vcl_active) {
734 147
                        VCLI_SetResult(cli, CLIS_CANT);
735 294
                        VCLI_Out(cli, "Cannot discard active VCL program %s\n",
736 147
                            vp->name);
737 147
                        return (-1);
738
                }
739 1281
                if (!vp->discard)
740 1281
                        VTAILQ_INSERT_TAIL(&discardhead, vp, discard_list);
741 1281
                vp->discard = 1;
742 1281
                marked++;
743 1281
        }
744
745 945
        if (marked == 0) {
746 126
                VCLI_SetResult(cli, CLIS_PARAM);
747 126
                VCLI_Out(cli, "No VCL name matches %s\n", glob);
748 126
        }
749
750 945
        return (marked);
751 1092
}
752
753
static void
754 84
mgt_vcl_discard_depfail(struct cli *cli, struct vclprog *vp)
755
{
756
        struct vcldep *vd;
757
        int n;
758
759 84
        AN(cli);
760 84
        AN(vp);
761 84
        assert(!VTAILQ_EMPTY(&vp->dto));
762
763 84
        VCLI_SetResult(cli, CLIS_CANT);
764 84
        AN(vp->warm);
765 84
        if (!mcf_is_label(vp))
766 126
                VCLI_Out(cli, "Cannot discard labeled VCL program %s:\n",
767 63
                    vp->name);
768
        else
769 42
                VCLI_Out(cli,
770
                    "Cannot discard VCL label %s, other VCLs depend on it:\n",
771 21
                    vp->name);
772 84
        n = 0;
773 273
        VTAILQ_FOREACH(vd, &vp->dto, lto) {
774 189
                if (n++ == 5) {
775 0
                        VCLI_Out(cli, "\t[...]");
776 0
                        break;
777
                }
778 189
                VCLI_Out(cli, "\t%s\n", vd->from->name);
779 189
        }
780 84
}
781
782
static int
783 1050
mgt_vcl_discard_depcheck(struct cli *cli)
784
{
785
        struct vclprog *vp;
786
        struct vcldep *vd;
787
788 1932
        VTAILQ_FOREACH(vp, &discardhead, discard_list) {
789 1134
                VTAILQ_FOREACH(vd, &vp->dto, lto)
790 252
                        if (!vd->from->discard) {
791 84
                                mgt_vcl_discard_depfail(cli, vp);
792 84
                                return (-1);
793
                        }
794 882
        }
795
796 966
        return (0);
797 1050
}
798
799
static void
800 357
mgt_vcl_discard_clear(void)
801
{
802
        struct vclprog *vp, *vp2;
803
804 756
        VTAILQ_FOREACH_SAFE(vp, &discardhead, discard_list, vp2) {
805 399
                AN(vp->discard);
806 399
                vp->discard = 0;
807 399
                VTAILQ_REMOVE(&discardhead, vp, discard_list);
808 399
        }
809 357
}
810
811
static void v_matchproto_(cli_func_t)
812 1050
mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv)
813
{
814
        const struct vclprog *vp;
815
816 1050
        (void)priv;
817
818 1050
        assert(VTAILQ_EMPTY(&discardhead));
819 4305
        VTAILQ_FOREACH(vp, &vclhead, list)
820 3255
                AZ(vp->discard);
821
822 1869
        for (av += 2; *av != NULL; av++) {
823 1092
                if (mgt_vcl_discard_mark(cli, *av) <= 0) {
824 273
                        mgt_vcl_discard_clear();
825 273
                        break;
826
                }
827 819
        }
828
829 1050
        if (mgt_vcl_discard_depcheck(cli) != 0)
830 84
                mgt_vcl_discard_clear();
831
832 1764
        while (!VTAILQ_EMPTY(&discardhead))
833 714
                mgt_vcl_discard(cli, VTAILQ_FIRST(&discardhead));
834 1050
}
835
836
static void v_matchproto_(cli_func_t)
837 1995
mcf_vcl_list(struct cli *cli, const char * const *av, void *priv)
838
{
839
        unsigned status;
840
        char *p;
841
        struct vclprog *vp;
842
        struct vcldep *vd;
843
        const struct vclstate *vs;
844
        struct vte *vte;
845
846
        /* NB: Shall generate same output as vcl_cli_list() */
847
848 1995
        (void)av;
849 1995
        (void)priv;
850
851 1995
        if (MCH_Running()) {
852 1722
                if (!mgt_cli_askchild(&status, &p, "vcl.list\n")) {
853 1722
                        VCLI_SetResult(cli, status);
854 1722
                        VCLI_Out(cli, "%s", p);
855 1722
                }
856 1722
                free(p);
857 1722
        } else {
858 273
                vte = VTE_new(7, 80);
859 273
                AN(vte);
860
861 1029
                VTAILQ_FOREACH(vp, &vclhead, list) {
862 1512
                        VTE_printf(vte, "%s",
863 756
                            vp == mgt_vcl_active ? "active" : "available");
864 756
                        vs = vp->warm ?  VCL_STATE_WARM : VCL_STATE_COLD;
865 756
                        VTE_printf(vte, "\t%s\t%s", vp->state->name, vs->name);
866 756
                        VTE_printf(vte, "\t-\t%s", vp->name);
867 756
                        if (mcf_is_label(vp)) {
868 147
                                vd = VTAILQ_FIRST(&vp->dfrom);
869 147
                                AN(vd);
870 147
                                VTE_printf(vte, "\t->\t%s", vd->to->name);
871 147
                                if (vp->nto > 0)
872 84
                                        VTE_printf(vte, " (%d return(vcl)%s)",
873 42
                                            vp->nto, vp->nto > 1 ? "'s" : "");
874 756
                        } else if (vp->nto > 0) {
875 210
                                VTE_printf(vte, "\t<-\t(%d label%s)",
876 105
                                    vp->nto, vp->nto > 1 ? "s" : "");
877 105
                        }
878 756
                        VTE_cat(vte, "\n");
879 756
                }
880 273
                AZ(VTE_finish(vte));
881 273
                AZ(VTE_format(vte, VCLI_VTE_format, cli));
882 273
                VTE_destroy(&vte);
883
        }
884 1995
}
885
886
static void v_matchproto_(cli_func_t)
887 168
mcf_vcl_list_json(struct cli *cli, const char * const *av, void *priv)
888
{
889
        unsigned status;
890
        char *p;
891
        struct vclprog *vp;
892
        struct vcldep *vd;
893
        const struct vclstate *vs;
894
895
        /* NB: Shall generate same output as vcl_cli_list() */
896
897 168
        (void)priv;
898 168
        if (MCH_Running()) {
899 105
                if (!mgt_cli_askchild(&status, &p, "vcl.list -j\n")) {
900 105
                        VCLI_SetResult(cli, status);
901 105
                        VCLI_Out(cli, "%s", p);
902 105
                }
903 105
                free(p);
904 105
        } else {
905 63
                VCLI_JSON_begin(cli, 2, av);
906 210
                VTAILQ_FOREACH(vp, &vclhead, list) {
907 147
                        VCLI_Out(cli, ",\n");
908 147
                        VCLI_Out(cli, "{\n");
909 147
                        VSB_indent(cli->sb, 2);
910 294
                        VCLI_Out(cli, "\"status\": \"%s\",\n",
911 147
                            vp == mgt_vcl_active ? "active" : "available");
912 147
                        VCLI_Out(cli, "\"state\": \"%s\",\n", vp->state->name);
913 147
                        vs = vp->warm ?  VCL_STATE_WARM : VCL_STATE_COLD;
914 147
                        VCLI_Out(cli, "\"temperature\": \"%s\",\n", vs->name);
915 147
                        VCLI_Out(cli, "\"name\": \"%s\"", vp->name);
916 147
                        if (mcf_is_label(vp)) {
917 63
                                vd = VTAILQ_FIRST(&vp->dfrom);
918 63
                                AN(vd);
919 63
                                VCLI_Out(cli, ",\n");
920 63
                                VCLI_Out(cli, "\"label\": {\n");
921 63
                                VSB_indent(cli->sb, 2);
922 63
                                VCLI_Out(cli, "\"name\": \"%s\"", vd->to->name);
923 63
                                if (vp->nto > 0)
924 0
                                        VCLI_Out(cli, ",\n\"refs\": %d",
925 0
                                                 vp->nto);
926 63
                                VSB_indent(cli->sb, -2);
927 63
                                VCLI_Out(cli, "\n");
928 63
                                VCLI_Out(cli, "}");
929 147
                        } else if (vp->nto > 0) {
930 21
                                VCLI_Out(cli, ",\n");
931 21
                                VCLI_Out(cli, "\"labels\": %d", vp->nto);
932 21
                        }
933 147
                        VSB_indent(cli->sb, -2);
934 147
                        VCLI_Out(cli, "\n}");
935 147
                }
936 63
                VCLI_JSON_end(cli);
937
        }
938 168
}
939
940
static int
941 294
mcf_vcl_check_dag(struct cli *cli, struct vclprog *tree, struct vclprog *target)
942
{
943
        struct vcldep *vd;
944
945 294
        if (target == tree)
946 63
                return (1);
947 231
        VTAILQ_FOREACH(vd, &tree->dfrom, lfrom) {
948 189
                if (!mcf_vcl_check_dag(cli, vd->to, target))
949 0
                        continue;
950 189
                if (mcf_is_label(tree))
951 126
                        VCLI_Out(cli, "Label %s points to vcl %s\n",
952 63
                            tree->name, vd->to->name);
953
                else
954 252
                        VCLI_Out(cli, "Vcl %s uses Label %s\n",
955 126
                            tree->name, vd->to->name);
956 189
                return (1);
957
        }
958 42
        return (0);
959 294
}
960
961
static void v_matchproto_(cli_func_t)
962 798
mcf_vcl_label(struct cli *cli, const char * const *av, void *priv)
963
{
964
        struct vclprog *vpl;
965
        struct vclprog *vpt;
966
        unsigned status;
967
        char *p;
968
        int i;
969
970 798
        (void)priv;
971 798
        if (mcf_invalid_vclname(cli, av[2]))
972 21
                return;
973 777
        vpl = mcf_vcl_byname(av[2]);
974 777
        if (vpl != NULL && !mcf_is_label(vpl)) {
975 21
                VCLI_SetResult(cli, CLIS_PARAM);
976 21
                VCLI_Out(cli, "%s is not a label", vpl->name);
977 21
                return;
978
        }
979 756
        vpt = mcf_find_vcl(cli, av[3]);
980 756
        if (vpt == NULL)
981 42
                return;
982 714
        if (mcf_is_label(vpt)) {
983 42
                VCLI_SetResult(cli, CLIS_CANT);
984 42
                VCLI_Out(cli, "VCL labels cannot point to labels");
985 42
                return;
986
        }
987 672
        if (vpl != NULL) {
988 126
                if (VTAILQ_FIRST(&vpl->dfrom)->to != vpt &&
989 105
                    mcf_vcl_check_dag(cli, vpt, vpl)) {
990 126
                        VCLI_Out(cli,
991
                            "Pointing label %s at %s would create a loop",
992 63
                            vpl->name, vpt->name);
993 63
                        VCLI_SetResult(cli, CLIS_PARAM);
994 63
                        return;
995
                }
996 63
        }
997
998 609
        if (mgt_vcl_requirewarm(cli, vpt))
999 42
                return;
1000
1001 567
        if (vpl != NULL) {
1002
                /* potentially fail before we delete the dependency */
1003 63
                if (mgt_vcl_requirewarm(cli, vpl))
1004 0
                        return;
1005 63
                mgt_vcl_dep_del(VTAILQ_FIRST(&vpl->dfrom));
1006 63
                AN(VTAILQ_EMPTY(&vpl->dfrom));
1007 63
        } else {
1008 504
                vpl = mgt_vcl_add(av[2], VCL_STATE_LABEL);
1009
        }
1010
1011 567
        AN(vpl);
1012 567
        if (mgt_vcl_requirewarm(cli, vpl))
1013 0
                return;
1014 567
        (void)mgt_vcl_dep_add(vpl, vpt);
1015
1016 567
        if (!MCH_Running())
1017 168
                return;
1018
1019 399
        i = mgt_cli_askchild(&status, &p, "vcl.label %s %s\n", av[2], av[3]);
1020 399
        if (i) {
1021 0
                VCLI_SetResult(cli, status);
1022 0
                VCLI_Out(cli, "%s", p);
1023 0
        }
1024 399
        free(p);
1025 798
}
1026
1027
static void v_matchproto_(cli_func_t)
1028 21
mcf_vcl_deps(struct cli *cli, const char * const *av, void *priv)
1029
{
1030
        struct vclprog *vp;
1031
        struct vcldep *vd;
1032
        struct vte *vte;
1033
1034 21
        (void)av;
1035 21
        (void)priv;
1036
1037 21
        vte = VTE_new(2, 80);
1038 21
        AN(vte);
1039
1040 252
        VTAILQ_FOREACH(vp, &vclhead, list) {
1041 231
                if (VTAILQ_EMPTY(&vp->dfrom)) {
1042 63
                        VTE_printf(vte, "%s\n", vp->name);
1043 63
                        continue;
1044
                }
1045 336
                VTAILQ_FOREACH(vd, &vp->dfrom, lfrom)
1046 168
                        VTE_printf(vte, "%s\t%s\n", vp->name, vd->to->name);
1047 168
        }
1048 21
        AZ(VTE_finish(vte));
1049 21
        AZ(VTE_format(vte, VCLI_VTE_format, cli));
1050 21
        VTE_destroy(&vte);
1051 21
}
1052
1053
static void v_matchproto_(cli_func_t)
1054 0
mcf_vcl_deps_json(struct cli *cli, const char * const *av, void *priv)
1055
{
1056
        struct vclprog *vp;
1057
        struct vcldep *vd;
1058
        const char *sepd, *sepa;
1059
1060 0
        (void)priv;
1061
1062 0
        VCLI_JSON_begin(cli, 1, av);
1063
1064 0
        VTAILQ_FOREACH(vp, &vclhead, list) {
1065 0
                VCLI_Out(cli, ",\n");
1066 0
                VCLI_Out(cli, "{\n");
1067 0
                VSB_indent(cli->sb, 2);
1068 0
                VCLI_Out(cli, "\"name\": \"%s\",\n", vp->name);
1069 0
                VCLI_Out(cli, "\"deps\": [");
1070 0
                VSB_indent(cli->sb, 2);
1071 0
                sepd = "";
1072 0
                sepa = "";
1073 0
                VTAILQ_FOREACH(vd, &vp->dfrom, lfrom) {
1074 0
                        VCLI_Out(cli, "%s\n", sepd);
1075 0
                        VCLI_Out(cli, "\"%s\"", vd->to->name);
1076 0
                        sepd = ",";
1077 0
                        sepa = "\n";
1078 0
                }
1079 0
                VSB_indent(cli->sb, -2);
1080 0
                VCLI_Out(cli, "%s", sepa);
1081 0
                VCLI_Out(cli, "]\n");
1082 0
                VSB_indent(cli->sb, -2);
1083 0
                VCLI_Out(cli, "}");
1084 0
        }
1085
1086 0
        VCLI_JSON_end(cli);
1087 0
}
1088
1089
/*--------------------------------------------------------------------*/
1090
1091
static int v_matchproto_(vev_cb_f)
1092 9648
mgt_vcl_poker(const struct vev *e, int what)
1093
{
1094
        struct vclprog *vp;
1095
        vtim_mono now;
1096
1097 9648
        (void)e;
1098 9648
        (void)what;
1099 9648
        e_poker->timeout = mgt_param.vcl_cooldown * .45;
1100 9648
        now = VTIM_mono();
1101 24989
        VTAILQ_FOREACH(vp, &vclhead, list) {
1102 15341
                if (vp->go_cold == 0)
1103 270
                        mgt_vcl_set_cooldown(vp, now);
1104 15071
                else if (vp->go_cold > 0 &&
1105 2935
                    vp->go_cold + mgt_param.vcl_cooldown < now)
1106 84
                        (void)mgt_vcl_settemp(NULL, vp, 0);
1107 15341
        }
1108 9648
        return (0);
1109
}
1110
1111
/*--------------------------------------------------------------------*/
1112
1113
static struct cli_proto cli_vcl[] = {
1114
        { CLICMD_VCL_LOAD,              "", mcf_vcl_load },
1115
        { CLICMD_VCL_INLINE,            "", mcf_vcl_inline },
1116
        { CLICMD_VCL_USE,               "", mcf_vcl_use },
1117
        { CLICMD_VCL_STATE,             "", mcf_vcl_state },
1118
        { CLICMD_VCL_DISCARD,           "", mcf_vcl_discard },
1119
        { CLICMD_VCL_LIST,              "", mcf_vcl_list, mcf_vcl_list_json },
1120
        { CLICMD_VCL_DEPS,              "", mcf_vcl_deps, mcf_vcl_deps_json },
1121
        { CLICMD_VCL_LABEL,             "", mcf_vcl_label },
1122
        { CLICMD_DEBUG_VCL_SYMTAB,      "d", mcf_vcl_symtab },
1123
        { NULL }
1124
};
1125
1126
/*--------------------------------------------------------------------*/
1127
1128
static void
1129 103696
mgt_vcl_atexit(void)
1130
{
1131
        struct vclprog *vp, *vp2;
1132
1133 103696
        if (getpid() != heritage.mgt_pid)
1134 82004
                return;
1135 21692
        mgt_vcl_active = NULL;
1136 43301
        while (!VTAILQ_EMPTY(&vclhead))
1137 48930
                VTAILQ_FOREACH_SAFE(vp, &vclhead, list, vp2)
1138 53508
                        if (VTAILQ_EMPTY(&vp->dto))
1139 26187
                                mgt_vcl_del(vp);
1140 103696
}
1141
1142
void
1143 21692
mgt_vcl_init(void)
1144
{
1145
1146 21692
        e_poker = VEV_Alloc();
1147 21692
        AN(e_poker);
1148 21692
        e_poker->timeout = 3;           // random, prime
1149
1150 21692
        e_poker->callback = mgt_vcl_poker;
1151 21692
        e_poker->name = "vcl poker";
1152 21692
        AZ(VEV_Start(mgt_evb, e_poker));
1153
1154 21692
        AZ(atexit(mgt_vcl_atexit));
1155
1156 21692
        VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_vcl);
1157 21692
}