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