1 /* 2 * nsd-mem.c -- nsd-mem(8) 3 * 4 * Copyright (c) 2013, NLnet Labs. All rights reserved. 5 * 6 * See LICENSE for the license. 7 * 8 */ 9 10 #include "config.h" 11 12 #include <assert.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <time.h> 17 #include <unistd.h> 18 #include <errno.h> 19 20 #include "nsd.h" 21 #include "tsig.h" 22 #include "options.h" 23 #include "namedb.h" 24 #include "difffile.h" 25 #include "util.h" 26 27 struct nsd nsd; 28 29 /* 30 * Print the help text. 31 * 32 */ 33 static void 34 usage (void) 35 { 36 fprintf(stderr, "Usage: nsd-mem [-c configfile]\n"); 37 fprintf(stderr, "Version %s. Report bugs to <%s>.\n", 38 PACKAGE_VERSION, PACKAGE_BUGREPORT); 39 } 40 41 /* zone memory structure */ 42 struct zone_mem { 43 /* size of data (allocated in db.region) */ 44 size_t data; 45 /* unused space (in db.region) due to alignment */ 46 size_t data_unused; 47 48 /* count of number of domains */ 49 size_t domaincount; 50 }; 51 52 /* total memory structure */ 53 struct tot_mem { 54 /* size of data (allocated in db.region) */ 55 size_t data; 56 /* unused space (in db.region) due to alignment */ 57 size_t data_unused; 58 59 /* count of number of domains */ 60 size_t domaincount; 61 62 /* options data */ 63 size_t opt_data; 64 /* unused in options region */ 65 size_t opt_unused; 66 /* dname compression table */ 67 size_t compresstable; 68 #ifdef RATELIMIT 69 /* size of rrl tables */ 70 size_t rrl; 71 #endif 72 73 /* total ram usage */ 74 size_t ram; 75 }; 76 77 static void 78 account_zone(struct namedb* db, struct zone_mem* zmem) 79 { 80 zmem->data = region_get_mem(db->region); 81 zmem->data_unused = region_get_mem_unused(db->region); 82 zmem->domaincount = domain_table_count(db->domains); 83 } 84 85 static void 86 pretty_mem(size_t x, const char* s) 87 { 88 char buf[32]; 89 memset(buf, 0, sizeof(buf)); 90 if(snprintf(buf, sizeof(buf), "%12lld", (long long)x) > 12) { 91 printf("%12lld %s\n", (long long)x, s); 92 return; 93 } 94 printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c %s\n", 95 buf[0], buf[1], buf[2], (buf[2]==' '?' ':'.'), 96 buf[3], buf[4], buf[5], (buf[5]==' '?' ':'.'), 97 buf[6], buf[7], buf[8], (buf[8]==' '?' ':'.'), 98 buf[9], buf[10], buf[11], s); 99 } 100 101 static void 102 print_zone_mem(struct zone_mem* z) 103 { 104 pretty_mem(z->data, "zone data"); 105 pretty_mem(z->data_unused, "zone unused space (due to alignment)"); 106 } 107 108 static void 109 account_total(struct nsd_options* opt, struct tot_mem* t) 110 { 111 t->opt_data = region_get_mem(opt->region); 112 t->opt_unused = region_get_mem_unused(opt->region); 113 t->compresstable = sizeof(uint16_t) * 114 (t->domaincount + 1 + EXTRA_DOMAIN_NUMBERS); 115 t->compresstable *= opt->server_count; 116 117 #ifdef RATELIMIT 118 #define SIZE_RRL_BUCKET (8 + 4 + 4 + 4 + 4 + 2) 119 t->rrl = opt->rrl_size * SIZE_RRL_BUCKET; 120 t->rrl *= opt->server_count; 121 #endif 122 123 t->ram = t->data + t->data_unused + t->opt_data + t->opt_unused + 124 t->compresstable; 125 #ifdef RATELIMIT 126 t->ram += t->rrl; 127 #endif 128 } 129 130 static void 131 print_tot_mem(struct tot_mem* t) 132 { 133 printf("\ntotal\n"); 134 pretty_mem(t->data, "data"); 135 pretty_mem(t->data_unused, "unused space (due to alignment)"); 136 pretty_mem(t->opt_data, "options"); 137 pretty_mem(t->opt_unused, "options unused space (due to alignment)"); 138 pretty_mem(t->compresstable, "name table (depends on servercount)"); 139 #ifdef RATELIMIT 140 pretty_mem(t->rrl, "RRL table (depends on servercount)"); 141 #endif 142 printf("\nsummary\n"); 143 144 pretty_mem(t->ram, "ram usage (excl space for buffers)"); 145 } 146 147 static void 148 add_mem(struct tot_mem* t, struct zone_mem* z) 149 { 150 t->data += z->data; 151 t->data_unused += z->data_unused; 152 t->domaincount += z->domaincount; 153 } 154 155 static void 156 check_zone_mem(const char* tf, struct zone_options* zo, 157 struct nsd_options* opt, struct tot_mem* totmem) 158 { 159 struct nsd nsd; 160 struct namedb* db; 161 const dname_type* dname = (const dname_type*)zo->node.key; 162 zone_type* zone; 163 struct udb_base* taskudb; 164 udb_ptr last_task; 165 struct zone_mem zmem; 166 167 printf("zone %s\n", zo->name); 168 169 /* init*/ 170 memset(&zmem, 0, sizeof(zmem)); 171 memset(&nsd, 0, sizeof(nsd)); 172 nsd.db = db = namedb_open(opt); 173 if(!db) error("cannot open namedb"); 174 zone = namedb_zone_create(db, dname, zo); 175 taskudb = task_file_create(tf); 176 udb_ptr_init(&last_task, taskudb); 177 178 /* read the zone */ 179 namedb_read_zonefile(&nsd, zone, taskudb, &last_task); 180 181 /* account the memory for this zone */ 182 account_zone(db, &zmem); 183 184 /* pretty print the memory for this zone */ 185 print_zone_mem(&zmem); 186 187 /* delete the zone from memory */ 188 namedb_close(db); 189 udb_base_free(taskudb); 190 unlink(tf); 191 192 /* add up totals */ 193 add_mem(totmem, &zmem); 194 } 195 196 static void 197 check_mem(struct nsd_options* opt) 198 { 199 struct tot_mem totmem; 200 struct zone_options* zo; 201 char tf[512]; 202 memset(&totmem, 0, sizeof(totmem)); 203 snprintf(tf, sizeof(tf), "./nsd-mem-task-%u.db", (unsigned)getpid()); 204 205 /* read all zones and account memory */ 206 RBTREE_FOR(zo, struct zone_options*, opt->zone_options) { 207 check_zone_mem(tf, zo, opt, &totmem); 208 } 209 210 /* calculate more total statistics */ 211 account_total(opt, &totmem); 212 /* print statistics */ 213 print_tot_mem(&totmem); 214 } 215 216 /* dummy functions to link */ 217 struct nsd; 218 int writepid(struct nsd * ATTR_UNUSED(nsd)) 219 { 220 return 0; 221 } 222 void unlinkpid(const char * ATTR_UNUSED(file)) 223 { 224 } 225 void bind8_stats(struct nsd * ATTR_UNUSED(nsd)) 226 { 227 } 228 229 void sig_handler(int ATTR_UNUSED(sig)) 230 { 231 } 232 233 extern char *optarg; 234 extern int optind; 235 236 int 237 main(int argc, char *argv[]) 238 { 239 /* Scratch variables... */ 240 int c; 241 struct nsd nsd; 242 const char *configfile = CONFIGFILE; 243 memset(&nsd, 0, sizeof(nsd)); 244 245 log_init("nsd-mem"); 246 247 /* Parse the command line... */ 248 while ((c = getopt(argc, argv, "c:h" 249 )) != -1) { 250 switch (c) { 251 case 'c': 252 configfile = optarg; 253 break; 254 case 'h': 255 usage(); 256 exit(0); 257 case '?': 258 default: 259 usage(); 260 exit(1); 261 } 262 } 263 argc -= optind; 264 /* argv += optind; move along argv for positional arguments */ 265 266 /* Commandline parse error */ 267 if (argc != 0) { 268 usage(); 269 exit(1); 270 } 271 272 /* Read options */ 273 nsd.options = nsd_options_create(region_create_custom(xalloc, free, 274 DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE, 275 DEFAULT_INITIAL_CLEANUP_SIZE, 1)); 276 tsig_init(nsd.options->region); 277 if(!parse_options_file(nsd.options, configfile, NULL, NULL)) { 278 error("could not read config: %s\n", configfile); 279 } 280 if(!parse_zone_list_file(nsd.options)) { 281 error("could not read zonelist file %s\n", 282 nsd.options->zonelistfile); 283 } 284 if (verbosity == 0) 285 verbosity = nsd.options->verbosity; 286 287 #ifdef HAVE_CHROOT 288 if(nsd.chrootdir == 0) nsd.chrootdir = nsd.options->chroot; 289 #ifdef CHROOTDIR 290 /* if still no chrootdir, fallback to default */ 291 if(nsd.chrootdir == 0) nsd.chrootdir = CHROOTDIR; 292 #endif /* CHROOTDIR */ 293 #endif /* HAVE_CHROOT */ 294 if(nsd.options->zonesdir && nsd.options->zonesdir[0]) { 295 if(chdir(nsd.options->zonesdir)) { 296 error("cannot chdir to '%s': %s", 297 nsd.options->zonesdir, strerror(errno)); 298 } 299 DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed directory to %s", 300 nsd.options->zonesdir)); 301 } 302 303 /* Chroot */ 304 #ifdef HAVE_CHROOT 305 if (nsd.chrootdir && strlen(nsd.chrootdir)) { 306 if(chdir(nsd.chrootdir)) { 307 error("unable to chdir to chroot: %s", strerror(errno)); 308 } 309 DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed root directory to %s", 310 nsd.chrootdir)); 311 } 312 #endif /* HAVE_CHROOT */ 313 314 check_mem(nsd.options); 315 316 exit(0); 317 } 318