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 
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 }

net-snmpに対してSat Sep 5 13:14:20 2009に生成されました。  doxygen 1.4.7