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 597983
VCL_Bo2Ctx(struct vrt_ctx *ctx, struct busyobj *bo)
80
{
81
82 597983
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
83 597983
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
84 597983
        CHECK_OBJ_NOTNULL(bo->wrk, WORKER_MAGIC);
85 597983
        ctx->vcl = bo->vcl;
86 597983
        ctx->syntax = ctx->vcl->conf->syntax;
87 597983
        ctx->vsl = bo->vsl;
88 597983
        ctx->http_bereq = bo->bereq;
89 597983
        ctx->http_beresp = bo->beresp;
90 597983
        ctx->bo = bo;
91 597983
        ctx->sp = bo->sp;
92 597983
        ctx->now = bo->t_prev;
93 597983
        ctx->ws = bo->ws;
94 597983
        ctx->vpi = bo->wrk->vpi;
95 597983
        ctx->vpi->handling = 0;
96 597983
        ctx->vpi->trace = bo->trace;
97 597983
}
98
99
void
100 957148
VCL_Req2Ctx(struct vrt_ctx *ctx, struct req *req)
101
{
102
103 957148
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
104 957148
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
105 957148
        CHECK_OBJ_NOTNULL(req->doclose, STREAM_CLOSE_MAGIC);
106
107 957148
        ctx->vcl = req->vcl;
108 957148
        ctx->syntax = ctx->vcl->conf->syntax;
109 957148
        ctx->vsl = req->vsl;
110 957148
        ctx->http_req = req->http;
111 957148
        CHECK_OBJ_NOTNULL(req->top, REQTOP_MAGIC);
112 957148
        ctx->http_req_top = req->top->topreq->http;
113 957148
        ctx->http_resp = req->resp;
114 957148
        ctx->req = req;
115 957148
        ctx->sp = req->sp;
116 957148
        ctx->now = req->t_prev;
117 957148
        ctx->ws = req->ws;
118 957148
        ctx->vpi = req->wrk->vpi;
119 957148
        ctx->vpi->handling = 0;
120 957148
        ctx->vpi->trace = req->trace;
121 957148
}
122
123
/*--------------------------------------------------------------------*/
124
125
struct vrt_ctx *
126 148398
VCL_Get_CliCtx(int msg)
127
{
128
129 148398
        ASSERT_CLI();
130 148398
        INIT_OBJ(&ctx_cli, VRT_CTX_MAGIC);
131 148398
        INIT_OBJ(&wrk_vpi_cli, WRK_VPI_MAGIC);
132 148398
        ctx_cli.vpi = &wrk_vpi_cli;
133 148398
        wrk_vpi_cli.trace = FEATURE(FEATURE_TRACE);
134 148398
        ctx_cli.now = VTIM_real();
135 148398
        if (msg) {
136 93200
                ctx_cli.msg = VSB_new_auto();
137 93200
                AN(ctx_cli.msg);
138 93200
        } else {
139 55198
                ctx_cli.vsl = &vsl_cli;
140
        }
141 148398
        ctx_cli.ws = &ws_cli;
142 148398
        WS_Assert(ctx_cli.ws);
143 148398
        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 148358
VCL_Rel_CliCtx(struct vrt_ctx **ctx)
156
{
157 148358
        struct vsb *r = NULL;
158
159 148358
        ASSERT_CLI();
160 148358
        assert(*ctx == &ctx_cli);
161 148358
        AN((*ctx)->vpi);
162 148358
        if (ctx_cli.msg) {
163 93200
                TAKE_OBJ_NOTNULL(r, &ctx_cli.msg, VSB_MAGIC);
164 93200
                AZ(VSB_finish(r));
165 93200
        }
166 148358
        if (ctx_cli.vsl)
167 55158
                VSL_Flush(ctx_cli.vsl, 0);
168 148358
        WS_Assert(ctx_cli.ws);
169 148358
        WS_Rollback(&ws_cli, ws_snapshot_cli);
170 148358
        INIT_OBJ(*ctx, VRT_CTX_MAGIC);
171 148358
        *ctx = NULL;
172
173 148358
        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 98158
vcl_event_handling(VRT_CTX)
189
{
190 98158
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
191
192 98158
        if (ctx->vpi->handling == 0)
193 96438
                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 98158
}
210
211
static int
212 98198
vcl_send_event(struct vcl *vcl, enum vcl_event_e ev, struct vsb **msg)
213
{
214
        int r, havemsg;
215 98198
        unsigned method = 0;
216
        struct vrt_ctx *ctx;
217
218 98198
        ASSERT_CLI();
219 98198
        ASSERT_VCL_ACTIVE();
220
221 98198
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
222 98198
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
223 98198
        AN(msg);
224 98198
        AZ(*msg);
225
226 98198
        switch (ev) {
227
        case VCL_EVENT_LOAD:
228 47400
                method = VCL_MET_INIT;
229
                /* FALLTHROUGH */
230
        case VCL_EVENT_WARM:
231 93200
                havemsg = 1;
232 93200
                break;
233
        case VCL_EVENT_DISCARD:
234 3101
                method = VCL_MET_FINI;
235
                /* FALLTHROUGH */
236
        case VCL_EVENT_COLD:
237 4998
                havemsg = 0;
238 4998
                break;
239
        default:
240 0
                WRONG("vcl_event");
241 0
        }
242
243 98198
        ctx = VCL_Get_CliCtx(havemsg);
244
245 98198
        AN(ctx->vpi);
246 98198
        AZ(ctx->vpi->handling);
247 98198
        AN(ctx->ws);
248
249 98198
        ctx->vcl = vcl;
250 98198
        ctx->syntax = ctx->vcl->conf->syntax;
251 98198
        ctx->method = method;
252
253 98198
        VCL_TaskEnter(cli_task_privs);
254 98198
        r = ctx->vcl->conf->event_vcl(ctx, ev);
255 98198
        VCL_TaskLeave(ctx, cli_task_privs);
256 98198
        r |= vcl_event_handling(ctx);
257
258 98198
        *msg = VCL_Rel_CliCtx(&ctx);
259
260 98198
        if (r && (ev == VCL_EVENT_COLD || ev == VCL_EVENT_DISCARD))
261 0
                WRONG("A VMOD cannot fail COLD or DISCARD events");
262
263 98198
        return (r);
264
}
265
266
/*--------------------------------------------------------------------*/
267
268
struct vcl *
269 98880
vcl_find(const char *name)
270
{
271
        struct vcl *vcl;
272
273 98880
        ASSERT_CLI();
274 149252
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
275 100812
                if (vcl->discard)
276 52
                        continue;
277 100760
                if (!strcmp(vcl->loaded_name, name))
278 50440
                        return (vcl);
279 50320
        }
280 48440
        return (NULL);
281 98880
}
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 54882
VCL_Update(struct vcl **vcc, struct vcl *vcl)
335
{
336
        struct vcl *old;
337
338 54882
        AN(vcc);
339
340 54882
        old = *vcc;
341 54882
        *vcc = NULL;
342
343 54882
        CHECK_OBJ_ORNULL(old, VCL_MAGIC);
344 54882
        ASSERT_VCL_ACTIVE();
345
346 54882
        Lck_Lock(&vcl_mtx);
347 54882
        if (old != NULL) {
348 2779
                assert(old->busy > 0);
349 2779
                old->busy--;
350 2779
        }
351
352 54882
        if (vcl == NULL)
353 54362
                vcl = vcl_active; /* Sample vcl_active under lock to avoid
354
                                   * race */
355 54882
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
356 54882
        if (vcl->label == NULL) {
357 54242
                AN(strcmp(vcl->state, VCL_TEMP_LABEL->name));
358 54242
                *vcc = vcl;
359 54242
        } else {
360 640
                AZ(strcmp(vcl->state, VCL_TEMP_LABEL->name));
361 640
                *vcc = vcl->label;
362
        }
363 54882
        CHECK_OBJ_NOTNULL(*vcc, VCL_MAGIC);
364 54882
        AZ((*vcc)->discard);
365 54882
        (*vcc)->busy++;
366 54882
        Lck_Unlock(&vcl_mtx);
367 54882
        assert((*vcc)->temp->is_warm);
368 54882
}
369
370
/*--------------------------------------------------------------------*/
371
372
static int
373 37840
vcl_iterdir(struct cli *cli, const char *pat, const struct vcl *vcl,
374
    vcl_be_func *func, void *priv)
375
{
376 37840
        int i, found = 0;
377
        struct vcldir *vdir;
378
379 37840
        Lck_AssertHeld(&vcl_mtx);
380 93320
        VTAILQ_FOREACH(vdir, &vcl->director_list, list) {
381 55480
                if (fnmatch(pat, vdir->cli_name, 0))
382 3880
                        continue;
383 51600
                found++;
384 51600
                i = func(cli, vdir->dir, priv);
385 51600
                if (i < 0)
386 0
                        return (i);
387 51600
                found += i;
388 51600
        }
389 37840
        return (found);
390 37840
}
391
392
int
393 37840
VCL_IterDirector(struct cli *cli, const char *pat,
394
    vcl_be_func *func, void *priv)
395
{
396 37840
        int i, found = 0;
397
        struct vsb *vsb;
398
        struct vcl *vcl;
399
400 37840
        ASSERT_CLI();
401 37840
        ASSERT_VCL_ACTIVE();
402 37840
        vsb = VSB_new_auto();
403 37840
        AN(vsb);
404 37840
        if (pat == NULL || *pat == '\0' || !strcmp(pat, "*")) {
405
                // all backends in active VCL
406 36080
                VSB_printf(vsb, "%s.*", VCL_Name(vcl_active));
407 36080
                vcl = vcl_active;
408 37840
        } else if (strchr(pat, '.') == NULL) {
409
                // pattern applies to active vcl
410 1520
                VSB_printf(vsb, "%s.%s", VCL_Name(vcl_active), pat);
411 1520
                vcl = vcl_active;
412 1520
        } else {
413
                // use pattern as is
414 240
                VSB_cat(vsb, pat);
415 240
                vcl = NULL;
416
        }
417 37840
        AZ(VSB_finish(vsb));
418 37840
        Lck_Lock(&vcl_mtx);
419 37840
        if (vcl != NULL) {
420 37600
                found = vcl_iterdir(cli, VSB_data(vsb), vcl, func, priv);
421 37600
        } else {
422 480
                VTAILQ_FOREACH(vcl, &vcl_head, list) {
423 240
                        i = vcl_iterdir(cli, VSB_data(vsb), vcl, func, priv);
424 240
                        if (i < 0) {
425 0
                                found = i;
426 0
                                break;
427
                        } else {
428 240
                                found += i;
429
                        }
430 240
                }
431
        }
432 37840
        Lck_Unlock(&vcl_mtx);
433 37840
        VSB_destroy(&vsb);
434 37840
        return (found);
435
}
436
437
static void
438 47457
vcl_BackendEvent(const struct vcl *vcl, enum vcl_event_e e)
439
{
440
        struct vcldir *vdir;
441
442 47457
        ASSERT_CLI();
443 47457
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
444 47457
        AZ(vcl->busy);
445
446 47457
        Lck_Lock(&vcl_mtx);
447 107790
        VTAILQ_FOREACH(vdir, &vcl->director_list, list)
448 60333
                VDI_Event(vdir->dir, e);
449 47457
        Lck_Unlock(&vcl_mtx);
450 47457
}
451
452
static void
453 3101
vcl_KillBackends(const struct vcl *vcl)
454
{
455
        struct vcldir *vdir, *vdir2;
456
457 3101
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
458 3101
        assert(vcl->temp == VCL_TEMP_COLD || vcl->temp == VCL_TEMP_INIT);
459
        /*
460
         * Unlocked because no further directors can be added, and the
461
         * remaining ones need to be able to remove themselves.
462
         */
463 5762
        VTAILQ_FOREACH_SAFE(vdir, &vcl->director_list, list, vdir2)
464 2661
                VDI_Event(vdir->dir, VCL_EVENT_DISCARD);
465 3101
        assert(VTAILQ_EMPTY(&vcl->director_list));
466 3101
}
467
468
/*--------------------------------------------------------------------*/
469
470
static struct vcl *
471 95200
VCL_Open(const char *fn, struct vsb *msg)
472
{
473
        struct vcl *vcl;
474
        void *dlh;
475
        struct VCL_conf const *cnf;
476
        const char *dlerr;
477
        int err;
478
479 95200
        AN(fn);
480 95200
        AN(msg);
481
482
#ifdef RTLD_NOLOAD
483
        /* Detect bogus caching by dlopen(3) */
484 95200
        dlh = dlopen(fn, RTLD_NOW | RTLD_NOLOAD);
485 95200
        AZ(dlh);
486
#endif
487 95200
        dlh = dlopen(fn, RTLD_NOW | RTLD_LOCAL);
488 95200
        if (dlh == NULL) {
489 40
                err = errno;
490 40
                dlerr = dlerror();
491 40
                VSB_cat(msg, "Could not load compiled VCL.\n");
492 40
                if (dlerr != NULL)
493 40
                        VSB_printf(msg, "\tdlopen() = %s\n", dlerr);
494 40
                if (err) {
495 0
                        VSB_printf(msg, "\terror = %s (%d)\n",
496 0
                            strerror(err), err);
497 0
                }
498 40
                VSB_cat(msg, "\thint: check for \"noexec\" mount\n");
499 40
                VSB_cat(msg, "\thint: check \"vmod_path\" parameter\n");
500 40
                return (NULL);
501
        }
502 95160
        cnf = dlsym(dlh, "VCL_conf");
503 95160
        if (cnf == NULL) {
504 0
                VSB_cat(msg, "Compiled VCL lacks metadata.\n");
505 0
                (void)dlclose(dlh);
506 0
                return (NULL);
507
        }
508 95160
        if (cnf->magic != VCL_CONF_MAGIC) {
509 0
                VSB_cat(msg, "Compiled VCL has mangled metadata.\n");
510 0
                (void)dlclose(dlh);
511 0
                return (NULL);
512
        }
513 95160
        if (cnf->syntax < heritage.min_vcl_version ||
514 95120
            cnf->syntax > heritage.max_vcl_version) {
515 80
                VSB_printf(msg, "Compiled VCL version (%.1f) not supported.\n",
516 40
                    .1 * cnf->syntax);
517 40
                (void)dlclose(dlh);
518 40
                return (NULL);
519
        }
520 95120
        ALLOC_OBJ(vcl, VCL_MAGIC);
521 95120
        AN(vcl);
522 95120
        vcl->dlh = dlh;
523 95120
        vcl->conf = cnf;
524 95120
        return (vcl);
525 95200
}
526
527
static void
528 50821
VCL_Close(struct vcl **vclp)
529
{
530
        struct vcl *vcl;
531
532 50821
        TAKE_OBJ_NOTNULL(vcl, vclp, VCL_MAGIC);
533 50821
        assert(VTAILQ_EMPTY(&vcl->filters));
534 50821
        AZ(dlclose(vcl->dlh));
535 50821
        FREE_OBJ(vcl);
536 50821
}
537
538
/*--------------------------------------------------------------------
539
 * NB: This function is called in/from the test-load subprocess.
540
 */
541
542
int
543 47800
VCL_TestLoad(const char *fn)
544
{
545
        struct vsb *vsb;
546
        struct vcl *vcl;
547 47800
        int retval = 0;
548
549 47800
        AN(fn);
550 47800
        vsb = VSB_new_auto();
551 47800
        AN(vsb);
552 47800
        vcl = VCL_Open(fn, vsb);
553 47800
        if (vcl == NULL) {
554 80
                AZ(VSB_finish(vsb));
555 80
                fprintf(stderr, "%s", VSB_data(vsb));
556 80
                retval = -1;
557 80
        } else
558 47720
                VCL_Close(&vcl);
559 47800
        VSB_destroy(&vsb);
560 47800
        return (retval);
561
}
562
563
/*--------------------------------------------------------------------*/
564
565
static struct vsb *
566 40
vcl_print_refs(const struct vcl *vcl)
567
{
568
        struct vsb *msg;
569
        struct vclref *ref;
570
571 40
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
572 40
        msg = VSB_new_auto();
573 40
        AN(msg);
574
575 40
        VSB_printf(msg, "VCL %s is waiting for:", vcl->loaded_name);
576 40
        Lck_Lock(&vcl_mtx);
577 80
        VTAILQ_FOREACH(ref, &vcl->ref_list, list)
578 40
                VSB_printf(msg, "\n\t- %s", ref->desc);
579 40
        Lck_Unlock(&vcl_mtx);
580 40
        AZ(VSB_finish(msg));
581 40
        return (msg);
582
}
583
584
static int
585 49779
vcl_set_state(struct vcl *vcl, const char *state, struct vsb **msg)
586
{
587 49779
        struct vsb *nomsg = NULL;
588 49779
        int i = 0;
589
590 49779
        ASSERT_CLI();
591
592 49779
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
593 49779
        AN(state);
594 49779
        AN(msg);
595 49779
        AZ(*msg);
596
597 49779
        AN(vcl->temp);
598
599 49779
        switch (state[0]) {
600
        case '0':
601 3859
                if (vcl->temp == VCL_TEMP_COLD)
602 80
                        break;
603 3779
                if (vcl->busy == 0 && vcl->temp->is_warm) {
604 1737
                        Lck_Lock(&vcl_mtx);
605 1737
                        vcl->temp = VTAILQ_EMPTY(&vcl->ref_list) ?
606
                            VCL_TEMP_COLD : VCL_TEMP_COOLING;
607 1737
                        Lck_Unlock(&vcl_mtx);
608 1737
                        vcl_BackendEvent(vcl, VCL_EVENT_COLD);
609 1737
                        AZ(vcl_send_event(vcl, VCL_EVENT_COLD, msg));
610 1737
                        AZ(*msg);
611 1737
                }
612 2042
                else if (vcl->busy)
613 1166
                        vcl->temp = VCL_TEMP_BUSY;
614 876
                else if (VTAILQ_EMPTY(&vcl->ref_list))
615 716
                        vcl->temp = VCL_TEMP_COLD;
616
                else
617 160
                        vcl->temp = VCL_TEMP_COOLING;
618 3779
                break;
619
        case '1':
620 45920
                if (vcl->temp == VCL_TEMP_WARM)
621 40
                        break;
622
                /* The warm VCL hasn't seen a cold event yet */
623 45880
                if (vcl->temp == VCL_TEMP_BUSY)
624 40
                        vcl->temp = VCL_TEMP_WARM;
625
                /* The VCL must first reach a stable cold state */
626 45840
                else if (vcl->temp == VCL_TEMP_COOLING) {
627 40
                        *msg = vcl_print_refs(vcl);
628 40
                        i = -1;
629 40
                }
630
                else {
631 45800
                        Lck_Lock(&vcl_mtx);
632 45800
                        vcl->temp = VCL_TEMP_WARM;
633 45800
                        Lck_Unlock(&vcl_mtx);
634 45800
                        i = vcl_send_event(vcl, VCL_EVENT_WARM, msg);
635 45800
                        if (i == 0) {
636 45680
                                vcl_BackendEvent(vcl, VCL_EVENT_WARM);
637 45680
                                break;
638
                        }
639 120
                        AZ(vcl_send_event(vcl, VCL_EVENT_COLD, &nomsg));
640 120
                        AZ(nomsg);
641 120
                        vcl->temp = VCL_TEMP_COLD;
642
                }
643 200
                break;
644
        default:
645 0
                WRONG("Wrong enum state");
646 0
        }
647 49779
        if (i == 0 && state[1])
648 47840
                bprintf(vcl->state, "%s", state + 1);
649
650 49779
        return (i);
651
}
652
653
static void
654 1720
vcl_cancel_load(struct vcl *vcl, struct cli *cli, struct vsb *msg,
655
    const char *name, const char *step)
656
{
657
658 1720
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
659 1720
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
660
661 1720
        VCLI_SetResult(cli, CLIS_CANT);
662 1720
        VCLI_Out(cli, "VCL \"%s\" Failed %s", name, step);
663 1720
        if (VSB_len(msg))
664 1720
                VCLI_Out(cli, "\nMessage:\n\t%s", VSB_data(msg));
665 1720
        VSB_destroy(&msg);
666
667 1720
        AZ(vcl_send_event(vcl, VCL_EVENT_DISCARD, &msg));
668 1720
        AZ(msg);
669
670 1720
        vcl_KillBackends(vcl);
671 1720
        free(vcl->loaded_name);
672 1720
        VCL_Close(&vcl);
673 1720
}
674
675
static void
676 47400
vcl_load(struct cli *cli,
677
    const char *name, const char *fn, const char *state)
678
{
679
        struct vcl *vcl;
680
        struct vsb *msg;
681
682 47400
        ASSERT_CLI();
683 47400
        ASSERT_VCL_ACTIVE();
684
685 47400
        vcl = vcl_find(name);
686 47400
        AZ(vcl);
687
688 47400
        msg = VSB_new_auto();
689 47400
        AN(msg);
690 47400
        vcl = VCL_Open(fn, msg);
691 47400
        AZ(VSB_finish(msg));
692 47400
        if (vcl == NULL) {
693 0
                VCLI_SetResult(cli, CLIS_PARAM);
694 0
                VCLI_Out(cli, "%s", VSB_data(msg));
695 0
                VSB_destroy(&msg);
696 0
                return;
697
        }
698
699 47400
        VSB_destroy(&msg);
700
701 47400
        vcl->loaded_name = strdup(name);
702 47400
        XXXAN(vcl->loaded_name);
703 47400
        VTAILQ_INIT(&vcl->director_list);
704 47400
        VTAILQ_INIT(&vcl->ref_list);
705 47400
        VTAILQ_INIT(&vcl->filters);
706
707 47400
        vcl->temp = VCL_TEMP_INIT;
708
709 47400
        if (vcl_send_event(vcl, VCL_EVENT_LOAD, &msg)) {
710 1680
                vcl_cancel_load(vcl, cli, msg, name, "initialization");
711 1680
                return;
712
        }
713 45720
        VSB_destroy(&msg);
714
715 45720
        if (vcl_set_state(vcl, state, &msg)) {
716 40
                assert(*state == '1');
717 40
                vcl_cancel_load(vcl, cli, msg, name, "warmup");
718 40
                return;
719
        }
720 45680
        if (msg)
721 45520
                VSB_destroy(&msg);
722
723 45680
        VCLI_Out(cli, "Loaded \"%s\" as \"%s\"", fn , name);
724 45680
        VTAILQ_INSERT_TAIL(&vcl_head, vcl, list);
725 45680
        VSC_C_main->n_vcl++;
726 45680
        VSC_C_main->n_vcl_avail++;
727 47400
}
728
729
/*--------------------------------------------------------------------*/
730
731
void
732 300011
VCL_Poll(void)
733
{
734 300011
        struct vsb *nomsg = NULL;
735
        struct vcl *vcl, *vcl2;
736
737 300011
        ASSERT_CLI();
738 300011
        ASSERT_VCL_ACTIVE();
739 630962
        VTAILQ_FOREACH_SAFE(vcl, &vcl_head, list, vcl2) {
740 330951
                if (vcl->temp == VCL_TEMP_BUSY ||
741 329888
                    vcl->temp == VCL_TEMP_COOLING)
742 1779
                        AZ(vcl_set_state(vcl, "0", &nomsg));
743 330951
                AZ(nomsg);
744 330951
                if (vcl->discard && vcl->temp == VCL_TEMP_COLD) {
745 1381
                        AZ(vcl->busy);
746 1381
                        assert(vcl != vcl_active);
747 1381
                        assert(VTAILQ_EMPTY(&vcl->ref_list));
748 1381
                        VTAILQ_REMOVE(&vcl_head, vcl, list);
749 1381
                        AZ(vcl_send_event(vcl, VCL_EVENT_DISCARD, &nomsg));
750 1381
                        AZ(nomsg);
751 1381
                        vcl_KillBackends(vcl);
752 1381
                        free(vcl->loaded_name);
753 1381
                        VCL_Close(&vcl);
754 1381
                        VSC_C_main->n_vcl--;
755 1381
                        VSC_C_main->n_vcl_discard--;
756 1381
                }
757 330951
        }
758 300011
}
759
760
/*--------------------------------------------------------------------*/
761
762
static void v_matchproto_(cli_func_t)
763 3000
vcl_cli_list(struct cli *cli, const char * const *av, void *priv)
764
{
765
        struct vcl *vcl;
766
        const char *flg;
767
        struct vte *vte;
768
769
        /* NB: Shall generate same output as mcf_vcl_list() */
770
771 3000
        (void)av;
772 3000
        (void)priv;
773 3000
        ASSERT_CLI();
774 3000
        ASSERT_VCL_ACTIVE();
775 3000
        vte = VTE_new(7, 80);
776 3000
        AN(vte);
777 9602
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
778 6602
                if (vcl == vcl_active) {
779 3000
                        flg = "active";
780 6602
                } else if (vcl->discard) {
781 162
                        flg = "discarded";
782 162
                } else
783 3440
                        flg = "available";
784 13204
                VTE_printf(vte, "%s\t%s\t%s\t\v%u\t%s", flg, vcl->state,
785 6602
                    vcl->temp->name, vcl->busy, vcl->loaded_name);
786 6602
                if (vcl->label != NULL) {
787 760
                        VTE_printf(vte, "\t->\t%s", vcl->label->loaded_name);
788 760
                        if (vcl->nrefs)
789 320
                                VTE_printf(vte, " (%d return(vcl)%s)",
790 160
                                    vcl->nrefs, vcl->nrefs > 1 ? "'s" : "");
791 6602
                } else if (vcl->nlabels > 0) {
792 1040
                        VTE_printf(vte, "\t<-\t(%d label%s)",
793 520
                            vcl->nlabels, vcl->nlabels > 1 ? "s" : "");
794 520
                }
795 6602
                VTE_cat(vte, "\n");
796 6602
        }
797 3000
        AZ(VTE_finish(vte));
798 3000
        AZ(VTE_format(vte, VCLI_VTE_format, cli));
799 3000
        VTE_destroy(&vte);
800 3000
}
801
802
static void v_matchproto_(cli_func_t)
803 200
vcl_cli_list_json(struct cli *cli, const char * const *av, void *priv)
804
{
805
        struct vcl *vcl;
806
807 200
        (void)priv;
808 200
        ASSERT_CLI();
809 200
        ASSERT_VCL_ACTIVE();
810 200
        VCLI_JSON_begin(cli, 2, av);
811 200
        VCLI_Out(cli, ",\n");
812 840
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
813 640
                VCLI_Out(cli, "{\n");
814 640
                VSB_indent(cli->sb, 2);
815 640
                VCLI_Out(cli, "\"status\": ");
816 640
                if (vcl == vcl_active) {
817 200
                        VCLI_Out(cli, "\"active\",\n");
818 640
                } else if (vcl->discard) {
819 0
                        VCLI_Out(cli, "\"discarded\",\n");
820 0
                } else
821 440
                        VCLI_Out(cli, "\"available\",\n");
822 640
                VCLI_Out(cli, "\"state\": \"%s\",\n", vcl->state);
823 640
                VCLI_Out(cli, "\"temperature\": \"%s\",\n", vcl->temp->name);
824 640
                VCLI_Out(cli, "\"busy\": %u,\n", vcl->busy);
825 640
                VCLI_Out(cli, "\"name\": \"%s\"", vcl->loaded_name);
826 640
                if (vcl->label != NULL) {
827 240
                        VCLI_Out(cli, ",\n");
828 240
                        VCLI_Out(cli, "\"label\": {\n");
829 240
                        VSB_indent(cli->sb, 2);
830 480
                                VCLI_Out(cli, "\"name\": \"%s\"",
831 240
                                         vcl->label->loaded_name);
832 240
                        if (vcl->nrefs)
833 0
                                VCLI_Out(cli, ",\n\"refs\": %d", vcl->nrefs);
834 240
                        VCLI_Out(cli, "\n");
835 240
                        VCLI_Out(cli, "}");
836 240
                        VSB_indent(cli->sb, -2);
837 640
                } else if (vcl->nlabels > 0) {
838 80
                        VCLI_Out(cli, ",\n");
839 80
                        VCLI_Out(cli, "\"labels\": %d", vcl->nlabels);
840 80
                }
841 640
                VSB_indent(cli->sb, -2);
842 640
                VCLI_Out(cli, "\n}");
843 640
                if (VTAILQ_NEXT(vcl, list) != NULL)
844 440
                        VCLI_Out(cli, ",\n");
845 640
        }
