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