varnish-cache/bin/varnishd/cache/cache_vcl.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2016 Varnish Software AS
3
 * 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
 */
31
32
#include "config.h"
33
34
#include <dlfcn.h>
35
#include <fnmatch.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
39
#include "cache_varnishd.h"
40
#include "common/heritage.h"
41
42
#include "vcl.h"
43
44
#include "cache_director.h"
45
#include "cache_vcl.h"
46
#include "vcli_serve.h"
47
#include "vte.h"
48
#include "vtim.h"
49
#include "vcc_interface.h"
50
51
const struct vcltemp VCL_TEMP_INIT[1] = {{ .name = "init", .is_cold = 1 }};
52
const struct vcltemp VCL_TEMP_COLD[1] = {{ .name = "cold", .is_cold = 1 }};
53
const struct vcltemp VCL_TEMP_WARM[1] = {{ .name = "warm", .is_warm = 1 }};
54
const struct vcltemp VCL_TEMP_BUSY[1] = {{ .name = "busy", .is_warm = 1 }};
55
const struct vcltemp VCL_TEMP_COOLING[1] = {{ .name = "cooling" }};
56
57
// not really a temperature
58
static const struct vcltemp VCL_TEMP_LABEL[1] = {{ .name = "label" }};
59
60
/*
61
 * XXX: Presently all modifications to this list happen from the
62
 * CLI event-engine, so no locking is necessary
63
 */
64
static VTAILQ_HEAD(, vcl)       vcl_head =
65
    VTAILQ_HEAD_INITIALIZER(vcl_head);
66
67
struct lock             vcl_mtx;
68
struct vcl              *vcl_active; /* protected by vcl_mtx */
69
70
static struct vrt_ctx ctx_cli;
71
static struct wrk_vpi wrk_vpi_cli;
72
static struct ws ws_cli;
73
static uintptr_t ws_snapshot_cli;
74
static struct vsl_log vsl_cli;
75
76
/*--------------------------------------------------------------------*/
77
78
void
79 30936
VCL_Bo2Ctx(struct vrt_ctx *ctx, struct busyobj *bo)
80
{
81
82 30936
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
83 30936
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
84 30936
        CHECK_OBJ_NOTNULL(bo->wrk, WORKER_MAGIC);
85 30936
        ctx->vcl = bo->vcl;
86 30936
        ctx->syntax = ctx->vcl->conf->syntax;
87 30936
        ctx->vsl = bo->vsl;
88 30936
        ctx->http_bereq = bo->bereq;
89 30936
        ctx->http_beresp = bo->beresp;
90 30936
        ctx->bo = bo;
91 30936
        ctx->sp = bo->sp;
92 30936
        ctx->now = bo->t_prev;
93 30936
        ctx->ws = bo->ws;
94 30936
        ctx->vpi = bo->wrk->vpi;
95 30936
        ctx->vpi->handling = 0;
96 30936
        ctx->vpi->trace = bo->trace;
97 30936
}
98
99
void
100 49984
VCL_Req2Ctx(struct vrt_ctx *ctx, struct req *req)
101
{
102
103 49984
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
104 49984
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
105 49984
        CHECK_OBJ_NOTNULL(req->doclose, STREAM_CLOSE_MAGIC);
106
107 49984
        ctx->vcl = req->vcl;
108 49984
        ctx->syntax = ctx->vcl->conf->syntax;
109 49984
        ctx->vsl = req->vsl;
110 49984
        ctx->http_req = req->http;
111 49984
        CHECK_OBJ_NOTNULL(req->top, REQTOP_MAGIC);
112 49984
        ctx->http_req_top = req->top->topreq->http;
113 49984
        ctx->http_resp = req->resp;
114 49984
        ctx->req = req;
115 49984
        ctx->sp = req->sp;
116 49984
        ctx->now = req->t_prev;
117 49984
        ctx->ws = req->ws;
118 49984
        ctx->vpi = req->wrk->vpi;
119 49984
        ctx->vpi->handling = 0;
120 49984
        ctx->vpi->trace = req->trace;
121 49984
}
122
123
/*--------------------------------------------------------------------*/
124
125
struct vrt_ctx *
126 7651
VCL_Get_CliCtx(int msg)
127
{
128
129 7651
        ASSERT_CLI();
130 7651
        INIT_OBJ(&ctx_cli, VRT_CTX_MAGIC);
131 7651
        INIT_OBJ(&wrk_vpi_cli, WRK_VPI_MAGIC);
132 7651
        ctx_cli.vpi = &wrk_vpi_cli;
133 7651
        wrk_vpi_cli.trace = FEATURE(FEATURE_TRACE);
134 7651
        ctx_cli.now = VTIM_real();
135 7651
        if (msg) {
136 4840
                ctx_cli.msg = VSB_new_auto();
137 4840
                AN(ctx_cli.msg);
138 4840
        } else {
139 2811
                ctx_cli.vsl = &vsl_cli;
140
        }
141 7651
        ctx_cli.ws = &ws_cli;
142 7651
        WS_Assert(ctx_cli.ws);
143 7651
        return (&ctx_cli);
144
}
145
146
/*
147
 * releases CLI ctx
148
 *
149
 * returns finished error msg vsb if VCL_Get_CliCtx(1) was called
150
 *
151
 * caller needs to VSB_destroy a non-NULL return value
152
 *
153
 */
154
struct vsb *
155 7649
VCL_Rel_CliCtx(struct vrt_ctx **ctx)
156
{
157 7649
        struct vsb *r = NULL;
158
159 7649
        ASSERT_CLI();
160 7649
        assert(*ctx == &ctx_cli);
161 7649
        AN((*ctx)->vpi);
162 7649
        if (ctx_cli.msg) {
163 4840
                TAKE_OBJ_NOTNULL(r, &ctx_cli.msg, VSB_MAGIC);
164 4840
                AZ(VSB_finish(r));
165 4840
        }
166 7649
        if (ctx_cli.vsl)
167 2809
                VSL_Flush(ctx_cli.vsl, 0);
168 7649
        WS_Assert(ctx_cli.ws);
169 7649
        WS_Rollback(&ws_cli, ws_snapshot_cli);
170 7649
        INIT_OBJ(*ctx, VRT_CTX_MAGIC);
171 7649
        *ctx = NULL;
172
173 7649
        return (r);
174
}
175
176
/*--------------------------------------------------------------------*/
177
178
/* VRT_fail() can be called
179
 * - from the vcl sub via a vmod
180
 * - via a PRIV_TASK .fini callback
181
 *
182
 * if this happens during init, we fail it
183
 * if during fini, we ignore, because otherwise VMOD authors would be forced to
184
 * handle VCL_MET_FINI specifically everywhere.
185
 */
