Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / scache_multi.c
blobb3ab24bce11219ad1fa7614d25dda7860cf3b5c5
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* scache_multi 3
6 /* SUMMARY
7 /* multi-session cache
8 /* SYNOPSIS
9 /* #include <scache.h>
10 /* DESCRIPTION
11 /* SCACHE *scache_multi_create()
12 /* DESCRIPTION
13 /* This module implements an in-memory, multi-session cache.
15 /* scache_multi_create() instantiates a session cache that
16 /* stores multiple sessions.
17 /* DIAGNOSTICS
18 /* Fatal error: memory allocation problem;
19 /* panic: internal consistency failure.
20 /* SEE ALSO
21 /* scache(3), generic session cache API
22 /* LICENSE
23 /* .ad
24 /* .fi
25 /* The Secure Mailer license must be distributed with this software.
26 /* AUTHOR(S)
27 /* Wietse Venema
28 /* IBM T.J. Watson Research
29 /* P.O. Box 704
30 /* Yorktown Heights, NY 10598, USA
31 /*--*/
33 /* System library. */
35 #include <sys_defs.h>
36 #include <unistd.h>
37 #include <stddef.h> /* offsetof() */
38 #include <string.h>
40 /* Utility library. */
42 #include <msg.h>
43 #include <ring.h>
44 #include <htable.h>
45 #include <vstring.h>
46 #include <mymalloc.h>
47 #include <events.h>
49 /*#define msg_verbose 1*/
51 /* Global library. */
53 #include <scache.h>
55 /* Application-specific. */
58 * SCACHE_MULTI is a derived type from the SCACHE super-class.
60 * Each destination has an entry in the destination hash table, and each
61 * destination->endpoint binding is kept in a circular list under its
62 * destination hash table entry.
64 * Likewise, each endpoint has an entry in the endpoint hash table, and each
65 * endpoint->session binding is kept in a circular list under its endpoint
66 * hash table entry.
68 * We do not attempt to limit the number of destination or endpoint entries,
69 * nor do we attempt to limit the number of sessions. Doing so would require
70 * a write-through cache. Currently, the CTABLE cache module supports only
71 * read-through caching.
73 * This is no problem because the number of cached destinations is limited by
74 * design. Sites that specify a wild-card domain pattern, and thus cache
75 * every session in recent history, may be in for a surprise.
77 typedef struct {
78 SCACHE scache[1]; /* super-class */
79 HTABLE *dest_cache; /* destination->endpoint bindings */
80 HTABLE *endp_cache; /* endpoint->session bindings */
81 int sess_count; /* number of cached sessions */
82 } SCACHE_MULTI;
85 * Storage for a destination or endpoint list head. Each list head knows its
86 * own hash table entry name, so that we can remove the list when it becomes
87 * empty. List items are stored in a circular list under the list head.
89 typedef struct {
90 RING ring[1]; /* circular list linkage */
91 char *parent_key; /* parent linkage: hash table */
92 SCACHE_MULTI *cache; /* parent linkage: cache */
93 } SCACHE_MULTI_HEAD;
95 #define RING_TO_MULTI_HEAD(p) RING_TO_APPL((p), SCACHE_MULTI_HEAD, ring)
98 * Storage for a destination->endpoint binding. This is an element in a
99 * circular list, whose list head specifies the destination.
101 typedef struct {
102 RING ring[1]; /* circular list linkage */
103 SCACHE_MULTI_HEAD *head; /* parent linkage: list head */
104 char *endp_label; /* endpoint name */
105 char *dest_prop; /* binding properties */
106 } SCACHE_MULTI_DEST;
108 #define RING_TO_MULTI_DEST(p) RING_TO_APPL((p), SCACHE_MULTI_DEST, ring)
110 static void scache_multi_expire_dest(int, char *);
113 * Storage for an endpoint->session binding. This is an element in a
114 * circular list, whose list head specifies the endpoint.
116 typedef struct {
117 RING ring[1]; /* circular list linkage */
118 SCACHE_MULTI_HEAD *head; /* parent linkage: list head */
119 int fd; /* cached session */
120 char *endp_prop; /* binding properties */
121 } SCACHE_MULTI_ENDP;
123 #define RING_TO_MULTI_ENDP(p) RING_TO_APPL((p), SCACHE_MULTI_ENDP, ring)
125 static void scache_multi_expire_endp(int, char *);
128 * When deleting a circular list element, are we deleting the entire
129 * circular list, or are we removing a single list element. We need this
130 * distinction to avoid a re-entrancy problem between htable_delete() and
131 * htable_free().
133 #define BOTTOM_UP 1 /* one item */
134 #define TOP_DOWN 2 /* whole list */
136 /* scache_multi_drop_endp - destroy endpoint->session binding */
138 static void scache_multi_drop_endp(SCACHE_MULTI_ENDP *endp, int direction)
140 const char *myname = "scache_multi_drop_endp";
141 SCACHE_MULTI_HEAD *head;
143 if (msg_verbose)
144 msg_info("%s: endp_prop=%s fd=%d", myname,
145 endp->endp_prop, endp->fd);
148 * Stop the timer.
150 event_cancel_timer(scache_multi_expire_endp, (char *) endp);
153 * In bottom-up mode, remove the list head from the endpoint hash when
154 * the list becomes empty. Otherwise, remove the endpoint->session
155 * binding from the list.
157 ring_detach(endp->ring);
158 head = endp->head;
159 head->cache->sess_count--;
160 if (direction == BOTTOM_UP && ring_pred(head->ring) == head->ring)
161 htable_delete(head->cache->endp_cache, head->parent_key, myfree);
164 * Destroy the endpoint->session binding.
166 if (endp->fd >= 0 && close(endp->fd) != 0)
167 msg_warn("%s: close(%d): %m", myname, endp->fd);
168 myfree(endp->endp_prop);
170 myfree((char *) endp);
173 /* scache_multi_expire_endp - event timer call-back */
175 static void scache_multi_expire_endp(int unused_event, char *context)
177 SCACHE_MULTI_ENDP *endp = (SCACHE_MULTI_ENDP *) context;
179 scache_multi_drop_endp(endp, BOTTOM_UP);
182 /* scache_multi_free_endp - hash table destructor call-back */
184 static void scache_multi_free_endp(char *ptr)
186 SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
187 SCACHE_MULTI_ENDP *endp;
188 RING *ring;
191 * Delete each endpoint->session binding in the list, then delete the
192 * list head. Note: this changes the list, so we must iterate carefully.
194 while ((ring = ring_succ(head->ring)) != head->ring) {
195 endp = RING_TO_MULTI_ENDP(ring);
196 scache_multi_drop_endp(endp, TOP_DOWN);
198 myfree((char *) head);
201 /* scache_multi_save_endp - save endpoint->session binding */
203 static void scache_multi_save_endp(SCACHE *scache, int ttl,
204 const char *endp_label,
205 const char *endp_prop, int fd)
207 const char *myname = "scache_multi_save_endp";
208 SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
209 SCACHE_MULTI_HEAD *head;
210 SCACHE_MULTI_ENDP *endp;
212 if (ttl < 0)
213 msg_panic("%s: bad ttl: %d", myname, ttl);
216 * Look up or instantiate the list head with the endpoint name.
218 if ((head = (SCACHE_MULTI_HEAD *)
219 htable_find(sp->endp_cache, endp_label)) == 0) {
220 head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
221 ring_init(head->ring);
222 head->parent_key =
223 htable_enter(sp->endp_cache, endp_label, (char *) head)->key;
224 head->cache = sp;
228 * Add the endpoint->session binding to the list. There can never be a
229 * duplicate, because each session must have a different file descriptor.
231 endp = (SCACHE_MULTI_ENDP *) mymalloc(sizeof(*endp));
232 endp->head = head;
233 endp->fd = fd;
234 endp->endp_prop = mystrdup(endp_prop);
235 ring_prepend(head->ring, endp->ring);
236 sp->sess_count++;
239 * Make sure this binding will go away eventually.
241 event_request_timer(scache_multi_expire_endp, (char *) endp, ttl);
243 if (msg_verbose)
244 msg_info("%s: endp_label=%s -> endp_prop=%s fd=%d",
245 myname, endp_label, endp_prop, fd);
248 /* scache_multi_find_endp - look up session for named endpoint */
250 static int scache_multi_find_endp(SCACHE *scache, const char *endp_label,
251 VSTRING *endp_prop)
253 const char *myname = "scache_multi_find_endp";
254 SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
255 SCACHE_MULTI_HEAD *head;
256 SCACHE_MULTI_ENDP *endp;
257 RING *ring;
258 int fd;
261 * Look up the list head with the endpoint name.
263 if ((head = (SCACHE_MULTI_HEAD *)
264 htable_find(sp->endp_cache, endp_label)) == 0) {
265 if (msg_verbose)
266 msg_info("%s: no endpoint cache: endp_label=%s",
267 myname, endp_label);
268 return (-1);
272 * Use the first available session. Remove the session from the cache
273 * because we're giving it to someone else.
275 if ((ring = ring_succ(head->ring)) != head->ring) {
276 endp = RING_TO_MULTI_ENDP(ring);
277 fd = endp->fd;
278 endp->fd = -1;
279 vstring_strcpy(endp_prop, endp->endp_prop);
280 if (msg_verbose)
281 msg_info("%s: found: endp_label=%s -> endp_prop=%s fd=%d",
282 myname, endp_label, endp->endp_prop, fd);
283 scache_multi_drop_endp(endp, BOTTOM_UP);
284 return (fd);
286 if (msg_verbose)
287 msg_info("%s: not found: endp_label=%s", myname, endp_label);
288 return (-1);
291 /* scache_multi_drop_dest - delete destination->endpoint binding */
293 static void scache_multi_drop_dest(SCACHE_MULTI_DEST *dest, int direction)
295 const char *myname = "scache_multi_drop_dest";
296 SCACHE_MULTI_HEAD *head;
298 if (msg_verbose)
299 msg_info("%s: dest_prop=%s endp_label=%s",
300 myname, dest->dest_prop, dest->endp_label);
303 * Stop the timer.
305 event_cancel_timer(scache_multi_expire_dest, (char *) dest);
308 * In bottom-up mode, remove the list head from the destination hash when
309 * the list becomes empty. Otherwise, remove the destination->endpoint
310 * binding from the list.
312 ring_detach(dest->ring);
313 head = dest->head;
314 if (direction == BOTTOM_UP && ring_pred(head->ring) == head->ring)
315 htable_delete(head->cache->dest_cache, head->parent_key, myfree);
318 * Destroy the destination->endpoint binding.
320 myfree(dest->dest_prop);
321 myfree(dest->endp_label);
323 myfree((char *) dest);
326 /* scache_multi_expire_dest - event timer call-back */
328 static void scache_multi_expire_dest(int unused_event, char *context)
330 SCACHE_MULTI_DEST *dest = (SCACHE_MULTI_DEST *) context;
332 scache_multi_drop_dest(dest, BOTTOM_UP);
335 /* scache_multi_free_dest - hash table destructor call-back */
337 static void scache_multi_free_dest(char *ptr)
339 SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
340 SCACHE_MULTI_DEST *dest;
341 RING *ring;
344 * Delete each destination->endpoint binding in the list, then delete the
345 * list head. Note: this changes the list, so we must iterate carefully.
347 while ((ring = ring_succ(head->ring)) != head->ring) {
348 dest = RING_TO_MULTI_DEST(ring);
349 scache_multi_drop_dest(dest, TOP_DOWN);
351 myfree((char *) head);
354 /* scache_multi_save_dest - save destination->endpoint binding */
356 static void scache_multi_save_dest(SCACHE *scache, int ttl,
357 const char *dest_label,
358 const char *dest_prop,
359 const char *endp_label)
361 const char *myname = "scache_multi_save_dest";
362 SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
363 SCACHE_MULTI_HEAD *head;
364 SCACHE_MULTI_DEST *dest;
365 RING *ring;
366 int refresh = 0;
368 if (ttl < 0)
369 msg_panic("%s: bad ttl: %d", myname, ttl);
372 * Look up or instantiate the list head with the destination name.
374 if ((head = (SCACHE_MULTI_HEAD *)
375 htable_find(sp->dest_cache, dest_label)) == 0) {
376 head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
377 ring_init(head->ring);
378 head->parent_key =
379 htable_enter(sp->dest_cache, dest_label, (char *) head)->key;
380 head->cache = sp;
384 * Look up or instantiate the destination->endpoint binding. Update the
385 * expiration time if this destination->endpoint binding already exists.
387 RING_FOREACH(ring, head->ring) {
388 dest = RING_TO_MULTI_DEST(ring);
389 if (strcmp(dest->endp_label, endp_label) == 0
390 && strcmp(dest->dest_prop, dest_prop) == 0) {
391 refresh = 1;
392 break;
395 if (refresh == 0) {
396 dest = (SCACHE_MULTI_DEST *) mymalloc(sizeof(*dest));
397 dest->head = head;
398 dest->endp_label = mystrdup(endp_label);
399 dest->dest_prop = mystrdup(dest_prop);
400 ring_prepend(head->ring, dest->ring);
404 * Make sure this binding will go away eventually.
406 event_request_timer(scache_multi_expire_dest, (char *) dest, ttl);
408 if (msg_verbose)
409 msg_info("%s: dest_label=%s -> dest_prop=%s endp_label=%s%s",
410 myname, dest_label, dest_prop, endp_label,
411 refresh ? " (refreshed)" : "");
414 /* scache_multi_find_dest - look up session for named destination */
416 static int scache_multi_find_dest(SCACHE *scache, const char *dest_label,
417 VSTRING *dest_prop,
418 VSTRING *endp_prop)
420 const char *myname = "scache_multi_find_dest";
421 SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
422 SCACHE_MULTI_HEAD *head;
423 SCACHE_MULTI_DEST *dest;
424 RING *ring;
425 int fd;
428 * Look up the list head with the destination name.
430 if ((head = (SCACHE_MULTI_HEAD *)
431 htable_find(sp->dest_cache, dest_label)) == 0) {
432 if (msg_verbose)
433 msg_info("%s: no destination cache: dest_label=%s",
434 myname, dest_label);
435 return (-1);
439 * Search endpoints for the first available session.
441 RING_FOREACH(ring, head->ring) {
442 dest = RING_TO_MULTI_DEST(ring);
443 fd = scache_multi_find_endp(scache, dest->endp_label, endp_prop);
444 if (fd >= 0) {
445 vstring_strcpy(dest_prop, dest->dest_prop);
446 return (fd);
449 if (msg_verbose)
450 msg_info("%s: not found: dest_label=%s", myname, dest_label);
451 return (-1);
454 /* scache_multi_size - size of multi-element cache object */
456 static void scache_multi_size(SCACHE *scache, SCACHE_SIZE *size)
458 SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
460 size->dest_count = sp->dest_cache->used;
461 size->endp_count = sp->endp_cache->used;
462 size->sess_count = sp->sess_count;
465 /* scache_multi_free - destroy multi-element cache object */
467 static void scache_multi_free(SCACHE *scache)
469 SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
471 htable_free(sp->dest_cache, scache_multi_free_dest);
472 htable_free(sp->endp_cache, scache_multi_free_endp);
474 myfree((char *) sp);
477 /* scache_multi_create - initialize */
479 SCACHE *scache_multi_create(void)
481 SCACHE_MULTI *sp = (SCACHE_MULTI *) mymalloc(sizeof(*sp));
483 sp->scache->save_endp = scache_multi_save_endp;
484 sp->scache->find_endp = scache_multi_find_endp;
485 sp->scache->save_dest = scache_multi_save_dest;
486 sp->scache->find_dest = scache_multi_find_dest;
487 sp->scache->size = scache_multi_size;
488 sp->scache->free = scache_multi_free;
490 sp->dest_cache = htable_create(1);
491 sp->endp_cache = htable_create(1);
492 sp->sess_count = 0;
494 return (sp->scache);