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 13183
VCL_Bo2Ctx(struct vrt_ctx *ctx, struct busyobj *bo)
74
{
75
76 13183
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
77 13183
        CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC);
78 13183
        ctx->vcl = bo->vcl;
79 13183
        ctx->vsl = bo->vsl;
80 13183
        ctx->http_bereq = bo->bereq;
81 13183
        ctx->http_beresp = bo->beresp;
82 13183
        ctx->bo = bo;
83 13183
        ctx->sp = bo->sp;
84 13183
        ctx->now = bo->t_prev;
85 13183
        ctx->ws = bo->ws;
86 13183
}
87
88
void
89 19594
VCL_Req2Ctx(struct vrt_ctx *ctx, struct req *req)
90
{
91
92 19594
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
93 19594
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
94
95 19594
        ctx->vcl = req->vcl;
96 19594
        ctx->vsl = req->vsl;
97 19594
        ctx->http_req = req->http;
98 19594
        ctx->http_req_top = req->top->http;
99 19594
        ctx->http_resp = req->resp;
100 19594
        ctx->req = req;
101 19594
        ctx->sp = req->sp;
102 19594
        ctx->now = req->t_prev;
103 19594
        ctx->ws = req->ws;
104 19594
}
105
106
/*--------------------------------------------------------------------*/
107
108
static struct vrt_ctx *
109 13205
vcl_get_ctx(unsigned method, int msg)
110
{
111
112 13205
        ASSERT_CLI();
113 13205
        AZ(ctx_cli.handling);
114 13205
        INIT_OBJ(&ctx_cli, VRT_CTX_MAGIC);
115 13205
        handling_cli = 0;
116 13205
        ctx_cli.handling = &handling_cli;
117 13205
        ctx_cli.method = method;
118 13205
        if (msg) {
119 1790
                ctx_cli.msg = VSB_new_auto();
120 1790
                AN(ctx_cli.msg);
121
        }
122 13205
        ctx_cli.ws = &ws_cli;
123 13205
        WS_Assert(ctx_cli.ws);
124 13205
        return (&ctx_cli);
125
}
126
127
static void
128 13205
vcl_rel_ctx(struct vrt_ctx **ctx)
129
{
130
131 13205
        ASSERT_CLI();
132 13205
        assert(*ctx == &ctx_cli);
133 13205
        AN((*ctx)->handling);
134 13205
        if (ctx_cli.msg)
135 1790
                VSB_destroy(&ctx_cli.msg);
136 13205
        WS_Assert(ctx_cli.ws);
137 13205
        WS_Reset(&ws_cli, ws_snapshot_cli);
138 13205
        INIT_OBJ(*ctx, VRT_CTX_MAGIC);
139 13205
        *ctx = NULL;
140 13205
}
141
142
/*--------------------------------------------------------------------*/
143
144
static int
145 3538
vcl_send_event(VRT_CTX, enum vcl_event_e ev)
146
{
147
        int r;
148
149 3538
        ASSERT_CLI();
150 3538
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
151 3538
        CHECK_OBJ_NOTNULL(ctx->vcl, VCL_MAGIC);
152 3538
        CHECK_OBJ_NOTNULL(ctx->vcl->conf, VCL_CONF_MAGIC);
153 3538
        assert(ev == VCL_EVENT_LOAD ||
154
               ev == VCL_EVENT_WARM ||
155
               ev == VCL_EVENT_COLD ||
156
               ev == VCL_EVENT_DISCARD);
157 3538
        AN(ctx->handling);
158 3538
        *ctx->handling = 0;
159 3538
        AN(ctx->ws);
160
161 3538
        if (ev == VCL_EVENT_LOAD || ev == VCL_EVENT_WARM)
162 3362
                AN(ctx->msg);
163
164 3538
        VCL_TaskEnter(ctx->vcl, cli_task_privs);
165 3538
        r = ctx->vcl->conf->event_vcl(ctx, ev);
166 3538
        VCL_TaskLeave(ctx->vcl, cli_task_privs);
167
168 3538
        if (r && (ev == VCL_EVENT_COLD || ev == VCL_EVENT_DISCARD))
169 0
                WRONG("A VMOD cannot fail COLD or DISCARD events");
170
171 3538
        return (r);
172
}
173
174
/*--------------------------------------------------------------------*/
175
176
struct vcl *
177 3606
vcl_find(const char *name)
178
{
179
        struct vcl *vcl;
180
181 3606
        ASSERT_CLI();
182 5280
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
183 3528
                if (vcl->discard)
184 32
                        continue;
185 3496
                if (!strcmp(vcl->loaded_name, name))
186 1854
                        return (vcl);
187
        }
