1 /*-
2 * Copyright (c) 2015-2016 Varnish Software AS
3 * Copyright 2017-2019 UPLEX - Nils Goroll Systemoptimierung
4 * All rights reserved.
5 *
6 * Authors: Dridi Boukelmoune <dridi.boukelmoune@gmail.com>
7 * Nils Goroll <nils.goroll@uplex.de>
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 * Support for SRV records.
31 *
32 * For now, this code is basically a copy of large extents of vmod_dynamic.c as
33 * a first iteration. Consolidation is left for later.
34 */
35
36 #include "config.h"
37
38 #include <arpa/inet.h>
39
40 #include <sys/socket.h>
41 #include <sys/types.h>
42
43 #include <errno.h>
44 #include <netdb.h>
45 #include <pthread.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49
50 #include <cache/cache.h>
51
52 #include <vtim.h>
53 #include <vrnd.h>
54
55 #include "vcc_dynamic_if.h"
56 #include "dyn_resolver.h"
57 #include "vmod_dynamic.h"
58 #include "vmb.h"
59
60 #define LOG(ctx, slt, srv, fmt, ...) \
61 do { \
62 if ((ctx)->vsl != NULL) \
63 VSLb((ctx)->vsl, slt, \
64 "vmod-dynamic: %s %s %s " fmt, \
65 (srv)->obj->vcl_conf, \
66 (srv)->obj->vcl_name, \
67 (srv)->service, __VA_ARGS__); \
68 else \
69 VSL(slt, 0, \
70 "vmod-dynamic: %s %s %s " fmt, \
71 (srv)->obj->vcl_conf, \
72 (srv)->obj->vcl_name, \
73 (srv)->service, __VA_ARGS__); \
74 } while (0)
75
76 #define DBG(ctx, srv, fmt, ...) \
77 do { \
78 if ((srv)->obj->debug) \
79 LOG(ctx, SLT_Debug, srv, fmt, __VA_ARGS__); \
80 } while (0)
81
82 static VCL_BACKEND v_matchproto_(vdi_resolve_f)
83 service_resolve(VRT_CTX, VCL_BACKEND);
84 static VCL_BOOL v_matchproto_(vdi_healthy_f)
85 service_healthy(VRT_CTX, VCL_BACKEND, VCL_TIME *);
86
87 static const struct vdi_methods vmod_dynamic_service_methods[1] = {{
88 .magic = VDI_METHODS_MAGIC,
89 .type = "dynamic service",
90 .healthy = service_healthy,
91 .resolve = service_resolve
92 }};
93
94 /*--------------------------------------------------------------------
95 * Service director implementation
96 */
97
98 /* select healthy backends */
99 struct backend_select {
100 VCL_BACKEND d;
101 uint32_t w;
102 };
103
v_matchproto_(vdi_resolve_f)104 static VCL_BACKEND v_matchproto_(vdi_resolve_f)
105 service_resolve(VRT_CTX, VCL_BACKEND d)
106 {
107 struct dynamic_service *srv;
108 const struct service_prios *prios;
109 const struct service_prio *p;
110 const struct service_target *t;
111 double deadline;
112 int ret;
113
114 CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
115 CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
116 CAST_OBJ_NOTNULL(srv, d->priv, DYNAMIC_SERVICE_MAGIC);
117
118 Lck_Lock(&srv->mtx);
119
120 if (srv->status < DYNAMIC_ST_ACTIVE) {
121 deadline = VTIM_real() + srv->obj->first_lookup_tmo;
122 ret = Lck_CondWait(&srv->resolve, &srv->mtx, deadline);
123 assert(ret == 0 || ret == ETIMEDOUT);
124 }
125
126 if (srv->status > DYNAMIC_ST_ACTIVE) {
127 Lck_Unlock(&srv->mtx);
128 return (NULL);
129 }
130 Lck_Unlock(&srv->mtx);
131
132 VRMB();
133 prios = srv->prios;
134
135 if (prios == NULL)
136 return (NULL);
137
138 struct backend_select h[prios->max_targets];
139 unsigned i, n, w;
140 long r;
141
142 memset(h, 0, sizeof h);
143
144 n = w = 0;
145 VTAILQ_FOREACH(p, &prios->head, list) {
146 CHECK_OBJ_NOTNULL(p, SERVICE_PRIO_MAGIC);
147 n = w = 0;
148 VTAILQ_FOREACH(t, &p->targets, list) {
149 CHECK_OBJ_NOTNULL(t, SERVICE_TARGET_MAGIC);
150 CHECK_OBJ_NOTNULL(t->dom, DYNAMIC_DOMAIN_MAGIC);
151 if (! VRT_Healthy(ctx, t->dom->dir, NULL))
152 continue;
153 h[n].d = t->dom->dir;
154 h[n].w = t->weight;
155 w += t->weight;
156 n++;
157 }
158 assert(n <= prios->max_targets);
159 if (n > 0)
160 break;
161 }
162 if (n == 0)
163 return (NULL);
164 if (n == 1)
165 return (h[0].d);
166 // fixup zero weight
167 if (w == 0) {
168 for (i = 0; i < n; i++)
169 h[i].w = 1;
170 w = n;
171 }
172 r = VRND_RandomTestable() % w;
173 w = 0;
174 for (i = 0; i < n; i++) {
175 w += h[i].w;
176 if (r < w)
177 return (h[i].d);
178 }
179 WRONG("");
180 }
181
v_matchproto_(vdi_healthy_f)182 static VCL_BOOL v_matchproto_(vdi_healthy_f)
183 service_healthy(VRT_CTX, VCL_BACKEND d, VCL_TIME *changed)
184 {
185 struct dynamic_service *srv;
186 const struct service_prios *prios;
187 const struct service_prio *p;
188 const struct service_target *t;
189 VCL_TIME c;
190 VCL_BOOL ret = 0;
191
192 CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
193 CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
194 CAST_OBJ_NOTNULL(srv, d->priv, DYNAMIC_SERVICE_MAGIC);
195
196 VRMB();
197 prios = srv->prios;
198
199 if (prios == NULL)
200 return (0);
201
202 if (changed != NULL)
203 *changed = 0;
204
205 VTAILQ_FOREACH(p, &prios->head, list) {
206 CHECK_OBJ_NOTNULL(p, SERVICE_PRIO_MAGIC);
207 VTAILQ_FOREACH(t, &p->targets, list) {
208 CHECK_OBJ_NOTNULL(t, SERVICE_TARGET_MAGIC);
209 CHECK_OBJ_NOTNULL(t->dom, DYNAMIC_DOMAIN_MAGIC);
210 ret |= VRT_Healthy(ctx, t->dom->dir, &c);
211 if (changed != NULL && c > *changed)
212 *changed = c;
213 }
214 }
215
216 return (ret);
217 }
218
219 /*--------------------------------------------------------------------
220 * Background job
221 */
222
223 /* add all the dom objects an ensure they are active */
224
225 static void
service_doms(VRT_CTX,struct vmod_dynamic_director * obj,struct service_prios * prios)226 service_doms(VRT_CTX, struct vmod_dynamic_director *obj,
227 struct service_prios *prios)
228 {
229 struct dynamic_domain *dom;
230 struct service_prio *p;
231 struct service_target *t;
232 char portbuf[6];
233 unsigned n;
234 int ret;
235 double deadline;
236
237 CHECK_OBJ_NOTNULL(prios, SERVICE_PRIOS_MAGIC);
238
239 Lck_Lock(&obj->mtx);
240 VTAILQ_FOREACH(p, &prios->head, list) {
241 CHECK_OBJ_NOTNULL(p, SERVICE_PRIO_MAGIC);
242 n = 0;
243 VTAILQ_FOREACH(t, &p->targets, list) {
244 CHECK_OBJ_NOTNULL(t, SERVICE_TARGET_MAGIC);
245 bprintf(portbuf, "%u", t->port);
246 t->dom = dynamic_get(ctx, obj, t->target, portbuf);
247 AN(t->dom);
248 t->dom->last_used = ctx->now;
249 n++;
250 }
251 p->n_targets = n;
252 if (n > prios->max_targets)
253 prios->max_targets = n;
254 }
255 Lck_Unlock(&obj->mtx);
256
257 VTAILQ_FOREACH(p, &prios->head, list) {
258 CHECK_OBJ_NOTNULL(p, SERVICE_PRIO_MAGIC);
259 VTAILQ_FOREACH(t, &p->targets, list) {
260 CHECK_OBJ_NOTNULL(t, SERVICE_TARGET_MAGIC);
261 dom = t->dom;
262 CHECK_OBJ_NOTNULL(dom, DYNAMIC_DOMAIN_MAGIC);
263 if (dom->status >= DYNAMIC_ST_ACTIVE)
264 continue;
265 Lck_Lock(&dom->mtx);
266 while (dom->status < DYNAMIC_ST_ACTIVE) {
267 deadline = VTIM_real() +
268 dom->obj->first_lookup_tmo;
269 ret = Lck_CondWait(&dom->resolve, &dom->mtx,
270 deadline);
271 assert(ret == 0 || ret == ETIMEDOUT);
272 }
273 Lck_Unlock(&dom->mtx);
274 }
275 }
276 }
277
278 static void
service_prios_free(struct service_prios ** priosp)279 service_prios_free(struct service_prios **priosp)
280 {
281 struct service_prios *prios = *priosp;
282 struct service_prio *p, *pt;
283 struct service_target *t, *tt;
284
285 TAKE_OBJ_NOTNULL(prios, priosp, SERVICE_PRIOS_MAGIC);
286 VTAILQ_FOREACH_SAFE(p, &prios->head, list, pt) {
287 CHECK_OBJ_NOTNULL(p, SERVICE_PRIO_MAGIC);
288 VTAILQ_FOREACH_SAFE(t, &p->targets, list, tt) {
289 CHECK_OBJ_NOTNULL(t, SERVICE_TARGET_MAGIC);
290 AN(t->target);
291 free(t->target);
292 FREE_OBJ(t);
293 }
294 FREE_OBJ(p);
295 }
296 FREE_OBJ(prios);
297 }
298
299 static struct service_prio *
service_prio(struct service_prios * prios,uint32_t priority)300 service_prio(struct service_prios *prios, uint32_t priority)
301 {
302 struct service_prio *p, *prio;
303
304 VTAILQ_FOREACH(p, &prios->head, list) {
305 if (p->priority == priority)
306 return (p);
307 if (p->priority > priority)
308 break;
309 }
310
311 ALLOC_OBJ(prio, SERVICE_PRIO_MAGIC);
312 AN(prio);
313 prio->priority = priority;
314 VTAILQ_INIT(&prio->targets);
315
316 if (p)
317 VTAILQ_INSERT_BEFORE(p, prio, list);
318 else
319 VTAILQ_INSERT_TAIL(&prios->head, prio, list);
320
321 return (prio);
322 }
323
324 /* we order targets for deterministic testing by target and port.
325 * being at it, we also just add the weight for identical target:port
326 */
327
328 static int
target_cmp(const struct service_target * t,const struct srv_info * i)329 target_cmp(const struct service_target *t, const struct srv_info *i)
330 {
331 int ret;
332
333 CHECK_OBJ_NOTNULL(t, SERVICE_TARGET_MAGIC);
334 AN(i);
335
336 ret = strcmp(t->target, i->target);
337 if (ret != 0)
338 return (ret);
339 if (t->port == i->port)
340 return (0);
341 return (t->port < i->port ? -1 : 1);
342 }
343
344 static struct service_target *
service_target(struct service_prio * prio,const struct srv_info * i)345 service_target(struct service_prio *prio, const struct srv_info *i)
346 {
347 struct service_target *t, *target;
348 int cmp;
349
350 VTAILQ_FOREACH(t, &prio->targets, list) {
351 cmp = target_cmp(t, i);
352 if (cmp == 0)
353 return (t);
354 if (cmp > 0)
355 break;
356 }
357
358 ALLOC_OBJ(target, SERVICE_TARGET_MAGIC);
359 AN(target);
360
361 if (t)
362 VTAILQ_INSERT_BEFORE(t, target, list);
363 else
364 VTAILQ_INSERT_TAIL(&prio->targets, target, list);
365
366 return (target);
367 }
368
369 static void
service_update(struct dynamic_service * srv,const struct res_cb * res,void * priv,vtim_real now)370 service_update(struct dynamic_service *srv, const struct res_cb *res,
371 void *priv, vtim_real now)
372 {
373 struct vrt_ctx ctx;
374 struct srv_info ibuf[1] = {{ 0 }};
375 struct srv_info *info;
376 void *state = NULL;
377 vtim_dur ttl = NAN;
378 struct service_prios *prios;
379 struct service_prio *prio = NULL;
380 struct service_target *target;
381
382 INIT_OBJ(&ctx, VRT_CTX_MAGIC);
383 ctx.vcl = srv->obj->vcl;
384 ctx.now = now;
385
386 /*
387 * we free any cold prio/target tree, create a new one, then swap
388 * the single head pointer after a membar
389 *
390 * director resolve races us
391 */
392
393 ALLOC_OBJ(prios, SERVICE_PRIOS_MAGIC);
394 AN(prios);
395 VTAILQ_INIT(&prios->head);
396 while ((info = res->srv_result(ibuf, priv, &state)) != NULL) {
397 DBG(&ctx, srv, "DNS SRV %s:%d priority %d weight %d ttl %d",
398 info->target, info->port, info->priority,
399 info->weight, info->ttl);
400
401 if (prio != NULL && prio->priority != info->priority)
402 prio = NULL;
403 if (prio == NULL)
404 prio = service_prio(prios, info->priority);
405
406 target = service_target(prio, info);
407
408 if (target->target != NULL) {
409 // existing
410 assert(target->port == info->port);
411 target->weight += info->weight;
412 free(info->target);
413 info->target = NULL;
414 } else {
415 target->port = info->port;
416 target->weight = info->weight;
417 // target is malloc'ed - take it
418 target->target = info->target;
419 info->target = NULL;
420 }
421
422 if (info->ttl != 0 && (isnan(ttl) || info->ttl < ttl))
423 ttl = info->ttl;
424
425 DBG(&ctx, srv, "target %s:%d priority %d weight %d ttl %f",
426 target->target, target->port, prio->priority,
427 target->weight, ttl);
428 }
429
430 service_doms(&ctx, srv->obj, prios);
431
432 if (srv->prios_cold != NULL)
433 service_prios_free(&srv->prios_cold);
434
435 VWMB();
436 AZ(srv->prios_cold);
437 srv->prios_cold = srv->prios;
438 srv->prios = prios;
439
440 if (isnan(ttl)) {
441 ttl = srv->obj->ttl;
442 } else if (srv->obj->ttl_from == cfg) {
443 ttl = srv->obj->ttl;
444 } else if (srv->obj->ttl_from == min) {
445 if (srv->obj->ttl < ttl)
446 ttl = srv->obj->ttl;
447 } else if (srv->obj->ttl_from == max) {
448 if (srv->obj->ttl > ttl)
449 ttl = srv->obj->ttl;
450 } else {
451 assert(srv->obj->ttl_from == dns);
452 }
453 srv->deadline = now + ttl;
454
455 DBG(&ctx, srv, "deadline %f ttl %f", srv->deadline, ttl);
456 }
457
458 static void
service_timestamp(struct dynamic_service * srv,const char * event,double start,double dfirst,double dprev)459 service_timestamp(struct dynamic_service *srv, const char *event, double start,
460 double dfirst, double dprev)
461 {
462
463 VSL(SLT_Timestamp, 0, "vmod-dynamic %s.%s(srv %s) %s: %.6f %.6f %.6f",
464 srv->obj->vcl_conf, srv->obj->vcl_name, srv->service,
465 event, start, dfirst, dprev);
466 }
467
468 static void*
service_lookup_thread(void * priv)469 service_lookup_thread(void *priv)
470 {
471 struct vmod_dynamic_director *obj;
472 struct dynamic_service *srv;
473 struct vrt_ctx ctx;
474 vtim_real lookup, results, update;
475 const struct res_cb *res;
476 void *res_priv = NULL;
477 int ret;
478
479 CAST_OBJ_NOTNULL(srv, priv, DYNAMIC_SERVICE_MAGIC);
480 INIT_OBJ(&ctx, VRT_CTX_MAGIC);
481
482 obj = srv->obj;
483 res = obj->resolver;
484
485 AN(res->srv_lookup);
486 AN(res->srv_result);
487 AN(res->srv_fini);
488
489 while (obj->active && srv->status <= DYNAMIC_ST_ACTIVE) {
490
491 lookup = VTIM_real();
492 service_timestamp(srv, "Lookup", lookup, 0., 0.);
493
494 ret = res->srv_lookup(obj->resolver_inst, srv->service,
495 &res_priv);
496
497 results = VTIM_real();
498 service_timestamp(srv, "Results", results, results - lookup,
499 results - lookup);
500
501 if (ret == 0) {
502 service_update(srv, res, res_priv, results);
503 update = VTIM_real();
504 service_timestamp(srv, "Update", update,
505 update - lookup, update - results);
506 // minimum update delay for lockless safety
507 update += 0.01;
508 if (srv->deadline < update)
509 srv->deadline = update;
510 // maximum update delay
511 if (obj->domain_usage_tmo > 0) {
512 update += obj->domain_usage_tmo / 2;
513 if (srv->deadline > update)
514 srv->deadline = update;
515 }
516 } else {
517 LOG(&ctx, SLT_Error, srv, "%s %d (%s)",
518 res->name, ret, res->strerror(ret));
519 srv->deadline = results + obj->retry_after;
520 dbg_res_details(NULL, srv->obj, res, res_priv);
521 }
522
523 res->srv_fini(&res_priv);
524 AZ(res_priv);
525
526 Lck_Lock(&srv->mtx);
527
528 if (srv->status == DYNAMIC_ST_READY) {
529 AZ(pthread_cond_broadcast(&srv->resolve));
530 srv->status = DYNAMIC_ST_ACTIVE;
531 }
532
533 /* Check status again after the blocking call */
534 if (obj->active && srv->status <= DYNAMIC_ST_ACTIVE) {
535 ret = Lck_CondWait(&srv->cond, &srv->mtx,
536 srv->deadline);
537 assert(ret == 0 || ret == ETIMEDOUT);
538 }
539
540 Lck_Unlock(&srv->mtx);
541 }
542
543 srv->status = DYNAMIC_ST_DONE;
544 service_timestamp(srv, "Done", VTIM_real(), 0., 0.);
545
546 return (NULL);
547 }
548
549 static void
service_free(VRT_CTX,struct dynamic_service * srv)550 service_free(VRT_CTX, struct dynamic_service *srv)
551 {
552 CHECK_OBJ_ORNULL(ctx, VRT_CTX_MAGIC);
553 CHECK_OBJ_NOTNULL(srv, DYNAMIC_SERVICE_MAGIC);
554 AZ(srv->thread);
555 assert(srv->status == DYNAMIC_ST_READY);
556
557 VRT_DelDirector(&srv->dir);
558
559 if (ctx != NULL) {
560 Lck_AssertHeld(&srv->obj->mtx);
561 LOG(ctx, SLT_VCL_Log, srv, "%s", "deleted");
562 }
563
564 if (srv->prios_cold != NULL)
565 service_prios_free(&srv->prios_cold);
566 if (srv->prios != NULL)
567 service_prios_free(&srv->prios);
568 AZ(srv->prios_cold);
569 AZ(srv->prios);
570
571 AZ(pthread_cond_destroy(&srv->resolve));
572 AZ(pthread_cond_destroy(&srv->cond));
573 Lck_Delete(&srv->mtx);
574 REPLACE(srv->service, NULL);
575 FREE_OBJ(srv);
576 }
577
578 static void
service_join(struct dynamic_service * srv)579 service_join(struct dynamic_service *srv)
580 {
581 CHECK_OBJ_NOTNULL(srv, DYNAMIC_SERVICE_MAGIC);
582 AN(srv->thread);
583 AZ(pthread_join(srv->thread, NULL));
584 assert(srv->status == DYNAMIC_ST_DONE);
585 srv->thread = 0;
586 srv->status = DYNAMIC_ST_READY;
587 }
588
589 // called from dynamic_stop
590 void
service_stop(struct vmod_dynamic_director * obj)591 service_stop(struct vmod_dynamic_director *obj)
592 {
593 struct dynamic_service *srv, *s2;
594
595 CHECK_OBJ_NOTNULL(obj, VMOD_DYNAMIC_DIRECTOR_MAGIC);
596
597 VTAILQ_FOREACH(srv, &obj->active_services, list) {
598 CHECK_OBJ_NOTNULL(srv, DYNAMIC_SERVICE_MAGIC);
599 Lck_Lock(&srv->mtx);
600 AN(srv->thread);
601 AZ(pthread_cond_signal(&srv->cond));
602 Lck_Unlock(&srv->mtx);
603 }
604 VTAILQ_FOREACH(srv, &obj->active_services, list)
605 service_join(srv);
606
607 VTAILQ_FOREACH_SAFE(srv, &obj->purged_services, list, s2) {
608 CHECK_OBJ_NOTNULL(srv, DYNAMIC_SERVICE_MAGIC);
609 assert(srv->status == DYNAMIC_ST_STALE ||
610 srv->status == DYNAMIC_ST_DONE);
611 service_join(srv);
612 VTAILQ_REMOVE(&obj->purged_services, srv, list);
613 service_free(NULL, srv);
614 }
615 }
616
617 // called from dynamic_start
618 void
service_start(VRT_CTX,struct vmod_dynamic_director * obj)619 service_start(VRT_CTX, struct vmod_dynamic_director *obj)
620 {
621 struct dynamic_service *srv;
622
623 (void) ctx;
624 Lck_AssertHeld(&obj->mtx);
625
626 VTAILQ_FOREACH(srv, &obj->active_services, list) {
627 CHECK_OBJ_NOTNULL(srv, DYNAMIC_SERVICE_MAGIC);
628 assert(srv->status == DYNAMIC_ST_READY);
629 AZ(srv->thread);
630 AZ(pthread_create(&srv->thread, NULL, service_lookup_thread,
631 srv));
632 }
633 }
634
635 // calledn from vmod_director__fini
636 void
service_fini(struct vmod_dynamic_director * obj)637 service_fini(struct vmod_dynamic_director *obj)
638 {
639 struct dynamic_service *srv, *s2;
640
641 CHECK_OBJ_NOTNULL(obj, VMOD_DYNAMIC_DIRECTOR_MAGIC);
642
643 VTAILQ_FOREACH_SAFE(srv, &obj->purged_services, list, s2) {
644 VTAILQ_REMOVE(&obj->purged_services, srv, list);
645 service_free(NULL, srv);
646 }
647
648 VTAILQ_FOREACH_SAFE(srv, &obj->active_services, list, s2) {
649 VTAILQ_REMOVE(&obj->active_services, srv, list);
650 service_free(NULL, srv);
651 }
652
653 }
654
655 static struct dynamic_service *
service_search(VRT_CTX,struct vmod_dynamic_director * obj,const char * service)656 service_search(VRT_CTX, struct vmod_dynamic_director *obj, const char *service)
657 {
658 struct dynamic_service *srv, *s, *s2;
659
660 CHECK_OBJ_NOTNULL(obj, VMOD_DYNAMIC_DIRECTOR_MAGIC);
661 Lck_AssertHeld(&obj->mtx);
662 AN(service);
663
664 srv = NULL;
665 VTAILQ_FOREACH_SAFE(s, &obj->active_services, list, s2) {
666 CHECK_OBJ_NOTNULL(s, DYNAMIC_SERVICE_MAGIC);
667 if (strcmp(s->service, service) == 0)
668 srv = s;
669 if (srv != s && s->status == DYNAMIC_ST_ACTIVE &&
670 obj->domain_usage_tmo > 0 &&
671 ctx->now - s->last_used > obj->domain_usage_tmo) {
672 LOG(ctx, SLT_VCL_Log, s, "%s", "timeout");
673 Lck_Lock(&s->mtx);
674 s->status = DYNAMIC_ST_STALE;
675 AZ(pthread_cond_signal(&s->cond));
676 Lck_Unlock(&s->mtx);
677 VTAILQ_REMOVE(&obj->active_services, s, list);
678 VTAILQ_INSERT_TAIL(&obj->purged_services, s, list);
679 }
680 }
681
682 VTAILQ_FOREACH_SAFE(s, &obj->purged_services, list, s2) {
683 CHECK_OBJ_NOTNULL(s, DYNAMIC_SERVICE_MAGIC);
684 if (s->status == DYNAMIC_ST_DONE) {
685 service_join(s);
686 VTAILQ_REMOVE(&obj->purged_services, s, list);
687 service_free(ctx, s);
688 }
689 }
690
691 return (srv);
692 }
693
694 static struct dynamic_service *
service_get(VRT_CTX,struct vmod_dynamic_director * obj,const char * service)695 service_get(VRT_CTX, struct vmod_dynamic_director *obj, const char *service)
696 {
697 struct dynamic_service *srv;
698
699 CHECK_OBJ_NOTNULL(obj, VMOD_DYNAMIC_DIRECTOR_MAGIC);
700 Lck_AssertHeld(&obj->mtx);
701 AN(service);
702
703 srv = service_search(ctx, obj, service);
704 if (srv != NULL)
705 return (srv);
706
707 ALLOC_OBJ(srv, DYNAMIC_SERVICE_MAGIC);
708 AN(srv);
709
710 REPLACE(srv->service, service);
711
712 srv->obj = obj;
713
714 srv->dir = VRT_AddDirector(ctx, vmod_dynamic_service_methods, srv,
715 "%s(%s)", obj->vcl_name, service);
716
717 Lck_New(&srv->mtx, lck_be);
718 AZ(pthread_cond_init(&srv->cond, NULL));
719 AZ(pthread_cond_init(&srv->resolve, NULL));
720
721 AZ(pthread_create(&srv->thread, NULL, service_lookup_thread, srv));
722
723 VTAILQ_INSERT_TAIL(&obj->active_services, srv, list);
724
725 return (srv);
726 }
727
728
v_matchproto_(td_dynamic_director_service)729 VCL_BACKEND v_matchproto_(td_dynamic_director_service)
730 vmod_director_service(VRT_CTX, struct VPFX(dynamic_director) *obj,
731 VCL_STRING service) {
732 struct dynamic_service *srv;
733 CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
734 CHECK_OBJ_NOTNULL(obj, VMOD_DYNAMIC_DIRECTOR_MAGIC);
735
736 if (obj->resolver_inst == NULL) {
737 VRT_fail(ctx, "xdynamic.service(): Only supported "
738 "with a resolver");
739 return (NULL);
740 }
741
742 Lck_Lock(&obj->mtx);
743 srv = service_get(ctx, obj, service);
744 AN(srv);
745 srv->last_used = ctx->now;
746 Lck_Unlock(&obj->mtx);
747
748 return (srv->dir);
749 }
750