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 607918
VCL_Bo2Ctx(struct vrt_ctx *ctx, struct busyobj *bo)
80
{
81
82 607918
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
83 607918
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
84 607918
        CHECK_OBJ_NOTNULL(bo->wrk, WORKER_MAGIC);
85 607918
        ctx->vcl = bo->vcl;
86 607918
        ctx->syntax = ctx->vcl->conf->syntax;
87 607918
        ctx->vsl = bo->vsl;
88 607918
        ctx->http_bereq = bo->bereq;
89 607918
        ctx->http_beresp = bo->beresp;
90 607918
        ctx->bo = bo;
91 607918
        ctx->sp = bo->sp;
92 607918
        ctx->now = bo->t_prev;
93 607918
        ctx->ws = bo->ws;
94 607918
        ctx->vpi = bo->wrk->vpi;
95 607918
        ctx->vpi->handling = 0;
96 607918
        ctx->vpi->trace = bo->trace;
97 607918
}
98
99
void
100 973837
VCL_Req2Ctx(struct vrt_ctx *ctx, struct req *req)
101
{
102
103 973837
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
104 973837
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
105 973837
        CHECK_OBJ_NOTNULL(req->doclose, STREAM_CLOSE_MAGIC);
106
107 973837
        ctx->vcl = req->vcl;
108 973837
        ctx->syntax = ctx->vcl->conf->syntax;
109 973837
        ctx->vsl = req->vsl;
110 973837
        ctx->http_req = req->http;
111 973837
        CHECK_OBJ_NOTNULL(req->top, REQTOP_MAGIC);
112 973837
        ctx->http_req_top = req->top->topreq->http;
113 973837
        ctx->http_resp = req->resp;
114 973837
        ctx->req = req;
115 973837
        ctx->sp = req->sp;
116 973837
        ctx->now = req->t_prev;
117 973837
        ctx->ws = req->ws;
118 973837
        ctx->vpi = req->wrk->vpi;
119 973837
        ctx->vpi->handling = 0;
120 973837
        ctx->vpi->trace = req->trace;
121 973837
}
122
123
/*--------------------------------------------------------------------*/
124
125
struct vrt_ctx *
126 150181
VCL_Get_CliCtx(int msg)
127
{
128
129 150181
        ASSERT_CLI();
130 150181
        INIT_OBJ(&ctx_cli, VRT_CTX_MAGIC);
131 150181
        INIT_OBJ(&wrk_vpi_cli, WRK_VPI_MAGIC);
132 150181
        ctx_cli.vpi = &wrk_vpi_cli;
133 150181
        wrk_vpi_cli.trace = FEATURE(FEATURE_TRACE);
134 150181
        ctx_cli.now = VTIM_real();
135 150181
        if (msg) {
136 94640
                ctx_cli.msg = VSB_new_auto();
137 94640
                AN(ctx_cli.msg);
138 94640
        } else {
139 55541
                ctx_cli.vsl = &vsl_cli;
140
        }
141 150181
        ctx_cli.ws = &ws_cli;
142 150181
        WS_Assert(ctx_cli.ws);
143 150181
        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 150141
VCL_Rel_CliCtx(struct vrt_ctx **ctx)
156
{
157 150141
        struct vsb *r = NULL;
158
159 150141
        ASSERT_CLI();
160 150141
        assert(*ctx == &ctx_cli);
161 150141
        AN((*ctx)->vpi);
162 150141
        if (ctx_cli.msg) {
163 94640
                TAKE_OBJ_NOTNULL(r, &ctx_cli.msg, VSB_MAGIC);
164 94640
                AZ(VSB_finish(r));
165 94640
        }
166 150141
        if (ctx_cli.vsl)
167 55501
                VSL_Flush(ctx_cli.vsl, 0);
168 150141
        WS_Assert(ctx_cli.ws);
169 150141
        WS_Rollback(&ws_cli, ws_snapshot_cli);
170 150141
        INIT_OBJ(*ctx, VRT_CTX_MAGIC);
171 150141
        *ctx = NULL;
172
173 150141
        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 99581
vcl_event_handling(VRT_CTX)
189
{
190 99581
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
191
192 99581
        if (ctx->vpi->handling == 0)
193 97861
                return (0);
194
195 1720
        assert(ctx->vpi->handling == VCL_RET_FAIL);
196
197 1720
        if (ctx->method == VCL_MET_INIT)
198 1640
                return (1);
199
200
        /*
201
         * EVENT_WARM / EVENT_COLD: method == 0
202
         * must not set handling
203
         */
204 80
        assert(ctx->method == VCL_MET_FINI);
205
206 80
        ctx->vpi->handling = 0;
207 80
        VRT_fail(ctx, "VRT_fail() from vcl_fini{} has no effect");
208 80
        return (0);
209 99581
}
210
211
static int
212 99621
vcl_send_event(struct vcl *vcl, enum vcl_event_e ev, struct vsb **msg)
213
{
214
        int r, havemsg;
215 99621
        unsigned method = 0;
216
        struct vrt_ctx *ctx;
217
218 99621
        ASSERT_CLI();
219 99621
        ASSERT_VCL_ACTIVE();
220
221 99621
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
222 99621
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
223 99621
        AN(msg);
224 99621
        AZ(*msg);
225
226 99621
        switch (ev) {
227
        case VCL_EVENT_LOAD:
228 48120
                method = VCL_MET_INIT;
229
                /* FALLTHROUGH */
230
        case VCL_EVENT_WARM:
231 94640
                havemsg = 1;
232 94640
                break;
233
        case VCL_EVENT_DISCARD:
234 3094
                method = VCL_MET_FINI;
235
                /* FALLTHROUGH */
236
        case VCL_EVENT_COLD:
237 4981
                havemsg = 0;
238 4981
                break;
239
        default:
240 0
                WRONG("vcl_event");
241 0
        }
242
243 99621
        ctx = VCL_Get_CliCtx(havemsg);
244
245 99621
        AN(ctx->vpi);
246 99621
        AZ(ctx->vpi->handling);
247 99621
        AN(ctx->ws);
248
249 99621
        ctx->vcl = vcl;
250 99621
        ctx->syntax = ctx->vcl->conf->syntax;
251 99621
        ctx->method = method;
252
253 99621
        VCL_TaskEnter(cli_task_privs);
254 99621
        r = ctx->vcl->conf->event_vcl(ctx, ev);
255 99621
        VCL_TaskLeave(ctx, cli_task_privs);
256 99621
        r |= vcl_event_handling(ctx);
257
258 99621
        *msg = VCL_Rel_CliCtx(&ctx);
259
260 99621
        if (r && (ev == VCL_EVENT_COLD || ev == VCL_EVENT_DISCARD))
261 0
                WRONG("A VMOD cannot fail COLD or DISCARD events");
262
263 99621
        return (r);
264
}
265
266
/*--------------------------------------------------------------------*/
267
268
struct vcl *
269 100080
vcl_find(const char *name)
270
{
271
        struct vcl *vcl;
272
273 100080
        ASSERT_CLI();
274 151098
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
275 101938
                if (vcl->discard)
276 58
                        continue;
277 101880
                if (!strcmp(vcl->loaded_name, name))
278 50920
                        return (vcl);
279 50960
        }
280 49160
        return (NULL);
281 100080
}
282
283
/*--------------------------------------------------------------------*/
284
285
static void
286 280
vcl_panic_conf(struct vsb *vsb, const struct VCL_conf *conf)
287
{
288
        unsigned u;
289
        const struct vpi_ii *ii;
290
291 280
        if (PAN_dump_struct(vsb, conf, VCL_CONF_MAGIC, "conf"))
292 0
                return;
293 280
        VSB_printf(vsb, "syntax = \"%u\",\n", conf->syntax);
294 280
        VSB_cat(vsb, "srcname = {\n");
295 280
        VSB_indent(vsb, 2);
296 840
        for (u = 0; u < conf->nsrc; ++u)
297 560
                VSB_printf(vsb, "[%u] = \"%s\",\n", u, conf->srcname[u]);
298 280
        VSB_indent(vsb, -2);
299 280
        VSB_cat(vsb, "},\n");
300 280
        VSB_cat(vsb, "instances = {\n");
301 280
        VSB_indent(vsb, 2);
302 280
        ii = conf->instance_info;
303 440
        while (ii != NULL && ii->p != NULL) {
304 320
                VSB_printf(vsb, "\"%s\" = %p,\n", ii->name,
305 160
                    (const void *)*(const uintptr_t *)ii->p);
306 160
                ii++;
307
        }
308 280
        VSB_indent(vsb, -2);
309 280
        VSB_cat(vsb, "},\n");
310 280
        VSB_indent(vsb, -2);
311 280
        VSB_cat(vsb, "},\n");
312 280
}
313
314
void
315 520
VCL_Panic(struct vsb *vsb, const char *nm, const struct vcl *vcl)
316
{
317
318 520
        AN(vsb);
319 520
        if (PAN_dump_struct(vsb, vcl, VCL_MAGIC, "vcl[%s]", nm))
320 240
                return;
321 280
        VSB_printf(vsb, "name = \"%s\",\n", vcl->loaded_name);
322 280
        VSB_printf(vsb, "busy = %u,\n", vcl->busy);
323 280
        VSB_printf(vsb, "discard = %u,\n", vcl->discard);
324 280
        VSB_printf(vsb, "state = %s,\n", vcl->state);
325 280
        VSB_printf(vsb, "temp = %s,\n", vcl->temp ? vcl->temp->name : "(null)");
326 280
        vcl_panic_conf(vsb, vcl->conf);
327 280
        VSB_indent(vsb, -2);
328 280
        VSB_cat(vsb, "},\n");
329 520
}
330
331
/*--------------------------------------------------------------------*/
332
333
void
334 56210
VCL_Update(struct vcl **vcc, struct vcl *vcl)
335
{
336
        struct vcl *old;
337
338 56210
        AN(vcc);
339
340 56210
        old = *vcc;
341 56210
        *vcc = NULL;
342
343 56210
        CHECK_OBJ_ORNULL(old, VCL_MAGIC);
344 56210
        ASSERT_VCL_ACTIVE();
345
346 56210
        Lck_Lock(&vcl_mtx);
347 56210
        if (old != NULL) {
348 2821
                assert(old->busy > 0);
349 2821
                old->busy--;
350 2821
        }
351
352 56210
        if (vcl == NULL)
353 55690
                vcl = vcl_active; /* Sample vcl_active under lock to avoid
354
                                   * race */
355 56210
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
356 56210
        if (vcl->label == NULL) {
357 55570
                AN(strcmp(vcl->state, VCL_TEMP_LABEL->name));
358 55570
                *vcc = vcl;
359 55570
        } else {
360 640
                AZ(strcmp(vcl->state, VCL_TEMP_LABEL->name));
361 640
                *vcc = vcl->label;
362
        }
363 56210
        CHECK_OBJ_NOTNULL(*vcc, VCL_MAGIC);
364 56210
        AZ((*vcc)->discard);
365 56210
        (*vcc)->busy++;
366 56210
        Lck_Unlock(&vcl_mtx);
367 56210
        assert((*vcc)->temp->is_warm);
368 56210
}
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 96320
vdire_new(struct lock *mtx, const struct vcltemp **tempp)
380
{
381
        struct vdire *vdire;
382
383 96320
        ALLOC_OBJ(vdire, VDIRE_MAGIC);
384 96320
        AN(vdire);
385 96320
        AN(mtx);
386 96320
        VTAILQ_INIT(&vdire->directors);
387 96320
        VTAILQ_INIT(&vdire->resigning);
388 96320
        vdire->mtx = mtx;
389 96320
        PTOK(pthread_cond_init(&vdire->cond, NULL));
390 96320
        vdire->tempp = tempp;
391 96320
        return (vdire);
392
}
393
394
/* starting an interation prevents removals from the directors list */
395
void
396 89261
vdire_start_iter(struct vdire *vdire)
397
{
398
399 89261
        CHECK_OBJ_NOTNULL(vdire, VDIRE_MAGIC);
400
401
        // https://github.com/varnishcache/varnish-cache/pull/4142#issuecomment-2593091097
402 89261
        ASSERT_CLI();
403
404 89261
        Lck_Lock(vdire->mtx);
405 89261
        while (! VTAILQ_EMPTY(&vdire->resigning))
406 0
                (void)Lck_CondWait(&vdire->cond, vdire->mtx);
407 89261
        vdire->iterating++;
408 89261
        Lck_Unlock(vdire->mtx);
409 89261
}
410
411
void
412 89261
vdire_end_iter(struct vdire *vdire)
413
{
414 89261
        struct vcldir_head resigning = VTAILQ_HEAD_INITIALIZER(resigning);
415 89261
        const struct vcltemp *temp = NULL;
416
        struct vcldir *vdir, *next;
417
        unsigned n;
418
419 89261
        CHECK_OBJ_NOTNULL(vdire, VDIRE_MAGIC);
420
421 89261
        Lck_Lock(vdire->mtx);
422 89261
        AN(vdire->iterating);
423 89261
        n = --vdire->iterating;
424
425 89261
        if (n == 0) {
426 89261
                VTAILQ_SWAP(&vdire->resigning, &resigning, vcldir, resigning_list);
427 89261
                VTAILQ_FOREACH(vdir, &resigning, resigning_list)
428 0
                        VTAILQ_REMOVE(&vdire->directors, vdir, directors_list);
429 89261
                temp = *vdire->tempp;
430 89261
                PTOK(pthread_cond_broadcast(&vdire->cond));
431 89261
        }
432 89261
        Lck_Unlock(vdire->mtx);
433
434 89261
        VTAILQ_FOREACH_SAFE(vdir, &resigning, resigning_list, next)
435 0
                vcldir_retire(vdir, temp);
436 89261
}
437
438
// if there are no iterators, remove from directors and retire
439
// otherwise put on resigning list to work when iterators end
440
void
441 4327
vdire_resign(struct vdire *vdire, struct vcldir *vdir)
442
{
443 4327
        const struct vcltemp *temp = NULL;
444
445 4327
        CHECK_OBJ_NOTNULL(vdire, VDIRE_MAGIC);
446 4327
        AN(vdir);
447
448 4327
        Lck_Lock(vdire->mtx);
449 4327
        if (vdire->iterating != 0) {
450 0
                VTAILQ_INSERT_TAIL(&vdire->resigning, vdir, resigning_list);
451 0
                vdir = NULL;
452 0
        } else {
453 4327
                VTAILQ_REMOVE(&vdire->directors, vdir, directors_list);
454 4327
                temp = *vdire->tempp;
455
        }
456 4327
        Lck_Unlock(vdire->mtx);
457
458 4327
        if (vdir != NULL)
459 4327
                vcldir_retire(vdir, temp);
460 4327
}
461
462
// unlocked version of vcl_iterdir
463
// pat can also be NULL (to iterate all)
464
static int
465 37960
vdire_iter(struct cli *cli, const char *pat, const struct vcl *vcl,
466
    vcl_be_func *func, void *priv)
467
{
468 37960
        int i, found = 0;
469
        struct vcldir *vdir;
470 37960
        struct vdire *vdire = vcl->vdire;
471
472 37960
        vdire_start_iter(vdire);
473 93440
        VTAILQ_FOREACH(vdir, &vdire->directors, directors_list) {
474 55480
                if (vdir->refcnt == 0)
475 0
                        continue;
476 55480
                if (pat != NULL && fnmatch(pat, vdir->cli_name, 0))
477 3720
                        continue;
478 51760
                found++;
479 51760
                i = func(cli, vdir->dir, priv);
480 51760
                if (i < 0) {
481 0
                        found = i;
482 0
                        break;
483
                }
484 51760
                found += i;
485 51760
        }
486 37960
        vdire_end_iter(vdire);
487 37960
        return (found);
488
}
489
490
491
/*--------------------------------------------------------------------*/
492
493
// XXX locked case across VCLs - should do without
494
static int
495 240
vcl_iterdir(struct cli *cli, const char *pat, const struct vcl *vcl,
496
    vcl_be_func *func, void *priv)
497
{
498 240
        int i, found = 0;
499
        struct vcldir *vdir;
500
501 240
        Lck_AssertHeld(&vcl_mtx);
502 600
        VTAILQ_FOREACH(vdir, &vcl->vdire->directors, directors_list) {
503 360
                if (fnmatch(pat, vdir->cli_name, 0))
504 160
                        continue;
505 200
                found++;
506 200
                i = func(cli, vdir->dir, priv);
507 200
                if (i < 0)
508 0
                        return (i);
509 200
                found += i;
510 200
        }
511 240
        return (found);
512 240
}
513
514
int
515 38200
VCL_IterDirector(struct cli *cli, const char *pat,
516
    vcl_be_func *func, void *priv)
517
{
518 38200
        int i, found = 0;
519
        struct vsb *vsb;
520
        struct vcl *vcl;
521
522 38200
        ASSERT_CLI();
523 38200
        ASSERT_VCL_ACTIVE();
524 38200
        vsb = VSB_new_auto();
525 38200
        AN(vsb);
526 38200
        if (pat == NULL || *pat == '\0' || !strcmp(pat, "*")) {
527
                // all backends in active VCL
528 36440
                VSB_printf(vsb, "%s.*", VCL_Name(vcl_active));
529 36440
                vcl = vcl_active;
530 38200
        } else if (strchr(pat, '.') == NULL) {
531
                // pattern applies to active vcl
532 1520
                VSB_printf(vsb, "%s.%s", VCL_Name(vcl_active), pat);
533 1520
                vcl = vcl_active;
534 1520
        } else {
535
                // use pattern as is
536 240
                VSB_cat(vsb, pat);
537 240
                vcl = NULL;
538
        }
539 38200
        AZ(VSB_finish(vsb));
540 38200
        if (vcl != NULL) {
541 37960
                found = vdire_iter(cli, VSB_data(vsb), vcl, func, priv);
542 37960
        } else {
543 240
                Lck_Lock(&vcl_mtx);
544 480
                VTAILQ_FOREACH(vcl, &vcl_head, list) {
545 240
                        i = vcl_iterdir(cli, VSB_data(vsb), vcl, func, priv);
546 240
                        if (i < 0) {
547 0
                                found = i;
548 0
                                break;
549
                        } else {
550 240
                                found += i;
551
                        }
552 240
                }
553 240
                Lck_Unlock(&vcl_mtx);
554
        }
555 38200
        VSB_destroy(&vsb);
556 38200
        return (found);
557
}
558
559
static void
560 48167
vcl_BackendEvent(const struct vcl *vcl, enum vcl_event_e e)
561
{
562
        struct vcldir *vdir;
563
        struct vdire *vdire;
564
565 48167
        ASSERT_CLI();
566 48167
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
567 48167
        AZ(vcl->busy);
568 48167
        vdire = vcl->vdire;
569
570 48167
        vdire_start_iter(vdire);
571 109207
        VTAILQ_FOREACH(vdir, &vdire->directors, directors_list)
572 61040
                VDI_Event(vdir->dir, e);
573 48167
        vdire_end_iter(vdire);
574 48167
}
575
576
static void
577 3094
vcl_KillBackends(const struct vcl *vcl)
578
{
579
        struct vcldir *vdir, *vdir2;
580
        struct vdire *vdire;
581
582 3094
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
583 3094
        assert(vcl->temp == VCL_TEMP_COLD || vcl->temp == VCL_TEMP_INIT);
584 3094
        vdire = vcl->vdire;
585 3094
        CHECK_OBJ_NOTNULL(vdire, VDIRE_MAGIC);
586
587
        /*
588
         * ensure all retirement has finished (vdire_start_iter waits for it)
589
         */
590 3094
        vdire_start_iter(vdire);
591 3094
        vdire_end_iter(vdire);
592 3094
        AZ(vdire->iterating);
593 3094
        assert(VTAILQ_EMPTY(&vdire->resigning));
594
595
        /*
596
         * Unlocked and sidelining vdire because no further directors can be added, and the
597
         * remaining ones need to be able to remove themselves.
598
         */
599 5748
        VTAILQ_FOREACH_SAFE(vdir, &vdire->directors, directors_list, vdir2)
600 2654
                VDI_Event(vdir->dir, VCL_EVENT_DISCARD);
601 3094
        assert(VTAILQ_EMPTY(&vdire->directors));
602 3094
}
603
604
/*--------------------------------------------------------------------*/
605
606
static struct vcl *
607 96400
VCL_Open(const char *fn, struct vsb *msg)
608
{
609
        struct vcl *vcl;
610
        void *dlh;
611
        struct VCL_conf const *cnf;
612
        const char *dlerr;
613
        int err;
614
615 96400
        AN(fn);
616 96400
        AN(msg);
617
618
#ifdef RTLD_NOLOAD
619
        /* Detect bogus caching by dlopen(3) */
620 96400
        dlh = dlopen(fn, RTLD_NOW | RTLD_NOLOAD);
621 96400
        AZ(dlh);
622
#endif
623 96400
        dlh = dlopen(fn, RTLD_NOW | RTLD_LOCAL);
624 96400
        if (dlh == NULL) {
625 40
                err = errno;
626 40
                dlerr = dlerror();
627 40
                VSB_cat(msg, "Could not load compiled VCL.\n");
628 40
                if (dlerr != NULL)
629 40
                        VSB_printf(msg, "\tdlopen() = %s\n", dlerr);
630 40
                if (err) {
631 0
                        VSB_printf(msg, "\terror = %s (%d)\n",
632 0
                            strerror(err), err);
633 0
                }
634 40
                VSB_cat(msg, "\thint: check for \"noexec\" mount\n");
635 40
                VSB_cat(msg, "\thint: check \"vmod_path\" parameter\n");
636 40
                return (NULL);
637
        }
638 96360
        cnf = dlsym(dlh, "VCL_conf");
639 96360
        if (cnf == NULL) {
640 0
                VSB_cat(msg, "Compiled VCL lacks metadata.\n");
641 0
                (void)dlclose(dlh);
642 0
                return (NULL);
643
        }
644 96360
        if (cnf->magic != VCL_CONF_MAGIC) {
645 0
                VSB_cat(msg, "Compiled VCL has mangled metadata.\n");
646 0
                (void)dlclose(dlh);
647 0
                return (NULL);
648
        }
649 96360
        if (cnf->syntax < heritage.min_vcl_version ||
650 96320
            cnf->syntax > heritage.max_vcl_version) {
651 80
                VSB_printf(msg, "Compiled VCL version (%.1f) not supported.\n",
652 40
                    .1 * cnf->syntax);
653 40
                (void)dlclose(dlh);
654 40
                return (NULL);
655
        }
656 96320
        ALLOC_OBJ(vcl, VCL_MAGIC);
657 96320
        AN(vcl);
658 96320
        vcl->dlh = dlh;
659 96320
        vcl->conf = cnf;
660 96320
        vcl->vdire = vdire_new(&vcl_mtx, &vcl->temp);
661 96320
        return (vcl);
662 96400
}
663
664
static void
665 51294
VCL_Close(struct vcl **vclp)
666
{
667
        struct vcl *vcl;
668
669 51294
        TAKE_OBJ_NOTNULL(vcl, vclp, VCL_MAGIC);
670 51294
        assert(VTAILQ_EMPTY(&vcl->filters));
671 51294
        AZ(dlclose(vcl->dlh));
672 51294
        PTOK(pthread_cond_destroy(&vcl->vdire->cond));
673 51294
        FREE_OBJ(vcl->vdire);
674 51294
        FREE_OBJ(vcl);
675 51294
}
676
677
/*--------------------------------------------------------------------
678
 * NB: This function is called in/from the test-load subprocess.
679
 */
680
681
int
682 48280
VCL_TestLoad(const char *fn)
683
{
684
        struct vsb *vsb;
685
        struct vcl *vcl;
686 48280
        int retval = 0;
687
688 48280
        AN(fn);
689 48280
        vsb = VSB_new_auto();
690 48280
        AN(vsb);
691 48280
        vcl = VCL_Open(fn, vsb);
692 48280
        if (vcl == NULL) {
693 80
                AZ(VSB_finish(vsb));
694 80
                fprintf(stderr, "%s", VSB_data(vsb));
695 80
                retval = -1;
696 80
        } else
697 48200
                VCL_Close(&vcl);
698 48280
        VSB_destroy(&vsb);
699 48280
        return (retval);
700
}
701
702
/*--------------------------------------------------------------------*/
703
704
static struct vsb *
705 40
vcl_print_refs(const struct vcl *vcl)
706
{
707
        struct vsb *msg;
708
        struct vclref *ref;
709
710 40
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
711 40
        msg = VSB_new_auto();
712 40
        AN(msg);
713
714 40
        VSB_printf(msg, "VCL %s is waiting for:", vcl->loaded_name);
715 40
        Lck_Lock(&vcl_mtx);
716 80
        VTAILQ_FOREACH(ref, &vcl->ref_list, list)
717 40
                VSB_printf(msg, "\n\t- %s", ref->desc);
718 40
        Lck_Unlock(&vcl_mtx);
719 40
        AZ(VSB_finish(msg));
720 40
        return (msg);
721
}
722
723
static int
724 50533
vcl_set_state(struct vcl *vcl, const char *state, struct vsb **msg)
725
{
726 50533
        struct vsb *nomsg = NULL;
727 50533
        int i = 0;
728
729 50533
        ASSERT_CLI();
730
731 50533
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
732 50533
        AN(state);
733 50533
        AN(msg);
734 50533
        AZ(*msg);
735
736 50533
        AN(vcl->temp);
737
738 50533
        switch (state[0]) {
739
        case '0':
740 3893
                if (vcl->temp == VCL_TEMP_COLD)
741 80
                        break;
742 3813
                if (vcl->busy == 0 && vcl->temp->is_warm) {
743 1727
                        Lck_Lock(&vcl_mtx);
744 1727
                        vcl->temp = VTAILQ_EMPTY(&vcl->ref_list) ?
745
                            VCL_TEMP_COLD : VCL_TEMP_COOLING;
746 1727
                        Lck_Unlock(&vcl_mtx);
747 1727
                        vcl_BackendEvent(vcl, VCL_EVENT_COLD);
748 1727
                        AZ(vcl_send_event(vcl, VCL_EVENT_COLD, msg));
749 1727
                        AZ(*msg);
750 1727
                }
751 2086
                else if (vcl->busy)
752 1213
                        vcl->temp = VCL_TEMP_BUSY;
753 873
                else if (VTAILQ_EMPTY(&vcl->ref_list))
754 713
                        vcl->temp = VCL_TEMP_COLD;
755
                else
756 160
                        vcl->temp = VCL_TEMP_COOLING;
757 3813
                break;
758
        case '1':
759 46640
                if (vcl->temp == VCL_TEMP_WARM)
760 40
                        break;
761
                /* The warm VCL hasn't seen a cold event yet */
762 46600
                if (vcl->temp == VCL_TEMP_BUSY)
763 40
                        vcl->temp = VCL_TEMP_WARM;
764
                /* The VCL must first reach a stable cold state */
765 46560
                else if (vcl->temp == VCL_TEMP_COOLING) {
766 40
                        *msg = vcl_print_refs(vcl);
767 40
                        i = -1;
768 40
                }
769
                else {
770 46520
                        Lck_Lock(&vcl_mtx);
771 46520
                        vcl->temp = VCL_TEMP_WARM;
772 46520
                        Lck_Unlock(&vcl_mtx);
773 46520
                        i = vcl_send_event(vcl, VCL_EVENT_WARM, msg);
774 46520
                        if (i == 0) {
775 46400
                                vcl_BackendEvent(vcl, VCL_EVENT_WARM);
776 46400
                                break;
777
                        }
778 120
                        AZ(vcl_send_event(vcl, VCL_EVENT_COLD, &nomsg));
779 120
                        AZ(nomsg);
780 120
                        vcl->temp = VCL_TEMP_COLD;
781
                }
782 200
                break;
783
        default:
784 0
                WRONG("Wrong enum state");
785 0
        }
786 50533
        if (i == 0 && state[1])
787 48560
                bprintf(vcl->state, "%s", state + 1);
788
789 50533
        return (i);
790
}
791
792
static void
793 1720
vcl_cancel_load(struct vcl *vcl, struct cli *cli, struct vsb *msg,
794
    const char *name, const char *step)
795
{
796
797 1720
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
798 1720
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
799
800 1720
        VCLI_SetResult(cli, CLIS_CANT);
801 1720
        VCLI_Out(cli, "VCL \"%s\" Failed %s", name, step);
802 1720
        if (VSB_len(msg))
803 1720
                VCLI_Out(cli, "\nMessage:\n\t%s", VSB_data(msg));
804 1720
        VSB_destroy(&msg);
805
806 1720
        AZ(vcl_send_event(vcl, VCL_EVENT_DISCARD, &msg));
807 1720
        AZ(msg);
808
809 1720
        vcl_KillBackends(vcl);
810 1720
        free(vcl->loaded_name);
811 1720
        VCL_Close(&vcl);
812 1720
}
813
814
static void
815 48120
vcl_load(struct cli *cli,
816
    const char *name, const char *fn, const char *state)
817
{
818
        struct vcl *vcl;
819
        struct vsb *msg;
820
821 48120
        ASSERT_CLI();
822 48120
        ASSERT_VCL_ACTIVE();
823
824 48120
        vcl = vcl_find(name);
825 48120
        AZ(vcl);
826
827 48120
        msg = VSB_new_auto();
828 48120
        AN(msg);
829 48120
        vcl = VCL_Open(fn, msg);
830 48120
        AZ(VSB_finish(msg));
831 48120
        if (vcl == NULL) {
832 0
                VCLI_SetResult(cli, CLIS_PARAM);
833 0
                VCLI_Out(cli, "%s", VSB_data(msg));
834 0
                VSB_destroy(&msg);
835 0
                return;
836
        }
837
838 48120
        VSB_destroy(&msg);
839
840 48120
        vcl->loaded_name = strdup(name);
841 48120
        XXXAN(vcl->loaded_name);
842 48120
        VTAILQ_INIT(&vcl->ref_list);
843 48120
        VTAILQ_INIT(&vcl->filters);
844
845 48120
        vcl->temp = VCL_TEMP_INIT;
846
847 48120
        if (vcl_send_event(vcl, VCL_EVENT_LOAD, &msg)) {
848 1680
                vcl_cancel_load(vcl, cli, msg, name, "initialization");
849 1680
                return;
850
        }
851 46440
        VSB_destroy(&msg);
852
853 46440
        if (vcl_set_state(vcl, state, &msg)) {
854 40
                assert(*state == '1');
855 40
                vcl_cancel_load(vcl, cli, msg, name, "warmup");
856 40
                return;
857
        }
858 46400
        if (msg)
859 46240
                VSB_destroy(&msg);
860
861 46400
        VCLI_Out(cli, "Loaded \"%s\" as \"%s\"", fn , name);
862 46400
        VTAILQ_INSERT_TAIL(&vcl_head, vcl, list);
863 46400
        VSC_C_main->n_vcl++;
864 46400
        VSC_C_main->n_vcl_avail++;
865 48120
}
866
867
/*--------------------------------------------------------------------*/
868
869
void
870 303757
VCL_Poll(void)
871
{
872 303757
        struct vsb *nomsg = NULL;
873
        struct vcl *vcl, *vcl2;
874
875 303757
        ASSERT_CLI();
876 303757
        ASSERT_VCL_ACTIVE();
877 639497
        VTAILQ_FOREACH_SAFE(vcl, &vcl_head, list, vcl2) {
878 335740
                if (vcl->temp == VCL_TEMP_BUSY ||
879 334640
                    vcl->temp == VCL_TEMP_COOLING)
880 1813
                        AZ(vcl_set_state(vcl, "0", &nomsg));
881 335740
                AZ(nomsg);
882 335740
                if (vcl->discard && vcl->temp == VCL_TEMP_COLD) {
883 1374
                        AZ(vcl->busy);
884 1374
                        assert(vcl != vcl_active);
885 1374
                        assert(VTAILQ_EMPTY(&vcl->ref_list));
886 1374
                        VTAILQ_REMOVE(&vcl_head, vcl, list);
887 1374
                        AZ(vcl_send_event(vcl, VCL_EVENT_DISCARD, &nomsg));
888 1374
                        AZ(nomsg);
889 1374
                        vcl_KillBackends(vcl);
890 1374
                        free(vcl->loaded_name);
891 1374
                        VCL_Close(&vcl);
892 1374
                        VSC_C_main->n_vcl--;
893 1374
                        VSC_C_main->n_vcl_discard--;
894 1374
                }
895 335740
        }
896 303757
}
897
898
/*--------------------------------------------------------------------*/
899
900
static void v_matchproto_(cli_func_t)
901 3000
vcl_cli_list(struct cli *cli, const char * const *av, void *priv)
902
{
903
        struct vcl *vcl;
904
        const char *flg;
905
        struct vte *vte;
906
907
        /* NB: Shall generate same output as mcf_vcl_list() */
908
909 3000
        (void)av;
910 3000
        (void)priv;
911 3000
        ASSERT_CLI();
912 3000
        ASSERT_VCL_ACTIVE();
913 3000
        vte = VTE_new(7, 80);
914 3000
        AN(vte);
915 9614
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
916 6614
                if (vcl == vcl_active) {
917 3000
                        flg = "active";
918 6614
                } else if (vcl->discard) {
919 174
                        flg = "discarded";
920 174
                } else
921 3440
                        flg = "available";
922 13228
                VTE_printf(vte, "%s\t%s\t%s\t\v%u\t%s", flg, vcl->state,
923 6614
                    vcl->temp->name, vcl->busy, vcl->loaded_name);
924 6614
                if (vcl->label != NULL) {
925 760
                        VTE_printf(vte, "\t->\t%s", vcl->label->loaded_name);
926 760
                        if (vcl->nrefs)
927 320
                                VTE_printf(vte, " (%d return(vcl)%s)",
928 160
                                    vcl->nrefs, vcl->nrefs > 1 ? "'s" : "");
929 6614
                } else if (vcl->nlabels > 0) {
930 1040
                        VTE_printf(vte, "\t<-\t(%d label%s)",
931 520
                            vcl->nlabels, vcl->nlabels > 1 ? "s" : "");
932 520
                }
933 6614
                VTE_cat(vte, "\n");
934 6614
        }
935 3000
        AZ(VTE_finish(vte));
936 3000
        AZ(VTE_format(vte, VCLI_VTE_format, cli));
937 3000
        VTE_destroy(&vte);
938 3000
}
939
940
static void v_matchproto_(cli_func_t)
941 200
vcl_cli_list_json(struct cli *cli, const char * const *av, void *priv)
942
{
943
        struct vcl *vcl;
944
945 200
        (void)priv;
946 200
        ASSERT_CLI();
947 200
        ASSERT_VCL_ACTIVE();
948 200
        VCLI_JSON_begin(cli, 2, av);
949 200
        VCLI_Out(cli, ",\n");
950 840
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
951 640
                VCLI_Out(cli, "{\n");
952 640
                VSB_indent(cli->sb, 2);
953 640
                VCLI_Out(cli, "\"status\": ");
954 640
                if (vcl == vcl_active) {
955 200
                        VCLI_Out(cli, "\"active\",\n");
956 640
                } else if (vcl->discard) {
957 0
                        VCLI_Out(cli, "\"discarded\",\n");
958 0
                } else
959 440
                        VCLI_Out(cli, "\"available\",\n");
960 640
                VCLI_Out(cli, "\"state\": \"%s\",\n", vcl->state);
961 640
                VCLI_Out(cli, "\"temperature\": \"%s\",\n", vcl->temp->name);
962 640
                VCLI_Out(cli, "\"busy\": %u,\n", vcl->busy);
963 640
                VCLI_Out(cli, "\"name\": \"%s\"", vcl->loaded_name);
964 640
                if (vcl->label != NULL) {
965 240
                        VCLI_Out(cli, ",\n");
966 240
                        VCLI_Out(cli, "\"label\": {\n");
967 240
                        VSB_indent(cli->sb, 2);
968 480
                                VCLI_Out(cli, "\"name\": \"%s\"",
969 240
                                         vcl->label->loaded_name);
970 240
                        if (vcl->nrefs)
971 0
                                VCLI_Out(cli, ",\n\"refs\": %d", vcl->nrefs);
972 240
                        VCLI_Out(cli, "\n");
973 240
                        VCLI_Out(cli, "}");
974 240
                        VSB_indent(cli->sb, -2);
975 640
                } else if (vcl->nlabels > 0) {
976 80
                        VCLI_Out(cli, ",\n");
977 80
                        VCLI_Out(cli, "\"labels\": %d", vcl->nlabels);
978 80
                }
979 640
                VSB_indent(cli->sb, -2);
980 640
                VCLI_Out(cli, "\n}");
981 640
                if (VTAILQ_NEXT(vcl, list) != NULL)
982 440
                        VCLI_Out(cli, ",\n");
983 640
        }
