#ifdef HAVE_DIRENT_H
#include <dirent.h>
+#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
-#endif
#include <ctype.h>
struct conf_item *next;
struct conf_part *parent;
int lineno;
+ const char *filename;
CONF_ITEM_TYPE type;
};
struct conf_pair {
CONF_ITEM item;
- char *attr;
- char *value;
- LRAD_TOKEN operator;
+ const char *attr;
+ const char *value;
+ FR_TOKEN operator;
+ FR_TOKEN value_type;
};
struct conf_part {
CONF_ITEM item;
static void *cf_data_find_internal(CONF_SECTION *cs, const char *name,
int flag);
+int cf_log_config = 1;
+int cf_log_modules = 1;
+
/*
* Isolate the scary casts in these tiny provably-safe functions
*/
* Create a new CONF_PAIR
*/
static CONF_PAIR *cf_pair_alloc(const char *attr, const char *value,
- LRAD_TOKEN operator, CONF_SECTION *parent)
+ FR_TOKEN operator, FR_TOKEN value_type,
+ CONF_SECTION *parent)
{
+ char *p;
+ size_t attr_len, value_len = 0;
CONF_PAIR *cp;
- cp = rad_malloc(sizeof(*cp));
+ if (!attr) return NULL;
+ attr_len = strlen(attr) + 1;
+ if (value) value_len = strlen(value) + 1;
+
+ p = rad_malloc(sizeof(*cp) + attr_len + value_len);
+
+ cp = (CONF_PAIR *) p;
memset(cp, 0, sizeof(*cp));
cp->item.type = CONF_ITEM_PAIR;
cp->item.parent = parent;
- cp->attr = strdup(attr);
- cp->value = strdup(value);
+
+ p += sizeof(*cp);
+ memcpy(p, attr, attr_len);
+ cp->attr = p;
+
+ if (value) {
+ p += attr_len;
+ memcpy(p, value, value_len);
+ cp->value = p;
+ }
+ cp->value_type = value_type;
cp->operator = operator;
return cp;
{
if (!cp || !*cp) return;
- if ((*cp)->attr)
- free((*cp)->attr);
- if ((*cp)->value)
- free((*cp)->value);
+ /*
+ * attr && value are allocated contiguous with cp.
+ */
#ifndef NDEBUG
memset(*cp, 0, sizeof(*cp));
{
if (!cd || !*cd) return;
- if ((*cd)->flag != 0) free((*cd)->name);
+ /* name is allocated contiguous with cd */
if (!(*cd)->free) {
free((*cd)->data);
} else {
/*
* Free strings we've parsed into data structures.
*/
-static void cf_section_parse_free(void *base, const CONF_PARSER *variables)
+void cf_section_parse_free(CONF_SECTION *cs, void *base)
{
int i;
+ const CONF_PARSER *variables = cs->variables;
/*
* Don't automatically free the strings if we're being
if (!cs || !*cs) return;
- if ((*cs)->variables) {
- cf_section_parse_free((*cs)->base, (*cs)->variables);
- }
+ cf_section_parse_free(*cs, (*cs)->base);
for (ci = (*cs)->children; ci; ci = next) {
next = ci->next;
break;
case CONF_ITEM_SECTION: {
-
CONF_SECTION *section = cf_itemtosection(ci);
cf_section_free(§ion);
}
}
}
- if ((*cs)->name1)
- free((*cs)->name1);
- if ((*cs)->name2)
- free((*cs)->name2);
+ /*
+ * Name1 and name2 are allocated contiguous with
+ * cs.
+ */
if ((*cs)->pair_tree)
rbtree_free((*cs)->pair_tree);
if ((*cs)->section_tree)
static CONF_SECTION *cf_section_alloc(const char *name1, const char *name2,
CONF_SECTION *parent)
{
+ size_t name1_len, name2_len = 0;
+ char *p;
CONF_SECTION *cs;
if (!name1) return NULL;
- cs = rad_malloc(sizeof(*cs));
+ name1_len = strlen(name1) + 1;
+ if (name2) name2_len = strlen(name2) + 1;
+
+ p = rad_malloc(sizeof(*cs) + name1_len + name2_len);
+
+ cs = (CONF_SECTION *) p;
memset(cs, 0, sizeof(*cs));
cs->item.type = CONF_ITEM_SECTION;
cs->item.parent = parent;
- cs->name1 = strdup(name1);
- if (!cs->name1) {
- cf_section_free(&cs);
- return NULL;
- }
-
+
+ p += sizeof(*cs);
+ memcpy(p, name1, name1_len);
+ cs->name1 = p;
+
if (name2 && *name2) {
- cs->name2 = strdup(name2);
- if (!cs->name2) {
- cf_section_free(&cs);
- return NULL;
- }
+ p += name1_len;
+ memcpy(p, name2, name2_len);
+ cs->name2 = p;
}
+
cs->pair_tree = rbtree_create(pair_cmp, NULL, 0);
if (!cs->pair_tree) {
cf_section_free(&cs);
return cs;
}
+/*
+ * Replace pair in a given section with a new pair,
+ * of the given value.
+ */
+int cf_pair_replace(CONF_SECTION *cs, CONF_PAIR *cp, const char *value)
+{
+ CONF_PAIR *newp;
+ CONF_ITEM *ci, *cn, **last;
+
+ newp = cf_pair_alloc(cp->attr, value, cp->operator, cp->value_type,
+ cs);
+ if (!newp) return -1;
+
+ ci = cf_pairtoitem(cp);
+ cn = cf_pairtoitem(newp);
+
+ /*
+ * Find the old one from the linked list, and replace it
+ * with the new one.
+ */
+ for (last = &cs->children; (*last) != NULL; last = &(*last)->next) {
+ if (*last == ci) {
+ cn->next = (*last)->next;
+ *last = cn;
+ ci->next = NULL;
+ break;
+ }
+ }
+
+ rbtree_deletebydata(cs->pair_tree, ci);
+
+ rbtree_insert(cs->pair_tree, cn);
+
+ return 0;
+}
+
/*
* Add an item to a configuration section.
case CONF_ITEM_PAIR:
rbtree_insert(cs->pair_tree, ci);
break;
-
+
case CONF_ITEM_SECTION: {
- const CONF_SECTION *cs_new = cf_itemtosection(ci);
-
+ CONF_SECTION *cs_new = cf_itemtosection(ci);
+
if (!cs->section_tree) {
cs->section_tree = rbtree_create(section_cmp, NULL, 0);
- /* ignore any errors */
+ if (!cs->section_tree) {
+ radlog(L_ERR, "Out of memory");
+ _exit(1);
+ }
}
-
- if (cs->section_tree) {
- rbtree_insert(cs->section_tree, cs_new); }
-
+
+ rbtree_insert(cs->section_tree, cs_new);
+
/*
* Two names: find the named instance.
*/
- if (cs_new->name2) {
+ {
CONF_SECTION *old_cs;
-
+
/*
* Find the FIRST
* CONF_SECTION having
*/
old_cs = rbtree_finddata(cs->section_tree, cs_new);
if (!old_cs) return; /* this is a bad error! */
-
+
if (!old_cs->name2_tree) {
old_cs->name2_tree = rbtree_create(name2_cmp,
NULL, 0);
} /* loop over ci */
}
+
+CONF_ITEM *cf_reference_item(const CONF_SECTION *parentcs,
+ CONF_SECTION *outercs,
+ const char *ptr)
+{
+ CONF_PAIR *cp;
+ CONF_SECTION *next;
+ const CONF_SECTION *cs = outercs;
+ char name[8192];
+ char *p;
+
+ strlcpy(name, ptr, sizeof(name));
+ p = name;
+
+ /*
+ * ".foo" means "foo from the current section"
+ */
+ if (*p == '.') {
+ p++;
+
+ /*
+ * ..foo means "foo from the section
+ * enclosing this section" (etc.)
+ */
+ while (*p == '.') {
+ if (cs->item.parent)
+ cs = cs->item.parent;
+ p++;
+ }
+
+ /*
+ * "foo.bar.baz" means "from the root"
+ */
+ } else if (strchr(p, '.') != NULL) {
+ if (!parentcs) goto no_such_item;
+
+ cs = parentcs;
+ }
+
+ while (*p) {
+ char *q, *r;
+
+ r = strchr(p, '[');
+ q = strchr(p, '.');
+ if (!r && !q) break;
+
+ if (r && q > r) q = NULL;
+ if (q && q < r) r = NULL;
+
+ /*
+ * Split off name2.
+ */
+ if (r) {
+ q = strchr(r + 1, ']');
+ if (!q) return NULL; /* parse error */
+
+ /*
+ * Points to foo[bar]xx: parse error,
+ * it should be foo[bar] or foo[bar].baz
+ */
+ if (q[1] && q[1] != '.') goto no_such_item;
+
+ *r = '\0';
+ *q = '\0';
+ next = cf_section_sub_find_name2(cs, p, r + 1);
+ *r = '[';
+ *q = ']';
+
+ /*
+ * Points to a named instance of a section.
+ */
+ if (!q[1]) {
+ if (!next) goto no_such_item;
+ return cf_sectiontoitem(next);
+ }
+
+ q++; /* ensure we skip the ']' and '.' */
+
+ } else {
+ *q = '\0';
+ next = cf_section_sub_find(cs, p);
+ *q = '.';
+ }
+
+ if (!next) break; /* it MAY be a pair in this section! */
+
+ cs = next;
+ p = q + 1;
+ }
+
+ if (!*p) goto no_such_item;
+
+ retry:
+ /*
+ * Find it in the current referenced
+ * section.
+ */
+ cp = cf_pair_find(cs, p);
+ if (cp) return cf_pairtoitem(cp);
+
+ next = cf_section_sub_find(cs, p);
+ if (next) return cf_sectiontoitem(next);
+
+ /*
+ * "foo" is "in the current section, OR in main".
+ */
+ if ((p == name) && (parentcs != NULL) && (cs != parentcs)) {
+ cs = parentcs;
+ goto retry;
+ }
+
+no_such_item:
+ DEBUG2("WARNING: No such configuration item %s", ptr);
+ return NULL;
+}
+
+
+CONF_SECTION *cf_top_section(CONF_SECTION *cs)
+{
+ while (cs->item.parent != NULL) {
+ cs = cs->item.parent;
+ }
+
+ return cs;
+}
+
+
/*
* Expand the variables in an input string.
*/
static const char *cf_expand_variables(const char *cf, int *lineno,
- const CONF_SECTION *outercs,
+ CONF_SECTION *outercs,
char *output, const char *input)
{
char *p;
const char *end, *ptr;
- char name[8192];
const CONF_SECTION *parentcs;
+ char name[8192];
/*
* Find the master parent conf section.
* We can't use mainconfig.config, because we're in the
* process of re-building it, and it isn't set up yet...
*/
- for (parentcs = outercs;
- parentcs->item.parent != NULL;
- parentcs = parentcs->item.parent) {
- /* do nothing */
- }
+ parentcs = cf_top_section(outercs);
p = output;
ptr = input;
* Ignore anything other than "${"
*/
if ((*ptr == '$') && (ptr[1] == '{')) {
- int up;
+ CONF_ITEM *ci;
CONF_PAIR *cp;
- const CONF_SECTION *cs;
/*
* FIXME: Add support for ${foo:-bar},
ptr += 2;
- cp = NULL;
- up = 0;
-
/*
- * ${.foo} means "foo from the current section"
+ * Can't really happen because input lines are
+ * capped at 8k, which is sizeof(name)
*/
- if (*ptr == '.') {
- up = 1;
- cs = outercs;
- ptr++;
-
- /*
- * ${..foo} means "foo from the section
- * enclosing this section" (etc.)
- */
- while (*ptr == '.') {
- if (cs->item.parent)
- cs = cs->item.parent;
- ptr++;
- }
-
- } else {
- const char *q;
- /*
- * ${foo} is local, with
- * main as lower priority
- */
- cs = outercs;
-
- /*
- * ${foo.bar.baz} is always rooted
- * from the top.
- */
- for (q = ptr; *q && q != end; q++) {
- if (*q == '.') {
- cs = parentcs;
- up = 1;
- break;
- }
- }
+ if ((size_t) (end - ptr) >= sizeof(name)) {
+ radlog(L_ERR, "%s[%d]: Reference string is too large",
+ cf, *lineno);
+ return NULL;
}
- while (cp == NULL) {
- char *q;
- /*
- * Find the next section.
- */
- for (q = name;
- (*ptr != 0) && (*ptr != '.') &&
- (ptr != end);
- q++, ptr++) {
- *q = *ptr;
- }
- *q = '\0';
-
- /*
- * The character is a '.', find a
- * section (as the user has given
- * us a subsection to find)
- */
- if (*ptr == '.') {
- CONF_SECTION *next;
-
- ptr++; /* skip the period */
-
- /*
- * Find the sub-section.
- */
- next = cf_section_sub_find(cs, name);
- if (next == NULL) {
- radlog(L_ERR, "config: No such section %s in variable %s", name, input);
- return NULL;
- }
- cs = next;
+ memcpy(name, ptr, end - ptr);
+ name[end - ptr] = '\0';
- } else { /* no period, must be a conf-part */
- /*
- * Find in the current referenced
- * section.
- */
- cp = cf_pair_find(cs, name);
- if (cp == NULL) {
- /*
- * It it was NOT ${..foo}
- * then look in the
- * top-level config items.
- */
- if (!up) cp = cf_pair_find(parentcs, name);
- }
- if (cp == NULL) {
- radlog(L_ERR, "config: No such configuration item %s in section %s when expanding string \"%s\"", name,
- cf_section_name1(cs),
- input);
- return NULL;
- }
- }
- } /* until cp is non-NULL */
+ ci = cf_reference_item(parentcs, outercs, name);
+ if (!ci || (ci->type != CONF_ITEM_PAIR)) {
+ radlog(L_ERR, "%s[%d]: Reference \"%s\" not found",
+ cf, *lineno, input);
+ return NULL;
+ }
/*
* Substitute the value of the variable.
*/
+ cp = cf_itemtopair(ci);
+ if (!cp->value) {
+ radlog(L_ERR, "%s[%d]: Reference \"%s\" has no value",
+ cf, *lineno, input);
+ return NULL;
+ }
strcpy(p, cp->value);
p += strlen(p);
ptr = end + 1;
return NULL;
}
+ /*
+ * Can't really happen because input lines are
+ * capped at 8k, which is sizeof(name)
+ */
+ if ((size_t) (end - ptr) >= sizeof(name)) {
+ radlog(L_ERR, "%s[%d]: Environment variable name is too large",
+ cf, *lineno);
+ return NULL;
+ }
+
memcpy(name, ptr, end - ptr);
name[end - ptr] = '\0';
int rcode = 0;
char **q;
const char *value;
- lrad_ipaddr_t ipaddr;
- const CONF_PAIR *cp;
+ fr_ipaddr_t ipaddr;
+ const CONF_PAIR *cp = NULL;
char ipbuf[128];
- cp = cf_pair_find(cs, name);
+ if (cs) cp = cf_pair_find(cs, name);
if (cp) {
value = cp->value;
value = dflt;
}
+ if (!value) {
+ return 0;
+ }
+
switch (type) {
case PW_TYPE_BOOLEAN:
/*
radlog(L_ERR, "Bad value \"%s\" for boolean variable %s", value, name);
return -1;
}
- DEBUG2("\t%s = %s", name, value);
+ cf_log_info(cs, "\t%s = %s", name, value);
break;
-
+
case PW_TYPE_INTEGER:
*(int *)data = strtol(value, 0, 0);
- DEBUG2("\t%s = %d", name, *(int *)data);
+ cf_log_info(cs, "\t%s = %d", name, *(int *)data);
break;
-
+
case PW_TYPE_STRING_PTR:
q = (char **) data;
if (*q != NULL) {
free(*q);
}
-
+
/*
* Expand variables which haven't already been
* expanded automagically when the configuration
if (value == dflt) {
char buffer[8192];
- int lineno = cs->item.lineno;
+ int lineno = 0;
+
+ if (cs) lineno = cs->item.lineno;
/*
* FIXME: sizeof(buffer)?
cs, buffer, value);
if (!value) return -1;
}
-
- DEBUG2("\t%s = \"%s\"", name, value ? value : "(null)");
+
+ cf_log_info(cs, "\t%s = \"%s\"", name, value ? value : "(null)");
*q = value ? strdup(value) : NULL;
break;
-
+
/*
* This is the same as PW_TYPE_STRING_PTR,
* except that we also "stat" the file, and
if (*q != NULL) {
free(*q);
}
-
+
/*
* Expand variables which haven't already been
* expanded automagically when the configuration
if (value == dflt) {
char buffer[8192];
- int lineno = cs->item.lineno;
+ int lineno = 0;
+
+ if (cs) lineno = cs->item.lineno;
/*
* FIXME: sizeof(buffer)?
cs, buffer, value);
if (!value) return -1;
}
-
- DEBUG2("\t%s = \"%s\"", name, value ? value : "(null)");
+
+ cf_log_info(cs, "\t%s = \"%s\"", name, value ? value : "(null)");
*q = value ? strdup(value) : NULL;
/*
* And now we "stat" the file.
+ *
+ * FIXME: This appears to leak memory on exit,
+ * and we don't use this information. So it's
+ * commented out for now.
*/
- if (*q) {
+ if (0 && *q) {
struct stat buf;
if (stat(*q, &buf) == 0) {
*/
if (strcmp(value, "*") == 0) {
*(uint32_t *) data = htonl(INADDR_ANY);
- DEBUG2("\t%s = *", name);
+ cf_log_info(cs, "\t%s = *", name);
break;
}
if (ip_hton(value, AF_INET, &ipaddr) < 0) {
radlog(L_ERR, "Can't find IP address for host %s", value);
return -1;
}
- DEBUG2("\t%s = %s IP address [%s]", name, value,
+
+ if (strspn(value, "0123456789.") == strlen(value)) {
+ cf_log_info(cs, "\t%s = %s", name, value);
+ } else {
+ cf_log_info(cs, "\t%s = %s IP address [%s]", name, value,
ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
+ }
*(uint32_t *) data = ipaddr.ipaddr.ip4addr.s_addr;
break;
-
+
case PW_TYPE_IPV6ADDR:
if (ip_hton(value, AF_INET6, &ipaddr) < 0) {
radlog(L_ERR, "Can't find IPv6 address for host %s", value);
return -1;
}
- DEBUG2("\t%s = %s IPv6 address [%s]", name, value,
+ cf_log_info(cs, "\t%s = %s IPv6 address [%s]", name, value,
ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
memcpy(data, &ipaddr.ipaddr.ip6addr,
sizeof(ipaddr.ipaddr.ip6addr));
break;
-
+
default:
radlog(L_ERR, "type %d not supported yet", type);
return -1;
break;
} /* switch over variable type */
-
+
return rcode;
}
int i;
void *data;
+ cs->variables = variables; /* this doesn't hurt anything */
+
if (!cs->name2) {
- DEBUG2("%.*s%s {", cs->depth, parse_spaces,
+ cf_log_info(cs, "%.*s%s {", cs->depth, parse_spaces,
cs->name1);
} else {
- DEBUG2("%.*s%s %s {", cs->depth, parse_spaces,
+ cf_log_info(cs, "%.*s%s %s {", cs->depth, parse_spaces,
cs->name1, cs->name2);
}
* Handle subsections specially
*/
if (variables[i].type == PW_TYPE_SUBSECTION) {
- const CONF_SECTION *subcs;
+ CONF_SECTION *subcs;
subcs = cf_section_sub_find(cs, variables[i].name);
-
+
/*
* If the configuration section is NOT there,
* then ignore it.
DEBUG2("Internal sanity check 1 failed in cf_section_parse");
goto error;
}
-
+
if (cf_section_parse(subcs, base,
(const CONF_PARSER *) variables[i].dflt) < 0) {
goto error;
}
continue;
} /* else it's a CONF_PAIR */
-
+
if (variables[i].data) {
data = variables[i].data; /* prefer this. */
} else if (base) {
}
} /* for all variables in the configuration section */
- DEBUG2("%.*s}", cs->depth, parse_spaces);
+ cf_log_info(cs, "%.*s}", cs->depth, parse_spaces);
cs->base = base;
- cs->variables = variables;
return 0;
error:
- DEBUG2("%.*s}", cs->depth, parse_spaces);
- cf_section_parse_free(base, variables);
+ cf_log_info(cs, "%.*s}", cs->depth, parse_spaces);
+ cf_section_parse_free(cs, base);
return -1;
}
/*
+ * Sanity check the "if" or "elsif", presuming that the first '('
+ * has already been eaten.
+ *
+ * We're not really parsing it here, just checking if it's mostly
+ * well-formed.
+ */
+static int condition_looks_ok(const char **ptr)
+{
+ int num_braces = 1;
+ int quote = 0;
+ const char *p = *ptr;
+
+ while (*p) {
+ if (quote) {
+ if (*p == quote) {
+ p++;
+ quote = 0;
+ continue;
+ }
+
+ if (*p == '\\') {
+ if (!p[1]) {
+ return 0; /* no trailing slash */
+ }
+ p += 2;
+ continue;
+ }
+ p++;
+ continue;
+ }
+
+ switch (*p) {
+ case '\\':
+ if (!p[1]) {
+ return 0; /* no trailing slash */
+ }
+ p += 2;
+ continue;
+
+ case '(':
+ num_braces++;
+ p++;
+ continue;
+
+ case ')':
+ if (num_braces == 1) {
+ const char *q = p + 1;
+
+ /*
+ * Validate that there isn't much
+ * else after the closing brace.
+ */
+ while ((*q == ' ') || (*q == '\t')) q++;
+
+ /*
+ * Parse error.
+ */
+ if (*q != '{') {
+ DEBUG2("Expected open brace '{' after condition at %s", p);
+ return 0;
+ }
+
+ *ptr = p + 1; /* include the trailing ')' */
+ return 1;
+ }
+ num_braces--;
+ p++;
+ continue;
+
+ case '"':
+ case '\'':
+ case '/':
+ case '`':
+ quote = *p;
+ /* FALL-THROUGH */
+
+ default:
+ p++;
+ break;
+ }
+ }
+
+ DEBUG3("Unexpected error");
+ return 0;
+}
+
+
+static const char *cf_local_file(CONF_SECTION *cs, const char *local,
+ char *buffer, size_t bufsize)
+{
+ size_t dirsize;
+ const char *p;
+ CONF_SECTION *parentcs = cf_top_section(cs);
+
+ p = strrchr(parentcs->item.filename, FR_DIR_SEP);
+ if (!p) return local;
+
+ dirsize = (p - parentcs->item.filename) + 1;
+
+ if ((dirsize + strlen(local)) >= bufsize) {
+ return NULL;
+ }
+
+ memcpy(buffer, parentcs->item.filename, dirsize);
+ strlcpy(buffer + dirsize, local, bufsize - dirsize);
+
+ return buffer;
+}
+
+
+/*
* Read a part of the config file.
*/
-static int cf_section_read(const char *file, int *lineno, FILE *fp,
+static int cf_section_read(const char *filename, int *lineno, FILE *fp,
CONF_SECTION *current)
{
CONF_SECTION *this, *css;
CONF_PAIR *cpn;
- char *ptr;
+ const char *ptr;
const char *value;
char buf[8192];
char buf1[8192];
char buf3[8192];
int t1, t2, t3;
char *cbuf = buf;
- int len;
+ size_t len;
this = current; /* add items here */
* Read, checking for line continuations ('\\' at EOL)
*/
for (;;) {
- int eof;
+ int at_eof;
/*
* Get data, and remember if we are at EOF.
*/
- eof = (fgets(cbuf, sizeof(buf) - (cbuf - buf), fp) == NULL);
+ at_eof = (fgets(cbuf, sizeof(buf) - (cbuf - buf), fp) == NULL);
(*lineno)++;
- len = strlen(cbuf);
-
/*
- * We've filled the buffer, and there isn't
- * a CR in it. Die!
+ * We read the entire 8k worth of data: complain.
+ * Note that we don't care if the last character
+ * is \n: it's still forbidden. This means that
+ * the maximum allowed length of text is 8k-1, which
+ * should be plenty.
*/
- if ((len == (sizeof(buf) - 1)) &&
- (cbuf[len - 1] != '\n')) {
+ len = strlen(cbuf);
+ if ((cbuf + len + 1) >= (buf + sizeof(buf))) {
radlog(L_ERR, "%s[%d]: Line too long",
- file, *lineno);
+ filename, *lineno);
return -1;
}
/*
- * Check for continuations.
+ * Not doing continuations: check for edge
+ * conditions.
*/
- if (cbuf[len - 1] == '\n') len--;
+ if (cbuf == buf) {
+ if (at_eof) break;
+
+ ptr = buf;
+ while (*ptr && isspace((int) *ptr)) ptr++;
- /*
- * Last character is '\\'. Over-write it,
- * and read another line.
- */
- if ((len > 0) && (cbuf[len - 1] == '\\')) {
+ if (!*ptr || (*ptr == '#')) continue;
+
+ } else if (at_eof || (len == 0)) {
+ radlog(L_ERR, "%s[%d]: Continuation at EOF is illegal",
+ filename, *lineno);
+ return -1;
+ }
+
+ /*
+ * See if there's a continuation.
+ */
+ while ((len > 0) &&
+ ((cbuf[len - 1] == '\n') || (cbuf[len - 1] == '\r'))) {
+ len--;
+ cbuf[len] = '\0';
+ }
+
+ if ((len > 0) && (cbuf[len - 1] == '\\')) {
cbuf[len - 1] = '\0';
cbuf += len - 1;
continue;
}
+ ptr = cbuf = buf;
+
/*
- * We're at EOF, and haven't read anything. Stop.
+ * The parser is getting to be evil.
*/
- if (eof && (cbuf == buf)) {
- break;
- }
+ while ((*ptr == ' ') || (*ptr == '\t')) ptr++;
- ptr = cbuf = buf;
- t1 = gettoken(&ptr, buf1, sizeof(buf1));
+ if (((ptr[0] == '%') && (ptr[1] == '{')) ||
+ (ptr[0] == '`')) {
+ int hack;
- if ((*buf1 == '#') || (*buf1 == '\0')) {
- continue;
- }
+ if (ptr[0] == '%') {
+ hack = rad_copy_variable(buf1, ptr);
+ } else {
+ hack = rad_copy_string(buf1, ptr);
+ }
+ if (hack < 0) {
+ radlog(L_ERR, "%s[%d]: Invalid expansion: %s",
+ filename, *lineno, ptr);
+ return -1;
+ }
+
+ t1 = T_BARE_WORD;
+ ptr += hack;
+
+ t2 = gettoken(&ptr, buf2, sizeof(buf2));
+ switch (t2) {
+ case T_EOL:
+ case T_HASH:
+ goto do_bare_word;
+
+ default:
+ radlog(L_ERR, "%s[%d]: Invalid expansion: %s",
+ filename, *lineno, ptr);
+ return -1;
+ }
+ } else {
+ t1 = gettoken(&ptr, buf1, sizeof(buf1));
+ }
/*
* The caller eats "name1 name2 {", and calls us
if (t1 == T_RCBRACE) {
if (this == current) {
radlog(L_ERR, "%s[%d]: Too many closing braces",
- file, *lineno);
+ filename, *lineno);
return -1;
-
+
}
this = this->item.parent;
continue;
* This *SHOULD* work for any level include.
* I really really really hate this file. -cparker
*/
- if (strcasecmp(buf1, "$INCLUDE") == 0) {
- t2 = getword(&ptr, buf2, sizeof(buf2));
+ if ((strcasecmp(buf1, "$INCLUDE") == 0) ||
+ (strcasecmp(buf1, "$-INCLUDE") == 0)) {
+ int relative = 1;
+
+ t2 = getword(&ptr, buf2, sizeof(buf2));
+
+ if (buf2[0] == '$') relative = 0;
- value = cf_expand_variables(file, lineno, this, buf, buf2);
+ value = cf_expand_variables(filename, lineno, this, buf, buf2);
if (!value) return -1;
+ if (!FR_DIR_IS_RELATIVE(value)) relative = 0;
+
+ if (relative) {
+ value = cf_local_file(current, value, buf3,
+ sizeof(buf3));
+ if (!value) {
+ radlog(L_ERR, "%s[%d]: Directories too deep.",
+ filename, *lineno);
+ return -1;
+ }
+ }
+
+
#ifdef HAVE_DIRENT_H
/*
* $INCLUDE foo/
struct dirent *dp;
struct stat stat_buf;
- DEBUG2( "Config: including files in directory: %s", value );
+ DEBUG2("including files in directory %s", value );
dir = opendir(value);
if (!dir) {
radlog(L_ERR, "%s[%d]: Error reading directory %s: %s",
- file, *lineno, value,
+ filename, *lineno, value,
strerror(errno));
return -1;
}
for (p = dp->d_name; *p != '\0'; p++) {
if (isalpha((int)*p) ||
isdigit((int)*p) ||
+ (*p == '-') ||
(*p == '_') ||
(*p == '.')) continue;
break;
} else
#endif
{ /* it was a normal file */
+ if (buf1[1] == '-') {
+ struct stat statbuf;
+
+ if (stat(value, &statbuf) < 0) {
+ DEBUG("WARNING: Not including file %s: %s", value, strerror(errno));
+ continue;
+ }
+ }
+
if (cf_file_include(value, this) < 0) {
return -1;
}
continue;
} /* we were in an include */
+ if (strcasecmp(buf1, "$template") == 0) {
+ CONF_ITEM *ci;
+ CONF_SECTION *parentcs, *templatecs;
+ t2 = getword(&ptr, buf2, sizeof(buf2));
+
+ parentcs = cf_top_section(current);
+
+ templatecs = cf_section_sub_find(parentcs, "templates");
+ if (!templatecs) {
+ radlog(L_ERR, "%s[%d]: No \"templates\" section for reference \"%s\"",
+ filename, *lineno, buf2);
+ return -1;
+ }
+
+ ci = cf_reference_item(parentcs, templatecs, buf2);
+ if (!ci || (ci->type != CONF_ITEM_SECTION)) {
+ radlog(L_ERR, "%s[%d]: Reference \"%s\" not found",
+ filename, *lineno, buf2);
+ return -1;
+ }
+
+ if (this->template) {
+ radlog(L_ERR, "%s[%d]: Section already has a template",
+ filename, *lineno);
+ return -1;
+ }
+
+ this->template = cf_itemtosection(ci);
+ continue;
+ }
+
/*
- * No '=': must be a section or sub-section.
+ * Ensure that the user can't add CONF_PAIRs
+ * with 'internal' names;
*/
- if (strchr(ptr, '=') == NULL) {
- t2 = gettoken(&ptr, buf2, sizeof(buf2));
- t3 = gettoken(&ptr, buf3, sizeof(buf3));
- } else {
- t2 = gettoken(&ptr, buf2, sizeof(buf2));
- t3 = getword(&ptr, buf3, sizeof(buf3));
+ if (buf1[0] == '_') {
+ radlog(L_ERR, "%s[%d]: Illegal configuration pair name \"%s\"",
+ filename, *lineno, buf1);
+ return -1;
}
/*
- * Perhaps a subsection.
+ * Grab the next token.
*/
- if (t2 == T_LCBRACE || t3 == T_LCBRACE) {
+ t2 = gettoken(&ptr, buf2, sizeof(buf2));
+ switch (t2) {
+ case T_EOL:
+ case T_HASH:
+ do_bare_word:
+ t3 = t2;
+ t2 = T_OP_EQ;
+ value = NULL;
+ goto do_set;
+
+ case T_OP_ADD:
+ case T_OP_CMP_EQ:
+ case T_OP_SUB:
+ case T_OP_LE:
+ case T_OP_GE:
+ case T_OP_CMP_FALSE:
+ if (!this || (strcmp(this->name1, "update") != 0)) {
+ radlog(L_ERR, "%s[%d]: Invalid operator in assignment",
+ filename, *lineno);
+ return -1;
+ }
+
+ case T_OP_EQ:
+ case T_OP_SET:
+ t3 = getstring(&ptr, buf3, sizeof(buf3));
+ if (t3 == T_OP_INVALID) {
+ radlog(L_ERR, "%s[%d]: Parse error: %s",
+ filename, *lineno,
+ fr_strerror());
+ return -1;
+ }
+
+ /*
+ * Handle variable substitution via ${foo}
+ */
+ if ((t3 == T_BARE_WORD) ||
+ (t3 == T_DOUBLE_QUOTED_STRING)) {
+ value = cf_expand_variables(filename, lineno, this,
+ buf, buf3);
+ if (!value) return -1;
+ } else if ((t3 == T_EOL) ||
+ (t3 == T_HASH)) {
+ value = NULL;
+ } else {
+ value = buf3;
+ }
+
+ /*
+ * Add this CONF_PAIR to our CONF_SECTION
+ */
+ do_set:
+ cpn = cf_pair_alloc(buf1, value, t2, t3, this);
+ cpn->item.filename = filename;
+ cpn->item.lineno = *lineno;
+ cf_item_add(this, cf_pairtoitem(cpn));
+ continue;
+
+ /*
+ * This horrible code is here to support
+ * if/then/else failover in the
+ * authorize, etc. sections. It makes no
+ * sense anywhere else.
+ */
+ case T_LBRACE:
+ if ((strcmp(buf1, "if") == 0) ||
+ (strcmp(buf1, "elsif") == 0)) {
+ const char *end = ptr;
+ CONF_SECTION *server;
+
+ if (!condition_looks_ok(&end)) {
+ radlog(L_ERR, "%s[%d]: Parse error in condition at: %s",
+ filename, *lineno, ptr);
+ return -1;
+ }
+
+ if ((size_t) (end - ptr) >= (sizeof(buf2) - 1)) {
+ radlog(L_ERR, "%s[%d]: Statement too complicated after \"%s\"",
+ filename, *lineno, buf1);
+ return -1;
+ }
+
+ /*
+ * More sanity checking. This is
+ * getting to be a horrible hack.
+ */
+ server = this;
+ while (server) {
+ if (strcmp(server->name1, "server") == 0) break;
+ server = server->item.parent;
+ }
+
+ if (0 && !server) {
+ radlog(L_ERR, "%s[%d]: Processing directives such as \"%s\" cannot be used here.",
+ filename, *lineno, buf1);
+ return -1;
+ }
+
+ buf2[0] = '(';
+ memcpy(buf2 + 1, ptr, end - ptr);
+ buf2[end - ptr + 1] = '\0';
+ ptr = end + 1;
+ t2 = T_BARE_WORD;
+ goto section_alloc;
+
+ } else {
+ radlog(L_ERR, "%s[%d]: Parse error after \"%s\"",
+ filename, *lineno, buf1);
+ return -1;
+ }
+
+ /* FALL-THROUGH */
+
+ /*
+ * No '=', must be a section or sub-section.
+ */
+ case T_BARE_WORD:
+ case T_DOUBLE_QUOTED_STRING:
+ case T_SINGLE_QUOTED_STRING:
+ t3 = gettoken(&ptr, buf3, sizeof(buf3));
+ if (t3 != T_LCBRACE) {
+ radlog(L_ERR, "%s[%d]: Expecting section start brace '{' after \"%s %s\"",
+ filename, *lineno, buf1, buf2);
+ return -1;
+ }
+
+ case T_LCBRACE:
+ section_alloc:
css = cf_section_alloc(buf1,
t2 == T_LCBRACE ? NULL : buf2,
this);
if (!css) {
radlog(L_ERR, "%s[%d]: Failed allocating memory for section",
- file, *lineno);
+ filename, *lineno);
+ return -1;
}
cf_item_add(this, cf_sectiontoitem(css));
+ css->item.filename = filename;
css->item.lineno = *lineno;
/*
*/
this = css;
continue;
- }
-
- /*
- * Ignore semi-colons.
- */
- if (*buf2 == ';')
- *buf2 = '\0';
-
- /*
- * Must be a normal attr = value line.
- */
- if (buf1[0] != 0 && buf2[0] == 0 && buf3[0] == 0) {
- t2 = T_OP_EQ;
- } else if (buf1[0] == 0 || buf2[0] == 0 ||
- (t2 < T_EQSTART || t2 > T_EQEND)) {
- radlog(L_ERR, "%s[%d]: Line is not in 'attribute = value' format",
- file, *lineno);
- return -1;
- }
- /*
- * Ensure that the user can't add CONF_PAIRs
- * with 'internal' names;
- */
- if (buf1[0] == '_') {
- radlog(L_ERR, "%s[%d]: Illegal configuration pair name \"%s\"",
- file, *lineno, buf1);
+ default:
+ radlog(L_ERR, "%s[%d]: Parse error after \"%s\"",
+ filename, *lineno, buf1);
return -1;
}
-
- /*
- * Handle variable substitution via ${foo}
- */
- value = cf_expand_variables(file, lineno, this, buf, buf3);
- if (!value) return -1;
-
-
- /*
- * Add this CONF_PAIR to our CONF_SECTION
- */
- cpn = cf_pair_alloc(buf1, value, t2, this);
- cpn->item.lineno = *lineno;
- cf_item_add(this, cf_pairtoitem(cpn));
}
/*
*/
if (feof(fp) && (this != current)) {
radlog(L_ERR, "%s[%d]: EOF reached without closing brace for section %s starting at line %d",
- file, *lineno,
+ filename, *lineno,
cf_section_name1(this), cf_section_lineno(this));
return -1;
}
/*
* Include one config file in another.
*/
-int cf_file_include(const char *file, CONF_SECTION *cs)
+int cf_file_include(const char *filename, CONF_SECTION *cs)
{
FILE *fp;
int lineno = 0;
struct stat statbuf;
time_t *mtime;
+ CONF_DATA *cd;
- DEBUG2( "Config: including file: %s", file);
+ DEBUG2( "including configuration file %s", filename);
- if (stat(file, &statbuf) == 0) {
+ if (stat(filename, &statbuf) == 0) {
#ifdef S_IWOTH
if ((statbuf.st_mode & S_IWOTH) != 0) {
radlog(L_ERR|L_CONS, "Configuration file %s is globally writable. Refusing to start due to insecure configuration.",
- file);
+ filename);
return -1;
}
#endif
#ifdef S_IROTH
if (0 && (statbuf.st_mode & S_IROTH) != 0) {
radlog(L_ERR|L_CONS, "Configuration file %s is globally readable. Refusing to start due to insecure configuration.",
- file);
+ filename);
return -1;
}
#endif
}
- fp = fopen(file, "r");
+ fp = fopen(filename, "r");
if (!fp) {
radlog(L_ERR|L_CONS, "Unable to open file \"%s\": %s",
- file, strerror(errno));
+ filename, strerror(errno));
return -1;
}
- /*
- * Read the section. It's OK to have EOF without a
- * matching close brace.
- */
- if (cf_section_read(file, &lineno, fp, cs) < 0) {
- fclose(fp);
+ if (cf_data_find_internal(cs, filename, PW_TYPE_FILENAME)) {
+ radlog(L_ERR, "Cannot include the same file twice: \"%s\"",
+ filename);
return -1;
}
*/
mtime = rad_malloc(sizeof(*mtime));
*mtime = statbuf.st_mtime;
- /* FIXME: error? */
- cf_data_add_internal(cs, file, mtime, free,
- PW_TYPE_FILENAME);
+
+ if (cf_data_add_internal(cs, filename, mtime, free,
+ PW_TYPE_FILENAME) < 0) {
+ fclose(fp);
+ radlog(L_ERR|L_CONS, "Internal error opening file \"%s\"",
+ filename);
+ return -1;
+ }
+
+ cd = cf_data_find_internal(cs, filename, PW_TYPE_FILENAME);
+ if (!cd) {
+ fclose(fp);
+ radlog(L_ERR|L_CONS, "Internal error opening file \"%s\"",
+ filename);
+ return -1;
+ }
+
+ if (!cs->item.filename) cs->item.filename = filename;
+
+ /*
+ * Read the section. It's OK to have EOF without a
+ * matching close brace.
+ */
+ if (cf_section_read(cd->name, &lineno, fp, cs) < 0) {
+ fclose(fp);
+ return -1;
+ }
fclose(fp);
return 0;
/*
* Bootstrap a config file.
*/
-CONF_SECTION *cf_file_read(const char *file)
+CONF_SECTION *cf_file_read(const char *filename)
{
+ char *p;
+ CONF_PAIR *cp;
CONF_SECTION *cs;
cs = cf_section_alloc("main", NULL, NULL);
if (!cs) return NULL;
- if (cf_file_include(file, cs) < 0) {
+ cp = cf_pair_alloc("confdir", filename, T_OP_SET, T_BARE_WORD, cs);
+ if (!cp) return NULL;
+
+ p = strrchr(cp->value, FR_DIR_SEP);
+ if (p) *p = '\0';
+
+ cp->item.filename = "internal";
+ cp->item.lineno = 0;
+ cf_item_add(cs, cf_pairtoitem(cp));
+
+ if (cf_file_include(filename, cs) < 0) {
cf_section_free(&cs);
return NULL;
}
CONF_ITEM *ci;
CONF_PAIR *cp = NULL;
- if (!cs) cs = mainconfig.config;
+ if (!cs) return NULL;
/*
* Find the name in the tree, for speed.
* Return the attr of a CONF_PAIR
*/
-char *cf_pair_attr(CONF_PAIR *pair)
+const char *cf_pair_attr(CONF_PAIR *pair)
{
return (pair ? pair->attr : NULL);
}
* Return the value of a CONF_PAIR
*/
-char *cf_pair_value(CONF_PAIR *pair)
+const char *cf_pair_value(CONF_PAIR *pair)
{
return (pair ? pair->value : NULL);
}
/*
+ * Copied here for error reporting.
+ */
+extern void fr_strerror_printf(const char *, ...);
+
+/*
+ * Turn a CONF_PAIR into a VALUE_PAIR
+ * For now, ignore the "value_type" field...
+ */
+VALUE_PAIR *cf_pairtovp(CONF_PAIR *pair)
+{
+ VALUE_PAIR *vp;
+
+ if (!pair) {
+ fr_strerror_printf("Internal error");
+ return NULL;
+ }
+
+ if (!pair->value) {
+ fr_strerror_printf("No value given for attribute %s", pair->attr);
+ return NULL;
+ }
+
+ /*
+ * pairmake handles tags. pairalloc() doesn't.
+ */
+ vp = pairmake(pair->attr, NULL, pair->operator);
+ if (!vp) {
+ return NULL;
+ }
+
+ if (pair->value_type == T_BARE_WORD) {
+ if ((vp->type == PW_TYPE_STRING) &&
+ (pair->value[0] == '0') && (pair->value[1] == 'x')) {
+ vp->type = PW_TYPE_OCTETS;
+ }
+ if (!pairparsevalue(vp, pair->value)) {
+ pairfree(&vp);
+ return NULL;
+ }
+ vp->flags.do_xlat = 0;
+
+ } else if (pair->value_type == T_SINGLE_QUOTED_STRING) {
+ if (!pairparsevalue(vp, pair->value)) {
+ pairfree(&vp);
+ return NULL;
+ }
+ vp->flags.do_xlat = 0;
+ } else {
+ vp->flags.do_xlat = 1;
+ }
+
+ return vp;
+}
+
+/*
* Return the first label of a CONF_SECTION
*/
/*
* Find a value in a CONF_SECTION
*/
-char *cf_section_value_find(const CONF_SECTION *cs, const char *attr)
+const char *cf_section_value_find(const CONF_SECTION *cs, const char *attr)
{
CONF_PAIR *cp;
*/
CONF_PAIR *cf_pair_find_next(const CONF_SECTION *cs,
- const CONF_PAIR *pair, const char *attr)
+ CONF_PAIR *pair, const char *attr)
{
CONF_ITEM *ci;
{
CONF_ITEM *ci;
+ if (!name) return NULL; /* can't find an un-named section */
+
/*
* Do the fast lookup if possible.
*/
- if (name && cs->section_tree) {
+ if (cs->section_tree) {
CONF_SECTION mycs;
mycs.name1 = name;
{
CONF_ITEM *ci;
- if (!name2) return cf_section_sub_find(cs, name1);
-
if (!cs) cs = mainconfig.config;
if (name1 && (cs->section_tree)) {
CONF_SECTION mycs, *master_cs;
-
+
mycs.name1 = name1;
mycs.name2 = name2;
-
+
master_cs = rbtree_finddata(cs->section_tree, &mycs);
if (master_cs) {
return rbtree_finddata(master_cs->name2_tree, &mycs);
return cf_itemtosection(ci);
}
+
+/*
+ * Return the next section after a CONF_SECTION
+ * with a certain name1 (char *name1). If the requested
+ * name1 is NULL, any name1 matches.
+ */
+
+CONF_SECTION *cf_section_find_next(CONF_SECTION *section,
+ CONF_SECTION *subsection,
+ const char *name1)
+{
+ if (!section->item.parent) return NULL;
+
+ return cf_subsection_find_next(section->item.parent, subsection, name1);
+}
+
/*
* Return the next item after a CONF_ITEM.
*/
return cf_sectiontoitem(section)->lineno;
}
+const char *cf_pair_filename(CONF_PAIR *pair)
+{
+ return cf_pairtoitem(pair)->filename;
+}
+
+const char *cf_section_filename(CONF_SECTION *section)
+{
+ return cf_sectiontoitem(section)->filename;
+}
+
int cf_pair_lineno(CONF_PAIR *pair)
{
return cf_pairtoitem(pair)->lineno;
static CONF_DATA *cf_data_alloc(CONF_SECTION *parent, const char *name,
void *data, void (*data_free)(void *))
{
+ char *p;
+ size_t name_len;
CONF_DATA *cd;
- cd = rad_malloc(sizeof(*cd));
+ name_len = strlen(name) + 1;
+
+ p = rad_malloc(sizeof(*cd) + name_len);
+ cd = (CONF_DATA *) p;
memset(cd, 0, sizeof(*cd));
cd->item.type = CONF_ITEM_DATA;
cd->item.parent = parent;
- cd->name = strdup(name);
cd->data = data;
cd->free = data_free;
+ p += sizeof(*cd);
+ memcpy(p, name, name_len);
+ cd->name = p;
return cd;
}
int flag)
{
if (!cs || !name) return NULL;
-
+
/*
* Find the name in the tree, for speed.
*/
if (cs->data_tree) {
- CONF_DATA mycd, *cd;
+ CONF_DATA mycd;
mycd.name = name;
mycd.flag = flag;
- cd = rbtree_finddata(cs->data_tree, &mycd);
- if (cd) return cd->data;
+ return rbtree_finddata(cs->data_tree, &mycd);
}
return NULL;
*/
void *cf_data_find(CONF_SECTION *cs, const char *name)
{
- return cf_data_find_internal(cs, name, 0);
+ CONF_DATA *cd = cf_data_find_internal(cs, name, 0);
+
+ if (cd) return cd->data;
+ return NULL;
}
return cf_data_add_internal(cs, name, data, data_free, 0);
}
-
+#if 0
/*
* Copy CONF_DATA from src to dst
*/
static void cf_section_copy_data(CONF_SECTION *s, CONF_SECTION *d)
{
-
+
CONF_ITEM *cd, *next, **last;
/*
rad_assert(d->data_tree == NULL);
d->data_tree = s->data_tree;
s->data_tree = NULL;
-
+
/*
* Walk through src, moving CONF_ITEM_DATA
* to dst, by hand.
last = &(s->children);
for (cd = s->children; cd != NULL; cd = next) {
next = cd->next;
-
+
/*
* Recursively copy data from child sections.
*/
if (cd->type == CONF_ITEM_SECTION) {
CONF_SECTION *s1, *d1;
-
+
s1 = cf_itemtosection(cd);
d1 = cf_section_sub_find_name2(d, s1->name1, s1->name2);
if (d1) {
last = &(cd->next);
continue;
}
-
+
/*
* Remove it from the src list
*/
*last = cd->next;
cd->next = NULL;
-
+
/*
* Add it to the dst list
*/
*/
if ((strcmp(pa->attr, pb->attr) != 0) ||
(strcmp(pa->value, pb->value) != 0)) return 0;
-
+
/*
* And go to the next element.
return 1; /* rcode means anything? */
}
+#endif
int cf_section_template(CONF_SECTION *cs, CONF_SECTION *template)
{
return 0;
}
+
+/*
+ * This is here to make the rest of the code easier to read. It
+ * ties conffile.c to log.c, but it means we don't have to
+ * pollute every other function with the knowledge of the
+ * configuration internals.
+ */
+void cf_log_err(CONF_ITEM *ci, const char *fmt, ...)
+{
+ va_list ap;
+ char buffer[256];
+
+ va_start(ap, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ va_end(ap);
+
+ radlog(L_ERR, "%s[%d]: %s", ci->filename, ci->lineno, buffer);
+}
+
+
+void cf_log_info(CONF_SECTION *cs, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (debug_flag > 1 && cf_log_config && cs) vradlog(L_DBG, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Wrapper to simplify the code.
+ */
+void cf_log_module(CONF_SECTION *cs, const char *fmt, ...)
+{
+ va_list ap;
+ char buffer[256];
+
+ va_start(ap, fmt);
+ if (debug_flag > 1 && cf_log_modules && cs) {
+ vsnprintf(buffer, sizeof(buffer), fmt, ap);
+
+ radlog(L_DBG, " Module: %s", buffer);
+ }
+ va_end(ap);
+}
+
+const CONF_PARSER *cf_section_parse_table(CONF_SECTION *cs)
+{
+ if (!cs) return NULL;
+
+ return cs->variables;
+}
+
#if 0
/*
* JMG dump_config tries to dump the config structure in a readable format
return 0;
}
-int dump_config(void)
+int dump_config(CONF_SECTION *cs)
{
- return dump_config_section(mainconfig.config, 0);
+ return dump_config_section(cs, 0);
}
#endif
+
+static const char *cf_pair_print_value(const CONF_PAIR *cp,
+ char *buffer, size_t buflen)
+{
+ char *p;
+
+ if (!cp->value) return "";
+
+ switch (cp->value_type) {
+ default:
+ case T_BARE_WORD:
+ snprintf(buffer, buflen, "%s", cp->value);
+ break;
+
+ case T_SINGLE_QUOTED_STRING:
+ snprintf(buffer, buflen, "'%s'", cp->value);
+ break;
+
+ case T_DOUBLE_QUOTED_STRING:
+ buffer[0] = '"';
+ fr_print_string(cp->value, strlen(cp->value),
+ buffer + 1, buflen - 3);
+ p = buffer + strlen(buffer); /* yuck... */
+ p[0] = '"';
+ p[1] = '\0';
+ break;
+ }
+
+ return buffer;
+}
+
+
+int cf_pair2xml(FILE *fp, const CONF_PAIR *cp)
+{
+ fprintf(fp, "<%s>", cp->attr);
+ if (cp->value) {
+ char buffer[2048];
+
+ char *p = buffer;
+ const char *q = cp->value;
+
+ while (*q && (p < (buffer + sizeof(buffer) - 1))) {
+ if (q[0] == '&') {
+ memcpy(p, "&", 4);
+ p += 5;
+
+ } else if (q[0] == '<') {
+ memcpy(p, "<", 4);
+ p += 4;
+
+ } else if (q[0] == '>') {
+ memcpy(p, ">", 4);
+ p += 4;
+
+ } else {
+ *(p++) = *q;
+ }
+ q++;
+ }
+
+ *p = '\0';
+ fprintf(fp, "%s", buffer);
+ }
+
+ fprintf(fp, "</%s>\n", cp->attr);
+
+ return 1;
+}
+
+int cf_section2xml(FILE *fp, const CONF_SECTION *cs)
+{
+ CONF_ITEM *ci, *next;
+
+ /*
+ * Section header
+ */
+ fprintf(fp, "<%s>\n", cs->name1);
+ if (cs->name2) {
+ fprintf(fp, "<_name2>%s</_name2>\n", cs->name2);
+ }
+
+ /*
+ * Loop over contents.
+ */
+ for (ci = cs->children; ci; ci = next) {
+ next = ci->next;
+
+ switch (ci->type) {
+ case CONF_ITEM_PAIR:
+ if (!cf_pair2xml(fp, (CONF_PAIR *) ci)) return 0;
+ break;
+
+ case CONF_ITEM_SECTION:
+ if (!cf_section2xml(fp, (CONF_SECTION *) ci)) return 0;
+ break;
+
+ default: /* should really be an error. */
+ break;
+
+ }
+ }
+
+ fprintf(fp, "</%s>\n", cs->name1);
+
+ return 1; /* success */
+}
+
+int cf_pair2file(FILE *fp, const CONF_PAIR *cp)
+{
+ char buffer[2048];
+
+ fprintf(fp, "\t%s = %s\n", cp->attr,
+ cf_pair_print_value(cp, buffer, sizeof(buffer)));
+
+ return 1;
+}
+
+int cf_section2file(FILE *fp, const CONF_SECTION *cs)
+{
+ const CONF_ITEM *ci, *next;
+
+ /*
+ * Section header
+ */
+ if (!cs->name2) {
+ fprintf(fp, "%s {\n", cs->name1);
+ } else {
+ fprintf(fp, "%s %s {\n",
+ cs->name1, cs->name2);
+ }
+
+ /*
+ * Loop over contents.
+ */
+ for (ci = cs->children; ci; ci = next) {
+ next = ci->next;
+
+ switch (ci->type) {
+ case CONF_ITEM_PAIR:
+ if (!cf_pair2file(fp, (const CONF_PAIR *) ci)) return 0;
+ break;
+
+ case CONF_ITEM_SECTION:
+ if (!cf_section2file(fp, (const CONF_SECTION *) ci)) return 0;
+ break;
+
+ default: /* should really be an error. */
+ break;
+
+ }
+ }
+
+ fprintf(fp, "}\n");
+
+ return 1; /* success */
+}