xref: /openbsd-src/usr.sbin/nsd/nsd-mem.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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 "udb.h"
25 #include "udbzone.h"
26 #include "util.h"
27 
28 static void error(const char *format, ...) ATTR_FORMAT(printf, 1, 2);
29 struct nsd nsd;
30 
31 /*
32  * Print the help text.
33  *
34  */
35 static void
36 usage (void)
37 {
38 	fprintf(stderr, "Usage: nsd-mem [-c configfile]\n");
39 	fprintf(stderr, "Version %s. Report bugs to <%s>.\n",
40 		PACKAGE_VERSION, PACKAGE_BUGREPORT);
41 }
42 
43 /*
44  * Something went wrong, give error messages and exit.
45  *
46  */
47 static void
48 error(const char *format, ...)
49 {
50 	va_list args;
51 	va_start(args, format);
52 	log_vmsg(LOG_ERR, format, args);
53 	va_end(args);
54 	exit(1);
55 }
56 
57 /* zone memory structure */
58 struct zone_mem {
59 	/* size of data (allocated in db.region) */
60 	size_t data;
61 	/* unused space (in db.region) due to alignment */
62 	size_t data_unused;
63 	/* udb data allocated */
64 	size_t udb_data;
65 	/* udb overhead (chunk2**x - data) */
66 	size_t udb_overhead;
67 
68 	/* count of number of domains */
69 	size_t domaincount;
70 };
71 
72 /* total memory structure */
73 struct tot_mem {
74 	/* size of data (allocated in db.region) */
75 	size_t data;
76 	/* unused space (in db.region) due to alignment */
77 	size_t data_unused;
78 	/* udb data allocated */
79 	size_t udb_data;
80 	/* udb overhead (chunk2**x - data) */
81 	size_t udb_overhead;
82 
83 	/* count of number of domains */
84 	size_t domaincount;
85 
86 	/* options data */
87 	size_t opt_data;
88 	/* unused in options region */
89 	size_t opt_unused;
90 	/* dname compression table */
91 	size_t compresstable;
92 #ifdef RATELIMIT
93 	/* size of rrl tables */
94 	size_t rrl;
95 #endif
96 
97 	/* total ram usage */
98 	size_t ram;
99 	/* total nsd.db disk usage */
100 	size_t disk;
101 };
102 
103 static void
104 account_zone(struct namedb* db, struct zone_mem* zmem)
105 {
106 	zmem->data = region_get_mem(db->region);
107 	zmem->data_unused = region_get_mem_unused(db->region);
108 	if(db->udb) {
109 		zmem->udb_data = (size_t)db->udb->alloc->disk->stat_data;
110 		zmem->udb_overhead = (size_t)(db->udb->alloc->disk->stat_alloc -
111 			db->udb->alloc->disk->stat_data);
112 	}
113 	zmem->domaincount = db->domains->nametree->count;
114 }
115 
116 static void
117 pretty_mem(size_t x, const char* s)
118 {
119 	char buf[32];
120 	memset(buf, 0, sizeof(buf));
121 	if(snprintf(buf, sizeof(buf), "%12lld", (long long)x) > 12) {
122 		printf("%12lld %s\n", (long long)x, s);
123 		return;
124 	}
125 	printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c %s\n",
126 		buf[0], buf[1], buf[2], (buf[2]==' '?' ':'.'),
127 		buf[3], buf[4], buf[5], (buf[5]==' '?' ':'.'),
128 		buf[6], buf[7], buf[8], (buf[8]==' '?' ':'.'),
129 		buf[9], buf[10], buf[11], s);
130 }
131 
132 static void
133 print_zone_mem(struct zone_mem* z)
134 {
135 	pretty_mem(z->data, "zone data");
136 	pretty_mem(z->data_unused, "zone unused space (due to alignment)");
137 	pretty_mem(z->udb_data, "data in nsd.db");
138 	pretty_mem(z->udb_overhead, "overhead in nsd.db");
139 }
140 
141 static void
142 account_total(nsd_options_t* opt, struct tot_mem* t)
143 {
144 	t->opt_data = region_get_mem(opt->region);
145 	t->opt_unused = region_get_mem_unused(opt->region);
146 	t->compresstable = sizeof(uint16_t) *
147 		(t->domaincount + 1 + EXTRA_DOMAIN_NUMBERS);
148 	t->compresstable *= opt->server_count;
149 
150 #ifdef RATELIMIT
151 #define SIZE_RRL_BUCKET (8 + 4 + 4 + 4 + 4 + 2)
152 	t->rrl = opt->rrl_size * SIZE_RRL_BUCKET;
153 	t->rrl *= opt->server_count;
154 #endif
155 
156 	t->ram = t->data + t->data_unused + t->opt_data + t->opt_unused +
157 		t->compresstable;
158 #ifdef RATELIMIT
159 	t->ram += t->rrl;
160 #endif
161 	t->disk = t->udb_data + t->udb_overhead;
162 }
163 
164 static void
165 print_tot_mem(struct tot_mem* t)
166 {
167 	printf("\ntotal\n");
168 	pretty_mem(t->data, "data");
169 	pretty_mem(t->data_unused, "unused space (due to alignment)");
170 	pretty_mem(t->opt_data, "options");
171 	pretty_mem(t->opt_unused, "options unused space (due to alignment)");
172 	pretty_mem(t->compresstable, "name table (depends on servercount)");
173 #ifdef RATELIMIT
174 	pretty_mem(t->rrl, "RRL table (depends on servercount)");
175 #endif
176 	pretty_mem(t->udb_data, "data in nsd.db");
177 	pretty_mem(t->udb_overhead, "overhead in nsd.db");
178 	printf("\nsummary\n");
179 
180 	pretty_mem(t->ram, "ram usage (excl space for buffers)");
181 	pretty_mem(t->disk, "disk usage (excl 12% space claimed for growth)");
182 }
183 
184 static void
185 add_mem(struct tot_mem* t, struct zone_mem* z)
186 {
187 	t->data += z->data;
188 	t->data_unused += z->data_unused;
189 	t->udb_data += z->udb_data;
190 	t->udb_overhead += z->udb_overhead;
191 	t->domaincount += z->domaincount;
192 }
193 
194 static void
195 check_zone_mem(const char* tf, const char* df, zone_options_t* zo,
196 	nsd_options_t* opt, struct tot_mem* totmem)
197 {
198 	struct nsd nsd;
199 	struct namedb* db;
200 	const dname_type* dname = (const dname_type*)zo->node.key;
201 	zone_type* zone;
202 	struct udb_base* taskudb;
203 	udb_ptr last_task;
204 	struct zone_mem zmem;
205 
206 	printf("zone %s\n", zo->name);
207 
208 	/* init*/
209 	memset(&zmem, 0, sizeof(zmem));
210 	memset(&nsd, 0, sizeof(nsd));
211 	nsd.db = db = namedb_open(df, opt);
212 	if(!db) error("cannot open %s: %s", df, strerror(errno));
213 	zone = namedb_zone_create(db, dname, zo);
214 	taskudb = udb_base_create_new(tf, &namedb_walkfunc, NULL);
215 	udb_ptr_init(&last_task, taskudb);
216 
217 	/* read the zone */
218 	namedb_read_zonefile(&nsd, zone, taskudb, &last_task);
219 
220 	/* account the memory for this zone */
221 	account_zone(db, &zmem);
222 
223 	/* pretty print the memory for this zone */
224 	print_zone_mem(&zmem);
225 
226 	/* delete the zone from memory */
227 	namedb_close(db);
228 	udb_base_free(taskudb);
229 	unlink(df);
230 	unlink(tf);
231 
232 	/* add up totals */
233 	add_mem(totmem, &zmem);
234 }
235 
236 static void
237 check_mem(nsd_options_t* opt)
238 {
239 	struct tot_mem totmem;
240 	zone_options_t* zo;
241 	char tf[512];
242 	char df[512];
243 	memset(&totmem, 0, sizeof(totmem));
244 	snprintf(tf, sizeof(tf), "./nsd-mem-task-%u.db", (unsigned)getpid());
245 	if(opt->database == NULL || opt->database[0] == 0)
246 		df[0] = 0;
247 	else snprintf(df, sizeof(df), "./nsd-mem-db-%u.db", (unsigned)getpid());
248 
249 	/* read all zones and account memory */
250 	RBTREE_FOR(zo, zone_options_t*, opt->zone_options) {
251 		check_zone_mem(tf, df, zo, opt, &totmem);
252 	}
253 
254 	/* calculate more total statistics */
255 	account_total(opt, &totmem);
256 	/* print statistics */
257 	print_tot_mem(&totmem);
258 
259 	/* final advice */
260 	if(opt->database != NULL && opt->database[0] != 0) {
261 		printf("\nFinal advice estimate:\n");
262 		printf("(The partial mmap causes reload&AXFR to take longer(disk access))\n");
263 		pretty_mem(totmem.ram + totmem.disk, "data and big mmap");
264 		pretty_mem(totmem.ram + totmem.disk/6, "data and partial mmap");
265 	}
266 }
267 
268 /* dummy functions to link */
269 struct nsd;
270 int writepid(struct nsd * ATTR_UNUSED(nsd))
271 {
272 	        return 0;
273 }
274 void unlinkpid(const char * ATTR_UNUSED(file))
275 {
276 }
277 void bind8_stats(struct nsd * ATTR_UNUSED(nsd))
278 {
279 }
280 
281 void sig_handler(int ATTR_UNUSED(sig))
282 {
283 }
284 
285 extern char *optarg;
286 extern int optind;
287 
288 int
289 main(int argc, char *argv[])
290 {
291 	/* Scratch variables... */
292 	int c;
293 	struct nsd nsd;
294 	const char *configfile = CONFIGFILE;
295 	memset(&nsd, 0, sizeof(nsd));
296 
297 	log_init("nsd-mem");
298 
299 	/* Parse the command line... */
300 	while ((c = getopt(argc, argv, "c:h"
301 		)) != -1) {
302 		switch (c) {
303 		case 'c':
304 			configfile = optarg;
305 			break;
306 		case 'h':
307 			usage();
308 			exit(0);
309 		case '?':
310 		default:
311 			usage();
312 			exit(1);
313 		}
314 	}
315 	argc -= optind;
316 	argv += optind;
317 
318 	/* Commandline parse error */
319 	if (argc != 0) {
320 		usage();
321 		exit(1);
322 	}
323 
324 	/* Read options */
325 	nsd.options = nsd_options_create(region_create_custom(xalloc, free,
326 		DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE,
327 		DEFAULT_INITIAL_CLEANUP_SIZE, 1));
328 	tsig_init(nsd.options->region);
329 	if(!parse_options_file(nsd.options, configfile, NULL, NULL)) {
330 		error("could not read config: %s\n", configfile);
331 	}
332 	if(!parse_zone_list_file(nsd.options)) {
333 		error("could not read zonelist file %s\n",
334 			nsd.options->zonelistfile);
335 	}
336 	if (verbosity == 0)
337 		verbosity = nsd.options->verbosity;
338 
339 #ifdef HAVE_CHROOT
340 	if(nsd.chrootdir == 0) nsd.chrootdir = nsd.options->chroot;
341 #ifdef CHROOTDIR
342 	/* if still no chrootdir, fallback to default */
343 	if(nsd.chrootdir == 0) nsd.chrootdir = CHROOTDIR;
344 #endif /* CHROOTDIR */
345 #endif /* HAVE_CHROOT */
346 	if(nsd.options->zonesdir && nsd.options->zonesdir[0]) {
347 		if(chdir(nsd.options->zonesdir)) {
348 			error("cannot chdir to '%s': %s",
349 				nsd.options->zonesdir, strerror(errno));
350 		}
351 		DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed directory to %s",
352 			nsd.options->zonesdir));
353 	}
354 
355 	/* Chroot */
356 #ifdef HAVE_CHROOT
357 	if (nsd.chrootdir && strlen(nsd.chrootdir)) {
358 		if(chdir(nsd.chrootdir)) {
359 			error("unable to chdir to chroot: %s", strerror(errno));
360 		}
361 		DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed root directory to %s",
362 			nsd.chrootdir));
363 	}
364 #endif /* HAVE_CHROOT */
365 
366 	check_mem(nsd.options);
367 
368 	exit(0);
369 }
370