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