varnish-cache/bin/varnishd/cache/cache_vcl.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2016 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 */
30
31
#include "config.h"
32
33
#include <errno.h>
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
48
const char * const VCL_TEMP_INIT = "init";
49
const char * const VCL_TEMP_COLD = "cold";
50
const char * const VCL_TEMP_WARM = "warm";
51
const char * const VCL_TEMP_BUSY = "busy";
52
const char * const VCL_TEMP_COOLING = "cooling";
53
const char * const VCL_TEMP_LABEL = "label";
54
55
/*
56
 * XXX: Presently all modifications to this list happen from the
57
 * CLI event-engine, so no locking is necessary
58
 */
59
static VTAILQ_HEAD(, vcl)       vcl_head =
60
    VTAILQ_HEAD_INITIALIZER(vcl_head);
61
62
struct lock             vcl_mtx;
63
struct vcl              *vcl_active; /* protected by vcl_mtx */
64
65
static struct vrt_ctx ctx_cli;
66
static unsigned handling_cli;
67
static struct ws ws_cli;
68
static uintptr_t ws_snapshot_cli;
69
70
/*--------------------------------------------------------------------*/
71
72
void
73 6562
VCL_Bo2Ctx(struct vrt_ctx *ctx, struct busyobj *bo)
74
{
75
76 6562
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
77 6562
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
78 6562
        ctx->vcl = bo->vcl;
79 6562
        ctx->vsl = bo->vsl;
80 6562
        ctx->http_bereq = bo->bereq;
81 6562
        ctx->http_beresp = bo->beresp;
82 6562
        ctx->bo = bo;
83 6562
        ctx->sp = bo->sp;
84 6562
        ctx->now = bo->t_prev;
85 6562
        ctx->ws = bo->ws;
86 6562
}
87
88
void
89 9681
VCL_Req2Ctx(struct vrt_ctx *ctx, struct req *req)
90
{
91
92 9681
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
93 9681
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
94
95 9681
        ctx->vcl = req->vcl;
96 9681
        ctx->vsl = req->vsl;
97 9681
        ctx->http_req = req->http;
98 9681
        ctx->http_req_top = req->top->http;
99 9681
        ctx->http_resp = req->resp;
100 9681
        ctx->req = req;
101 9681
        ctx->sp = req->sp;
102 9681
        ctx->now = req->t_prev;
103 9681
        ctx->ws = req->ws;
104 9681
}
105
106
/*--------------------------------------------------------------------*/
107
108
static struct vrt_ctx *
109 6519
vcl_get_ctx(unsigned method, int msg)
110
{
111
112 6519
        ASSERT_CLI();
113 6519
        AZ(ctx_cli.handling);
114 6519
        INIT_OBJ(&ctx_cli, VRT_CTX_MAGIC);
115 6519
        handling_cli = 0;
116 6519
        ctx_cli.handling = &handling_cli;
117 6519
        ctx_cli.method = method;
118 6519
        if (msg) {
119 884
                ctx_cli.msg = VSB_new_auto();
120 884
                AN(ctx_cli.msg);
121
        }
122 6519
        ctx_cli.ws = &ws_cli;
123 6519
        WS_Assert(ctx_cli.ws);
124 6519
        VRTPRIV_init(cli_task_privs);
125 6519
        return (&ctx_cli);
126
}
127
128
static void
129 6519
vcl_rel_ctx(struct vrt_ctx **ctx)
130
{
131
132 6519
        ASSERT_CLI();
133 6519
        assert(*ctx == &ctx_cli);
134 6519
        AN((*ctx)->handling);
135 6519
        if (ctx_cli.msg)
136 884
                VSB_destroy(&ctx_cli.msg);
137 6519
        WS_Assert(ctx_cli.ws);
138 6519
        WS_Reset(&ws_cli, ws_snapshot_cli);
139 6519
        INIT_OBJ(*ctx, VRT_CTX_MAGIC);
140 6519
        *ctx = NULL;
141 6519
        VRTPRIV_dynamic_kill(cli_task_privs, (uintptr_t)cli_task_privs);
142 6519
}
143
144
/*--------------------------------------------------------------------*/
145
146
static int
147 1711
vcl_send_event(VRT_CTX, enum vcl_event_e ev)
148
{
149
        int r;
150
151 1711
        ASSERT_CLI();
152 1711
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
153 1711
        CHECK_OBJ_NOTNULL(ctx->vcl, VCL_MAGIC);
154 1711
        CHECK_OBJ_NOTNULL(ctx->vcl->conf, VCL_CONF_MAGIC);
155 1711
        assert(ev == VCL_EVENT_LOAD ||
156
               ev == VCL_EVENT_WARM ||
157
               ev == VCL_EVENT_COLD ||
158
               ev == VCL_EVENT_DISCARD);
159 1711
        AN(ctx->handling);
160 1711
        *ctx->handling = 0;
161 1711
        AN(ctx->ws);
162
163 1711
        if (ev == VCL_EVENT_LOAD || ev == VCL_EVENT_WARM)
164 1660
                AN(ctx->msg);
165
166 1711
        r = ctx->vcl->conf->event_vcl(ctx, ev);
167
168 1711
        if (r && (ev == VCL_EVENT_COLD || ev == VCL_EVENT_DISCARD))
169 0
                WRONG("A VMOD cannot fail COLD or DISCARD events");
170
171 1711
        return (r);
172
}
173
174
/*--------------------------------------------------------------------*/
175
176
struct vcl *
177 1783
vcl_find(const char *name)
178
{
179
        struct vcl *vcl;
180
181 1783
        ASSERT_CLI();
182 2593
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
183 1728
                if (vcl->discard)
184 16
                        continue;
185 1712
                if (!strcmp(vcl->loaded_name, name))
186 918
                        return (vcl);
187
        }
