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