xref: /openbsd/usr.sbin/nsd/nsd-mem.c (revision bf87c3c0)
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
usage(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
account_zone(struct namedb * db,struct zone_mem * zmem)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
pretty_mem(size_t x,const char * s)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
print_zone_mem(struct zone_mem * z)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
account_total(struct nsd_options * opt,struct tot_mem * t)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
print_tot_mem(struct tot_mem * t)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
add_mem(struct tot_mem * t,struct zone_mem * z)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
check_zone_mem(const char * tf,struct zone_options * zo,struct nsd_options * opt,struct tot_mem * totmem)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
check_mem(struct nsd_options * opt)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;
writepid(struct nsd * ATTR_UNUSED (nsd))218 int writepid(struct nsd * ATTR_UNUSED(nsd))
219 {
220 	        return 0;
221 }
unlinkpid(const char * ATTR_UNUSED (file))222 void unlinkpid(const char * ATTR_UNUSED(file))
223 {
224 }
bind8_stats(struct nsd * ATTR_UNUSED (nsd))225 void bind8_stats(struct nsd * ATTR_UNUSED(nsd))
226 {
227 }
228 
sig_handler(int ATTR_UNUSED (sig))229 void sig_handler(int ATTR_UNUSED(sig))
230 {
231 }
232 
233 extern char *optarg;
234 extern int optind;
235 
236 int
main(int argc,char * argv[])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, 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