188 1752
        return (NULL);
189
}
190
191
/*--------------------------------------------------------------------*/
192
193
void
194 10
VCL_Panic(struct vsb *vsb, const struct vcl *vcl)
195
{
196
        int i;
197
198 10
        AN(vsb);
199 10
        if (vcl == NULL)
200 0
                return;
201 10
        VSB_printf(vsb, "vcl = {\n");
202 10
        VSB_indent(vsb, 2);
203 10
        PAN_CheckMagic(vsb, vcl, VCL_MAGIC);
204 10
        VSB_printf(vsb, "name = \"%s\",\n", vcl->loaded_name);
205 10
        VSB_printf(vsb, "busy = %u,\n", vcl->busy);
206 10
        VSB_printf(vsb, "discard = %u,\n", vcl->discard);
207 10
        VSB_printf(vsb, "state = %s,\n", vcl->state);
208 10
        VSB_printf(vsb, "temp = %s,\n", (const volatile char *)vcl->temp);
209 10
        VSB_printf(vsb, "conf = {\n");
210 10
        VSB_indent(vsb, 2);
211 10
        if (vcl->conf == NULL) {
212 0
                VSB_printf(vsb, "conf = NULL\n");
213
        } else {
214 10
                PAN_CheckMagic(vsb, vcl->conf, VCL_CONF_MAGIC);
215 10
                VSB_printf(vsb, "srcname = {\n");
216 10
                VSB_indent(vsb, 2);
217 30
                for (i = 0; i < vcl->conf->nsrc; ++i)
218 20
                        VSB_printf(vsb, "\"%s\",\n", vcl->conf->srcname[i]);
219 10
                VSB_indent(vsb, -2);
220 10
                VSB_printf(vsb, "},\n");
221
        }
222 10
        VSB_indent(vsb, -2);
223 10
        VSB_printf(vsb, "},\n");
224 10
        VSB_indent(vsb, -2);
225 10
        VSB_printf(vsb, "},\n");
226
}
227
228
/*--------------------------------------------------------------------*/
229
230
void
231 1867
vcl_get(struct vcl **vcc, struct vcl *vcl)
232
{
233 1867
        AN(vcc);
234
235 1867
        Lck_Lock(&vcl_mtx);
236 1867
        if (vcl == NULL)
237 1847
                vcl = vcl_active; /* Sample vcl_active under lock to avoid
238
                                   * race */
239 1867
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
240 1867
        if (vcl->label == NULL) {
241 1841
                AN(strcmp(vcl->state, VCL_TEMP_LABEL));
242 1841
                *vcc = vcl;
243
        } else {
244 26
                AZ(strcmp(vcl->state, VCL_TEMP_LABEL));
245 26
                *vcc = vcl->label;
246
        }
247 1867
        CHECK_OBJ_NOTNULL(*vcc, VCL_MAGIC);
248 1867
        AZ((*vcc)->discard);
249 1867
        (*vcc)->busy++;
250 1867
        Lck_Unlock(&vcl_mtx);
251 1867
        AZ(errno=pthread_rwlock_rdlock(&(*vcc)->temp_rwl));
252 1867
        assert(VCL_WARM(*vcc));
253 1867
        AZ(errno=pthread_rwlock_unlock(&(*vcc)->temp_rwl));
254 1867
}
255
256
/*--------------------------------------------------------------------*/
257
258
static int
259 1444
vcl_iterdir(struct cli *cli, const char *pat, const struct vcl *vcl,
260
    vcl_be_func *func, void *priv)
261
{
262 1444
        int i, found = 0;
263
        struct vcldir *vdir;
264
265 3622
        VTAILQ_FOREACH(vdir, &vcl->director_list, list) {
266 2178
                if (fnmatch(pat, vdir->cli_name, 0))
267 194
                        continue;
268 1984
                found++;
269 1984
                i = func(cli, vdir->dir, priv);
270 1984
                if (i < 0)
271 0
                        return (i);
272 1984
                found += i;
273
        }
274 1444
        return (found);
275
}
276
277
int
278 1444
VCL_IterDirector(struct cli *cli, const char *pat,
279
    vcl_be_func *func, void *priv)
280
{
281 1444
        int i, found = 0;
282
        struct vsb *vsb;
283
        struct vcl *vcl;
284
285 1444
        ASSERT_CLI();
286 1444
        vsb = VSB_new_auto();
287 1444
        AN(vsb);
288 1444
        if (pat == NULL || *pat == '\0' || !strcmp(pat, "*")) {
289
                // all backends in active VCL
290 1364
                VSB_printf(vsb, "%s.*", VCL_Name(vcl_active));
291 1364
                vcl = vcl_active;
292 80
        } else if (strchr(pat, '.') == NULL) {
293
                // pattern applies to active vcl
294 68
                VSB_printf(vsb, "%s.%s", VCL_Name(vcl_active), pat);
295 68
                vcl = vcl_active;
296
        } else {
297
                // use pattern as is
298 12
                VSB_cat(vsb, pat);
299 12
                vcl = NULL;
300
        }
301 1444
        AZ(VSB_finish(vsb));
302 1444
        Lck_Lock(&vcl_mtx);
303 1444
        if (vcl != NULL) {
304 1432
                found = vcl_iterdir(cli, VSB_data(vsb), vcl, func, priv);
305
        } else {
306 24
                VTAILQ_FOREACH(vcl, &vcl_head, list) {
307 12
                        i = vcl_iterdir(cli, VSB_data(vsb), vcl, func, priv);
308 12
                        if (i < 0) {
309 0
                                found = i;
310 0
                                break;
311
                        } else {
312 12
                                found += i;
313
                        }
314
                }
315
        }
316 1444
        Lck_Unlock(&vcl_mtx);
317 1444
        VSB_destroy(&vsb);
318 1444
        return (found);
319
}
320
321
static void
322 1702
vcl_BackendEvent(const struct vcl *vcl, enum vcl_event_e e)
323
{
324
        struct vcldir *vdir;
325
326 1702
        ASSERT_CLI();
327 1702
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
328 1702
        AZ(vcl->busy);
329
330 3886
        VTAILQ_FOREACH(vdir, &vcl->director_list, list)
331 2184
                VDI_Event(vdir->dir, e);
332 1702
}
333
334
static void
335 118
vcl_KillBackends(struct vcl *vcl)
336
{
337
        struct vcldir *vdir;
338
339 118
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
340 118
        AZ(vcl->busy);
341 118
        assert(VTAILQ_EMPTY(&vcl->ref_list));
342
        while (1) {
343 366
                vdir = VTAILQ_FIRST(&vcl->director_list);
344 242
                if (vdir == NULL)
345 118
                        break;
346 124
                VTAILQ_REMOVE(&vcl->director_list, vdir, list);
347 124
                REPLACE(vdir->cli_name, NULL);
348 124
                AN(vdir->methods->destroy);
349 124
                vdir->methods->destroy(vdir->dir);
350 124
                FREE_OBJ(vdir->dir);
351 124
                FREE_OBJ(vdir);
352
        }
353 118
}
354
355
/*--------------------------------------------------------------------*/
356
357
static struct vcl *
358 3462
VCL_Open(const char *fn, struct vsb *msg)
359
{
360
        struct vcl *vcl;
361
        void *dlh;
362
        struct VCL_conf const *cnf;
363
364 3462
        AN(fn);
365 3462
        AN(msg);
366
367
#ifdef RTLD_NOLOAD
368
        /* Detect bogus caching by dlopen(3) */
369 3462
        dlh = dlopen(fn, RTLD_NOW | RTLD_NOLOAD);
370 3462
        AZ(dlh);
371
#endif
372 3462
        dlh = dlopen(fn, RTLD_NOW | RTLD_LOCAL);
373 3462
        if (dlh == NULL) {
374 0
                VSB_printf(msg, "Could not load compiled VCL.\n");
375 0
                VSB_printf(msg, "\tdlopen() = %s\n", dlerror());
376 0
                return (NULL);
377
        }
378 3462
        cnf = dlsym(dlh, "VCL_conf");
379 3462
        if (cnf == NULL) {
380 0
                VSB_printf(msg, "Compiled VCL lacks metadata.\n");
381 0
                (void)dlclose(dlh);
382 0
                return (NULL);
383
        }
384 3462
        if (cnf->magic != VCL_CONF_MAGIC) {
385 0
                VSB_printf(msg, "Compiled VCL has mangled metadata.\n");
386 0
                (void)dlclose(dlh);
387 0
                return (NULL);
388
        }
389 3462
        if (cnf->syntax < heritage.min_vcl || cnf->syntax > heritage.max_vcl) {
390 2
                VSB_printf(msg, "Compiled VCL version (%.1f) not supported.\n",
391 2
                    .1 * cnf->syntax);
392 2
                (void)dlclose(dlh);
393 2
                return (NULL);
394
        }
395 3460
        ALLOC_OBJ(vcl, VCL_MAGIC);
396 3460
        AN(vcl);
397 3460
        AZ(errno=pthread_rwlock_init(&vcl->temp_rwl, NULL));
398 3460
        vcl->dlh = dlh;
399 3460
        vcl->conf = cnf;
400 3460
        return (vcl);
401
}
402
403
static void
404 1866
VCL_Close(struct vcl **vclp)
405
{
406
        struct vcl *vcl;
407
408 1866
        CHECK_OBJ_NOTNULL(*vclp, VCL_MAGIC);
409 1866
        vcl = *vclp;
410 1866
        *vclp = NULL;
411 1866
        assert(VTAILQ_EMPTY(&vcl->vfps));
412 1866
        assert(VTAILQ_EMPTY(&vcl->vdps));
413 1866
        AZ(dlclose(vcl->dlh));
414 1866
        AZ(errno=pthread_rwlock_destroy(&vcl->temp_rwl));
415 1866
        FREE_OBJ(vcl);
416 1866
}
417
418
/*--------------------------------------------------------------------
419
 * NB: This function is called from the test-load subprocess.
420
 */
