1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /*
30 * This module contains a cache used to optimized scope and DA
31 * discovery. Entries live for a short duration only (about 10 seconds),
32 * although their lifetime can be advanced somewhat by frequent use.
33 * The intent is that the canonical source for DAs will always be slpd,
34 * so the short lifetime of cache entries is designed to force clients
35 * to consult slpd frequently so as to pick up the latest DA state
36 * quickly.
37 *
38 * The cache is managed by a thread which monitors calls into the cache.
39 * If the cache has been unused for a certain amount of time, the thread
40 * frees the cache and exits.
41 *
42 * The cache is keyed on the queries sent to slpd to access slpd's DA
43 * table. Associated with each query is a reply (in the format of an
44 * on-the-wire SLP SRVRPLY message).
45 * The cache is accessed by the following two functions:
46 *
47 * slp_find_das_cached: searches the cache
48 * slp_put_das_cached: adds a reply to the cache
49 *
50 * All parameters added to the cache are copied in first, and all results
51 * read from the cache are copied out, so all memory must be freed by
52 * the caller.
53 */
54
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <thread.h>
58 #include <synch.h>
59 #include <syslog.h>
60 #include <string.h>
61 #include <sys/types.h>
62 #include <time.h>
63 #include <errno.h>
64 #include <slp-internal.h>
65
66 /* These constants control the behaviour of the cache */
67 #define MAX_LIFETIME 25 /* max lifetime, in seconds */
68 #define ADVANCE_PER_USE 5 /* seconds lifetime is extended on each use */
69 #define INIT_LIFETIME 10 /* cache entries start with this lifetime */
70
71 /* Management thread components */
72 #define IDLE_TIMEOUT 30 /* thread will exit after this idle time */
73 static int cache_thr_running;
74 static mutex_t start_lock = DEFAULTMUTEX;
75 static int cache_called;
76 static cond_t cache_called_cond;
77 static mutex_t cache_called_lock = DEFAULTMUTEX;
78 static SLPError start_cache_thr();
79 static void cache_thr();
80
81 /* The cache and cache synchronization */
82 static void *da_cache;
83 static mutex_t cache_lock = DEFAULTMUTEX;
84 struct cache_entry {
85 const char *query;
86 const char *reply;
87 unsigned int reply_len;
88 time_t max_life;
89 time_t expires;
90 };
91 typedef struct cache_entry cache_entry_t;
92
93 /* cache management and searching */
94 static int compare_entries(const void *, const void *);
95 static void free_cache_entry(void *, VISIT);
96
97 /*
98 * Searches the cache for the reply to 'query'. Returns the reply if
99 * found, otherwise NULL.
100 * The caller must free the result.
101 */
slp_find_das_cached(const char * query)102 char *slp_find_das_cached(const char *query) {
103 cache_entry_t ce[1], **ans;
104 char *reply = NULL;
105 time_t now;
106
107 if (!cache_thr_running) {
108 if (start_cache_thr() != SLP_OK) {
109 return (NULL);
110 }
111 }
112
113 (void) mutex_lock(&cache_lock);
114 ce->query = query;
115
116 ans = slp_tfind(ce, &da_cache, compare_entries);
117 if (ans) {
118 now = time(NULL);
119 if ((*ans)->expires < now || (*ans)->max_life < now) {
120 goto done;
121 }
122
123 /* copy out the reply */
124 if (!(reply = malloc((*ans)->reply_len))) {
125 slp_err(LOG_CRIT, 0, "slp_find_das_cached",
126 "out of memory");
127 goto done;
128 }
129 (void) memcpy(reply, (*ans)->reply, (*ans)->reply_len);
130 (*ans)->expires += ADVANCE_PER_USE;
131 }
132
133 /* notify cache thread of call */
134 (void) mutex_lock(&cache_called_lock);
135 cache_called = 1;
136 (void) cond_signal(&cache_called_cond);
137 (void) mutex_unlock(&cache_called_lock);
138
139 done:
140 (void) mutex_unlock(&cache_lock);
141 return (reply);
142 }
143
144 /*
145 * Adds 'reply' to the cache under the index 'query'. Both parameters
146 * are copied in first, so the caller may free them after the call.
147 * 'len' is the length of 'reply' in bytes.
148 */
slp_put_das_cached(const char * query,const char * reply,unsigned int len)149 void slp_put_das_cached(const char *query, const char *reply,
150 unsigned int len) {
151 cache_entry_t *ce, **ce2;
152 time_t now;
153
154 if (!cache_thr_running) {
155 if (start_cache_thr() != SLP_OK) {
156 return;
157 }
158 }
159
160 /* create the cache entry for this reply */
161 if (!(ce = malloc(sizeof (*ce)))) {
162 slp_err(LOG_CRIT, 0, "slp_put_das_cached", "out of memory");
163 return;
164 }
165
166 if (!(ce->query = strdup(query))) {
167 free(ce);
168 slp_err(LOG_CRIT, 0, "slp_put_das_cached", "out of memory");
169 return;
170 }
171
172 if (!(ce->reply = malloc(len))) {
173 free((void *) (ce->query));
174 free(ce);
175 slp_err(LOG_CRIT, 0, "slp_put_das_cached", "out of memory");
176 return;
177 }
178 (void) memcpy((void *) (ce->reply), reply, len);
179 ce->reply_len = len;
180 now = time(NULL);
181 ce->max_life = now + MAX_LIFETIME;
182 ce->expires = now + INIT_LIFETIME;
183
184 /* write to the cache */
185 (void) mutex_lock(&cache_lock);
186 ce2 = slp_tsearch((void *) ce, &da_cache, compare_entries);
187 if (ce != *ce2) {
188 /* overwrite existing entry */
189 free((void *) ((*ce2)->query));
190 free((void *) ((*ce2)->reply));
191 free(*ce2);
192 *ce2 = ce;
193 }
194
195 (void) mutex_unlock(&cache_lock);
196 }
197
compare_entries(const void * x1,const void * x2)198 static int compare_entries(const void *x1, const void *x2) {
199 cache_entry_t *e1 = (cache_entry_t *)x1;
200 cache_entry_t *e2 = (cache_entry_t *)x2;
201
202 return (strcasecmp(e1->query, e2->query));
203 }
204
free_cache_entry(void * node,VISIT order)205 static void free_cache_entry(void *node, VISIT order) {
206 if (order == endorder || order == leaf) {
207 cache_entry_t *ce = *(cache_entry_t **)node;
208
209 free((void *) (ce->query));
210 free((void *) (ce->reply));
211 free(ce);
212 free(node);
213 }
214 }
215
start_cache_thr()216 static SLPError start_cache_thr() {
217 int terr;
218 SLPError err = SLP_OK;
219
220 (void) mutex_lock(&start_lock);
221
222 if (cache_thr_running) {
223 goto start_done;
224 }
225
226 (void) cond_init(&cache_called_cond, 0, NULL);
227
228 if ((terr = thr_create(
229 0, NULL, (void *(*)(void *)) cache_thr,
230 NULL, 0, NULL)) != 0) {
231 slp_err(LOG_CRIT, 0, "start_cache_thr",
232 "could not start thread: %s", strerror(terr));
233 err = SLP_INTERNAL_SYSTEM_ERROR;
234 goto start_done;
235 }
236 cache_thr_running = 1;
237
238 start_done:
239 (void) mutex_unlock(&start_lock);
240 return (err);
241 }
242
cache_thr()243 static void cache_thr() {
244 timestruc_t timeout;
245 timeout.tv_nsec = 0;
246
247 (void) mutex_lock(&cache_called_lock);
248 cache_called = 0;
249
250 while (cache_called == 0) {
251 int err;
252
253 timeout.tv_sec = IDLE_TIMEOUT;
254 err = cond_reltimedwait(&cache_called_cond,
255 &cache_called_lock, &timeout);
256
257 if (err == ETIME) {
258 (void) mutex_lock(&cache_lock);
259 /* free cache */
260 if (da_cache) {
261 slp_twalk(da_cache,
262 (void (*)(void *, VISIT, int, void *))free_cache_entry,
263 0, NULL);
264 }
265 da_cache = NULL;
266 (void) mutex_unlock(&cache_lock);
267 cache_thr_running = 0;
268 (void) mutex_unlock(&cache_called_lock);
269 thr_exit(NULL);
270 } else {
271 cache_called = 0;
272 }
273 }
274 }
275