186
187
static int
188 5089
vcl_event_handling(VRT_CTX)
189
{
190 5089
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
191
192 5089
        if (ctx->vpi->handling == 0)
193 5003
                return (0);
194
195 86
        assert(ctx->vpi->handling == VCL_RET_FAIL);
196
197 86
        if (ctx->method == VCL_MET_INIT)
198 82
                return (1);
199
200
        /*
201
         * EVENT_WARM / EVENT_COLD: method == 0
202
         * must not set handling
203
         */
204 4
        assert(ctx->method == VCL_MET_FINI);
205
206 4
        ctx->vpi->handling = 0;
207 4
        VRT_fail(ctx, "VRT_fail() from vcl_fini{} has no effect");
208 4
        return (0);
209 5089
}
210
211
static int
212 5091
vcl_send_event(struct vcl *vcl, enum vcl_event_e ev, struct vsb **msg)
213
{
214
        int r, havemsg;
215 5091
        unsigned method = 0;
216
        struct vrt_ctx *ctx;
217
218 5091
        ASSERT_CLI();
219 5091
        ASSERT_VCL_ACTIVE();
220
221 5091
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
222 5091
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
223 5091
        AN(msg);
224 5091
        AZ(*msg);
225
226 5091
        switch (ev) {
227
        case VCL_EVENT_LOAD:
228 2460
                method = VCL_MET_INIT;
229
                /* FALLTHROUGH */
230
        case VCL_EVENT_WARM:
231 4840
                havemsg = 1;
232 4840
                break;
233
        case VCL_EVENT_DISCARD:
234 156
                method = VCL_MET_FINI;
235
                /* FALLTHROUGH */
236
        case VCL_EVENT_COLD:
237 251
                havemsg = 0;
238 251
                break;
239
        default:
240 0
                WRONG("vcl_event");
241 0
        }
242
243 5091
        ctx = VCL_Get_CliCtx(havemsg);
244
245 5091
        AN(ctx->vpi);
246 5091
        AZ(ctx->vpi->handling);
247 5091
        AN(ctx->ws);
248
249 5091
        ctx->vcl = vcl;
250 5091
        ctx->syntax = ctx->vcl->conf->syntax;
251 5091
        ctx->method = method;
252
253 5091
        VCL_TaskEnter(cli_task_privs);
254 5091
        r = ctx->vcl->conf->event_vcl(ctx, ev);
255 5091
        VCL_TaskLeave(ctx, cli_task_privs);
256 5091
        r |= vcl_event_handling(ctx);
257
258 5091
        *msg = VCL_Rel_CliCtx(&ctx);
259
260 5091
        if (r && (ev == VCL_EVENT_COLD || ev == VCL_EVENT_DISCARD))
261 0
                WRONG("A VMOD cannot fail COLD or DISCARD events");
262
263 5091
        return (r);
264
}
265
266
/*--------------------------------------------------------------------*/
267
268
struct vcl *
269 5110
vcl_find(const char *name)
270
{
271
        struct vcl *vcl;
272
273 5110
        ASSERT_CLI();
274 7723
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
275 5211
                if (vcl->discard)
276 1
                        continue;
277 5210
                if (!strcmp(vcl->loaded_name, name))
278 2598
                        return (vcl);
279 2612
        }
280 2512
        return (NULL);
281 5110
}
282
283
/*--------------------------------------------------------------------*/
284
285
static void
286 14
vcl_panic_conf(struct vsb *vsb, const struct VCL_conf *conf)
287
{
288
        unsigned u;
289
        const struct vpi_ii *ii;
290
291 14
        if (PAN_dump_struct(vsb, conf, VCL_CONF_MAGIC, "conf"))
292 0
                return;
293 14
        VSB_printf(vsb, "syntax = \"%u\",\n", conf->syntax);
294 14
        VSB_cat(vsb, "srcname = {\n");
295 14
        VSB_indent(vsb, 2);
296 42
        for (u = 0; u < conf->nsrc; ++u)
297 28
                VSB_printf(vsb, "[%u] = \"%s\",\n", u, conf->srcname[u]);
298 14
        VSB_indent(vsb, -2);
299 14
        VSB_cat(vsb, "},\n");
300 14
        VSB_cat(vsb, "instances = {\n");
301 14
        VSB_indent(vsb, 2);
302 14
        ii = conf->instance_info;
303 22
        while (ii != NULL && ii->p != NULL) {
304 16
                VSB_printf(vsb, "\"%s\" = %p,\n", ii->name,
305 8
                    (const void *)*(const uintptr_t *)ii->p);
306 8
                ii++;
307
        }
308 14
        VSB_indent(vsb, -2);
309 14
        VSB_cat(vsb, "},\n");
310 14
        VSB_indent(vsb, -2);
311 14
        VSB_cat(vsb, "},\n");
312 14
}
313
314
void
315 26
VCL_Panic(struct vsb *vsb, const char *nm, const struct vcl *vcl)
316
{
317
318 26
        AN(vsb);
319 26
        if (PAN_dump_struct(vsb, vcl, VCL_MAGIC, "vcl[%s]", nm))
320 12
                return;
321 14
        VSB_printf(vsb, "name = \"%s\",\n", vcl->loaded_name);
322 14
        VSB_printf(vsb, "busy = %u,\n", vcl->busy);
323 14
        VSB_printf(vsb, "discard = %u,\n", vcl->discard);
324 14
        VSB_printf(vsb, "state = %s,\n", vcl->state);
325 14
        VSB_printf(vsb, "temp = %s,\n", vcl->temp ? vcl->temp->name : "(null)");
326 14
        vcl_panic_conf(vsb, vcl->conf);
327 14
        VSB_indent(vsb, -2);
328 14
        VSB_cat(vsb, "},\n");
329 26
}
330
331
/*--------------------------------------------------------------------*/
332
333
void
334 2892
VCL_Update(struct vcl **vcc, struct vcl *vcl)
335
{
336
        struct vcl *old;
337
338 2892
        AN(vcc);
339
340 2892
        old = *vcc;
341 2892
        *vcc = NULL;
342
343 2892
        CHECK_OBJ_ORNULL(old, VCL_MAGIC);
344 2892
        ASSERT_VCL_ACTIVE();
345
346 2892
        Lck_Lock(&vcl_mtx);
347 2892
        if (old != NULL) {
348 151
                assert(old->busy > 0);
349 151
                old->busy--;
350 151
        }
351
352 2892
        if (vcl == NULL)
353 2866
                vcl = vcl_active; /* Sample vcl_active under lock to avoid
354
                                   * race */
355 2892
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
356 2892
        if (vcl->label == NULL) {
357 2860
                AN(strcmp(vcl->state, VCL_TEMP_LABEL->name));
358 2860
                *vcc = vcl;
359 2860
        } else {
360 32
                AZ(strcmp(vcl->state, VCL_TEMP_LABEL->name));
361 32
                *vcc = vcl->label;
362
        }
363 2892
        CHECK_OBJ_NOTNULL(*vcc, VCL_MAGIC);
364 2892
        AZ((*vcc)->discard);
365 2892
        (*vcc)->busy++;
366 2892
        Lck_Unlock(&vcl_mtx);
367 2892
        assert((*vcc)->temp->is_warm);
368 2892
}
369
370
/*--------------------------------------------------------------------
371
 * vdire: Vcl DIrector REsignation Management (born out of a dire situation)
372
 * iterators over the director list need to register.
373
 * while iterating, directors can not retire immediately,
374
 * they get put on a list of resigning directors. The
375
 * last iterator executes the retirement.
376
 */