421
422
int
423 1750
VCL_TestLoad(const char *fn)
424
{
425
        struct vsb *vsb;
426
        struct vcl *vcl;
427 1750
        int retval = 0;
428
429 1750
        AN(fn);
430 1750
        vsb = VSB_new_auto();
431 1750
        AN(vsb);
432 1750
        vcl = VCL_Open(fn, vsb);
433 1750
        if (vcl == NULL) {
434 2
                AZ(VSB_finish(vsb));
435 2
                fprintf(stderr, "%s", VSB_data(vsb));
436 2
                retval = -1;
437
        } else
438 1748
                VCL_Close(&vcl);
439 1750
        VSB_destroy(&vsb);
440 1750
        return (retval);
441
}
442
443
/*--------------------------------------------------------------------*/
444
445
static void
446 2
vcl_print_refs(VRT_CTX)
447
{
448
        struct vcl *vcl;
449
        struct vclref *ref;
450
451 2
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
452 2
        CHECK_OBJ_NOTNULL(ctx->vcl, VCL_MAGIC);
453 2
        AN(ctx->msg);
454 2
        vcl = ctx->vcl;
455 2
        VSB_printf(ctx->msg, "VCL %s is waiting for:", vcl->loaded_name);
456 2
        Lck_Lock(&vcl_mtx);
457 4
        VTAILQ_FOREACH(ref, &ctx->vcl->ref_list, list)
458 2
                VSB_printf(ctx->msg, "\n\t- %s", ref->desc);
459 2
        Lck_Unlock(&vcl_mtx);
460 2
}
461
462
static int
463 1820
vcl_set_state(VRT_CTX, const char *state)
464
{
465
        struct vcl *vcl;
466 1820
        int i = 0;
467
468 1820
        ASSERT_CLI();
469 1820
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
470 1820
        CHECK_OBJ_NOTNULL(ctx->vcl, VCL_MAGIC);
471 1820
        CHECK_OBJ_NOTNULL(ctx->vcl->conf, VCL_CONF_MAGIC);
472 1820
        AN(ctx->handling);
473 1820
        AN(ctx->vcl);
474 1820
        AN(state);
475 1820
        assert(ctx->msg != NULL || *state == '0');
476
477 1820
        vcl = ctx->vcl;
478 1820
        AZ(errno=pthread_rwlock_wrlock(&vcl->temp_rwl));
479 1820
        AN(vcl->temp);
480
481 1820
        switch (state[0]) {
482
        case '0':
483 168
                assert(vcl->temp != VCL_TEMP_COLD);
484 168
                if (vcl->busy == 0 && VCL_WARM(vcl)) {
485
486 116
                        vcl->temp = VTAILQ_EMPTY(&vcl->ref_list) ?
487 58
                            VCL_TEMP_COLD : VCL_TEMP_COOLING;
488 58
                        AZ(vcl_send_event(ctx, VCL_EVENT_COLD));
489 58
                        vcl_BackendEvent(vcl, VCL_EVENT_COLD);
490
                }
491 110
                else if (vcl->busy)
492 82
                        vcl->temp = VCL_TEMP_BUSY;
493 28
                else if (VTAILQ_EMPTY(&vcl->ref_list))
494 20
                        vcl->temp = VCL_TEMP_COLD;
495
                else
496 8
                        vcl->temp = VCL_TEMP_COOLING;
497 168
                break;
498
        case '1':
499 1652
                assert(vcl->temp != VCL_TEMP_WARM);
500
                /* The warm VCL hasn't seen a cold event yet */
501 1652
                if (vcl->temp == VCL_TEMP_BUSY)
502 0
                        vcl->temp = VCL_TEMP_WARM;
503
                /* The VCL must first reach a stable cold state */
504 1652
                else if (vcl->temp == VCL_TEMP_COOLING) {
505 2
                        vcl_print_refs(ctx);
506 2
                        i = -1;
507
                }
508
                else {
509 1650
                        vcl->temp = VCL_TEMP_WARM;
510 1650
                        i = vcl_send_event(ctx, VCL_EVENT_WARM);
511 1650
                        if (i == 0)
512 1644
                                vcl_BackendEvent(vcl, VCL_EVENT_WARM);
513
                        else
514 6
                                AZ(vcl->conf->event_vcl(ctx, VCL_EVENT_COLD));
515
                }
516 1652
                break;
517
        default:
518 0
                WRONG("Wrong enum state");
519
        }
520 1820
        AZ(errno=pthread_rwlock_unlock(&vcl->temp_rwl));
521
522 1820
        return (i);
523
}
524
525
static void
526 74
vcl_cancel_load(VRT_CTX, struct cli *cli, const char *name, const char *step)
527
{
528 74
        struct vcl *vcl = ctx->vcl;
529
530 74
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
531 74
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
532
533 74
        AZ(VSB_finish(ctx->msg));
534 74
        VCLI_SetResult(cli, CLIS_CANT);
535 74
        VCLI_Out(cli, "VCL \"%s\" Failed %s", name, step);
536 74
        if (VSB_len(ctx->msg))
537 74
                VCLI_Out(cli, "\nMessage:\n\t%s", VSB_data(ctx->msg));
538 74
        *ctx->handling = 0;
539 74
        AZ(vcl_send_event(ctx, VCL_EVENT_DISCARD));
540 74
        vcl_KillBackends(vcl);
541 74
        free(vcl->loaded_name);
542 74
        VCL_Close(&vcl);
543 74
}
544
545
static void
546 1712
vcl_load(struct cli *cli, struct vrt_ctx *ctx,
547
    const char *name, const char *fn, const char *state)