846 200
        VCLI_JSON_end(cli);
847 200
}
848
849
static void v_matchproto_(cli_func_t)
850 47400
vcl_cli_load(struct cli *cli, const char * const *av, void *priv)
851
{
852
853 47400
        AZ(priv);
854 47400
        ASSERT_CLI();
855
        // XXX move back code from vcl_load?
856 47400
        vcl_load(cli, av[2], av[3], av[4]);
857 47400
}
858
859
static void v_matchproto_(cli_func_t)
860 2320
vcl_cli_state(struct cli *cli, const char * const *av, void *priv)
861
{
862
        struct vcl *vcl;
863 2320
        struct vsb *msg = NULL;
864
865 2320
        AZ(priv);
866 2320
        ASSERT_CLI();
867 2320
        ASSERT_VCL_ACTIVE();
868 2320
        AN(av[2]);
869 2320
        AN(av[3]);
870
871 2320
        vcl = vcl_find(av[2]);
872 2320
        AN(vcl);
873
874 2320
        if (vcl_set_state(vcl, av[3], &msg)) {
875 120
                CHECK_OBJ_NOTNULL(msg, VSB_MAGIC);
876
877 120
                VCLI_SetResult(cli, CLIS_CANT);
878 240
                VCLI_Out(cli, "Failed <vcl.state %s %s>", vcl->loaded_name,
879 120
                    av[3] + 1);
880 120
                if (VSB_len(msg))
881 120
                        VCLI_Out(cli, "\nMessage:\n\t%s", VSB_data(msg));
882 120
        }
883 2320
        if (msg)
884 280
                VSB_destroy(&msg);
885 2320
}
886
887
static void v_matchproto_(cli_func_t)
888 1560
vcl_cli_discard(struct cli *cli, const char * const *av, void *priv)
889
{
890
        struct vcl *vcl;
891
892 1560
        ASSERT_CLI();
893 1560
        ASSERT_VCL_ACTIVE();
894 1560
        (void)cli;
895 1560
        AZ(priv);
896 1560
        vcl = vcl_find(av[2]);
897 1560
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);              // MGT ensures this
898 1560
        Lck_Lock(&vcl_mtx);