377
378
static struct vdire *
379 4928
vdire_new(struct lock *mtx, const struct vcltemp **tempp)
380
{
381
        struct vdire *vdire;
382
383 4928
        ALLOC_OBJ(vdire, VDIRE_MAGIC);
384 4928
        AN(vdire);
385 4928
        AN(mtx);
386 4928
        VTAILQ_INIT(&vdire->directors);
387 4928
        VTAILQ_INIT(&vdire->resigning);
388 4928
        vdire->mtx = mtx;
389 4928
        PTOK(pthread_cond_init(&vdire->cond, NULL));
390 4928
        vdire->tempp = tempp;
391 4928
        return (vdire);
392
}
393
394
/* starting an interation prevents removals from the directors list */
395
void
396 2092
vdire_start_iter(struct vdire *vdire)
397
{
398
399 2092
        CHECK_OBJ_NOTNULL(vdire, VDIRE_MAGIC);
400
401
        // https://github.com/varnishcache/varnish-cache/pull/4142#issuecomment-2593091097
402 2092
        ASSERT_CLI();
403
404 2092
        Lck_Lock(vdire->mtx);
405 2092
        while (! VTAILQ_EMPTY(&vdire->resigning))
406 0
                (void)Lck_CondWait(&vdire->cond, vdire->mtx);
407 2092
        vdire->iterating++;
408 2092
        Lck_Unlock(vdire->mtx);
409 2092
}
410
411
void
412 4561
vdire_end_iter(struct vdire *vdire)
413
{
414 4561
        struct vcldir_head resigning = VTAILQ_HEAD_INITIALIZER(resigning);
415 4561
        const struct vcltemp *temp = NULL;
416
        struct vcldir *vdir, *next;
417
        unsigned n;
418
419 4561
        CHECK_OBJ_NOTNULL(vdire, VDIRE_MAGIC);
420
421 4561
        Lck_Lock(vdire->mtx);
422 4561
        AN(vdire->iterating);
423 4561
        n = --vdire->iterating;
424
425 4561
        if (n == 0) {
426 4561
                VTAILQ_SWAP(&vdire->resigning, &resigning, vcldir, resigning_list);
427 4561
                VTAILQ_FOREACH(vdir, &resigning, resigning_list)
428 0
                        VTAILQ_REMOVE(&vdire->directors, vdir, directors_list);
429 4561
                temp = *vdire->tempp;
430 4561
                PTOK(pthread_cond_broadcast(&vdire->cond));
431 4561
        }
432 4561
        Lck_Unlock(vdire->mtx);
433
434 4561
        VTAILQ_FOREACH_SAFE(vdir, &resigning, resigning_list, next)
435 0
                vcldir_retire(vdir, temp);
436 4561
}
437
438
/*
439
 * like vdire_start_iter, but also prepare for backend_event_*()
440
 * by setting the checkpoint
441
 */
442
static void
443 2469
vdire_start_event(struct vdire *vdire, const struct vcltemp *temp)
444
{
445
446 2469
        CHECK_OBJ_NOTNULL(vdire, VDIRE_MAGIC);
447 2469
        AN(temp);
448
449 2469
        Lck_AssertHeld(vdire->mtx);
450
451
        // https://github.com/varnishcache/varnish-cache/pull/4142#issuecomment-2593091097
452 2469
        ASSERT_CLI();
453
454 2469
        AZ(vdire->checkpoint);
455 2469
        vdire->checkpoint = VTAILQ_LAST(&vdire->directors, vcldir_head);
456 2469
        AN(vdire->tempp);
457 2469
        *vdire->tempp = temp;
458 2469
        vdire->iterating++;
459 2469
}
460
461
static void
462 2469
vdire_end_event(struct vdire *vdire)
463
{
464 2469
        vdire->checkpoint = NULL;
465 2469
        vdire_end_iter(vdire);
466 2469
}
467
468
// if there are no iterators, remove from directors and retire
469
// otherwise put on resigning list to work when iterators end
470
void
471 217
vdire_resign(struct vdire *vdire, struct vcldir *vdir)
472
{
473 217
        const struct vcltemp *temp = NULL;
474
475 217
        CHECK_OBJ_NOTNULL(vdire, VDIRE_MAGIC);
476 217
        AN(vdir);
477
478 217
        Lck_Lock(vdire->mtx);
479 217
        if (vdire->iterating != 0) {
480 0
                VTAILQ_INSERT_TAIL(&vdire->resigning, vdir, resigning_list);
481 0
                vdir = NULL;
482 0
        } else {
483 217
                VTAILQ_REMOVE(&vdire->directors, vdir, directors_list);
484 217
                temp = *vdire->tempp;
485
        }
486 217
        Lck_Unlock(vdire->mtx);
487
488 217
        if (vdir != NULL)
489 217
                vcldir_retire(vdir, temp);
490 217
}
491
492
// unlocked version of vcl_iterdir
493
// pat can also be NULL (to iterate all)
494
static int
495 1934
vdire_iter(struct cli *cli, const char *pat, const struct vcl *vcl,
496
    vcl_be_func *func, void *priv)
497
{
498 1934
        int i, found = 0;
499
        struct vcldir *vdir;
500 1934
        struct vdire *vdire = vcl->vdire;
501
502 1934
        vdire_start_iter(vdire);
503 4740
        VTAILQ_FOREACH(vdir, &vdire->directors, directors_list) {
504 2806
                CHECK_OBJ(vdir, VCLDIR_MAGIC);
505 2806
                if (vdir->refcnt == 0)
506 0
                        continue;
507 2806
                if (pat != NULL && fnmatch(pat, vdir->cli_name, 0))
508 186
                        continue;
509 2620
                found++;
510 2620
                i = func(cli, vdir->dir, priv);
511 2620
                if (i < 0) {
512 0
                        found = i;
513 0
                        break;
514
                }
515 2620
                found += i;
516 2620
        }
517 1934
        vdire_end_iter(vdire);
518 1934
        return (found);
519
}
520
521
522
/*--------------------------------------------------------------------*/
523
524
// XXX locked case across VCLs - should do without
525
static int
526 12
vcl_iterdir(struct cli *cli, const char *pat, const struct vcl *vcl,
527
    vcl_be_func *func, void *priv)
