1*68e5c67bSchristos /*	$NetBSD: scache_multi.c,v 1.2 2017/02/14 01:16:45 christos Exp $	*/
241fbaed0Stron 
341fbaed0Stron /*++
441fbaed0Stron /* NAME
541fbaed0Stron /*	scache_multi 3
641fbaed0Stron /* SUMMARY
741fbaed0Stron /*	multi-session cache
841fbaed0Stron /* SYNOPSIS
941fbaed0Stron /*	#include <scache.h>
1041fbaed0Stron /* DESCRIPTION
1141fbaed0Stron /*	SCACHE *scache_multi_create()
1241fbaed0Stron /* DESCRIPTION
1341fbaed0Stron /*	This module implements an in-memory, multi-session cache.
1441fbaed0Stron /*
1541fbaed0Stron /*	scache_multi_create() instantiates a session cache that
1641fbaed0Stron /*	stores multiple sessions.
1741fbaed0Stron /* DIAGNOSTICS
1841fbaed0Stron /*	Fatal error: memory allocation problem;
1941fbaed0Stron /*	panic: internal consistency failure.
2041fbaed0Stron /* SEE ALSO
2141fbaed0Stron /*	scache(3), generic session cache API
2241fbaed0Stron /* LICENSE
2341fbaed0Stron /* .ad
2441fbaed0Stron /* .fi
2541fbaed0Stron /*	The Secure Mailer license must be distributed with this software.
2641fbaed0Stron /* AUTHOR(S)
2741fbaed0Stron /*	Wietse Venema
2841fbaed0Stron /*	IBM T.J. Watson Research
2941fbaed0Stron /*	P.O. Box 704
3041fbaed0Stron /*	Yorktown Heights, NY 10598, USA
3141fbaed0Stron /*--*/
3241fbaed0Stron 
3341fbaed0Stron /* System library. */
3441fbaed0Stron 
3541fbaed0Stron #include <sys_defs.h>
3641fbaed0Stron #include <unistd.h>
3741fbaed0Stron #include <stddef.h>			/* offsetof() */
3841fbaed0Stron #include <string.h>
3941fbaed0Stron 
4041fbaed0Stron /* Utility library. */
4141fbaed0Stron 
4241fbaed0Stron #include <msg.h>
4341fbaed0Stron #include <ring.h>
4441fbaed0Stron #include <htable.h>
4541fbaed0Stron #include <vstring.h>
4641fbaed0Stron #include <mymalloc.h>
4741fbaed0Stron #include <events.h>
4841fbaed0Stron 
4941fbaed0Stron /*#define msg_verbose 1*/
5041fbaed0Stron 
5141fbaed0Stron /* Global library. */
5241fbaed0Stron 
5341fbaed0Stron #include <scache.h>
5441fbaed0Stron 
5541fbaed0Stron /* Application-specific. */
5641fbaed0Stron 
5741fbaed0Stron  /*
5841fbaed0Stron   * SCACHE_MULTI is a derived type from the SCACHE super-class.
5941fbaed0Stron   *
6041fbaed0Stron   * Each destination has an entry in the destination hash table, and each
6141fbaed0Stron   * destination->endpoint binding is kept in a circular list under its
6241fbaed0Stron   * destination hash table entry.
6341fbaed0Stron   *
6441fbaed0Stron   * Likewise, each endpoint has an entry in the endpoint hash table, and each
6541fbaed0Stron   * endpoint->session binding is kept in a circular list under its endpoint
6641fbaed0Stron   * hash table entry.
6741fbaed0Stron   *
6841fbaed0Stron   * We do not attempt to limit the number of destination or endpoint entries,
6941fbaed0Stron   * nor do we attempt to limit the number of sessions. Doing so would require
7041fbaed0Stron   * a write-through cache. Currently, the CTABLE cache module supports only
7141fbaed0Stron   * read-through caching.
7241fbaed0Stron   *
7341fbaed0Stron   * This is no problem because the number of cached destinations is limited by
7441fbaed0Stron   * design. Sites that specify a wild-card domain pattern, and thus cache
7541fbaed0Stron   * every session in recent history, may be in for a surprise.
7641fbaed0Stron   */
7741fbaed0Stron typedef struct {
7841fbaed0Stron     SCACHE  scache[1];			/* super-class */
7941fbaed0Stron     HTABLE *dest_cache;			/* destination->endpoint bindings */
8041fbaed0Stron     HTABLE *endp_cache;			/* endpoint->session bindings */
8141fbaed0Stron     int     sess_count;			/* number of cached sessions */
8241fbaed0Stron } SCACHE_MULTI;
8341fbaed0Stron 
8441fbaed0Stron  /*
8541fbaed0Stron   * Storage for a destination or endpoint list head. Each list head knows its
8641fbaed0Stron   * own hash table entry name, so that we can remove the list when it becomes
8741fbaed0Stron   * empty. List items are stored in a circular list under the list head.
8841fbaed0Stron   */
8941fbaed0Stron typedef struct {
9041fbaed0Stron     RING    ring[1];			/* circular list linkage */
9141fbaed0Stron     char   *parent_key;			/* parent linkage: hash table */
9241fbaed0Stron     SCACHE_MULTI *cache;		/* parent linkage: cache */
9341fbaed0Stron } SCACHE_MULTI_HEAD;
9441fbaed0Stron 
9541fbaed0Stron #define RING_TO_MULTI_HEAD(p) RING_TO_APPL((p), SCACHE_MULTI_HEAD, ring)
9641fbaed0Stron 
9741fbaed0Stron  /*
9841fbaed0Stron   * Storage for a destination->endpoint binding. This is an element in a
9941fbaed0Stron   * circular list, whose list head specifies the destination.
10041fbaed0Stron   */
10141fbaed0Stron typedef struct {
10241fbaed0Stron     RING    ring[1];			/* circular list linkage */
10341fbaed0Stron     SCACHE_MULTI_HEAD *head;		/* parent linkage: list head */
10441fbaed0Stron     char   *endp_label;			/* endpoint name */
10541fbaed0Stron     char   *dest_prop;			/* binding properties */
10641fbaed0Stron } SCACHE_MULTI_DEST;
10741fbaed0Stron 
10841fbaed0Stron #define RING_TO_MULTI_DEST(p) RING_TO_APPL((p), SCACHE_MULTI_DEST, ring)
10941fbaed0Stron 
110837e7c1aSchristos static void scache_multi_expire_dest(int, void *);
11141fbaed0Stron 
11241fbaed0Stron  /*
11341fbaed0Stron   * Storage for an endpoint->session binding. This is an element in a
11441fbaed0Stron   * circular list, whose list head specifies the endpoint.
11541fbaed0Stron   */
11641fbaed0Stron typedef struct {
11741fbaed0Stron     RING    ring[1];			/* circular list linkage */
11841fbaed0Stron     SCACHE_MULTI_HEAD *head;		/* parent linkage: list head */
11941fbaed0Stron     int     fd;				/* cached session */
12041fbaed0Stron     char   *endp_prop;			/* binding properties */
12141fbaed0Stron } SCACHE_MULTI_ENDP;
12241fbaed0Stron 
12341fbaed0Stron #define RING_TO_MULTI_ENDP(p) RING_TO_APPL((p), SCACHE_MULTI_ENDP, ring)
12441fbaed0Stron 
125837e7c1aSchristos static void scache_multi_expire_endp(int, void *);
12641fbaed0Stron 
12741fbaed0Stron  /*
12841fbaed0Stron   * When deleting a circular list element, are we deleting the entire
12941fbaed0Stron   * circular list, or are we removing a single list element. We need this
13041fbaed0Stron   * distinction to avoid a re-entrancy problem between htable_delete() and
13141fbaed0Stron   * htable_free().
13241fbaed0Stron   */
13341fbaed0Stron #define BOTTOM_UP	1		/* one item */
13441fbaed0Stron #define TOP_DOWN	2		/* whole list */
13541fbaed0Stron 
13641fbaed0Stron /* scache_multi_drop_endp - destroy endpoint->session binding */
13741fbaed0Stron 
scache_multi_drop_endp(SCACHE_MULTI_ENDP * endp,int direction)13841fbaed0Stron static void scache_multi_drop_endp(SCACHE_MULTI_ENDP *endp, int direction)
13941fbaed0Stron {
14041fbaed0Stron     const char *myname = "scache_multi_drop_endp";
14141fbaed0Stron     SCACHE_MULTI_HEAD *head;
14241fbaed0Stron 
14341fbaed0Stron     if (msg_verbose)
14441fbaed0Stron 	msg_info("%s: endp_prop=%s fd=%d", myname,
14541fbaed0Stron 		 endp->endp_prop, endp->fd);
14641fbaed0Stron 
14741fbaed0Stron     /*
14841fbaed0Stron      * Stop the timer.
14941fbaed0Stron      */
150837e7c1aSchristos     event_cancel_timer(scache_multi_expire_endp, (void *) endp);
15141fbaed0Stron 
15241fbaed0Stron     /*
15341fbaed0Stron      * In bottom-up mode, remove the list head from the endpoint hash when
15441fbaed0Stron      * the list becomes empty. Otherwise, remove the endpoint->session
15541fbaed0Stron      * binding from the list.
15641fbaed0Stron      */
15741fbaed0Stron     ring_detach(endp->ring);
15841fbaed0Stron     head = endp->head;
15941fbaed0Stron     head->cache->sess_count--;
16041fbaed0Stron     if (direction == BOTTOM_UP && ring_pred(head->ring) == head->ring)
16141fbaed0Stron 	htable_delete(head->cache->endp_cache, head->parent_key, myfree);
16241fbaed0Stron 
16341fbaed0Stron     /*
16441fbaed0Stron      * Destroy the endpoint->session binding.
16541fbaed0Stron      */
16641fbaed0Stron     if (endp->fd >= 0 && close(endp->fd) != 0)
16741fbaed0Stron 	msg_warn("%s: close(%d): %m", myname, endp->fd);
16841fbaed0Stron     myfree(endp->endp_prop);
16941fbaed0Stron 
170837e7c1aSchristos     myfree((void *) endp);
17141fbaed0Stron }
17241fbaed0Stron 
17341fbaed0Stron /* scache_multi_expire_endp - event timer call-back */
17441fbaed0Stron 
scache_multi_expire_endp(int unused_event,void * context)175837e7c1aSchristos static void scache_multi_expire_endp(int unused_event, void *context)
17641fbaed0Stron {
17741fbaed0Stron     SCACHE_MULTI_ENDP *endp = (SCACHE_MULTI_ENDP *) context;
17841fbaed0Stron 
17941fbaed0Stron     scache_multi_drop_endp(endp, BOTTOM_UP);
18041fbaed0Stron }
18141fbaed0Stron 
18241fbaed0Stron /* scache_multi_free_endp - hash table destructor call-back */
18341fbaed0Stron 
scache_multi_free_endp(void * ptr)184837e7c1aSchristos static void scache_multi_free_endp(void *ptr)
18541fbaed0Stron {
18641fbaed0Stron     SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
18741fbaed0Stron     SCACHE_MULTI_ENDP *endp;
18841fbaed0Stron     RING   *ring;
18941fbaed0Stron 
19041fbaed0Stron     /*
19141fbaed0Stron      * Delete each endpoint->session binding in the list, then delete the
19241fbaed0Stron      * list head. Note: this changes the list, so we must iterate carefully.
19341fbaed0Stron      */
19441fbaed0Stron     while ((ring = ring_succ(head->ring)) != head->ring) {
19541fbaed0Stron 	endp = RING_TO_MULTI_ENDP(ring);
19641fbaed0Stron 	scache_multi_drop_endp(endp, TOP_DOWN);
19741fbaed0Stron     }
198837e7c1aSchristos     myfree((void *) head);
19941fbaed0Stron }
20041fbaed0Stron 
20141fbaed0Stron /* scache_multi_save_endp - save endpoint->session binding */
20241fbaed0Stron 
scache_multi_save_endp(SCACHE * scache,int ttl,const char * endp_label,const char * endp_prop,int fd)20341fbaed0Stron static void scache_multi_save_endp(SCACHE *scache, int ttl,
20441fbaed0Stron 				           const char *endp_label,
20541fbaed0Stron 				           const char *endp_prop, int fd)
20641fbaed0Stron {
20741fbaed0Stron     const char *myname = "scache_multi_save_endp";
20841fbaed0Stron     SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
20941fbaed0Stron     SCACHE_MULTI_HEAD *head;
21041fbaed0Stron     SCACHE_MULTI_ENDP *endp;
21141fbaed0Stron 
21241fbaed0Stron     if (ttl < 0)
21341fbaed0Stron 	msg_panic("%s: bad ttl: %d", myname, ttl);
21441fbaed0Stron 
21541fbaed0Stron     /*
21641fbaed0Stron      * Look up or instantiate the list head with the endpoint name.
21741fbaed0Stron      */
21841fbaed0Stron     if ((head = (SCACHE_MULTI_HEAD *)
21941fbaed0Stron 	 htable_find(sp->endp_cache, endp_label)) == 0) {
22041fbaed0Stron 	head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
22141fbaed0Stron 	ring_init(head->ring);
22241fbaed0Stron 	head->parent_key =
223837e7c1aSchristos 	    htable_enter(sp->endp_cache, endp_label, (void *) head)->key;
22441fbaed0Stron 	head->cache = sp;
22541fbaed0Stron     }
22641fbaed0Stron 
22741fbaed0Stron     /*
22841fbaed0Stron      * Add the endpoint->session binding to the list. There can never be a
22941fbaed0Stron      * duplicate, because each session must have a different file descriptor.
23041fbaed0Stron      */
23141fbaed0Stron     endp = (SCACHE_MULTI_ENDP *) mymalloc(sizeof(*endp));
23241fbaed0Stron     endp->head = head;
23341fbaed0Stron     endp->fd = fd;
23441fbaed0Stron     endp->endp_prop = mystrdup(endp_prop);
23541fbaed0Stron     ring_prepend(head->ring, endp->ring);
23641fbaed0Stron     sp->sess_count++;
23741fbaed0Stron 
23841fbaed0Stron     /*
23941fbaed0Stron      * Make sure this binding will go away eventually.
24041fbaed0Stron      */
241837e7c1aSchristos     event_request_timer(scache_multi_expire_endp, (void *) endp, ttl);
24241fbaed0Stron 
24341fbaed0Stron     if (msg_verbose)
24441fbaed0Stron 	msg_info("%s: endp_label=%s -> endp_prop=%s fd=%d",
24541fbaed0Stron 		 myname, endp_label, endp_prop, fd);
24641fbaed0Stron }
24741fbaed0Stron 
24841fbaed0Stron /* scache_multi_find_endp - look up session for named endpoint */
24941fbaed0Stron 
scache_multi_find_endp(SCACHE * scache,const char * endp_label,VSTRING * endp_prop)25041fbaed0Stron static int scache_multi_find_endp(SCACHE *scache, const char *endp_label,
25141fbaed0Stron 				          VSTRING *endp_prop)
25241fbaed0Stron {
25341fbaed0Stron     const char *myname = "scache_multi_find_endp";
25441fbaed0Stron     SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
25541fbaed0Stron     SCACHE_MULTI_HEAD *head;
25641fbaed0Stron     SCACHE_MULTI_ENDP *endp;
25741fbaed0Stron     RING   *ring;
25841fbaed0Stron     int     fd;
25941fbaed0Stron 
26041fbaed0Stron     /*
26141fbaed0Stron      * Look up the list head with the endpoint name.
26241fbaed0Stron      */
26341fbaed0Stron     if ((head = (SCACHE_MULTI_HEAD *)
26441fbaed0Stron 	 htable_find(sp->endp_cache, endp_label)) == 0) {
26541fbaed0Stron 	if (msg_verbose)
26641fbaed0Stron 	    msg_info("%s: no endpoint cache: endp_label=%s",
26741fbaed0Stron 		     myname, endp_label);
26841fbaed0Stron 	return (-1);
26941fbaed0Stron     }
27041fbaed0Stron 
27141fbaed0Stron     /*
27241fbaed0Stron      * Use the first available session. Remove the session from the cache
27341fbaed0Stron      * because we're giving it to someone else.
27441fbaed0Stron      */
27541fbaed0Stron     if ((ring = ring_succ(head->ring)) != head->ring) {
27641fbaed0Stron 	endp = RING_TO_MULTI_ENDP(ring);
27741fbaed0Stron 	fd = endp->fd;
27841fbaed0Stron 	endp->fd = -1;
27941fbaed0Stron 	vstring_strcpy(endp_prop, endp->endp_prop);
28041fbaed0Stron 	if (msg_verbose)
28141fbaed0Stron 	    msg_info("%s: found: endp_label=%s -> endp_prop=%s fd=%d",
28241fbaed0Stron 		     myname, endp_label, endp->endp_prop, fd);
28341fbaed0Stron 	scache_multi_drop_endp(endp, BOTTOM_UP);
28441fbaed0Stron 	return (fd);
28541fbaed0Stron     }
28641fbaed0Stron     if (msg_verbose)
28741fbaed0Stron 	msg_info("%s: not found: endp_label=%s", myname, endp_label);
28841fbaed0Stron     return (-1);
28941fbaed0Stron }
29041fbaed0Stron 
29141fbaed0Stron /* scache_multi_drop_dest - delete destination->endpoint binding */
29241fbaed0Stron 
scache_multi_drop_dest(SCACHE_MULTI_DEST * dest,int direction)29341fbaed0Stron static void scache_multi_drop_dest(SCACHE_MULTI_DEST *dest, int direction)
29441fbaed0Stron {
29541fbaed0Stron     const char *myname = "scache_multi_drop_dest";
29641fbaed0Stron     SCACHE_MULTI_HEAD *head;
29741fbaed0Stron 
29841fbaed0Stron     if (msg_verbose)
29941fbaed0Stron 	msg_info("%s: dest_prop=%s endp_label=%s",
30041fbaed0Stron 		 myname, dest->dest_prop, dest->endp_label);
30141fbaed0Stron 
30241fbaed0Stron     /*
30341fbaed0Stron      * Stop the timer.
30441fbaed0Stron      */
305837e7c1aSchristos     event_cancel_timer(scache_multi_expire_dest, (void *) dest);
30641fbaed0Stron 
30741fbaed0Stron     /*
30841fbaed0Stron      * In bottom-up mode, remove the list head from the destination hash when
30941fbaed0Stron      * the list becomes empty. Otherwise, remove the destination->endpoint
31041fbaed0Stron      * binding from the list.
31141fbaed0Stron      */
31241fbaed0Stron     ring_detach(dest->ring);
31341fbaed0Stron     head = dest->head;
31441fbaed0Stron     if (direction == BOTTOM_UP && ring_pred(head->ring) == head->ring)
31541fbaed0Stron 	htable_delete(head->cache->dest_cache, head->parent_key, myfree);
31641fbaed0Stron 
31741fbaed0Stron     /*
31841fbaed0Stron      * Destroy the destination->endpoint binding.
31941fbaed0Stron      */
32041fbaed0Stron     myfree(dest->dest_prop);
32141fbaed0Stron     myfree(dest->endp_label);
32241fbaed0Stron 
323837e7c1aSchristos     myfree((void *) dest);
32441fbaed0Stron }
32541fbaed0Stron 
32641fbaed0Stron /* scache_multi_expire_dest - event timer call-back */
32741fbaed0Stron 
scache_multi_expire_dest(int unused_event,void * context)328837e7c1aSchristos static void scache_multi_expire_dest(int unused_event, void *context)
32941fbaed0Stron {
33041fbaed0Stron     SCACHE_MULTI_DEST *dest = (SCACHE_MULTI_DEST *) context;
33141fbaed0Stron 
33241fbaed0Stron     scache_multi_drop_dest(dest, BOTTOM_UP);
33341fbaed0Stron }
33441fbaed0Stron 
33541fbaed0Stron /* scache_multi_free_dest - hash table destructor call-back */
33641fbaed0Stron 
scache_multi_free_dest(void * ptr)337837e7c1aSchristos static void scache_multi_free_dest(void *ptr)
33841fbaed0Stron {
33941fbaed0Stron     SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
34041fbaed0Stron     SCACHE_MULTI_DEST *dest;
34141fbaed0Stron     RING   *ring;
34241fbaed0Stron 
34341fbaed0Stron     /*
34441fbaed0Stron      * Delete each destination->endpoint binding in the list, then delete the
34541fbaed0Stron      * list head. Note: this changes the list, so we must iterate carefully.
34641fbaed0Stron      */
34741fbaed0Stron     while ((ring = ring_succ(head->ring)) != head->ring) {
34841fbaed0Stron 	dest = RING_TO_MULTI_DEST(ring);
34941fbaed0Stron 	scache_multi_drop_dest(dest, TOP_DOWN);
35041fbaed0Stron     }
351837e7c1aSchristos     myfree((void *) head);
35241fbaed0Stron }
35341fbaed0Stron 
35441fbaed0Stron /* scache_multi_save_dest - save destination->endpoint binding */
35541fbaed0Stron 
scache_multi_save_dest(SCACHE * scache,int ttl,const char * dest_label,const char * dest_prop,const char * endp_label)35641fbaed0Stron static void scache_multi_save_dest(SCACHE *scache, int ttl,
35741fbaed0Stron 				           const char *dest_label,
35841fbaed0Stron 				           const char *dest_prop,
35941fbaed0Stron 				           const char *endp_label)
36041fbaed0Stron {
36141fbaed0Stron     const char *myname = "scache_multi_save_dest";
36241fbaed0Stron     SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
36341fbaed0Stron     SCACHE_MULTI_HEAD *head;
36441fbaed0Stron     SCACHE_MULTI_DEST *dest;
36541fbaed0Stron     RING   *ring;
36641fbaed0Stron     int     refresh = 0;
36741fbaed0Stron 
36841fbaed0Stron     if (ttl < 0)
36941fbaed0Stron 	msg_panic("%s: bad ttl: %d", myname, ttl);
37041fbaed0Stron 
37141fbaed0Stron     /*
37241fbaed0Stron      * Look up or instantiate the list head with the destination name.
37341fbaed0Stron      */
37441fbaed0Stron     if ((head = (SCACHE_MULTI_HEAD *)
37541fbaed0Stron 	 htable_find(sp->dest_cache, dest_label)) == 0) {
37641fbaed0Stron 	head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
37741fbaed0Stron 	ring_init(head->ring);
37841fbaed0Stron 	head->parent_key =
379837e7c1aSchristos 	    htable_enter(sp->dest_cache, dest_label, (void *) head)->key;
38041fbaed0Stron 	head->cache = sp;
38141fbaed0Stron     }
38241fbaed0Stron 
38341fbaed0Stron     /*
38441fbaed0Stron      * Look up or instantiate the destination->endpoint binding. Update the
38541fbaed0Stron      * expiration time if this destination->endpoint binding already exists.
38641fbaed0Stron      */
38741fbaed0Stron     RING_FOREACH(ring, head->ring) {
38841fbaed0Stron 	dest = RING_TO_MULTI_DEST(ring);
38941fbaed0Stron 	if (strcmp(dest->endp_label, endp_label) == 0
39041fbaed0Stron 	    && strcmp(dest->dest_prop, dest_prop) == 0) {
39141fbaed0Stron 	    refresh = 1;
39241fbaed0Stron 	    break;
39341fbaed0Stron 	}
39441fbaed0Stron     }
39541fbaed0Stron     if (refresh == 0) {
39641fbaed0Stron 	dest = (SCACHE_MULTI_DEST *) mymalloc(sizeof(*dest));
39741fbaed0Stron 	dest->head = head;
39841fbaed0Stron 	dest->endp_label = mystrdup(endp_label);
39941fbaed0Stron 	dest->dest_prop = mystrdup(dest_prop);
40041fbaed0Stron 	ring_prepend(head->ring, dest->ring);
40141fbaed0Stron     }
40241fbaed0Stron 
40341fbaed0Stron     /*
40441fbaed0Stron      * Make sure this binding will go away eventually.
40541fbaed0Stron      */
406837e7c1aSchristos     event_request_timer(scache_multi_expire_dest, (void *) dest, ttl);
40741fbaed0Stron 
40841fbaed0Stron     if (msg_verbose)
40941fbaed0Stron 	msg_info("%s: dest_label=%s -> dest_prop=%s endp_label=%s%s",
41041fbaed0Stron 		 myname, dest_label, dest_prop, endp_label,
41141fbaed0Stron 		 refresh ? " (refreshed)" : "");
41241fbaed0Stron }
41341fbaed0Stron 
41441fbaed0Stron /* scache_multi_find_dest - look up session for named destination */
41541fbaed0Stron 
scache_multi_find_dest(SCACHE * scache,const char * dest_label,VSTRING * dest_prop,VSTRING * endp_prop)41641fbaed0Stron static int scache_multi_find_dest(SCACHE *scache, const char *dest_label,
41741fbaed0Stron 				          VSTRING *dest_prop,
41841fbaed0Stron 				          VSTRING *endp_prop)
41941fbaed0Stron {
42041fbaed0Stron     const char *myname = "scache_multi_find_dest";
42141fbaed0Stron     SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
42241fbaed0Stron     SCACHE_MULTI_HEAD *head;
42341fbaed0Stron     SCACHE_MULTI_DEST *dest;
42441fbaed0Stron     RING   *ring;
42541fbaed0Stron     int     fd;
42641fbaed0Stron 
42741fbaed0Stron     /*
42841fbaed0Stron      * Look up the list head with the destination name.
42941fbaed0Stron      */
43041fbaed0Stron     if ((head = (SCACHE_MULTI_HEAD *)
43141fbaed0Stron 	 htable_find(sp->dest_cache, dest_label)) == 0) {
43241fbaed0Stron 	if (msg_verbose)
43341fbaed0Stron 	    msg_info("%s: no destination cache: dest_label=%s",
43441fbaed0Stron 		     myname, dest_label);
43541fbaed0Stron 	return (-1);
43641fbaed0Stron     }
43741fbaed0Stron 
43841fbaed0Stron     /*
43941fbaed0Stron      * Search endpoints for the first available session.
44041fbaed0Stron      */
44141fbaed0Stron     RING_FOREACH(ring, head->ring) {
44241fbaed0Stron 	dest = RING_TO_MULTI_DEST(ring);
44341fbaed0Stron 	fd = scache_multi_find_endp(scache, dest->endp_label, endp_prop);
44441fbaed0Stron 	if (fd >= 0) {
44541fbaed0Stron 	    vstring_strcpy(dest_prop, dest->dest_prop);
44641fbaed0Stron 	    return (fd);
44741fbaed0Stron 	}
44841fbaed0Stron     }
44941fbaed0Stron     if (msg_verbose)
45041fbaed0Stron 	msg_info("%s: not found: dest_label=%s", myname, dest_label);
45141fbaed0Stron     return (-1);
45241fbaed0Stron }
45341fbaed0Stron 
45441fbaed0Stron /* scache_multi_size - size of multi-element cache object */
45541fbaed0Stron 
scache_multi_size(SCACHE * scache,SCACHE_SIZE * size)45641fbaed0Stron static void scache_multi_size(SCACHE *scache, SCACHE_SIZE *size)
45741fbaed0Stron {
45841fbaed0Stron     SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
45941fbaed0Stron 
46041fbaed0Stron     size->dest_count = sp->dest_cache->used;
46141fbaed0Stron     size->endp_count = sp->endp_cache->used;
46241fbaed0Stron     size->sess_count = sp->sess_count;
46341fbaed0Stron }
46441fbaed0Stron 
46541fbaed0Stron /* scache_multi_free - destroy multi-element cache object */
46641fbaed0Stron 
scache_multi_free(SCACHE * scache)46741fbaed0Stron static void scache_multi_free(SCACHE *scache)
46841fbaed0Stron {
46941fbaed0Stron     SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
47041fbaed0Stron 
47141fbaed0Stron     htable_free(sp->dest_cache, scache_multi_free_dest);
47241fbaed0Stron     htable_free(sp->endp_cache, scache_multi_free_endp);
47341fbaed0Stron 
474837e7c1aSchristos     myfree((void *) sp);
47541fbaed0Stron }
47641fbaed0Stron 
47741fbaed0Stron /* scache_multi_create - initialize */
47841fbaed0Stron 
scache_multi_create(void)47941fbaed0Stron SCACHE *scache_multi_create(void)
48041fbaed0Stron {
48141fbaed0Stron     SCACHE_MULTI *sp = (SCACHE_MULTI *) mymalloc(sizeof(*sp));
48241fbaed0Stron 
48341fbaed0Stron     sp->scache->save_endp = scache_multi_save_endp;
48441fbaed0Stron     sp->scache->find_endp = scache_multi_find_endp;
48541fbaed0Stron     sp->scache->save_dest = scache_multi_save_dest;
48641fbaed0Stron     sp->scache->find_dest = scache_multi_find_dest;
48741fbaed0Stron     sp->scache->size = scache_multi_size;
48841fbaed0Stron     sp->scache->free = scache_multi_free;
48941fbaed0Stron 
49041fbaed0Stron     sp->dest_cache = htable_create(1);
49141fbaed0Stron     sp->endp_cache = htable_create(1);
49241fbaed0Stron     sp->sess_count = 0;
49341fbaed0Stron 
49441fbaed0Stron     return (sp->scache);
49541fbaed0Stron }
496