📄 conffile.c
字号:
/* * conffile.c Read the radiusd.conf file. * * Yep I should learn to use lex & yacc, or at least * write a decent parser. I know how to do that, really :) * miquels@cistron.nl * * Version: $Id: conffile.c,v 1.100.2.1.2.5 2007/03/05 14:20:38 aland Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2000 The FreeRADIUS server project * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl> * Copyright 2000 Alan DeKok <aland@ox.org> */#include "autoconf.h"#include "libradius.h"#include <stdlib.h>#include <string.h>#ifdef HAVE_NETINET_IN_H# include <netinet/in.h>#endif#ifdef HAVE_SYS_STAT_H#include <sys/stat.h>#endif#include "radiusd.h"#include "rad_assert.h"#include "conffile.h"#include "token.h"#include "modules.h"static const char rcsid[] ="$Id: conffile.c,v 1.100.2.1.2.5 2007/03/05 14:20:38 aland Exp $";#define xstrdup strduptypedef enum conf_type { CONF_ITEM_PAIR, CONF_ITEM_SECTION} CONF_ITEM_TYPE;struct conf_item { struct conf_item *next; struct conf_part *parent; int lineno; CONF_ITEM_TYPE type;};struct conf_pair { CONF_ITEM item; char *attr; char *value; LRAD_TOKEN operator;};struct conf_part { CONF_ITEM item; char *name1; char *name2; void *base; const CONF_PARSER *variables; struct conf_item *children;};/* * Isolate the scary casts in these tiny provably-safe functions */CONF_PAIR *cf_itemtopair(CONF_ITEM *ci){ if (ci == NULL) return NULL; rad_assert(ci->type == CONF_ITEM_PAIR); return (CONF_PAIR *)ci;}CONF_SECTION *cf_itemtosection(CONF_ITEM *ci){ if (ci == NULL) return NULL; rad_assert(ci->type == CONF_ITEM_SECTION); return (CONF_SECTION *)ci;}CONF_ITEM *cf_pairtoitem(CONF_PAIR *cp){ if (cp == NULL) return NULL; return (CONF_ITEM *)cp;}CONF_ITEM *cf_sectiontoitem(CONF_SECTION *cs){ if (cs == NULL) return NULL; return (CONF_ITEM *)cs;}/* * Create a new CONF_PAIR */static CONF_PAIR *cf_pair_alloc(const char *attr, const char *value, LRAD_TOKEN operator, CONF_SECTION *parent){ CONF_PAIR *cp; cp = (CONF_PAIR *)rad_malloc(sizeof(CONF_PAIR)); memset(cp, 0, sizeof(CONF_PAIR)); cp->item.type = CONF_ITEM_PAIR; cp->item.parent = parent; cp->attr = xstrdup(attr); cp->value = xstrdup(value); cp->operator = operator; return cp;}/* * Free a CONF_PAIR */void cf_pair_free(CONF_PAIR **cp){ if (!cp || !*cp) return; if ((*cp)->attr) free((*cp)->attr); if ((*cp)->value) free((*cp)->value);#ifndef NDEBUG memset(*cp, 0, sizeof(*cp));#endif free(*cp); *cp = NULL;}/* * Free strings we've parsed into data structures. */static void cf_section_parse_free(void *base, const CONF_PARSER *variables){ int i; /* * Don't automatically free the strings if we're being * called from a module. */ if (base || !variables) return; /* * Free up dynamically allocated string pointers. */ for (i = 0; variables[i].name != NULL; i++) { char **p; if (variables[i].type != PW_TYPE_STRING_PTR) { continue; } /* * Prefer the data, if it's there. * Else use the base + offset. */ if (!variables[i].data) { continue; } /* * FIXME: Add base */ p = (char **) (variables[i].data); free(*p); *p = NULL; }}/* * Allocate a CONF_SECTION */static CONF_SECTION *cf_section_alloc(const char *name1, const char *name2, CONF_SECTION *parent, const char *cf){ CONF_SECTION *cs; if (name1 == NULL || !name1[0]) name1 = "main"; cs = (CONF_SECTION *)rad_malloc(sizeof(CONF_SECTION)); memset(cs, 0, sizeof(CONF_SECTION)); cs->item.type = CONF_ITEM_SECTION; cs->item.parent = parent; cs->name1 = strdup(name1); cs->name2 = (name2 && *name2) ? xstrdup(name2) : NULL; return cs;}/* * Free a CONF_SECTION */void cf_section_free(CONF_SECTION **cs){ CONF_ITEM *ci, *next; if (!cs || !*cs) return; if ((*cs)->variables) { cf_section_parse_free((*cs)->base, (*cs)->variables); } for (ci = (*cs)->children; ci; ci = next) { next = ci->next; if (ci->type==CONF_ITEM_PAIR) { CONF_PAIR *pair = cf_itemtopair(ci); cf_pair_free(&pair); } else { CONF_SECTION *section = cf_itemtosection(ci); cf_section_free(§ion); } } if ((*cs)->name1) free((*cs)->name1); if ((*cs)->name2) free((*cs)->name2); /* * And free the section */#ifndef NDEBUG memset(*cs, 0, sizeof(*cs));#endif free(*cs); *cs = NULL;}/* * Add an item to a configuration section. */static void cf_item_add(CONF_SECTION *cs, CONF_ITEM *ci_new){ CONF_ITEM *ci; for (ci = cs->children; ci && ci->next; ci = ci->next) ; if (ci == NULL) cs->children = ci_new; else ci->next = ci_new;}/* * Expand the variables in an input string. */static const char *cf_expand_variables(const char *cf, int *lineno, CONF_SECTION *outercs, char *output, const char *input){ char *p; const char *end, *ptr; char name[8192]; CONF_SECTION *parentcs; /* * 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 */ } p = output; ptr = input; while (*ptr) { /* * Ignore anything other than "${" */ if ((*ptr == '$') && (ptr[1] == '{')) { int up; CONF_PAIR *cp; CONF_SECTION *cs; /* * Look for trailing '}', and log a * warning for anything that doesn't match, * and exit with a fatal error. */ end = strchr(ptr, '}'); if (end == NULL) { *p = '\0'; radlog(L_INFO, "%s[%d]: Variable expansion missing }", cf, *lineno); return NULL; } ptr += 2; cp = NULL; up = 0; /* * ${.foo} means "foo from the current section" */ 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; } } } 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; } 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 entry %s for string %s", name, input); return NULL; } } } /* until cp is non-NULL */ /* * Substitute the value of the variable. */ strcpy(p, cp->value); p += strlen(p); ptr = end + 1; } else if (memcmp(ptr, "$ENV{", 5) == 0) { char *env; ptr += 5; /* * Look for trailing '}', and log a * warning for anything that doesn't match, * and exit with a fatal error. */ end = strchr(ptr, '}'); if (end == NULL) { *p = '\0'; radlog(L_INFO, "%s[%d]: Environment variable expansion missing }", cf, *lineno); return NULL; } memcpy(name, ptr, end - ptr); name[end - ptr] = '\0'; /* * Get the environment variable. * If none exists, then make it an empty string. */ env = getenv(name); if (env == NULL) { *name = '\0'; env = name; } strcpy(p, env); p += strlen(p); ptr = end + 1; } else { /* * Copy it over verbatim. */ *(p++) = *(ptr++); } } /* loop over all of the input string. */ *p = '\0'; return output;}/* * Parse a configuration section into user-supplied variables. */int cf_section_parse(CONF_SECTION *cs, void *base, const CONF_PARSER *variables){ int i; int rcode; char **q; CONF_PAIR *cp; CONF_SECTION *subsection; uint32_t ipaddr; char buffer[8192]; const char *value; void *data; /* * Handle the user-supplied variables. */ for (i = 0; variables[i].name != NULL; i++) { value = variables[i].dflt; if (variables[i].data) { data = variables[i].data; /* prefer this. */ } else if (base) { data = ((char *)base) + variables[i].offset; } else { data = variables[i].data; } cp = cf_pair_find(cs, variables[i].name); if (cp) { value = cp->value; } switch (variables[i].type) { case PW_TYPE_SUBSECTION: subsection = cf_section_sub_find(cs,variables[i].name); /* * If the configuration section is NOT there, * then ignore it. * * FIXME! This is probably wrong... we should * probably set the items to their default values. */ if (subsection == NULL) { break; } rcode = cf_section_parse(subsection, base, (CONF_PARSER *) data); if (rcode < 0) { cf_section_parse_free(base, variables); return -1; } break; case PW_TYPE_BOOLEAN: /* * Allow yes/no and on/off */ if ((strcasecmp(value, "yes") == 0) || (strcasecmp(value, "on") == 0)) { *(int *)data = 1; } else if ((strcasecmp(value, "no") == 0) || (strcasecmp(value, "off") == 0)) { *(int *)data = 0; } else { *(int *)data = 0; radlog(L_ERR, "Bad value \"%s\" for boolean variable %s", value, variables[i].name); cf_section_parse_free(base, variables); return -1; } DEBUG2(" %s: %s = %s", cs->name1, variables[i].name, value); break; case PW_TYPE_INTEGER: *(int *)data = strtol(value, 0, 0); DEBUG2(" %s: %s = %d", cs->name1, variables[i].name, *(int *)data); break; case PW_TYPE_STRING_PTR: q = (char **) data; if (*q != NULL) { free(*q); } /* * Expand variables while parsing, * but ONLY expand ones which haven't already * been expanded. */ if (value && (value == variables[i].dflt)) { value = cf_expand_variables("?", &cs->item.lineno, cs, buffer, value); if (!value) { cf_section_parse_free(base, variables); return -1; } } DEBUG2(" %s: %s = \"%s\"", cs->name1, variables[i].name, value ? value : "(null)"); *q = value ? strdup(value) : NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -