00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <net-snmp/net-snmp-config.h>
00012
00013 #include <net-snmp/net-snmp-includes.h>
00014 #include <net-snmp/agent/net-snmp-agent-includes.h>
00015
00016 #include <net-snmp/agent/cache_handler.h>
00017
00018 #if HAVE_STRING_H
00019 #include <string.h>
00020 #else
00021 #include <strings.h>
00022 #endif
00023
00024 static netsnmp_cache *cache_head = NULL;
00025 static int cache_outstanding_valid = 0;
00026 static int _cache_load( netsnmp_cache *cache );
00027
00028 #define CACHE_RELEASE_FREQUENCY 60
00029
00030 void release_cached_resources(unsigned int regNo,
00031 void *clientargs);
00032
00121 netsnmp_cache *
00122 netsnmp_cache_get_head(void)
00123 {
00124 return cache_head;
00125 }
00126
00129 netsnmp_cache *
00130 netsnmp_cache_find_by_oid(const oid * rootoid, int rootoid_len)
00131 {
00132 netsnmp_cache *cache;
00133
00134 for (cache = cache_head; cache; cache = cache->next) {
00135 if (0 == netsnmp_oid_equals(cache->rootoid, cache->rootoid_len,
00136 rootoid, rootoid_len))
00137 return cache;
00138 }
00139
00140 return NULL;
00141 }
00142
00145 netsnmp_cache *
00146 netsnmp_cache_create(int timeout, NetsnmpCacheLoad * load_hook,
00147 NetsnmpCacheFree * free_hook,
00148 const oid * rootoid, int rootoid_len)
00149 {
00150 netsnmp_cache *cache = NULL;
00151
00152 cache = SNMP_MALLOC_TYPEDEF(netsnmp_cache);
00153 if (NULL == cache) {
00154 snmp_log(LOG_ERR,"malloc error in netsnmp_cache_create\n");
00155 return NULL;
00156 }
00157 cache->timeout = timeout;
00158 cache->load_cache = load_hook;
00159 cache->free_cache = free_hook;
00160 cache->enabled = 1;
00161
00162 if(0 == cache->timeout)
00163 cache->timeout = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
00164 NETSNMP_DS_AGENT_CACHE_TIMEOUT);
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174 if (rootoid) {
00175 cache->rootoid = snmp_duplicate_objid(rootoid, rootoid_len);
00176 cache->rootoid_len = rootoid_len;
00177 cache->next = cache_head;
00178 if (cache_head)
00179 cache_head->prev = cache;
00180 cache_head = cache;
00181 }
00182
00183 return cache;
00184 }
00185
00188 int
00189 netsnmp_cache_free(netsnmp_cache *cache)
00190 {
00191 netsnmp_cache *pos;
00192
00193 if (NULL == cache)
00194 return SNMPERR_SUCCESS;
00195
00196 for (pos = cache_head; pos; pos = pos->next) {
00197 if (pos == cache) {
00198 snmp_log(LOG_WARNING, "not freeing cache (still in list)\n");
00199 return SNMP_ERR_GENERR;
00200 }
00201 }
00202
00203 if (cache->rootoid)
00204 free(cache->rootoid);
00205
00206 free(cache);
00207
00208 return SNMPERR_SUCCESS;
00209 }
00210
00212 static void
00213 _timer_reload(unsigned int regNo, void *clientargs)
00214 {
00215 netsnmp_cache *cache = (netsnmp_cache *)clientargs;
00216
00217 DEBUGMSGT(("cache_timer:start", "loading cache %p\n", cache));
00218
00219 cache->expired = 1;
00220
00221 _cache_load(cache);
00222 }
00223
00225 unsigned int
00226 netsnmp_cache_timer_start(netsnmp_cache *cache)
00227 {
00228 if(NULL == cache)
00229 return 0;
00230
00231 DEBUGMSGTL(( "cache_timer:start", "OID: "));
00232 DEBUGMSGOID(("cache_timer:start", cache->rootoid, cache->rootoid_len));
00233 DEBUGMSG(( "cache_timer:start", "\n"));
00234
00235 if(0 != cache->timer_id) {
00236 snmp_log(LOG_WARNING, "cache has existing timer id.\n");
00237 return cache->timer_id;
00238 }
00239
00240 if(! (cache->flags & NETSNMP_CACHE_AUTO_RELOAD)) {
00241 snmp_log(LOG_ERR,
00242 "cache_timer_start called but auto_reload not set.\n");
00243 return 0;
00244 }
00245
00246 cache->timer_id = snmp_alarm_register(cache->timeout, SA_REPEAT,
00247 _timer_reload, cache);
00248 if(0 == cache->timer_id) {
00249 snmp_log(LOG_ERR,"could not register alarm\n");
00250 return 0;
00251 }
00252
00253 cache->flags &= ~NETSNMP_CACHE_AUTO_RELOAD;
00254 DEBUGMSGT(("cache_timer:start",
00255 "starting timer %lu for cache %p\n", cache->timer_id, cache));
00256 return cache->timer_id;
00257 }
00258
00260 void
00261 netsnmp_cache_timer_stop(netsnmp_cache *cache)
00262 {
00263 if(NULL == cache)
00264 return;
00265
00266 if(0 == cache->timer_id) {
00267 snmp_log(LOG_WARNING, "cache has no timer id.\n");
00268 return;
00269 }
00270
00271 DEBUGMSGT(("cache_timer:stop",
00272 "stopping timer %lu for cache %p\n", cache->timer_id, cache));
00273
00274 snmp_alarm_unregister(cache->timer_id);
00275 cache->flags |= NETSNMP_CACHE_AUTO_RELOAD;
00276 }
00277
00278
00281 netsnmp_mib_handler *
00282 netsnmp_cache_handler_get(netsnmp_cache* cache)
00283 {
00284 netsnmp_mib_handler *ret = NULL;
00285
00286 ret = netsnmp_create_handler("cache_handler",
00287 netsnmp_cache_helper_handler);
00288 if (ret) {
00289 ret->flags |= MIB_HANDLER_AUTO_NEXT;
00290 ret->myvoid = (void *) cache;
00291
00292 if(NULL != cache) {
00293 if ((cache->flags & NETSNMP_CACHE_PRELOAD) && ! cache->valid) {
00294
00295
00296
00297
00298 (void)_cache_load(cache);
00299 }
00300 if (cache->flags & NETSNMP_CACHE_AUTO_RELOAD)
00301 netsnmp_cache_timer_start(cache);
00302
00303 }
00304 }
00305 return ret;
00306 }
00307
00310 netsnmp_mib_handler *
00311 netsnmp_get_cache_handler(int timeout, NetsnmpCacheLoad * load_hook,
00312 NetsnmpCacheFree * free_hook,
00313 const oid * rootoid, int rootoid_len)
00314 {
00315 netsnmp_mib_handler *ret = NULL;
00316 netsnmp_cache *cache = NULL;
00317
00318 ret = netsnmp_cache_handler_get(NULL);
00319 if (ret) {
00320 cache = netsnmp_cache_create(timeout, load_hook, free_hook,
00321 rootoid, rootoid_len);
00322 ret->myvoid = (void *) cache;
00323 }
00324 return ret;
00325 }
00326
00329 int
00330 netsnmp_cache_handler_register(netsnmp_handler_registration * reginfo,
00331 netsnmp_cache* cache)
00332 {
00333 netsnmp_mib_handler *handler = NULL;
00334 handler = netsnmp_cache_handler_get(cache);
00335
00336 netsnmp_inject_handler(reginfo, handler);
00337 return netsnmp_register_handler(reginfo);
00338 }
00339
00342 int
00343 netsnmp_register_cache_handler(netsnmp_handler_registration * reginfo,
00344 int timeout, NetsnmpCacheLoad * load_hook,
00345 NetsnmpCacheFree * free_hook)
00346 {
00347 netsnmp_mib_handler *handler = NULL;
00348 handler = netsnmp_get_cache_handler(timeout, load_hook, free_hook,
00349 reginfo->rootoid,
00350 reginfo->rootoid_len);
00351
00352 netsnmp_inject_handler(reginfo, handler);
00353 return netsnmp_register_handler(reginfo);
00354 }
00355
00356 NETSNMP_STATIC_INLINE char *
00357 _build_cache_name(const char *name)
00358 {
00359 char *dup = malloc(strlen(name) + strlen(CACHE_NAME) + 2);
00360 if (NULL == dup)
00361 return NULL;
00362 sprintf(dup, "%s:%s", CACHE_NAME, name);
00363 return dup;
00364 }
00365
00367 void
00368 netsnmp_cache_reqinfo_insert(netsnmp_cache* cache,
00369 netsnmp_agent_request_info * reqinfo,
00370 const char *name)
00371 {
00372 char *cache_name = _build_cache_name(name);
00373 if (NULL == netsnmp_agent_get_list_data(reqinfo, cache_name)) {
00374 DEBUGMSGTL(("verbose:helper:cache_handler", " adding '%s' to %p\n",
00375 cache_name, reqinfo));
00376 netsnmp_agent_add_list_data(reqinfo,
00377 netsnmp_create_data_list(cache_name,
00378 cache, NULL));
00379 }
00380 SNMP_FREE(cache_name);
00381 }
00382
00384 netsnmp_cache *
00385 netsnmp_cache_reqinfo_extract(netsnmp_agent_request_info * reqinfo,
00386 const char *name)
00387 {
00388 netsnmp_cache *result;
00389 char *cache_name = _build_cache_name(name);
00390 result = netsnmp_agent_get_list_data(reqinfo, cache_name);
00391 SNMP_FREE(cache_name);
00392 return result;
00393 }
00394
00396 netsnmp_cache *
00397 netsnmp_extract_cache_info(netsnmp_agent_request_info * reqinfo)
00398 {
00399 return netsnmp_cache_reqinfo_extract(reqinfo, CACHE_NAME);
00400 }
00401
00402
00404 int
00405 netsnmp_cache_check_expired(netsnmp_cache *cache)
00406 {
00407 if(NULL == cache)
00408 return 0;
00409
00410 if(!cache->valid || (NULL == cache->timestamp) || (-1 == cache->timeout))
00411 cache->expired = 1;
00412 else
00413 cache->expired = atime_ready(cache->timestamp, 1000 * cache->timeout);
00414
00415 return cache->expired;
00416 }
00417
00419 int
00420 netsnmp_cache_check_and_reload(netsnmp_cache * cache)
00421 {
00422 if (!cache) {
00423 DEBUGMSGT(("helper:cache_handler", " no cache\n"));
00424 return 0;
00425 }
00426 if (!cache->valid || netsnmp_cache_check_expired(cache))
00427 return _cache_load( cache );
00428 else {
00429 DEBUGMSGT(("helper:cache_handler", " cached (%d)\n",
00430 cache->timeout));
00431 return 0;
00432 }
00433 }
00434
00436 int
00437 netsnmp_cache_is_valid(netsnmp_agent_request_info * reqinfo,
00438 const char* name)
00439 {
00440 netsnmp_cache *cache = netsnmp_cache_reqinfo_extract(reqinfo, name);
00441 return (cache && cache->valid);
00442 }
00443
00447 int
00448 netsnmp_is_cache_valid(netsnmp_agent_request_info * reqinfo)
00449 {
00450 return netsnmp_cache_is_valid(reqinfo, CACHE_NAME);
00451 }
00452
00454 int
00455 netsnmp_cache_helper_handler(netsnmp_mib_handler * handler,
00456 netsnmp_handler_registration * reginfo,
00457 netsnmp_agent_request_info * reqinfo,
00458 netsnmp_request_info * requests)
00459 {
00460 netsnmp_cache *cache = NULL;
00461 netsnmp_handler_args cache_hint;
00462
00463 DEBUGMSGTL(("helper:cache_handler", "Got request (%d) for %s: ",
00464 reqinfo->mode, reginfo->handlerName));
00465 DEBUGMSGOID(("helper:cache_handler", reginfo->rootoid,
00466 reginfo->rootoid_len));
00467 DEBUGMSG(("helper:cache_handler", "\n"));
00468
00469 netsnmp_assert(handler->flags & MIB_HANDLER_AUTO_NEXT);
00470
00471 cache = (netsnmp_cache *) handler->myvoid;
00472 if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
00473 NETSNMP_DS_AGENT_NO_CACHING) ||
00474 !cache || !cache->enabled || !cache->load_cache) {
00475 DEBUGMSGT(("helper:cache_handler", " caching disabled or "
00476 "cache not found, disabled or had no load method\n"));
00477 return SNMP_ERR_NOERROR;
00478 }
00479
00480
00481
00482
00483
00484 cache_hint.handler = handler;
00485 cache_hint.reginfo = reginfo;
00486 cache_hint.reqinfo = reqinfo;
00487 cache_hint.requests = requests;
00488 cache->cache_hint = &cache_hint;
00489
00490 switch (reqinfo->mode) {
00491
00492 case MODE_GET:
00493 case MODE_GETNEXT:
00494 case MODE_GETBULK:
00495 case MODE_SET_RESERVE1:
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505 if (netsnmp_cache_is_valid(reqinfo, reginfo->handlerName))
00506 break;
00507
00508
00509
00510
00511
00512 netsnmp_cache_check_and_reload(cache);
00513 netsnmp_cache_reqinfo_insert(cache, reqinfo, reginfo->handlerName);
00515 break;
00516
00517 case MODE_SET_RESERVE2:
00518 case MODE_SET_FREE:
00519 case MODE_SET_ACTION:
00520 case MODE_SET_UNDO:
00521 netsnmp_assert(netsnmp_cache_is_valid(reqinfo, reginfo->handlerName));
00523 break;
00524
00525
00526
00527
00528
00529
00530 case MODE_SET_COMMIT:
00531 if (cache->valid &&
00532 ! (cache->flags & NETSNMP_CACHE_DONT_INVALIDATE_ON_SET) ) {
00533 cache->free_cache(cache, cache->magic);
00534 cache->valid = 0;
00535 }
00537 break;
00538
00539 default:
00540 snmp_log(LOG_WARNING, "cache_handler: Unrecognised mode (%d)\n",
00541 reqinfo->mode);
00542 netsnmp_request_set_error_all(requests, SNMP_ERR_GENERR);
00543 return SNMP_ERR_GENERR;
00544 }
00545 return SNMP_ERR_NOERROR;
00546 }
00547
00548 static void
00549 _cache_free( netsnmp_cache *cache )
00550 {
00551 if (NULL != cache->free_cache) {
00552 cache->free_cache(cache, cache->magic);
00553 cache->valid = 0;
00554 }
00555 }
00556
00557 static int
00558 _cache_load( netsnmp_cache *cache )
00559 {
00560 int ret = -1;
00561
00562
00563
00564
00565 if (cache->valid &&
00566 (! (cache->flags & NETSNMP_CACHE_DONT_FREE_BEFORE_LOAD)))
00567 _cache_free(cache);
00568
00569 if ( cache->load_cache)
00570 ret = cache->load_cache(cache, cache->magic);
00571 if (ret < 0) {
00572 DEBUGMSGT(("helper:cache_handler", " load failed (%d)\n", ret));
00573 cache->valid = 0;
00574 return ret;
00575 }
00576 cache->valid = 1;
00577 cache->expired = 0;
00578
00579
00580
00581
00582
00583 if ((!cache_outstanding_valid) &&
00584 (! (cache->flags & NETSNMP_CACHE_DONT_FREE_EXPIRED))) {
00585 snmp_alarm_register(CACHE_RELEASE_FREQUENCY,
00586 0, release_cached_resources, NULL);
00587 cache_outstanding_valid = 1;
00588 }
00589 if (cache->timestamp)
00590 atime_setMarker(cache->timestamp);
00591 else
00592 cache->timestamp = atime_newMarker();
00593 DEBUGMSGT(("helper:cache_handler", " loaded (%d)\n", cache->timeout));
00594
00595 return ret;
00596 }
00597
00598
00599
00607 void
00608 release_cached_resources(unsigned int regNo, void *clientargs)
00609 {
00610 netsnmp_cache *cache = NULL;
00611
00612 cache_outstanding_valid = 0;
00613 DEBUGMSGTL(("helper:cache_handler", "running auto-release\n"));
00614 for (cache = cache_head; cache; cache = cache->next) {
00615 DEBUGMSGTL(("helper:cache_handler"," checking %p (flags 0x%x)\n",
00616 cache, cache->flags));
00617 if (cache->valid &&
00618 ! (cache->flags & NETSNMP_CACHE_DONT_AUTO_RELEASE)) {
00619 DEBUGMSGTL(("helper:cache_handler"," releasing %p\n", cache));
00620
00621
00622
00623
00624
00625
00626 if (netsnmp_cache_check_expired(cache)) {
00627 if(! (cache->flags & NETSNMP_CACHE_DONT_FREE_EXPIRED))
00628 _cache_free(cache);
00629 } else {
00630 cache_outstanding_valid = 1;
00631 }
00632 }
00633 }
00634
00635
00636
00637
00638 if (cache_outstanding_valid) {
00639 snmp_alarm_register(CACHE_RELEASE_FREQUENCY,
00640 0, release_cached_resources, NULL);
00641 }
00642 }