528
{
529 12
        int i, found = 0;
530
        struct vcldir *vdir;
531
532 12
        Lck_AssertHeld(&vcl_mtx);
533 30
        VTAILQ_FOREACH(vdir, &vcl->vdire->directors, directors_list) {
534 18
                CHECK_OBJ(vdir, VCLDIR_MAGIC);
535 18
                if (fnmatch(pat, vdir->cli_name, 0))
536 8
                        continue;
537 10
                found++;
538 10
                i = func(cli, vdir->dir, priv);
539 10
                if (i < 0)
540 0
                        return (i);
541 10
                found += i;
542 10
        }
543 12
        return (found);
544 12
}
545
546
int
547 1946
VCL_IterDirector(struct cli *cli, const char *pat,
548
    vcl_be_func *func, void *priv)
549
{
550 1946
        int i, found = 0;
551
        struct vsb *vsb;
552
        struct vcl *vcl;
553
554 1946
        ASSERT_CLI();
555 1946
        ASSERT_VCL_ACTIVE();
556 1946
        vsb = VSB_new_auto();
557 1946
        AN(vsb);
558 1946
        if (pat == NULL || *pat == '\0' || !strcmp(pat, "*")) {
559
                // all backends in active VCL
560 1858
                VSB_printf(vsb, "%s.*", VCL_Name(vcl_active));
561 1858
                vcl = vcl_active;
562 1946
        } else if (strchr(pat, '.') == NULL) {
563
                // pattern applies to active vcl
564 76
                VSB_printf(vsb, "%s.%s", VCL_Name(vcl_active), pat);
565 76
                vcl = vcl_active;
566 76
        } else {
567
                // use pattern as is
568 12
                VSB_cat(vsb, pat);
569 12
                vcl = NULL;
570
        }
571 1946
        AZ(VSB_finish(vsb));
572 1946
        if (vcl != NULL) {
573 1934
                found = vdire_iter(cli, VSB_data(vsb), vcl, func, priv);
574 1934
        } else {
575 12
                Lck_Lock(&vcl_mtx);
576 24
                VTAILQ_FOREACH(vcl, &vcl_head, list) {
577 12
                        i = vcl_iterdir(cli, VSB_data(vsb), vcl, func, priv);
578 12
                        if (i < 0) {
579 0
                                found = i;
580 0
                                break;
581
                        } else {
582 12
                                found += i;
583
                        }
584 12
                }
585 12
                Lck_Unlock(&vcl_mtx);
586
        }
587 1946
        VSB_destroy(&vsb);
588 1946
        return (found);
589
}
590
591
/*
592
 * vdire_start_event() must have been called before
593
 *
594
 * send event to directors pre checkpoint
595
 */
596
static void
597 2463
backend_event_base(const struct vcl *vcl, enum vcl_event_e e)
598
{
599
        struct vcldir *vdir;
600
        struct vdire *vdire;
601
602 2463
        ASSERT_CLI();
603 2463
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
604 2463
        AZ(vcl->busy);
605 2463
        vdire = vcl->vdire;
606 2463
        AN(vdire->iterating);
607
608 2463
        vdir = vdire->checkpoint;
609 2463
        if (vdir == NULL)
610 270
                return;
611
612 2193
        CHECK_OBJ(vdir, VCLDIR_MAGIC);
613 5107
        VTAILQ_FOREACH_REVERSE_FROM(vdir, &vdire->directors,
614
            vcldir_head, directors_list) {
615 2914
                CHECK_OBJ(vdir, VCLDIR_MAGIC);
616 2914
                VDI_Event(vdir->dir, e);
617 2914
        }
618 2463
}
619
620
/*
621
 * vdire_start_event() must have been called before
622
 *
623
 * set a new temperature.
624
 * send event to directors added post checkpoint, but before
625
 * the new temperature
626
 */
