"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.11.23/lib/dns/cache.c" (7 Sep 2020, 40425 Bytes) of package /linux/misc/dns/bind9/9.11.23/bind-9.11.23.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cache.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.17.3_vs_9.17.4.

    1 /*
    2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3  *
    4  * This Source Code Form is subject to the terms of the Mozilla Public
    5  * License, v. 2.0. If a copy of the MPL was not distributed with this
    6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7  *
    8  * See the COPYRIGHT file distributed with this work for additional
    9  * information regarding copyright ownership.
   10  */
   11 
   12 
   13 /*! \file */
   14 
   15 #include <config.h>
   16 
   17 #include <inttypes.h>
   18 #include <stdbool.h>
   19 
   20 #include <isc/json.h>
   21 #include <isc/mem.h>
   22 #include <isc/print.h>
   23 #include <isc/string.h>
   24 #include <isc/stats.h>
   25 #include <isc/task.h>
   26 #include <isc/time.h>
   27 #include <isc/timer.h>
   28 #include <isc/util.h>
   29 #include <isc/xml.h>
   30 
   31 #include <dns/cache.h>
   32 #include <dns/db.h>
   33 #include <dns/dbiterator.h>
   34 #include <dns/events.h>
   35 #include <dns/lib.h>
   36 #include <dns/log.h>
   37 #include <dns/masterdump.h>
   38 #include <dns/rdata.h>
   39 #include <dns/rdataset.h>
   40 #include <dns/rdatasetiter.h>
   41 #include <dns/result.h>
   42 #include <dns/stats.h>
   43 
   44 #include "rbtdb.h"
   45 
   46 #define CACHE_MAGIC     ISC_MAGIC('$', '$', '$', '$')
   47 #define VALID_CACHE(cache)  ISC_MAGIC_VALID(cache, CACHE_MAGIC)
   48 
   49 /*!
   50  * Control incremental cleaning.
   51  * DNS_CACHE_MINSIZE is how many bytes is the floor for dns_cache_setcachesize().
   52  * See also DNS_CACHE_CLEANERINCREMENT
   53  */
   54 #define DNS_CACHE_MINSIZE   2097152U /*%< Bytes.  2097152 = 2 MB */
   55 /*!
   56  * Control incremental cleaning.
   57  * CLEANERINCREMENT is how many nodes are examined in one pass.
   58  * See also DNS_CACHE_MINSIZE
   59  */
   60 #define DNS_CACHE_CLEANERINCREMENT  1000U   /*%< Number of nodes. */
   61 
   62 /***
   63  ***    Types
   64  ***/
   65 
   66 /*
   67  * A cache_cleaner_t encapsulates the state of the periodic
   68  * cache cleaning.
   69  */
   70 
   71 typedef struct cache_cleaner cache_cleaner_t;
   72 
   73 typedef enum {
   74     cleaner_s_idle, /*%< Waiting for cleaning-interval to expire. */
   75     cleaner_s_busy, /*%< Currently cleaning. */
   76     cleaner_s_done  /*%< Freed enough memory after being overmem. */
   77 } cleaner_state_t;
   78 
   79 /*
   80  * Convenience macros for comprehensive assertion checking.
   81  */
   82 #define CLEANER_IDLE(c) ((c)->state == cleaner_s_idle && \
   83              (c)->resched_event != NULL)
   84 #define CLEANER_BUSY(c) ((c)->state == cleaner_s_busy && \
   85              (c)->iterator != NULL && \
   86              (c)->resched_event == NULL)
   87 
   88 /*%
   89  * Accesses to a cache cleaner object are synchronized through
   90  * task/event serialization, or locked from the cache object.
   91  */
   92 struct cache_cleaner {
   93     isc_mutex_t lock;
   94     /*%<
   95      * Locks overmem_event, overmem.  Note: never allocate memory
   96      * while holding this lock - that could lead to deadlock since
   97      * the lock is take by water() which is called from the memory
   98      * allocator.
   99      */
  100 
  101     dns_cache_t *cache;
  102     isc_task_t  *task;
  103     unsigned int    cleaning_interval; /*% The cleaning-interval from
  104                           named.conf, in seconds. */
  105     isc_timer_t *cleaning_timer;
  106     isc_event_t *resched_event; /*% Sent by cleaner task to
  107                        itself to reschedule */
  108     isc_event_t *overmem_event;
  109 
  110     dns_dbiterator_t *iterator;
  111     unsigned int    increment;  /*% Number of names to
  112                        clean in one increment */
  113     cleaner_state_t state;      /*% Idle/Busy. */
  114     bool    overmem;    /*% The cache is in an overmem state. */
  115     bool     replaceiterator;
  116 };
  117 
  118 /*%
  119  * The actual cache object.
  120  */
  121 
  122 struct dns_cache {
  123     /* Unlocked. */
  124     unsigned int        magic;
  125     isc_mutex_t     lock;
  126     isc_mutex_t     filelock;
  127     isc_mem_t       *mctx;      /* Main cache memory */
  128     isc_mem_t       *hmctx;     /* Heap memory */
  129     char            *name;
  130 
  131     /* Locked by 'lock'. */
  132     int         references;
  133     int         live_tasks;
  134     dns_rdataclass_t    rdclass;
  135     dns_db_t        *db;
  136     cache_cleaner_t     cleaner;
  137     char            *db_type;
  138     int         db_argc;
  139     char            **db_argv;
  140     size_t          size;
  141     isc_stats_t     *stats;
  142 
  143     /* Locked by 'filelock'. */
  144     char            *filename;
  145     /* Access to the on-disk cache file is also locked by 'filelock'. */
  146 };
  147 
  148 /***
  149  ***    Functions
  150  ***/
  151 
  152 static isc_result_t
  153 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
  154            isc_timermgr_t *timermgr, cache_cleaner_t *cleaner);
  155 
  156 static void
  157 cleaning_timer_action(isc_task_t *task, isc_event_t *event);
  158 
  159 static void
  160 incremental_cleaning_action(isc_task_t *task, isc_event_t *event);
  161 
  162 static void
  163 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event);
  164 
  165 static void
  166 overmem_cleaning_action(isc_task_t *task, isc_event_t *event);
  167 
  168 static inline isc_result_t
  169 cache_create_db(dns_cache_t *cache, dns_db_t **db) {
  170     return (dns_db_create(cache->mctx, cache->db_type, dns_rootname,
  171                   dns_dbtype_cache, cache->rdclass,
  172                   cache->db_argc, cache->db_argv, db));
  173 }
  174 
  175 isc_result_t
  176 dns_cache_create(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
  177          isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
  178          const char *db_type, unsigned int db_argc, char **db_argv,
  179          dns_cache_t **cachep)
  180 {
  181     return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass, "",
  182                   db_type, db_argc, db_argv, cachep));
  183 }
  184 
  185 isc_result_t
  186 dns_cache_create2(isc_mem_t *cmctx, isc_taskmgr_t *taskmgr,
  187           isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
  188           const char *cachename, const char *db_type,
  189           unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
  190 {
  191     return (dns_cache_create3(cmctx, cmctx, taskmgr, timermgr, rdclass,
  192                   cachename, db_type, db_argc, db_argv,
  193                   cachep));
  194 }
  195 
  196 isc_result_t
  197 dns_cache_create3(isc_mem_t *cmctx, isc_mem_t *hmctx, isc_taskmgr_t *taskmgr,
  198           isc_timermgr_t *timermgr, dns_rdataclass_t rdclass,
  199           const char *cachename, const char *db_type,
  200           unsigned int db_argc, char **db_argv, dns_cache_t **cachep)
  201 {
  202     isc_result_t result;
  203     dns_cache_t *cache;
  204     int i, extra = 0;
  205     isc_task_t *dbtask;
  206 
  207     REQUIRE(cachep != NULL);
  208     REQUIRE(*cachep == NULL);
  209     REQUIRE(cmctx != NULL);
  210     REQUIRE(hmctx != NULL);
  211     REQUIRE(cachename != NULL);
  212 
  213     cache = isc_mem_get(cmctx, sizeof(*cache));
  214     if (cache == NULL)
  215         return (ISC_R_NOMEMORY);
  216 
  217     cache->mctx = cache->hmctx = NULL;
  218     isc_mem_attach(cmctx, &cache->mctx);
  219     isc_mem_attach(hmctx, &cache->hmctx);
  220 
  221     cache->name = NULL;
  222     if (cachename != NULL) {
  223         cache->name = isc_mem_strdup(cmctx, cachename);
  224         if (cache->name == NULL) {
  225             result = ISC_R_NOMEMORY;
  226             goto cleanup_mem;
  227         }
  228     }
  229 
  230     result = isc_mutex_init(&cache->lock);
  231     if (result != ISC_R_SUCCESS)
  232         goto cleanup_mem;
  233 
  234     result = isc_mutex_init(&cache->filelock);
  235     if (result != ISC_R_SUCCESS)
  236         goto cleanup_lock;
  237 
  238     cache->references = 1;
  239     cache->live_tasks = 0;
  240     cache->rdclass = rdclass;
  241 
  242     cache->stats = NULL;
  243     result = isc_stats_create(cmctx, &cache->stats,
  244                   dns_cachestatscounter_max);
  245     if (result != ISC_R_SUCCESS)
  246         goto cleanup_filelock;
  247 
  248     cache->db_type = isc_mem_strdup(cmctx, db_type);
  249     if (cache->db_type == NULL) {
  250         result = ISC_R_NOMEMORY;
  251         goto cleanup_stats;
  252     }
  253 
  254     /*
  255      * For databases of type "rbt" we pass hmctx to dns_db_create()
  256      * via cache->db_argv, followed by the rest of the arguments in
  257      * db_argv (of which there really shouldn't be any).
  258      */
  259     if (strcmp(cache->db_type, "rbt") == 0)
  260         extra = 1;
  261 
  262     cache->db_argc = db_argc + extra;
  263     cache->db_argv = NULL;
  264 
  265     if (cache->db_argc != 0) {
  266         cache->db_argv = isc_mem_get(cmctx,
  267                          cache->db_argc * sizeof(char *));
  268         if (cache->db_argv == NULL) {
  269             result = ISC_R_NOMEMORY;
  270             goto cleanup_dbtype;
  271         }
  272 
  273         for (i = 0; i < cache->db_argc; i++)
  274             cache->db_argv[i] = NULL;
  275 
  276         cache->db_argv[0] = (char *) hmctx;
  277         for (i = extra; i < cache->db_argc; i++) {
  278             cache->db_argv[i] = isc_mem_strdup(cmctx,
  279                                db_argv[i - extra]);
  280             if (cache->db_argv[i] == NULL) {
  281                 result = ISC_R_NOMEMORY;
  282                 goto cleanup_dbargv;
  283             }
  284         }
  285     }
  286 
  287     /*
  288      * Create the database
  289      */
  290     cache->db = NULL;
  291     result = cache_create_db(cache, &cache->db);
  292     if (result != ISC_R_SUCCESS)
  293         goto cleanup_dbargv;
  294     if (taskmgr != NULL) {
  295         dbtask = NULL;
  296         result = isc_task_create(taskmgr, 1, &dbtask);
  297         if (result != ISC_R_SUCCESS)
  298             goto cleanup_db;
  299 
  300         isc_task_setname(dbtask, "cache_dbtask", NULL);
  301         dns_db_settask(cache->db, dbtask);
  302         isc_task_detach(&dbtask);
  303     }
  304 
  305     cache->filename = NULL;
  306 
  307     cache->magic = CACHE_MAGIC;
  308 
  309     /*
  310      * RBT-type cache DB has its own mechanism of cache cleaning and doesn't
  311      * need the control of the generic cleaner.
  312      */
  313     if (strcmp(db_type, "rbt") == 0)
  314         result = cache_cleaner_init(cache, NULL, NULL, &cache->cleaner);
  315     else {
  316         result = cache_cleaner_init(cache, taskmgr, timermgr,
  317                         &cache->cleaner);
  318     }
  319     if (result != ISC_R_SUCCESS)
  320         goto cleanup_db;
  321 
  322     result = dns_db_setcachestats(cache->db, cache->stats);
  323     if (result != ISC_R_SUCCESS)
  324         goto cleanup_db;
  325 
  326 
  327     *cachep = cache;
  328     return (ISC_R_SUCCESS);
  329 
  330  cleanup_db:
  331     dns_db_detach(&cache->db);
  332  cleanup_dbargv:
  333     for (i = extra; i < cache->db_argc; i++)
  334         if (cache->db_argv[i] != NULL)
  335             isc_mem_free(cmctx, cache->db_argv[i]);
  336     if (cache->db_argv != NULL)
  337         isc_mem_put(cmctx, cache->db_argv,
  338                 cache->db_argc * sizeof(char *));
  339  cleanup_dbtype:
  340     isc_mem_free(cmctx, cache->db_type);
  341  cleanup_filelock:
  342     DESTROYLOCK(&cache->filelock);
  343  cleanup_stats:
  344     isc_stats_detach(&cache->stats);
  345  cleanup_lock:
  346     DESTROYLOCK(&cache->lock);
  347  cleanup_mem:
  348     if (cache->name != NULL)
  349         isc_mem_free(cmctx, cache->name);
  350     isc_mem_detach(&cache->hmctx);
  351     isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
  352     return (result);
  353 }
  354 
  355 static void
  356 cache_free(dns_cache_t *cache) {
  357     int i;
  358 
  359     REQUIRE(VALID_CACHE(cache));
  360     REQUIRE(cache->references == 0);
  361 
  362     isc_mem_setwater(cache->mctx, NULL, NULL, 0, 0);
  363 
  364     if (cache->cleaner.task != NULL)
  365         isc_task_detach(&cache->cleaner.task);
  366 
  367     if (cache->cleaner.overmem_event != NULL)
  368         isc_event_free(&cache->cleaner.overmem_event);
  369 
  370     if (cache->cleaner.resched_event != NULL)
  371         isc_event_free(&cache->cleaner.resched_event);
  372 
  373     if (cache->cleaner.iterator != NULL)
  374         dns_dbiterator_destroy(&cache->cleaner.iterator);
  375 
  376     DESTROYLOCK(&cache->cleaner.lock);
  377 
  378     if (cache->filename) {
  379         isc_mem_free(cache->mctx, cache->filename);
  380         cache->filename = NULL;
  381     }
  382 
  383     if (cache->db != NULL)
  384         dns_db_detach(&cache->db);
  385 
  386     if (cache->db_argv != NULL) {
  387         /*
  388          * We don't free db_argv[0] in "rbt" cache databases
  389          * as it's a pointer to hmctx
  390          */
  391         int extra = 0;
  392         if (strcmp(cache->db_type, "rbt") == 0)
  393             extra = 1;
  394         for (i = extra; i < cache->db_argc; i++)
  395             if (cache->db_argv[i] != NULL)
  396                 isc_mem_free(cache->mctx, cache->db_argv[i]);
  397         isc_mem_put(cache->mctx, cache->db_argv,
  398                 cache->db_argc * sizeof(char *));
  399     }
  400 
  401     if (cache->db_type != NULL)
  402         isc_mem_free(cache->mctx, cache->db_type);
  403 
  404     if (cache->name != NULL)
  405         isc_mem_free(cache->mctx, cache->name);
  406 
  407     if (cache->stats != NULL)
  408         isc_stats_detach(&cache->stats);
  409 
  410     DESTROYLOCK(&cache->lock);
  411     DESTROYLOCK(&cache->filelock);
  412 
  413     cache->magic = 0;
  414     isc_mem_detach(&cache->hmctx);
  415     isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache));
  416 }
  417 
  418 
  419 void
  420 dns_cache_attach(dns_cache_t *cache, dns_cache_t **targetp) {
  421 
  422     REQUIRE(VALID_CACHE(cache));
  423     REQUIRE(targetp != NULL && *targetp == NULL);
  424 
  425     LOCK(&cache->lock);
  426     cache->references++;
  427     UNLOCK(&cache->lock);
  428 
  429     *targetp = cache;
  430 }
  431 
  432 void
  433 dns_cache_detach(dns_cache_t **cachep) {
  434     dns_cache_t *cache;
  435     bool free_cache = false;
  436 
  437     REQUIRE(cachep != NULL);
  438     cache = *cachep;
  439     REQUIRE(VALID_CACHE(cache));
  440 
  441     LOCK(&cache->lock);
  442     REQUIRE(cache->references > 0);
  443     cache->references--;
  444     if (cache->references == 0) {
  445         cache->cleaner.overmem = false;
  446         free_cache = true;
  447     }
  448 
  449     *cachep = NULL;
  450 
  451     if (free_cache) {
  452         /*
  453          * When the cache is shut down, dump it to a file if one is
  454          * specified.
  455          */
  456         isc_result_t result = dns_cache_dump(cache);
  457         if (result != ISC_R_SUCCESS)
  458             isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
  459                       DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
  460                       "error dumping cache: %s ",
  461                       isc_result_totext(result));
  462 
  463         /*
  464          * If the cleaner task exists, let it free the cache.
  465          */
  466         if (cache->live_tasks > 0) {
  467             isc_task_shutdown(cache->cleaner.task);
  468             free_cache = false;
  469         }
  470     }
  471 
  472     UNLOCK(&cache->lock);
  473 
  474     if (free_cache)
  475         cache_free(cache);
  476 }
  477 
  478 void
  479 dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) {
  480     REQUIRE(VALID_CACHE(cache));
  481     REQUIRE(dbp != NULL && *dbp == NULL);
  482     REQUIRE(cache->db != NULL);
  483 
  484     LOCK(&cache->lock);
  485     dns_db_attach(cache->db, dbp);
  486     UNLOCK(&cache->lock);
  487 
  488 }
  489 
  490 isc_result_t
  491 dns_cache_setfilename(dns_cache_t *cache, const char *filename) {
  492     char *newname;
  493 
  494     REQUIRE(VALID_CACHE(cache));
  495     REQUIRE(filename != NULL);
  496 
  497     newname = isc_mem_strdup(cache->mctx, filename);
  498     if (newname == NULL)
  499         return (ISC_R_NOMEMORY);
  500 
  501     LOCK(&cache->filelock);
  502     if (cache->filename)
  503         isc_mem_free(cache->mctx, cache->filename);
  504     cache->filename = newname;
  505     UNLOCK(&cache->filelock);
  506 
  507     return (ISC_R_SUCCESS);
  508 }
  509 
  510 isc_result_t
  511 dns_cache_load(dns_cache_t *cache) {
  512     isc_result_t result;
  513 
  514     REQUIRE(VALID_CACHE(cache));
  515 
  516     if (cache->filename == NULL)
  517         return (ISC_R_SUCCESS);
  518 
  519     LOCK(&cache->filelock);
  520     result = dns_db_load(cache->db, cache->filename);
  521     UNLOCK(&cache->filelock);
  522 
  523     return (result);
  524 }
  525 
  526 isc_result_t
  527 dns_cache_dump(dns_cache_t *cache) {
  528     isc_result_t result;
  529 
  530     REQUIRE(VALID_CACHE(cache));
  531 
  532     if (cache->filename == NULL)
  533         return (ISC_R_SUCCESS);
  534 
  535     LOCK(&cache->filelock);
  536     result = dns_master_dump(cache->mctx, cache->db, NULL,
  537                  &dns_master_style_cache, cache->filename);
  538     UNLOCK(&cache->filelock);
  539     return (result);
  540 
  541 }
  542 
  543 void
  544 dns_cache_setcleaninginterval(dns_cache_t *cache, unsigned int t) {
  545     isc_interval_t interval;
  546     isc_result_t result;
  547 
  548     LOCK(&cache->lock);
  549 
  550     /*
  551      * It may be the case that the cache has already shut down.
  552      * If so, it has no timer.
  553      */
  554     if (cache->cleaner.cleaning_timer == NULL)
  555         goto unlock;
  556 
  557     cache->cleaner.cleaning_interval = t;
  558 
  559     if (t == 0) {
  560         result = isc_timer_reset(cache->cleaner.cleaning_timer,
  561                      isc_timertype_inactive,
  562                      NULL, NULL, true);
  563     } else {
  564         isc_interval_set(&interval, cache->cleaner.cleaning_interval,
  565                  0);
  566         result = isc_timer_reset(cache->cleaner.cleaning_timer,
  567                      isc_timertype_ticker,
  568                      NULL, &interval, false);
  569     }
  570     if (result != ISC_R_SUCCESS)
  571         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
  572                   DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
  573                   "could not set cache cleaning interval: %s",
  574                   isc_result_totext(result));
  575 
  576  unlock:
  577     UNLOCK(&cache->lock);
  578 }
  579 
  580 unsigned int
  581 dns_cache_getcleaninginterval(dns_cache_t *cache) {
  582     unsigned int t;
  583 
  584     REQUIRE(VALID_CACHE(cache));
  585 
  586     LOCK(&cache->lock);
  587     t = cache->cleaner.cleaning_interval;
  588     UNLOCK(&cache->lock);
  589 
  590     return (t);
  591 }
  592 
  593 const char *
  594 dns_cache_getname(dns_cache_t *cache) {
  595     REQUIRE(VALID_CACHE(cache));
  596 
  597     return (cache->name);
  598 }
  599 
  600 /*
  601  * Initialize the cache cleaner object at *cleaner.
  602  * Space for the object must be allocated by the caller.
  603  */
  604 
  605 static isc_result_t
  606 cache_cleaner_init(dns_cache_t *cache, isc_taskmgr_t *taskmgr,
  607            isc_timermgr_t *timermgr, cache_cleaner_t *cleaner)
  608 {
  609     isc_result_t result;
  610 
  611     result = isc_mutex_init(&cleaner->lock);
  612     if (result != ISC_R_SUCCESS)
  613         goto fail;
  614 
  615     cleaner->increment = DNS_CACHE_CLEANERINCREMENT;
  616     cleaner->state = cleaner_s_idle;
  617     cleaner->cache = cache;
  618     cleaner->iterator = NULL;
  619     cleaner->overmem = false;
  620     cleaner->replaceiterator = false;
  621 
  622     cleaner->task = NULL;
  623     cleaner->cleaning_timer = NULL;
  624     cleaner->resched_event = NULL;
  625     cleaner->overmem_event = NULL;
  626     cleaner->cleaning_interval = 0; /* Initially turned off. */
  627 
  628     result = dns_db_createiterator(cleaner->cache->db, false,
  629                        &cleaner->iterator);
  630     if (result != ISC_R_SUCCESS)
  631         goto cleanup;
  632 
  633     if (taskmgr != NULL && timermgr != NULL) {
  634         result = isc_task_create(taskmgr, 1, &cleaner->task);
  635         if (result != ISC_R_SUCCESS) {
  636             UNEXPECTED_ERROR(__FILE__, __LINE__,
  637                      "isc_task_create() failed: %s",
  638                      dns_result_totext(result));
  639             result = ISC_R_UNEXPECTED;
  640             goto cleanup;
  641         }
  642         cleaner->cache->live_tasks++;
  643         isc_task_setname(cleaner->task, "cachecleaner", cleaner);
  644 
  645         result = isc_task_onshutdown(cleaner->task,
  646                          cleaner_shutdown_action, cache);
  647         if (result != ISC_R_SUCCESS) {
  648             UNEXPECTED_ERROR(__FILE__, __LINE__,
  649                      "cache cleaner: "
  650                      "isc_task_onshutdown() failed: %s",
  651                      dns_result_totext(result));
  652             goto cleanup;
  653         }
  654 
  655         result = isc_timer_create(timermgr, isc_timertype_inactive,
  656                        NULL, NULL, cleaner->task,
  657                        cleaning_timer_action, cleaner,
  658                        &cleaner->cleaning_timer);
  659         if (result != ISC_R_SUCCESS) {
  660             UNEXPECTED_ERROR(__FILE__, __LINE__,
  661                      "isc_timer_create() failed: %s",
  662                      dns_result_totext(result));
  663             result = ISC_R_UNEXPECTED;
  664             goto cleanup;
  665         }
  666 
  667         cleaner->resched_event =
  668             isc_event_allocate(cache->mctx, cleaner,
  669                        DNS_EVENT_CACHECLEAN,
  670                        incremental_cleaning_action,
  671                        cleaner, sizeof(isc_event_t));
  672         if (cleaner->resched_event == NULL) {
  673             result = ISC_R_NOMEMORY;
  674             goto cleanup;
  675         }
  676 
  677         cleaner->overmem_event =
  678             isc_event_allocate(cache->mctx, cleaner,
  679                        DNS_EVENT_CACHEOVERMEM,
  680                        overmem_cleaning_action,
  681                        cleaner, sizeof(isc_event_t));
  682         if (cleaner->overmem_event == NULL) {
  683             result = ISC_R_NOMEMORY;
  684             goto cleanup;
  685         }
  686     }
  687 
  688     return (ISC_R_SUCCESS);
  689 
  690  cleanup:
  691     if (cleaner->overmem_event != NULL)
  692         isc_event_free(&cleaner->overmem_event);
  693     if (cleaner->resched_event != NULL)
  694         isc_event_free(&cleaner->resched_event);
  695     if (cleaner->cleaning_timer != NULL)
  696         isc_timer_detach(&cleaner->cleaning_timer);
  697     if (cleaner->task != NULL)
  698         isc_task_detach(&cleaner->task);
  699     if (cleaner->iterator != NULL)
  700         dns_dbiterator_destroy(&cleaner->iterator);
  701     DESTROYLOCK(&cleaner->lock);
  702  fail:
  703     return (result);
  704 }
  705 
  706 static void
  707 begin_cleaning(cache_cleaner_t *cleaner) {
  708     isc_result_t result = ISC_R_SUCCESS;
  709 
  710     REQUIRE(CLEANER_IDLE(cleaner));
  711 
  712     /*
  713      * Create an iterator, if it does not already exist, and
  714      * position it at the beginning of the cache.
  715      */
  716     if (cleaner->iterator == NULL)
  717         result = dns_db_createiterator(cleaner->cache->db, false,
  718                            &cleaner->iterator);
  719     if (result != ISC_R_SUCCESS)
  720         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
  721                   DNS_LOGMODULE_CACHE, ISC_LOG_WARNING,
  722                   "cache cleaner could not create "
  723                   "iterator: %s", isc_result_totext(result));
  724     else {
  725         dns_dbiterator_setcleanmode(cleaner->iterator, true);
  726         result = dns_dbiterator_first(cleaner->iterator);
  727     }
  728     if (result != ISC_R_SUCCESS) {
  729         /*
  730          * If the result is ISC_R_NOMORE, the database is empty,
  731          * so there is nothing to be cleaned.
  732          */
  733         if (result != ISC_R_NOMORE && cleaner->iterator != NULL) {
  734             UNEXPECTED_ERROR(__FILE__, __LINE__,
  735                      "cache cleaner: "
  736                      "dns_dbiterator_first() failed: %s",
  737                      dns_result_totext(result));
  738             dns_dbiterator_destroy(&cleaner->iterator);
  739         } else if (cleaner->iterator != NULL) {
  740             result = dns_dbiterator_pause(cleaner->iterator);
  741             RUNTIME_CHECK(result == ISC_R_SUCCESS);
  742         }
  743     } else {
  744         /*
  745          * Pause the iterator to free its lock.
  746          */
  747         result = dns_dbiterator_pause(cleaner->iterator);
  748         RUNTIME_CHECK(result == ISC_R_SUCCESS);
  749 
  750         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
  751                   DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1),
  752                   "begin cache cleaning, mem inuse %lu",
  753                 (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
  754         cleaner->state = cleaner_s_busy;
  755         isc_task_send(cleaner->task, &cleaner->resched_event);
  756     }
  757 
  758     return;
  759 }
  760 
  761 static void
  762 end_cleaning(cache_cleaner_t *cleaner, isc_event_t *event) {
  763     isc_result_t result;
  764 
  765     REQUIRE(CLEANER_BUSY(cleaner));
  766     REQUIRE(event != NULL);
  767 
  768     result = dns_dbiterator_pause(cleaner->iterator);
  769     if (result != ISC_R_SUCCESS)
  770         dns_dbiterator_destroy(&cleaner->iterator);
  771 
  772     dns_cache_setcleaninginterval(cleaner->cache,
  773                       cleaner->cleaning_interval);
  774 
  775     isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
  776               ISC_LOG_DEBUG(1), "end cache cleaning, mem inuse %lu",
  777               (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
  778 
  779     cleaner->state = cleaner_s_idle;
  780     cleaner->resched_event = event;
  781 }
  782 
  783 /*
  784  * This is run once for every cache-cleaning-interval as defined in named.conf.
  785  */
  786 static void
  787 cleaning_timer_action(isc_task_t *task, isc_event_t *event) {
  788     cache_cleaner_t *cleaner = event->ev_arg;
  789 
  790     UNUSED(task);
  791 
  792     INSIST(task == cleaner->task);
  793     INSIST(event->ev_type == ISC_TIMEREVENT_TICK);
  794 
  795     isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
  796               ISC_LOG_DEBUG(1), "cache cleaning timer fired, "
  797               "cleaner state = %d", cleaner->state);
  798 
  799     if (cleaner->state == cleaner_s_idle)
  800         begin_cleaning(cleaner);
  801 
  802     isc_event_free(&event);
  803 }
  804 
  805 /*
  806  * This is called when the cache either surpasses its upper limit
  807  * or shrinks beyond its lower limit.
  808  */
  809 static void
  810 overmem_cleaning_action(isc_task_t *task, isc_event_t *event) {
  811     cache_cleaner_t *cleaner = event->ev_arg;
  812     bool want_cleaning = false;
  813 
  814     UNUSED(task);
  815 
  816     INSIST(task == cleaner->task);
  817     INSIST(event->ev_type == DNS_EVENT_CACHEOVERMEM);
  818     INSIST(cleaner->overmem_event == NULL);
  819 
  820     isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
  821               ISC_LOG_DEBUG(1), "overmem_cleaning_action called, "
  822               "overmem = %d, state = %d", cleaner->overmem,
  823               cleaner->state);
  824 
  825     LOCK(&cleaner->lock);
  826 
  827     if (cleaner->overmem) {
  828         if (cleaner->state == cleaner_s_idle)
  829             want_cleaning = true;
  830     } else {
  831         if (cleaner->state == cleaner_s_busy)
  832             /*
  833              * end_cleaning() can't be called here because
  834              * then both cleaner->overmem_event and
  835              * cleaner->resched_event will point to this
  836              * event.  Set the state to done, and then
  837              * when the incremental_cleaning_action() event
  838              * is posted, it will handle the end_cleaning.
  839              */
  840             cleaner->state = cleaner_s_done;
  841     }
  842 
  843     cleaner->overmem_event = event;
  844 
  845     UNLOCK(&cleaner->lock);
  846 
  847     if (want_cleaning)
  848         begin_cleaning(cleaner);
  849 }
  850 
  851 /*
  852  * Do incremental cleaning.
  853  */
  854 static void
  855 incremental_cleaning_action(isc_task_t *task, isc_event_t *event) {
  856     cache_cleaner_t *cleaner = event->ev_arg;
  857     isc_result_t result;
  858     unsigned int n_names;
  859     isc_time_t start;
  860 
  861     UNUSED(task);
  862 
  863     INSIST(task == cleaner->task);
  864     INSIST(event->ev_type == DNS_EVENT_CACHECLEAN);
  865 
  866     if (cleaner->state == cleaner_s_done) {
  867         cleaner->state = cleaner_s_busy;
  868         end_cleaning(cleaner, event);
  869         LOCK(&cleaner->cache->lock);
  870         LOCK(&cleaner->lock);
  871         if (cleaner->replaceiterator) {
  872             dns_dbiterator_destroy(&cleaner->iterator);
  873             (void) dns_db_createiterator(cleaner->cache->db,
  874                              false,
  875                              &cleaner->iterator);
  876             cleaner->replaceiterator = false;
  877         }
  878         UNLOCK(&cleaner->lock);
  879         UNLOCK(&cleaner->cache->lock);
  880         return;
  881     }
  882 
  883     INSIST(CLEANER_BUSY(cleaner));
  884 
  885     n_names = cleaner->increment;
  886 
  887     REQUIRE(DNS_DBITERATOR_VALID(cleaner->iterator));
  888 
  889     isc_time_now(&start);
  890     while (n_names-- > 0) {
  891         dns_dbnode_t *node = NULL;
  892 
  893         result = dns_dbiterator_current(cleaner->iterator, &node,
  894                         NULL);
  895         if (result != ISC_R_SUCCESS) {
  896             UNEXPECTED_ERROR(__FILE__, __LINE__,
  897                  "cache cleaner: dns_dbiterator_current() "
  898                  "failed: %s", dns_result_totext(result));
  899 
  900             end_cleaning(cleaner, event);
  901             return;
  902         }
  903 
  904         /*
  905          * The node was not needed, but was required by
  906          * dns_dbiterator_current().  Give up its reference.
  907          */
  908         dns_db_detachnode(cleaner->cache->db, &node);
  909 
  910         /*
  911          * Step to the next node.
  912          */
  913         result = dns_dbiterator_next(cleaner->iterator);
  914 
  915         if (result != ISC_R_SUCCESS) {
  916             /*
  917              * Either the end was reached (ISC_R_NOMORE) or
  918              * some error was signaled.  If the cache is still
  919              * overmem and no error was encountered,
  920              * keep trying to clean it, otherwise stop cleaning.
  921              */
  922             if (result != ISC_R_NOMORE)
  923                 UNEXPECTED_ERROR(__FILE__, __LINE__,
  924                          "cache cleaner: "
  925                          "dns_dbiterator_next() "
  926                          "failed: %s",
  927                          dns_result_totext(result));
  928             else if (cleaner->overmem) {
  929                 result = dns_dbiterator_first(cleaner->
  930                                   iterator);
  931                 if (result == ISC_R_SUCCESS) {
  932                     isc_log_write(dns_lctx,
  933                               DNS_LOGCATEGORY_DATABASE,
  934                               DNS_LOGMODULE_CACHE,
  935                               ISC_LOG_DEBUG(1),
  936                               "cache cleaner: "
  937                               "still overmem, "
  938                               "reset and try again");
  939                     continue;
  940                 }
  941             }
  942 
  943             end_cleaning(cleaner, event);
  944             return;
  945         }
  946     }
  947 
  948     /*
  949      * We have successfully performed a cleaning increment but have
  950      * not gone through the entire cache.  Free the iterator locks
  951      * and reschedule another batch.  If it fails, just try to continue
  952      * anyway.
  953      */
  954     result = dns_dbiterator_pause(cleaner->iterator);
  955     RUNTIME_CHECK(result == ISC_R_SUCCESS);
  956 
  957     isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE,
  958               ISC_LOG_DEBUG(1), "cache cleaner: checked %u nodes, "
  959               "mem inuse %lu, sleeping", cleaner->increment,
  960               (unsigned long)isc_mem_inuse(cleaner->cache->mctx));
  961 
  962     isc_task_send(task, &event);
  963     INSIST(CLEANER_BUSY(cleaner));
  964     return;
  965 }
  966 
  967 /*
  968  * Do immediate cleaning.
  969  */
  970 isc_result_t
  971 dns_cache_clean(dns_cache_t *cache, isc_stdtime_t now) {
  972     isc_result_t result;
  973     dns_dbiterator_t *iterator = NULL;
  974 
  975     REQUIRE(VALID_CACHE(cache));
  976 
  977     result = dns_db_createiterator(cache->db, 0, &iterator);
  978     if (result != ISC_R_SUCCESS)
  979         return result;
  980 
  981     result = dns_dbiterator_first(iterator);
  982 
  983     while (result == ISC_R_SUCCESS) {
  984         dns_dbnode_t *node = NULL;
  985         result = dns_dbiterator_current(iterator, &node,
  986                         (dns_name_t *)NULL);
  987         if (result != ISC_R_SUCCESS)
  988             break;
  989 
  990         /*
  991          * Check TTLs, mark expired rdatasets stale.
  992          */
  993         result = dns_db_expirenode(cache->db, node, now);
  994         if (result != ISC_R_SUCCESS) {
  995             UNEXPECTED_ERROR(__FILE__, __LINE__,
  996                      "cache cleaner: dns_db_expirenode() "
  997                      "failed: %s",
  998                      dns_result_totext(result));
  999             /*
 1000              * Continue anyway.
 1001              */
 1002         }
 1003 
 1004         /*
 1005          * This is where the actual freeing takes place.
 1006          */
 1007         dns_db_detachnode(cache->db, &node);
 1008 
 1009         result = dns_dbiterator_next(iterator);
 1010     }
 1011 
 1012     dns_dbiterator_destroy(&iterator);
 1013 
 1014     if (result == ISC_R_NOMORE)
 1015         result = ISC_R_SUCCESS;
 1016 
 1017     return (result);
 1018 }
 1019 
 1020 static void
 1021 water(void *arg, int mark) {
 1022     dns_cache_t *cache = arg;
 1023     bool overmem = (mark == ISC_MEM_HIWATER);
 1024 
 1025     REQUIRE(VALID_CACHE(cache));
 1026 
 1027     LOCK(&cache->cleaner.lock);
 1028 
 1029     if (overmem != cache->cleaner.overmem) {
 1030         dns_db_overmem(cache->db, overmem);
 1031         cache->cleaner.overmem = overmem;
 1032         isc_mem_waterack(cache->mctx, mark);
 1033     }
 1034 
 1035     if (cache->cleaner.overmem_event != NULL)
 1036         isc_task_send(cache->cleaner.task,
 1037                   &cache->cleaner.overmem_event);
 1038 
 1039     UNLOCK(&cache->cleaner.lock);
 1040 }
 1041 
 1042 void
 1043 dns_cache_setcachesize(dns_cache_t *cache, size_t size) {
 1044     size_t hiwater, lowater;
 1045 
 1046     REQUIRE(VALID_CACHE(cache));
 1047 
 1048     /*
 1049      * Impose a minimum cache size; pathological things happen if there
 1050      * is too little room.
 1051      */
 1052     if (size != 0U && size < DNS_CACHE_MINSIZE)
 1053         size = DNS_CACHE_MINSIZE;
 1054 
 1055     LOCK(&cache->lock);
 1056     cache->size = size;
 1057     UNLOCK(&cache->lock);
 1058 
 1059     hiwater = size - (size >> 3);   /* Approximately 7/8ths. */
 1060     lowater = size - (size >> 2);   /* Approximately 3/4ths. */
 1061 
 1062     /*
 1063      * If the cache was overmem and cleaning, but now with the new limits
 1064      * it is no longer in an overmem condition, then the next
 1065      * isc_mem_put for cache memory will do the right thing and trigger
 1066      * water().
 1067      */
 1068 
 1069     if (size == 0U || hiwater == 0U || lowater == 0U)
 1070         /*
 1071          * Disable cache memory limiting.
 1072          */
 1073         isc_mem_setwater(cache->mctx, water, cache, 0, 0);
 1074     else
 1075         /*
 1076          * Establish new cache memory limits (either for the first
 1077          * time, or replacing other limits).
 1078          */
 1079         isc_mem_setwater(cache->mctx, water, cache, hiwater, lowater);
 1080 }
 1081 
 1082 size_t
 1083 dns_cache_getcachesize(dns_cache_t *cache) {
 1084     size_t size;
 1085 
 1086     REQUIRE(VALID_CACHE(cache));
 1087 
 1088     LOCK(&cache->lock);
 1089     size = cache->size;
 1090     UNLOCK(&cache->lock);
 1091 
 1092     return (size);
 1093 }
 1094 
 1095 /*
 1096  * The cleaner task is shutting down; do the necessary cleanup.
 1097  */
 1098 static void
 1099 cleaner_shutdown_action(isc_task_t *task, isc_event_t *event) {
 1100     dns_cache_t *cache = event->ev_arg;
 1101     bool should_free = false;
 1102 
 1103     UNUSED(task);
 1104 
 1105     INSIST(task == cache->cleaner.task);
 1106     INSIST(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
 1107 
 1108     if (CLEANER_BUSY(&cache->cleaner))
 1109         end_cleaning(&cache->cleaner, event);
 1110     else
 1111         isc_event_free(&event);
 1112 
 1113     LOCK(&cache->lock);
 1114 
 1115     cache->live_tasks--;
 1116     INSIST(cache->live_tasks == 0);
 1117 
 1118     if (cache->references == 0)
 1119         should_free = true;
 1120 
 1121     /*
 1122      * By detaching the timer in the context of its task,
 1123      * we are guaranteed that there will be no further timer
 1124      * events.
 1125      */
 1126     if (cache->cleaner.cleaning_timer != NULL)
 1127         isc_timer_detach(&cache->cleaner.cleaning_timer);
 1128 
 1129     /* Make sure we don't reschedule anymore. */
 1130     (void)isc_task_purge(task, NULL, DNS_EVENT_CACHECLEAN, NULL);
 1131 
 1132     UNLOCK(&cache->lock);
 1133 
 1134     if (should_free)
 1135         cache_free(cache);
 1136 }
 1137 
 1138 isc_result_t
 1139 dns_cache_flush(dns_cache_t *cache) {
 1140     dns_db_t *db = NULL, *olddb;
 1141     dns_dbiterator_t *dbiterator = NULL, *olddbiterator = NULL;
 1142     isc_result_t result;
 1143 
 1144     result = cache_create_db(cache, &db);
 1145     if (result != ISC_R_SUCCESS)
 1146         return (result);
 1147 
 1148     result = dns_db_createiterator(db, false, &dbiterator);
 1149     if (result != ISC_R_SUCCESS) {
 1150         dns_db_detach(&db);
 1151         return (result);
 1152     }
 1153 
 1154     LOCK(&cache->lock);
 1155     LOCK(&cache->cleaner.lock);
 1156     if (cache->cleaner.state == cleaner_s_idle) {
 1157         olddbiterator = cache->cleaner.iterator;
 1158         cache->cleaner.iterator = dbiterator;
 1159         dbiterator = NULL;
 1160     } else {
 1161         if (cache->cleaner.state == cleaner_s_busy)
 1162             cache->cleaner.state = cleaner_s_done;
 1163         cache->cleaner.replaceiterator = true;
 1164     }
 1165     olddb = cache->db;
 1166     cache->db = db;
 1167     dns_db_setcachestats(cache->db, cache->stats);
 1168     UNLOCK(&cache->cleaner.lock);
 1169     UNLOCK(&cache->lock);
 1170 
 1171     if (dbiterator != NULL)
 1172         dns_dbiterator_destroy(&dbiterator);
 1173     if (olddbiterator != NULL)
 1174         dns_dbiterator_destroy(&olddbiterator);
 1175     dns_db_detach(&olddb);
 1176 
 1177     return (ISC_R_SUCCESS);
 1178 }
 1179 
 1180 static isc_result_t
 1181 clearnode(dns_db_t *db, dns_dbnode_t *node) {
 1182     isc_result_t result;
 1183     dns_rdatasetiter_t *iter = NULL;
 1184 
 1185     result = dns_db_allrdatasets(db, node, NULL, (isc_stdtime_t)0, &iter);
 1186     if (result != ISC_R_SUCCESS)
 1187         return (result);
 1188 
 1189     for (result = dns_rdatasetiter_first(iter);
 1190          result == ISC_R_SUCCESS;
 1191          result = dns_rdatasetiter_next(iter))
 1192     {
 1193         dns_rdataset_t rdataset;
 1194         dns_rdataset_init(&rdataset);
 1195 
 1196         dns_rdatasetiter_current(iter, &rdataset);
 1197         result = dns_db_deleterdataset(db, node, NULL,
 1198                            rdataset.type, rdataset.covers);
 1199         dns_rdataset_disassociate(&rdataset);
 1200         if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED)
 1201             break;
 1202     }
 1203 
 1204     if (result == ISC_R_NOMORE)
 1205         result = ISC_R_SUCCESS;
 1206 
 1207     dns_rdatasetiter_destroy(&iter);
 1208     return (result);
 1209 }
 1210 
 1211 static isc_result_t
 1212 cleartree(dns_db_t *db, dns_name_t *name) {
 1213     isc_result_t result, answer = ISC_R_SUCCESS;
 1214     dns_dbiterator_t *iter = NULL;
 1215     dns_dbnode_t *node = NULL, *top = NULL;
 1216     dns_fixedname_t fnodename;
 1217     dns_name_t *nodename;
 1218 
 1219     /*
 1220      * Create the node if it doesn't exist so dns_dbiterator_seek()
 1221      * can find it.  We will continue even if this fails.
 1222      */
 1223     (void)dns_db_findnode(db, name, true, &top);
 1224 
 1225     nodename = dns_fixedname_initname(&fnodename);
 1226 
 1227     result = dns_db_createiterator(db, 0, &iter);
 1228     if (result != ISC_R_SUCCESS)
 1229         goto cleanup;
 1230 
 1231     result = dns_dbiterator_seek(iter, name);
 1232     if (result == DNS_R_PARTIALMATCH)
 1233         result = dns_dbiterator_next(iter);
 1234     if (result != ISC_R_SUCCESS)
 1235         goto cleanup;
 1236 
 1237     while (result == ISC_R_SUCCESS) {
 1238         result = dns_dbiterator_current(iter, &node, nodename);
 1239         if (result == DNS_R_NEWORIGIN)
 1240             result = ISC_R_SUCCESS;
 1241         if (result != ISC_R_SUCCESS)
 1242             goto cleanup;
 1243         /*
 1244          * Are we done?
 1245          */
 1246         if (! dns_name_issubdomain(nodename, name))
 1247             goto cleanup;
 1248 
 1249         /*
 1250          * If clearnode fails record and move onto the next node.
 1251          */
 1252         result = clearnode(db, node);
 1253         if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS)
 1254             answer = result;
 1255         dns_db_detachnode(db, &node);
 1256         result = dns_dbiterator_next(iter);
 1257     }
 1258 
 1259  cleanup:
 1260     if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND)
 1261         result = ISC_R_SUCCESS;
 1262     if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS)
 1263         answer = result;
 1264     if (node != NULL)
 1265         dns_db_detachnode(db, &node);
 1266     if (iter != NULL)
 1267         dns_dbiterator_destroy(&iter);
 1268     if (top != NULL)
 1269         dns_db_detachnode(db, &top);
 1270 
 1271     return (answer);
 1272 }
 1273 
 1274 isc_result_t
 1275 dns_cache_flushname(dns_cache_t *cache, dns_name_t *name) {
 1276     return (dns_cache_flushnode(cache, name, false));
 1277 }
 1278 
 1279 isc_result_t
 1280 dns_cache_flushnode(dns_cache_t *cache, dns_name_t *name,
 1281             bool tree)
 1282 {
 1283     isc_result_t result;
 1284     dns_dbnode_t *node = NULL;
 1285     dns_db_t *db = NULL;
 1286 
 1287     if (tree && dns_name_equal(name, dns_rootname))
 1288         return (dns_cache_flush(cache));
 1289 
 1290     LOCK(&cache->lock);
 1291     if (cache->db != NULL)
 1292         dns_db_attach(cache->db, &db);
 1293     UNLOCK(&cache->lock);
 1294     if (db == NULL)
 1295         return (ISC_R_SUCCESS);
 1296 
 1297     if (tree) {
 1298         result = cleartree(cache->db, name);
 1299     } else {
 1300         result = dns_db_findnode(cache->db, name, false, &node);
 1301         if (result == ISC_R_NOTFOUND) {
 1302             result = ISC_R_SUCCESS;
 1303             goto cleanup_db;
 1304         }
 1305         if (result != ISC_R_SUCCESS)
 1306             goto cleanup_db;
 1307         result = clearnode(cache->db, node);
 1308         dns_db_detachnode(cache->db, &node);
 1309     }
 1310 
 1311  cleanup_db:
 1312     dns_db_detach(&db);
 1313     return (result);
 1314 }
 1315 
 1316 isc_stats_t *
 1317 dns_cache_getstats(dns_cache_t *cache) {
 1318     REQUIRE(VALID_CACHE(cache));
 1319     return (cache->stats);
 1320 }
 1321 
 1322 void
 1323 dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) {
 1324     REQUIRE(VALID_CACHE(cache));
 1325     if (cache->stats == NULL)
 1326         return;
 1327 
 1328     switch (result) {
 1329     case ISC_R_SUCCESS:
 1330     case DNS_R_NCACHENXDOMAIN:
 1331     case DNS_R_NCACHENXRRSET:
 1332     case DNS_R_CNAME:
 1333     case DNS_R_DNAME:
 1334     case DNS_R_GLUE:
 1335     case DNS_R_ZONECUT:
 1336         isc_stats_increment(cache->stats,
 1337                     dns_cachestatscounter_queryhits);
 1338         break;
 1339     default:
 1340         isc_stats_increment(cache->stats,
 1341                     dns_cachestatscounter_querymisses);
 1342     }
 1343 }
 1344 
 1345 /*
 1346  * XXX: Much of the following code has been copied in from statschannel.c.
 1347  * We should refactor this into a generic function in stats.c that can be
 1348  * called from both places.
 1349  */
 1350 typedef struct
 1351 cache_dumparg {
 1352     isc_statsformat_t   type;
 1353     void            *arg;       /* type dependent argument */
 1354     int         ncounters;  /* for general statistics */
 1355     int         *counterindices; /* for general statistics */
 1356     uint64_t        *countervalues;  /* for general statistics */
 1357     isc_result_t        result;
 1358 } cache_dumparg_t;
 1359 
 1360 static void
 1361 getcounter(isc_statscounter_t counter, uint64_t val, void *arg) {
 1362     cache_dumparg_t *dumparg = arg;
 1363 
 1364     REQUIRE(counter < dumparg->ncounters);
 1365     dumparg->countervalues[counter] = val;
 1366 }
 1367 
 1368 static void
 1369 getcounters(isc_stats_t *stats, isc_statsformat_t type, int ncounters,
 1370         int *indices, uint64_t *values)
 1371 {
 1372     cache_dumparg_t dumparg;
 1373 
 1374     memset(values, 0, sizeof(values[0]) * ncounters);
 1375 
 1376     dumparg.type = type;
 1377     dumparg.ncounters = ncounters;
 1378     dumparg.counterindices = indices;
 1379     dumparg.countervalues = values;
 1380 
 1381     isc_stats_dump(stats, getcounter, &dumparg, ISC_STATSDUMP_VERBOSE);
 1382 }
 1383 
 1384 void
 1385 dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) {
 1386     int indices[dns_cachestatscounter_max];
 1387     uint64_t values[dns_cachestatscounter_max];
 1388 
 1389     REQUIRE(VALID_CACHE(cache));
 1390 
 1391     getcounters(cache->stats, isc_statsformat_file,
 1392             dns_cachestatscounter_max, indices, values);
 1393 
 1394     fprintf(fp, "%20" PRIu64 " %s\n",
 1395         values[dns_cachestatscounter_hits],
 1396         "cache hits");
 1397     fprintf(fp, "%20" PRIu64 " %s\n",
 1398         values[dns_cachestatscounter_misses],
 1399         "cache misses");
 1400     fprintf(fp, "%20" PRIu64 " %s\n",
 1401         values[dns_cachestatscounter_queryhits],
 1402         "cache hits (from query)");
 1403     fprintf(fp, "%20" PRIu64 " %s\n",
 1404         values[dns_cachestatscounter_querymisses],
 1405         "cache misses (from query)");
 1406     fprintf(fp, "%20" PRIu64 " %s\n",
 1407         values[dns_cachestatscounter_deletelru],
 1408         "cache records deleted due to memory exhaustion");
 1409     fprintf(fp, "%20" PRIu64 " %s\n",
 1410         values[dns_cachestatscounter_deletettl],
 1411         "cache records deleted due to TTL expiration");
 1412     fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db),
 1413         "cache database nodes");
 1414     fprintf(fp, "%20" PRIu64 " %s\n",
 1415         (uint64_t) dns_db_hashsize(cache->db),
 1416         "cache database hash buckets");
 1417 
 1418     fprintf(fp, "%20" PRIu64 " %s\n",
 1419         (uint64_t) isc_mem_total(cache->mctx),
 1420         "cache tree memory total");
 1421     fprintf(fp, "%20" PRIu64 " %s\n",
 1422         (uint64_t) isc_mem_inuse(cache->mctx),
 1423         "cache tree memory in use");
 1424     fprintf(fp, "%20" PRIu64 " %s\n",
 1425         (uint64_t) isc_mem_maxinuse(cache->mctx),
 1426         "cache tree highest memory in use");
 1427 
 1428     fprintf(fp, "%20" PRIu64 " %s\n",
 1429         (uint64_t) isc_mem_total(cache->hmctx),
 1430         "cache heap memory total");
 1431     fprintf(fp, "%20" PRIu64 " %s\n",
 1432         (uint64_t) isc_mem_inuse(cache->hmctx),
 1433         "cache heap memory in use");
 1434     fprintf(fp, "%20" PRIu64 " %s\n",
 1435         (uint64_t) isc_mem_maxinuse(cache->hmctx),
 1436         "cache heap highest memory in use");
 1437 }
 1438 
 1439 #ifdef HAVE_LIBXML2
 1440 #define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0)
 1441 static int
 1442 renderstat(const char *name, uint64_t value, xmlTextWriterPtr writer) {
 1443     int xmlrc;
 1444 
 1445     TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
 1446     TRY0(xmlTextWriterWriteAttribute(writer,
 1447                      ISC_XMLCHAR "name", ISC_XMLCHAR name));
 1448     TRY0(xmlTextWriterWriteFormatString(writer,
 1449                         "%" PRIu64,
 1450                         value));
 1451     TRY0(xmlTextWriterEndElement(writer)); /* counter */
 1452 
 1453 error:
 1454     return (xmlrc);
 1455 }
 1456 
 1457 int
 1458 dns_cache_renderxml(dns_cache_t *cache, xmlTextWriterPtr writer) {
 1459     int indices[dns_cachestatscounter_max];
 1460     uint64_t values[dns_cachestatscounter_max];
 1461     int xmlrc;
 1462 
 1463     REQUIRE(VALID_CACHE(cache));
 1464 
 1465     getcounters(cache->stats, isc_statsformat_file,
 1466             dns_cachestatscounter_max, indices, values);
 1467     TRY0(renderstat("CacheHits",
 1468            values[dns_cachestatscounter_hits], writer));
 1469     TRY0(renderstat("CacheMisses",
 1470            values[dns_cachestatscounter_misses], writer));
 1471     TRY0(renderstat("QueryHits",
 1472            values[dns_cachestatscounter_queryhits], writer));
 1473     TRY0(renderstat("QueryMisses",
 1474            values[dns_cachestatscounter_querymisses], writer));
 1475     TRY0(renderstat("DeleteLRU",
 1476            values[dns_cachestatscounter_deletelru], writer));
 1477     TRY0(renderstat("DeleteTTL",
 1478            values[dns_cachestatscounter_deletettl], writer));
 1479 
 1480     TRY0(renderstat("CacheNodes", dns_db_nodecount(cache->db), writer));
 1481     TRY0(renderstat("CacheBuckets", dns_db_hashsize(cache->db), writer));
 1482 
 1483     TRY0(renderstat("TreeMemTotal", isc_mem_total(cache->mctx), writer));
 1484     TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->mctx), writer));
 1485     TRY0(renderstat("TreeMemMax", isc_mem_maxinuse(cache->mctx), writer));
 1486 
 1487     TRY0(renderstat("HeapMemTotal", isc_mem_total(cache->hmctx), writer));
 1488     TRY0(renderstat("HeapMemInUse", isc_mem_inuse(cache->hmctx), writer));
 1489     TRY0(renderstat("HeapMemMax", isc_mem_maxinuse(cache->hmctx), writer));
 1490 error:
 1491     return (xmlrc);
 1492 }
 1493 #endif
 1494 
 1495 #ifdef HAVE_JSON
 1496 #define CHECKMEM(m) do { \
 1497     if (m == NULL) { \
 1498         result = ISC_R_NOMEMORY;\
 1499         goto error;\
 1500     } \
 1501 } while(0)
 1502 
 1503 isc_result_t
 1504 dns_cache_renderjson(dns_cache_t *cache, json_object *cstats) {
 1505     isc_result_t result = ISC_R_SUCCESS;
 1506     int indices[dns_cachestatscounter_max];
 1507     uint64_t values[dns_cachestatscounter_max];
 1508     json_object *obj;
 1509 
 1510     REQUIRE(VALID_CACHE(cache));
 1511 
 1512     getcounters(cache->stats, isc_statsformat_file,
 1513             dns_cachestatscounter_max, indices, values);
 1514 
 1515     obj = json_object_new_int64(values[dns_cachestatscounter_hits]);
 1516     CHECKMEM(obj);
 1517     json_object_object_add(cstats, "CacheHits", obj);
 1518 
 1519     obj = json_object_new_int64(values[dns_cachestatscounter_misses]);
 1520     CHECKMEM(obj);
 1521     json_object_object_add(cstats, "CacheMisses", obj);
 1522 
 1523     obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]);
 1524     CHECKMEM(obj);
 1525     json_object_object_add(cstats, "QueryHits", obj);
 1526 
 1527     obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]);
 1528     CHECKMEM(obj);
 1529     json_object_object_add(cstats, "QueryMisses", obj);
 1530 
 1531     obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]);
 1532     CHECKMEM(obj);
 1533     json_object_object_add(cstats, "DeleteLRU", obj);
 1534 
 1535     obj = json_object_new_int64(values[dns_cachestatscounter_deletettl]);
 1536     CHECKMEM(obj);
 1537     json_object_object_add(cstats, "DeleteTTL", obj);
 1538 
 1539     obj = json_object_new_int64(dns_db_nodecount(cache->db));
 1540     CHECKMEM(obj);
 1541     json_object_object_add(cstats, "CacheNodes", obj);
 1542 
 1543     obj = json_object_new_int64(dns_db_hashsize(cache->db));
 1544     CHECKMEM(obj);
 1545     json_object_object_add(cstats, "CacheBuckets", obj);
 1546 
 1547     obj = json_object_new_int64(isc_mem_total(cache->mctx));
 1548     CHECKMEM(obj);
 1549     json_object_object_add(cstats, "TreeMemTotal", obj);
 1550 
 1551     obj = json_object_new_int64(isc_mem_inuse(cache->mctx));
 1552     CHECKMEM(obj);
 1553     json_object_object_add(cstats, "TreeMemInUse", obj);
 1554 
 1555     obj = json_object_new_int64(isc_mem_maxinuse(cache->mctx));
 1556     CHECKMEM(obj);
 1557     json_object_object_add(cstats, "TreeMemMax", obj);
 1558 
 1559     obj = json_object_new_int64(isc_mem_total(cache->hmctx));
 1560     CHECKMEM(obj);
 1561     json_object_object_add(cstats, "HeapMemTotal", obj);
 1562 
 1563     obj = json_object_new_int64(isc_mem_inuse(cache->hmctx));
 1564     CHECKMEM(obj);
 1565     json_object_object_add(cstats, "HeapMemInUse", obj);
 1566 
 1567     obj = json_object_new_int64(isc_mem_maxinuse(cache->hmctx));
 1568     CHECKMEM(obj);
 1569     json_object_object_add(cstats, "HeapMemMax", obj);
 1570 
 1571     result = ISC_R_SUCCESS;
 1572 error:
 1573     return (result);
 1574 }
 1575 #endif