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 $ */
5 * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved.
6 * Copyright (c) 2000, 2001, 2002 HÃ¥kan Olsson. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
30 * This code was written under funding by Ericsson Radio Systems.
33 #include <sys/param.h>
35 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
50 static void conf_load_defaults (int);
52 static int conf_find_trans_xf (int, char *);
55 size_t strlcpy(char *, const char *, size_t);
58 TAILQ_ENTRY (conf_trans
) link
;
60 enum conf_op
{ CONF_SET
, CONF_REMOVE
, CONF_REMOVE_SECTION
} op
;
68 TAILQ_HEAD (conf_trans_head
, conf_trans
) conf_trans_queue
;
73 const u_int8_t bin2asc
[]
74 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
76 const u_int8_t asc2bin
[] =
78 255, 255, 255, 255, 255, 255, 255, 255,
79 255, 255, 255, 255, 255, 255, 255, 255,
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, 62, 255, 255, 255, 63,
84 52, 53, 54, 55, 56, 57, 58, 59,
85 60, 61, 255, 255, 255, 255, 255, 255,
86 255, 0, 1, 2, 3, 4, 5, 6,
87 7, 8, 9, 10, 11, 12, 13, 14,
88 15, 16, 17, 18, 19, 20, 21, 22,
89 23, 24, 25, 255, 255, 255, 255, 255,
90 255, 26, 27, 28, 29, 30, 31, 32,
91 33, 34, 35, 36, 37, 38, 39, 40,
92 41, 42, 43, 44, 45, 46, 47, 48,
93 49, 50, 51, 255, 255, 255, 255, 255
97 LIST_ENTRY (conf_binding
) link
;
105 LIST_HEAD (conf_bindings
, conf_binding
) conf_bindings
[256];
107 static char *conf_addr
;
109 static __inline__ u_int8_t
116 hash
= ((hash
<< 1) | (hash
>> 7)) ^ tolower (*s
);
123 * Insert a tag-value combination from LINE (the equal sign is at POS)
126 conf_remove_now (char *section
, char *tag
)
128 struct conf_binding
*cb
, *next
;
130 for (cb
= LIST_FIRST (&conf_bindings
[conf_hash (section
)]); cb
; cb
= next
)
132 next
= LIST_NEXT (cb
, link
);
133 if (strcasecmp (cb
->section
, section
) == 0
134 && strcasecmp (cb
->tag
, tag
) == 0)
136 LIST_REMOVE (cb
, link
);
137 warnx("[%s]:%s->%s removed", section
, tag
, cb
->value
);
149 conf_remove_section_now (char *section
)
151 struct conf_binding
*cb
, *next
;
154 for (cb
= LIST_FIRST (&conf_bindings
[conf_hash (section
)]); cb
; cb
= next
)
156 next
= LIST_NEXT (cb
, link
);
157 if (strcasecmp (cb
->section
, section
) == 0)
160 LIST_REMOVE (cb
, link
);
161 warnx("[%s]:%s->%s removed", section
, cb
->tag
, cb
->value
);
172 * Insert a tag-value combination from LINE (the equal sign is at POS)
173 * into SECTION of our configuration database.
176 conf_set_now (char *section
, char *tag
, char *value
, int override
,
179 struct conf_binding
*node
= 0;
182 conf_remove_now (section
, tag
);
183 else if (conf_get_str (section
, tag
))
186 warnx("conf_set: duplicate tag [%s]:%s, ignoring...\n", section
, tag
);
190 node
= calloc (1, sizeof *node
);
193 warnx("conf_set: calloc (1, %lu) failed", (unsigned long)sizeof *node
);
196 node
->section
= strdup (section
);
197 node
->tag
= strdup (tag
);
198 node
->value
= strdup (value
);
199 node
->is_default
= is_default
;
201 LIST_INSERT_HEAD (&conf_bindings
[conf_hash (section
)], node
, link
);
206 * Parse the line LINE of SZ bytes. Skip Comments, recognize section
207 * headers and feed tag-value pairs into our configuration database.
210 conf_parse_line (int trans
, char *line
, size_t sz
)
215 static char *section
= 0;
220 /* Lines starting with '#' or ';' are comments. */
221 if (*line
== '#' || *line
== ';')
224 /* '[section]' parsing... */
227 for (i
= 1; i
< sz
; i
++)
234 warnx("conf_parse_line: %d:"
235 "non-matched ']', ignoring until next section", ln
);
239 section
= malloc (i
);
242 warnx("conf_parse_line: %d: malloc (%lu) failed", ln
,
246 strlcpy (section
, line
+ 1, i
);
250 /* Deal with assignments. */
251 for (i
= 0; i
< sz
; i
++)
254 /* If no section, we are ignoring the lines. */
257 warnx("conf_parse_line: %d: ignoring line due to no section", ln
);
260 line
[strcspn (line
, " \t=")] = '\0';
261 val
= line
+ i
+ 1 + strspn (line
+ i
+ 1, " \t");
262 /* Skip trailing whitespace, if any */
263 for (j
= sz
- (val
- line
) - 1; j
> 0 && isspace (val
[j
]); j
--)
265 /* XXX Perhaps should we not ignore errors? */
266 conf_set (trans
, section
, line
, val
, 0, 0);
270 /* Other non-empty lines are weird. */
271 i
= strspn (line
, " \t");
273 warnx("conf_parse_line: %d: syntax error", ln
);
278 /* Parse the mapped configuration file. */
280 conf_parse (int trans
, char *buf
, size_t sz
)
283 char *bufend
= buf
+ sz
;
291 /* Check for escaped newlines. */
292 if (cp
> buf
&& *(cp
- 1) == '\\')
293 *(cp
- 1) = *cp
= ' ';
297 conf_parse_line (trans
, line
, cp
- line
);
304 warnx("conf_parse: last line non-terminated, ignored.");
308 conf_load_defaults (int tr
)
319 for (i
= 0; i
< sizeof conf_bindings
/ sizeof conf_bindings
[0]; i
++)
320 LIST_INIT (&conf_bindings
[i
]);
321 TAILQ_INIT (&conf_trans_queue
);
325 /* Open the config file and map it into our address space, then parse it. */
329 struct conf_binding
*cb
= 0;
333 char *new_conf_addr
= 0;
336 if ((stat (conf_path
, &sb
) == 0) || (errno
!= ENOENT
))
339 fd
= open (conf_path
, O_RDONLY
, 0);
342 warnx("conf_reinit: open (\"%s\", O_RDONLY) failed", conf_path
);
346 new_conf_addr
= malloc (sz
);
349 warnx("conf_reinit: malloc (%lu) failed", (unsigned long)sz
);
353 /* XXX I assume short reads won't happen here. */
354 if (read (fd
, new_conf_addr
, sz
) != (int)sz
)
356 warnx("conf_reinit: read (%d, %p, %lu) failed",
357 fd
, new_conf_addr
, (unsigned long)sz
);
362 trans
= conf_begin ();
364 /* XXX Should we not care about errors and rollback? */
365 conf_parse (trans
, new_conf_addr
, sz
);
368 trans
= conf_begin ();
370 /* Load default configuration values. */
371 conf_load_defaults (trans
);
373 /* Free potential existing configuration. */
376 for (i
= 0; i
< sizeof conf_bindings
/ sizeof conf_bindings
[0]; i
++)
377 for (cb
= LIST_FIRST (&conf_bindings
[i
]); cb
;
378 cb
= LIST_FIRST (&conf_bindings
[i
]))
379 conf_remove_now (cb
->section
, cb
->tag
);
384 conf_addr
= new_conf_addr
;
389 free (new_conf_addr
);
394 * Return the numeric value denoted by TAG in section SECTION or DEF
395 * if that tag does not exist.
398 conf_get_num (char *section
, char *tag
, int def
)
400 char *value
= conf_get_str (section
, tag
);
407 /* Validate X according to the range denoted by TAG in section SECTION. */
409 conf_match_num (char *section
, char *tag
, int x
)
411 char *value
= conf_get_str (section
, tag
);
412 int val
, min
, max
, n
;
416 n
= sscanf (value
, "%d,%d:%d", &val
, &min
, &max
);
420 warnx("conf_match_num: %s:%s %d==%d?", section
, tag
, val
, x
);
423 warnx("conf_match_num: %s:%s %d<=%d<=%d?", section
, tag
, min
, x
, max
);
424 return min
<= x
&& max
>= x
;
426 warnx("conf_match_num: section %s tag %s: invalid number spec %s",
427 section
, tag
, value
);
432 /* Return the string value denoted by TAG in section SECTION. */
434 conf_get_str (char *section
, char *tag
)
436 struct conf_binding
*cb
;
438 for (cb
= LIST_FIRST (&conf_bindings
[conf_hash (section
)]); cb
;
439 cb
= LIST_NEXT (cb
, link
))
440 if (strcasecmp (section
, cb
->section
) == 0
441 && strcasecmp (tag
, cb
->tag
) == 0)
448 /* Return the string value denoted by TAG in section SECTION
449 returns DEF if not found . */
451 conf_get_str_with_def(char *section
, char *tag
, char *def
)
453 struct conf_binding
*cb
;
455 for (cb
= LIST_FIRST (&conf_bindings
[conf_hash (section
)]); cb
;
456 cb
= LIST_NEXT (cb
, link
))
457 if (strcasecmp (section
, cb
->section
) == 0
458 && strcasecmp (tag
, cb
->tag
) == 0)
466 * Build a list of string values out of the comma separated value denoted by
470 conf_get_list (char *section
, char *tag
)
472 char *liststr
= 0, *p
, *field
, *t
;
473 struct conf_list
*list
= 0;
474 struct conf_list_node
*node
;
476 list
= malloc (sizeof *list
);
479 TAILQ_INIT (&list
->fields
);
481 liststr
= conf_get_str (section
, tag
);
484 liststr
= strdup (liststr
);
488 while ((field
= strsep (&p
, ",")) != NULL
)
490 /* Skip leading whitespace */
491 while (isspace (*field
))
493 /* Skip trailing whitespace */
495 for (t
= p
- 1; t
> field
&& isspace (*t
); t
--)
499 warnx("conf_get_list: empty field, ignoring...");
503 node
= calloc (1, sizeof *node
);
506 node
->field
= strdup (field
);
509 TAILQ_INSERT_TAIL (&list
->fields
, node
, link
);
516 conf_free_list (list
);
523 conf_get_tag_list (char *section
)
525 struct conf_list
*list
= 0;
526 struct conf_list_node
*node
;
527 struct conf_binding
*cb
;
529 list
= malloc (sizeof *list
);
532 TAILQ_INIT (&list
->fields
);
534 for (cb
= LIST_FIRST (&conf_bindings
[conf_hash (section
)]); cb
;
535 cb
= LIST_NEXT (cb
, link
))
536 if (strcasecmp (section
, cb
->section
) == 0)
539 node
= calloc (1, sizeof *node
);
542 node
->field
= strdup (cb
->tag
);
545 TAILQ_INSERT_TAIL (&list
->fields
, node
, link
);
551 conf_free_list (list
);
555 /* Decode a PEM encoded buffer. */
557 conf_decode_base64 (u_int8_t
*out
, u_int32_t
*len
, u_char
*buf
)
560 u_int8_t c1
, c2
, c3
, c4
;
564 if (*buf
> 127 || (c1
= asc2bin
[*buf
]) == 255)
568 if (*buf
> 127 || (c2
= asc2bin
[*buf
]) == 255)
577 /* Check last four bit */
581 if (strcmp ((char *)buf
, "==") == 0)
586 else if (*buf
> 127 || (c3
= asc2bin
[*buf
]) == 255)
595 /* Check last two bit */
599 if (strcmp ((char *)buf
, "="))
603 else if (*buf
> 127 || (c4
= asc2bin
[*buf
]) == 255)
610 *out
++ = (c1
<< 2) | (c2
>> 4);
611 *out
++ = (c2
<< 4) | (c3
>> 2);
612 *out
++ = (c3
<< 6) | c4
;
621 conf_free_list (struct conf_list
*list
)
623 struct conf_list_node
*node
= TAILQ_FIRST (&list
->fields
);
627 TAILQ_REMOVE (&list
->fields
, node
, link
);
631 node
= TAILQ_FIRST (&list
->fields
);
644 static struct conf_trans
*
645 conf_trans_node (int transaction
, enum conf_op op
)
647 struct conf_trans
*node
;
649 node
= calloc (1, sizeof *node
);
652 warnx("conf_trans_node: calloc (1, %lu) failed",
653 (unsigned long)sizeof *node
);
656 node
->trans
= transaction
;
658 TAILQ_INSERT_TAIL (&conf_trans_queue
, node
, link
);
662 /* Queue a set operation. */
664 conf_set (int transaction
, char *section
, char *tag
, char *value
, int override
,
667 struct conf_trans
*node
;
669 node
= conf_trans_node (transaction
, CONF_SET
);
672 node
->section
= strdup (section
);
675 warnx("conf_set: strdup (\"%s\") failed", section
);
678 node
->tag
= strdup (tag
);
681 warnx("conf_set: strdup (\"%s\") failed", tag
);
684 node
->value
= strdup (value
);
687 warnx("conf_set: strdup (\"%s\") failed", value
);
690 node
->override
= override
;
691 node
->is_default
= is_default
;
698 free (node
->section
);
704 /* Queue a remove operation. */
706 conf_remove (int transaction
, char *section
, char *tag
)
708 struct conf_trans
*node
;
710 node
= conf_trans_node (transaction
, CONF_REMOVE
);
713 node
->section
= strdup (section
);
716 warnx("conf_remove: strdup (\"%s\") failed", section
);
719 node
->tag
= strdup (tag
);
722 warnx("conf_remove: strdup (\"%s\") failed", tag
);
729 free (node
->section
);
735 /* Queue a remove section operation. */
737 conf_remove_section (int transaction
, char *section
)
739 struct conf_trans
*node
;
741 node
= conf_trans_node (transaction
, CONF_REMOVE_SECTION
);
744 node
->section
= strdup (section
);
747 warnx("conf_remove_section: strdup (\"%s\") failed", section
);
758 /* Execute all queued operations for this transaction. Cleanup. */
760 conf_end (int transaction
, int commit
)
762 struct conf_trans
*node
, *next
;
764 for (node
= TAILQ_FIRST (&conf_trans_queue
); node
; node
= next
)
766 next
= TAILQ_NEXT (node
, link
);
767 if (node
->trans
== transaction
)
773 conf_set_now (node
->section
, node
->tag
, node
->value
,
774 node
->override
, node
->is_default
);
777 conf_remove_now (node
->section
, node
->tag
);
779 case CONF_REMOVE_SECTION
:
780 conf_remove_section_now (node
->section
);
783 warnx("conf_end: unknown operation: %d", node
->op
);
785 TAILQ_REMOVE (&conf_trans_queue
, node
, link
);
787 free (node
->section
);
799 * Dump running configuration upon SIGUSR1.
800 * Configuration is "stored in reverse order", so reverse it again.
808 conf_report_dump (struct dumper
*node
)
810 /* Recursive, cleanup when we're done. */
813 conf_report_dump (node
->next
);
816 warnx("%s=\t%s", node
->s
, node
->v
);
819 warnx("%s", node
->s
);
820 if (strlen (node
->s
) > 0)
830 struct conf_binding
*cb
, *last
= 0;
832 char *current_section
= (char *)0;
833 struct dumper
*dumper
, *dnode
;
835 dumper
= dnode
= (struct dumper
*)calloc (1, sizeof *dumper
);
839 warnx("conf_report: dumping running configuration");
841 for (i
= 0; i
< sizeof conf_bindings
/ sizeof conf_bindings
[0]; i
++)
842 for (cb
= LIST_FIRST (&conf_bindings
[i
]); cb
;
843 cb
= LIST_NEXT (cb
, link
))
847 /* Dump this entry. */
848 if (!current_section
|| strcmp (cb
->section
, current_section
))
852 len
= strlen (current_section
) + 3;
853 dnode
->s
= malloc (len
);
857 snprintf (dnode
->s
, len
, "[%s]", current_section
);
859 = (struct dumper
*)calloc (1, sizeof (struct dumper
));
866 = (struct dumper
*)calloc (1, sizeof (struct dumper
));
871 current_section
= cb
->section
;
874 dnode
->v
= cb
->value
;
875 dnode
->next
= (struct dumper
*)calloc (1, sizeof (struct dumper
));
885 len
= strlen (last
->section
) + 3;
886 dnode
->s
= malloc (len
);
889 snprintf (dnode
->s
, len
, "[%s]", last
->section
);
892 conf_report_dump (dumper
);
897 warnx("conf_report: malloc/calloc failed");
898 while ((dnode
= dumper
) != 0)
900 dumper
= dumper
->next
;