627
static void
628 6
backend_event_delta(const struct vcl *vcl, enum vcl_event_e e, const struct vcltemp *temp)
629
{
630
        struct vcldir *vdir, *end;
631
        struct vdire *vdire;
632
633 6
        ASSERT_CLI();
634 6
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
635 6
        AZ(vcl->busy);
636 6
        vdire = vcl->vdire;
637 6
        AN(temp);
638 6
        AN(vdire->iterating);
639
640 6
        Lck_Lock(vdire->mtx);
641 6
        if (vdire->checkpoint == NULL)
642 0
                vdir = VTAILQ_FIRST(&vdire->directors);
643
        else
644 6
                vdir = VTAILQ_NEXT(vdire->checkpoint, directors_list);
645 6
        AN(vdire->tempp);
646 6
        *vdire->tempp = temp;
647 6
        end = VTAILQ_LAST(&vdire->directors, vcldir_head);
648 6
        Lck_Unlock(vdire->mtx);
649
650 6
        while (vdir != NULL) {
651 0
                CHECK_OBJ(vdir, VCLDIR_MAGIC);
652 0
                VDI_Event(vdir->dir, e);
653 0
                if (vdir == end)
654 0
                        break;
655 0
                vdir = VTAILQ_NEXT(vdir, directors_list);
656
        }
657 6
}
658
659
static void
660 156
vcl_KillBackends(const struct vcl *vcl)
661
{
662
        struct vcldir *vdir, *vdir2;
663
        struct vdire *vdire;
664
665 156
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
666 156
        assert(vcl->temp == VCL_TEMP_COLD || vcl->temp == VCL_TEMP_INIT);
667 156
        vdire = vcl->vdire;
668 156
        CHECK_OBJ_NOTNULL(vdire, VDIRE_MAGIC);
669
670
        /*
671
         * ensure all retirement has finished (vdire_start_iter waits for it)
672
         */
673 156
        vdire_start_iter(vdire);
674 156
        vdire_end_iter(vdire);
675 156
        AZ(vdire->iterating);
676 156
        assert(VTAILQ_EMPTY(&vdire->resigning));
677
678
        /*
679
         * Unlocked and sidelining vdire because no further directors can be added, and the
680
         * remaining ones need to be able to remove themselves.
681
         */
682 290
        VTAILQ_FOREACH_SAFE(vdir, &vdire->directors, directors_list, vdir2) {
683 134
                CHECK_OBJ(vdir, VCLDIR_MAGIC);
684 134
                VDI_Event(vdir->dir, VCL_EVENT_DISCARD);
685 134
        }
686 156
        assert(VTAILQ_EMPTY(&vdire->directors));
687 156
}
688
689
/*--------------------------------------------------------------------*/
690
691
static struct vcl *
692 4932
VCL_Open(const char *fn, struct vsb *msg)
693
{
694
        struct vcl *vcl;
695
        void *dlh;
696
        struct VCL_conf const *cnf;
697
        const char *dlerr;
698
        int err;
699
700 4932
        AN(fn);
701 4932
        AN(msg);
702
703
#ifdef RTLD_NOLOAD
704
        /* Detect bogus caching by dlopen(3) */
705 4932
        dlh = dlopen(fn, RTLD_NOW | RTLD_NOLOAD);
706 4932
        AZ(dlh);
707
#endif
708 4932
        dlh = dlopen(fn, RTLD_NOW | RTLD_LOCAL);
709 4932
        if (dlh == NULL) {
710 2
                err = errno;
711 2
                dlerr = dlerror();
712 2
                VSB_cat(msg, "Could not load compiled VCL.\n");
713 2
                if (dlerr != NULL)
714 2
                        VSB_printf(msg, "\tdlopen() = %s\n", dlerr);
715 2
                if (err) {
716 0
                        VSB_printf(msg, "\terror = %s (%d)\n",
717 0
                            strerror(err), err);
718 0
                }
719 2
                VSB_cat(msg, "\thint: check for \"noexec\" mount\n");
720 2
                VSB_cat(msg, "\thint: check \"vmod_path\" parameter\n");
721 2
                return (NULL);
722
        }
723 4930
        cnf = dlsym(dlh, "VCL_conf");
724 4930
        if (cnf == NULL) {
725 0
                VSB_cat(msg, "Compiled VCL lacks metadata.\n");
726 0
                (void)dlclose(dlh);
727 0
                return (NULL);
728
        }
729 4930
        if (cnf->magic != VCL_CONF_MAGIC) {
730 0
                VSB_cat(msg, "Compiled VCL has mangled metadata.\n");
731 0
                (void)dlclose(dlh);
732 0
                return (NULL);
733
        }
734 4930
        if (cnf->syntax < heritage.min_vcl_version ||
735 4928
            cnf->syntax > heritage.max_vcl_version) {
736 4
                VSB_printf(msg, "Compiled VCL version (%.1f) not supported.\n",
737 2
                    .1 * cnf->syntax);
738 2
                (void)dlclose(dlh);
739 2
                return (NULL);
740
        }
741 4928
        ALLOC_OBJ(vcl, VCL_MAGIC);
742 4928
        AN(vcl);
743 4928
        vcl->dlh = dlh;
744 4928
        vcl->conf = cnf;
745 4928
        vcl->vdire = vdire_new(&vcl_mtx, &vcl->temp);
746 4928
        return (vcl);
747 4932
}
748
749
static void
750 2624
VCL_Close(struct vcl **vclp)
751
{
752
        struct vcl *vcl;
753
754 2624
        TAKE_OBJ_NOTNULL(vcl, vclp, VCL_MAGIC);
755 2624
        assert(VTAILQ_EMPTY(&vcl->filters));
756 2624
        AZ(dlclose(vcl->dlh));
757 2624
        PTOK(pthread_cond_destroy(&vcl->vdire->cond));
758 2624
        FREE_OBJ(vcl->vdire);
759 2624
        FREE_OBJ(vcl);
760 2624
}
761
762
/*--------------------------------------------------------------------
763
 * NB: This function is called in/from the test-load subprocess.
764
 */
765
766
int
767 2472
VCL_TestLoad(const char *fn)
768
{
769
        struct vsb *vsb;
770
        struct vcl *vcl;
771 2472
        int retval = 0;
772
773 2472
        AN(fn);
774 2472
        vsb = VSB_new_auto();
775 2472
        AN(vsb);
776 2472
        vcl = VCL_Open(fn, vsb);
777 2472
        if (vcl == NULL) {
778 4
                AZ(VSB_finish(vsb));
779 4
                fprintf(stderr, "%s", VSB_data(vsb));
780 4
                retval = -1;
781 4
        } else
782 2468
                VCL_Close(&vcl);
783 2472
        VSB_destroy(&vsb);
784 2472
        return (retval);
785
}
786
787
/*--------------------------------------------------------------------*/
788
789
static struct vsb *
790 2
vcl_print_refs(const struct vcl *vcl)
791
{
792
        struct vsb *msg;
793
        struct vclref *ref;
794
795 2
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
796 2
        msg = VSB_new_auto();
797 2
        AN(msg);
798
799 2
        VSB_printf(msg, "VCL %s is waiting for:", vcl->loaded_name);
800 2
        Lck_Lock(&vcl_mtx);
801 4
        VTAILQ_FOREACH(ref, &vcl->ref_list, list)
802 2
                VSB_printf(msg, "\n\t- %s", ref->desc);
803 2
        Lck_Unlock(&vcl_mtx);
804 2
        AZ(VSB_finish(msg));
805 2
        return (msg);
806
}
807
808
static int
809 2576
vcl_set_state(struct vcl *vcl, const char *state, struct vsb **msg)
810
{
811 2576
        struct vsb *nomsg = NULL;
812 2576
        int i = 0;
813
814 2576
        ASSERT_CLI();
815
816 2576
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
817 2576
        AN(state);
818 2576
        AN(msg);
819 2576
        AZ(*msg);
820
821 2576
        AN(vcl->temp);
822
823 2576
        switch (state[0]) {
824
        case '0':
825 190
                if (vcl->temp == VCL_TEMP_COLD)
826 4
                        break;
827 186
                if (vcl->busy == 0 && vcl->temp->is_warm) {
828 87
                        Lck_Lock(&vcl_mtx);
829 87
                        vdire_start_event(vcl->vdire, VTAILQ_EMPTY(&vcl->ref_list) ?
830
                            VCL_TEMP_COLD : VCL_TEMP_COOLING);
831 87
                        Lck_Unlock(&vcl_mtx);
832 87
                        backend_event_base(vcl, VCL_EVENT_COLD);
833 87
                        vdire_end_event(vcl->vdire);
834
                        // delta directors at VCL_TEMP_COLD do not need an event
835 87
                        AZ(vcl_send_event(vcl, VCL_EVENT_COLD, msg));
836 87
                        AZ(*msg);
837 87
                }
838 99
                else if (vcl->busy)
839 56
                        vcl->temp = VCL_TEMP_BUSY;
840 43
                else if (VTAILQ_EMPTY(&vcl->ref_list))
841 35
                        vcl->temp = VCL_TEMP_COLD;
842
                else
843 8
                        vcl->temp = VCL_TEMP_COOLING;
844 186
                break;
845
        case '1':
846 2386
                if (vcl->temp == VCL_TEMP_WARM)
847 2
                        break;
848
                /* The warm VCL hasn't seen a cold event yet */
849 2384
                if (vcl->temp == VCL_TEMP_BUSY)
850 2
                        vcl->temp = VCL_TEMP_WARM;
851
                /* The VCL must first reach a stable cold state */
852 2382
                else if (vcl->temp == VCL_TEMP_COOLING) {
853 2
                        *msg = vcl_print_refs(vcl);
854 2
                        i = -1;
855 2
                }
856
                else {
857 2380
                        Lck_Lock(&vcl_mtx);
858 2380
                        vdire_start_event(vcl->vdire, VCL_TEMP_WARM);
859 2380
                        Lck_Unlock(&vcl_mtx);
860 2380
                        i = vcl_send_event(vcl, VCL_EVENT_WARM, msg);
861 2380
                        if (i == 0) {
862 2374
                                backend_event_base(vcl, VCL_EVENT_WARM);
863
                                // delta directors are already warm
864 2374
                                vdire_end_event(vcl->vdire);
865 2374
                                break;
866
                        }
867 6
                        AZ(vcl_send_event(vcl, VCL_EVENT_COLD, &nomsg));
868 6
                        AZ(nomsg);
869 6
                        backend_event_delta(vcl, VCL_EVENT_COLD, VCL_TEMP_COLD);
870 6
                        vdire_end_event(vcl->vdire);
871
                }
872 10
                break;
873
        default:
874 0
                WRONG("Wrong enum state");
875 0
        }
876 2576
        if (i == 0 && state[1])
877 2482
                bprintf(vcl->state, "%s", state + 1);
878
879 2576
        return (i);
880
}
881
882
static void
883 86
vcl_cancel_load(struct vcl *vcl, struct cli *cli, struct vsb *msg,
884
    const char *name, const char *step)