188 865
        return (NULL);
189
}
190
191
/*--------------------------------------------------------------------*/
192
193
void
194 5
VCL_Panic(struct vsb *vsb, const struct vcl *vcl)
195
{
196
        int i;
197
198 5
        AN(vsb);
199 5
        if (vcl == NULL)
200 0
                return;
201 5
        VSB_printf(vsb, "vcl = {\n");
202 5
        VSB_indent(vsb, 2);
203 5
        PAN_CheckMagic(vsb, vcl, VCL_MAGIC);
204 5
        VSB_printf(vsb, "name = \"%s\",\n", vcl->loaded_name);
205 5
        VSB_printf(vsb, "busy = %u,\n", vcl->busy);
206 5
        VSB_printf(vsb, "discard = %u,\n", vcl->discard);
207 5
        VSB_printf(vsb, "state = %s,\n", vcl->state);
208 5
        VSB_printf(vsb, "temp = %s,\n", (const volatile char *)vcl->temp);
209 5
        VSB_printf(vsb, "conf = {\n");
210 5
        VSB_indent(vsb, 2);
211 5
        if (vcl->conf == NULL) {
212 0
                VSB_printf(vsb, "conf = NULL\n");
213
        } else {
214 5
                PAN_CheckMagic(vsb, vcl->conf, VCL_CONF_MAGIC);
215 5
                VSB_printf(vsb, "srcname = {\n");
216 5
                VSB_indent(vsb, 2);
217 15
                for (i = 0; i < vcl->conf->nsrc; ++i)
218 10
                        VSB_printf(vsb, "\"%s\",\n", vcl->conf->srcname[i]);
219 5
                VSB_indent(vsb, -2);
220 5
                VSB_printf(vsb, "},\n");
221
        }
222 5
        VSB_indent(vsb, -2);
223 5
        VSB_printf(vsb, "},\n");
224 5
        VSB_indent(vsb, -2);
225 5
        VSB_printf(vsb, "},\n");
226
}
227
228
/*--------------------------------------------------------------------*/
229
230
void
231 926
vcl_get(struct vcl **vcc, struct vcl *vcl)
232
{
233 926
        AN(vcc);
234
235 926
        Lck_Lock(&vcl_mtx);
236 926
        if (vcl == NULL)
237 916
                vcl = vcl_active; /* Sample vcl_active under lock to avoid
238
                                   * race */
239 926
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
240 926
        if (vcl->label == NULL) {
241 913
                AN(strcmp(vcl->state, VCL_TEMP_LABEL));
242 913
                *vcc = vcl;
243
        } else {
244 13
                AZ(strcmp(vcl->state, VCL_TEMP_LABEL));
245 13
                *vcc = vcl->label;
246
        }
247 926
        CHECK_OBJ_NOTNULL(*vcc, VCL_MAGIC);
248 926
        AZ((*vcc)->discard);
249 926
        (*vcc)->busy++;
250 926
        Lck_Unlock(&vcl_mtx);
251 926
        AZ(errno=pthread_rwlock_rdlock(&(*vcc)->temp_rwl));
252 926
        assert(VCL_WARM(*vcc));
253 926
        AZ(errno=pthread_rwlock_unlock(&(*vcc)->temp_rwl));
254 926
}
255
256
/*--------------------------------------------------------------------*/
257
258
static int
259 720
vcl_iterdir(struct cli *cli, const char *pat, const struct vcl *vcl,
260
    vcl_be_func *func, void *priv)
