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