1 /*	$OpenBSD: conf.c,v 1.24 2000/10/27 19:22:36 niklas Exp $	*/
2 /*	$EOM: conf.c,v 1.46 2000/10/26 16:17:19 ho Exp $	*/
3 
4 /*
5  * Copyright (c) 1998, 1999, 2000 Niklas Hallqvist.  All rights reserved.
6  * Copyright (c) 2000 H�kan Olsson.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by Ericsson Radio Systems.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * This code was written under funding by Ericsson Radio Systems.
36  */
37 
38 #include <sys/types.h>
39 
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif /* HAVE_CONFIG_H */
43 
44 #ifndef WIN32
45 #include <sys/param.h>
46 #include <sys/mman.h>
47 #endif /* WIN32 */
48 #include <sys/stat.h>
49 #include <sys/queue.h>
50 
51 #include <ctype.h>
52 #include <fcntl.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include <errno.h>
58 
59 #ifdef HAVE_ERR
60 #include <err.h>
61 #endif /* HAVE_ERR */
62 
63 #include "conf.h"
64 
65 struct conf_trans {
66 	TAILQ_ENTRY (conf_trans) link;
67 	int trans;
68 	enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op;
69 	char *section;
70 	char *tag;
71 	char *value;
72 	int override;
73 	int is_default;
74 };
75 
76 TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue;
77 
78 /*
79  * Radix-64 Encoding.
80  */
81 const u_int8_t bin2asc[]
82   = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
83 
84 const u_int8_t asc2bin[] =
85 {
86 	255, 255, 255, 255, 255, 255, 255, 255,
87 	255, 255, 255, 255, 255, 255, 255, 255,
88 	255, 255, 255, 255, 255, 255, 255, 255,
89 	255, 255, 255, 255, 255, 255, 255, 255,
90 	255, 255, 255, 255, 255, 255, 255, 255,
91 	255, 255, 255,  62, 255, 255, 255,  63,
92 	52,  53,  54,  55,  56,  57,  58,  59,
93 	60,  61, 255, 255, 255, 255, 255, 255,
94 	255,   0,   1,   2,   3,   4,   5,   6,
95 	7,   8,   9,  10,  11,  12,  13,  14,
96 	15,  16,  17,  18,  19,  20,  21,  22,
97 	23,  24,  25, 255, 255, 255, 255, 255,
98 	255,  26,  27,  28,  29,  30,  31,  32,
99 	33,  34,  35,  36,  37,  38,  39,  40,
100 	41,  42,  43,  44,  45,  46,  47,  48,
101 	49,  50,  51, 255, 255, 255, 255, 255
102 };
103 
104 struct conf_binding {
105 	LIST_ENTRY (conf_binding) link;
106 	char *section;
107 	char *tag;
108 	char *value;
109 	int is_default;
110 };
111 
112 extern char *conf_path;
LIST_HEAD(conf_bindings,conf_binding)113 LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256];
114 
115 static char *conf_addr;
116 
117 static __inline__ u_int8_t
118 conf_hash(char *s)
119 {
120 	u_int8_t hash = 0;
121 
122 	while (*s) {
123 		hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s);
124 		s++;
125 	}
126 	return (hash);
127 }
128 
129 /*
130  * Insert a tag-value combination from LINE (the equal sign is at POS)
131  */
132 static int
conf_remove_now(char * section,char * tag)133 conf_remove_now(char *section, char *tag)
134 {
135 	struct conf_binding *cb, *next;
136 
137 	for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
138 	     cb = next) {
139 		next = LIST_NEXT (cb, link);
140 		if (strcasecmp (cb->section, section) == 0
141 		    && strcasecmp (cb->tag, tag) == 0) {
142 			LIST_REMOVE (cb, link);
143 			free (cb->section);
144 			free (cb->tag);
145 			free (cb->value);
146 			free (cb);
147 			return (0);
148 		}
149 	}
150 	return (1);
151 }
152 
153 static int
conf_remove_section_now(char * section)154 conf_remove_section_now (char *section)
155 {
156 	struct conf_binding *cb, *next;
157 	int unseen = 1;
158 
159 	for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
160 	     cb = next) {
161 		next = LIST_NEXT (cb, link);
162 		if (strcasecmp (cb->section, section) == 0) {
163 			unseen = 0;
164 			LIST_REMOVE (cb, link);
165 			free (cb->section);
166 			free (cb->tag);
167 			free (cb->value);
168 			free (cb);
169 		}
170 	}
171 
172 	return (unseen);
173 }
174 
175 /*
176  * Insert a tag-value combination from LINE (the equal sign is at POS)
177  * into SECTION of our configuration database.
178  */
179 static int
conf_set_now(char * section,char * tag,char * value,int override,int is_default)180 conf_set_now (char *section, char *tag, char *value, int override,
181 	      int is_default)
182 {
183 	struct conf_binding *node = 0;
184 
185 	if (override)
186 		conf_remove_now (section, tag);
187 	else if (conf_get_str (section, tag)) {
188 		if (!is_default)
189 			warnx("conf_set: duplicate tag [%s]:%s, ignoring...\n",
190 			      section, tag);
191 		return (1);
192 	}
193 
194 	node = calloc (1, sizeof *node);
195 	if (!node) {
196 		warn("conf_set: calloc (1, %d) failed", sizeof *node);
197 		return (1);
198 	}
199 	node->section = strdup (section);
200 	node->tag = strdup (tag);
201 	node->value = strdup (value);
202 	node->is_default = is_default;
203 
204 	LIST_INSERT_HEAD (&conf_bindings[conf_hash (section)], node, link);
205 	return (0);
206 }
207 
208 /*
209  * Parse the line LINE of SZ bytes.  Skip Comments, recognize section
210  * headers and feed tag-value pairs into our configuration database.
211  */
212 static void
conf_parse_line(int trans,char * line,size_t sz)213 conf_parse_line (int trans, char *line, size_t sz)
214 {
215 	char *cp = line;
216 	int i;
217 	static char *section = 0;
218 	static int ln = 0;
219 
220 	ln++;
221 
222 	/* Lines starting with '#' or ';' are comments.  */
223 	if (*line == '#' || *line == ';')
224 		return;
225 
226 	/* '[section]' parsing...  */
227 	if (*line == '[') {
228 		for (i = 1; i < sz; i++)
229 			if (line[i] == ']')
230 				break;
231 		if (i == sz) {
232 			warnx("conf_parse_line: %d:"
233 			      "non-matched ']', ignoring until next section",
234 			      ln);
235 			section = 0;
236 			return;
237 		}
238 		if (section)
239 			free (section);
240 		section = malloc (i);
241 		strncpy (section, line + 1, i - 1);
242 		section[i - 1] = '\0';
243 		return;
244 	}
245 
246 	/* Deal with assignments.  */
247 	for (i = 0; i < sz; i++)
248 		if (cp[i] == '=') {
249 			/* If no section, we are ignoring the lines.  */
250 			if (!section) {
251 				warnx("conf_parse_line: %d: ignoring line due to no section",
252 				      ln);
253 				return;
254 			}
255 			line[strcspn (line, " \t=")] = '\0';
256 			/* XXX Perhaps should we not ignore errors?  */
257 			conf_set (trans, section, line,
258 				  line + i + 1 + strspn (line + i + 1, " \t"), 0, 0);
259 			return;
260 		}
261 
262 	/* Other non-empty lines are wierd.  */
263 	i = strspn (line, " \t");
264 	if (line[i])
265 		warnx("conf_parse_line: %d: syntax error", ln);
266 
267 	return;
268 }
269 
270 /* Parse the mapped configuration file.  */
271 static void
conf_parse(int trans,char * buf,size_t sz)272 conf_parse (int trans, char *buf, size_t sz)
273 {
274 	char *cp = buf;
275 	char *bufend = buf + sz;
276 	char *line;
277 
278 	line = cp;
279 	while (cp < bufend) {
280 		if (*cp == '\n') {
281 			/* Check for escaped newlines.  */
282 			if (cp > buf && *(cp - 1) == '\\')
283 				*(cp - 1) = *cp = ' ';
284 			else {
285 				*cp = '\0';
286 				conf_parse_line (trans, line, cp - line);
287 				line = cp + 1;
288 			}
289 		}
290 		cp++;
291 	}
292 	if (cp != line)
293 		warnx("conf_parse: last line non-terminated, ignored.");
294 }
295 
296 void
conf_load_defaults(int tr)297 conf_load_defaults (int tr)
298 {
299 	/* No defaults so far *
300 	conf_set (tr, "General", "Port", "80", 0, 1);
301 	conf_set (tr, "General", "IP-Address", "0.0.0.0", 0, 1);
302 	*/
303 
304 	return;
305 }
306 
307 void
conf_init(void)308 conf_init (void)
309 {
310 	int i;
311 
312 	for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
313 		LIST_INIT (&conf_bindings[i]);
314 	TAILQ_INIT (&conf_trans_queue);
315 	conf_reinit ();
316 }
317 
318 /* Open the config file and map it into our address space, then parse it.  */
319 void
conf_reinit(void)320 conf_reinit (void)
321 {
322 	struct conf_binding *cb = 0;
323 	int fd, i, trans;
324 	off_t sz;
325 	char *new_conf_addr = 0;
326 	struct stat sb;
327 
328 	if ((stat (conf_path, &sb) == 0) || (errno != ENOENT)) {
329 		sz = sb.st_size;
330 		fd = open (conf_path, O_RDONLY);
331 		if (fd == -1) {
332 			warn("conf_reinit: open (\"%s\", O_RDONLY) failed",
333 			     conf_path);
334 			return;
335 		}
336 
337 		new_conf_addr = malloc (sz);
338 		if (!new_conf_addr) {
339 			warn("conf_reinit: malloc (%d) failed", (int)sz);
340 			goto fail;
341 		}
342 
343 		/* XXX I assume short reads won't happen here.  */
344 		if (read(fd, new_conf_addr, sz) != sz) {
345 			warn("conf_reinit: read (%d, %p, %d) failed",
346 			     fd, new_conf_addr, (int)sz);
347 			goto fail;
348 		}
349 		close (fd);
350 
351 		trans = conf_begin ();
352 
353 		/* XXX Should we not care about errors and rollback?  */
354 		conf_parse (trans, new_conf_addr, sz);
355 	}
356 	else
357 		trans = conf_begin ();
358 
359 	/* Load default configuration values.  */
360 	conf_load_defaults (trans);
361 
362 	/* Free potential existing configuration.  */
363 	if (conf_addr) {
364 		for (i = 0;
365 		     i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
366 			for (cb = LIST_FIRST (&conf_bindings[i]); cb;
367 			     cb = LIST_FIRST (&conf_bindings[i]))
368 				conf_remove_now (cb->section, cb->tag);
369 		free (conf_addr);
370 	}
371 
372 	conf_end (trans, 1);
373 	conf_addr = new_conf_addr;
374 	return;
375 
376  fail:
377 	if (new_conf_addr)
378 		free (new_conf_addr);
379 	close (fd);
380 }
381 
382 /*
383  * Return the numeric value denoted by TAG in section SECTION or DEF
384  * if that tag does not exist.
385  */
386 int
conf_get_num(char * section,char * tag,int def)387 conf_get_num (char *section, char *tag, int def)
388 {
389 	char *value = conf_get_str (section, tag);
390 
391 	if (value)
392 		return (atoi (value));
393 	return (def);
394 }
395 
396 double
conf_get_fnum(char * section,char * tag,double def)397 conf_get_fnum (char *section, char *tag, double def)
398 {
399 	char *value = conf_get_str (section, tag);
400 
401 	if (value)
402 		return (strtod(value, (char **)NULL));
403 
404 	return (def);
405 }
406 
407 /* Validate X according to the range denoted by TAG in section SECTION.  */
408 int
conf_match_num(char * section,char * tag,int x)409 conf_match_num (char *section, char *tag, int x)
410 {
411 	char *value = conf_get_str (section, tag);
412 	int val, min, max, n;
413 
414 	if (!value)
415 		return (0);
416 	n = sscanf (value, "%d,%d:%d", &val, &min, &max);
417 	switch (n) {
418 	case 1:
419 		return (x == val);
420 	case 3:
421 		return (min <= x && max >= x);
422 	default:
423 		warn("conf_match_num: section %s tag %s: "
424 		     "invalid number spec %s", section, tag, value);
425 	}
426 	return (0);
427 }
428 
429 /* Return the string value denoted by TAG in section SECTION.  */
430 char *
conf_get_str(char * section,char * tag)431 conf_get_str (char *section, char *tag)
432 {
433 	struct conf_binding *cb;
434 
435 	for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
436 	     cb = LIST_NEXT (cb, link))
437 		if (strcasecmp (section, cb->section) == 0
438 		    && strcasecmp (tag, cb->tag) == 0)
439 			return (cb->value);
440 
441 	return (0);
442 }
443 
444 /*
445  * Build a list of string values out of the comma separated value denoted by
446  * TAG in SECTION.
447  */
448 struct conf_list *
conf_get_list(char * section,char * tag)449 conf_get_list (char *section, char *tag)
450 {
451 	char *liststr = 0, *p, *field;
452 	struct conf_list *list = 0;
453 	struct conf_list_node *node;
454 
455 	list = malloc (sizeof *list);
456 	if (!list)
457 		goto cleanup;
458 	TAILQ_INIT (&list->fields);
459 	list->cnt = 0;
460 	liststr = conf_get_str (section, tag);
461 	if (!liststr)
462 		goto cleanup;
463 	liststr = strdup (liststr);
464 	if (!liststr)
465 		goto cleanup;
466 	p = liststr;
467 	while ((field = (char*)strsep (&p, ", \t")) != NULL) {
468 		if (*field == '\0') {
469 			warnx("conf_get_list: empty field, ignoring...");
470 			continue;
471 		}
472 		list->cnt++;
473 		node = calloc (1, sizeof *node);
474 		if (!node)
475 			goto cleanup;
476 		node->field = strdup (field);
477 		if (!node->field)
478 			goto cleanup;
479 		TAILQ_INSERT_TAIL (&list->fields, node, link);
480 	}
481 	free (liststr);
482 	return (list);
483 
484  cleanup:
485 	if (list)
486 		conf_free_list (list);
487 	if (liststr)
488 		free (liststr);
489 	return (0);
490 }
491 
492 struct conf_list *
conf_get_tag_list(char * section)493 conf_get_tag_list (char *section)
494 {
495 	struct conf_list *list = 0;
496 	struct conf_list_node *node;
497 	struct conf_binding *cb;
498 
499 	list = malloc (sizeof *list);
500 	if (!list)
501 		goto cleanup;
502 	TAILQ_INIT (&list->fields);
503 	list->cnt = 0;
504 	for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb;
505 	     cb = LIST_NEXT (cb, link))
506 		if (strcasecmp (section, cb->section) == 0) {
507 			list->cnt++;
508 			node = calloc (1, sizeof *node);
509 			if (!node)
510 				goto cleanup;
511 			node->field = strdup (cb->tag);
512 			if (!node->field)
513 				goto cleanup;
514 			TAILQ_INSERT_TAIL (&list->fields, node, link);
515 		}
516 	return (list);
517 
518  cleanup:
519 	if (list)
520 		conf_free_list (list);
521 	return (0);
522 }
523 
524 /* Decode a PEM encoded buffer.  */
525 int
conf_decode_base64(u_int8_t * out,u_int32_t * len,u_char * buf)526 conf_decode_base64 (u_int8_t *out, u_int32_t *len, u_char *buf)
527 {
528 	u_int32_t c = 0;
529 	u_int8_t c1, c2, c3, c4;
530 
531 	while (*buf) {
532 		if (*buf > 127 || (c1 = asc2bin[*buf]) == 255)
533 			return (0);
534 		buf++;
535 
536 		if (*buf > 127 || (c2 = asc2bin[*buf]) == 255)
537 			return (0);
538 		buf++;
539 
540 		if (*buf == '=') {
541 			c3 = c4 = 0;
542 			c++;
543 
544 			/* Check last four bit */
545 			if (c2 & 0xF)
546 				return (0);
547 
548 			if (!strcmp (buf, "=="))
549 				buf++;
550 			else
551 				return (0);
552 		}
553 		else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255)
554 			return (0);
555 		else {
556 			if (*++buf == '=') {
557 				c4 = 0;
558 				c += 2;
559 
560 				/* Check last two bit */
561 				if (c3 & 3)
562 					return (0);
563 
564 				if (strcmp (buf, "="))
565 					return (0);
566 
567 			}
568 			else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255)
569 				return (0);
570 			else
571 				c += 3;
572 		}
573 
574 		buf++;
575 		*out++ = (c1 << 2) | (c2 >> 4);
576 		*out++ = (c2 << 4) | (c3 >> 2);
577 		*out++ = (c3 << 6) | c4;
578 	}
579 
580 	*len = c;
581 	return (1);
582 
583 }
584 
585 /* Read a line from a stream to the buffer.  */
586 int
conf_get_line(FILE * stream,char * buf,u_int32_t len)587 conf_get_line (FILE *stream, char *buf, u_int32_t len)
588 {
589 	int c;
590 
591 	while (len-- > 1) {
592 		c = fgetc (stream);
593 		if (c == '\n') {
594 			*buf = 0;
595 			return (1);
596 		}
597 		else if (c == EOF)
598 			break;
599 
600 		*buf++ = c;
601 	}
602 
603 	*buf = 0;
604 	return (0);
605 }
606 
607 void
conf_free_list(struct conf_list * list)608 conf_free_list (struct conf_list *list)
609 {
610 	struct conf_list_node *node = TAILQ_FIRST (&list->fields);
611 
612 	while (node) {
613 		TAILQ_REMOVE (&list->fields, node, link);
614 		if (node->field)
615 			free (node->field);
616 		free (node);
617 		node = TAILQ_FIRST (&list->fields);
618 	}
619 	free (list);
620 }
621 
622 int
conf_begin(void)623 conf_begin (void)
624 {
625 	static int seq = 0;
626 
627 	return (++seq);
628 }
629 
630 static struct conf_trans *
conf_trans_node(int transaction,enum conf_op op)631 conf_trans_node (int transaction, enum conf_op op)
632 {
633 	struct conf_trans *node;
634 
635 	node = calloc (1, sizeof *node);
636 	if (!node) {
637 		warn("conf_trans_node: calloc (1, %d) failed", sizeof *node);
638 		return (0);
639 	}
640 	node->trans = transaction;
641 	node->op = op;
642 	TAILQ_INSERT_TAIL (&conf_trans_queue, node, link);
643 
644 	return (node);
645 }
646 
647 /* Queue a set operation.  */
648 int
conf_set(int transaction,char * section,char * tag,char * value,int override,int is_default)649 conf_set (int transaction, char *section, char *tag, char *value, int override,
650 	  int is_default)
651 {
652 	struct conf_trans *node;
653 
654 	node = conf_trans_node (transaction, CONF_SET);
655 	if (!node)
656 		return (1);
657 	node->section = strdup (section);
658 	if (!node->section) {
659 		warn("conf_set: strdup (\"%s\") failed", section);
660 		goto fail;
661 	}
662 	node->tag = strdup (tag);
663 	if (!node->tag) {
664 		warn("conf_set: strdup (\"%s\") failed", tag);
665 		goto fail;
666 	}
667 	node->value = strdup (value);
668 	if (!node->value) {
669 		warn("conf_set: strdup (\"%s\") failed", value);
670 		goto fail;
671 	}
672 	node->override = override;
673 	node->is_default = is_default;
674 	return (0);
675 
676  fail:
677 	if (node->tag)
678 		free (node->tag);
679 	if (node->section)
680 		free (node->section);
681 	if (node)
682 		free (node);
683 	return (1);
684 }
685 
686 /* Queue a remove operation.  */
687 int
conf_remove(int transaction,char * section,char * tag)688 conf_remove (int transaction, char *section, char *tag)
689 {
690 	struct conf_trans *node;
691 
692 	node = conf_trans_node (transaction, CONF_REMOVE);
693 	if (!node)
694 		goto fail;
695 	node->section = strdup (section);
696 	if (!node->section) {
697 		warn("conf_remove: strdup (\"%s\") failed", section);
698 		goto fail;
699 	}
700 	node->tag = strdup (tag);
701 	if (!node->tag) {
702 		warn("conf_remove: strdup (\"%s\") failed", tag);
703 		goto fail;
704 	}
705 	return (0);
706 
707  fail:
708 	if (node->section)
709 		free (node->section);
710 	if (node)
711 		free (node);
712 	return (1);
713 }
714 
715 /* Queue a remove section operation.  */
716 int
conf_remove_section(int transaction,char * section)717 conf_remove_section (int transaction, char *section)
718 {
719 	struct conf_trans *node;
720 
721 	node = conf_trans_node (transaction, CONF_REMOVE_SECTION);
722 	if (!node)
723 		goto fail;
724 	node->section = strdup (section);
725 	if (!node->section) {
726 		warn("conf_remove_section: strdup (\"%s\") failed", section);
727 		goto fail;
728 	}
729 	return (0);
730 
731  fail:
732 	if (node)
733 		free (node);
734 	return (1);
735 }
736 
737 /* Execute all queued operations for this transaction.  Cleanup.  */
738 int
conf_end(int transaction,int commit)739 conf_end (int transaction, int commit)
740 {
741 	struct conf_trans *node, *next;
742 
743 	for (node = TAILQ_FIRST (&conf_trans_queue); node; node = next) {
744 		next = TAILQ_NEXT (node, link);
745 		if (node->trans == transaction) {
746 			if (commit)
747 				switch (node->op) {
748 				case CONF_SET:
749 					conf_set_now (node->section, node->tag, node->value,
750 						      node->override, node->is_default);
751 					break;
752 				case CONF_REMOVE:
753 					conf_remove_now (node->section, node->tag);
754 					break;
755 				case CONF_REMOVE_SECTION:
756 					conf_remove_section_now (node->section);
757 					break;
758 				default:
759 					warnx("conf_end: unknown operation: %d", node->op);
760 				}
761 			TAILQ_REMOVE (&conf_trans_queue, node, link);
762 			if (node->section)
763 				free (node->section);
764 			if (node->tag)
765 				free (node->tag);
766 			if (node->value)
767 				free (node->value);
768 			free (node);
769 		}
770 	}
771 	return (0);
772 }
773 
774 /* Dump running configuration upon SIGUSR1. */
775 /* XXX Configuration is "stored in reverse order", so reverse it. */
776 struct dumper {
777 	char *s, *v;
778 	struct dumper *next;
779 };
780 
781 static void
conf_report_dump(struct dumper * node)782 conf_report_dump (struct dumper *node)
783 {
784 	/* Recursive, cleanup when we're done. */
785 
786 	if (node->next)
787 		conf_report_dump (node->next);
788 
789 	if (node->v)
790 		fprintf(stderr, "%s=\t%s", node->s, node->v);
791 	else if (node->s) {
792 		fprintf(stderr, "%s", node->s);
793 		if (strlen (node->s) > 0)
794 			free (node->s);
795 	}
796 
797 	free (node);
798 }
799 
800 void
conf_report(void)801 conf_report (void)
802 {
803 	struct conf_binding *cb, *last = NULL;
804 	int i;
805 	char *current_section = (char *)0;
806 	struct dumper *dumper, *dnode;
807 
808 	dumper = dnode = (struct dumper *)calloc (1, sizeof *dumper);
809 	if (!dumper)
810 		goto mem_fail;
811 
812 	fprintf(stderr, "conf_report: dumping running configuration");
813 
814 	for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++)
815 		for (cb = LIST_FIRST (&conf_bindings[i]); cb;
816 		     cb = LIST_NEXT (cb, link)) {
817 			if (!cb->is_default) {
818 				/* Dump this entry */
819 				if (!current_section ||
820 				    strcmp (cb->section, current_section)) {
821 					if (current_section) {
822 						dnode->s = malloc (strlen (current_section) + 3);
823 						if (!dnode->s)
824 							goto mem_fail;
825 
826 						sprintf (dnode->s, "[%s]", current_section);
827 						dnode->next
828 							= (struct dumper *)calloc (1, sizeof (struct dumper));
829 						dnode = dnode->next;
830 						if (!dnode)
831 							goto mem_fail;
832 
833 						dnode->s = "";
834 						dnode->next
835 							= (struct dumper *)calloc (1, sizeof (struct dumper));
836 						dnode = dnode->next;
837 						if (!dnode)
838 							goto mem_fail;
839 					}
840 					current_section = cb->section;
841 				}
842 				dnode->s = cb->tag;
843 				dnode->v = cb->value;
844 				dnode->next = (struct dumper *)calloc (1, sizeof (struct dumper));
845 				dnode = dnode->next;
846 				if (!dnode)
847 					goto mem_fail;
848 				last = cb;
849 			}
850 		}
851 
852 	if (last) {
853 		dnode->s = malloc (strlen (last->section) + 3);
854 		if (!dnode->s)
855 			goto mem_fail;
856 		sprintf (dnode->s, "[%s]", last->section);
857 	}
858 
859 	conf_report_dump (dumper);
860 
861 	return;
862 
863  mem_fail:
864 	warn("conf_report: memory allocation failure.");
865 	while ((dnode = dumper) != NULL) {
866 		dumper = dumper->next;
867 		if (dnode->s)
868 			free (dnode->s);
869 		free (dnode);
870 	}
871 	return;
872 }
873