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