diff options
Diffstat (limited to 'agent/helpers/cache_handler.c')
-rw-r--r-- | agent/helpers/cache_handler.c | 771 |
1 files changed, 771 insertions, 0 deletions
diff --git a/agent/helpers/cache_handler.c b/agent/helpers/cache_handler.c new file mode 100644 index 0000000..520a4cf --- /dev/null +++ b/agent/helpers/cache_handler.c @@ -0,0 +1,771 @@ +/* Portions of this file are subject to the following copyright(s). See + * the Net-SNMP's COPYING file for more details and other copyrights + * that may apply: + */ +/* + * Portions of this file are copyrighted by: + * Copyright (C) 2007 Apple, Inc. All rights reserved. + * Use is subject to license terms specified in the COPYING file + * distributed with the Net-SNMP package. + */ +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-features.h> + +#if HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> + +#include <net-snmp/agent/cache_handler.h> + +netsnmp_feature_child_of(cache_handler, mib_helpers) + +netsnmp_feature_child_of(cache_find_by_oid, cache_handler) +netsnmp_feature_child_of(cache_get_head, cache_handler) + +static netsnmp_cache *cache_head = NULL; +static int cache_outstanding_valid = 0; +static int _cache_load( netsnmp_cache *cache ); + +#define CACHE_RELEASE_FREQUENCY 60 /* Check for expired caches every 60s */ + +void release_cached_resources(unsigned int regNo, + void *clientargs); + +/** @defgroup cache_handler cache_handler + * Maintains a cache of data for use by lower level handlers. + * @ingroup utilities + * This helper checks to see whether the data has been loaded "recently" + * (according to the timeout for that particular cache) and calls the + * registered "load_cache" routine if necessary. + * The lower handlers can then work with this local cached data. + * + * A timeout value of -1 will cause netsnmp_cache_check_expired() to + * always return true, and thus the cache will be reloaded for every + * request. + * + * To minimze resource use by the agent, a periodic callback checks for + * expired caches, and will call the free_cache function for any expired + * cache. + * + * The load_cache routine should return a negative number if the cache + * was not successfully loaded. 0 or any positive number indicates successs. + * + * + * Several flags can be set to affect the operations on the cache. + * + * If NETSNMP_CACHE_DONT_INVALIDATE_ON_SET is set, the free_cache method + * will not be called after a set request has processed. It is assumed that + * the lower mib handler using the cache has maintained cache consistency. + * + * If NETSNMP_CACHE_DONT_FREE_BEFORE_LOAD is set, the free_cache method + * will not be called before the load_cache method is called. It is assumed + * that the load_cache routine will properly deal with being called with a + * valid cache. + * + * If NETSNMP_CACHE_DONT_FREE_EXPIRED is set, the free_cache method will + * not be called with the cache expires. The expired flag will be set, but + * the valid flag will not be cleared. It is assumed that the load_cache + * routine will properly deal with being called with a valid cache. + * + * If NETSNMP_CACHE_PRELOAD is set when a the cache handler is created, + * the cache load routine will be called immediately. + * + * If NETSNMP_CACHE_DONT_AUTO_RELEASE is set, the periodic callback that + * checks for expired caches will skip the cache. The cache will only be + * checked for expiration when a request triggers the cache handler. This + * is useful if the cache has it's own periodic callback to keep the cache + * fresh. + * + * If NETSNMP_CACHE_AUTO_RELOAD is set, a timer will be set up to reload + * the cache when it expires. This is useful for keeping the cache fresh, + * even in the absence of incoming snmp requests. + * + * If NETSNMP_CACHE_RESET_TIMER_ON_USE is set, the expiry timer will be + * reset on each cache access. In practice the 'timeout' becomes a timer + * which triggers when the cache is no longer needed. This is useful + * if the cache is automatically kept synchronized: e.g. by receiving + * change notifications from Netlink, inotify or similar. This should + * not be used if cache is not synchronized automatically as it would + * result in stale cache information when if polling happens too fast. + * + * + * Here are some suggestions for some common situations. + * + * Cached File: + * If your table is based on a file that may periodically change, + * you can test the modification date to see if the file has + * changed since the last cache load. To get the cache helper to call + * the load function for every request, set the timeout to -1, which + * will cause the cache to always report that it is expired. This means + * that you will want to prevent the agent from flushing the cache when + * it has expired, and you will have to flush it manually if you + * detect that the file has changed. To accomplish this, set the + * following flags: + * + * NETSNMP_CACHE_DONT_FREE_EXPIRED + * NETSNMP_CACHE_DONT_AUTO_RELEASE + * + * + * Constant (periodic) reload: + * If you want the cache kept up to date regularly, even if no requests + * for the table are received, you can have your cache load routine + * called periodically. This is very useful if you need to monitor the + * data for changes (eg a <i>LastChanged</i> object). You will need to + * prevent the agent from flushing the cache when it expires. Set the + * cache timeout to the frequency, in seconds, that you wish to + * reload your cache, and set the following flags: + * + * NETSNMP_CACHE_DONT_FREE_EXPIRED + * NETSNMP_CACHE_DONT_AUTO_RELEASE + * NETSNMP_CACHE_AUTO_RELOAD + * + * Dynamically updated, unloaded after timeout: + * If the cache is kept up to date dynamically by listening for + * change notifications somehow, but it should not be in memory + * if it's not needed. Set the following flag: + * + * NETSNMP_CACHE_RESET_TIMER_ON_USE + * + * @{ + */ + +static void +_cache_free( netsnmp_cache *cache ); + +#ifndef NETSNMP_FEATURE_REMOVE_CACHE_GET_HEAD +/** get cache head + * @internal + * unadvertised function to get cache head. You really should not + * do this, since the internal storage mechanism might change. + */ +netsnmp_cache * +netsnmp_cache_get_head(void) +{ + return cache_head; +} +#endif /* NETSNMP_FEATURE_REMOVE_CACHE_GET_HEAD */ + +#ifndef NETSNMP_FEATURE_REMOVE_CACHE_FIND_BY_OID +/** find existing cache + */ +netsnmp_cache * +netsnmp_cache_find_by_oid(const oid * rootoid, int rootoid_len) +{ + netsnmp_cache *cache; + + for (cache = cache_head; cache; cache = cache->next) { + if (0 == netsnmp_oid_equals(cache->rootoid, cache->rootoid_len, + rootoid, rootoid_len)) + return cache; + } + + return NULL; +} +#endif /* NETSNMP_FEATURE_REMOVE_CACHE_FIND_BY_OID */ + +/** returns a cache + */ +netsnmp_cache * +netsnmp_cache_create(int timeout, NetsnmpCacheLoad * load_hook, + NetsnmpCacheFree * free_hook, + const oid * rootoid, int rootoid_len) +{ + netsnmp_cache *cache = NULL; + + cache = SNMP_MALLOC_TYPEDEF(netsnmp_cache); + if (NULL == cache) { + snmp_log(LOG_ERR,"malloc error in netsnmp_cache_create\n"); + return NULL; + } + cache->timeout = timeout; + cache->load_cache = load_hook; + cache->free_cache = free_hook; + cache->enabled = 1; + + if(0 == cache->timeout) + cache->timeout = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_CACHE_TIMEOUT); + + + /* + * Add the registered OID information, and tack + * this onto the list for cache SNMP management + * + * Note that this list is not ordered. + * table_iterator rules again! + */ + if (rootoid) { + cache->rootoid = snmp_duplicate_objid(rootoid, rootoid_len); + cache->rootoid_len = rootoid_len; + cache->next = cache_head; + if (cache_head) + cache_head->prev = cache; + cache_head = cache; + } + + return cache; +} + +static netsnmp_cache * +netsnmp_cache_ref(netsnmp_cache *cache) +{ + cache->refcnt++; + return cache; +} + +static void +netsnmp_cache_deref(netsnmp_cache *cache) +{ + if (--cache->refcnt == 0) { + netsnmp_cache_remove(cache); + netsnmp_cache_free(cache); + } +} + +/** frees a cache + */ +int +netsnmp_cache_free(netsnmp_cache *cache) +{ + netsnmp_cache *pos; + + if (NULL == cache) + return SNMPERR_SUCCESS; + + for (pos = cache_head; pos; pos = pos->next) { + if (pos == cache) { + size_t out_len = 0; + size_t buf_len = 0; + char *buf = NULL; + + sprint_realloc_objid((u_char **) &buf, &buf_len, &out_len, + 1, pos->rootoid, pos->rootoid_len); + snmp_log(LOG_WARNING, + "not freeing cache with root OID %s (still in list)\n", + buf); + free(buf); + return SNMP_ERR_GENERR; + } + } + + if(0 != cache->timer_id) + netsnmp_cache_timer_stop(cache); + + if (cache->valid) + _cache_free(cache); + + if (cache->timestampM) + free(cache->timestampM); + + if (cache->rootoid) + free(cache->rootoid); + + free(cache); + + return SNMPERR_SUCCESS; +} + +/** removes a cache + */ +int +netsnmp_cache_remove(netsnmp_cache *cache) +{ + netsnmp_cache *cur,*prev; + + if (!cache || !cache_head) + return -1; + + if (cache == cache_head) { + cache_head = cache_head->next; + if (cache_head) + cache_head->prev = NULL; + return 0; + } + + prev = cache_head; + cur = cache_head->next; + for (; cur; prev = cur, cur = cur->next) { + if (cache == cur) { + prev->next = cur->next; + if (cur->next) + cur->next->prev = cur->prev; + return 0; + } + } + return -1; +} + +/** callback function to call cache load function */ +static void +_timer_reload(unsigned int regNo, void *clientargs) +{ + netsnmp_cache *cache = (netsnmp_cache *)clientargs; + + DEBUGMSGT(("cache_timer:start", "loading cache %p\n", cache)); + + cache->expired = 1; + + _cache_load(cache); +} + +/** starts the recurring cache_load callback */ +unsigned int +netsnmp_cache_timer_start(netsnmp_cache *cache) +{ + if(NULL == cache) + return 0; + + DEBUGMSGTL(( "cache_timer:start", "OID: ")); + DEBUGMSGOID(("cache_timer:start", cache->rootoid, cache->rootoid_len)); + DEBUGMSG(( "cache_timer:start", "\n")); + + if(0 != cache->timer_id) { + snmp_log(LOG_WARNING, "cache has existing timer id.\n"); + return cache->timer_id; + } + + if(! (cache->flags & NETSNMP_CACHE_AUTO_RELOAD)) { + snmp_log(LOG_ERR, + "cache_timer_start called but auto_reload not set.\n"); + return 0; + } + + cache->timer_id = snmp_alarm_register(cache->timeout, SA_REPEAT, + _timer_reload, cache); + if(0 == cache->timer_id) { + snmp_log(LOG_ERR,"could not register alarm\n"); + return 0; + } + + cache->flags &= ~NETSNMP_CACHE_AUTO_RELOAD; + DEBUGMSGT(("cache_timer:start", + "starting timer %lu for cache %p\n", cache->timer_id, cache)); + return cache->timer_id; +} + +/** stops the recurring cache_load callback */ +void +netsnmp_cache_timer_stop(netsnmp_cache *cache) +{ + if(NULL == cache) + return; + + if(0 == cache->timer_id) { + snmp_log(LOG_WARNING, "cache has no timer id.\n"); + return; + } + + DEBUGMSGT(("cache_timer:stop", + "stopping timer %lu for cache %p\n", cache->timer_id, cache)); + + snmp_alarm_unregister(cache->timer_id); + cache->flags |= NETSNMP_CACHE_AUTO_RELOAD; +} + + +/** returns a cache handler that can be injected into a given handler chain. + */ +netsnmp_mib_handler * +netsnmp_cache_handler_get(netsnmp_cache* cache) +{ + netsnmp_mib_handler *ret = NULL; + + ret = netsnmp_create_handler("cache_handler", + netsnmp_cache_helper_handler); + if (ret) { + ret->flags |= MIB_HANDLER_AUTO_NEXT; + ret->myvoid = (void *) cache; + + if(NULL != cache) { + if ((cache->flags & NETSNMP_CACHE_PRELOAD) && ! cache->valid) { + /* + * load cache, ignore rc + * (failed load doesn't affect registration) + */ + (void)_cache_load(cache); + } + if (cache->flags & NETSNMP_CACHE_AUTO_RELOAD) + netsnmp_cache_timer_start(cache); + + } + } + return ret; +} + +/** Makes sure that memory allocated for the cache is freed when the handler + * is unregistered. + */ +void netsnmp_cache_handler_owns_cache(netsnmp_mib_handler *handler) +{ + netsnmp_assert(handler->myvoid); + ((netsnmp_cache *)handler->myvoid)->refcnt++; + handler->data_clone = (void *(*)(void *))netsnmp_cache_ref; + handler->data_free = (void(*)(void*))netsnmp_cache_deref; +} + +/** returns a cache handler that can be injected into a given handler chain. + */ +netsnmp_mib_handler * +netsnmp_get_cache_handler(int timeout, NetsnmpCacheLoad * load_hook, + NetsnmpCacheFree * free_hook, + const oid * rootoid, int rootoid_len) +{ + netsnmp_mib_handler *ret = NULL; + netsnmp_cache *cache = NULL; + + ret = netsnmp_cache_handler_get(NULL); + if (ret) { + cache = netsnmp_cache_create(timeout, load_hook, free_hook, + rootoid, rootoid_len); + ret->myvoid = (void *) cache; + netsnmp_cache_handler_owns_cache(ret); + } + return ret; +} + +/** functionally the same as calling netsnmp_register_handler() but also + * injects a cache handler at the same time for you. */ +netsnmp_feature_child_of(netsnmp_cache_handler_register,netsnmp_unused) +#ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_CACHE_HANDLER_REGISTER +int +netsnmp_cache_handler_register(netsnmp_handler_registration * reginfo, + netsnmp_cache* cache) +{ + netsnmp_mib_handler *handler = NULL; + handler = netsnmp_cache_handler_get(cache); + + netsnmp_inject_handler(reginfo, handler); + return netsnmp_register_handler(reginfo); +} +#endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_CACHE_HANDLER_REGISTER */ + +/** functionally the same as calling netsnmp_register_handler() but also + * injects a cache handler at the same time for you. */ +netsnmp_feature_child_of(netsnmp_register_cache_handler,netsnmp_unused) +#ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_REGISTER_CACHE_HANDLER +int +netsnmp_register_cache_handler(netsnmp_handler_registration * reginfo, + int timeout, NetsnmpCacheLoad * load_hook, + NetsnmpCacheFree * free_hook) +{ + netsnmp_mib_handler *handler = NULL; + handler = netsnmp_get_cache_handler(timeout, load_hook, free_hook, + reginfo->rootoid, + reginfo->rootoid_len); + + netsnmp_inject_handler(reginfo, handler); + return netsnmp_register_handler(reginfo); +} +#endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_REGISTER_CACHE_HANDLER */ + +static char * +_build_cache_name(const char *name) +{ + char *dup = (char*)malloc(strlen(name) + strlen(CACHE_NAME) + 2); + if (NULL == dup) + return NULL; + sprintf(dup, "%s:%s", CACHE_NAME, name); + return dup; +} + +/** Insert the cache information for a given request (PDU) */ +void +netsnmp_cache_reqinfo_insert(netsnmp_cache* cache, + netsnmp_agent_request_info * reqinfo, + const char *name) +{ + char *cache_name = _build_cache_name(name); + if (NULL == netsnmp_agent_get_list_data(reqinfo, cache_name)) { + DEBUGMSGTL(("verbose:helper:cache_handler", " adding '%s' to %p\n", + cache_name, reqinfo)); + netsnmp_agent_add_list_data(reqinfo, + netsnmp_create_data_list(cache_name, + cache, NULL)); + } + SNMP_FREE(cache_name); +} + +/** Extract the cache information for a given request (PDU) */ +netsnmp_cache * +netsnmp_cache_reqinfo_extract(netsnmp_agent_request_info * reqinfo, + const char *name) +{ + netsnmp_cache *result; + char *cache_name = _build_cache_name(name); + result = (netsnmp_cache*)netsnmp_agent_get_list_data(reqinfo, cache_name); + SNMP_FREE(cache_name); + return result; +} + +/** Extract the cache information for a given request (PDU) */ +netsnmp_feature_child_of(netsnmp_extract_cache_info,netsnmp_unused) +#ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_EXTRACT_CACHE_INFO +netsnmp_cache * +netsnmp_extract_cache_info(netsnmp_agent_request_info * reqinfo) +{ + return netsnmp_cache_reqinfo_extract(reqinfo, CACHE_NAME); +} +#endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_EXTRACT_CACHE_INFO */ + + +/** Check if the cache timeout has passed. Sets and return the expired flag. */ +int +netsnmp_cache_check_expired(netsnmp_cache *cache) +{ + if(NULL == cache) + return 0; + if (cache->expired) + return 1; + if(!cache->valid || (NULL == cache->timestampM) || (-1 == cache->timeout)) + cache->expired = 1; + else + cache->expired = netsnmp_ready_monotonic(cache->timestampM, + 1000 * cache->timeout); + + return cache->expired; +} + +/** Reload the cache if required */ +int +netsnmp_cache_check_and_reload(netsnmp_cache * cache) +{ + if (!cache) { + DEBUGMSGT(("helper:cache_handler", " no cache\n")); + return 0; /* ?? or -1 */ + } + if (!cache->valid || netsnmp_cache_check_expired(cache)) + return _cache_load( cache ); + else { + DEBUGMSGT(("helper:cache_handler", " cached (%d)\n", + cache->timeout)); + return 0; + } +} + +/** Is the cache valid for a given request? */ +int +netsnmp_cache_is_valid(netsnmp_agent_request_info * reqinfo, + const char* name) +{ + netsnmp_cache *cache = netsnmp_cache_reqinfo_extract(reqinfo, name); + return (cache && cache->valid); +} + +/** Is the cache valid for a given request? + * for backwards compatability. netsnmp_cache_is_valid() is preferred. + */ +netsnmp_feature_child_of(netsnmp_is_cache_valid,netsnmp_unused) +#ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_IS_CACHE_VALID +int +netsnmp_is_cache_valid(netsnmp_agent_request_info * reqinfo) +{ + return netsnmp_cache_is_valid(reqinfo, CACHE_NAME); +} +#endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_IS_CACHE_VALID */ + +/** Implements the cache handler */ +int +netsnmp_cache_helper_handler(netsnmp_mib_handler * handler, + netsnmp_handler_registration * reginfo, + netsnmp_agent_request_info * reqinfo, + netsnmp_request_info * requests) +{ + char addrstr[32]; + + netsnmp_cache *cache = NULL; + netsnmp_handler_args cache_hint; + + DEBUGMSGTL(("helper:cache_handler", "Got request (%d) for %s: ", + reqinfo->mode, reginfo->handlerName)); + DEBUGMSGOID(("helper:cache_handler", reginfo->rootoid, + reginfo->rootoid_len)); + DEBUGMSG(("helper:cache_handler", "\n")); + + netsnmp_assert(handler->flags & MIB_HANDLER_AUTO_NEXT); + + cache = (netsnmp_cache *) handler->myvoid; + if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, + NETSNMP_DS_AGENT_NO_CACHING) || + !cache || !cache->enabled || !cache->load_cache) { + DEBUGMSGT(("helper:cache_handler", " caching disabled or " + "cache not found, disabled or had no load method\n")); + return SNMP_ERR_NOERROR; + } + snprintf(addrstr,sizeof(addrstr), "%ld", (long int)cache); + DEBUGMSGTL(("helper:cache_handler", "using cache %s: ", addrstr)); + DEBUGMSGOID(("helper:cache_handler", cache->rootoid, cache->rootoid_len)); + DEBUGMSG(("helper:cache_handler", "\n")); + + /* + * Make the handler-chain parameters available to + * the cache_load hook routine. + */ + cache_hint.handler = handler; + cache_hint.reginfo = reginfo; + cache_hint.reqinfo = reqinfo; + cache_hint.requests = requests; + cache->cache_hint = &cache_hint; + + switch (reqinfo->mode) { + + case MODE_GET: + case MODE_GETNEXT: + case MODE_GETBULK: +#ifndef NETSNMP_NO_WRITE_SUPPORT + case MODE_SET_RESERVE1: +#endif /* !NETSNMP_NO_WRITE_SUPPORT */ + + /* + * only touch cache once per pdu request, to prevent a cache + * reload while a module is using cached data. + * + * XXX: this won't catch a request reloading the cache while + * a previous (delegated) request is still using the cache. + * maybe use a reference counter? + */ + if (netsnmp_cache_is_valid(reqinfo, addrstr)) + break; + + /* + * call the load hook, and update the cache timestamp. + * If it's not already there, add to reqinfo + */ + netsnmp_cache_check_and_reload(cache); + netsnmp_cache_reqinfo_insert(cache, reqinfo, addrstr); + /** next handler called automatically - 'AUTO_NEXT' */ + break; + +#ifndef NETSNMP_NO_WRITE_SUPPORT + case MODE_SET_RESERVE2: + case MODE_SET_FREE: + case MODE_SET_ACTION: + case MODE_SET_UNDO: + netsnmp_assert(netsnmp_cache_is_valid(reqinfo, addrstr)); + /** next handler called automatically - 'AUTO_NEXT' */ + break; + + /* + * A (successful) SET request wouldn't typically trigger a reload of + * the cache, but might well invalidate the current contents. + * Only do this on the last pass through. + */ + case MODE_SET_COMMIT: + if (cache->valid && + ! (cache->flags & NETSNMP_CACHE_DONT_INVALIDATE_ON_SET) ) { + cache->free_cache(cache, cache->magic); + cache->valid = 0; + } + /** next handler called automatically - 'AUTO_NEXT' */ + break; +#endif /* NETSNMP_NO_WRITE_SUPPORT */ + + default: + snmp_log(LOG_WARNING, "cache_handler: Unrecognised mode (%d)\n", + reqinfo->mode); + netsnmp_request_set_error_all(requests, SNMP_ERR_GENERR); + return SNMP_ERR_GENERR; + } + if (cache->flags & NETSNMP_CACHE_RESET_TIMER_ON_USE) + netsnmp_set_monotonic_marker(&cache->timestampM); + return SNMP_ERR_NOERROR; +} + +static void +_cache_free( netsnmp_cache *cache ) +{ + if (NULL != cache->free_cache) { + cache->free_cache(cache, cache->magic); + cache->valid = 0; + } +} + +static int +_cache_load( netsnmp_cache *cache ) +{ + int ret = -1; + + /* + * If we've got a valid cache, then release it before reloading + */ + if (cache->valid && + (! (cache->flags & NETSNMP_CACHE_DONT_FREE_BEFORE_LOAD))) + _cache_free(cache); + + if ( cache->load_cache) + ret = cache->load_cache(cache, cache->magic); + if (ret < 0) { + DEBUGMSGT(("helper:cache_handler", " load failed (%d)\n", ret)); + cache->valid = 0; + return ret; + } + cache->valid = 1; + cache->expired = 0; + + /* + * If we didn't previously have any valid caches outstanding, + * then schedule a pass of the auto-release routine. + */ + if ((!cache_outstanding_valid) && + (! (cache->flags & NETSNMP_CACHE_DONT_FREE_EXPIRED))) { + snmp_alarm_register(CACHE_RELEASE_FREQUENCY, + 0, release_cached_resources, NULL); + cache_outstanding_valid = 1; + } + netsnmp_set_monotonic_marker(&cache->timestampM); + DEBUGMSGT(("helper:cache_handler", " loaded (%d)\n", cache->timeout)); + + return ret; +} + + + +/** run regularly to automatically release cached resources. + * xxx - method to prevent cache from expiring while a request + * is being processed (e.g. delegated request). proposal: + * set a flag, which would be cleared when request finished + * (which could be acomplished by a dummy data list item in + * agent req info & custom free function). + */ +void +release_cached_resources(unsigned int regNo, void *clientargs) +{ + netsnmp_cache *cache = NULL; + + cache_outstanding_valid = 0; + DEBUGMSGTL(("helper:cache_handler", "running auto-release\n")); + for (cache = cache_head; cache; cache = cache->next) { + DEBUGMSGTL(("helper:cache_handler"," checking %p (flags 0x%x)\n", + cache, cache->flags)); + if (cache->valid && + ! (cache->flags & NETSNMP_CACHE_DONT_AUTO_RELEASE)) { + DEBUGMSGTL(("helper:cache_handler"," releasing %p\n", cache)); + /* + * Check to see if this cache has timed out. + * If so, release the cached resources. + * Otherwise, note that we still have at + * least one active cache. + */ + if (netsnmp_cache_check_expired(cache)) { + if(! (cache->flags & NETSNMP_CACHE_DONT_FREE_EXPIRED)) + _cache_free(cache); + } else { + cache_outstanding_valid = 1; + } + } + } + /* + * If there are any caches still valid & active, + * then schedule another pass. + */ + if (cache_outstanding_valid) { + snmp_alarm_register(CACHE_RELEASE_FREQUENCY, + 0, release_cached_resources, NULL); + } +} +/** @} */ + |