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 */ 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 */ 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 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 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 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 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