Postfix3.3.1
全て データ構造 ファイル 関数 変数 型定義 マクロ定義
scache_multi.c
[詳解]
1 /*++
2 /* NAME
3 /* scache_multi 3
4 /* SUMMARY
5 /* multi-session cache
6 /* SYNOPSIS
7 /* #include <scache.h>
8 /* DESCRIPTION
9 /* SCACHE *scache_multi_create()
10 /* DESCRIPTION
11 /* This module implements an in-memory, multi-session cache.
12 /*
13 /* scache_multi_create() instantiates a session cache that
14 /* stores multiple sessions.
15 /* DIAGNOSTICS
16 /* Fatal error: memory allocation problem;
17 /* panic: internal consistency failure.
18 /* SEE ALSO
19 /* scache(3), generic session cache API
20 /* LICENSE
21 /* .ad
22 /* .fi
23 /* The Secure Mailer license must be distributed with this software.
24 /* AUTHOR(S)
25 /* Wietse Venema
26 /* IBM T.J. Watson Research
27 /* P.O. Box 704
28 /* Yorktown Heights, NY 10598, USA
29 /*--*/
30 
31 /* System library. */
32 
33 #include <sys_defs.h>
34 #include <unistd.h>
35 #include <stddef.h> /* offsetof() */
36 #include <string.h>
37 
38 /* Utility library. */
39 
40 #include <msg.h>
41 #include <ring.h>
42 #include <htable.h>
43 #include <vstring.h>
44 #include <mymalloc.h>
45 #include <events.h>
46 
47 /*#define msg_verbose 1*/
48 
49 /* Global library. */
50 
51 #include <scache.h>
52 
53 /* Application-specific. */
54 
55  /*
56  * SCACHE_MULTI is a derived type from the SCACHE super-class.
57  *
58  * Each destination has an entry in the destination hash table, and each
59  * destination->endpoint binding is kept in a circular list under its
60  * destination hash table entry.
61  *
62  * Likewise, each endpoint has an entry in the endpoint hash table, and each
63  * endpoint->session binding is kept in a circular list under its endpoint
64  * hash table entry.
65  *
66  * We do not attempt to limit the number of destination or endpoint entries,
67  * nor do we attempt to limit the number of sessions. Doing so would require
68  * a write-through cache. Currently, the CTABLE cache module supports only
69  * read-through caching.
70  *
71  * This is no problem because the number of cached destinations is limited by
72  * design. Sites that specify a wild-card domain pattern, and thus cache
73  * every session in recent history, may be in for a surprise.
74  */
75 typedef struct {
76  SCACHE scache[1]; /* super-class */
77  HTABLE *dest_cache; /* destination->endpoint bindings */
78  HTABLE *endp_cache; /* endpoint->session bindings */
79  int sess_count; /* number of cached sessions */
80 } SCACHE_MULTI;
81 
82  /*
83  * Storage for a destination or endpoint list head. Each list head knows its
84  * own hash table entry name, so that we can remove the list when it becomes
85  * empty. List items are stored in a circular list under the list head.
86  */
87 typedef struct {
88  RING ring[1]; /* circular list linkage */
89  char *parent_key; /* parent linkage: hash table */
90  SCACHE_MULTI *cache; /* parent linkage: cache */
92 
93 #define RING_TO_MULTI_HEAD(p) RING_TO_APPL((p), SCACHE_MULTI_HEAD, ring)
94 
95  /*
96  * Storage for a destination->endpoint binding. This is an element in a
97  * circular list, whose list head specifies the destination.
98  */
99 typedef struct {
100  RING ring[1]; /* circular list linkage */
101  SCACHE_MULTI_HEAD *head; /* parent linkage: list head */
102  char *endp_label; /* endpoint name */
103  char *dest_prop; /* binding properties */
105 
106 #define RING_TO_MULTI_DEST(p) RING_TO_APPL((p), SCACHE_MULTI_DEST, ring)
107 
108 static void scache_multi_expire_dest(int, void *);
109 
110  /*
111  * Storage for an endpoint->session binding. This is an element in a
112  * circular list, whose list head specifies the endpoint.
113  */
114 typedef struct {
115  RING ring[1]; /* circular list linkage */
116  SCACHE_MULTI_HEAD *head; /* parent linkage: list head */
117  int fd; /* cached session */
118  char *endp_prop; /* binding properties */
120 
121 #define RING_TO_MULTI_ENDP(p) RING_TO_APPL((p), SCACHE_MULTI_ENDP, ring)
122 
123 static void scache_multi_expire_endp(int, void *);
124 
125  /*
126  * When deleting a circular list element, are we deleting the entire
127  * circular list, or are we removing a single list element. We need this
128  * distinction to avoid a re-entrancy problem between htable_delete() and
129  * htable_free().
130  */
131 #define BOTTOM_UP 1 /* one item */
132 #define TOP_DOWN 2 /* whole list */
133 
134 /* scache_multi_drop_endp - destroy endpoint->session binding */
135 
136 static void scache_multi_drop_endp(SCACHE_MULTI_ENDP *endp, int direction)
137 {
138  const char *myname = "scache_multi_drop_endp";
139  SCACHE_MULTI_HEAD *head;
140 
141  if (msg_verbose)
142  msg_info("%s: endp_prop=%s fd=%d", myname,
143  endp->endp_prop, endp->fd);
144 
145  /*
146  * Stop the timer.
147  */
148  event_cancel_timer(scache_multi_expire_endp, (void *) endp);
149 
150  /*
151  * In bottom-up mode, remove the list head from the endpoint hash when
152  * the list becomes empty. Otherwise, remove the endpoint->session
153  * binding from the list.
154  */
155  ring_detach(endp->ring);
156  head = endp->head;
157  head->cache->sess_count--;
158  if (direction == BOTTOM_UP && ring_pred(head->ring) == head->ring)
160 
161  /*
162  * Destroy the endpoint->session binding.
163  */
164  if (endp->fd >= 0 && close(endp->fd) != 0)
165  msg_warn("%s: close(%d): %m", myname, endp->fd);
166  myfree(endp->endp_prop);
167 
168  myfree((void *) endp);
169 }
170 
171 /* scache_multi_expire_endp - event timer call-back */
172 
173 static void scache_multi_expire_endp(int unused_event, void *context)
174 {
175  SCACHE_MULTI_ENDP *endp = (SCACHE_MULTI_ENDP *) context;
176 
177  scache_multi_drop_endp(endp, BOTTOM_UP);
178 }
179 
180 /* scache_multi_free_endp - hash table destructor call-back */
181 
182 static void scache_multi_free_endp(void *ptr)
183 {
184  SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
185  SCACHE_MULTI_ENDP *endp;
186  RING *ring;
187 
188  /*
189  * Delete each endpoint->session binding in the list, then delete the
190  * list head. Note: this changes the list, so we must iterate carefully.
191  */
192  while ((ring = ring_succ(head->ring)) != head->ring) {
193  endp = RING_TO_MULTI_ENDP(ring);
194  scache_multi_drop_endp(endp, TOP_DOWN);
195  }
196  myfree((void *) head);
197 }
198 
199 /* scache_multi_save_endp - save endpoint->session binding */
200 
201 static void scache_multi_save_endp(SCACHE *scache, int ttl,
202  const char *endp_label,
203  const char *endp_prop, int fd)
204 {
205  const char *myname = "scache_multi_save_endp";
206  SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
207  SCACHE_MULTI_HEAD *head;
208  SCACHE_MULTI_ENDP *endp;
209 
210  if (ttl < 0)
211  msg_panic("%s: bad ttl: %d", myname, ttl);
212 
213  /*
214  * Look up or instantiate the list head with the endpoint name.
215  */
216  if ((head = (SCACHE_MULTI_HEAD *)
217  htable_find(sp->endp_cache, endp_label)) == 0) {
218  head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
219  ring_init(head->ring);
220  head->parent_key =
221  htable_enter(sp->endp_cache, endp_label, (void *) head)->key;
222  head->cache = sp;
223  }
224 
225  /*
226  * Add the endpoint->session binding to the list. There can never be a
227  * duplicate, because each session must have a different file descriptor.
228  */
229  endp = (SCACHE_MULTI_ENDP *) mymalloc(sizeof(*endp));
230  endp->head = head;
231  endp->fd = fd;
232  endp->endp_prop = mystrdup(endp_prop);
233  ring_prepend(head->ring, endp->ring);
234  sp->sess_count++;
235 
236  /*
237  * Make sure this binding will go away eventually.
238  */
239  event_request_timer(scache_multi_expire_endp, (void *) endp, ttl);
240 
241  if (msg_verbose)
242  msg_info("%s: endp_label=%s -> endp_prop=%s fd=%d",
243  myname, endp_label, endp_prop, fd);
244 }
245 
246 /* scache_multi_find_endp - look up session for named endpoint */
247 
248 static int scache_multi_find_endp(SCACHE *scache, const char *endp_label,
249  VSTRING *endp_prop)
250 {
251  const char *myname = "scache_multi_find_endp";
252  SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
253  SCACHE_MULTI_HEAD *head;
254  SCACHE_MULTI_ENDP *endp;
255  RING *ring;
256  int fd;
257 
258  /*
259  * Look up the list head with the endpoint name.
260  */
261  if ((head = (SCACHE_MULTI_HEAD *)
262  htable_find(sp->endp_cache, endp_label)) == 0) {
263  if (msg_verbose)
264  msg_info("%s: no endpoint cache: endp_label=%s",
265  myname, endp_label);
266  return (-1);
267  }
268 
269  /*
270  * Use the first available session. Remove the session from the cache
271  * because we're giving it to someone else.
272  */
273  if ((ring = ring_succ(head->ring)) != head->ring) {
274  endp = RING_TO_MULTI_ENDP(ring);
275  fd = endp->fd;
276  endp->fd = -1;
277  vstring_strcpy(endp_prop, endp->endp_prop);
278  if (msg_verbose)
279  msg_info("%s: found: endp_label=%s -> endp_prop=%s fd=%d",
280  myname, endp_label, endp->endp_prop, fd);
281  scache_multi_drop_endp(endp, BOTTOM_UP);
282  return (fd);
283  }
284  if (msg_verbose)
285  msg_info("%s: not found: endp_label=%s", myname, endp_label);
286  return (-1);
287 }
288 
289 /* scache_multi_drop_dest - delete destination->endpoint binding */
290 
291 static void scache_multi_drop_dest(SCACHE_MULTI_DEST *dest, int direction)
292 {
293  const char *myname = "scache_multi_drop_dest";
294  SCACHE_MULTI_HEAD *head;
295 
296  if (msg_verbose)
297  msg_info("%s: dest_prop=%s endp_label=%s",
298  myname, dest->dest_prop, dest->endp_label);
299 
300  /*
301  * Stop the timer.
302  */
303  event_cancel_timer(scache_multi_expire_dest, (void *) dest);
304 
305  /*
306  * In bottom-up mode, remove the list head from the destination hash when
307  * the list becomes empty. Otherwise, remove the destination->endpoint
308  * binding from the list.
309  */
310  ring_detach(dest->ring);
311  head = dest->head;
312  if (direction == BOTTOM_UP && ring_pred(head->ring) == head->ring)
314 
315  /*
316  * Destroy the destination->endpoint binding.
317  */
318  myfree(dest->dest_prop);
319  myfree(dest->endp_label);
320 
321  myfree((void *) dest);
322 }
323 
324 /* scache_multi_expire_dest - event timer call-back */
325 
326 static void scache_multi_expire_dest(int unused_event, void *context)
327 {
328  SCACHE_MULTI_DEST *dest = (SCACHE_MULTI_DEST *) context;
329 
330  scache_multi_drop_dest(dest, BOTTOM_UP);
331 }
332 
333 /* scache_multi_free_dest - hash table destructor call-back */
334 
335 static void scache_multi_free_dest(void *ptr)
336 {
337  SCACHE_MULTI_HEAD *head = (SCACHE_MULTI_HEAD *) ptr;
338  SCACHE_MULTI_DEST *dest;
339  RING *ring;
340 
341  /*
342  * Delete each destination->endpoint binding in the list, then delete the
343  * list head. Note: this changes the list, so we must iterate carefully.
344  */
345  while ((ring = ring_succ(head->ring)) != head->ring) {
346  dest = RING_TO_MULTI_DEST(ring);
347  scache_multi_drop_dest(dest, TOP_DOWN);
348  }
349  myfree((void *) head);
350 }
351 
352 /* scache_multi_save_dest - save destination->endpoint binding */
353 
354 static void scache_multi_save_dest(SCACHE *scache, int ttl,
355  const char *dest_label,
356  const char *dest_prop,
357  const char *endp_label)
358 {
359  const char *myname = "scache_multi_save_dest";
360  SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
361  SCACHE_MULTI_HEAD *head;
362  SCACHE_MULTI_DEST *dest;
363  RING *ring;
364  int refresh = 0;
365 
366  if (ttl < 0)
367  msg_panic("%s: bad ttl: %d", myname, ttl);
368 
369  /*
370  * Look up or instantiate the list head with the destination name.
371  */
372  if ((head = (SCACHE_MULTI_HEAD *)
373  htable_find(sp->dest_cache, dest_label)) == 0) {
374  head = (SCACHE_MULTI_HEAD *) mymalloc(sizeof(*head));
375  ring_init(head->ring);
376  head->parent_key =
377  htable_enter(sp->dest_cache, dest_label, (void *) head)->key;
378  head->cache = sp;
379  }
380 
381  /*
382  * Look up or instantiate the destination->endpoint binding. Update the
383  * expiration time if this destination->endpoint binding already exists.
384  */
385  RING_FOREACH(ring, head->ring) {
386  dest = RING_TO_MULTI_DEST(ring);
387  if (strcmp(dest->endp_label, endp_label) == 0
388  && strcmp(dest->dest_prop, dest_prop) == 0) {
389  refresh = 1;
390  break;
391  }
392  }
393  if (refresh == 0) {
394  dest = (SCACHE_MULTI_DEST *) mymalloc(sizeof(*dest));
395  dest->head = head;
396  dest->endp_label = mystrdup(endp_label);
397  dest->dest_prop = mystrdup(dest_prop);
398  ring_prepend(head->ring, dest->ring);
399  }
400 
401  /*
402  * Make sure this binding will go away eventually.
403  */
404  event_request_timer(scache_multi_expire_dest, (void *) dest, ttl);
405 
406  if (msg_verbose)
407  msg_info("%s: dest_label=%s -> dest_prop=%s endp_label=%s%s",
408  myname, dest_label, dest_prop, endp_label,
409  refresh ? " (refreshed)" : "");
410 }
411 
412 /* scache_multi_find_dest - look up session for named destination */
413 
414 static int scache_multi_find_dest(SCACHE *scache, const char *dest_label,
415  VSTRING *dest_prop,
416  VSTRING *endp_prop)
417 {
418  const char *myname = "scache_multi_find_dest";
419  SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
420  SCACHE_MULTI_HEAD *head;
421  SCACHE_MULTI_DEST *dest;
422  RING *ring;
423  int fd;
424 
425  /*
426  * Look up the list head with the destination name.
427  */
428  if ((head = (SCACHE_MULTI_HEAD *)
429  htable_find(sp->dest_cache, dest_label)) == 0) {
430  if (msg_verbose)
431  msg_info("%s: no destination cache: dest_label=%s",
432  myname, dest_label);
433  return (-1);
434  }
435 
436  /*
437  * Search endpoints for the first available session.
438  */
439  RING_FOREACH(ring, head->ring) {
440  dest = RING_TO_MULTI_DEST(ring);
441  fd = scache_multi_find_endp(scache, dest->endp_label, endp_prop);
442  if (fd >= 0) {
443  vstring_strcpy(dest_prop, dest->dest_prop);
444  return (fd);
445  }
446  }
447  if (msg_verbose)
448  msg_info("%s: not found: dest_label=%s", myname, dest_label);
449  return (-1);
450 }
451 
452 /* scache_multi_size - size of multi-element cache object */
453 
454 static void scache_multi_size(SCACHE *scache, SCACHE_SIZE *size)
455 {
456  SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
457 
458  size->dest_count = sp->dest_cache->used;
459  size->endp_count = sp->endp_cache->used;
460  size->sess_count = sp->sess_count;
461 }
462 
463 /* scache_multi_free - destroy multi-element cache object */
464 
465 static void scache_multi_free(SCACHE *scache)
466 {
467  SCACHE_MULTI *sp = (SCACHE_MULTI *) scache;
468 
469  htable_free(sp->dest_cache, scache_multi_free_dest);
470  htable_free(sp->endp_cache, scache_multi_free_endp);
471 
472  myfree((void *) sp);
473 }
474 
475 /* scache_multi_create - initialize */
476 
478 {
479  SCACHE_MULTI *sp = (SCACHE_MULTI *) mymalloc(sizeof(*sp));
480 
481  sp->scache->save_endp = scache_multi_save_endp;
482  sp->scache->find_endp = scache_multi_find_endp;
483  sp->scache->save_dest = scache_multi_save_dest;
484  sp->scache->find_dest = scache_multi_find_dest;
485  sp->scache->size = scache_multi_size;
486  sp->scache->free = scache_multi_free;
487 
488  sp->dest_cache = htable_create(1);
489  sp->endp_cache = htable_create(1);
490  sp->sess_count = 0;
491 
492  return (sp->scache);
493 }
int msg_verbose
Definition: msg.c:177
void htable_free(HTABLE *table, void(*free_fn)(void *))
Definition: htable.c:287
Definition: ring.h:19
void myfree(void *ptr)
Definition: mymalloc.c:207
void ring_detach(RING *entry)
Definition: ring.c:111
int dest_count
Definition: scache.h:94
char * mystrdup(const char *str)
Definition: mymalloc.c:225
#define ring_pred(c)
Definition: ring.h:30
void(* size)(struct SCACHE *, SCACHE_SIZE *)
Definition: scache.h:108
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
void ring_init(RING *ring)
Definition: ring.c:79
ssize_t used
Definition: htable.h:27
SCACHE_MULTI_HEAD * head
Definition: scache_multi.c:101
HTABLE * dest_cache
Definition: scache_multi.c:77
SCACHE scache[1]
Definition: scache_multi.c:76
Definition: htable.h:25
SCACHE_FIND_DEST_FN find_dest
Definition: scache.h:107
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
HTABLE * endp_cache
Definition: scache_multi.c:78
#define BOTTOM_UP
Definition: scache_multi.c:131
HTABLE * htable_create(ssize_t size)
Definition: htable.c:179
SCACHE * scache_multi_create(void)
Definition: scache_multi.c:477
SCACHE_MULTI * cache
Definition: scache_multi.c:90
int sess_count
Definition: scache.h:96
#define RING_TO_MULTI_ENDP(p)
Definition: scache_multi.c:121
#define RING_FOREACH(entry, head)
Definition: ring.h:32
void msg_warn(const char *fmt,...)
Definition: msg.c:215
char * key
Definition: htable.h:17
void * htable_find(HTABLE *table, const char *key)
Definition: htable.c:227
SCACHE_SAVE_ENDP_FN save_endp
Definition: scache.h:104
SCACHE_SAVE_DEST_FN save_dest
Definition: scache.h:106
int endp_count
Definition: scache.h:95
SCACHE_FIND_ENDP_FN find_endp
Definition: scache.h:105
#define ring_succ(c)
Definition: ring.h:29
time_t event_request_timer(EVENT_NOTIFY_TIME_FN callback, void *context, int delay)
Definition: events.c:894
Definition: scache.h:103
void(* free)(struct SCACHE *)
Definition: scache.h:109
SCACHE_MULTI_HEAD * head
Definition: scache_multi.c:116
#define RING_TO_MULTI_DEST(p)
Definition: scache_multi.c:106
void htable_delete(HTABLE *table, const char *key, void(*free_fn)(void *))
Definition: htable.c:257
int event_cancel_timer(EVENT_NOTIFY_TIME_FN callback, void *context)
Definition: events.c:965
#define TOP_DOWN
Definition: scache_multi.c:132
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
HTABLE_INFO * htable_enter(HTABLE *table, const char *key, void *value)
Definition: htable.c:212
void msg_info(const char *fmt,...)
Definition: msg.c:199
void ring_prepend(RING *ring, RING *entry)
Definition: ring.c:99