548
{
549
        struct vcl *vcl;
550
        int i;
551
552 1712
        ASSERT_CLI();
553
554 1712
        vcl = vcl_find(name);
555 1712
        AZ(vcl);
556
557 1712
        vcl = VCL_Open(fn, ctx->msg);
558 1712
        if (vcl == NULL) {
559 0
                AZ(VSB_finish(ctx->msg));
560 0
                VCLI_SetResult(cli, CLIS_PARAM);
561 0
                VCLI_Out(cli, "%s", VSB_data(ctx->msg));
562 0
                return;
563
        }
564
565 1712
        vcl->loaded_name = strdup(name);
566 1712
        XXXAN(vcl->loaded_name);
567 1712
        VTAILQ_INIT(&vcl->director_list);
568 1712
        VTAILQ_INIT(&vcl->ref_list);
569 1712
        VTAILQ_INIT(&vcl->vfps);
570 1712
        VTAILQ_INIT(&vcl->vdps);
571
572 1712
        vcl->temp = VCL_TEMP_INIT;
573
574 1712
        ctx->vcl = vcl;
575
576 1712
        VSB_clear(ctx->msg);
577 1712
        i = vcl_send_event(ctx, VCL_EVENT_LOAD);
578 1712
        if (i || *ctx->handling != VCL_RET_OK) {
579 72
                vcl_cancel_load(ctx, cli, name, "initialization");
580 72
                return;
581
        }
582 1640
        assert(*ctx->handling == VCL_RET_OK);
583 1640
        VSB_clear(ctx->msg);
584 1640
        i = vcl_set_state(ctx, state);
585 1640
        if (i) {
586 2
                assert(*state == '1');
587 2
                vcl_cancel_load(ctx, cli, name, "warmup");
588 2
                return;
589
        }
590 1638
        bprintf(vcl->state, "%s", state + 1);
591 1638
        VCLI_Out(cli, "Loaded \"%s\" as \"%s\"", fn , name);
592 1638
        VTAILQ_INSERT_TAIL(&vcl_head, vcl, list);
593 1638
        Lck_Lock(&vcl_mtx);
594 1638
        if (vcl_active == NULL)
595 1374
                vcl_active = vcl;
596 1638
        Lck_Unlock(&vcl_mtx);
597 1638
        VSC_C_main->n_vcl++;
598 1638
        VSC_C_main->n_vcl_avail++;
599
}
600
601
/*--------------------------------------------------------------------*/
602
603
void
604 11415
VCL_Poll(void)
605
{
606
        struct vrt_ctx *ctx;
607
        struct vcl *vcl, *vcl2;
608
609 11415
        ASSERT_CLI();
610 11415
        ctx = vcl_get_ctx(0, 0);
611 24012
        VTAILQ_FOREACH_SAFE(vcl, &vcl_head, list, vcl2) {
612 25118
                if (vcl->temp == VCL_TEMP_BUSY ||
613 12521
                    vcl->temp == VCL_TEMP_COOLING) {
614 102
                        ctx->vcl = vcl;
615 102
                        ctx->syntax = ctx->vcl->conf->syntax;
616 102
                        ctx->method = 0;
617 102
                        (void)vcl_set_state(ctx, "0");
618
                }
619 12597
                if (vcl->discard && vcl->temp == VCL_TEMP_COLD) {
620 44
                        AZ(vcl->busy);
621 44
                        assert(vcl != vcl_active);
622 44
                        assert(VTAILQ_EMPTY(&vcl->ref_list));
623 44
                        VTAILQ_REMOVE(&vcl_head, vcl, list);
624 44
                        ctx->method = VCL_MET_FINI;
625 44
                        ctx->vcl = vcl;
626 44
                        ctx->syntax = ctx->vcl->conf->syntax;
627 44
                        AZ(vcl_send_event(ctx, VCL_EVENT_DISCARD));
628 44
                        vcl_KillBackends(vcl);
629 44
                        free(vcl->loaded_name);
630 44
                        VCL_Close(&vcl);
631 44
                        VSC_C_main->n_vcl--;
632 44
                        VSC_C_main->n_vcl_discard--;
633
                }
634
        }
635 11415
        vcl_rel_ctx(&ctx);
636 11415
}
637
638
/*--------------------------------------------------------------------*/
639
640
static void v_matchproto_(cli_func_t)
641 92
vcl_cli_list(struct cli *cli, const char * const *av, void *priv)
642
{
643
        struct vcl *vcl;
644
        const char *flg;
645
646
        /* NB: Shall generate same output as mcf_vcl_list() */
647
648
        (void)av;
649
        (void)priv;
650 92
        ASSERT_CLI();
651 284
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
652 192
                if (vcl == vcl_active) {
653 92
                        flg = "active";
654 100
                } else if (vcl->discard) {
655 8
                        flg = "discarded";
656
                } else
657 92
                        flg = "available";
658 384
                VCLI_Out(cli, "%-10s %5s/%-8s %6u %s",
659 192
                    flg, vcl->state, vcl->temp, vcl->busy, vcl->loaded_name);
660 192
                if (vcl->label != NULL) {
661 36
                        VCLI_Out(cli, " -> %s", vcl->label->loaded_name);
662 36
                        if (vcl->nrefs)
663 8
                                VCLI_Out(cli, " (%d return(vcl)%s)",
664 8
                                    vcl->nrefs, vcl->nrefs > 1 ? "'s" : "");
665 156
                } else if (vcl->nlabels > 0) {
666 24
                        VCLI_Out(cli, " (%d label%s)",
667 24
                            vcl->nlabels, vcl->nlabels > 1 ? "s" : "");
668
                }
669 192
                VCLI_Out(cli, "\n");
670
        }
