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