885
{
886
887 86
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
888 86
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
889
890 86
        VCLI_SetResult(cli, CLIS_CANT);
891 86
        VCLI_Out(cli, "VCL \"%s\" Failed %s", name, step);
892 86
        if (VSB_len(msg))
893 86
                VCLI_Out(cli, "\nMessage:\n\t%s", VSB_data(msg));
894 86
        VSB_destroy(&msg);
895
896 86
        AZ(vcl_send_event(vcl, VCL_EVENT_DISCARD, &msg));
897 86
        AZ(msg);
898
899 86
        vcl_KillBackends(vcl);
900 86
        free(vcl->loaded_name);
901 86
        VCL_Close(&vcl);
902 86
}
903
904
static void
905 2460
vcl_load(struct cli *cli,
906
    const char *name, const char *fn, const char *state)
907
{
908
        struct vcl *vcl;
909
        struct vsb *msg;
910
911 2460
        ASSERT_CLI();
912 2460
        ASSERT_VCL_ACTIVE();
913
914 2460
        vcl = vcl_find(name);
915 2460
        AZ(vcl);
916
917 2460
        msg = VSB_new_auto();
918 2460
        AN(msg);
919 2460
        vcl = VCL_Open(fn, msg);
920 2460
        AZ(VSB_finish(msg));
921 2460
        if (vcl == NULL) {
922 0
                VCLI_SetResult(cli, CLIS_PARAM);
923 0
                VCLI_Out(cli, "%s", VSB_data(msg));
924 0
                VSB_destroy(&msg);
925 0
                return;
926
        }
927
928 2460
        VSB_destroy(&msg);
929
930 2460
        vcl->loaded_name = strdup(name);
931 2460
        XXXAN(vcl->loaded_name);
932 2460
        VTAILQ_INIT(&vcl->ref_list);
933 2460
        VTAILQ_INIT(&vcl->filters);
934
935 2460
        vcl->temp = VCL_TEMP_INIT;
936
937 2460
        if (vcl_send_event(vcl, VCL_EVENT_LOAD, &msg)) {
938 84
                vcl_cancel_load(vcl, cli, msg, name, "initialization");
939 84
                return;
940
        }
941 2376
        VSB_destroy(&msg);
942
943 2376
        if (vcl_set_state(vcl, state, &msg)) {
944 2
                assert(*state == '1');
945 2
                vcl_cancel_load(vcl, cli, msg, name, "warmup");
946 2
                return;
947
        }
948 2374
        if (msg)
949 2366
                VSB_destroy(&msg);
950
951 2374
        VCLI_Out(cli, "Loaded \"%s\" as \"%s\"", fn , name);
952 2374
        VTAILQ_INSERT_TAIL(&vcl_head, vcl, list);
953 2374
        VSC_C_main->n_vcl++;
954 2374
        VSC_C_main->n_vcl_avail++;
955 2460
}
956
957
/*--------------------------------------------------------------------*/
958
959
void
960 15498
VCL_Poll(void)
961
{
962 15498
        struct vsb *nomsg = NULL;
963
        struct vcl *vcl, *vcl2;
964
965 15498
        ASSERT_CLI();
966 15498
        ASSERT_VCL_ACTIVE();
967 32620
        VTAILQ_FOREACH_SAFE(vcl, &vcl_head, list, vcl2) {
968 17122
                if (vcl->temp == VCL_TEMP_BUSY ||
969 17071
                    vcl->temp == VCL_TEMP_COOLING)
970 86
                        AZ(vcl_set_state(vcl, "0", &nomsg));
971 17122
                AZ(nomsg);
972 17122
                if (vcl->discard && vcl->temp == VCL_TEMP_COLD) {
973 70
                        AZ(vcl->busy);
974 70
                        assert(vcl != vcl_active);
975 70
                        assert(VTAILQ_EMPTY(&vcl->ref_list));
976 70
                        VTAILQ_REMOVE(&vcl_head, vcl, list);
977 70
                        AZ(vcl_send_event(vcl, VCL_EVENT_DISCARD, &nomsg));
978 70
                        AZ(nomsg);
979 70
                        vcl_KillBackends(vcl);
980 70
                        free(vcl->loaded_name);
981 70
                        VCL_Close(&vcl);
982 70
                        VSC_C_main->n_vcl--;
983 70
                        VSC_C_main->n_vcl_discard--;
984 70
                }
985 17122
        }
986 15498
}
987
988
/*--------------------------------------------------------------------*/
989
990
static void v_matchproto_(cli_func_t)
991 150
vcl_cli_list(struct cli *cli, const char * const *av, void *priv)
992
{
993
        struct vcl *vcl;
994
        const char *flg;
995
        struct vte *vte;
996
997
        /* NB: Shall generate same output as mcf_vcl_list() */
998
999 150
        (void)av;
1000 150
        (void)priv;
1001 150
        ASSERT_CLI();
1002 150
        ASSERT_VCL_ACTIVE();
1003 150
        vte = VTE_new(7, 80);
1004 150
        AN(vte);
1005 478
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
1006 328
                if (vcl == vcl_active) {
1007 150
                        flg = "active";
1008 328
                } else if (vcl->discard) {
1009 6
                        flg = "discarded";
1010 6
                } else
1011 172
                        flg = "available";
1012 656
                VTE_printf(vte, "%s\t%s\t%s\t\v%u\t%s", flg, vcl->state,
1013 328
                    vcl->temp->name, vcl->busy, vcl->loaded_name);
1014 328
                if (vcl->label != NULL) {
1015 38
                        VTE_printf(vte, "\t->\t%s", vcl->label->loaded_name);
1016 38
                        if (vcl->nrefs)
1017 16
                                VTE_printf(vte, " (%d return(vcl)%s)",
1018 8
                                    vcl->nrefs, vcl->nrefs > 1 ? "'s" : "");
1019 328
                } else if (vcl->nlabels > 0) {
1020 52
                        VTE_printf(vte, "\t<-\t(%d label%s)",
1021 26
                            vcl->nlabels, vcl->nlabels > 1 ? "s" : "");
1022 26
                }
1023 328
                VTE_cat(vte, "\n");
1024 328
        }