899 1560
        assert (vcl != vcl_active);     // MGT ensures this
900 1560
        AZ(vcl->nlabels);               // MGT ensures this
901 1560
        VSC_C_main->n_vcl_discard++;
902 1560
        VSC_C_main->n_vcl_avail--;
903 1560
        vcl->discard = 1;
904 1560
        if (vcl->label != NULL) {
905 160
                AZ(strcmp(vcl->state, VCL_TEMP_LABEL->name));
906 160
                vcl->label->nlabels--;
907 160
                vcl->label= NULL;
908 160
        }
909 1560
        Lck_Unlock(&vcl_mtx);
910
911 1560
        if (!strcmp(vcl->state, VCL_TEMP_LABEL->name)) {
912 160
                VTAILQ_REMOVE(&vcl_head, vcl, list);
913 160
                free(vcl->loaded_name);
914 160
                FREE_OBJ(vcl);
915 1560
        } else if (vcl->temp == VCL_TEMP_COLD) {
916 1048
                VCL_Poll();
917 1048
        }
918 1560
}
919
920
static void v_matchproto_(cli_func_t)
921 1080
vcl_cli_label(struct cli *cli, const char * const *av, void *priv)
922
{
923
        struct vcl *lbl;
924
        struct vcl *vcl;
925
926 1080
        ASSERT_CLI();
927 1080
        ASSERT_VCL_ACTIVE();
928 1080
        (void)cli;
929 1080
        (void)priv;
930 1080
        vcl = vcl_find(av[3]);
931 1080
        AN(vcl);                                // MGT ensures this
932 1080
        lbl = vcl_find(av[2]);
933 1080
        if (lbl == NULL) {
934 960
                ALLOC_OBJ(lbl, VCL_MAGIC);
935 960
                AN(lbl);
936 960
                bprintf(lbl->state, "%s", VCL_TEMP_LABEL->name);
937 960
                lbl->temp = VCL_TEMP_WARM;
938 960
                REPLACE(lbl->loaded_name, av[2]);
939 960
                VTAILQ_INSERT_TAIL(&vcl_head, lbl, list);
940 960
        }
941 1080
        if (lbl->label != NULL)
942 120
                lbl->label->nlabels--;
943 1080
        lbl->label = vcl;
944 1080
        vcl->nlabels++;
945 1080
}
946
947
static void v_matchproto_(cli_func_t)
948 44200
vcl_cli_use(struct cli *cli, const char * const *av, void *priv)
949
{
950
        struct vcl *vcl;
951
952 44200
        ASSERT_CLI();
953 44200
        ASSERT_VCL_ACTIVE();
954 44200
        AN(cli);
955 44200
        AZ(priv);
956 44200
        vcl = vcl_find(av[2]);
957 44200
        AN(vcl);                                // MGT ensures this
958 44200
        assert(vcl->temp == VCL_TEMP_WARM);     // MGT ensures this
959 44200
        Lck_Lock(&vcl_mtx);
960 44200
        vcl_active = vcl;
961 44200
        Lck_Unlock(&vcl_mtx);
962 44200
}
963
964
static void v_matchproto_(cli_func_t)
965 480
vcl_cli_show(struct cli *cli, const char * const *av, void *priv)
966
{
967
        struct vcl *vcl;
968 480
        int verbose = 0;
969 480
        int i = 2;
970
        unsigned u;
971
972 480
        ASSERT_CLI();
973 480
        ASSERT_VCL_ACTIVE();
974 480
        AZ(priv);
975
976 480
        if (av[i] != NULL && !strcmp(av[i], "-v")) {
977 160
                verbose = 1;
978 160
                i++;
979 160
        }
980
981 480
        if (av[i] == NULL) {
982 80
                vcl = vcl_active;
983 80
                AN(vcl);
984 80
        } else {
985 400
                vcl = vcl_find(av[i]);
986 400
                i++;
987
        }
988
989 480
        if (av[i] != NULL) {
990 40
                VCLI_Out(cli, "Too many parameters: '%s'", av[i]);
991 40
                VCLI_SetResult(cli, CLIS_PARAM);
992 40
                return;
993
        }
994
995 440
        if (vcl == NULL) {
996 40
                VCLI_Out(cli, "No VCL named '%s'", av[i - 1]);
997 40
                VCLI_SetResult(cli, CLIS_PARAM);
998 40
                return;
999
        }
1000 400
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
1001 400
        if (vcl->label) {
1002 80
                vcl = vcl->label;
1003 80
                CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
1004 80
                AZ(vcl->label);
1005 80
        }
1006 400
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
1007 400
        if (verbose) {
1008 480
                for (u = 0; u < vcl->conf->nsrc; u++)
1009 640
                        VCLI_Out(cli, "// VCL.SHOW %u %zd %s\n%s\n",
1010 320
                            u, strlen(vcl->conf->srcbody[u]),
1011 320
                            vcl->conf->srcname[u],
1012 320
                            vcl->conf->srcbody[u]);
1013 160
        } else {
1014 240
                VCLI_Out(cli, "%s", vcl->conf->srcbody[0]);
1015
        }
1016 480
}
1017
1018
/*--------------------------------------------------------------------*/
1019
1020
static struct cli_proto vcl_cmds[] = {
1021
        { CLICMD_VCL_LOAD,              "", vcl_cli_load },
1022
        { CLICMD_VCL_LIST,              "", vcl_cli_list, vcl_cli_list_json },
1023
        { CLICMD_VCL_STATE,             "", vcl_cli_state },
1024
        { CLICMD_VCL_DISCARD,           "", vcl_cli_discard },
1025
        { CLICMD_VCL_USE,               "", vcl_cli_use },
1026
        { CLICMD_VCL_SHOW,              "", vcl_cli_show },
1027
        { CLICMD_VCL_LABEL,             "", vcl_cli_label },
1028
        { NULL }
1029
};
1030
1031
void
1032 36676
VCL_Init(void)
1033
{
1034
1035 36676
        assert(cache_param->workspace_client > 0);
1036 73352
        WS_Init(&ws_cli, "cli", malloc(cache_param->workspace_client),
1037 36676
            cache_param->workspace_client);
1038 36676
        ws_snapshot_cli = WS_Snapshot(&ws_cli);
1039 36676
        CLI_AddFuncs(vcl_cmds);
1040 36676
        Lck_New(&vcl_mtx, lck_vcl);
1041 36676
        VSL_Setup(&vsl_cli, NULL, 0);
1042 36676
}