261
{
262 720
        int i, found = 0;
263
        struct vcldir *vdir;
264
265 1807
        VTAILQ_FOREACH(vdir, &vcl->director_list, list) {
266 1087
                if (fnmatch(pat, vdir->cli_name, 0))
267 97
                        continue;
268 990
                found++;
269 990
                i = func(cli, vdir->dir, priv);
270 990
                if (i < 0)
271 0
                        return (i);
272 990
                found += i;
273
        }
274 720
        return (found);
275
}
276
277
int
278 720
VCL_IterDirector(struct cli *cli, const char *pat,
279
    vcl_be_func *func, void *priv)
280
{
281 720
        int i, found = 0;
282
        struct vsb *vsb;
283
        struct vcl *vcl;
284
285 720
        ASSERT_CLI();
286 720
        vsb = VSB_new_auto();
287 720
        AN(vsb);
288 720
        if (pat == NULL || *pat == '\0' || !strcmp(pat, "*")) {
289
                // all backends in active VCL
290 680
                VSB_printf(vsb, "%s.*", VCL_Name(vcl_active));
291 680
                vcl = vcl_active;
292 40
        } else if (strchr(pat, '.') == NULL) {
293
                // pattern applies to active vcl
294 34
                VSB_printf(vsb, "%s.%s", VCL_Name(vcl_active), pat);
295 34
                vcl = vcl_active;
296
        } else {
297
                // use pattern as is
298 6
                VSB_cat(vsb, pat);
299 6
                vcl = NULL;
300
        }
301 720
        AZ(VSB_finish(vsb));
302 720
        Lck_Lock(&vcl_mtx);
303 720
        if (vcl != NULL) {
304 714
                found = vcl_iterdir(cli, VSB_data(vsb), vcl, func, priv);
305
        } else {
306 12
                VTAILQ_FOREACH(vcl, &vcl_head, list) {
307 6
                        i = vcl_iterdir(cli, VSB_data(vsb), vcl, func, priv);
308 6
                        if (i < 0) {
309 0
                                found = i;
310 0
                                break;
311
                        } else {
312 6
                                found += i;
313
                        }
314
                }
315
        }
316 720
        Lck_Unlock(&vcl_mtx);
317 720
        VSB_destroy(&vsb);
318 720
        return (found);
319
}
320
321
static void
322 841
vcl_BackendEvent(const struct vcl *vcl, enum vcl_event_e e)
323
{
324
        struct vcldir *vdir;
325
326 841
        ASSERT_CLI();
327 841
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
328 841
        AZ(vcl->busy);
329
330 1924
        VTAILQ_FOREACH(vdir, &vcl->director_list, list)
331 1083
                VDI_Event(vdir->dir, e);
332 841
}
333
334
static void
335 58
vcl_KillBackends(struct vcl *vcl)
336
{
337
        struct vcldir *vdir;
338
339 58
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
340 58
        AZ(vcl->busy);
341 58
        assert(VTAILQ_EMPTY(&vcl->ref_list));
342
        while (1) {
343 180
                vdir = VTAILQ_FIRST(&vcl->director_list);
344 119
                if (vdir == NULL)
345 58
                        break;
346 61
                VTAILQ_REMOVE(&vcl->director_list, vdir, list);
347 61
                REPLACE(vdir->cli_name, NULL);
348 61
                AN(vdir->methods->destroy);
349 61
                vdir->methods->destroy(vdir->dir);
350 61
                FREE_OBJ(vdir);
351
        }
352 58
}
353
354
/*--------------------------------------------------------------------*/
355
356
static struct vcl *
357 1709
VCL_Open(const char *fn, struct vsb *msg)
358
{
359
        struct vcl *vcl;
360
        void *dlh;
361
        struct VCL_conf const *cnf;
362
363 1709
        AN(fn);
364 1709
        AN(msg);
365
366
#ifdef RTLD_NOLOAD
367
        /* Detect bogus caching by dlopen(3) */
368 1709
        dlh = dlopen(fn, RTLD_NOW | RTLD_NOLOAD);
369 1709
        AZ(dlh);
370
#endif
371 1709
        dlh = dlopen(fn, RTLD_NOW | RTLD_LOCAL);
372 1709
        if (dlh == NULL) {
373 0
                VSB_printf(msg, "Could not load compiled VCL.\n");
374 0
                VSB_printf(msg, "\tdlopen() = %s\n", dlerror());
375 0
                return (NULL);
376
        }
377 1709
        cnf = dlsym(dlh, "VCL_conf");
378 1709
        if (cnf == NULL) {
379 0
                VSB_printf(msg, "Compiled VCL lacks metadata.\n");
380 0
                (void)dlclose(dlh);
381 0
                return (NULL);
382
        }
383 1709
        if (cnf->magic != VCL_CONF_MAGIC) {
384 0
                VSB_printf(msg, "Compiled VCL has mangled metadata.\n");
385 0
                (void)dlclose(dlh);
386 0
                return (NULL);
387
        }
388 1709
        if (cnf->syntax < heritage.min_vcl || cnf->syntax > heritage.max_vcl) {
389 1
                VSB_printf(msg, "Compiled VCL version (%.1f) not supported.\n",
390 1
                    .1 * cnf->syntax);
391 1
                (void)dlclose(dlh);
392 1
                return (NULL);
393
        }
394 1708
        ALLOC_OBJ(vcl, VCL_MAGIC);
395 1708
        AN(vcl);
396 1708
        AZ(errno=pthread_rwlock_init(&vcl->temp_rwl, NULL));
397 1708
        vcl->dlh = dlh;
398 1708
        vcl->conf = cnf;
399 1708
        return (vcl);
400
}
401
402
static void
403 921
VCL_Close(struct vcl **vclp)
404
{
405
        struct vcl *vcl;
406
407 921
        CHECK_OBJ_NOTNULL(*vclp, VCL_MAGIC);
408 921
        vcl = *vclp;
409 921
        *vclp = NULL;
410 921
        AZ(dlclose(vcl->dlh));
411 921
        AZ(errno=pthread_rwlock_destroy(&vcl->temp_rwl));
412 921
        FREE_OBJ(vcl);
413 921
}
414
415
/*--------------------------------------------------------------------
416
 * NB: This function is called from the test-load subprocess.
417
 */
