X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmain%2Fconffile.c;h=ee47afb7928a501d8c9cf77272775c56cf91e5b2;hb=671bde96d7e9e68b8dbf871db4d9ded33964a4d4;hp=0e3e49b4e4124caa95db4ed5a5a4b9a9ee0f2118;hpb=4ebfb47a7cab5f065e93e69885c956d6674806b8;p=freeradius.git diff --git a/src/main/conffile.c b/src/main/conffile.c index 0e3e49b..ee47afb 100644 --- a/src/main/conffile.c +++ b/src/main/conffile.c @@ -34,11 +34,11 @@ RCSID("$Id$") #ifdef HAVE_DIRENT_H #include +#endif #ifdef HAVE_SYS_STAT_H #include #endif -#endif #include @@ -53,13 +53,15 @@ struct conf_item { 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; @@ -97,6 +99,9 @@ static int cf_data_add_internal(CONF_SECTION *cs, const char *name, 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 */ @@ -145,16 +150,34 @@ static CONF_ITEM *cf_datatoitem(CONF_DATA *cd) * 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; @@ -167,10 +190,9 @@ void cf_pair_free(CONF_PAIR **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)); @@ -185,7 +207,7 @@ static void cf_data_free(CONF_DATA **cd) { if (!cd || !*cd) return; - if ((*cd)->flag != 0) free((*cd)->name); + /* name is allocated contiguous with cd */ if (!(*cd)->free) { free((*cd)->data); } else { @@ -260,9 +282,10 @@ static int data_cmp(const void *a, const void *b) /* * 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 @@ -318,9 +341,7 @@ void cf_section_free(CONF_SECTION **cs) 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; @@ -333,7 +354,6 @@ void cf_section_free(CONF_SECTION **cs) break; case CONF_ITEM_SECTION: { - CONF_SECTION *section = cf_itemtosection(ci); cf_section_free(§ion); } @@ -350,10 +370,10 @@ void cf_section_free(CONF_SECTION **cs) } } - 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) @@ -381,27 +401,32 @@ void cf_section_free(CONF_SECTION **cs) 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); @@ -422,6 +447,42 @@ static CONF_SECTION *cf_section_alloc(const char *name1, const char *name2, 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. @@ -450,24 +511,26 @@ static void cf_item_add(CONF_SECTION *cs, CONF_ITEM *ci) 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 @@ -477,7 +540,7 @@ static void cf_item_add(CONF_SECTION *cs, CONF_ITEM *ci) */ 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); @@ -505,28 +568,151 @@ static void cf_item_add(CONF_SECTION *cs, CONF_ITEM *ci) } /* 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; @@ -535,9 +721,8 @@ static const char *cf_expand_variables(const char *cf, int *lineno, * 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}, @@ -559,107 +744,35 @@ static const char *cf_expand_variables(const char *cf, int *lineno, 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; @@ -682,6 +795,16 @@ static const char *cf_expand_variables(const char *cf, int *lineno, 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'; @@ -727,11 +850,11 @@ int cf_item_parse(CONF_SECTION *cs, const char *name, 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; @@ -743,6 +866,10 @@ int cf_item_parse(CONF_SECTION *cs, const char *name, value = dflt; } + if (!value) { + return 0; + } + switch (type) { case PW_TYPE_BOOLEAN: /* @@ -759,20 +886,20 @@ int cf_item_parse(CONF_SECTION *cs, const char *name, 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 @@ -781,7 +908,9 @@ int cf_item_parse(CONF_SECTION *cs, const char *name, if (value == dflt) { char buffer[8192]; - int lineno = cs->item.lineno; + int lineno = 0; + + if (cs) lineno = cs->item.lineno; /* * FIXME: sizeof(buffer)? @@ -791,11 +920,11 @@ int cf_item_parse(CONF_SECTION *cs, const char *name, 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 @@ -806,7 +935,7 @@ int cf_item_parse(CONF_SECTION *cs, const char *name, if (*q != NULL) { free(*q); } - + /* * Expand variables which haven't already been * expanded automagically when the configuration @@ -815,7 +944,9 @@ int cf_item_parse(CONF_SECTION *cs, const char *name, if (value == dflt) { char buffer[8192]; - int lineno = cs->item.lineno; + int lineno = 0; + + if (cs) lineno = cs->item.lineno; /* * FIXME: sizeof(buffer)? @@ -825,14 +956,18 @@ int cf_item_parse(CONF_SECTION *cs, const char *name, 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) { @@ -853,35 +988,40 @@ int cf_item_parse(CONF_SECTION *cs, const char *name, */ 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; } @@ -896,11 +1036,13 @@ int cf_section_parse(CONF_SECTION *cs, void *base, 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); } @@ -912,9 +1054,9 @@ int cf_section_parse(CONF_SECTION *cs, void *base, * 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. @@ -928,14 +1070,14 @@ int cf_section_parse(CONF_SECTION *cs, void *base, 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) { @@ -954,30 +1096,140 @@ int cf_section_parse(CONF_SECTION *cs, void *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]; @@ -985,7 +1237,7 @@ static int cf_section_read(const char *file, int *lineno, FILE *fp, char buf3[8192]; int t1, t2, t3; char *cbuf = buf; - int len; + size_t len; this = current; /* add items here */ @@ -993,55 +1245,100 @@ static int cf_section_read(const char *file, int *lineno, FILE *fp, * 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 @@ -1052,9 +1349,9 @@ static int cf_section_read(const char *file, int *lineno, FILE *fp, 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; @@ -1066,12 +1363,30 @@ static int cf_section_read(const char *file, int *lineno, FILE *fp, * 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/ @@ -1084,11 +1399,11 @@ static int cf_section_read(const char *file, int *lineno, FILE *fp, 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; } @@ -1107,6 +1422,7 @@ static int cf_section_read(const char *file, int *lineno, FILE *fp, for (p = dp->d_name; *p != '\0'; p++) { if (isalpha((int)*p) || isdigit((int)*p) || + (*p == '-') || (*p == '_') || (*p == '.')) continue; break; @@ -1130,6 +1446,15 @@ static int cf_section_read(const char *file, int *lineno, FILE *fp, } 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; } @@ -1137,29 +1462,187 @@ static int cf_section_read(const char *file, int *lineno, FILE *fp, 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; /* @@ -1167,49 +1650,12 @@ static int cf_section_read(const char *file, int *lineno, FILE *fp, */ 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)); } /* @@ -1217,7 +1663,7 @@ static int cf_section_read(const char *file, int *lineno, FILE *fp, */ 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; } @@ -1228,20 +1674,21 @@ static int cf_section_read(const char *file, int *lineno, FILE *fp, /* * 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 @@ -1249,25 +1696,22 @@ int cf_file_include(const char *file, CONF_SECTION *cs) #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; } @@ -1276,9 +1720,33 @@ int cf_file_include(const char *file, CONF_SECTION *cs) */ 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; @@ -1287,14 +1755,26 @@ int cf_file_include(const char *file, CONF_SECTION *cs) /* * 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; } @@ -1310,7 +1790,7 @@ CONF_PAIR *cf_pair_find(const CONF_SECTION *cs, const char *name) CONF_ITEM *ci; CONF_PAIR *cp = NULL; - if (!cs) cs = mainconfig.config; + if (!cs) return NULL; /* * Find the name in the tree, for speed. @@ -1340,7 +1820,7 @@ CONF_PAIR *cf_pair_find(const CONF_SECTION *cs, const char *name) * 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); } @@ -1349,12 +1829,67 @@ char *cf_pair_attr(CONF_PAIR *pair) * 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 */ @@ -1375,7 +1910,7 @@ const char *cf_section_name2(const CONF_SECTION *cs) /* * 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; @@ -1391,7 +1926,7 @@ char *cf_section_value_find(const CONF_SECTION *cs, const char *attr) */ 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; @@ -1436,10 +1971,12 @@ CONF_SECTION *cf_section_sub_find(const CONF_SECTION *cs, const char *name) { 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; @@ -1467,16 +2004,14 @@ CONF_SECTION *cf_section_sub_find_name2(const CONF_SECTION *cs, { 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); @@ -1545,6 +2080,22 @@ CONF_SECTION *cf_subsection_find_next(CONF_SECTION *section, 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. */ @@ -1568,6 +2119,16 @@ int cf_section_lineno(CONF_SECTION *section) 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; @@ -1586,17 +2147,24 @@ int cf_item_is_pair(CONF_ITEM *item) 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; } @@ -1605,17 +2173,16 @@ static void *cf_data_find_internal(CONF_SECTION *cs, const char *name, 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; @@ -1626,7 +2193,10 @@ static void *cf_data_find_internal(CONF_SECTION *cs, const char *name, */ 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; } @@ -1664,13 +2234,13 @@ int cf_data_add(CONF_SECTION *cs, const char *name, 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; /* @@ -1681,7 +2251,7 @@ static void cf_section_copy_data(CONF_SECTION *s, CONF_SECTION *d) 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. @@ -1689,13 +2259,13 @@ static void cf_section_copy_data(CONF_SECTION *s, CONF_SECTION *d) 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) { @@ -1712,13 +2282,13 @@ static void cf_section_copy_data(CONF_SECTION *s, CONF_SECTION *d) last = &(cd->next); continue; } - + /* * Remove it from the src list */ *last = cd->next; cd->next = NULL; - + /* * Add it to the dst list */ @@ -1810,7 +2380,7 @@ static int cf_section_cmp(CONF_SECTION *a, CONF_SECTION *b) */ if ((strcmp(pa->attr, pb->attr) != 0) || (strcmp(pa->value, pb->value) != 0)) return 0; - + /* * And go to the next element. @@ -1864,6 +2434,7 @@ int cf_section_migrate(CONF_SECTION *dst, CONF_SECTION *src) return 1; /* rcode means anything? */ } +#endif int cf_section_template(CONF_SECTION *cs, CONF_SECTION *template) { @@ -1874,6 +2445,59 @@ 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 @@ -1919,8 +2543,164 @@ static int dump_config_section(CONF_SECTION *cs, int indent) 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, "\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\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, "\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 */ +}