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