1*f8f1e192Sniklas /* $OpenBSD: conf.c,v 1.10 1999/08/05 22:41:08 niklas Exp $ */ 2*f8f1e192Sniklas /* $EOM: conf.c,v 1.19 1999/08/05 14:57:59 niklas Exp $ */ 32040585eSniklas 42040585eSniklas /* 5a9753648Sniklas * Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved. 62040585eSniklas * 72040585eSniklas * Redistribution and use in source and binary forms, with or without 82040585eSniklas * modification, are permitted provided that the following conditions 92040585eSniklas * are met: 102040585eSniklas * 1. Redistributions of source code must retain the above copyright 112040585eSniklas * notice, this list of conditions and the following disclaimer. 122040585eSniklas * 2. Redistributions in binary form must reproduce the above copyright 132040585eSniklas * notice, this list of conditions and the following disclaimer in the 142040585eSniklas * documentation and/or other materials provided with the distribution. 152040585eSniklas * 3. All advertising materials mentioning features or use of this software 162040585eSniklas * must display the following acknowledgement: 172040585eSniklas * This product includes software developed by Ericsson Radio Systems. 182040585eSniklas * 4. The name of the author may not be used to endorse or promote products 192040585eSniklas * derived from this software without specific prior written permission. 202040585eSniklas * 212040585eSniklas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 222040585eSniklas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 232040585eSniklas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 242040585eSniklas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 252040585eSniklas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 262040585eSniklas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 272040585eSniklas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 282040585eSniklas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 292040585eSniklas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 302040585eSniklas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 312040585eSniklas */ 322040585eSniklas 332040585eSniklas /* 342040585eSniklas * This code was written under funding by Ericsson Radio Systems. 352040585eSniklas */ 362040585eSniklas 372040585eSniklas #include <sys/param.h> 382040585eSniklas #include <sys/mman.h> 392040585eSniklas #include <sys/queue.h> 402040585eSniklas #include <sys/stat.h> 412040585eSniklas #include <ctype.h> 422040585eSniklas #include <fcntl.h> 432040585eSniklas #include <stdio.h> 442040585eSniklas #include <stdlib.h> 452040585eSniklas #include <string.h> 462040585eSniklas #include <unistd.h> 472040585eSniklas 48a2d30fd1Sniklas #include "sysdep.h" 49a2d30fd1Sniklas 50a2d30fd1Sniklas #include "app.h" 512040585eSniklas #include "conf.h" 522040585eSniklas #include "log.h" 532040585eSniklas 54*f8f1e192Sniklas struct conf_trans { 55*f8f1e192Sniklas TAILQ_ENTRY (conf_trans) link; 56*f8f1e192Sniklas int trans; 57*f8f1e192Sniklas enum conf_op { CONF_SET, CONF_REMOVE, CONF_REMOVE_SECTION } op; 58*f8f1e192Sniklas char *section; 59*f8f1e192Sniklas char *tag; 60*f8f1e192Sniklas char *value; 61*f8f1e192Sniklas int override; 62*f8f1e192Sniklas }; 63*f8f1e192Sniklas 64*f8f1e192Sniklas TAILQ_HEAD (conf_trans_head, conf_trans) conf_trans_queue; 65*f8f1e192Sniklas 662040585eSniklas /* 672040585eSniklas * Radix-64 Encoding. 682040585eSniklas */ 692040585eSniklas const u_int8_t bin2asc[] = 702040585eSniklas "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 712040585eSniklas 722040585eSniklas const u_int8_t asc2bin[] = 732040585eSniklas { 742040585eSniklas 255, 255, 255, 255, 255, 255, 255, 255, 752040585eSniklas 255, 255, 255, 255, 255, 255, 255, 255, 762040585eSniklas 255, 255, 255, 255, 255, 255, 255, 255, 772040585eSniklas 255, 255, 255, 255, 255, 255, 255, 255, 782040585eSniklas 255, 255, 255, 255, 255, 255, 255, 255, 792040585eSniklas 255, 255, 255, 62, 255, 255, 255, 63, 802040585eSniklas 52, 53, 54, 55, 56, 57, 58, 59, 812040585eSniklas 60, 61, 255, 255, 255, 255, 255, 255, 822040585eSniklas 255, 0, 1, 2, 3, 4, 5, 6, 832040585eSniklas 7, 8, 9, 10, 11, 12, 13, 14, 842040585eSniklas 15, 16, 17, 18, 19, 20, 21, 22, 852040585eSniklas 23, 24, 25, 255, 255, 255, 255, 255, 862040585eSniklas 255, 26, 27, 28, 29, 30, 31, 32, 872040585eSniklas 33, 34, 35, 36, 37, 38, 39, 40, 882040585eSniklas 41, 42, 43, 44, 45, 46, 47, 48, 892040585eSniklas 49, 50, 51, 255, 255, 255, 255, 255 902040585eSniklas }; 912040585eSniklas 922040585eSniklas struct conf_binding { 932040585eSniklas LIST_ENTRY (conf_binding) link; 942040585eSniklas char *section; 952040585eSniklas char *tag; 962040585eSniklas char *value; 972040585eSniklas }; 982040585eSniklas 992040585eSniklas char *conf_path = CONFIG_FILE; 100*f8f1e192Sniklas LIST_HEAD (conf_bindings, conf_binding) conf_bindings[256]; 1012040585eSniklas 1022040585eSniklas static char *conf_addr; 1032040585eSniklas 104*f8f1e192Sniklas static __inline__ u_int8_t 105*f8f1e192Sniklas conf_hash (char *s) 106*f8f1e192Sniklas { 107*f8f1e192Sniklas u_int8_t hash = 0; 108*f8f1e192Sniklas 109*f8f1e192Sniklas while (*s) 110*f8f1e192Sniklas { 111*f8f1e192Sniklas hash = ((hash << 1) | (hash >> 7)) ^ tolower (*s); 112*f8f1e192Sniklas s++; 113*f8f1e192Sniklas } 114*f8f1e192Sniklas return hash; 115*f8f1e192Sniklas } 116*f8f1e192Sniklas 117*f8f1e192Sniklas /* 118*f8f1e192Sniklas * Insert a tag-value combination from LINE (the equal sign is at POS) 119*f8f1e192Sniklas */ 120*f8f1e192Sniklas static int 121*f8f1e192Sniklas conf_remove_now (char *section, char *tag) 122*f8f1e192Sniklas { 123*f8f1e192Sniklas struct conf_binding *cb, *next; 124*f8f1e192Sniklas 125*f8f1e192Sniklas for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next) 126*f8f1e192Sniklas { 127*f8f1e192Sniklas next = LIST_NEXT (cb, link); 128*f8f1e192Sniklas if (strcasecmp (cb->section, section) == 0 129*f8f1e192Sniklas && strcasecmp (cb->tag, tag) == 0) 130*f8f1e192Sniklas { 131*f8f1e192Sniklas LIST_REMOVE (cb, link); 132*f8f1e192Sniklas log_debug (LOG_MISC, 70, "[%s]:%s->%s removed", section, tag, 133*f8f1e192Sniklas cb->value); 134*f8f1e192Sniklas free (cb->section); 135*f8f1e192Sniklas free (cb->tag); 136*f8f1e192Sniklas free (cb->value); 137*f8f1e192Sniklas free (cb); 138*f8f1e192Sniklas return 0; 139*f8f1e192Sniklas } 140*f8f1e192Sniklas } 141*f8f1e192Sniklas return 1; 142*f8f1e192Sniklas } 143*f8f1e192Sniklas 144*f8f1e192Sniklas static int 145*f8f1e192Sniklas conf_remove_section_now (char *section) 146*f8f1e192Sniklas { 147*f8f1e192Sniklas struct conf_binding *cb, *next; 148*f8f1e192Sniklas int unseen = 1; 149*f8f1e192Sniklas 150*f8f1e192Sniklas for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; cb = next) 151*f8f1e192Sniklas { 152*f8f1e192Sniklas next = LIST_NEXT (cb, link); 153*f8f1e192Sniklas if (strcasecmp (cb->section, section) == 0) 154*f8f1e192Sniklas { 155*f8f1e192Sniklas unseen = 0; 156*f8f1e192Sniklas LIST_REMOVE (cb, link); 157*f8f1e192Sniklas log_debug (LOG_MISC, 70, "[%s]:%s->%s removed", section, cb->tag, 158*f8f1e192Sniklas cb->value); 159*f8f1e192Sniklas free (cb->section); 160*f8f1e192Sniklas free (cb->tag); 161*f8f1e192Sniklas free (cb->value); 162*f8f1e192Sniklas free (cb); 163*f8f1e192Sniklas } 164*f8f1e192Sniklas } 165*f8f1e192Sniklas return unseen; 166*f8f1e192Sniklas } 167*f8f1e192Sniklas 1682040585eSniklas /* 1692040585eSniklas * Insert a tag-value combination from LINE (the equal sign is at POS) 1702040585eSniklas * into SECTION of our configuration database. 1712040585eSniklas */ 172*f8f1e192Sniklas static int 173*f8f1e192Sniklas conf_set_now (char *section, char *tag, char *value, int override) 1742040585eSniklas { 175*f8f1e192Sniklas struct conf_binding *node = 0; 1762040585eSniklas 177*f8f1e192Sniklas if (override) 178*f8f1e192Sniklas conf_remove_now (section, tag); 179*f8f1e192Sniklas else if (conf_get_str (section, tag)) 1802040585eSniklas { 1812040585eSniklas log_print ("conf_set: duplicate tag [%s]:%s, ignoring...\n", section, 182*f8f1e192Sniklas tag); 183*f8f1e192Sniklas return 1; 1842040585eSniklas } 185*f8f1e192Sniklas 186*f8f1e192Sniklas node = calloc (1, sizeof *node); 187*f8f1e192Sniklas if (!node) 188*f8f1e192Sniklas { 189*f8f1e192Sniklas log_error ("conf_set: calloc (1, %d) failed", sizeof *node); 190*f8f1e192Sniklas return 1; 191*f8f1e192Sniklas } 192*f8f1e192Sniklas node->section = section; 193*f8f1e192Sniklas node->tag = tag; 194*f8f1e192Sniklas node->value = value; 195*f8f1e192Sniklas 196*f8f1e192Sniklas LIST_INSERT_HEAD (&conf_bindings[conf_hash (section)], node, link); 197*f8f1e192Sniklas log_debug (LOG_MISC, 70, "[%s]:%s->%s", node->section, node->tag, 1982040585eSniklas node->value); 199*f8f1e192Sniklas return 0; 2002040585eSniklas } 2012040585eSniklas 2022040585eSniklas /* 2032040585eSniklas * Parse the line LINE of SZ bytes. Skip Comments, recognize section 2042040585eSniklas * headers and feed tag-value pairs into our configuration database. 2052040585eSniklas */ 2062040585eSniklas static void 207*f8f1e192Sniklas conf_parse_line (int trans, char *line, size_t sz) 2082040585eSniklas { 2092040585eSniklas char *cp = line; 2102040585eSniklas int i; 2112040585eSniklas static char *section = 0; 2122040585eSniklas static int ln = 0; 2132040585eSniklas 2142040585eSniklas ln++; 2152040585eSniklas 2162040585eSniklas /* Lines starting with '#' or ';' are comments. */ 2172040585eSniklas if (*line == '#' || *line == ';') 2182040585eSniklas return; 2192040585eSniklas 2202040585eSniklas /* '[section]' parsing... */ 2212040585eSniklas if (*line == '[') 2222040585eSniklas { 2232040585eSniklas for (i = 1; i < sz; i++) 2242040585eSniklas if (line[i] == ']') 2252040585eSniklas break; 2262040585eSniklas if (i == sz) 2272040585eSniklas { 2282040585eSniklas log_print ("conf_parse_line: %d:" 2292040585eSniklas "non-matched ']', ignoring until next section", ln); 2302040585eSniklas section = 0; 2312040585eSniklas return; 2322040585eSniklas } 2332040585eSniklas section = malloc (i); 2342040585eSniklas strncpy (section, line + 1, i - 1); 2352040585eSniklas section[i - 1] = '\0'; 2362040585eSniklas return; 2372040585eSniklas } 2382040585eSniklas 2392040585eSniklas /* Deal with assignments. */ 2402040585eSniklas for (i = 0; i < sz; i++) 2412040585eSniklas if (cp[i] == '=') 2422040585eSniklas { 2432040585eSniklas /* If no section, we are ignoring the lines. */ 2442040585eSniklas if (!section) 2452040585eSniklas { 2462040585eSniklas log_print ("conf_parse_line: %d: ignoring line due to no section", 2472040585eSniklas ln); 2482040585eSniklas return; 2492040585eSniklas } 250*f8f1e192Sniklas line[strcspn (line, " \t=")] = '\0'; 251*f8f1e192Sniklas /* XXX Perhaps should we not ignore errors? */ 252*f8f1e192Sniklas conf_set (trans, section, line, 253*f8f1e192Sniklas line + i + 1 + strspn (line + i + 1, " \t"), 0); 2542040585eSniklas return; 2552040585eSniklas } 2562040585eSniklas 2572040585eSniklas /* Other non-empty lines are wierd. */ 2582040585eSniklas i = strspn (line, " \t"); 2592040585eSniklas if (line[i]) 2602040585eSniklas log_print ("conf_parse_line: %d: syntax error", ln); 2612040585eSniklas 2622040585eSniklas return; 2632040585eSniklas } 2642040585eSniklas 2652040585eSniklas /* Parse the mapped configuration file. */ 2662040585eSniklas static void 267*f8f1e192Sniklas conf_parse (int trans, char *buf, size_t sz) 2682040585eSniklas { 269*f8f1e192Sniklas char *cp = buf; 270*f8f1e192Sniklas char *bufend = buf + sz; 2712040585eSniklas char *line; 2722040585eSniklas 2732040585eSniklas line = cp; 274*f8f1e192Sniklas while (cp < bufend) 2752040585eSniklas { 2762040585eSniklas if (*cp == '\n') 2772040585eSniklas { 2782040585eSniklas /* Check for escaped newlines. */ 279*f8f1e192Sniklas if (cp > buf && *(cp - 1) == '\\') 2802040585eSniklas *(cp - 1) = *cp = ' '; 2812040585eSniklas else 2822040585eSniklas { 2832040585eSniklas *cp = '\0'; 284*f8f1e192Sniklas conf_parse_line (trans, line, cp - line); 2852040585eSniklas line = cp + 1; 2862040585eSniklas } 2872040585eSniklas } 2882040585eSniklas cp++; 2892040585eSniklas } 2902040585eSniklas if (cp != line) 2912040585eSniklas log_print ("conf_parse: last line non-terminated, ignored."); 2922040585eSniklas } 2932040585eSniklas 2942040585eSniklas void 2952040585eSniklas conf_init (void) 2962040585eSniklas { 297*f8f1e192Sniklas int i; 2982040585eSniklas 299*f8f1e192Sniklas for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) 300*f8f1e192Sniklas LIST_INIT (&conf_bindings[i]); 301*f8f1e192Sniklas TAILQ_INIT (&conf_trans_queue); 302*f8f1e192Sniklas conf_reinit (); 3032040585eSniklas } 3042040585eSniklas 305*f8f1e192Sniklas /* Open the config file and map it into our address space, then parse it. */ 306*f8f1e192Sniklas void 307*f8f1e192Sniklas conf_reinit (void) 308*f8f1e192Sniklas { 309*f8f1e192Sniklas struct conf_binding *cb = 0; 310*f8f1e192Sniklas int fd, i, trans; 311*f8f1e192Sniklas struct stat st; 312*f8f1e192Sniklas off_t sz; 313*f8f1e192Sniklas char *new_conf_addr = 0; 314*f8f1e192Sniklas 3152040585eSniklas fd = open (conf_path, O_RDONLY); 3162040585eSniklas if (fd == -1) 317*f8f1e192Sniklas { 318*f8f1e192Sniklas log_error ("open (\"%s\", O_RDONLY) failed", conf_path); 319*f8f1e192Sniklas return; 320*f8f1e192Sniklas } 3212040585eSniklas if (fstat (fd, &st) == -1) 322*f8f1e192Sniklas { 323*f8f1e192Sniklas log_error ("fstat (%d, &st) failed", fd); 324*f8f1e192Sniklas goto fail; 325*f8f1e192Sniklas } 326*f8f1e192Sniklas sz = st.st_size; 327*f8f1e192Sniklas new_conf_addr = malloc (sz); 328*f8f1e192Sniklas if (!new_conf_addr) 329*f8f1e192Sniklas { 330*f8f1e192Sniklas log_error ("malloc (%d) failed", sz); 331*f8f1e192Sniklas goto fail; 332*f8f1e192Sniklas } 3332040585eSniklas /* XXX I assume short reads won't happen here. */ 334*f8f1e192Sniklas if (read (fd, new_conf_addr, sz) != sz) 335*f8f1e192Sniklas { 336*f8f1e192Sniklas log_error ("read (%d, %p, %d) failed", fd, new_conf_addr, sz); 337*f8f1e192Sniklas goto fail; 338*f8f1e192Sniklas } 3392040585eSniklas close (fd); 3402040585eSniklas 341*f8f1e192Sniklas trans = conf_begin (); 342*f8f1e192Sniklas 343*f8f1e192Sniklas /* XXX Should we not care about errors and rollback? */ 344*f8f1e192Sniklas conf_parse (trans, new_conf_addr, sz); 345*f8f1e192Sniklas 346*f8f1e192Sniklas /* Free potential existing configuration. */ 347*f8f1e192Sniklas if (conf_addr) 348*f8f1e192Sniklas { 349*f8f1e192Sniklas for (i = 0; i < sizeof conf_bindings / sizeof conf_bindings[0]; i++) 350*f8f1e192Sniklas for (cb = LIST_FIRST (&conf_bindings[i]); cb; 351*f8f1e192Sniklas cb = LIST_FIRST (&conf_bindings[i])) 352*f8f1e192Sniklas conf_remove_now (cb->section, cb->tag); 353*f8f1e192Sniklas free (conf_addr); 354*f8f1e192Sniklas } 355*f8f1e192Sniklas 356*f8f1e192Sniklas conf_end (trans, 1); 357*f8f1e192Sniklas conf_addr = new_conf_addr; 358*f8f1e192Sniklas return; 359*f8f1e192Sniklas 360*f8f1e192Sniklas fail: 361*f8f1e192Sniklas if (new_conf_addr) 362*f8f1e192Sniklas free (new_conf_addr); 363*f8f1e192Sniklas close (fd); 3642040585eSniklas } 3652040585eSniklas 366a2d30fd1Sniklas /* 367a2d30fd1Sniklas * Return the numeric value denoted by TAG in section SECTION or DEF 368a2d30fd1Sniklas * if that tag does not exist. 369a2d30fd1Sniklas */ 3702040585eSniklas int 371a2d30fd1Sniklas conf_get_num (char *section, char *tag, int def) 3722040585eSniklas { 3732040585eSniklas char *value = conf_get_str (section, tag); 3742040585eSniklas 3752040585eSniklas if (value) 3762040585eSniklas return atoi (value); 377a2d30fd1Sniklas return def; 3782040585eSniklas } 3792040585eSniklas 38082d8fe06Sniklas /* Validate X according to the range denoted by TAG in section SECTION. */ 38182d8fe06Sniklas int 38282d8fe06Sniklas conf_match_num (char *section, char *tag, int x) 38382d8fe06Sniklas { 38482d8fe06Sniklas char *value = conf_get_str (section, tag); 38582d8fe06Sniklas int val, min, max, n; 38682d8fe06Sniklas 38782d8fe06Sniklas if (!value) 38882d8fe06Sniklas return 0; 38982d8fe06Sniklas n = sscanf (value, "%d,%d:%d", &val, &min, &max); 39082d8fe06Sniklas switch (n) 39182d8fe06Sniklas { 39282d8fe06Sniklas case 1: 39382d8fe06Sniklas log_debug (LOG_MISC, 90, "conf_match_num: %s:%s %d==%d?", section, tag, 39482d8fe06Sniklas val, x); 39582d8fe06Sniklas return x == val; 39682d8fe06Sniklas case 3: 39782d8fe06Sniklas log_debug (LOG_MISC, 90, "conf_match_num: %s:%s %d<=%d<=%d?", section, 39882d8fe06Sniklas tag, min, x, max); 39982d8fe06Sniklas return min <= x && max >= x; 40082d8fe06Sniklas default: 40182d8fe06Sniklas log_error ("conf_match_num: section %s tag %s: invalid number spec %s", 40282d8fe06Sniklas section, tag, value); 40382d8fe06Sniklas } 40482d8fe06Sniklas return 0; 40582d8fe06Sniklas } 40682d8fe06Sniklas 4072040585eSniklas /* Return the string value denoted by TAG in section SECTION. */ 4082040585eSniklas char * 4092040585eSniklas conf_get_str (char *section, char *tag) 4102040585eSniklas { 4112040585eSniklas struct conf_binding *cb; 4122040585eSniklas 413*f8f1e192Sniklas for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; 414*f8f1e192Sniklas cb = LIST_NEXT (cb, link)) 4152040585eSniklas if (strcasecmp (section, cb->section) == 0 4162040585eSniklas && strcasecmp (tag, cb->tag) == 0) 4172040585eSniklas { 418*f8f1e192Sniklas log_debug (LOG_MISC, 60, "conf_get_str: [%s]:%s->%s", section, 4192040585eSniklas tag, cb->value); 4202040585eSniklas return cb->value; 4212040585eSniklas } 4222040585eSniklas log_debug (LOG_MISC, 60, 423*f8f1e192Sniklas "conf_get_str: configuration value not found [%s]:%s", section, 4242040585eSniklas tag); 4252040585eSniklas return 0; 4262040585eSniklas } 4272040585eSniklas 428a9753648Sniklas /* 429a9753648Sniklas * Build a list of string values out of the comma separated value denoted by 430a9753648Sniklas * TAG in SECTION. 431a9753648Sniklas */ 4322040585eSniklas struct conf_list * 4332040585eSniklas conf_get_list (char *section, char *tag) 4342040585eSniklas { 4352040585eSniklas char *liststr = 0, *p, *field; 4362040585eSniklas struct conf_list *list = 0; 4372040585eSniklas struct conf_list_node *node; 4382040585eSniklas 4392040585eSniklas list = malloc (sizeof *list); 4402040585eSniklas if (!list) 4412040585eSniklas goto cleanup; 4422040585eSniklas TAILQ_INIT (&list->fields); 4432040585eSniklas list->cnt = 0; 4442040585eSniklas liststr = conf_get_str (section, tag); 4452040585eSniklas if (!liststr) 4462040585eSniklas goto cleanup; 4472040585eSniklas liststr = strdup (liststr); 4482040585eSniklas if (!liststr) 4492040585eSniklas goto cleanup; 4502040585eSniklas p = liststr; 4512040585eSniklas while ((field = strsep (&p, ", \t")) != NULL) 4522040585eSniklas { 4532040585eSniklas if (*field == '\0') 4542040585eSniklas { 4552040585eSniklas log_print ("conf_get_list: empty field, ignoring..."); 4562040585eSniklas continue; 4572040585eSniklas } 4582040585eSniklas list->cnt++; 459a9753648Sniklas node = calloc (1, sizeof *node); 4602040585eSniklas if (!node) 4612040585eSniklas goto cleanup; 462a9753648Sniklas node->field = strdup (field); 463a9753648Sniklas if (!node->field) 464a9753648Sniklas goto cleanup; 4652040585eSniklas TAILQ_INSERT_TAIL (&list->fields, node, link); 4662040585eSniklas } 467a9753648Sniklas free (liststr); 4682040585eSniklas return list; 4692040585eSniklas 4702040585eSniklas cleanup: 4712040585eSniklas if (list) 4722040585eSniklas conf_free_list (list); 4732040585eSniklas if (liststr) 4742040585eSniklas free (liststr); 4752040585eSniklas return 0; 4762040585eSniklas } 4772040585eSniklas 47882d8fe06Sniklas struct conf_list * 47982d8fe06Sniklas conf_get_tag_list (char *section) 48082d8fe06Sniklas { 48182d8fe06Sniklas struct conf_list *list = 0; 48282d8fe06Sniklas struct conf_list_node *node; 48382d8fe06Sniklas struct conf_binding *cb; 48482d8fe06Sniklas 48582d8fe06Sniklas list = malloc (sizeof *list); 48682d8fe06Sniklas if (!list) 48782d8fe06Sniklas goto cleanup; 48882d8fe06Sniklas TAILQ_INIT (&list->fields); 48982d8fe06Sniklas list->cnt = 0; 490*f8f1e192Sniklas for (cb = LIST_FIRST (&conf_bindings[conf_hash (section)]); cb; 491*f8f1e192Sniklas cb = LIST_NEXT (cb, link)) 49282d8fe06Sniklas if (strcasecmp (section, cb->section) == 0) 49382d8fe06Sniklas { 49482d8fe06Sniklas list->cnt++; 495a9753648Sniklas node = calloc (1, sizeof *node); 49682d8fe06Sniklas if (!node) 49782d8fe06Sniklas goto cleanup; 498a9753648Sniklas node->field = strdup (cb->tag); 499a9753648Sniklas if (!node->field) 500a9753648Sniklas goto cleanup; 50182d8fe06Sniklas TAILQ_INSERT_TAIL (&list->fields, node, link); 50282d8fe06Sniklas } 50382d8fe06Sniklas return list; 50482d8fe06Sniklas 50582d8fe06Sniklas cleanup: 50682d8fe06Sniklas if (list) 50782d8fe06Sniklas conf_free_list (list); 50882d8fe06Sniklas return 0; 50982d8fe06Sniklas } 51082d8fe06Sniklas 5112040585eSniklas /* Decode a PEM encoded buffer. */ 5122040585eSniklas int 5132040585eSniklas conf_decode_base64(u_int8_t *out, u_int32_t *len, u_char *buf) 5142040585eSniklas { 5152040585eSniklas u_int32_t c = 0; 5162040585eSniklas u_int8_t c1, c2, c3, c4; 5172040585eSniklas 5182040585eSniklas while (*buf) 5192040585eSniklas { 5202040585eSniklas if (*buf > 127 || (c1 = asc2bin[*buf]) == 255) 5212040585eSniklas return 0; 5222040585eSniklas buf++; 5232040585eSniklas 5242040585eSniklas if (*buf > 127 || (c2 = asc2bin[*buf]) == 255) 5252040585eSniklas return 0; 5262040585eSniklas buf++; 5272040585eSniklas 5282040585eSniklas if (*buf == '=') 5292040585eSniklas { 5302040585eSniklas c3 = c4 = 0; 5312040585eSniklas c++; 5322040585eSniklas 5332040585eSniklas /* Check last four bit */ 5342040585eSniklas if (c2 & 0xF) 5352040585eSniklas return 0; 5362040585eSniklas 5372040585eSniklas if (!strcmp (buf, "==")) 5382040585eSniklas buf++; 5392040585eSniklas else 5402040585eSniklas return 0; 5412040585eSniklas } 5422040585eSniklas else if (*buf > 127 || (c3 = asc2bin[*buf]) == 255) 5432040585eSniklas return 0; 5442040585eSniklas else 5452040585eSniklas { 5462040585eSniklas if (*++buf == '=') 5472040585eSniklas { 5482040585eSniklas c4 = 0; 5492040585eSniklas c += 2; 5502040585eSniklas 5512040585eSniklas /* Check last two bit */ 5522040585eSniklas if (c3 & 3) 5532040585eSniklas return 0; 5542040585eSniklas 5552040585eSniklas if (strcmp(buf, "=")) 5562040585eSniklas return 0; 5572040585eSniklas 5582040585eSniklas } 5592040585eSniklas else if (*buf > 127 || (c4 = asc2bin[*buf]) == 255) 5602040585eSniklas return 0; 5612040585eSniklas else 5622040585eSniklas c += 3; 5632040585eSniklas } 5642040585eSniklas 5652040585eSniklas buf++; 5662040585eSniklas *out++ = (c1 << 2) | (c2 >> 4); 5672040585eSniklas *out++ = (c2 << 4) | (c3 >> 2); 5682040585eSniklas *out++ = (c3 << 6) | c4; 5692040585eSniklas } 5702040585eSniklas 5712040585eSniklas *len = c; 5722040585eSniklas return 1; 5732040585eSniklas 5742040585eSniklas } 5752040585eSniklas 5762040585eSniklas /* Read a line from a stream to the buffer. */ 5772040585eSniklas int 5782040585eSniklas conf_get_line (FILE *stream, char *buf, u_int32_t len) 5792040585eSniklas { 5802040585eSniklas char c; 5812040585eSniklas 5822040585eSniklas while (len-- > 1) 5832040585eSniklas { 5842040585eSniklas c = fgetc (stream); 5852040585eSniklas if (c == '\n') 5862040585eSniklas { 5872040585eSniklas *buf = 0; 5882040585eSniklas return 1; 5892040585eSniklas } 5902040585eSniklas else if (c == EOF) 5912040585eSniklas break; 5922040585eSniklas 5932040585eSniklas *buf++ = c; 5942040585eSniklas } 5952040585eSniklas 5962040585eSniklas *buf = 0; 5972040585eSniklas return 0; 5982040585eSniklas } 5992040585eSniklas 6002040585eSniklas void 6012040585eSniklas conf_free_list (struct conf_list *list) 6022040585eSniklas { 603a9753648Sniklas struct conf_list_node *node = TAILQ_FIRST (&list->fields); 604a9753648Sniklas 605a9753648Sniklas while (node) 606a9753648Sniklas { 607a9753648Sniklas TAILQ_REMOVE (&list->fields, node, link); 608a9753648Sniklas if (node->field) 609a9753648Sniklas free (node->field); 610a9753648Sniklas free (node); 611a9753648Sniklas node = TAILQ_FIRST (&list->fields); 612a9753648Sniklas } 6132040585eSniklas free (list); 6142040585eSniklas } 615*f8f1e192Sniklas 616*f8f1e192Sniklas int 617*f8f1e192Sniklas conf_begin (void) 618*f8f1e192Sniklas { 619*f8f1e192Sniklas static int seq = 0; 620*f8f1e192Sniklas 621*f8f1e192Sniklas return ++seq; 622*f8f1e192Sniklas } 623*f8f1e192Sniklas 624*f8f1e192Sniklas static struct conf_trans * 625*f8f1e192Sniklas conf_trans_node (int transaction, enum conf_op op) 626*f8f1e192Sniklas { 627*f8f1e192Sniklas struct conf_trans *node; 628*f8f1e192Sniklas 629*f8f1e192Sniklas node = calloc (1, sizeof *node); 630*f8f1e192Sniklas if (!node) 631*f8f1e192Sniklas { 632*f8f1e192Sniklas log_error ("conf_trans_node: calloc (1, %d) failed", sizeof *node); 633*f8f1e192Sniklas return 0; 634*f8f1e192Sniklas } 635*f8f1e192Sniklas node->trans = transaction; 636*f8f1e192Sniklas node->op = op; 637*f8f1e192Sniklas TAILQ_INSERT_TAIL (&conf_trans_queue, node, link); 638*f8f1e192Sniklas return node; 639*f8f1e192Sniklas } 640*f8f1e192Sniklas 641*f8f1e192Sniklas /* Queue a set operation. */ 642*f8f1e192Sniklas int 643*f8f1e192Sniklas conf_set (int transaction, char *section, char *tag, char *value, int override) 644*f8f1e192Sniklas { 645*f8f1e192Sniklas struct conf_trans *node; 646*f8f1e192Sniklas 647*f8f1e192Sniklas node = conf_trans_node (transaction, CONF_SET); 648*f8f1e192Sniklas if (!node) 649*f8f1e192Sniklas return 1; 650*f8f1e192Sniklas node->section = strdup (section); 651*f8f1e192Sniklas if (!node->section) 652*f8f1e192Sniklas { 653*f8f1e192Sniklas log_error ("conf_set: strdup (\"%s\") failed", section); 654*f8f1e192Sniklas goto fail; 655*f8f1e192Sniklas } 656*f8f1e192Sniklas node->tag = strdup (tag); 657*f8f1e192Sniklas if (!node->tag) 658*f8f1e192Sniklas { 659*f8f1e192Sniklas log_error ("conf_set: strdup (\"%s\") failed", tag); 660*f8f1e192Sniklas goto fail; 661*f8f1e192Sniklas } 662*f8f1e192Sniklas node->value = strdup (value); 663*f8f1e192Sniklas if (!node->value) 664*f8f1e192Sniklas { 665*f8f1e192Sniklas log_error ("conf_set: strdup (\"%s\") failed", value); 666*f8f1e192Sniklas goto fail; 667*f8f1e192Sniklas } 668*f8f1e192Sniklas node->override = override; 669*f8f1e192Sniklas return 0; 670*f8f1e192Sniklas 671*f8f1e192Sniklas fail: 672*f8f1e192Sniklas if (node->tag) 673*f8f1e192Sniklas free (node->tag); 674*f8f1e192Sniklas if (node->section) 675*f8f1e192Sniklas free (node->section); 676*f8f1e192Sniklas if (node) 677*f8f1e192Sniklas free (node); 678*f8f1e192Sniklas return 1; 679*f8f1e192Sniklas } 680*f8f1e192Sniklas 681*f8f1e192Sniklas /* Queue a remove operation. */ 682*f8f1e192Sniklas int 683*f8f1e192Sniklas conf_remove (int transaction, char *section, char *tag) 684*f8f1e192Sniklas { 685*f8f1e192Sniklas struct conf_trans *node; 686*f8f1e192Sniklas 687*f8f1e192Sniklas node = conf_trans_node (transaction, CONF_REMOVE); 688*f8f1e192Sniklas if (!node) 689*f8f1e192Sniklas goto fail; 690*f8f1e192Sniklas node->section = strdup (section); 691*f8f1e192Sniklas if (!node->section) 692*f8f1e192Sniklas { 693*f8f1e192Sniklas log_error ("conf_remove: strdup (\"%s\") failed", section); 694*f8f1e192Sniklas goto fail; 695*f8f1e192Sniklas } 696*f8f1e192Sniklas node->tag = strdup (tag); 697*f8f1e192Sniklas if (!node->tag) 698*f8f1e192Sniklas { 699*f8f1e192Sniklas log_error ("conf_remove: strdup (\"%s\") failed", tag); 700*f8f1e192Sniklas goto fail; 701*f8f1e192Sniklas } 702*f8f1e192Sniklas return 0; 703*f8f1e192Sniklas 704*f8f1e192Sniklas fail: 705*f8f1e192Sniklas if (node->section) 706*f8f1e192Sniklas free (node->section); 707*f8f1e192Sniklas if (node) 708*f8f1e192Sniklas free (node); 709*f8f1e192Sniklas return 1; 710*f8f1e192Sniklas } 711*f8f1e192Sniklas 712*f8f1e192Sniklas /* Queue a remove section operation. */ 713*f8f1e192Sniklas int 714*f8f1e192Sniklas conf_remove_section (int transaction, char *section) 715*f8f1e192Sniklas { 716*f8f1e192Sniklas struct conf_trans *node; 717*f8f1e192Sniklas 718*f8f1e192Sniklas node = conf_trans_node (transaction, CONF_REMOVE_SECTION); 719*f8f1e192Sniklas if (!node) 720*f8f1e192Sniklas goto fail; 721*f8f1e192Sniklas node->section = strdup (section); 722*f8f1e192Sniklas if (!node->section) 723*f8f1e192Sniklas { 724*f8f1e192Sniklas log_error ("conf_remove_section: strdup (\"%s\") failed", section); 725*f8f1e192Sniklas goto fail; 726*f8f1e192Sniklas } 727*f8f1e192Sniklas return 0; 728*f8f1e192Sniklas 729*f8f1e192Sniklas fail: 730*f8f1e192Sniklas if (node) 731*f8f1e192Sniklas free (node); 732*f8f1e192Sniklas return 1; 733*f8f1e192Sniklas } 734*f8f1e192Sniklas 735*f8f1e192Sniklas /* Execute all queued operations for this transaction. Cleanup. */ 736*f8f1e192Sniklas int 737*f8f1e192Sniklas conf_end (int transaction, int commit) 738*f8f1e192Sniklas { 739*f8f1e192Sniklas struct conf_trans *node, *next; 740*f8f1e192Sniklas 741*f8f1e192Sniklas for (node = TAILQ_FIRST (&conf_trans_queue); node; node = next) 742*f8f1e192Sniklas { 743*f8f1e192Sniklas next = TAILQ_NEXT (node, link); 744*f8f1e192Sniklas if (node->trans == transaction) 745*f8f1e192Sniklas { 746*f8f1e192Sniklas if (commit) 747*f8f1e192Sniklas switch (node->op) 748*f8f1e192Sniklas { 749*f8f1e192Sniklas case CONF_SET: 750*f8f1e192Sniklas conf_set_now (node->section, node->tag, node->value, 751*f8f1e192Sniklas node->override); 752*f8f1e192Sniklas break; 753*f8f1e192Sniklas case CONF_REMOVE: 754*f8f1e192Sniklas conf_remove_now (node->section, node->tag); 755*f8f1e192Sniklas break; 756*f8f1e192Sniklas case CONF_REMOVE_SECTION: 757*f8f1e192Sniklas conf_remove_section_now (node->section); 758*f8f1e192Sniklas break; 759*f8f1e192Sniklas default: 760*f8f1e192Sniklas log_print ("conf_end: unknown operation: %d", node->op); 761*f8f1e192Sniklas } 762*f8f1e192Sniklas TAILQ_REMOVE (&conf_trans_queue, node, link); 763*f8f1e192Sniklas free (node); 764*f8f1e192Sniklas } 765*f8f1e192Sniklas } 766*f8f1e192Sniklas return 0; 767*f8f1e192Sniklas } 768