671 92
}
672
673
static void v_matchproto_(cli_func_t)
674 10
vcl_cli_list_json(struct cli *cli, const char * const *av, void *priv)
675
{
676
        struct vcl *vcl;
677
678
        (void)priv;
679 10
        ASSERT_CLI();
680 10
        VCLI_JSON_begin(cli, 2, av);
681 10
        VCLI_Out(cli, ",\n");
682 42
        VTAILQ_FOREACH(vcl, &vcl_head, list) {
683 32
                VCLI_Out(cli, "{\n");
684 32
                VSB_indent(cli->sb, 2);
685 32
                VCLI_Out(cli, "\"status\": ");
686 32
                if (vcl == vcl_active) {
687 10
                        VCLI_Out(cli, "\"active\",\n");
688 22
                } else if (vcl->discard) {
689 0
                        VCLI_Out(cli, "\"discarded\",\n");
690
                } else
691 22
                        VCLI_Out(cli, "\"available\",\n");
692 32
                VCLI_Out(cli, "\"state\": \"%s\",\n", vcl->state);
693 32
                VCLI_Out(cli, "\"temperature\": \"%s\",\n", vcl->temp);
694 32
                VCLI_Out(cli, "\"busy\": %u,\n", vcl->busy);
695 32
                VCLI_Out(cli, "\"name\": \"%s\"", vcl->loaded_name);
696 32
                if (vcl->label != NULL) {
697 12
                        VCLI_Out(cli, ",\n");
698 12
                        VCLI_Out(cli, "\"label\": {\n");
699 12
                        VSB_indent(cli->sb, 2);
700 12
                                VCLI_Out(cli, "\"name\": \"%s\"",
701 12
                                         vcl->label->loaded_name);
702 12
                        if (vcl->nrefs)
703 0
                                VCLI_Out(cli, ",\n\"refs\": %d", vcl->nrefs);
704 12
                        VCLI_Out(cli, "\n");
705 12
                        VCLI_Out(cli, "}");
706 12
                        VSB_indent(cli->sb, -2);
707 20
                } else if (vcl->nlabels > 0) {
708 4
                        VCLI_Out(cli, ",\n");
709 4
                        VCLI_Out(cli, "\"labels\": %d", vcl->nlabels);
710
                }
711 32
                VSB_indent(cli->sb, -2);
712 32
                VCLI_Out(cli, "\n}");
713 32
                if (VTAILQ_NEXT(vcl, list) != NULL)
714 22
                        VCLI_Out(cli, ",\n");
715
        }
716 10
        VCLI_JSON_end(cli);
717 10
}
718
719
static void v_matchproto_(cli_func_t)
720 1712
vcl_cli_load(struct cli *cli, const char * const *av, void *priv)
721
{
722
        struct vrt_ctx *ctx;
723
724 1712
        AZ(priv);
725 1712
        ASSERT_CLI();
726 1712
        ctx = vcl_get_ctx(VCL_MET_INIT, 1);
727 1712
        vcl_load(cli, ctx, av[2], av[3], av[4]);
728 1712
        vcl_rel_ctx(&ctx);
729 1712
}
730
731
static void v_matchproto_(cli_func_t)
732 78
vcl_cli_state(struct cli *cli, const char * const *av, void *priv)
733
{
734
        struct vrt_ctx *ctx;
735
736 78
        AZ(priv);
737 78
        ASSERT_CLI();
738 78
        AN(av[2]);
739 78
        AN(av[3]);
740 78
        ctx = vcl_get_ctx(0, 1);
741 78
        ctx->vcl = vcl_find(av[2]);
742 78
        AN(ctx->vcl);                   // MGT ensures this
743 78
        if (vcl_set_state(ctx, av[3]) == 0) {
744 72
                bprintf(ctx->vcl->state, "%s", av[3] + 1);
745
        } else {
746 6
                AZ(VSB_finish(ctx->msg));
747 6
                VCLI_SetResult(cli, CLIS_CANT);
748 6
                VCLI_Out(cli, "Failed <vcl.state %s %s>", ctx->vcl->loaded_name,
749 6
                    av[3] + 1);
750 6
                if (VSB_len(ctx->msg))
751 6
                        VCLI_Out(cli, "\nMessage:\n\t%s", VSB_data(ctx->msg));
752
        }
753 78
        vcl_rel_ctx(&ctx);
754 78
}
755
756
static void v_matchproto_(cli_func_t)
757 58
vcl_cli_discard(struct cli *cli, const char * const *av, void *priv)
758
{
759
        struct vcl *vcl;
760
761 58
        ASSERT_CLI();
762
        (void)cli;
763 58
        AZ(priv);
764 58
        vcl = vcl_find(av[2]);
765 58
        AN(vcl);                        // MGT ensures this
766 58
        Lck_Lock(&vcl_mtx);
767 58
        assert (vcl != vcl_active);     // MGT ensures this
768 58
        AZ(vcl->nlabels);               // MGT ensures this
769 58
        VSC_C_main->n_vcl_discard++;
770 58
        VSC_C_main->n_vcl_avail--;
771 58
        vcl->discard = 1;
772 58
        if (vcl->label != NULL) {
773 8
                AZ(strcmp(vcl->state, VCL_TEMP_LABEL));
774 8
                vcl->label->nlabels--;
775 8
                vcl->label= NULL;
776
        }
777 58
        Lck_Unlock(&vcl_mtx);
778
779 58
        if (!strcmp(vcl->state, VCL_TEMP_LABEL)) {
780 8
                VTAILQ_REMOVE(&vcl_head, vcl, list);
781 8
                free(vcl->loaded_name);
782 8
                AZ(errno=pthread_rwlock_destroy(&vcl->temp_rwl));
783 8
                FREE_OBJ(vcl);
784 50
        } else if (vcl->temp == VCL_TEMP_COLD) {
785 38
                VCL_Poll();
786
        }
787 58
}
788
789
static void v_matchproto_(cli_func_t)
790 42
vcl_cli_label(struct cli *cli, const char * const *av, void *priv)
791
{
792
        struct vcl *lbl;
793
        struct vcl *vcl;
794
795 42
        ASSERT_CLI();
796
        (void)cli;
797
        (void)priv;
798 42
        vcl = vcl_find(av[3]);
799 42
        AN(vcl);                                // MGT ensures this
800 42
        lbl = vcl_find(av[2]);
801 42
        if (lbl == NULL) {
802 38
                ALLOC_OBJ(lbl, VCL_MAGIC);
803 38
                AN(lbl);
804 38
                bprintf(lbl->state, "%s", VCL_TEMP_LABEL);
805 38
                lbl->temp = VCL_TEMP_WARM;
806 38
                REPLACE(lbl->loaded_name, av[2]);
807 38
                AZ(errno=pthread_rwlock_init(&lbl->temp_rwl, NULL));
808 38
                VTAILQ_INSERT_TAIL(&vcl_head, lbl, list);
809
        }
810 42
        if (lbl->label != NULL)
811 4
                lbl->label->nlabels--;
812 42
        lbl->label = vcl;
813 42
        vcl->nlabels++;
814 42
}
815
816
static void v_matchproto_(cli_func_t)
817 1626
vcl_cli_use(struct cli *cli, const char * const *av, void *priv)
818
{
819
        struct vcl *vcl;
820
821 1626
        ASSERT_CLI();
822 1626
        AN(cli);
823 1626
        AZ(priv);
824 1626
        vcl = vcl_find(av[2]);
825 1626
        AN(vcl);                                // MGT ensures this
826 1626
        assert(vcl->temp == VCL_TEMP_WARM);     // MGT ensures this
827 1626
        Lck_Lock(&vcl_mtx);
828 1626
        vcl_active = vcl;
829 1626
        Lck_Unlock(&vcl_mtx);
830 1626
}
831
832
static void v_matchproto_(cli_func_t)
833 20
vcl_cli_show(struct cli *cli, const char * const *av, void *priv)
834
{
835
        struct vcl *vcl;
836 20
        int verbose = 0;
837
        int i;
838
839 20
        ASSERT_CLI();
840 20
        AZ(priv);
841 20
        if (!strcmp(av[2], "-v") && av[3] == NULL) {
842 2
                VCLI_Out(cli, "Too few parameters");
843 2
                VCLI_SetResult(cli, CLIS_TOOFEW);
844 2
                return;
845 18
        } else if (strcmp(av[2], "-v") && av[3] != NULL) {
846 2
                VCLI_Out(cli, "Unknown options '%s'", av[2]);
847 2
                VCLI_SetResult(cli, CLIS_PARAM);
848 2
                return;
849 16
        } else if (av[3] != NULL) {
850 4
                verbose = 1;
851 4
                vcl = vcl_find(av[3]);
852
        } else
853 12
                vcl = vcl_find(av[2]);
854
855 16
        if (vcl == NULL) {
856 2
                VCLI_Out(cli, "No VCL named '%s'",
857 2
                    av[3] == NULL ? av[2] : av[3]);
858 2
                VCLI_SetResult(cli, CLIS_PARAM);
859 2
                return;
860
        }
861 14
        CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC);
