net-snmp 5.7
|
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 #include <net-snmp/net-snmp-features.h> 00013 00014 #if HAVE_STRING_H 00015 #include <string.h> 00016 #else 00017 #include <strings.h> 00018 #endif 00019 00020 #include <net-snmp/net-snmp-includes.h> 00021 #include <net-snmp/agent/net-snmp-agent-includes.h> 00022 00023 #include <net-snmp/agent/cache_handler.h> 00024 00025 netsnmp_feature_child_of(cache_handler, mib_helpers) 00026 00027 netsnmp_feature_child_of(cache_find_by_oid, cache_handler) 00028 netsnmp_feature_child_of(cache_get_head, cache_handler) 00029 00030 static netsnmp_cache *cache_head = NULL; 00031 static int cache_outstanding_valid = 0; 00032 static int _cache_load( netsnmp_cache *cache ); 00033 00034 #define CACHE_RELEASE_FREQUENCY 60 /* Check for expired caches every 60s */ 00035 00036 void release_cached_resources(unsigned int regNo, 00037 void *clientargs); 00038 00137 static void 00138 _cache_free( netsnmp_cache *cache ); 00139 00140 #ifndef NETSNMP_FEATURE_REMOVE_CACHE_GET_HEAD 00141 00146 netsnmp_cache * 00147 netsnmp_cache_get_head(void) 00148 { 00149 return cache_head; 00150 } 00151 #endif /* NETSNMP_FEATURE_REMOVE_CACHE_GET_HEAD */ 00152 00153 #ifndef NETSNMP_FEATURE_REMOVE_CACHE_FIND_BY_OID 00154 00156 netsnmp_cache * 00157 netsnmp_cache_find_by_oid(const oid * rootoid, int rootoid_len) 00158 { 00159 netsnmp_cache *cache; 00160 00161 for (cache = cache_head; cache; cache = cache->next) { 00162 if (0 == netsnmp_oid_equals(cache->rootoid, cache->rootoid_len, 00163 rootoid, rootoid_len)) 00164 return cache; 00165 } 00166 00167 return NULL; 00168 } 00169 #endif /* NETSNMP_FEATURE_REMOVE_CACHE_FIND_BY_OID */ 00170 00173 netsnmp_cache * 00174 netsnmp_cache_create(int timeout, NetsnmpCacheLoad * load_hook, 00175 NetsnmpCacheFree * free_hook, 00176 const oid * rootoid, int rootoid_len) 00177 { 00178 netsnmp_cache *cache = NULL; 00179 00180 cache = SNMP_MALLOC_TYPEDEF(netsnmp_cache); 00181 if (NULL == cache) { 00182 snmp_log(LOG_ERR,"malloc error in netsnmp_cache_create\n"); 00183 return NULL; 00184 } 00185 cache->timeout = timeout; 00186 cache->load_cache = load_hook; 00187 cache->free_cache = free_hook; 00188 cache->enabled = 1; 00189 00190 if(0 == cache->timeout) 00191 cache->timeout = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, 00192 NETSNMP_DS_AGENT_CACHE_TIMEOUT); 00193 00194 00195 /* 00196 * Add the registered OID information, and tack 00197 * this onto the list for cache SNMP management 00198 * 00199 * Note that this list is not ordered. 00200 * table_iterator rules again! 00201 */ 00202 if (rootoid) { 00203 cache->rootoid = snmp_duplicate_objid(rootoid, rootoid_len); 00204 cache->rootoid_len = rootoid_len; 00205 cache->next = cache_head; 00206 if (cache_head) 00207 cache_head->prev = cache; 00208 cache_head = cache; 00209 } 00210 00211 return cache; 00212 } 00213 00214 static netsnmp_cache * 00215 netsnmp_cache_ref(netsnmp_cache *cache) 00216 { 00217 cache->refcnt++; 00218 return cache; 00219 } 00220 00221 static void 00222 netsnmp_cache_deref(netsnmp_cache *cache) 00223 { 00224 if (--cache->refcnt == 0) { 00225 netsnmp_cache_remove(cache); 00226 netsnmp_cache_free(cache); 00227 } 00228 } 00229 00232 int 00233 netsnmp_cache_free(netsnmp_cache *cache) 00234 { 00235 netsnmp_cache *pos; 00236 00237 if (NULL == cache) 00238 return SNMPERR_SUCCESS; 00239 00240 for (pos = cache_head; pos; pos = pos->next) { 00241 if (pos == cache) { 00242 size_t out_len = 0; 00243 size_t buf_len = 0; 00244 char *buf = NULL; 00245 00246 sprint_realloc_objid((u_char **) &buf, &buf_len, &out_len, 00247 1, pos->rootoid, pos->rootoid_len); 00248 snmp_log(LOG_WARNING, 00249 "not freeing cache with root OID %s (still in list)\n", 00250 buf); 00251 free(buf); 00252 return SNMP_ERR_GENERR; 00253 } 00254 } 00255 00256 if(0 != cache->timer_id) 00257 netsnmp_cache_timer_stop(cache); 00258 00259 if (cache->valid) 00260 _cache_free(cache); 00261 00262 if (cache->timestamp) 00263 free(cache->timestamp); 00264 00265 if (cache->rootoid) 00266 free(cache->rootoid); 00267 00268 free(cache); 00269 00270 return SNMPERR_SUCCESS; 00271 } 00272 00275 int 00276 netsnmp_cache_remove(netsnmp_cache *cache) 00277 { 00278 netsnmp_cache *cur,*prev; 00279 00280 if (!cache || !cache_head) 00281 return -1; 00282 00283 if (cache == cache_head) { 00284 cache_head = cache_head->next; 00285 if (cache_head) 00286 cache_head->prev = NULL; 00287 return 0; 00288 } 00289 00290 prev = cache_head; 00291 cur = cache_head->next; 00292 for (; cur; prev = cur, cur = cur->next) { 00293 if (cache == cur) { 00294 prev->next = cur->next; 00295 if (cur->next) 00296 cur->next->prev = cur->prev; 00297 return 0; 00298 } 00299 } 00300 return -1; 00301 } 00302 00304 static void 00305 _timer_reload(unsigned int regNo, void *clientargs) 00306 { 00307 netsnmp_cache *cache = (netsnmp_cache *)clientargs; 00308 00309 DEBUGMSGT(("cache_timer:start", "loading cache %p\n", cache)); 00310 00311 cache->expired = 1; 00312 00313 _cache_load(cache); 00314 } 00315 00317 unsigned int 00318 netsnmp_cache_timer_start(netsnmp_cache *cache) 00319 { 00320 if(NULL == cache) 00321 return 0; 00322 00323 DEBUGMSGTL(( "cache_timer:start", "OID: ")); 00324 DEBUGMSGOID(("cache_timer:start", cache->rootoid, cache->rootoid_len)); 00325 DEBUGMSG(( "cache_timer:start", "\n")); 00326 00327 if(0 != cache->timer_id) { 00328 snmp_log(LOG_WARNING, "cache has existing timer id.\n"); 00329 return cache->timer_id; 00330 } 00331 00332 if(! (cache->flags & NETSNMP_CACHE_AUTO_RELOAD)) { 00333 snmp_log(LOG_ERR, 00334 "cache_timer_start called but auto_reload not set.\n"); 00335 return 0; 00336 } 00337 00338 cache->timer_id = snmp_alarm_register(cache->timeout, SA_REPEAT, 00339 _timer_reload, cache); 00340 if(0 == cache->timer_id) { 00341 snmp_log(LOG_ERR,"could not register alarm\n"); 00342 return 0; 00343 } 00344 00345 cache->flags &= ~NETSNMP_CACHE_AUTO_RELOAD; 00346 DEBUGMSGT(("cache_timer:start", 00347 "starting timer %lu for cache %p\n", cache->timer_id, cache)); 00348 return cache->timer_id; 00349 } 00350 00352 void 00353 netsnmp_cache_timer_stop(netsnmp_cache *cache) 00354 { 00355 if(NULL == cache) 00356 return; 00357 00358 if(0 == cache->timer_id) { 00359 snmp_log(LOG_WARNING, "cache has no timer id.\n"); 00360 return; 00361 } 00362 00363 DEBUGMSGT(("cache_timer:stop", 00364 "stopping timer %lu for cache %p\n", cache->timer_id, cache)); 00365 00366 snmp_alarm_unregister(cache->timer_id); 00367 cache->flags |= NETSNMP_CACHE_AUTO_RELOAD; 00368 } 00369 00370 00373 netsnmp_mib_handler * 00374 netsnmp_cache_handler_get(netsnmp_cache* cache) 00375 { 00376 netsnmp_mib_handler *ret = NULL; 00377 00378 ret = netsnmp_create_handler("cache_handler", 00379 netsnmp_cache_helper_handler); 00380 if (ret) { 00381 ret->flags |= MIB_HANDLER_AUTO_NEXT; 00382 ret->myvoid = (void *) cache; 00383 00384 if(NULL != cache) { 00385 if ((cache->flags & NETSNMP_CACHE_PRELOAD) && ! cache->valid) { 00386 /* 00387 * load cache, ignore rc 00388 * (failed load doesn't affect registration) 00389 */ 00390 (void)_cache_load(cache); 00391 } 00392 if (cache->flags & NETSNMP_CACHE_AUTO_RELOAD) 00393 netsnmp_cache_timer_start(cache); 00394 00395 } 00396 } 00397 return ret; 00398 } 00399 00403 void netsnmp_cache_handler_owns_cache(netsnmp_mib_handler *handler) 00404 { 00405 netsnmp_assert(handler->myvoid); 00406 ((netsnmp_cache *)handler->myvoid)->refcnt++; 00407 handler->data_clone = (void *(*)(void *))netsnmp_cache_ref; 00408 handler->data_free = (void(*)(void*))netsnmp_cache_deref; 00409 } 00410 00413 netsnmp_mib_handler * 00414 netsnmp_get_cache_handler(int timeout, NetsnmpCacheLoad * load_hook, 00415 NetsnmpCacheFree * free_hook, 00416 const oid * rootoid, int rootoid_len) 00417 { 00418 netsnmp_mib_handler *ret = NULL; 00419 netsnmp_cache *cache = NULL; 00420 00421 ret = netsnmp_cache_handler_get(NULL); 00422 if (ret) { 00423 cache = netsnmp_cache_create(timeout, load_hook, free_hook, 00424 rootoid, rootoid_len); 00425 ret->myvoid = (void *) cache; 00426 netsnmp_cache_handler_owns_cache(ret); 00427 } 00428 return ret; 00429 } 00430 00433 netsnmp_feature_child_of(netsnmp_cache_handler_register,netsnmp_unused) 00434 #ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_CACHE_HANDLER_REGISTER 00435 int 00436 netsnmp_cache_handler_register(netsnmp_handler_registration * reginfo, 00437 netsnmp_cache* cache) 00438 { 00439 netsnmp_mib_handler *handler = NULL; 00440 handler = netsnmp_cache_handler_get(cache); 00441 00442 netsnmp_inject_handler(reginfo, handler); 00443 return netsnmp_register_handler(reginfo); 00444 } 00445 #endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_CACHE_HANDLER_REGISTER */ 00446 00449 netsnmp_feature_child_of(netsnmp_register_cache_handler,netsnmp_unused) 00450 #ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_REGISTER_CACHE_HANDLER 00451 int 00452 netsnmp_register_cache_handler(netsnmp_handler_registration * reginfo, 00453 int timeout, NetsnmpCacheLoad * load_hook, 00454 NetsnmpCacheFree * free_hook) 00455 { 00456 netsnmp_mib_handler *handler = NULL; 00457 handler = netsnmp_get_cache_handler(timeout, load_hook, free_hook, 00458 reginfo->rootoid, 00459 reginfo->rootoid_len); 00460 00461 netsnmp_inject_handler(reginfo, handler); 00462 return netsnmp_register_handler(reginfo); 00463 } 00464 #endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_REGISTER_CACHE_HANDLER */ 00465 00466 static char * 00467 _build_cache_name(const char *name) 00468 { 00469 char *dup = (char*)malloc(strlen(name) + strlen(CACHE_NAME) + 2); 00470 if (NULL == dup) 00471 return NULL; 00472 sprintf(dup, "%s:%s", CACHE_NAME, name); 00473 return dup; 00474 } 00475 00477 void 00478 netsnmp_cache_reqinfo_insert(netsnmp_cache* cache, 00479 netsnmp_agent_request_info * reqinfo, 00480 const char *name) 00481 { 00482 char *cache_name = _build_cache_name(name); 00483 if (NULL == netsnmp_agent_get_list_data(reqinfo, cache_name)) { 00484 DEBUGMSGTL(("verbose:helper:cache_handler", " adding '%s' to %p\n", 00485 cache_name, reqinfo)); 00486 netsnmp_agent_add_list_data(reqinfo, 00487 netsnmp_create_data_list(cache_name, 00488 cache, NULL)); 00489 } 00490 SNMP_FREE(cache_name); 00491 } 00492 00494 netsnmp_cache * 00495 netsnmp_cache_reqinfo_extract(netsnmp_agent_request_info * reqinfo, 00496 const char *name) 00497 { 00498 netsnmp_cache *result; 00499 char *cache_name = _build_cache_name(name); 00500 result = (netsnmp_cache*)netsnmp_agent_get_list_data(reqinfo, cache_name); 00501 SNMP_FREE(cache_name); 00502 return result; 00503 } 00504 00506 netsnmp_feature_child_of(netsnmp_extract_cache_info,netsnmp_unused) 00507 #ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_EXTRACT_CACHE_INFO 00508 netsnmp_cache * 00509 netsnmp_extract_cache_info(netsnmp_agent_request_info * reqinfo) 00510 { 00511 return netsnmp_cache_reqinfo_extract(reqinfo, CACHE_NAME); 00512 } 00513 #endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_EXTRACT_CACHE_INFO */ 00514 00515 00517 int 00518 netsnmp_cache_check_expired(netsnmp_cache *cache) 00519 { 00520 if(NULL == cache) 00521 return 0; 00522 if (cache->expired) 00523 return 1; 00524 if(!cache->valid || (NULL == cache->timestamp) || (-1 == cache->timeout)) 00525 cache->expired = 1; 00526 else 00527 cache->expired = atime_ready(cache->timestamp, 1000 * cache->timeout); 00528 00529 return cache->expired; 00530 } 00531 00533 int 00534 netsnmp_cache_check_and_reload(netsnmp_cache * cache) 00535 { 00536 if (!cache) { 00537 DEBUGMSGT(("helper:cache_handler", " no cache\n")); 00538 return 0; /* ?? or -1 */ 00539 } 00540 if (!cache->valid || netsnmp_cache_check_expired(cache)) 00541 return _cache_load( cache ); 00542 else { 00543 DEBUGMSGT(("helper:cache_handler", " cached (%d)\n", 00544 cache->timeout)); 00545 return 0; 00546 } 00547 } 00548 00550 int 00551 netsnmp_cache_is_valid(netsnmp_agent_request_info * reqinfo, 00552 const char* name) 00553 { 00554 netsnmp_cache *cache = netsnmp_cache_reqinfo_extract(reqinfo, name); 00555 return (cache && cache->valid); 00556 } 00557 00561 netsnmp_feature_child_of(netsnmp_is_cache_valid,netsnmp_unused) 00562 #ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_IS_CACHE_VALID 00563 int 00564 netsnmp_is_cache_valid(netsnmp_agent_request_info * reqinfo) 00565 { 00566 return netsnmp_cache_is_valid(reqinfo, CACHE_NAME); 00567 } 00568 #endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_IS_CACHE_VALID */ 00569 00571 int 00572 netsnmp_cache_helper_handler(netsnmp_mib_handler * handler, 00573 netsnmp_handler_registration * reginfo, 00574 netsnmp_agent_request_info * reqinfo, 00575 netsnmp_request_info * requests) 00576 { 00577 char addrstr[32]; 00578 00579 netsnmp_cache *cache = NULL; 00580 netsnmp_handler_args cache_hint; 00581 00582 DEBUGMSGTL(("helper:cache_handler", "Got request (%d) for %s: ", 00583 reqinfo->mode, reginfo->handlerName)); 00584 DEBUGMSGOID(("helper:cache_handler", reginfo->rootoid, 00585 reginfo->rootoid_len)); 00586 DEBUGMSG(("helper:cache_handler", "\n")); 00587 00588 netsnmp_assert(handler->flags & MIB_HANDLER_AUTO_NEXT); 00589 00590 cache = (netsnmp_cache *) handler->myvoid; 00591 if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 00592 NETSNMP_DS_AGENT_NO_CACHING) || 00593 !cache || !cache->enabled || !cache->load_cache) { 00594 DEBUGMSGT(("helper:cache_handler", " caching disabled or " 00595 "cache not found, disabled or had no load method\n")); 00596 return SNMP_ERR_NOERROR; 00597 } 00598 snprintf(addrstr,sizeof(addrstr), "%ld", (long int)cache); 00599 DEBUGMSGTL(("helper:cache_handler", "using cache %s: ", addrstr)); 00600 DEBUGMSGOID(("helper:cache_handler", cache->rootoid, cache->rootoid_len)); 00601 DEBUGMSG(("helper:cache_handler", "\n")); 00602 00603 /* 00604 * Make the handler-chain parameters available to 00605 * the cache_load hook routine. 00606 */ 00607 cache_hint.handler = handler; 00608 cache_hint.reginfo = reginfo; 00609 cache_hint.reqinfo = reqinfo; 00610 cache_hint.requests = requests; 00611 cache->cache_hint = &cache_hint; 00612 00613 switch (reqinfo->mode) { 00614 00615 case MODE_GET: 00616 case MODE_GETNEXT: 00617 case MODE_GETBULK: 00618 #ifndef NETSNMP_NO_WRITE_SUPPORT 00619 case MODE_SET_RESERVE1: 00620 #endif /* !NETSNMP_NO_WRITE_SUPPORT */ 00621 00622 /* 00623 * only touch cache once per pdu request, to prevent a cache 00624 * reload while a module is using cached data. 00625 * 00626 * XXX: this won't catch a request reloading the cache while 00627 * a previous (delegated) request is still using the cache. 00628 * maybe use a reference counter? 00629 */ 00630 if (netsnmp_cache_is_valid(reqinfo, addrstr)) 00631 break; 00632 00633 /* 00634 * call the load hook, and update the cache timestamp. 00635 * If it's not already there, add to reqinfo 00636 */ 00637 netsnmp_cache_check_and_reload(cache); 00638 netsnmp_cache_reqinfo_insert(cache, reqinfo, addrstr); 00640 break; 00641 00642 #ifndef NETSNMP_NO_WRITE_SUPPORT 00643 case MODE_SET_RESERVE2: 00644 case MODE_SET_FREE: 00645 case MODE_SET_ACTION: 00646 case MODE_SET_UNDO: 00647 netsnmp_assert(netsnmp_cache_is_valid(reqinfo, addrstr)); 00649 break; 00650 00651 /* 00652 * A (successful) SET request wouldn't typically trigger a reload of 00653 * the cache, but might well invalidate the current contents. 00654 * Only do this on the last pass through. 00655 */ 00656 case MODE_SET_COMMIT: 00657 if (cache->valid && 00658 ! (cache->flags & NETSNMP_CACHE_DONT_INVALIDATE_ON_SET) ) { 00659 cache->free_cache(cache, cache->magic); 00660 cache->valid = 0; 00661 } 00663 break; 00664 #endif /* NETSNMP_NO_WRITE_SUPPORT */ 00665 00666 default: 00667 snmp_log(LOG_WARNING, "cache_handler: Unrecognised mode (%d)\n", 00668 reqinfo->mode); 00669 netsnmp_request_set_error_all(requests, SNMP_ERR_GENERR); 00670 return SNMP_ERR_GENERR; 00671 } 00672 if (cache->flags & NETSNMP_CACHE_RESET_TIMER_ON_USE) 00673 atime_setMarker(cache->timestamp); 00674 return SNMP_ERR_NOERROR; 00675 } 00676 00677 static void 00678 _cache_free( netsnmp_cache *cache ) 00679 { 00680 if (NULL != cache->free_cache) { 00681 cache->free_cache(cache, cache->magic); 00682 cache->valid = 0; 00683 } 00684 } 00685 00686 static int 00687 _cache_load( netsnmp_cache *cache ) 00688 { 00689 int ret = -1; 00690 00691 /* 00692 * If we've got a valid cache, then release it before reloading 00693 */ 00694 if (cache->valid && 00695 (! (cache->flags & NETSNMP_CACHE_DONT_FREE_BEFORE_LOAD))) 00696 _cache_free(cache); 00697 00698 if ( cache->load_cache) 00699 ret = cache->load_cache(cache, cache->magic); 00700 if (ret < 0) { 00701 DEBUGMSGT(("helper:cache_handler", " load failed (%d)\n", ret)); 00702 cache->valid = 0; 00703 return ret; 00704 } 00705 cache->valid = 1; 00706 cache->expired = 0; 00707 00708 /* 00709 * If we didn't previously have any valid caches outstanding, 00710 * then schedule a pass of the auto-release routine. 00711 */ 00712 if ((!cache_outstanding_valid) && 00713 (! (cache->flags & NETSNMP_CACHE_DONT_FREE_EXPIRED))) { 00714 snmp_alarm_register(CACHE_RELEASE_FREQUENCY, 00715 0, release_cached_resources, NULL); 00716 cache_outstanding_valid = 1; 00717 } 00718 if (cache->timestamp) 00719 atime_setMarker(cache->timestamp); 00720 else 00721 cache->timestamp = atime_newMarker(); 00722 DEBUGMSGT(("helper:cache_handler", " loaded (%d)\n", cache->timeout)); 00723 00724 return ret; 00725 } 00726 00727 00728 00736 void 00737 release_cached_resources(unsigned int regNo, void *clientargs) 00738 { 00739 netsnmp_cache *cache = NULL; 00740 00741 cache_outstanding_valid = 0; 00742 DEBUGMSGTL(("helper:cache_handler", "running auto-release\n")); 00743 for (cache = cache_head; cache; cache = cache->next) { 00744 DEBUGMSGTL(("helper:cache_handler"," checking %p (flags 0x%x)\n", 00745 cache, cache->flags)); 00746 if (cache->valid && 00747 ! (cache->flags & NETSNMP_CACHE_DONT_AUTO_RELEASE)) { 00748 DEBUGMSGTL(("helper:cache_handler"," releasing %p\n", cache)); 00749 /* 00750 * Check to see if this cache has timed out. 00751 * If so, release the cached resources. 00752 * Otherwise, note that we still have at 00753 * least one active cache. 00754 */ 00755 if (netsnmp_cache_check_expired(cache)) { 00756 if(! (cache->flags & NETSNMP_CACHE_DONT_FREE_EXPIRED)) 00757 _cache_free(cache); 00758 } else { 00759 cache_outstanding_valid = 1; 00760 } 00761 } 00762 } 00763 /* 00764 * If there are any caches still valid & active, 00765 * then schedule another pass. 00766 */ 00767 if (cache_outstanding_valid) { 00768 snmp_alarm_register(CACHE_RELEASE_FREQUENCY, 00769 0, release_cached_resources, NULL); 00770 } 00771 }