cache_handler.c
上传用户:wxp200602
上传日期:2007-10-30
资源大小:4028k
文件大小:20k
- #include <net-snmp/net-snmp-config.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>
- #if HAVE_DMALLOC_H
- #include <dmalloc.h>
- #endif
- 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 route 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.
- *
- *
- * 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
- *
- * @{
- */
- /** 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;
- }
- /** find existing cache
- */
- netsnmp_cache *
- netsnmp_cache_find_by_oid(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;
- }
- /** returns a cache
- */
- netsnmp_cache *
- netsnmp_cache_create(int timeout, NetsnmpCacheLoad * load_hook,
- NetsnmpCacheFree * free_hook,
- 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_createn");
- 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;
- }
- /** 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 %pn", 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;
- 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 alarmn");
- return 0;
- }
- DEBUGMSGT(("cache_timer:start",
- "starting timer %d for cache %pn", 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 %d for cache %pn", cache->timer_id, cache));
- snmp_alarm_unregister(cache->timer_id);
- }
- /** 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;
- }
- /** 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,
- 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;
- }
- return ret;
- }
- /** functionally the same as calling netsnmp_register_handler() but also
- * injects a cache handler at the same time for you. */
- 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);
- }
- /** functionally the same as calling netsnmp_register_handler() but also
- * injects a cache handler at the same time for you. */
- 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);
- }
- NETSNMP_STATIC_INLINE char *
- _build_cache_name(const char *name)
- {
- char *dup = 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 %pn",
- 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_agent_get_list_data(reqinfo, cache_name);
- SNMP_FREE(cache_name);
- return result;
- }
- /** Extract the cache information for a given request (PDU) */
- netsnmp_cache *
- netsnmp_extract_cache_info(netsnmp_agent_request_info * reqinfo)
- {
- return netsnmp_cache_reqinfo_extract(reqinfo, CACHE_NAME);
- }
- /** 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->valid || (NULL == cache->timestamp) || (-1 == cache->timeout))
- cache->expired = 1;
- else
- cache->expired = atime_ready(cache->timestamp, 1000 * cache->timeout);
-
- return cache->expired;
- }
- /** Reload the cache if required */
- int
- netsnmp_cache_check_and_reload(netsnmp_cache * cache)
- {
- if (!cache) {
- DEBUGMSG(("helper:cache_handler", " no cachen"));
- return 0; /* ?? or -1 */
- }
- if (!cache->valid || netsnmp_cache_check_expired(cache))
- return _cache_load( cache );
- else {
- DEBUGMSG(("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.
- */
- int
- netsnmp_is_cache_valid(netsnmp_agent_request_info * reqinfo)
- {
- return netsnmp_cache_is_valid(reqinfo, CACHE_NAME);
- }
- /** 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)
- {
- netsnmp_cache *cache = NULL;
- DEBUGMSGTL(("helper:cache_handler", "Got request (%d) for %s: ",
- reqinfo->mode, reginfo->handlerName));
- DEBUGMSGOID(("helper:cache_handler", reginfo->rootoid,
- reginfo->rootoid_len));
- 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) {
- DEBUGMSG(("helper:cache_handler", " caching disabled or "
- "cache not found, disabled or had no load methodn"));
- return SNMP_ERR_NOERROR;
- }
- switch (reqinfo->mode) {
- case MODE_GET:
- case MODE_GETNEXT:
- case MODE_GETBULK:
- case MODE_SET_RESERVE1: {
- netsnmp_handler_args cache_hint;
- /*
- * 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, reginfo->handlerName))
- return SNMP_ERR_NOERROR;
- if (cache->flags & NETSNMP_CACHE_HINT_HANDLER_ARGS) {
- netsnmp_assert(NULL == cache->cache_hint);
- cache_hint.handler = handler;
- cache_hint.reginfo = reginfo;
- cache_hint.reqinfo = reqinfo;
- cache_hint.requests = requests;
- cache->cache_hint = &cache_hint;
- }
- /*
- * 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, reginfo->handlerName);
- cache->cache_hint = NULL;
- /** next handler called automatically - 'AUTO_NEXT' */
- }
- return SNMP_ERR_NOERROR;
- case MODE_SET_RESERVE2:
- case MODE_SET_FREE:
- case MODE_SET_ACTION:
- case MODE_SET_UNDO:
- netsnmp_assert(netsnmp_cache_is_valid(reqinfo, reginfo->handlerName));
- /** next handler called automatically - 'AUTO_NEXT' */
- return SNMP_ERR_NOERROR;
- /*
- * 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' */
- return SNMP_ERR_NOERROR;
- default:
- snmp_log(LOG_WARNING, "cache_handler: Unrecognised mode (%d)n",
- reqinfo->mode);
- netsnmp_set_all_requests_error(reqinfo, requests,
- SNMP_ERR_GENERR);
- return SNMP_ERR_GENERR;
- }
- netsnmp_set_all_requests_error(reqinfo, requests, SNMP_ERR_GENERR);
- return SNMP_ERR_GENERR; /* should never get here */
- }
- 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) {
- DEBUGMSG(("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;
- }
- if (cache->timestamp)
- atime_setMarker(cache->timestamp);
- else
- cache->timestamp = atime_newMarker();
- DEBUGMSG(("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-releasen"));
- 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 %pn", 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);
- }
- }