418
419
int
420 864
VCL_TestLoad(const char *fn)
421
{
422
        struct vsb *vsb;
423
        struct vcl *vcl;
424 864
        int retval = 0;
425
426 864
        AN(fn);
427 864
        vsb = VSB_new_auto();
428 864
        AN(vsb);
429 864
        vcl = VCL_Open(fn, vsb);
430 864
        if (vcl == NULL) {
431 1
                AZ(VSB_finish(vsb));
432 1
                fprintf(stderr, "%s", VSB_data(vsb));
433 1
                retval = -1;
434
        } else
435 863
                VCL_Close(&vcl);
436 864
        VSB_destroy(&vsb);
437 864
        return (retval);
438
}
439
440
/*--------------------------------------------------------------------*/
441
442
static void
443 1
vcl_print_refs(VRT_CTX)
444
{
445
        struct vcl *vcl;
446
        struct vclref *ref;
447
448 1
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
449 1
        CHECK_OBJ_NOTNULL(ctx->vcl, VCL_MAGIC);
450 1
        AN(ctx->msg);
451 1
        vcl = ctx->vcl;
452 1
        VSB_printf(ctx->msg, "VCL %s is waiting for:", vcl->loaded_name);
453 1
        Lck_Lock(&vcl_mtx);
454 2
        VTAILQ_FOREACH(ref, &ctx->vcl->ref_list, list)
455 1
                VSB_printf(ctx->msg, "\n\t- %s", ref->desc);
456 1
        Lck_Unlock(&vcl_mtx);
457 1
}
458
459
static int
460 899
vcl_set_state(VRT_CTX, const char *state)
461
{
462
        struct vcl *vcl;
463 899
        int i = 0;
464
465 899
        ASSERT_CLI();
466 899
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
467 899
        CHECK_OBJ_NOTNULL(ctx->vcl, VCL_MAGIC);
468 899
        CHECK_OBJ_NOTNULL(ctx->vcl->conf, VCL_CONF_MAGIC);
469 899
        AN(ctx->handling);
470 899
        AN(ctx->vcl);
471 899
        AN(state);
472 899
        assert(ctx->msg != NULL || *state == '0');
473
474 899
        vcl = ctx->vcl;
475 899
        AZ(errno=pthread_rwlock_wrlock(&vcl->temp_rwl));
476 899
        AN(vcl->temp);
477
478 899
        switch (state[0]) {
479
        case '0':
480 83
                assert(vcl->temp != VCL_TEMP_COLD);
481 83
                if (vcl->busy == 0 && VCL_WARM(vcl)) {
482
483 58
                        vcl->temp = VTAILQ_EMPTY(&vcl->ref_list) ?
484 29
                            VCL_TEMP_COLD : VCL_TEMP_COOLING;
485 29
                        AZ(vcl_send_event(ctx, VCL_EVENT_COLD));
486 29
                        vcl_BackendEvent(vcl, VCL_EVENT_COLD);
487
                }
488 54
                else if (vcl->busy)
489 41
                        vcl->temp = VCL_TEMP_BUSY;
490 13
                else if (VTAILQ_EMPTY(&vcl->ref_list))
491 10
                        vcl->temp = VCL_TEMP_COLD;
492
                else
493 3
                        vcl->temp = VCL_TEMP_COOLING;
494 83
                break;
495
        case '1':
496 816
                assert(vcl->temp != VCL_TEMP_WARM);
497
                /* The warm VCL hasn't seen a cold event yet */
498 816
                if (vcl->temp == VCL_TEMP_BUSY)
499 0
                        vcl->temp = VCL_TEMP_WARM;
500
                /* The VCL must first reach a stable cold state */
501 816
                else if (vcl->temp == VCL_TEMP_COOLING) {
502 1
                        vcl_print_refs(ctx);
503 1
                        i = -1;
504
                }
505
                else {
506 815
                        vcl->temp = VCL_TEMP_WARM;
507 815
                        i = vcl_send_event(ctx, VCL_EVENT_WARM);
508 815
                        if (i == 0)
509 812
                                vcl_BackendEvent(vcl, VCL_EVENT_WARM);
510
                        else
511 3
                                AZ(vcl->conf->event_vcl(ctx, VCL_EVENT_COLD));
512
                }
513 816
                break;
514
        default:
515 0
                WRONG("Wrong enum state");
516
        }
517 899
        AZ(errno=pthread_rwlock_unlock(&vcl->temp_rwl));
518
519 899
        return (i);
520
}
521
522
static void
523 36
vcl_cancel_load(VRT_CTX, struct cli *cli, const char *name, const char *step)
524
{
525 36
        struct vcl *vcl = ctx->vcl;
526
527 36
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
528 36
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
529
530 36
        AZ(VSB_finish(ctx->msg));
531 36
        VCLI_SetResult(cli, CLIS_CANT);
532 36
        VCLI_Out(cli, "VCL \"%s\" Failed %s", name, step);
533 36
        if (VSB_len(ctx->msg))
534 35
                VCLI_Out(cli, "\nMessage:\n\t%s", VSB_data(ctx->msg));
535 36
        *ctx->handling = 0;
536 36
        AZ(vcl->conf->event_vcl(ctx, VCL_EVENT_DISCARD));
537 36
        vcl_KillBackends(vcl);
538 36
        free(vcl->loaded_name);
539 36
        VCL_Close(&vcl);
540 36
}
541
542
static void
543 845
vcl_load(struct cli *cli, struct vrt_ctx *ctx,
544
    const char *name, const char *fn, const char *state)