984 200
        VCLI_JSON_end(cli);
985 200
}
986
987
static void v_matchproto_(cli_func_t)
988 48120
vcl_cli_load(struct cli *cli, const char * const *av, void *priv)
989
{
990
991 48120
        AZ(priv);
992 48120
        ASSERT_CLI();
993
        // XXX move back code from vcl_load?
994 48120
        vcl_load(cli, av[2], av[3], av[4]);
995 48120
}
996
997
static void v_matchproto_(cli_func_t)
998 2320
vcl_cli_state(struct cli *cli, const char * const *av, void *priv)
999
{
1000
        struct vcl *vcl;
1001 2320
        struct vsb *msg = NULL;
1002
1003 2320
        AZ(priv);
1004 2320
        ASSERT_CLI();
1005 2320
        ASSERT_VCL_ACTIVE();
1006 2320
        AN(av[2]);
1007 2320
        AN(av[3]);
1008
1009 2320
        vcl = vcl_find(av[2]);
1010 2320
        AN(vcl);
1011
1012 2320
        if (vcl_set_state(vcl, av[3], &msg)) {
1013 120
                CHECK_OBJ_NOTNULL(msg, VSB_MAGIC);
1014
1015 120
                VCLI_SetResult(cli, CLIS_CANT);
1016 240
                VCLI_Out(cli, "Failed <vcl.state %s %s>", vcl->loaded_name,
1017 120
                    av[3] + 1);
1018 120
                if (VSB_len(msg))
1019 120
                        VCLI_Out(cli, "\nMessage:\n\t%s", VSB_data(msg));
1020 120
        }
1021 2320
        if (msg)
1022 280
                VSB_destroy(&msg);
1023 2320
}
1024
1025
static void v_matchproto_(cli_func_t)
1026 1560
vcl_cli_discard(struct cli *cli, const char * const *av, void *priv)
1027
{
1028
        struct vcl *vcl;
1029
1030 1560
        ASSERT_CLI();
1031 1560
        ASSERT_VCL_ACTIVE();
1032 1560
        (void)cli;
1033 1560
        AZ(priv);
1034 1560
        vcl = vcl_find(av[2]);
1035 1560
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);              // MGT ensures this
1036 1560
        Lck_Lock(&vcl_mtx);
