1 /* $NetBSD: cache.c,v 1.13 2025/01/26 16:25:22 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 /*! \file */ 17 18 #include <inttypes.h> 19 #include <stdbool.h> 20 21 #include <isc/loop.h> 22 #include <isc/mem.h> 23 #include <isc/refcount.h> 24 #include <isc/result.h> 25 #include <isc/stats.h> 26 #include <isc/string.h> 27 #include <isc/time.h> 28 #include <isc/timer.h> 29 #include <isc/util.h> 30 31 #include <dns/cache.h> 32 #include <dns/db.h> 33 #include <dns/dbiterator.h> 34 #include <dns/log.h> 35 #include <dns/masterdump.h> 36 #include <dns/rdata.h> 37 #include <dns/rdataset.h> 38 #include <dns/rdatasetiter.h> 39 #include <dns/stats.h> 40 41 #ifdef HAVE_JSON_C 42 #include <json_object.h> 43 #endif /* HAVE_JSON_C */ 44 45 #ifdef HAVE_LIBXML2 46 #include <libxml/xmlwriter.h> 47 #define ISC_XMLCHAR (const xmlChar *) 48 #endif /* HAVE_LIBXML2 */ 49 50 #define CACHE_MAGIC ISC_MAGIC('$', '$', '$', '$') 51 #define VALID_CACHE(cache) ISC_MAGIC_VALID(cache, CACHE_MAGIC) 52 53 /* 54 * DNS_CACHE_MINSIZE is how many bytes is the floor for 55 * dns_cache_setcachesize(). 56 */ 57 #define DNS_CACHE_MINSIZE 2097152U /*%< Bytes. 2097152 = 2 MB */ 58 59 /*** 60 *** Types 61 ***/ 62 63 /*% 64 * The actual cache object. 65 */ 66 67 struct dns_cache { 68 /* Unlocked. */ 69 unsigned int magic; 70 isc_mutex_t lock; 71 isc_mem_t *mctx; /* Memory context for the dns_cache object */ 72 isc_mem_t *hmctx; /* Heap memory */ 73 isc_mem_t *tmctx; /* Tree memory */ 74 isc_loopmgr_t *loopmgr; 75 char *name; 76 isc_refcount_t references; 77 78 /* Locked by 'lock'. */ 79 dns_rdataclass_t rdclass; 80 dns_db_t *db; 81 size_t size; 82 dns_ttl_t serve_stale_ttl; 83 dns_ttl_t serve_stale_refresh; 84 isc_stats_t *stats; 85 uint32_t maxrrperset; 86 uint32_t maxtypepername; 87 }; 88 89 /*** 90 *** Functions 91 ***/ 92 93 static isc_result_t 94 cache_create_db(dns_cache_t *cache, dns_db_t **dbp, isc_mem_t **tmctxp, 95 isc_mem_t **hmctxp) { 96 isc_result_t result; 97 char *argv[1] = { 0 }; 98 dns_db_t *db = NULL; 99 isc_mem_t *tmctx = NULL, *hmctx = NULL; 100 101 /* 102 * This will be the cache memory context, which is subject 103 * to cleaning when the configured memory limits are exceeded. 104 */ 105 isc_mem_create(&tmctx); 106 isc_mem_setname(tmctx, "cache"); 107 108 /* 109 * This will be passed to RBTDB to use for heaps. This is separate 110 * from the main cache memory because it can grow quite large under 111 * heavy load and could otherwise cause the cache to be cleaned too 112 * aggressively. 113 */ 114 isc_mem_create(&hmctx); 115 isc_mem_setname(hmctx, "cache_heap"); 116 117 /* 118 * For databases of type "qpcache" or "rbt" (which are the 119 * only cache implementations currently in existence) we pass 120 * hmctx to dns_db_create() via argv[0]. 121 */ 122 argv[0] = (char *)hmctx; 123 result = dns_db_create(tmctx, CACHEDB_DEFAULT, dns_rootname, 124 dns_dbtype_cache, cache->rdclass, 1, argv, &db); 125 if (result != ISC_R_SUCCESS) { 126 goto cleanup_mctx; 127 } 128 result = dns_db_setcachestats(db, cache->stats); 129 if (result != ISC_R_SUCCESS) { 130 goto cleanup_db; 131 } 132 133 dns_db_setservestalettl(db, cache->serve_stale_ttl); 134 dns_db_setservestalerefresh(db, cache->serve_stale_refresh); 135 dns_db_setmaxrrperset(db, cache->maxrrperset); 136 dns_db_setmaxtypepername(db, cache->maxtypepername); 137 138 /* 139 * XXX this is only used by the RBT cache, and can 140 * be removed when it is. 141 */ 142 dns_db_setloop(db, isc_loop_main(cache->loopmgr)); 143 144 *dbp = db; 145 *hmctxp = hmctx; 146 *tmctxp = tmctx; 147 148 return ISC_R_SUCCESS; 149 150 cleanup_db: 151 dns_db_detach(&db); 152 cleanup_mctx: 153 isc_mem_detach(&hmctx); 154 isc_mem_detach(&tmctx); 155 156 return result; 157 } 158 159 static void 160 cache_destroy(dns_cache_t *cache) { 161 isc_stats_detach(&cache->stats); 162 isc_mutex_destroy(&cache->lock); 163 isc_mem_free(cache->mctx, cache->name); 164 if (cache->hmctx != NULL) { 165 isc_mem_detach(&cache->hmctx); 166 } 167 if (cache->tmctx != NULL) { 168 isc_mem_detach(&cache->tmctx); 169 } 170 isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache)); 171 } 172 173 isc_result_t 174 dns_cache_create(isc_loopmgr_t *loopmgr, dns_rdataclass_t rdclass, 175 const char *cachename, isc_mem_t *mctx, dns_cache_t **cachep) { 176 isc_result_t result; 177 dns_cache_t *cache = NULL; 178 179 REQUIRE(loopmgr != NULL); 180 REQUIRE(cachename != NULL); 181 REQUIRE(cachep != NULL && *cachep == NULL); 182 183 cache = isc_mem_get(mctx, sizeof(*cache)); 184 *cache = (dns_cache_t){ 185 .rdclass = rdclass, 186 .name = isc_mem_strdup(mctx, cachename), 187 .loopmgr = loopmgr, 188 .references = ISC_REFCOUNT_INITIALIZER(1), 189 .magic = CACHE_MAGIC, 190 }; 191 192 isc_mutex_init(&cache->lock); 193 isc_mem_attach(mctx, &cache->mctx); 194 195 isc_stats_create(mctx, &cache->stats, dns_cachestatscounter_max); 196 197 /* 198 * Create the database 199 */ 200 result = cache_create_db(cache, &cache->db, &cache->tmctx, 201 &cache->hmctx); 202 if (result != ISC_R_SUCCESS) { 203 goto cleanup; 204 } 205 206 *cachep = cache; 207 return ISC_R_SUCCESS; 208 209 cleanup: 210 cache_destroy(cache); 211 return result; 212 } 213 214 static void 215 cache_cleanup(dns_cache_t *cache) { 216 REQUIRE(VALID_CACHE(cache)); 217 218 isc_refcount_destroy(&cache->references); 219 cache->magic = 0; 220 221 isc_mem_clearwater(cache->tmctx); 222 dns_db_detach(&cache->db); 223 224 cache_destroy(cache); 225 } 226 227 #if DNS_CACHE_TRACE 228 ISC_REFCOUNT_TRACE_IMPL(dns_cache, cache_cleanup); 229 #else 230 ISC_REFCOUNT_IMPL(dns_cache, cache_cleanup); 231 #endif 232 233 void 234 dns_cache_attachdb(dns_cache_t *cache, dns_db_t **dbp) { 235 REQUIRE(VALID_CACHE(cache)); 236 REQUIRE(dbp != NULL && *dbp == NULL); 237 REQUIRE(cache->db != NULL); 238 239 LOCK(&cache->lock); 240 dns_db_attach(cache->db, dbp); 241 UNLOCK(&cache->lock); 242 } 243 244 const char * 245 dns_cache_getname(dns_cache_t *cache) { 246 REQUIRE(VALID_CACHE(cache)); 247 248 return cache->name; 249 } 250 251 static void 252 updatewater(dns_cache_t *cache) { 253 size_t hi = cache->size - (cache->size >> 3); /* ~ 7/8ths. */ 254 size_t lo = cache->size - (cache->size >> 2); /* ~ 3/4ths. */ 255 if (cache->size == 0U || hi == 0U || lo == 0U) { 256 isc_mem_clearwater(cache->tmctx); 257 } else { 258 isc_mem_setwater(cache->tmctx, hi, lo); 259 } 260 } 261 262 void 263 dns_cache_setcachesize(dns_cache_t *cache, size_t size) { 264 REQUIRE(VALID_CACHE(cache)); 265 266 /* 267 * Impose a minimum cache size; pathological things happen if there 268 * is too little room. 269 */ 270 if (size != 0U && size < DNS_CACHE_MINSIZE) { 271 size = DNS_CACHE_MINSIZE; 272 } 273 274 LOCK(&cache->lock); 275 cache->size = size; 276 updatewater(cache); 277 UNLOCK(&cache->lock); 278 } 279 280 size_t 281 dns_cache_getcachesize(dns_cache_t *cache) { 282 size_t size; 283 284 REQUIRE(VALID_CACHE(cache)); 285 286 LOCK(&cache->lock); 287 size = cache->size; 288 UNLOCK(&cache->lock); 289 290 return size; 291 } 292 293 void 294 dns_cache_setservestalettl(dns_cache_t *cache, dns_ttl_t ttl) { 295 REQUIRE(VALID_CACHE(cache)); 296 297 LOCK(&cache->lock); 298 cache->serve_stale_ttl = ttl; 299 UNLOCK(&cache->lock); 300 301 (void)dns_db_setservestalettl(cache->db, ttl); 302 } 303 304 dns_ttl_t 305 dns_cache_getservestalettl(dns_cache_t *cache) { 306 dns_ttl_t ttl; 307 isc_result_t result; 308 309 REQUIRE(VALID_CACHE(cache)); 310 311 /* 312 * Could get it straight from the dns_cache_t, but use db 313 * to confirm the value that the db is really using. 314 */ 315 result = dns_db_getservestalettl(cache->db, &ttl); 316 return result == ISC_R_SUCCESS ? ttl : 0; 317 } 318 319 void 320 dns_cache_setservestalerefresh(dns_cache_t *cache, dns_ttl_t interval) { 321 REQUIRE(VALID_CACHE(cache)); 322 323 LOCK(&cache->lock); 324 cache->serve_stale_refresh = interval; 325 UNLOCK(&cache->lock); 326 327 (void)dns_db_setservestalerefresh(cache->db, interval); 328 } 329 330 dns_ttl_t 331 dns_cache_getservestalerefresh(dns_cache_t *cache) { 332 isc_result_t result; 333 dns_ttl_t interval; 334 335 REQUIRE(VALID_CACHE(cache)); 336 337 result = dns_db_getservestalerefresh(cache->db, &interval); 338 return result == ISC_R_SUCCESS ? interval : 0; 339 } 340 341 isc_result_t 342 dns_cache_flush(dns_cache_t *cache) { 343 dns_db_t *db = NULL, *olddb; 344 isc_mem_t *tmctx = NULL, *oldtmctx; 345 isc_mem_t *hmctx = NULL, *oldhmctx; 346 isc_result_t result; 347 348 result = cache_create_db(cache, &db, &tmctx, &hmctx); 349 if (result != ISC_R_SUCCESS) { 350 return result; 351 } 352 353 LOCK(&cache->lock); 354 isc_mem_clearwater(cache->tmctx); 355 oldhmctx = cache->hmctx; 356 cache->hmctx = hmctx; 357 oldtmctx = cache->tmctx; 358 cache->tmctx = tmctx; 359 updatewater(cache); 360 olddb = cache->db; 361 cache->db = db; 362 UNLOCK(&cache->lock); 363 364 dns_db_detach(&olddb); 365 isc_mem_detach(&oldhmctx); 366 isc_mem_detach(&oldtmctx); 367 368 return ISC_R_SUCCESS; 369 } 370 371 static isc_result_t 372 clearnode(dns_db_t *db, dns_dbnode_t *node) { 373 isc_result_t result; 374 dns_rdatasetiter_t *iter = NULL; 375 376 result = dns_db_allrdatasets(db, node, NULL, DNS_DB_STALEOK, 377 (isc_stdtime_t)0, &iter); 378 if (result != ISC_R_SUCCESS) { 379 return result; 380 } 381 382 for (result = dns_rdatasetiter_first(iter); result == ISC_R_SUCCESS; 383 result = dns_rdatasetiter_next(iter)) 384 { 385 dns_rdataset_t rdataset; 386 dns_rdataset_init(&rdataset); 387 388 dns_rdatasetiter_current(iter, &rdataset); 389 result = dns_db_deleterdataset(db, node, NULL, rdataset.type, 390 rdataset.covers); 391 dns_rdataset_disassociate(&rdataset); 392 if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) { 393 break; 394 } 395 } 396 397 if (result == ISC_R_NOMORE) { 398 result = ISC_R_SUCCESS; 399 } 400 401 dns_rdatasetiter_destroy(&iter); 402 return result; 403 } 404 405 static isc_result_t 406 cleartree(dns_db_t *db, const dns_name_t *name) { 407 isc_result_t result, answer = ISC_R_SUCCESS; 408 dns_dbiterator_t *iter = NULL; 409 dns_dbnode_t *node = NULL, *top = NULL; 410 dns_fixedname_t fnodename; 411 dns_name_t *nodename; 412 413 /* 414 * Create the node if it doesn't exist so dns_dbiterator_seek() 415 * can find it. We will continue even if this fails. 416 */ 417 (void)dns_db_findnode(db, name, true, &top); 418 419 nodename = dns_fixedname_initname(&fnodename); 420 421 result = dns_db_createiterator(db, 0, &iter); 422 if (result != ISC_R_SUCCESS) { 423 goto cleanup; 424 } 425 426 result = dns_dbiterator_seek(iter, name); 427 if (result == DNS_R_PARTIALMATCH) { 428 result = dns_dbiterator_next(iter); 429 } 430 if (result != ISC_R_SUCCESS) { 431 goto cleanup; 432 } 433 434 while (result == ISC_R_SUCCESS) { 435 result = dns_dbiterator_current(iter, &node, nodename); 436 if (result == DNS_R_NEWORIGIN) { 437 result = ISC_R_SUCCESS; 438 } 439 if (result != ISC_R_SUCCESS) { 440 goto cleanup; 441 } 442 /* 443 * Are we done? 444 */ 445 if (!dns_name_issubdomain(nodename, name)) { 446 goto cleanup; 447 } 448 449 /* 450 * If clearnode fails record and move onto the next node. 451 */ 452 result = clearnode(db, node); 453 if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) { 454 answer = result; 455 } 456 dns_db_detachnode(db, &node); 457 result = dns_dbiterator_next(iter); 458 } 459 460 cleanup: 461 if (result == ISC_R_NOMORE || result == ISC_R_NOTFOUND) { 462 result = ISC_R_SUCCESS; 463 } 464 if (result != ISC_R_SUCCESS && answer == ISC_R_SUCCESS) { 465 answer = result; 466 } 467 if (node != NULL) { 468 dns_db_detachnode(db, &node); 469 } 470 if (iter != NULL) { 471 dns_dbiterator_destroy(&iter); 472 } 473 if (top != NULL) { 474 dns_db_detachnode(db, &top); 475 } 476 477 return answer; 478 } 479 480 isc_result_t 481 dns_cache_flushname(dns_cache_t *cache, const dns_name_t *name) { 482 return dns_cache_flushnode(cache, name, false); 483 } 484 485 isc_result_t 486 dns_cache_flushnode(dns_cache_t *cache, const dns_name_t *name, bool tree) { 487 isc_result_t result; 488 dns_dbnode_t *node = NULL; 489 dns_db_t *db = NULL; 490 491 if (tree && dns_name_equal(name, dns_rootname)) { 492 return dns_cache_flush(cache); 493 } 494 495 LOCK(&cache->lock); 496 if (cache->db != NULL) { 497 dns_db_attach(cache->db, &db); 498 } 499 UNLOCK(&cache->lock); 500 if (db == NULL) { 501 return ISC_R_SUCCESS; 502 } 503 504 if (tree) { 505 result = cleartree(cache->db, name); 506 } else { 507 result = dns_db_findnode(cache->db, name, false, &node); 508 if (result == ISC_R_NOTFOUND) { 509 result = ISC_R_SUCCESS; 510 goto cleanup_db; 511 } 512 if (result != ISC_R_SUCCESS) { 513 goto cleanup_db; 514 } 515 result = clearnode(cache->db, node); 516 dns_db_detachnode(cache->db, &node); 517 } 518 519 cleanup_db: 520 dns_db_detach(&db); 521 return result; 522 } 523 524 isc_stats_t * 525 dns_cache_getstats(dns_cache_t *cache) { 526 REQUIRE(VALID_CACHE(cache)); 527 return cache->stats; 528 } 529 530 void 531 dns_cache_updatestats(dns_cache_t *cache, isc_result_t result) { 532 REQUIRE(VALID_CACHE(cache)); 533 if (cache->stats == NULL) { 534 return; 535 } 536 537 switch (result) { 538 case ISC_R_SUCCESS: 539 case DNS_R_NCACHENXDOMAIN: 540 case DNS_R_NCACHENXRRSET: 541 case DNS_R_CNAME: 542 case DNS_R_DNAME: 543 case DNS_R_GLUE: 544 case DNS_R_ZONECUT: 545 case DNS_R_COVERINGNSEC: 546 isc_stats_increment(cache->stats, 547 dns_cachestatscounter_queryhits); 548 break; 549 default: 550 isc_stats_increment(cache->stats, 551 dns_cachestatscounter_querymisses); 552 } 553 } 554 555 void 556 dns_cache_setmaxrrperset(dns_cache_t *cache, uint32_t value) { 557 REQUIRE(VALID_CACHE(cache)); 558 559 cache->maxrrperset = value; 560 if (cache->db != NULL) { 561 dns_db_setmaxrrperset(cache->db, value); 562 } 563 } 564 565 void 566 dns_cache_setmaxtypepername(dns_cache_t *cache, uint32_t value) { 567 REQUIRE(VALID_CACHE(cache)); 568 569 cache->maxtypepername = value; 570 if (cache->db != NULL) { 571 dns_db_setmaxtypepername(cache->db, value); 572 } 573 } 574 575 /* 576 * XXX: Much of the following code has been copied in from statschannel.c. 577 * We should refactor this into a generic function in stats.c that can be 578 * called from both places. 579 */ 580 typedef struct cache_dumparg { 581 isc_statsformat_t type; 582 void *arg; /* type dependent argument */ 583 int ncounters; /* for general statistics */ 584 int *counterindices; /* for general statistics */ 585 uint64_t *countervalues; /* for general statistics */ 586 isc_result_t result; 587 } cache_dumparg_t; 588 589 static void 590 getcounter(isc_statscounter_t counter, uint64_t val, void *arg) { 591 cache_dumparg_t *dumparg = arg; 592 593 REQUIRE(counter < dumparg->ncounters); 594 dumparg->countervalues[counter] = val; 595 } 596 597 static void 598 getcounters(isc_stats_t *stats, isc_statsformat_t type, int ncounters, 599 int *indices, uint64_t *values) { 600 cache_dumparg_t dumparg; 601 602 memset(values, 0, sizeof(values[0]) * ncounters); 603 604 dumparg.type = type; 605 dumparg.ncounters = ncounters; 606 dumparg.counterindices = indices; 607 dumparg.countervalues = values; 608 609 isc_stats_dump(stats, getcounter, &dumparg, ISC_STATSDUMP_VERBOSE); 610 } 611 612 void 613 dns_cache_dumpstats(dns_cache_t *cache, FILE *fp) { 614 int indices[dns_cachestatscounter_max]; 615 uint64_t values[dns_cachestatscounter_max]; 616 617 REQUIRE(VALID_CACHE(cache)); 618 619 getcounters(cache->stats, isc_statsformat_file, 620 dns_cachestatscounter_max, indices, values); 621 622 fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_hits], 623 "cache hits"); 624 fprintf(fp, "%20" PRIu64 " %s\n", values[dns_cachestatscounter_misses], 625 "cache misses"); 626 fprintf(fp, "%20" PRIu64 " %s\n", 627 values[dns_cachestatscounter_queryhits], 628 "cache hits (from query)"); 629 fprintf(fp, "%20" PRIu64 " %s\n", 630 values[dns_cachestatscounter_querymisses], 631 "cache misses (from query)"); 632 fprintf(fp, "%20" PRIu64 " %s\n", 633 values[dns_cachestatscounter_deletelru], 634 "cache records deleted due to memory exhaustion"); 635 fprintf(fp, "%20" PRIu64 " %s\n", 636 values[dns_cachestatscounter_deletettl], 637 "cache records deleted due to TTL expiration"); 638 fprintf(fp, "%20" PRIu64 " %s\n", 639 values[dns_cachestatscounter_coveringnsec], 640 "covering nsec returned"); 641 fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db, dns_dbtree_main), 642 "cache database nodes"); 643 fprintf(fp, "%20u %s\n", dns_db_nodecount(cache->db, dns_dbtree_nsec), 644 "cache NSEC auxiliary database nodes"); 645 fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)dns_db_hashsize(cache->db), 646 "cache database hash buckets"); 647 648 fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->tmctx), 649 "cache tree memory in use"); 650 651 fprintf(fp, "%20" PRIu64 " %s\n", (uint64_t)isc_mem_inuse(cache->hmctx), 652 "cache heap memory in use"); 653 } 654 655 #ifdef HAVE_LIBXML2 656 #define TRY0(a) \ 657 do { \ 658 xmlrc = (a); \ 659 if (xmlrc < 0) \ 660 goto error; \ 661 } while (0) 662 static int 663 renderstat(const char *name, uint64_t value, xmlTextWriterPtr writer) { 664 int xmlrc; 665 666 TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter")); 667 TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name", 668 ISC_XMLCHAR name)); 669 TRY0(xmlTextWriterWriteFormatString(writer, "%" PRIu64 "", value)); 670 TRY0(xmlTextWriterEndElement(writer)); /* counter */ 671 672 error: 673 return xmlrc; 674 } 675 676 int 677 dns_cache_renderxml(dns_cache_t *cache, void *writer0) { 678 int indices[dns_cachestatscounter_max]; 679 uint64_t values[dns_cachestatscounter_max]; 680 int xmlrc; 681 xmlTextWriterPtr writer = (xmlTextWriterPtr)writer0; 682 683 REQUIRE(VALID_CACHE(cache)); 684 685 getcounters(cache->stats, isc_statsformat_file, 686 dns_cachestatscounter_max, indices, values); 687 TRY0(renderstat("CacheHits", values[dns_cachestatscounter_hits], 688 writer)); 689 TRY0(renderstat("CacheMisses", values[dns_cachestatscounter_misses], 690 writer)); 691 TRY0(renderstat("QueryHits", values[dns_cachestatscounter_queryhits], 692 writer)); 693 TRY0(renderstat("QueryMisses", 694 values[dns_cachestatscounter_querymisses], writer)); 695 TRY0(renderstat("DeleteLRU", values[dns_cachestatscounter_deletelru], 696 writer)); 697 TRY0(renderstat("DeleteTTL", values[dns_cachestatscounter_deletettl], 698 writer)); 699 TRY0(renderstat("CoveringNSEC", 700 values[dns_cachestatscounter_coveringnsec], writer)); 701 702 TRY0(renderstat("CacheNodes", 703 dns_db_nodecount(cache->db, dns_dbtree_main), writer)); 704 TRY0(renderstat("CacheNSECNodes", 705 dns_db_nodecount(cache->db, dns_dbtree_nsec), writer)); 706 TRY0(renderstat("CacheBuckets", dns_db_hashsize(cache->db), writer)); 707 708 TRY0(renderstat("TreeMemInUse", isc_mem_inuse(cache->tmctx), writer)); 709 710 TRY0(renderstat("HeapMemInUse", isc_mem_inuse(cache->hmctx), writer)); 711 error: 712 return xmlrc; 713 } 714 #endif /* ifdef HAVE_LIBXML2 */ 715 716 #ifdef HAVE_JSON_C 717 #define CHECKMEM(m) \ 718 do { \ 719 if (m == NULL) { \ 720 result = ISC_R_NOMEMORY; \ 721 goto error; \ 722 } \ 723 } while (0) 724 725 isc_result_t 726 dns_cache_renderjson(dns_cache_t *cache, void *cstats0) { 727 isc_result_t result = ISC_R_SUCCESS; 728 int indices[dns_cachestatscounter_max]; 729 uint64_t values[dns_cachestatscounter_max]; 730 json_object *obj; 731 json_object *cstats = (json_object *)cstats0; 732 733 REQUIRE(VALID_CACHE(cache)); 734 735 getcounters(cache->stats, isc_statsformat_file, 736 dns_cachestatscounter_max, indices, values); 737 738 obj = json_object_new_int64(values[dns_cachestatscounter_hits]); 739 CHECKMEM(obj); 740 json_object_object_add(cstats, "CacheHits", obj); 741 742 obj = json_object_new_int64(values[dns_cachestatscounter_misses]); 743 CHECKMEM(obj); 744 json_object_object_add(cstats, "CacheMisses", obj); 745 746 obj = json_object_new_int64(values[dns_cachestatscounter_queryhits]); 747 CHECKMEM(obj); 748 json_object_object_add(cstats, "QueryHits", obj); 749 750 obj = json_object_new_int64(values[dns_cachestatscounter_querymisses]); 751 CHECKMEM(obj); 752 json_object_object_add(cstats, "QueryMisses", obj); 753 754 obj = json_object_new_int64(values[dns_cachestatscounter_deletelru]); 755 CHECKMEM(obj); 756 json_object_object_add(cstats, "DeleteLRU", obj); 757 758 obj = json_object_new_int64(values[dns_cachestatscounter_deletettl]); 759 CHECKMEM(obj); 760 json_object_object_add(cstats, "DeleteTTL", obj); 761 762 obj = json_object_new_int64(values[dns_cachestatscounter_coveringnsec]); 763 CHECKMEM(obj); 764 json_object_object_add(cstats, "CoveringNSEC", obj); 765 766 obj = json_object_new_int64( 767 dns_db_nodecount(cache->db, dns_dbtree_main)); 768 CHECKMEM(obj); 769 json_object_object_add(cstats, "CacheNodes", obj); 770 771 obj = json_object_new_int64( 772 dns_db_nodecount(cache->db, dns_dbtree_nsec)); 773 CHECKMEM(obj); 774 json_object_object_add(cstats, "CacheNSECNodes", obj); 775 776 obj = json_object_new_int64(dns_db_hashsize(cache->db)); 777 CHECKMEM(obj); 778 json_object_object_add(cstats, "CacheBuckets", obj); 779 780 obj = json_object_new_int64(isc_mem_inuse(cache->tmctx)); 781 CHECKMEM(obj); 782 json_object_object_add(cstats, "TreeMemInUse", obj); 783 784 obj = json_object_new_int64(isc_mem_inuse(cache->hmctx)); 785 CHECKMEM(obj); 786 json_object_object_add(cstats, "HeapMemInUse", obj); 787 788 result = ISC_R_SUCCESS; 789 error: 790 return result; 791 } 792 #endif /* ifdef HAVE_JSON_C */ 793