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
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
00159
00160
00161
00162
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
00261
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;
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
00447
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
00464
00465
00466
00467
00468
00469
00470 if (netsnmp_cache_is_valid(reqinfo, reginfo->handlerName))
00471 return SNMP_ERR_NOERROR;
00472
00473
00474
00475
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
00493
00494
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;
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
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
00548
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
00589
00590
00591
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
00603
00604
00605 if (cache_outstanding_valid) {
00606 snmp_alarm_register(CACHE_RELEASE_FREQUENCY,
00607 0, release_cached_resources, NULL);
00608 }
00609 }