1037 1560
        assert (vcl != vcl_active);     // MGT ensures this
1038 1560
        AZ(vcl->nlabels);               // MGT ensures this
1039 1560
        VSC_C_main->n_vcl_discard++;
1040 1560
        VSC_C_main->n_vcl_avail--;
1041 1560
        vcl->discard = 1;
1042 1560
        if (vcl->label != NULL) {
1043 160
                AZ(strcmp(vcl->state, VCL_TEMP_LABEL->name));
1044 160
                vcl->label->nlabels--;
1045 160
                vcl->label= NULL;
1046 160
        }
1047 1560
        Lck_Unlock(&vcl_mtx);
1048
1049 1560
        if (!strcmp(vcl->state, VCL_TEMP_LABEL->name)) {
1050 160
                VTAILQ_REMOVE(&vcl_head, vcl, list);
1051 160
                free(vcl->loaded_name);
1052 160
                AZ(vcl->vdire);
1053 160
                FREE_OBJ(vcl);
1054 1560
        } else if (vcl->temp == VCL_TEMP_COLD) {
1055 1043
                VCL_Poll();
1056 1043
        }
1057 1560
}
1058
1059
static void v_matchproto_(cli_func_t)
1060 1080
vcl_cli_label(struct cli *cli, const char * const *av, void *priv)
1061
{
1062
        struct vcl *lbl;
1063
        struct vcl *vcl;
1064
1065 1080
        ASSERT_CLI();
1066 1080
        ASSERT_VCL_ACTIVE();
1067 1080
        (void)cli;
1068 1080
        (void)priv;
1069 1080
        vcl = vcl_find(av[3]);
1070 1080
        AN(vcl);                                // MGT ensures this
1071 1080
        lbl = vcl_find(av[2]);
1072 1080
        if (lbl == NULL) {
1073 960
                ALLOC_OBJ(lbl, VCL_MAGIC);
1074 960
                AN(lbl);
1075 960
                bprintf(lbl->state, "%s", VCL_TEMP_LABEL->name);
1076 960
                lbl->temp = VCL_TEMP_WARM;
1077 960
                REPLACE(lbl->loaded_name, av[2]);
1078 960
                VTAILQ_INSERT_TAIL(&vcl_head, lbl, list);
1079 960
        }
1080 1080
        if (lbl->label != NULL)
1081 120
                lbl->label->nlabels--;
1082 1080
        lbl->label = vcl;
1083 1080
        vcl->nlabels++;
1084 1080
}
1085
1086
static void v_matchproto_(cli_func_t)
1087 44680
vcl_cli_use(struct cli *cli, const char * const *av, void *priv)
1088
{
1089
        struct vcl *vcl;
1090
1091 44680
        ASSERT_CLI();
1092 44680
        ASSERT_VCL_ACTIVE();
1093 44680
        AN(cli);
1094 44680
        AZ(priv);
1095 44680
        vcl = vcl_find(av[2]);
1096 44680
        AN(vcl);                                // MGT ensures this
1097 44680
        assert(vcl->temp == VCL_TEMP_WARM);     // MGT ensures this
1098 44680
        Lck_Lock(&vcl_mtx);
1099 44680
        vcl_active = vcl;
1100 44680
        Lck_Unlock(&vcl_mtx);
1101 44680
}
1102
1103
static void v_matchproto_(cli_func_t)
1104 480
vcl_cli_show(struct cli *cli, const char * const *av, void *priv)
1105
{
1106
        struct vcl *vcl;
1107 480
        int verbose = 0;
1108 480
        int i = 2;
1109
        unsigned u;
1110
1111 480
        ASSERT_CLI();
1112 480
        ASSERT_VCL_ACTIVE();
1113 480
        AZ(priv);
1114
1115 480
        if (av[i] != NULL && !strcmp(av[i], "-v")) {
1116 160
                verbose = 1;
1117 160
                i++;
1118 160
        }
1119
1120 480
        if (av[i] == NULL) {
1121 80
                vcl = vcl_active;
1122 80
                AN(vcl);
1123 80
        } else {
1124 400
                vcl = vcl_find(av[i]);
1125 400
                i++;
1126
        }
1127
1128 480
        if (av[i] != NULL) {
1129 40
                VCLI_Out(cli, "Too many parameters: '%s'", av[i]);
1130 40
                VCLI_SetResult(cli, CLIS_PARAM);
1131 40
                return;
1132
        }
1133
1134 440
        if (vcl == NULL) {
1135 40
                VCLI_Out(cli, "No VCL named '%s'", av[i - 1]);
1136 40
                VCLI_SetResult(cli, CLIS_PARAM);
1137 40
                return;
1138
        }
1139 400
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
1140 400
        if (vcl->label) {
1141 80
                vcl = vcl->label;
1142 80
                CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
1143 80
                AZ(vcl->label);
1144 80
        }
1145 400
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
1146 400
        if (verbose) {
1147 480
                for (u = 0; u < vcl->conf->nsrc; u++)
1148 640
                        VCLI_Out(cli, "// VCL.SHOW %u %zd %s\n%s\n",
1149 320
                            u, strlen(vcl->conf->srcbody[u]),
1150 320
                            vcl->conf->srcname[u],
1151 320
                            vcl->conf->srcbody[u]);
1152 160
        } else {
1153 240
                VCLI_Out(cli, "%s", vcl->conf->srcbody[0]);
1154
        }
1155 480
}
1156
1157
/*--------------------------------------------------------------------*/
1158
1159
static struct cli_proto vcl_cmds[] = {
1160
        { CLICMD_VCL_LOAD,              "", vcl_cli_load },
1161
        { CLICMD_VCL_LIST,              "", vcl_cli_list, vcl_cli_list_json },
1162
        { CLICMD_VCL_STATE,             "", vcl_cli_state },
1163
        { CLICMD_VCL_DISCARD,           "", vcl_cli_discard },
1164
        { CLICMD_VCL_USE,               "", vcl_cli_use },
1165
        { CLICMD_VCL_SHOW,              "", vcl_cli_show },
1166
        { CLICMD_VCL_LABEL,             "", vcl_cli_label },
1167
        { NULL }
1168
};
1169
1170
void
1171 37152
VCL_Init(void)
1172
{
1173
1174 37152
        assert(cache_param->workspace_client > 0);
1175 74304
        WS_Init(&ws_cli, "cli", malloc(cache_param->workspace_client),
1176 37152
            cache_param->workspace_client);
1177 37152
        ws_snapshot_cli = WS_Snapshot(&ws_cli);
1178 37152
        CLI_AddFuncs(vcl_cmds);
1179 37152
        Lck_New(&vcl_mtx, lck_vcl);
1180 37152
        VSL_Setup(&vsl_cli, NULL, 0);
1181 37152
}