1025 150
        AZ(VTE_finish(vte));
1026 150
        AZ(VTE_format(vte, VCLI_VTE_format, cli));
1027 150
        VTE_destroy(&vte);
1028 150
}
1029
1030
static void v_matchproto_(cli_func_t)
1031 10
vcl_cli_list_json(struct cli *cli, const char * const *av, void *priv)
1032
{
1033
        struct vcl *vcl;
1034
1035 10
        (void)priv;
1036 10
        ASSERT_CLI();
1037 10
        ASSERT_VCL_ACTIVE();
1038 10
        VCLI_JSON_begin(cli, 2, av);
1039 10
        VCLI_Out(cli, ",\n");
1040 42
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
1041 32
                VCLI_Out(cli, "{\n");
1042 32
                VSB_indent(cli->sb, 2);
1043 32
                VCLI_Out(cli, "\"status\": ");
1044 32
                if (vcl == vcl_active) {
1045 10
                        VCLI_Out(cli, "\"active\",\n");
1046 32
                } else if (vcl->discard) {
1047 0
                        VCLI_Out(cli, "\"discarded\",\n");
1048 0
                } else
1049 22
                        VCLI_Out(cli, "\"available\",\n");
1050 32
                VCLI_Out(cli, "\"state\": \"%s\",\n", vcl->state);
1051 32
                VCLI_Out(cli, "\"temperature\": \"%s\",\n", vcl->temp->name);
1052 32
                VCLI_Out(cli, "\"busy\": %u,\n", vcl->busy);
1053 32
                VCLI_Out(cli, "\"name\": \"%s\"", vcl->loaded_name);
1054 32
                if (vcl->label != NULL) {
1055 12
                        VCLI_Out(cli, ",\n");
1056 12
                        VCLI_Out(cli, "\"label\": {\n");
1057 12
                        VSB_indent(cli->sb, 2);
1058 24
                                VCLI_Out(cli, "\"name\": \"%s\"",
1059 12
                                         vcl->label->loaded_name);
1060 12
                        if (vcl->nrefs)
1061 0
                                VCLI_Out(cli, ",\n\"refs\": %d", vcl->nrefs);
1062 12
                        VCLI_Out(cli, "\n");
1063 12
                        VCLI_Out(cli, "}");
1064 12
                        VSB_indent(cli->sb, -2);
1065 32
                } else if (vcl->nlabels > 0) {
1066 4
                        VCLI_Out(cli, ",\n");
1067 4
                        VCLI_Out(cli, "\"labels\": %d", vcl->nlabels);
1068 4
                }
1069 32
                VSB_indent(cli->sb, -2);
1070 32
                VCLI_Out(cli, "\n}");
1071 32
                if (VTAILQ_NEXT(vcl, list) != NULL)
1072 22
                        VCLI_Out(cli, ",\n");
1073 32
        }
1074 10
        VCLI_JSON_end(cli);
1075 10
}
1076
1077
static void v_matchproto_(cli_func_t)
1078 2460
vcl_cli_load(struct cli *cli, const char * const *av, void *priv)
1079
{
1080
1081 2460
        AZ(priv);
1082 2460
        ASSERT_CLI();
1083
        // XXX move back code from vcl_load?
1084 2460
        vcl_load(cli, av[2], av[3], av[4]);
1085 2460
}
1086
1087
static void v_matchproto_(cli_func_t)
1088 116
vcl_cli_state(struct cli *cli, const char * const *av, void *priv)
1089
{
1090
        struct vcl *vcl;
1091 116
        struct vsb *msg = NULL;
1092
1093 116
        AZ(priv);
1094 116
        ASSERT_CLI();
1095 116
        ASSERT_VCL_ACTIVE();
1096 116
        AN(av[2]);
1097 116
        AN(av[3]);
1098
1099 116
        vcl = vcl_find(av[2]);
1100 116
        AN(vcl);
1101
1102 116
        if (vcl_set_state(vcl, av[3], &msg)) {
1103 6
                CHECK_OBJ_NOTNULL(msg, VSB_MAGIC);
1104
1105 6
                VCLI_SetResult(cli, CLIS_CANT);
1106 12
                VCLI_Out(cli, "Failed <vcl.state %s %s>", vcl->loaded_name,
1107 6
                    av[3] + 1);
1108 6
                if (VSB_len(msg))
1109 6
                        VCLI_Out(cli, "\nMessage:\n\t%s", VSB_data(msg));
1110 6
        }
1111 116
        if (msg)
1112 14
                VSB_destroy(&msg);
1113 116
}
1114
1115
static void v_matchproto_(cli_func_t)
1116 78
vcl_cli_discard(struct cli *cli, const char * const *av, void *priv)
1117
{
1118
        struct vcl *vcl;
1119
1120 78
        ASSERT_CLI();
1121 78
        ASSERT_VCL_ACTIVE();
1122 78
        (void)cli;
1123 78
        AZ(priv);
1124 78
        vcl = vcl_find(av[2]);
1125 78
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);              // MGT ensures this
1126 78
        Lck_Lock(&vcl_mtx);
1127 78
        assert (vcl != vcl_active);     // MGT ensures this
1128 78
        AZ(vcl->nlabels);               // MGT ensures this
1129 78
        VSC_C_main->n_vcl_discard++;
1130 78
        VSC_C_main->n_vcl_avail--;
1131 78
        vcl->discard = 1;
1132 78
        if (vcl->label != NULL) {
1133 8
                AZ(strcmp(vcl->state, VCL_TEMP_LABEL->name));
1134 8
                vcl->label->nlabels--;
1135 8
                vcl->label= NULL;
1136 8
        }
1137 78
        Lck_Unlock(&vcl_mtx);
