00001 #include <net-snmp/net-snmp-config.h> 00002 00003 #if HAVE_STRING_H 00004 #include <string.h> 00005 #else 00006 #include <strings.h> 00007 #endif 00008 00009 #include <net-snmp/net-snmp-includes.h> 00010 #include <net-snmp/agent/net-snmp-agent-includes.h> 00011 00012 #include <net-snmp/agent/cache_handler.h> 00013 00014 static netsnmp_cache *cache_head = NULL; 00015 static int cache_outstanding_valid = 0; 00016 static int _cache_load( netsnmp_cache *cache ); 00017 00018 #define CACHE_RELEASE_FREQUENCY 60 /* Check for expired caches every 60s */ 00019 00020 void release_cached_resources(unsigned int regNo, 00021 void *clientargs); 00022 00110 netsnmp_cache * 00111 netsnmp_cache_get_head(void) 00112 { 00113 return cache_head; 00114 } 00115 00118 netsnmp_cache * 00119 netsnmp_cache_find_by_oid(oid * rootoid, int rootoid_len) 00120 { 00121 netsnmp_cache *cache; 00122 00123 for (cache = cache_head; cache; cache = cache->next) { 00124 if (0 == netsnmp_oid_equals(cache->rootoid, cache->rootoid_len, 00125 rootoid, rootoid_len)) 00126 return cache; 00127 } 00128 00129 return NULL; 00130 } 00131 00134 netsnmp_cache * 00135 netsnmp_cache_create(int timeout, NetsnmpCacheLoad * load_hook, 00136 NetsnmpCacheFree * free_hook, 00137 oid * rootoid, int rootoid_len) 00138 { 00139 netsnmp_cache *cache = NULL; 00140 00141 cache = SNMP_MALLOC_TYPEDEF(netsnmp_cache); 00142 if (NULL == cache) { 00143 snmp_log(LOG_ERR,"malloc error in netsnmp_cache_create\n"); 00144 return NULL; 00145 } 00146 cache->timeout = timeout; 00147 cache->load_cache = load_hook; 00148 cache->free_cache = free_hook; 00149 cache->enabled = 1; 00150 00151 if(0 == cache->timeout) 00152 cache->timeout = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, 00153 NETSNMP_DS_AGENT_CACHE_TIMEOUT); 00154 00155 00156 /* 00157 * Add the registered OID information, and tack 00158 * this onto the list for cache SNMP management 00159 * 00160 * Note that this list is not ordered. 00161 * table_iterator rules again! 00162 */ 00163 if (rootoid) { 00164 cache->rootoid = snmp_duplicate_objid(rootoid, rootoid_len); 00165 cache->rootoid_len = rootoid_len; 00166 cache->next = cache_head; 00167 if (cache_head) 00168 cache_head->prev = cache; 00169 cache_head = cache; 00170 } 00171 00172 return cache; 00173 } 00174 00176 static void 00177 _timer_reload(unsigned int regNo, void *clientargs) 00178 { 00179 netsnmp_cache *cache = (netsnmp_cache *)clientargs; 00180 00181 DEBUGMSGT(("cache_timer:start", "loading cache %p\n", cache)); 00182 00183 cache->expired = 1; 00184 00185 _cache_load(cache); 00186 } 00187 00189 unsigned int 00190 netsnmp_cache_timer_start(netsnmp_cache *cache) 00191 { 00192 if(NULL == cache) 00193 return 0; 00194 00195 DEBUGMSGTL(( "cache_timer:start", "OID: ")); 00196 DEBUGMSGOID(("cache_timer:start", cache->rootoid, cache->rootoid_len)); 00197 DEBUGMSG(( "cache_timer:start", "\n")); 00198 00199 if(0 != cache->timer_id) { 00200 snmp_log(LOG_WARNING, "cache has existing timer id.\n"); 00201 return cache->timer_id; 00202 } 00203 00204 if(! (cache->flags & NETSNMP_CACHE_AUTO_RELOAD)) { 00205 snmp_log(LOG_ERR, 00206 "cache_timer_start called but auto_reload not set.\n"); 00207 return 0; 00208 } 00209 00210 cache->timer_id = snmp_alarm_register(cache->timeout, SA_REPEAT, 00211 _timer_reload, cache); 00212 if(0 == cache->timer_id) { 00213 snmp_log(LOG_ERR,"could not register alarm\n"); 00214 return 0; 00215 } 00216 00217 cache->flags &= ~NETSNMP_CACHE_AUTO_RELOAD; 00218 DEBUGMSGT(("cache_timer:start", 00219 "starting timer %d for cache %p\n", cache->timer_id, cache)); 00220 return cache->timer_id; 00221 } 00222 00224 void 00225 netsnmp_cache_timer_stop(netsnmp_cache *cache) 00226 { 00227 if(NULL == cache) 00228 return; 00229 00230 if(0 == cache->timer_id) { 00231 snmp_log(LOG_WARNING, "cache has no timer id.\n"); 00232 return; 00233 } 00234 00235 DEBUGMSGT(("cache_timer:stop", 00236 "stopping timer %d for cache %p\n", cache->timer_id, cache)); 00237 00238 snmp_alarm_unregister(cache->timer_id); 00239 cache->flags |= NETSNMP_CACHE_AUTO_RELOAD; 00240 } 00241 00242 00245 netsnmp_mib_handler * 00246 netsnmp_cache_handler_get(netsnmp_cache* cache) 00247 { 00248 netsnmp_mib_handler *ret = NULL; 00249 00250 ret = netsnmp_create_handler("cache_handler", 00251 netsnmp_cache_helper_handler); 00252 if (ret) { 00253 ret->flags |= MIB_HANDLER_AUTO_NEXT; 00254 ret->myvoid = (void *) cache; 00255 00256 if(NULL != cache) { 00257 if ((cache->flags & NETSNMP_CACHE_PRELOAD) && ! cache->valid) { 00258 /* 00259 * load cache, ignore rc 00260 * (failed load doesn't affect registration) 00261 */ 00262 (void)_cache_load(cache); 00263 } 00264 if (cache->flags & NETSNMP_CACHE_AUTO_RELOAD) 00265 netsnmp_cache_timer_start(cache); 00266 00267 } 00268 } 00269 return ret; 00270 } 00271 00274 netsnmp_mib_handler * 00275 netsnmp_get_cache_handler(int timeout, NetsnmpCacheLoad * load_hook, 00276 NetsnmpCacheFree * free_hook, 00277 oid * rootoid, int rootoid_len) 00278 { 00279 netsnmp_mib_handler *ret = NULL; 00280 netsnmp_cache *cache = NULL; 00281 00282 ret = netsnmp_cache_handler_get(NULL); 00283 if (ret) { 00284 cache = netsnmp_cache_create(timeout, load_hook, free_hook, 00285 rootoid, rootoid_len); 00286 ret->myvoid = (void *) cache; 00287 } 00288 return ret; 00289 } 00290 00293 int 00294 netsnmp_cache_handler_register(netsnmp_handler_registration * reginfo, 00295 netsnmp_cache* cache) 00296 { 00297 netsnmp_mib_handler *handler = NULL; 00298 handler = netsnmp_cache_handler_get(cache); 00299 00300 netsnmp_inject_handler(reginfo, handler); 00301 return netsnmp_register_handler(reginfo); 00302 } 00303 00306 int 00307 netsnmp_register_cache_handler(netsnmp_handler_registration * reginfo, 00308 int timeout, NetsnmpCacheLoad * load_hook, 00309 NetsnmpCacheFree * free_hook) 00310 { 00311 netsnmp_mib_handler *handler = NULL; 00312 handler = netsnmp_get_cache_handler(timeout, load_hook, free_hook, 00313 reginfo->rootoid, 00314 reginfo->rootoid_len); 00315 00316 netsnmp_inject_handler(reginfo, handler); 00317 return netsnmp_register_handler(reginfo); 00318 } 00319 00320 NETSNMP_STATIC_INLINE char * 00321 _build_cache_name(const char *name) 00322 { 00323 char *dup = malloc(strlen(name) + strlen(CACHE_NAME) + 2); 00324 if (NULL == dup) 00325 return NULL; 00326 sprintf(dup, "%s:%s", CACHE_NAME, name); 00327 return dup; 00328 } 00329 00331 void 00332 netsnmp_cache_reqinfo_insert(netsnmp_cache* cache, 00333 netsnmp_agent_request_info * reqinfo, 00334 const char *name) 00335 { 00336 char *cache_name = _build_cache_name(name); 00337 if (NULL == netsnmp_agent_get_list_data(reqinfo, cache_name)) { 00338 DEBUGMSGTL(("verbose:helper:cache_handler", " adding '%s' to %p\n", 00339 cache_name, reqinfo)); 00340 netsnmp_agent_add_list_data(reqinfo, 00341 netsnmp_create_data_list(cache_name, 00342 cache, NULL)); 00343 } 00344 SNMP_FREE(cache_name); 00345 } 00346 00348 netsnmp_cache * 00349 netsnmp_cache_reqinfo_extract(netsnmp_agent_request_info * reqinfo, 00350 const char *name) 00351 { 00352 netsnmp_cache *result; 00353 char *cache_name = _build_cache_name(name); 00354 result = netsnmp_agent_get_list_data(reqinfo, cache_name); 00355 SNMP_FREE(cache_name); 00356 return result; 00357 } 00358 00360 netsnmp_cache * 00361 netsnmp_extract_cache_info(netsnmp_agent_request_info * reqinfo) 00362 { 00363 return netsnmp_cache_reqinfo_extract(reqinfo, CACHE_NAME); 00364 } 00365 00366 00368 int 00369 netsnmp_cache_check_expired(netsnmp_cache *cache) 00370 { 00371 if(NULL == cache) 00372 return 0; 00373 00374 if(!cache->valid || (NULL == cache->timestamp) || (-1 == cache->timeout)) 00375 cache->expired = 1; 00376 else 00377 cache->expired = atime_ready(cache->timestamp, 1000 * cache->timeout); 00378 00379 return cache->expired; 00380 } 00381 00383 int 00384 netsnmp_cache_check_and_reload(netsnmp_cache * cache) 00385 { 00386 if (!cache) { 00387 DEBUGMSG(("helper:cache_handler", " no cache\n")); 00388 return 0; /* ?? or -1 */ 00389 } 00390 if (!cache->valid || netsnmp_cache_check_expired(cache)) 00391 return _cache_load( cache ); 00392 else { 00393 DEBUGMSG(("helper:cache_handler", " cached (%d)\n", 00394 cache->timeout)); 00395 return 0; 00396 } 00397 } 00398 00400 int 00401 netsnmp_cache_is_valid(netsnmp_agent_request_info * reqinfo, 00402 const char* name) 00403 { 00404 netsnmp_cache *cache = netsnmp_cache_reqinfo_extract(reqinfo, name); 00405 return (cache && cache->valid); 00406 } 00407 00411 int 00412 netsnmp_is_cache_valid(netsnmp_agent_request_info * reqinfo) 00413 { 00414 return netsnmp_cache_is_valid(reqinfo, CACHE_NAME); 00415 } 00416 00418 int 00419 netsnmp_cache_helper_handler(netsnmp_mib_handler * handler, 00420 netsnmp_handler_registration * reginfo, 00421 netsnmp_agent_request_info * reqinfo, 00422 netsnmp_request_info * requests) 00423 { 00424 netsnmp_cache *cache = NULL; 00425 netsnmp_handler_args cache_hint; 00426 00427 DEBUGMSGTL(("helper:cache_handler", "Got request (%d) for %s: ", 00428 reqinfo->mode, reginfo->handlerName)); 00429 DEBUGMSGOID(("helper:cache_handler", reginfo->rootoid, 00430 reginfo->rootoid_len)); 00431 DEBUGMSG(("helper:cache_handler", "\n")); 00432 00433 netsnmp_assert(handler->flags & MIB_HANDLER_AUTO_NEXT); 00434 00435 cache = (netsnmp_cache *) handler->myvoid; 00436 if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 00437 NETSNMP_DS_AGENT_NO_CACHING) || 00438 !cache || !cache->enabled || !cache->load_cache) { 00439 DEBUGMSG(("helper:cache_handler", " caching disabled or " 00440 "cache not found, disabled or had no load method\n")); 00441 return SNMP_ERR_NOERROR; 00442 } 00443 00444 /* 00445 * Make the handler-chain parameters available to 00446 * the cache_load hook routine. 00447 */ 00448 cache_hint.handler = handler; 00449 cache_hint.reginfo = reginfo; 00450 cache_hint.reqinfo = reqinfo; 00451 cache_hint.requests = requests; 00452 cache->cache_hint = &cache_hint; 00453 00454 switch (reqinfo->mode) { 00455 00456 case MODE_GET: 00457 case MODE_GETNEXT: 00458 case MODE_GETBULK: 00459 case MODE_SET_RESERVE1: { 00460 00461 /* 00462 * only touch cache once per pdu request, to prevent a cache 00463 * reload while a module is using cached data. 00464 * 00465 * XXX: this won't catch a request reloading the cache while 00466 * a previous (delegated) request is still using the cache. 00467 * maybe use a reference counter? 00468 */ 00469 if (netsnmp_cache_is_valid(reqinfo, reginfo->handlerName)) 00470 return SNMP_ERR_NOERROR; 00471 00472 /* 00473 * call the load hook, and update the cache timestamp. 00474 * If it's not already there, add to reqinfo 00475 */ 00476 netsnmp_cache_check_and_reload(cache); 00477 netsnmp_cache_reqinfo_insert(cache, reqinfo, reginfo->handlerName); 00479 } 00480 return SNMP_ERR_NOERROR; 00481 00482 case MODE_SET_RESERVE2: 00483 case MODE_SET_FREE: 00484 case MODE_SET_ACTION: 00485 case MODE_SET_UNDO: 00486 netsnmp_assert(netsnmp_cache_is_valid(reqinfo, reginfo->handlerName)); 00488 return SNMP_ERR_NOERROR; 00489 00490 /* 00491 * A (successful) SET request wouldn't typically trigger a reload of 00492 * the cache, but might well invalidate the current contents. 00493 * Only do this on the last pass through. 00494 */ 00495 case MODE_SET_COMMIT: 00496 if (cache->valid && 00497 ! (cache->flags & NETSNMP_CACHE_DONT_INVALIDATE_ON_SET) ) { 00498 cache->free_cache(cache, cache->magic); 00499 cache->valid = 0; 00500 } 00502 return SNMP_ERR_NOERROR; 00503 00504 default: 00505 snmp_log(LOG_WARNING, "cache_handler: Unrecognised mode (%d)\n", 00506 reqinfo->mode); 00507 netsnmp_request_set_error_all(requests, SNMP_ERR_GENERR); 00508 return SNMP_ERR_GENERR; 00509 } 00510 netsnmp_request_set_error_all(requests, SNMP_ERR_GENERR); 00511 return SNMP_ERR_GENERR; /* should never get here */ 00512 } 00513 00514 static void 00515 _cache_free( netsnmp_cache *cache ) 00516 { 00517 if (NULL != cache->free_cache) { 00518 cache->free_cache(cache, cache->magic); 00519 cache->valid = 0; 00520 } 00521 } 00522 00523 static int 00524 _cache_load( netsnmp_cache *cache ) 00525 { 00526 int ret = -1; 00527 00528 /* 00529 * If we've got a valid cache, then release it before reloading 00530 */ 00531 if (cache->valid && 00532 (! (cache->flags & NETSNMP_CACHE_DONT_FREE_BEFORE_LOAD))) 00533 _cache_free(cache); 00534 00535 if ( cache->load_cache) 00536 ret = cache->load_cache(cache, cache->magic); 00537 if (ret < 0) { 00538 DEBUGMSG(("helper:cache_handler", " load failed (%d)\n", 00539 ret)); 00540 cache->valid = 0; 00541 return ret; 00542 } 00543 cache->valid = 1; 00544 cache->expired = 0; 00545 00546 /* 00547 * If we didn't previously have any valid caches outstanding, 00548 * then schedule a pass of the auto-release routine. 00549 */ 00550 if ((!cache_outstanding_valid) && 00551 (! (cache->flags & NETSNMP_CACHE_DONT_FREE_EXPIRED))) { 00552 snmp_alarm_register(CACHE_RELEASE_FREQUENCY, 00553 0, release_cached_resources, NULL); 00554 cache_outstanding_valid = 1; 00555 } 00556 if (cache->timestamp) 00557 atime_setMarker(cache->timestamp); 00558 else 00559 cache->timestamp = atime_newMarker(); 00560 DEBUGMSG(("helper:cache_handler", " loaded (%d)\n", 00561 cache->timeout)); 00562 00563 return ret; 00564 } 00565 00566 00567 00575 void 00576 release_cached_resources(unsigned int regNo, void *clientargs) 00577 { 00578 netsnmp_cache *cache = NULL; 00579 00580 cache_outstanding_valid = 0; 00581 DEBUGMSGTL(("helper:cache_handler", "running auto-release\n")); 00582 for (cache = cache_head; cache; cache = cache->next) { 00583 DEBUGMSGTL(("helper:cache_handler"," checking %p (flags 0x%x)\n", 00584 cache, cache->flags)); 00585 if (cache->valid && 00586 ! (cache->flags & NETSNMP_CACHE_DONT_AUTO_RELEASE)) { 00587 DEBUGMSGTL(("helper:cache_handler"," releasing %p\n", cache)); 00588 /* 00589 * Check to see if this cache has timed out. 00590 * If so, release the cached resources. 00591 * Otherwise, note that we still have at 00592 * least one active cache. 00593 */ 00594 if (netsnmp_cache_check_expired(cache)) { 00595 if(! (cache->flags & NETSNMP_CACHE_DONT_FREE_EXPIRED)) 00596 _cache_free(cache); 00597 } else { 00598 cache_outstanding_valid = 1; 00599 } 00600 } 00601 } 00602 /* 00603 * If there are any caches still valid & active, 00604 * then schedule another pass. 00605 */ 00606 if (cache_outstanding_valid) { 00607 snmp_alarm_register(CACHE_RELEASE_FREQUENCY, 00608 0, release_cached_resources, NULL); 00609 } 00610 }
1.3.9.1
Last modified: Thursday, 01-Mar-2007 16:20:09 PST
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.