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