1138
1139 78
        if (!strcmp(vcl->state, VCL_TEMP_LABEL->name)) {
1140 8
                VTAILQ_REMOVE(&vcl_head, vcl, list);
1141 8
                free(vcl->loaded_name);
1142 8
                AZ(vcl->vdire);
1143 8
                FREE_OBJ(vcl);
1144 78
        } else if (vcl->temp == VCL_TEMP_COLD) {
1145 53
                VCL_Poll();
1146 53
        }
1147 78
}
1148
1149
static void v_matchproto_(cli_func_t)
1150 54
vcl_cli_label(struct cli *cli, const char * const *av, void *priv)
1151
{
1152
        struct vcl *lbl;
1153
        struct vcl *vcl;
1154
1155 54
        ASSERT_CLI();
1156 54
        ASSERT_VCL_ACTIVE();
1157 54
        (void)cli;
1158 54
        (void)priv;
1159 54
        vcl = vcl_find(av[3]);
1160 54
        AN(vcl);                                // MGT ensures this
1161 54
        lbl = vcl_find(av[2]);
1162 54
        if (lbl == NULL) {
1163 48
                ALLOC_OBJ(lbl, VCL_MAGIC);
1164 48
                AN(lbl);
1165 48
                bprintf(lbl->state, "%s", VCL_TEMP_LABEL->name);
1166 48
                lbl->temp = VCL_TEMP_WARM;
1167 48
                REPLACE(lbl->loaded_name, av[2]);
1168 48
                VTAILQ_INSERT_TAIL(&vcl_head, lbl, list);
1169 48
        }
1170 54
        if (lbl->label != NULL)
1171 6
                lbl->label->nlabels--;
1172 54
        lbl->label = vcl;
1173 54
        vcl->nlabels++;
1174 54
}
1175
1176
static void v_matchproto_(cli_func_t)
1177 2286
vcl_cli_use(struct cli *cli, const char * const *av, void *priv)
1178
{
1179
        struct vcl *vcl;
1180
1181 2286
        ASSERT_CLI();
1182 2286
        ASSERT_VCL_ACTIVE();
1183 2286
        AN(cli);
1184 2286
        AZ(priv);
1185 2286
        vcl = vcl_find(av[2]);
1186 2286
        AN(vcl);                                // MGT ensures this
1187 2286
        assert(vcl->temp == VCL_TEMP_WARM);     // MGT ensures this
1188 2286
        Lck_Lock(&vcl_mtx);
1189 2286
        vcl_active = vcl;
1190 2286
        Lck_Unlock(&vcl_mtx);
1191 2286
}
1192
1193
static void v_matchproto_(cli_func_t)
1194 24
vcl_cli_show(struct cli *cli, const char * const *av, void *priv)
1195
{
1196
        struct vcl *vcl;
1197 24
        int verbose = 0;
1198 24
        int i = 2;
1199
        unsigned u;
1200
1201 24
        ASSERT_CLI();
1202 24
        ASSERT_VCL_ACTIVE();
1203 24
        AZ(priv);
1204
1205 24
        if (av[i] != NULL && !strcmp(av[i], "-v")) {
1206 8
                verbose = 1;
1207 8
                i++;
1208 8
        }
1209
1210 24
        if (av[i] == NULL) {
1211 4
                vcl = vcl_active;
1212 4
                AN(vcl);
1213 4
        } else {
1214 20
                vcl = vcl_find(av[i]);
1215 20
                i++;
1216
        }
1217
1218 24
        if (av[i] != NULL) {
1219 2
                VCLI_Out(cli, "Too many parameters: '%s'", av[i]);
1220 2
                VCLI_SetResult(cli, CLIS_PARAM);
1221 2
                return;
1222
        }
1223
1224 22
        if (vcl == NULL) {
1225 2
                VCLI_Out(cli, "No VCL named '%s'", av[i - 1]);
1226 2
                VCLI_SetResult(cli, CLIS_PARAM);
1227 2
                return;
1228
        }
1229 20
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
1230 20
        if (vcl->label) {
1231 4
                vcl = vcl->label;
1232 4
                CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
1233 4
                AZ(vcl->label);
1234 4
        }
1235 20
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
1236 20
        if (verbose) {
1237 24
                for (u = 0; u < vcl->conf->nsrc; u++)
1238 32
                        VCLI_Out(cli, "// VCL.SHOW %u %zd %s\n%s\n",
1239 16
                            u, strlen(vcl->conf->srcbody[u]),
1240 16
                            vcl->conf->srcname[u],
1241 16
                            vcl->conf->srcbody[u]);
1242 8
        } else {
1243 12
                VCLI_Out(cli, "%s", vcl->conf->srcbody[0]);
1244
        }
1245 24
}
1246
1247
/*--------------------------------------------------------------------*/
1248
1249
static struct cli_proto vcl_cmds[] = {
1250
        { CLICMD_VCL_LOAD,              "", vcl_cli_load },
1251
        { CLICMD_VCL_LIST,              "", vcl_cli_list, vcl_cli_list_json },
1252
        { CLICMD_VCL_STATE,             "", vcl_cli_state },
1253
        { CLICMD_VCL_DISCARD,           "", vcl_cli_discard },
1254
        { CLICMD_VCL_USE,               "", vcl_cli_use },
1255
        { CLICMD_VCL_SHOW,              "", vcl_cli_show },
1256
        { CLICMD_VCL_LABEL,             "", vcl_cli_label },
1257
        { NULL }
1258
};
1259
1260
void
1261 1898
VCL_Init(void)
1262
{
1263
1264 1898
        assert(cache_param->workspace_client > 0);
1265 3796
        WS_Init(&ws_cli, "cli", malloc(cache_param->workspace_client),
1266 1898
            cache_param->workspace_client);
1267 1898
        ws_snapshot_cli = WS_Snapshot(&ws_cli);
1268 1898
        CLI_AddFuncs(vcl_cmds);
1269 1898
        Lck_New(&vcl_mtx, lck_vcl);
1270 1898
        VSL_Setup(&vsl_cli, NULL, 0);
1271 1898
}
1272
1273
void
1274 1872
VCL_Shutdown(void)
1275
{
1276
        struct vcl *vcl;
1277 1872
        unsigned c = 0;
1278
1279 15061
        while (1) {
1280 15061
                Lck_Lock(&vcl_mtx);
1281 19736
                VTAILQ_FOREACH(vcl, &vcl_head, list)
1282 17864
                        if (vcl->busy)
1283 13189
                                break;
1284 15061
                Lck_Unlock(&vcl_mtx);
1285 15061
                if (vcl == NULL)
1286 1872
                        return;
1287 13189
                if (++c % 10 == 0) {
1288 64
                        fprintf(stderr, "shutdown waiting for %u reference%s "
1289 32
                            "on %s\n", vcl->busy, vcl->busy > 1 ? "s" : "",
1290 32
                            vcl->loaded_name);
1291 32
                }
1292 13189
                usleep(100 * 1000);
1293
        }
1294
}