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