862 14
        CHECK_OBJ_NOTNULL(vcl->conf, VCL_CONF_MAGIC);
863 14
        if (verbose) {
864 12
                for (i = 0; i < vcl->conf->nsrc; i++)
865 24
                        VCLI_Out(cli, "// VCL.SHOW %d %zd %s\n%s\n",
866 8
                            i, strlen(vcl->conf->srcbody[i]),
867 8
                            vcl->conf->srcname[i],
868 8
                            vcl->conf->srcbody[i]);
869
        } else {
870 10
                VCLI_Out(cli, "%s", vcl->conf->srcbody[0]);
871
        }
872
}
873
874
/*--------------------------------------------------------------------*/
875
876
static struct cli_proto vcl_cmds[] = {
877
        { CLICMD_VCL_LOAD,              "", vcl_cli_load },
878
        { CLICMD_VCL_LIST,              "", vcl_cli_list, vcl_cli_list_json },
879
        { CLICMD_VCL_STATE,             "", vcl_cli_state },
880
        { CLICMD_VCL_DISCARD,           "", vcl_cli_discard },
881
        { CLICMD_VCL_USE,               "", vcl_cli_use },
882
        { CLICMD_VCL_SHOW,              "", vcl_cli_show },
883
        { CLICMD_VCL_LABEL,             "", vcl_cli_label },
884
        { NULL }
885
};
886
887
void
888 1376
VCL_Init(void)
889
{
890
891 1376
        assert(cache_param->workspace_client > 0);
892 1376
        WS_Init(&ws_cli, "cli", malloc(cache_param->workspace_client),
893 1376
            cache_param->workspace_client);
894 1376
        ws_snapshot_cli = WS_Snapshot(&ws_cli);
895 1376
        CLI_AddFuncs(vcl_cmds);
896 1376
        Lck_New(&vcl_mtx, lck_vcl);
897 1376
}