/* ** Copyright (c) 2006, 2007 Sendmail, Inc. and its suppliers. ** All rights reserved. ** ** $Id: config.c,v 1.5 2007/10/22 01:28:31 msk Exp $ */ #ifndef lint static char config_c_id[] = "@(#)$Id: config.c,v 1.5 2007/10/22 01:28:31 msk Exp $"; #endif /* !lint */ /* system includes */ #include #include #include #include #include /* dkim-filter includes */ #include "config.h" /* ** CONFIG_FREE -- release memory associated with a config list ** ** Parameters: ** head -- head of the config list ** ** Return value: ** None. */ void config_free(struct config *head) { struct config *next; struct config *cur; cur = head; while (cur != NULL) { next = cur->cfg_next; free(cur); cur = next; } } /* ** CONFIG_LOAD -- load configuration from a file ** ** Parameters: ** in -- stream from which to load ** cd -- array of (struct configdef) elements containing the ** configuration syntax to assert ** line -- line number where an error occurred (updated) ** ** Return value: ** Pointer to a (struct config) which is the head of a list of ** loaded configuration items, or NULL on error; if NULL, "line" is ** updated to indicate which line number contained the error. */ struct config * config_load(FILE *in, struct configdef *def, unsigned int *line) { int arg; int n = -1; int err = 0; unsigned int myline = 0; int value = -1; char *p; char *str = NULL; struct config *new; struct config *cur = NULL; char buf[BUFRSZ + 1]; assert(in != NULL); assert(def != NULL); memset(buf, '\0', sizeof buf); while (fgets(buf, sizeof buf - 1, in) != NULL) { myline++; /* read a line; truncate at newline or "#" */ for (p = buf; *p != '\0'; p++) { if (*p == '#' || *p == '\n') { *p = '\0'; break; } } arg = 0; /* break down the arguments */ /* XXX -- need something better than strtok(), for quoting */ for (p = strtok(buf, " \t"); err == 0 && p != NULL; p = strtok(NULL, " \t")) { /* recognize the first? */ if (arg == 0) { for (n = 0; ; n++) { /* nope */ if (def[n].cd_name == NULL) { err = 1; break; } if (strcasecmp(def[n].cd_name, p) == 0) break; } } else { char *q; switch (def[n].cd_type) { case CONFIG_TYPE_STRING: str = p; break; case CONFIG_TYPE_BOOLEAN: if (p[0] == 't' || p[0] == 'T' || p[0] == 'y' || p[0] == 'Y' || p[0] == '1') value = 1; else if (p[0] == 'f' || p[0] == 'F' || p[0] == 'n' || p[0] == 'N' || p[0] == '0') value = 0; else err = 1; break; case CONFIG_TYPE_INTEGER: value = (int) strtol(p, &q, 0); if (*q != '\0') err = 1; break; default: assert(0); /* NOTREACHED */ return NULL; } } arg++; } /* no arguments */ if (arg == 0) continue; /* a parse error, or only one argument, is no good */ if (arg == 1 || err == 1) { config_free(cur); if (line != NULL) *line = myline; return NULL; } new = (struct config *) malloc(sizeof(struct config)); if (new == NULL) { config_free(cur); if (line != NULL) *line = myline; return NULL; } new->cfg_next = cur; new->cfg_name = def[n].cd_name; new->cfg_type = def[n].cd_type; switch (new->cfg_type) { case CONFIG_TYPE_STRING: new->cfg_string = strdup(str); break; case CONFIG_TYPE_BOOLEAN: new->cfg_bool = (bool) value; break; case CONFIG_TYPE_INTEGER: new->cfg_int = value; break; default: assert(0); } cur = new; } return cur; } /* ** CONFIG_CHECK -- verify that stuff marked "required" is present ** ** Parameters: ** head -- head of config list ** def -- definitions ** ** Return value: ** Name of the first parameter in "def" that was marked "required" ** yet absent from the configuration parsed, or NULL if nothing ** required was missing. */ char * config_check(struct config *head, struct configdef *def) { int n; struct config *cur; assert(head != NULL); assert(def != NULL); for (n = 0; ; n++) { if (def[n].cd_name == NULL) return NULL; if (!def[n].cd_req) continue; for (cur = head; cur != NULL; cur = cur->cfg_next) { if (cur->cfg_name == def[n].cd_name) break; } if (cur == NULL) return def[n].cd_name; } /* NOTREACHED */ } /* ** CONFIG_GET -- retrieve a parameter's value ** ** Parameter: ** head -- head of config list ** name -- name of the parameter of interest ** value -- where to write the result (returned) ** size -- bytes available at "value" ** ** Return value: ** 1 if the data was found, 0 otherwise, -1 if the request was illegal ** ** Notes: ** "value" is a (void *). It can be used directly, such as: ** ** int x; ** ** (void) config_get(conflist, "MyInteger", (void *) &x); */ int config_get(struct config *head, const char *name, void *value, size_t size) { struct config *cur; assert(head != NULL); assert(name != NULL); assert(value != NULL); assert(size > 0); for (cur = head; cur != NULL; cur = cur->cfg_next) { if (strcasecmp(cur->cfg_name, name) == 0) { switch (cur->cfg_type) { case CONFIG_TYPE_BOOLEAN: if (size != sizeof(bool)) return -1; memcpy(value, &cur->cfg_bool, size); break; case CONFIG_TYPE_INTEGER: if (size != sizeof(int)) return -1; memcpy(value, &cur->cfg_int, size); break; default: if (size != sizeof(char *)) return -1; memcpy(value, &cur->cfg_string, size); break; } return 1; } } return 0; }