Logo
Home page Net-SNMP

Archive Search:

Require all words?

Site Search:
Google
Main Page | Modules | Data Structures | File List | Data Fields | Related Pages | Examples

cache_handler.c

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

Generated on Fri Dec 30 13:47:44 2005 for net-snmp by  doxygen 1.3.9.1

Valid CSS!


Last modified: Thursday, 01-Mar-2007 16:20:09 PST
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.