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