545
{
546
        struct vcl *vcl;
547
        int i;
548
549 845
        ASSERT_CLI();
550
551 845
        vcl = vcl_find(name);
552 845
        AZ(vcl);
553
554 845
        vcl = VCL_Open(fn, ctx->msg);
555 845
        if (vcl == NULL) {
556 0
                AZ(VSB_finish(ctx->msg));
557 0
                VCLI_SetResult(cli, CLIS_PARAM);
558 0
                VCLI_Out(cli, "%s", VSB_data(ctx->msg));
559 0
                return;
560
        }
561
562 845
        vcl->loaded_name = strdup(name);
563 845
        XXXAN(vcl->loaded_name);
564 845
        VTAILQ_INIT(&vcl->director_list);
565 845
        VTAILQ_INIT(&vcl->ref_list);
566 845
        VTAILQ_INIT(&vcl->vfps);
567
568 845
        vcl->temp = VCL_TEMP_INIT;
569
570 845
        ctx->vcl = vcl;
571
572 845
        VSB_clear(ctx->msg);
573 845
        i = vcl_send_event(ctx, VCL_EVENT_LOAD);
574 845
        if (i || *ctx->handling != VCL_RET_OK) {
575 35
                vcl_cancel_load(ctx, cli, name, "initialization");
576 35
                return;
577
        }
578 810
        assert(*ctx->handling == VCL_RET_OK);
579 810
        VSB_clear(ctx->msg);
580 810
        i = vcl_set_state(ctx, state);
581 810
        if (i) {
582 1
                assert(*state == '1');
583 1
                vcl_cancel_load(ctx, cli, name, "warmup");
584 1
                return;
585
        }
586 809
        bprintf(vcl->state, "%s", state + 1);
587 809
        VCLI_Out(cli, "Loaded \"%s\" as \"%s\"", fn , name);
588 809
        VTAILQ_INSERT_TAIL(&vcl_head, vcl, list);
589 809
        Lck_Lock(&vcl_mtx);
590 809
        if (vcl_active == NULL)
591 684
                vcl_active = vcl;
592 809
        Lck_Unlock(&vcl_mtx);
593 809
        VSC_C_main->n_vcl++;
594 809
        VSC_C_main->n_vcl_avail++;
595
}
596
597
/*--------------------------------------------------------------------*/
598
599
void
600 5635
VCL_Poll(void)
601
{
602
        struct vrt_ctx *ctx;
603
        struct vcl *vcl, *vcl2;
604
605 5635
        ASSERT_CLI();
606 5635
        ctx = vcl_get_ctx(0, 0);
607 11807
        VTAILQ_FOREACH_SAFE(vcl, &vcl_head, list, vcl2) {
608 12306
                if (vcl->temp == VCL_TEMP_BUSY ||
609 6134
                    vcl->temp == VCL_TEMP_COOLING) {
610 50
                        ctx->vcl = vcl;
611 50
                        ctx->syntax = ctx->vcl->conf->syntax;
612 50
                        ctx->method = 0;
613 50
                        (void)vcl_set_state(ctx, "0");
614
                }
615 6172
                if (vcl->discard && vcl->temp == VCL_TEMP_COLD) {
616 22
                        AZ(vcl->busy);
617 22
                        assert(vcl != vcl_active);
618 22
                        assert(VTAILQ_EMPTY(&vcl->ref_list));
619 22
                        VTAILQ_REMOVE(&vcl_head, vcl, list);
620 22
                        ctx->method = VCL_MET_FINI;
621 22
                        ctx->vcl = vcl;
622 22
                        ctx->syntax = ctx->vcl->conf->syntax;
623 22
                        AZ(vcl_send_event(ctx, VCL_EVENT_DISCARD));
624 22
                        vcl_KillBackends(vcl);
625 22
                        free(vcl->loaded_name);
626 22
                        VCL_Close(&vcl);
627 22
                        VSC_C_main->n_vcl--;
628 22
                        VSC_C_main->n_vcl_discard--;
629
                }
630
        }
631 5635
        vcl_rel_ctx(&ctx);
632 5635
}
633
634
/*--------------------------------------------------------------------*/
635
636
static void v_matchproto_(cli_func_t)
637 45
vcl_cli_list(struct cli *cli, const char * const *av, void *priv)
638
{
639
        struct vcl *vcl;
640
        const char *flg;
641
642
        /* NB: Shall generate same output as mcf_vcl_list() */
643
644
        (void)av;
645
        (void)priv;
646 45
        ASSERT_CLI();
647 139
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
648 94
                if (vcl == vcl_active) {
649 45
                        flg = "active";
650 49
                } else if (vcl->discard) {
651 4
                        flg = "discarded";
652
                } else
653 45
                        flg = "available";
654 188
                VCLI_Out(cli, "%-10s %5s/%-8s %6u %s",
655 94
                    flg, vcl->state, vcl->temp, vcl->busy, vcl->loaded_name);
656 94
                if (vcl->label != NULL) {
657 18
                        VCLI_Out(cli, " -> %s", vcl->label->loaded_name);
658 18
                        if (vcl->nrefs)
659 4
                                VCLI_Out(cli, " (%d return(vcl)%s)",
660 4
                                    vcl->nrefs, vcl->nrefs > 1 ? "'s" : "");
661 76
                } else if (vcl->nlabels > 0) {
662 12
                        VCLI_Out(cli, " (%d label%s)",
663 12
                            vcl->nlabels, vcl->nlabels > 1 ? "s" : "");
664
                }
665 94
                VCLI_Out(cli, "\n");
666
        }
