cache_handler.c
上传用户:wxp200602
上传日期:2007-10-30
资源大小:4028k
文件大小:20k
源码类别:

SNMP编程

开发平台:

Unix_Linux

  1. #include <net-snmp/net-snmp-config.h>
  2. #if HAVE_STRING_H
  3. #include <string.h>
  4. #else
  5. #include <strings.h>
  6. #endif
  7. #include <net-snmp/net-snmp-includes.h>
  8. #include <net-snmp/agent/net-snmp-agent-includes.h>
  9. #include <net-snmp/agent/cache_handler.h>
  10. #if HAVE_DMALLOC_H
  11. #include <dmalloc.h>
  12. #endif
  13. static netsnmp_cache  *cache_head = NULL;
  14. static int             cache_outstanding_valid = 0;
  15. static int             _cache_load( netsnmp_cache *cache );
  16. #define CACHE_RELEASE_FREQUENCY 60      /* Check for expired caches every 60s */
  17. void            release_cached_resources(unsigned int regNo,
  18.                                          void *clientargs);
  19. /** @defgroup cache_handler cache_handler: Maintains a cache of data for use by lower level handlers.
  20.  *  @ingroup utilities
  21.  *  This helper checks to see whether the data has been loaded "recently"
  22.  *  (according to the timeout for that particular cache) and calls the
  23.  *  registered "load_cache" routine if necessary.
  24.  *  The lower handlers can then work with this local cached data.
  25.  *
  26.  *  A timeout value of -1 will cause netsnmp_cache_check_expired() to
  27.  *  always return true, and thus the cache will be reloaded for every
  28.  *  request.
  29.  *
  30.  *  To minimze resource use by the agent, a periodic callback checks for
  31.  *  expired caches, and will call the free_cache function for any expired
  32.  *  cache.
  33.  *
  34.  *  The load_cache route should return a negative number if the cache
  35.  *  was not successfully loaded. 0 or any positive number indicates successs.
  36.  *
  37.  *
  38.  *  Several flags can be set to affect the operations on the cache.
  39.  *
  40.  *  If NETSNMP_CACHE_DONT_INVALIDATE_ON_SET is set, the free_cache method
  41.  *  will not be called after a set request has processed. It is assumed that
  42.  *  the lower mib handler using the cache has maintained cache consistency.
  43.  *
  44.  *  If NETSNMP_CACHE_DONT_FREE_BEFORE_LOAD is set, the free_cache method
  45.  *  will not be called before the load_cache method is called. It is assumed
  46.  *  that the load_cache routine will properly deal with being called with a
  47.  *  valid cache.
  48.  *
  49.  *  If NETSNMP_CACHE_DONT_FREE_EXPIRED is set, the free_cache method will
  50.  *  not be called with the cache expires. The expired flag will be set, but
  51.  *  the valid flag will not be cleared. It is assumed that the load_cache
  52.  *  routine will properly deal with being called with a valid cache.
  53.  *
  54.  *  If NETSNMP_CACHE_PRELOAD is set when a the cache handler is created,
  55.  *  the cache load routine will be called immediately.
  56.  *
  57.  *  If NETSNMP_CACHE_DONT_AUTO_RELEASE is set, the periodic callback that
  58.  *  checks for expired caches will skip the cache. The cache will only be
  59.  *  checked for expiration when a request triggers the cache handler. This
  60.  *  is useful if the cache has it's own periodic callback to keep the cache
  61.  *  fresh.
  62.  *
  63.  *  If NETSNMP_CACHE_AUTO_RELOAD is set, a timer will be set up to reload
  64.  *  the cache when it expires. This is useful for keeping the cache fresh,
  65.  *  even in the absence of incoming snmp requests.
  66.  *
  67.  *
  68.  *  Here are some suggestions for some common situations.
  69.  *
  70.  *  Cached File:
  71.  *      If your table is based on a file that may periodically change,
  72.  *      you can test the modification date to see if the file has
  73.  *      changed since the last cache load. To get the cache helper to call
  74.  *      the load function for every request, set the timeout to -1, which
  75.  *      will cause the cache to always report that it is expired. This means
  76.  *      that you will want to prevent the agent from flushing the cache when
  77.  *      it has expired, and you will have to flush it manually if you
  78.  *      detect that the file has changed. To accomplish this, set the
  79.  *      following flags:
  80.  *
  81.  *          NETSNMP_CACHE_DONT_FREE_EXPIRED
  82.  *          NETSNMP_CACHE_DONT_AUTO_RELEASE
  83.  *
  84.  *
  85.  *  Constant (periodic) reload:
  86.  *      If you want the cache kept up to date regularly, even if no requests
  87.  *      for the table are received, you can have your cache load routine
  88.  *      called periodically. This is very useful if you need to monitor the
  89.  *      data for changes (eg a <i>LastChanged</i> object). You will need to
  90.  *      prevent the agent from flushing the cache when it expires. Set the
  91.  *      cache timeout to the frequency, in seconds, that you wish to
  92.  *      reload your cache, and set the following flags:
  93.  *
  94.  *          NETSNMP_CACHE_DONT_FREE_EXPIRED
  95.  *          NETSNMP_CACHE_DONT_AUTO_RELEASE
  96.  *          NETSNMP_CACHE_AUTO_RELOAD
  97.  *
  98.  *  @{
  99.  */
  100. /** get cache head
  101.  * @internal
  102.  * unadvertised function to get cache head. You really should not
  103.  * do this, since the internal storage mechanism might change.
  104.  */
  105. netsnmp_cache *
  106. netsnmp_cache_get_head(void)
  107. {
  108.     return cache_head;
  109. }
  110. /** find existing cache
  111.  */
  112. netsnmp_cache *
  113. netsnmp_cache_find_by_oid(oid * rootoid, int rootoid_len)
  114. {
  115.     netsnmp_cache  *cache;
  116.     for (cache = cache_head; cache; cache = cache->next) {
  117.         if (0 == netsnmp_oid_equals(cache->rootoid, cache->rootoid_len,
  118.                                     rootoid, rootoid_len))
  119.             return cache;
  120.     }
  121.     
  122.     return NULL;
  123. }
  124. /** returns a cache
  125.  */
  126. netsnmp_cache *
  127. netsnmp_cache_create(int timeout, NetsnmpCacheLoad * load_hook,
  128.                      NetsnmpCacheFree * free_hook,
  129.                      oid * rootoid, int rootoid_len)
  130. {
  131.     netsnmp_cache  *cache = NULL;
  132.     cache = SNMP_MALLOC_TYPEDEF(netsnmp_cache);
  133.     if (NULL == cache) {
  134.         snmp_log(LOG_ERR,"malloc error in netsnmp_cache_createn");
  135.         return NULL;
  136.     }
  137.     cache->timeout = timeout;
  138.     cache->load_cache = load_hook;
  139.     cache->free_cache = free_hook;
  140.     cache->enabled = 1;
  141.     if(0 == cache->timeout)
  142.         cache->timeout = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
  143.                                             NETSNMP_DS_AGENT_CACHE_TIMEOUT);
  144.     
  145.     /*
  146.      * Add the registered OID information, and tack
  147.      * this onto the list for cache SNMP management
  148.      *
  149.      * Note that this list is not ordered.
  150.      *    table_iterator rules again!
  151.      */
  152.     if (rootoid) {
  153.         cache->rootoid = snmp_duplicate_objid(rootoid, rootoid_len);
  154.         cache->rootoid_len = rootoid_len;
  155.         cache->next = cache_head;
  156.         if (cache_head)
  157.             cache_head->prev = cache;
  158.         cache_head = cache;
  159.     }
  160.     return cache;
  161. }
  162. /** callback function to call cache load function */
  163. static void
  164. _timer_reload(unsigned int regNo, void *clientargs)
  165. {
  166.     netsnmp_cache *cache = (netsnmp_cache *)clientargs;
  167.     DEBUGMSGT(("cache_timer:start", "loading cache %pn", cache));
  168.     cache->expired = 1;
  169.     _cache_load(cache);
  170. }
  171. /** starts the recurring cache_load callback */
  172. unsigned int
  173. netsnmp_cache_timer_start(netsnmp_cache *cache)
  174. {
  175.     if(NULL == cache)
  176.         return 0;
  177.     if(0 != cache->timer_id) {
  178.         snmp_log(LOG_WARNING, "cache has existing timer id.n");
  179.         return cache->timer_id;
  180.     }
  181.     
  182.     if(! (cache->flags & NETSNMP_CACHE_AUTO_RELOAD)) {
  183.         snmp_log(LOG_ERR,
  184.                  "cache_timer_start called but auto_reload not set.n");
  185.         return 0;
  186.     }
  187.     cache->timer_id = snmp_alarm_register(cache->timeout, SA_REPEAT,
  188.                                           _timer_reload, cache);
  189.     if(0 == cache->timer_id) {
  190.         snmp_log(LOG_ERR,"could not register alarmn");
  191.         return 0;
  192.     }
  193.     DEBUGMSGT(("cache_timer:start",
  194.                "starting timer %d for cache %pn", cache->timer_id, cache));
  195.     return cache->timer_id;
  196. }
  197. /** stops the recurring cache_load callback */
  198. void
  199. netsnmp_cache_timer_stop(netsnmp_cache *cache)
  200. {
  201.     if(NULL == cache)
  202.         return;
  203.     if(0 == cache->timer_id) {
  204.         snmp_log(LOG_WARNING, "cache has no timer id.n");
  205.         return;
  206.     }
  207.     DEBUGMSGT(("cache_timer:stop",
  208.                "stopping timer %d for cache %pn", cache->timer_id, cache));
  209.     snmp_alarm_unregister(cache->timer_id);
  210. }
  211. /** returns a cache handler that can be injected into a given handler chain.  
  212.  */
  213. netsnmp_mib_handler *
  214. netsnmp_cache_handler_get(netsnmp_cache* cache)
  215. {
  216.     netsnmp_mib_handler *ret = NULL;
  217.     
  218.     ret = netsnmp_create_handler("cache_handler",
  219.                                  netsnmp_cache_helper_handler);
  220.     if (ret) {
  221.         ret->flags |= MIB_HANDLER_AUTO_NEXT;
  222.         ret->myvoid = (void *) cache;
  223.         
  224.         if(NULL != cache) {
  225.             if ((cache->flags & NETSNMP_CACHE_PRELOAD) && ! cache->valid) {
  226.                 /*
  227.                  * load cache, ignore rc
  228.                  * (failed load doesn't affect registration)
  229.                  */
  230.                 (void)_cache_load(cache);
  231.             }
  232.             if (cache->flags & NETSNMP_CACHE_AUTO_RELOAD)
  233.                 netsnmp_cache_timer_start(cache);
  234.             
  235.         }
  236.     }
  237.     return ret;
  238. }
  239. /** returns a cache handler that can be injected into a given handler chain.  
  240.  */
  241. netsnmp_mib_handler *
  242. netsnmp_get_cache_handler(int timeout, NetsnmpCacheLoad * load_hook,
  243.                           NetsnmpCacheFree * free_hook,
  244.                           oid * rootoid, int rootoid_len)
  245. {
  246.     netsnmp_mib_handler *ret = NULL;
  247.     netsnmp_cache  *cache = NULL;
  248.     ret = netsnmp_cache_handler_get(NULL);
  249.     if (ret) {
  250.         cache = netsnmp_cache_create(timeout, load_hook, free_hook,
  251.                                      rootoid, rootoid_len);
  252.         ret->myvoid = (void *) cache;
  253.     }
  254.     return ret;
  255. }
  256. /** functionally the same as calling netsnmp_register_handler() but also
  257.  * injects a cache handler at the same time for you. */
  258. int
  259. netsnmp_cache_handler_register(netsnmp_handler_registration * reginfo,
  260.                                netsnmp_cache* cache)
  261. {
  262.     netsnmp_mib_handler *handler = NULL;
  263.     handler = netsnmp_cache_handler_get(cache);
  264.     netsnmp_inject_handler(reginfo, handler);
  265.     return netsnmp_register_handler(reginfo);
  266. }
  267. /** functionally the same as calling netsnmp_register_handler() but also
  268.  * injects a cache handler at the same time for you. */
  269. int
  270. netsnmp_register_cache_handler(netsnmp_handler_registration * reginfo,
  271.                                int timeout, NetsnmpCacheLoad * load_hook,
  272.                                NetsnmpCacheFree * free_hook)
  273. {
  274.     netsnmp_mib_handler *handler = NULL;
  275.     handler = netsnmp_get_cache_handler(timeout, load_hook, free_hook,
  276.                                         reginfo->rootoid,
  277.                                         reginfo->rootoid_len);
  278.     netsnmp_inject_handler(reginfo, handler);
  279.     return netsnmp_register_handler(reginfo);
  280. }
  281. NETSNMP_STATIC_INLINE char *
  282. _build_cache_name(const char *name)
  283. {
  284.     char *dup = malloc(strlen(name) + strlen(CACHE_NAME) + 2);
  285.     if (NULL == dup)
  286.         return NULL;
  287.     sprintf(dup, "%s:%s", CACHE_NAME, name);
  288.     return dup;
  289. }
  290. /** Insert the cache information for a given request (PDU) */
  291. void
  292. netsnmp_cache_reqinfo_insert(netsnmp_cache* cache,
  293.                              netsnmp_agent_request_info * reqinfo,
  294.                              const char *name)
  295. {
  296.     char *cache_name = _build_cache_name(name);
  297.     if (NULL == netsnmp_agent_get_list_data(reqinfo, cache_name)) {
  298.         DEBUGMSGTL(("verbose:helper:cache_handler", " adding '%s' to %pn",
  299.                     cache_name, reqinfo));
  300.         netsnmp_agent_add_list_data(reqinfo,
  301.                                     netsnmp_create_data_list(cache_name,
  302.                                                              cache, NULL));
  303.     }
  304.     SNMP_FREE(cache_name);
  305. }
  306. /** Extract the cache information for a given request (PDU) */
  307. netsnmp_cache  *
  308. netsnmp_cache_reqinfo_extract(netsnmp_agent_request_info * reqinfo,
  309.                               const char *name)
  310. {
  311.     netsnmp_cache  *result;
  312.     char *cache_name = _build_cache_name(name);
  313.     result = netsnmp_agent_get_list_data(reqinfo, cache_name);
  314.     SNMP_FREE(cache_name);
  315.     return result;
  316. }
  317. /** Extract the cache information for a given request (PDU) */
  318. netsnmp_cache  *
  319. netsnmp_extract_cache_info(netsnmp_agent_request_info * reqinfo)
  320. {
  321.     return netsnmp_cache_reqinfo_extract(reqinfo, CACHE_NAME);
  322. }
  323. /** Check if the cache timeout has passed. Sets and return the expired flag. */
  324. int
  325. netsnmp_cache_check_expired(netsnmp_cache *cache)
  326. {
  327.     if(NULL == cache)
  328.         return 0;
  329.     
  330.     if(!cache->valid || (NULL == cache->timestamp) || (-1 == cache->timeout))
  331.         cache->expired = 1;
  332.     else
  333.         cache->expired = atime_ready(cache->timestamp, 1000 * cache->timeout);
  334.     
  335.     return cache->expired;
  336. }
  337. /** Reload the cache if required */
  338. int
  339. netsnmp_cache_check_and_reload(netsnmp_cache * cache)
  340. {
  341.     if (!cache) {
  342.         DEBUGMSG(("helper:cache_handler", " no cachen"));
  343.         return 0; /* ?? or -1 */
  344.     }
  345.     if (!cache->valid || netsnmp_cache_check_expired(cache))
  346.         return _cache_load( cache );
  347.     else {
  348.         DEBUGMSG(("helper:cache_handler", " cached (%d)n",
  349.                   cache->timeout));
  350.         return 0;
  351.     }
  352. }
  353. /** Is the cache valid for a given request? */
  354. int
  355. netsnmp_cache_is_valid(netsnmp_agent_request_info * reqinfo, 
  356.                        const char* name)
  357. {
  358.     netsnmp_cache  *cache = netsnmp_cache_reqinfo_extract(reqinfo, name);
  359.     return (cache && cache->valid);
  360. }
  361. /** Is the cache valid for a given request?
  362.  * for backwards compatability. netsnmp_cache_is_valid() is preferred.
  363.  */
  364. int
  365. netsnmp_is_cache_valid(netsnmp_agent_request_info * reqinfo)
  366. {
  367.     return netsnmp_cache_is_valid(reqinfo, CACHE_NAME);
  368. }
  369. /** Implements the cache handler */
  370. int
  371. netsnmp_cache_helper_handler(netsnmp_mib_handler * handler,
  372.                              netsnmp_handler_registration * reginfo,
  373.                              netsnmp_agent_request_info * reqinfo,
  374.                              netsnmp_request_info * requests)
  375. {
  376.     netsnmp_cache  *cache = NULL;
  377.     DEBUGMSGTL(("helper:cache_handler", "Got request (%d) for %s: ",
  378.                 reqinfo->mode, reginfo->handlerName));
  379.     DEBUGMSGOID(("helper:cache_handler", reginfo->rootoid,
  380.                  reginfo->rootoid_len));
  381.     netsnmp_assert(handler->flags & MIB_HANDLER_AUTO_NEXT);
  382.     cache = (netsnmp_cache *) handler->myvoid;
  383.     if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
  384.                                NETSNMP_DS_AGENT_NO_CACHING) ||
  385.         !cache || !cache->enabled || !cache->load_cache) {
  386.         DEBUGMSG(("helper:cache_handler", " caching disabled or "
  387.                   "cache not found, disabled or had no load methodn"));
  388.         return SNMP_ERR_NOERROR;
  389.     }
  390.     switch (reqinfo->mode) {
  391.     case MODE_GET:
  392.     case MODE_GETNEXT:
  393.     case MODE_GETBULK:
  394.     case MODE_SET_RESERVE1: {
  395.         netsnmp_handler_args cache_hint;
  396.         /*
  397.          * only touch cache once per pdu request, to prevent a cache
  398.          * reload while a module is using cached data.
  399.          *
  400.          * XXX: this won't catch a request reloading the cache while
  401.          * a previous (delegated) request is still using the cache.
  402.          * maybe use a reference counter?
  403.          */
  404.         if (netsnmp_cache_is_valid(reqinfo, reginfo->handlerName))
  405.             return SNMP_ERR_NOERROR;
  406.         if (cache->flags & NETSNMP_CACHE_HINT_HANDLER_ARGS) {
  407.             netsnmp_assert(NULL == cache->cache_hint);
  408.             cache_hint.handler = handler;
  409.             cache_hint.reginfo = reginfo;
  410.             cache_hint.reqinfo = reqinfo;
  411.             cache_hint.requests = requests;
  412.             cache->cache_hint = &cache_hint;
  413.         }
  414.         /*
  415.          * call the load hook, and update the cache timestamp.
  416.          * If it's not already there, add to reqinfo
  417.          */
  418.         netsnmp_cache_check_and_reload(cache);
  419.         netsnmp_cache_reqinfo_insert(cache, reqinfo, reginfo->handlerName);
  420.         cache->cache_hint = NULL;
  421.         /** next handler called automatically - 'AUTO_NEXT' */
  422.         }
  423.         return SNMP_ERR_NOERROR;
  424.     case MODE_SET_RESERVE2:
  425.     case MODE_SET_FREE:
  426.     case MODE_SET_ACTION:
  427.     case MODE_SET_UNDO:
  428.         netsnmp_assert(netsnmp_cache_is_valid(reqinfo, reginfo->handlerName));
  429.         /** next handler called automatically - 'AUTO_NEXT' */
  430.         return SNMP_ERR_NOERROR;
  431.         /*
  432.          * A (successful) SET request wouldn't typically trigger a reload of
  433.          *  the cache, but might well invalidate the current contents.
  434.          * Only do this on the last pass through.
  435.          */
  436.     case MODE_SET_COMMIT:
  437.         if (cache->valid && 
  438.             ! (cache->flags & NETSNMP_CACHE_DONT_INVALIDATE_ON_SET) ) {
  439.             cache->free_cache(cache, cache->magic);
  440.             cache->valid = 0;
  441.         }
  442.         /** next handler called automatically - 'AUTO_NEXT' */
  443.         return SNMP_ERR_NOERROR;
  444.     default:
  445.         snmp_log(LOG_WARNING, "cache_handler: Unrecognised mode (%d)n",
  446.                  reqinfo->mode);
  447.         netsnmp_set_all_requests_error(reqinfo, requests,
  448.                                        SNMP_ERR_GENERR);
  449.         return SNMP_ERR_GENERR;
  450.     }
  451.     netsnmp_set_all_requests_error(reqinfo, requests, SNMP_ERR_GENERR);
  452.     return SNMP_ERR_GENERR;     /* should never get here */
  453. }
  454. static void
  455. _cache_free( netsnmp_cache *cache )
  456. {
  457.     if (NULL != cache->free_cache) {
  458.         cache->free_cache(cache, cache->magic);
  459.         cache->valid = 0;
  460.     }
  461. }
  462. static int
  463. _cache_load( netsnmp_cache *cache )
  464. {
  465.     int ret = -1;
  466.     /*
  467.      * If we've got a valid cache, then release it before reloading
  468.      */
  469.     if (cache->valid &&
  470.         (! (cache->flags & NETSNMP_CACHE_DONT_FREE_BEFORE_LOAD)))
  471.         _cache_free(cache);
  472.     if ( cache->load_cache)
  473.         ret = cache->load_cache(cache, cache->magic);
  474.     if (ret < 0) {
  475.         DEBUGMSG(("helper:cache_handler", " load failed (%d)n",
  476.                   ret));
  477.         cache->valid = 0;
  478.         return ret;
  479.     }
  480.     cache->valid = 1;
  481.     cache->expired = 0;
  482.     /*
  483.      * If we didn't previously have any valid caches outstanding,
  484.      *   then schedule a pass of the auto-release routine.
  485.      */
  486.     if ((!cache_outstanding_valid) &&
  487.         (! (cache->flags & NETSNMP_CACHE_DONT_FREE_EXPIRED))) {
  488.         snmp_alarm_register(CACHE_RELEASE_FREQUENCY,
  489.                             0, release_cached_resources, NULL);
  490.         cache_outstanding_valid = 1;
  491.     }
  492.     if (cache->timestamp)
  493.         atime_setMarker(cache->timestamp);
  494.     else
  495.         cache->timestamp = atime_newMarker();
  496.     DEBUGMSG(("helper:cache_handler", " loaded (%d)n",
  497.               cache->timeout));
  498.     return ret;
  499. }
  500. /** run regularly to automatically release cached resources.
  501.  * xxx - method to prevent cache from expiring while a request
  502.  *     is being processed (e.g. delegated request). proposal:
  503.  *     set a flag, which would be cleared when request finished
  504.  *     (which could be acomplished by a dummy data list item in
  505.  *     agent req info & custom free function).
  506.  */
  507. void
  508. release_cached_resources(unsigned int regNo, void *clientargs)
  509. {
  510.     netsnmp_cache  *cache = NULL;
  511.     cache_outstanding_valid = 0;
  512.     DEBUGMSGTL(("helper:cache_handler", "running auto-releasen"));
  513.     for (cache = cache_head; cache; cache = cache->next) {
  514.         DEBUGMSGTL(("helper:cache_handler"," checking %p (flags 0x%x)n",
  515.                      cache, cache->flags));
  516.         if (cache->valid &&
  517.             ! (cache->flags & NETSNMP_CACHE_DONT_AUTO_RELEASE)) {
  518.             DEBUGMSGTL(("helper:cache_handler","  releasing %pn", cache));
  519.             /*
  520.              * Check to see if this cache has timed out.
  521.              * If so, release the cached resources.
  522.              * Otherwise, note that we still have at
  523.              *   least one active cache.
  524.              */
  525.             if (netsnmp_cache_check_expired(cache)) {
  526.                 if(! (cache->flags & NETSNMP_CACHE_DONT_FREE_EXPIRED))
  527.                     _cache_free(cache);
  528.             } else {
  529.                 cache_outstanding_valid = 1;
  530.             }
  531.         }
  532.     }
  533.     /*
  534.      * If there are any caches still valid & active,
  535.      *   then schedule another pass.
  536.      */
  537.     if (cache_outstanding_valid) {
  538.         snmp_alarm_register(CACHE_RELEASE_FREQUENCY,
  539.                             0, release_cached_resources, NULL);
  540.     }
  541. }