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
27struct nsd nsd;
28
29/*
30 * Print the help text.
31 *
32 */
33static void
34usage (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 */
42struct 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 */
53struct 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
77static void
78account_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
85static void
86pretty_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
101static void
102print_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
108static void
109account_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
130static void
131print_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
147static void
148add_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
155static void
156check_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
196static void
197check_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 */
217struct nsd;
218int writepid(struct nsd * ATTR_UNUSED(nsd))
219{
220	        return 0;
221}
222void unlinkpid(const char * ATTR_UNUSED(file))
223{
224}
225void bind8_stats(struct nsd * ATTR_UNUSED(nsd))
226{
227}
228
229void sig_handler(int ATTR_UNUSED(sig))
230{
231}
232
233extern char *optarg;
234extern int optind;
235
236int
237main(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