667 45
}
668
669
static void v_matchproto_(cli_func_t)
670 845
vcl_cli_load(struct cli *cli, const char * const *av, void *priv)
671
{
672
        struct vrt_ctx *ctx;
673
674 845
        AZ(priv);
675 845
        ASSERT_CLI();
676 845
        ctx = vcl_get_ctx(VCL_MET_INIT, 1);
677 845
        vcl_load(cli, ctx, av[2], av[3], av[4]);
678 845
        vcl_rel_ctx(&ctx);
679 845
}
680
681
static void v_matchproto_(cli_func_t)
682 39
vcl_cli_state(struct cli *cli, const char * const *av, void *priv)
683
{
684
        struct vrt_ctx *ctx;
685
686 39
        AZ(priv);
687 39
        ASSERT_CLI();
688 39
        AN(av[2]);
689 39
        AN(av[3]);
690 39
        ctx = vcl_get_ctx(0, 1);
691 39
        ctx->vcl = vcl_find(av[2]);
692 39
        AN(ctx->vcl);                   // MGT ensures this
693 39
        if (vcl_set_state(ctx, av[3]) == 0) {
694 36
                bprintf(ctx->vcl->state, "%s", av[3] + 1);
695
        } else {
696 3
                AZ(VSB_finish(ctx->msg));
697 3
                VCLI_SetResult(cli, CLIS_CANT);
698 3
                VCLI_Out(cli, "Failed <vcl.state %s %s>", ctx->vcl->loaded_name,
699 3
                    av[3] + 1);
700 3
                if (VSB_len(ctx->msg))
701 3
                        VCLI_Out(cli, "\nMessage:\n\t%s", VSB_data(ctx->msg));
702
        }
703 39
        vcl_rel_ctx(&ctx);
704 39
}
705
706
static void v_matchproto_(cli_func_t)
707 29
vcl_cli_discard(struct cli *cli, const char * const *av, void *priv)
708
{
709
        struct vcl *vcl;
710
711 29
        ASSERT_CLI();
712
        (void)cli;
713 29
        AZ(priv);
714 29
        vcl = vcl_find(av[2]);
715 29
        AN(vcl);                        // MGT ensures this
716 29
        Lck_Lock(&vcl_mtx);
717 29
        assert (vcl != vcl_active);     // MGT ensures this
718 29
        AZ(vcl->nlabels);               // MGT ensures this
719 29
        VSC_C_main->n_vcl_discard++;
720 29
        VSC_C_main->n_vcl_avail--;
721 29
        vcl->discard = 1;
722 29
        if (vcl->label != NULL) {
723 4
                AZ(strcmp(vcl->state, VCL_TEMP_LABEL));
724 4
                vcl->label->nlabels--;
725 4
                vcl->label= NULL;
726
        }
727 29
        Lck_Unlock(&vcl_mtx);
728
729 29
        if (!strcmp(vcl->state, VCL_TEMP_LABEL)) {
730 4
                VTAILQ_REMOVE(&vcl_head, vcl, list);
731 4
                free(vcl->loaded_name);
732 4
                AZ(errno=pthread_rwlock_destroy(&vcl->temp_rwl));
733 4
                FREE_OBJ(vcl);
734 25
        } else if (vcl->temp == VCL_TEMP_COLD) {
735 19
                VCL_Poll();
736
        }
737 29
}
738
739
static void v_matchproto_(cli_func_t)
740 21
vcl_cli_label(struct cli *cli, const char * const *av, void *priv)
741
{
742
        struct vcl *lbl;
743
        struct vcl *vcl;
744
745 21
        ASSERT_CLI();
746
        (void)cli;
747
        (void)priv;
748 21
        vcl = vcl_find(av[3]);
749 21
        AN(vcl);                                // MGT ensures this
750 21
        lbl = vcl_find(av[2]);
751 21
        if (lbl == NULL) {
752 19
                ALLOC_OBJ(lbl, VCL_MAGIC);
753 19
                AN(lbl);
754 19
                bprintf(lbl->state, "%s", VCL_TEMP_LABEL);
755 19
                lbl->temp = VCL_TEMP_WARM;
756 19
                REPLACE(lbl->loaded_name, av[2]);
757 19
                AZ(errno=pthread_rwlock_init(&lbl->temp_rwl, NULL));
758 19
                VTAILQ_INSERT_TAIL(&vcl_head, lbl, list);
759
        }
760 21
        if (lbl->label != NULL)
761 2
                lbl->label->nlabels--;
762 21
        lbl->label = vcl;
763 21
        vcl->nlabels++;
764 21
}
765
766
static void v_matchproto_(cli_func_t)
767 804
vcl_cli_use(struct cli *cli, const char * const *av, void *priv)
768
{
769
        struct vcl *vcl;
770
771 804
        ASSERT_CLI();
772 804
        AN(cli);
773 804
        AZ(priv);
774 804
        vcl = vcl_find(av[2]);
775 804
        AN(vcl);                                // MGT ensures this
776 804
        assert(vcl->temp == VCL_TEMP_WARM);     // MGT ensures this
777 804
        Lck_Lock(&vcl_mtx);
778 804
        vcl_active = vcl;
779 804
        Lck_Unlock(&vcl_mtx);
780 804
}
781
782
static void v_matchproto_(cli_func_t)
783 10
vcl_cli_show(struct cli *cli, const char * const *av, void *priv)
784
{
785
        struct vcl *vcl;
786 10
        int verbose = 0;
787
        int i;
788
789 10
        ASSERT_CLI();
790 10
        AZ(priv);
791 10
        if (!strcmp(av[2], "-v") && av[3] == NULL) {
792 1
                VCLI_Out(cli, "Too few parameters");
793 1
                VCLI_SetResult(cli, CLIS_TOOFEW);
794 1
                return;
795 9
        } else if (strcmp(av[2], "-v") && av[3] != NULL) {
796 1
                VCLI_Out(cli, "Unknown options '%s'", av[2]);
797 1
                VCLI_SetResult(cli, CLIS_PARAM);
798 1
                return;
799 8
        } else if (av[3] != NULL) {
800 2
                verbose = 1;
801 2
                vcl = vcl_find(av[3]);
802
        } else
803 6
                vcl = vcl_find(av[2]);
804
805 8
        if (vcl == NULL) {
806 1
                VCLI_Out(cli, "No VCL named '%s'",
807 1
                    av[3] == NULL ? av[2] : av[3]);
808 1
                VCLI_SetResult(cli, CLIS_PARAM);
809 1
                return;
810
        }
811 7
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
812 7
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
813 7
        if (verbose) {
814 6
                for (i = 0; i < vcl->conf->nsrc; i++)
815 12
                        VCLI_Out(cli, "// VCL.SHOW %d %zd %s\n%s\n",
816 4
                            i, strlen(vcl->conf->srcbody[i]),
817 4
                            vcl->conf->srcname[i],
818 4
                            vcl->conf->srcbody[i]);
819
        } else {
820 5
                VCLI_Out(cli, "%s", vcl->conf->srcbody[0]);
821
        }
822
}
823
824
/*--------------------------------------------------------------------*/
825
826
static struct cli_proto vcl_cmds[] = {
827
        { CLICMD_VCL_LOAD,              "", vcl_cli_load },
828
        { CLICMD_VCL_LIST,              "", vcl_cli_list },
829
        { CLICMD_VCL_STATE,             "", vcl_cli_state },
830
        { CLICMD_VCL_DISCARD,           "", vcl_cli_discard },
831
        { CLICMD_VCL_USE,               "", vcl_cli_use },
832
        { CLICMD_VCL_SHOW,              "", vcl_cli_show },
833
        { CLICMD_VCL_LABEL,             "", vcl_cli_label },
834
        { NULL }
835
};
836
837
void
838 685
VCL_Init(void)
839
{
840
841 685
        assert(cache_param->workspace_client > 0);
842 685
        WS_Init(&ws_cli, "cli", malloc(cache_param->workspace_client),
843 685
            cache_param->workspace_client);
844 685
        ws_snapshot_cli = WS_Snapshot(&ws_cli);
845 685
        CLI_AddFuncs(vcl_cmds);
846 685
        Lck_New(&vcl_mtx, lck_vcl);
847 685
}