1 /* $NetBSD: dict_cache.c,v 1.1.1.2 2013/01/02 18:59:12 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* dict_cache 3 6 /* SUMMARY 7 /* External cache manager 8 /* SYNOPSIS 9 /* #include <dict_cache.h> 10 /* 11 /* DICT_CACHE *dict_cache_open(dbname, open_flags, dict_flags) 12 /* const char *dbname; 13 /* int open_flags; 14 /* int dict_flags; 15 /* 16 /* void dict_cache_close(cache) 17 /* DICT_CACHE *cache; 18 /* 19 /* const char *dict_cache_lookup(cache, cache_key) 20 /* DICT_CACHE *cache; 21 /* const char *cache_key; 22 /* 23 /* int dict_cache_update(cache, cache_key, cache_val) 24 /* DICT_CACHE *cache; 25 /* const char *cache_key; 26 /* const char *cache_val; 27 /* 28 /* int dict_cache_delete(cache, cache_key) 29 /* DICT_CACHE *cache; 30 /* const char *cache_key; 31 /* 32 /* int dict_cache_sequence(cache, first_next, cache_key, cache_val) 33 /* DICT_CACHE *cache; 34 /* int first_next; 35 /* const char **cache_key; 36 /* const char **cache_val; 37 /* AUXILIARY FUNCTIONS 38 /* void dict_cache_control(cache, name, value, ...) 39 /* DICT_CACHE *cache; 40 /* int name; 41 /* 42 /* typedef int (*DICT_CACHE_VALIDATOR_FN) (const char *cache_key, 43 /* const char *cache_val, char *context); 44 /* 45 /* const char *dict_cache_name(cache) 46 /* DICT_CACHE *cache; 47 /* DESCRIPTION 48 /* This module maintains external cache files with support 49 /* for expiration. The underlying table must implement the 50 /* "lookup", "update", "delete" and "sequence" operations. 51 /* 52 /* Although this API is similar to the one documented in 53 /* dict_open(3), there are subtle differences in the interaction 54 /* between the iterators that access all cache elements, and 55 /* other operations that access individual cache elements. 56 /* 57 /* In particular, when a "sequence" or "cleanup" operation is 58 /* in progress the cache intercepts requests to delete the 59 /* "current" entry, as this would cause some databases to 60 /* mis-behave. Instead, the cache implements a "delete behind" 61 /* strategy, and deletes such an entry after the "sequence" 62 /* or "cleanup" operation moves on to the next cache element. 63 /* The "delete behind" strategy also affects the cache lookup 64 /* and update operations as detailed below. 65 /* 66 /* dict_cache_open() is a wrapper around the dict_open() 67 /* function. It opens the specified cache and returns a handle 68 /* that must be used for subsequent access. This function does 69 /* not return in case of error. 70 /* 71 /* dict_cache_close() closes the specified cache and releases 72 /* memory that was allocated by dict_cache_open(), and terminates 73 /* any thread that was started with dict_cache_control(). 74 /* 75 /* dict_cache_lookup() looks up the specified cache entry. 76 /* The result value is a null pointer when the cache entry was 77 /* not found, or when the entry is scheduled for "delete 78 /* behind". 79 /* 80 /* dict_cache_update() updates the specified cache entry. If 81 /* the entry is scheduled for "delete behind", the delete 82 /* operation is canceled (because of this, the cache must be 83 /* opened with DICT_FLAG_DUP_REPLACE). This function does not 84 /* return in case of error. 85 /* 86 /* dict_cache_delete() removes the specified cache entry. If 87 /* this is the "current" entry of a "sequence" operation, the 88 /* entry is scheduled for "delete behind". The result value 89 /* is zero when the entry was found. 90 /* 91 /* dict_cache_sequence() iterates over the specified cache and 92 /* returns each entry in an implementation-defined order. The 93 /* result value is zero when a cache entry was found. 94 /* 95 /* Important: programs must not use both dict_cache_sequence() 96 /* and the built-in cache cleanup feature. 97 /* 98 /* dict_cache_control() provides control over the built-in 99 /* cache cleanup feature and logging. The arguments are a list 100 /* of (name, value) pairs, terminated with DICT_CACHE_CTL_END. 101 /* The following lists the names and the types of the corresponding 102 /* value arguments. 103 /* .IP "DICT_CACHE_FLAGS (int flags)" 104 /* The arguments to this command are the bit-wise OR of zero 105 /* or more of the following: 106 /* .RS 107 /* .IP DICT_CACHE_FLAG_VERBOSE 108 /* Enable verbose logging of cache activity. 109 /* .IP DICT_CACHE_FLAG_EXP_SUMMARY 110 /* Log cache statistics after each cache cleanup run. 111 /* .RE 112 /* .IP "DICT_CACHE_CTL_INTERVAL (int interval)" 113 /* The interval between cache cleanup runs. Specify a null 114 /* validator or interval to stop cache cleanup. 115 /* .IP "DICT_CACHE_CTL_VALIDATOR (DICT_CACHE_VALIDATOR_FN validator)" 116 /* An application call-back routine that returns non-zero when 117 /* a cache entry should be kept. The call-back function should 118 /* not make changes to the cache. Specify a null validator or 119 /* interval to stop cache cleanup. 120 /* .IP "DICT_CACHE_CTL_CONTEXT (char *context)" 121 /* Application context that is passed to the validator function. 122 /* .RE 123 /* .PP 124 /* dict_cache_name() returns the name of the specified cache. 125 /* 126 /* Arguments: 127 /* .IP "dbname, open_flags, dict_flags" 128 /* These are passed unchanged to dict_open(). The cache must 129 /* be opened with DICT_FLAG_DUP_REPLACE. 130 /* .IP cache 131 /* Cache handle created with dict_cache_open(). 132 /* .IP cache_key 133 /* Cache lookup key. 134 /* .IP cache_val 135 /* Information that is stored under a cache lookup key. 136 /* .IP first_next 137 /* One of DICT_SEQ_FUN_FIRST (first cache element) or 138 /* DICT_SEQ_FUN_NEXT (next cache element). 139 /* .sp 140 /* Note: there is no "stop" request. To ensure that the "delete 141 /* behind" strategy does not interfere with database access, 142 /* allow dict_cache_sequence() to run to completion. 143 /* .IP table 144 /* A bare dictonary handle. 145 /* DIAGNOSTICS 146 /* When a request is satisfied, the lookup routine returns 147 /* non-null, and the update, delete and sequence routines 148 /* return zero. The cache->error value is zero when a request 149 /* could not be satisfied because an item did not exist (delete, 150 /* sequence) or if it could not be updated. The cache->error 151 /* value is non-zero only when a request could not be satisfied, 152 /* and the cause was a database error. 153 /* 154 /* Cache access errors are logged with a warning message. To 155 /* avoid spamming the log, each type of operation logs no more 156 /* than one cache access error per second, per cache. Specify 157 /* the DICT_CACHE_FLAG_VERBOSE flag (see above) to log all 158 /* warnings. 159 /* BUGS 160 /* There should be a way to suspend automatic program suicide 161 /* until a cache cleanup run is completed. Some entries may 162 /* never be removed when the process max_idle time is less 163 /* than the time needed to make a full pass over the cache. 164 /* LICENSE 165 /* .ad 166 /* .fi 167 /* The Secure Mailer license must be distributed with this software. 168 /* HISTORY 169 /* .ad 170 /* .fi 171 /* A predecessor of this code was written first for the Postfix 172 /* tlsmgr(8) daemon. 173 /* AUTHOR(S) 174 /* Wietse Venema 175 /* IBM T.J. Watson Research 176 /* P.O. Box 704 177 /* Yorktown Heights, NY 10598, USA 178 /*--*/ 179 180 /* System library. */ 181 182 #include <sys_defs.h> 183 #include <string.h> 184 #include <stdlib.h> 185 186 /* Utility library. */ 187 188 #include <msg.h> 189 #include <dict.h> 190 #include <mymalloc.h> 191 #include <events.h> 192 #include <dict_cache.h> 193 194 /* Application-specific. */ 195 196 /* 197 * XXX Deleting entries while enumerating a map can he tricky. Some map 198 * types have a concept of cursor and support a "delete the current element" 199 * operation. Some map types without cursors don't behave well when the 200 * current first/next entry is deleted (example: with Berkeley DB < 2, the 201 * "next" operation produces garbage). To avoid trouble, we delete an entry 202 * after advancing the current first/next position beyond it; we use the 203 * same strategy with application requests to delete the current entry. 204 */ 205 206 /* 207 * Opaque data structure. Use dict_cache_name() to access the name of the 208 * underlying database. 209 */ 210 struct DICT_CACHE { 211 char *name; /* full name including proxy: */ 212 int cache_flags; /* see below */ 213 int user_flags; /* logging */ 214 DICT *db; /* database handle */ 215 int error; /* last operation only */ 216 217 /* Delete-behind support. */ 218 char *saved_curr_key; /* "current" cache lookup key */ 219 char *saved_curr_val; /* "current" cache lookup result */ 220 221 /* Cleanup support. */ 222 int exp_interval; /* time between cleanup runs */ 223 DICT_CACHE_VALIDATOR_FN exp_validator; /* expiration call-back */ 224 char *exp_context; /* call-back context */ 225 int retained; /* entries retained in cleanup run */ 226 int dropped; /* entries removed in cleanup run */ 227 228 /* Rate-limited logging support. */ 229 int log_delay; 230 time_t upd_log_stamp; /* last update warning */ 231 time_t get_log_stamp; /* last lookup warning */ 232 time_t del_log_stamp; /* last delete warning */ 233 time_t seq_log_stamp; /* last sequence warning */ 234 }; 235 236 #define DC_FLAG_DEL_SAVED_CURRENT_KEY (1<<0) /* delete-behind is scheduled */ 237 238 /* 239 * Don't log cache access errors more than once per second. 240 */ 241 #define DC_DEF_LOG_DELAY 1 242 243 /* 244 * Macros to make obscure code more readable. 245 */ 246 #define DC_SCHEDULE_FOR_DELETE_BEHIND(cp) \ 247 ((cp)->cache_flags |= DC_FLAG_DEL_SAVED_CURRENT_KEY) 248 249 #define DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key) \ 250 ((cp)->saved_curr_key && strcmp((cp)->saved_curr_key, (cache_key)) == 0) 251 252 #define DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp) \ 253 (/* NOT: (cp)->saved_curr_key && */ \ 254 ((cp)->cache_flags & DC_FLAG_DEL_SAVED_CURRENT_KEY) != 0) 255 256 #define DC_CANCEL_DELETE_BEHIND(cp) \ 257 ((cp)->cache_flags &= ~DC_FLAG_DEL_SAVED_CURRENT_KEY) 258 259 /* 260 * Special key to store the time of the last cache cleanup run completion. 261 */ 262 #define DC_LAST_CACHE_CLEANUP_COMPLETED "_LAST_CACHE_CLEANUP_COMPLETED_" 263 264 /* dict_cache_lookup - load entry from cache */ 265 266 const char *dict_cache_lookup(DICT_CACHE *cp, const char *cache_key) 267 { 268 const char *myname = "dict_cache_lookup"; 269 const char *cache_val; 270 DICT *db = cp->db; 271 272 /* 273 * Search for the cache entry. Don't return an entry that is scheduled 274 * for delete-behind. 275 */ 276 if (DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp) 277 && DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key)) { 278 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 279 msg_info("%s: key=%s (pretend not found - scheduled for deletion)", 280 myname, cache_key); 281 DICT_ERR_VAL_RETURN(cp, DICT_ERR_NONE, (char *) 0); 282 } else { 283 cache_val = dict_get(db, cache_key); 284 if (cache_val == 0 && db->error != 0) 285 msg_rate_delay(&cp->get_log_stamp, cp->log_delay, msg_warn, 286 "%s: cache lookup for '%s' failed due to error", 287 cp->name, cache_key); 288 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 289 msg_info("%s: key=%s value=%s", myname, cache_key, 290 cache_val ? cache_val : db->error ? 291 "error" : "(not found)"); 292 DICT_ERR_VAL_RETURN(cp, db->error, cache_val); 293 } 294 } 295 296 /* dict_cache_update - save entry to cache */ 297 298 int dict_cache_update(DICT_CACHE *cp, const char *cache_key, 299 const char *cache_val) 300 { 301 const char *myname = "dict_cache_update"; 302 DICT *db = cp->db; 303 int put_res; 304 305 /* 306 * Store the cache entry and cancel the delete-behind operation. 307 */ 308 if (DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp) 309 && DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key)) { 310 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 311 msg_info("%s: cancel delete-behind for key=%s", myname, cache_key); 312 DC_CANCEL_DELETE_BEHIND(cp); 313 } 314 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 315 msg_info("%s: key=%s value=%s", myname, cache_key, cache_val); 316 put_res = dict_put(db, cache_key, cache_val); 317 if (put_res != 0) 318 msg_rate_delay(&cp->upd_log_stamp, cp->log_delay, msg_warn, 319 "%s: could not update entry for %s", cp->name, cache_key); 320 DICT_ERR_VAL_RETURN(cp, db->error, put_res); 321 } 322 323 /* dict_cache_delete - delete entry from cache */ 324 325 int dict_cache_delete(DICT_CACHE *cp, const char *cache_key) 326 { 327 const char *myname = "dict_cache_delete"; 328 int del_res; 329 DICT *db = cp->db; 330 331 /* 332 * Delete the entry, unless we would delete the current first/next entry. 333 * In that case, schedule the "current" entry for delete-behind to avoid 334 * mis-behavior by some databases. 335 */ 336 if (DC_MATCH_SAVED_CURRENT_KEY(cp, cache_key)) { 337 DC_SCHEDULE_FOR_DELETE_BEHIND(cp); 338 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 339 msg_info("%s: key=%s (current entry - schedule for delete-behind)", 340 myname, cache_key); 341 DICT_ERR_VAL_RETURN(cp, DICT_ERR_NONE, DICT_STAT_SUCCESS); 342 } else { 343 del_res = dict_del(db, cache_key); 344 if (del_res != 0) 345 msg_rate_delay(&cp->del_log_stamp, cp->log_delay, msg_warn, 346 "%s: could not delete entry for %s", cp->name, cache_key); 347 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 348 msg_info("%s: key=%s (%s)", myname, cache_key, 349 del_res == 0 ? "found" : 350 db->error ? "error" : "not found"); 351 DICT_ERR_VAL_RETURN(cp, db->error, del_res); 352 } 353 } 354 355 /* dict_cache_sequence - look up the first/next cache entry */ 356 357 int dict_cache_sequence(DICT_CACHE *cp, int first_next, 358 const char **cache_key, 359 const char **cache_val) 360 { 361 const char *myname = "dict_cache_sequence"; 362 int seq_res; 363 const char *raw_cache_key; 364 const char *raw_cache_val; 365 char *previous_curr_key; 366 char *previous_curr_val; 367 DICT *db = cp->db; 368 369 /* 370 * Find the first or next database entry. Hide the record with the cache 371 * cleanup completion time stamp. 372 */ 373 seq_res = dict_seq(db, first_next, &raw_cache_key, &raw_cache_val); 374 if (seq_res == 0 375 && strcmp(raw_cache_key, DC_LAST_CACHE_CLEANUP_COMPLETED) == 0) 376 seq_res = 377 dict_seq(db, DICT_SEQ_FUN_NEXT, &raw_cache_key, &raw_cache_val); 378 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 379 msg_info("%s: key=%s value=%s", myname, 380 seq_res == 0 ? raw_cache_key : db->error ? 381 "(error)" : "(not found)", 382 seq_res == 0 ? raw_cache_val : db->error ? 383 "(error)" : "(not found)"); 384 if (db->error) 385 msg_rate_delay(&cp->seq_log_stamp, cp->log_delay, msg_warn, 386 "%s: sequence error", cp->name); 387 388 /* 389 * Save the current cache_key and cache_val before they are clobbered by 390 * our own delete operation below. This also prevents surprises when the 391 * application accesses the database after this function returns. 392 * 393 * We also use the saved cache_key to protect the current entry against 394 * application delete requests. 395 */ 396 previous_curr_key = cp->saved_curr_key; 397 previous_curr_val = cp->saved_curr_val; 398 if (seq_res == 0) { 399 cp->saved_curr_key = mystrdup(raw_cache_key); 400 cp->saved_curr_val = mystrdup(raw_cache_val); 401 } else { 402 cp->saved_curr_key = 0; 403 cp->saved_curr_val = 0; 404 } 405 406 /* 407 * Delete behind. 408 */ 409 if (db->error == 0 && DC_IS_SCHEDULED_FOR_DELETE_BEHIND(cp)) { 410 DC_CANCEL_DELETE_BEHIND(cp); 411 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 412 msg_info("%s: delete-behind key=%s value=%s", 413 myname, previous_curr_key, previous_curr_val); 414 if (dict_del(db, previous_curr_key) != 0) 415 msg_rate_delay(&cp->del_log_stamp, cp->log_delay, msg_warn, 416 "%s: could not delete entry for %s", 417 cp->name, previous_curr_key); 418 } 419 420 /* 421 * Clean up previous iteration key and value. 422 */ 423 if (previous_curr_key) 424 myfree(previous_curr_key); 425 if (previous_curr_val) 426 myfree(previous_curr_val); 427 428 /* 429 * Return the result. 430 */ 431 *cache_key = (cp)->saved_curr_key; 432 *cache_val = (cp)->saved_curr_val; 433 DICT_ERR_VAL_RETURN(cp, db->error, seq_res); 434 } 435 436 /* dict_cache_delete_behind_reset - reset "delete behind" state */ 437 438 static void dict_cache_delete_behind_reset(DICT_CACHE *cp) 439 { 440 #define FREE_AND_WIPE(s) do { if (s) { myfree(s); (s) = 0; } } while (0) 441 442 DC_CANCEL_DELETE_BEHIND(cp); 443 FREE_AND_WIPE(cp->saved_curr_key); 444 FREE_AND_WIPE(cp->saved_curr_val); 445 } 446 447 /* dict_cache_clean_stat_log_reset - log and reset cache cleanup statistics */ 448 449 static void dict_cache_clean_stat_log_reset(DICT_CACHE *cp, 450 const char *full_partial) 451 { 452 if (cp->user_flags & DICT_CACHE_FLAG_STATISTICS) 453 msg_info("cache %s %s cleanup: retained=%d dropped=%d entries", 454 cp->name, full_partial, cp->retained, cp->dropped); 455 cp->retained = cp->dropped = 0; 456 } 457 458 /* dict_cache_clean_event - examine one cache entry */ 459 460 static void dict_cache_clean_event(int unused_event, char *cache_context) 461 { 462 const char *myname = "dict_cache_clean_event"; 463 DICT_CACHE *cp = (DICT_CACHE *) cache_context; 464 const char *cache_key; 465 const char *cache_val; 466 int next_interval; 467 VSTRING *stamp_buf; 468 int first_next; 469 470 /* 471 * We interleave cache cleanup with other processing, so that the 472 * application's service remains available, with perhaps increased 473 * latency. 474 */ 475 476 /* 477 * Start a new cache cleanup run. 478 */ 479 if (cp->saved_curr_key == 0) { 480 cp->retained = cp->dropped = 0; 481 first_next = DICT_SEQ_FUN_FIRST; 482 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 483 msg_info("%s: start %s cache cleanup", myname, cp->name); 484 } 485 486 /* 487 * Continue a cache cleanup run in progress. 488 */ 489 else { 490 first_next = DICT_SEQ_FUN_NEXT; 491 } 492 493 /* 494 * Examine one cache entry. 495 */ 496 if (dict_cache_sequence(cp, first_next, &cache_key, &cache_val) == 0) { 497 if (cp->exp_validator(cache_key, cache_val, cp->exp_context) == 0) { 498 DC_SCHEDULE_FOR_DELETE_BEHIND(cp); 499 cp->dropped++; 500 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 501 msg_info("%s: drop %s cache entry for %s", 502 myname, cp->name, cache_key); 503 } else { 504 cp->retained++; 505 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 506 msg_info("%s: keep %s cache entry for %s", 507 myname, cp->name, cache_key); 508 } 509 next_interval = 0; 510 } 511 512 /* 513 * Cache cleanup completed. Report vital statistics. 514 */ 515 else if (cp->error != 0) { 516 msg_warn("%s: cache cleanup scan terminated due to error", cp->name); 517 dict_cache_clean_stat_log_reset(cp, "partial"); 518 next_interval = cp->exp_interval; 519 } else { 520 if (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) 521 msg_info("%s: done %s cache cleanup scan", myname, cp->name); 522 dict_cache_clean_stat_log_reset(cp, "full"); 523 stamp_buf = vstring_alloc(100); 524 vstring_sprintf(stamp_buf, "%ld", (long) event_time()); 525 dict_put(cp->db, DC_LAST_CACHE_CLEANUP_COMPLETED, 526 vstring_str(stamp_buf)); 527 vstring_free(stamp_buf); 528 next_interval = cp->exp_interval; 529 } 530 event_request_timer(dict_cache_clean_event, cache_context, next_interval); 531 } 532 533 /* dict_cache_control - schedule or stop the cache cleanup thread */ 534 535 void dict_cache_control(DICT_CACHE *cp,...) 536 { 537 const char *myname = "dict_cache_control"; 538 const char *last_done; 539 time_t next_interval; 540 int cache_cleanup_is_active = (cp->exp_validator && cp->exp_interval); 541 va_list ap; 542 int name; 543 544 /* 545 * Update the control settings. 546 */ 547 va_start(ap, cp); 548 while ((name = va_arg(ap, int)) > 0) { 549 switch (name) { 550 case DICT_CACHE_CTL_END: 551 break; 552 case DICT_CACHE_CTL_FLAGS: 553 cp->user_flags = va_arg(ap, int); 554 cp->log_delay = (cp->user_flags & DICT_CACHE_FLAG_VERBOSE) ? 555 0 : DC_DEF_LOG_DELAY; 556 break; 557 case DICT_CACHE_CTL_INTERVAL: 558 cp->exp_interval = va_arg(ap, int); 559 if (cp->exp_interval < 0) 560 msg_panic("%s: bad %s cache cleanup interval %d", 561 myname, cp->name, cp->exp_interval); 562 break; 563 case DICT_CACHE_CTL_VALIDATOR: 564 cp->exp_validator = va_arg(ap, DICT_CACHE_VALIDATOR_FN); 565 break; 566 case DICT_CACHE_CTL_CONTEXT: 567 cp->exp_context = va_arg(ap, char *); 568 break; 569 default: 570 msg_panic("%s: bad command: %d", myname, name); 571 } 572 } 573 va_end(ap); 574 575 /* 576 * Schedule the cache cleanup thread. 577 */ 578 if (cp->exp_interval && cp->exp_validator) { 579 580 /* 581 * Sanity checks. 582 */ 583 if (cache_cleanup_is_active) 584 msg_panic("%s: %s cache cleanup is already scheduled", 585 myname, cp->name); 586 587 /* 588 * The next start time depends on the last completion time. 589 */ 590 #define NEXT_START(last, delta) ((delta) + (unsigned long) atol(last)) 591 #define NOW (time((time_t *) 0)) /* NOT: event_time() */ 592 593 if ((last_done = dict_get(cp->db, DC_LAST_CACHE_CLEANUP_COMPLETED)) == 0 594 || (next_interval = (NEXT_START(last_done, cp->exp_interval) - NOW)) < 0) 595 next_interval = 0; 596 if (next_interval > cp->exp_interval) 597 next_interval = cp->exp_interval; 598 if ((cp->user_flags & DICT_CACHE_FLAG_VERBOSE) && next_interval > 0) 599 msg_info("%s cache cleanup will start after %ds", 600 cp->name, (int) next_interval); 601 event_request_timer(dict_cache_clean_event, (char *) cp, 602 (int) next_interval); 603 } 604 605 /* 606 * Cancel the cache cleanup thread. 607 */ 608 else if (cache_cleanup_is_active) { 609 if (cp->retained || cp->dropped) 610 dict_cache_clean_stat_log_reset(cp, "partial"); 611 dict_cache_delete_behind_reset(cp); 612 event_cancel_timer(dict_cache_clean_event, (char *) cp); 613 } 614 } 615 616 /* dict_cache_open - open cache file */ 617 618 DICT_CACHE *dict_cache_open(const char *dbname, int open_flags, int dict_flags) 619 { 620 DICT_CACHE *cp; 621 DICT *dict; 622 623 /* 624 * Open the database as requested. Don't attempt to second-guess the 625 * application. 626 */ 627 dict = dict_open(dbname, open_flags, dict_flags); 628 629 /* 630 * Create the DICT_CACHE object. 631 */ 632 cp = (DICT_CACHE *) mymalloc(sizeof(*cp)); 633 cp->name = mystrdup(dbname); 634 cp->cache_flags = 0; 635 cp->user_flags = 0; 636 cp->db = dict; 637 cp->saved_curr_key = 0; 638 cp->saved_curr_val = 0; 639 cp->exp_interval = 0; 640 cp->exp_validator = 0; 641 cp->exp_context = 0; 642 cp->retained = 0; 643 cp->dropped = 0; 644 cp->log_delay = DC_DEF_LOG_DELAY; 645 cp->upd_log_stamp = cp->get_log_stamp = 646 cp->del_log_stamp = cp->seq_log_stamp = 0; 647 648 return (cp); 649 } 650 651 /* dict_cache_close - close cache file */ 652 653 void dict_cache_close(DICT_CACHE *cp) 654 { 655 656 /* 657 * Destroy the DICT_CACHE object. 658 */ 659 myfree(cp->name); 660 dict_cache_control(cp, DICT_CACHE_CTL_INTERVAL, 0, DICT_CACHE_CTL_END); 661 dict_close(cp->db); 662 if (cp->saved_curr_key) 663 myfree(cp->saved_curr_key); 664 if (cp->saved_curr_val) 665 myfree(cp->saved_curr_val); 666 myfree((char *) cp); 667 } 668 669 /* dict_cache_name - get the cache name */ 670 671 const char *dict_cache_name(DICT_CACHE *cp) 672 { 673 674 /* 675 * This is used for verbose logging or warning messages, so the cost of 676 * call is only made where needed (well sort off - code that does not 677 * execute still presents overhead for the processor pipeline, processor 678 * cache, etc). 679 */ 680 return (cp->name); 681 } 682