*
* 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 <freeradius-devel/autoconf.h>
#include <stdlib.h>
#include <string.h>
-#if HAVE_NETINET_IN_H
+#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
-#include "radiusd.h"
-#include "rad_assert.h"
-#include "conffile.h"
-#include "token.h"
-#include "modules.h"
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#endif
+
+#include <ctype.h>
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/conffile.h>
+#include <freeradius-devel/token.h>
+#include <freeradius-devel/modules.h>
static const char rcsid[] =
"$Id$";
-#define xstrdup strdup
-
typedef enum conf_type {
+ CONF_ITEM_INVALID = 0,
CONF_ITEM_PAIR,
- CONF_ITEM_SECTION
+ CONF_ITEM_SECTION,
+ CONF_ITEM_DATA
} CONF_ITEM_TYPE;
struct conf_item {
};
struct conf_part {
CONF_ITEM item;
- char *name1;
- char *name2;
+ const char *name1;
+ const char *name2;
struct conf_item *children;
+ struct conf_item *tail; /* for speed */
+ rbtree_t *pair_tree; /* and a partridge.. */
+ rbtree_t *section_tree; /* no jokes here */
+ rbtree_t *name2_tree; /* for sections of the same name2 */
+ rbtree_t *data_tree;
};
-CONF_SECTION *config = NULL;
/*
- * Yucky hacks.
+ * Internal data that is associated with a configuration section,
+ * so that we don't have to track it separately.
*/
-extern RADCLIENT *clients;
-extern REALM *realms;
+struct conf_data {
+ CONF_ITEM item;
+ const char *name;
+ int flag;
+ void *data; /* user data */
+ void (*free)(void *); /* free user data function */
+};
-static int generate_realms(const char *filename);
-static int generate_clients(const char *filename);
-static CONF_SECTION *conf_read(const char *fromfile, int fromline,
- const char *conffile, CONF_SECTION *parent);
-#ifndef RADIUS_CONFIG
-#define RADIUS_CONFIG "radiusd.conf"
-#endif
+static int cf_data_add_internal(CONF_SECTION *cs, const char *name,
+ void *data, void (*data_free)(void *),
+ int flag);
+static void *cf_data_find_internal(CONF_SECTION *cs, const char *name,
+ int flag);
/*
* Isolate the scary casts in these tiny provably-safe functions
return (CONF_ITEM *)cs;
}
+static CONF_DATA *cf_itemtodata(CONF_ITEM *ci)
+{
+ if (ci == NULL)
+ return NULL;
+ rad_assert(ci->type == CONF_ITEM_DATA);
+ return (CONF_DATA *)ci;
+}
+static CONF_ITEM *cf_datatoitem(CONF_DATA *cd)
+{
+ if (cd == NULL)
+ return NULL;
+ return (CONF_ITEM *)cd;
+}
+
/*
* Create a new CONF_PAIR
*/
static CONF_PAIR *cf_pair_alloc(const char *attr, const char *value,
- LRAD_TOKEN operator, CONF_SECTION *parent)
+ LRAD_TOKEN operator, CONF_SECTION *parent)
{
CONF_PAIR *cp;
- cp = (CONF_PAIR *)rad_malloc(sizeof(CONF_PAIR));
- memset(cp, 0, sizeof(CONF_PAIR));
+ cp = rad_malloc(sizeof(*cp));
+ memset(cp, 0, sizeof(*cp));
cp->item.type = CONF_ITEM_PAIR;
cp->item.parent = parent;
- cp->attr = xstrdup(attr);
- cp->value = xstrdup(value);
+ cp->attr = strdup(attr);
+ cp->value = strdup(value);
cp->operator = operator;
return cp;
*cp = NULL;
}
+
+static void cf_data_free(CONF_DATA **cd)
+{
+ if (!cd || !*cd) return;
+
+ free((*cd)->name);
+ if (!(*cd)->free) {
+ free((*cd)->data);
+ } else {
+ ((*cd)->free)((*cd)->data);
+ }
+#ifndef NDEBUG
+ memset(*cd, 0, sizeof(*cd));
+#endif
+ free(*cd);
+ *cd = NULL;
+}
+
/*
- * Allocate a CONF_SECTION
+ * rbtree callback function
*/
-static CONF_SECTION *cf_section_alloc(const char *name1, const char *name2,
- CONF_SECTION *parent)
+static int pair_cmp(const void *a, const void *b)
{
- CONF_SECTION *cs;
+ const CONF_PAIR *one = a;
+ const CONF_PAIR *two = b;
- if (name1 == NULL || !name1[0])
- name1 = "main";
+ return strcmp(one->attr, two->attr);
+}
- 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;
+/*
+ * rbtree callback function
+ */
+static int section_cmp(const void *a, const void *b)
+{
+ const CONF_SECTION *one = a;
+ const CONF_SECTION *two = b;
+
+ return strcmp(one->name1, two->name1);
+}
+
+
+/*
+ * rbtree callback function
+ */
+static int name2_cmp(const void *a, const void *b)
+{
+ const CONF_SECTION *one = a;
+ const CONF_SECTION *two = b;
+
+ rad_assert(strcmp(one->name1, two->name1) == 0);
+
+ if (!one->name2 && !two->name2) return 0;
+ if (!one->name2) return -1;
+ if (!two->name2) return +1;
+
+ return strcmp(one->name2, two->name2);
+}
+
+
+/*
+ * rbtree callback function
+ */
+static int data_cmp(const void *a, const void *b)
+{
+ int rcode;
+
+ const CONF_DATA *one = a;
+ const CONF_DATA *two = b;
+
+ rcode = one->flag - two->flag;
+ if (rcode != 0) return rcode;
+
+ return strcmp(one->name, two->name);
}
/*
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);
+
+ switch (ci->type) {
+ case CONF_ITEM_PAIR: {
+ CONF_PAIR *pair = cf_itemtopair(ci);
+ cf_pair_free(&pair);
+ }
+ break;
+
+ case CONF_ITEM_SECTION: {
+
+ CONF_SECTION *section = cf_itemtosection(ci);
+ cf_section_free(§ion);
+ }
+ break;
+
+ case CONF_ITEM_DATA: {
+ CONF_DATA *data = cf_itemtodata(ci);
+ cf_data_free(&data);
+ }
+ break;
+
+ default: /* should really be an error. */
+ break;
}
}
- if ((*cs)->name1)
+ if ((*cs)->name1)
free((*cs)->name1);
- if ((*cs)->name2)
+ if ((*cs)->name2)
free((*cs)->name2);
+ if ((*cs)->pair_tree)
+ rbtree_free((*cs)->pair_tree);
+ if ((*cs)->section_tree)
+ rbtree_free((*cs)->section_tree);
+ if ((*cs)->name2_tree)
+ rbtree_free((*cs)->name2_tree);
+ if ((*cs)->data_tree)
+ rbtree_free((*cs)->data_tree);
/*
* And free the section
*cs = NULL;
}
+
/*
- * Add an item to a configuration section.
+ * Allocate a CONF_SECTION
*/
-static void cf_item_add(CONF_SECTION *cs, CONF_ITEM *ci_new)
+static CONF_SECTION *cf_section_alloc(const char *name1, const char *name2,
+ CONF_SECTION *parent)
{
- CONF_ITEM *ci;
+ CONF_SECTION *cs;
+
+ if (name1 == NULL || !name1[0])
+ name1 = "main";
+
+ cs = rad_malloc(sizeof(*cs));
+ 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;
+ }
- for (ci = cs->children; ci && ci->next; ci = ci->next)
- ;
+ if (name2 && *name2) {
+ cs->name2 = strdup(name2);
+ if (!cs->name2) {
+ cf_section_free(&cs);
+ return NULL;
+ }
+ }
+ cs->pair_tree = rbtree_create(pair_cmp, NULL, 0);
+ if (!cs->pair_tree) {
+ cf_section_free(&cs);
+ return NULL;
+ }
- if (ci == NULL)
- cs->children = ci_new;
- else
- ci->next = ci_new;
+ /*
+ * Don't create a data tree, it may not be needed.
+ */
+
+ /*
+ * Don't create the section tree here, it may not
+ * be needed.
+ */
+ return cs;
+}
+
+
+/*
+ * Add an item to a configuration section.
+ */
+static void cf_item_add(CONF_SECTION *cs, CONF_ITEM *ci)
+{
+ if (!cs->children) {
+ rad_assert(cs->tail == NULL);
+ cs->children = ci;
+ } else {
+ rad_assert(cs->tail != NULL);
+ cs->tail->next = ci;
+ }
+
+ /*
+ * Update the trees (and tail) for each item added.
+ */
+ for (/* nothing */; ci != NULL; ci = ci->next) {
+ cs->tail = ci;
+
+ /*
+ * For fast lookups, pair's and sections get
+ * added to rbtree's.
+ */
+ switch (ci->type) {
+ case CONF_ITEM_PAIR:
+ rbtree_insert(cs->pair_tree, ci);
+ break;
+
+ case CONF_ITEM_SECTION: {
+ const 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) {
+ 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
+ * the given name1, and
+ * create a new tree
+ * under it.
+ */
+ 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);
+ }
+ if (old_cs->name2_tree) {
+ rbtree_insert(old_cs->name2_tree, cs_new);
+ }
+ } /* had a name2 */
+ break;
+ } /* was a section */
+
+ case CONF_ITEM_DATA:
+ if (!cs->data_tree) {
+ cs->data_tree = rbtree_create(data_cmp, NULL, 0);
+ }
+ if (cs->data_tree) {
+ rbtree_insert(cs->data_tree, ci);
+ }
+ break;
+
+ default: /* FIXME: assert & error! */
+ break;
+
+ } /* switch over conf types */
+ } /* loop over ci */
}
/*
* Expand the variables in an input string.
*/
static const char *cf_expand_variables(const char *cf, int *lineno,
- CONF_SECTION *cs,
- char *output, const char *input)
+ const CONF_SECTION *outercs,
+ char *output, const char *input)
{
char *p;
const char *end, *ptr;
- char name[1024];
- CONF_PAIR *cpn;
- CONF_SECTION *outercs;
+ char name[8192];
+ const 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 >= ' ') {
+ while (*ptr) {
/*
* Ignore anything other than "${"
*/
if ((*ptr == '$') && (ptr[1] == '{')) {
+ int up;
+ CONF_PAIR *cp;
+ const CONF_SECTION *cs;
+
+ /*
+ * FIXME: Add support for ${foo:-bar},
+ * like in xlat.c
+ */
+
/*
* Look for trailing '}', and log a
* warning for anything that doesn't match,
cf, *lineno);
return NULL;
}
-
+
ptr += 2;
-
- memcpy(name, ptr, end - ptr);
- name[end - ptr] = '\0';
-
- cpn = cf_pair_find(cs, name);
-
+
+ cp = NULL;
+ up = 0;
+
/*
- * Also look recursively up the section tree,
- * so things like ${confdir} can be defined
- * there and used inside the module config
- * sections.
+ * ${.foo} means "foo from the current section"
*/
- for (outercs=cs->item.parent;
- (cpn == NULL) && (outercs != NULL);
- outercs=outercs->item.parent) {
- cpn = cf_pair_find(outercs, name);
- }
- if (!cpn) {
- radlog(L_ERR, "%s[%d]: Unknown variable \"%s\"",
- cf, *lineno, name);
- return NULL;
+ 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, cpn->value);
+ strcpy(p, cp->value);
p += strlen(p);
ptr = end + 1;
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.
*(p++) = *(ptr++);
}
} /* loop over all of the input string. */
-
+
*p = '\0';
return output;
}
+
/*
- * Parse a configuration section into user-supplied variables.
+ * Parses an item (not a CONF_ITEM) into the specified format,
+ * with a default value.
+ *
+ * Returns -1 on error, 0 for correctly parsed, and 1 if the
+ * default value was used. Note that the default value will be
+ * used ONLY if the CONF_PAIR is NULL.
*/
-int cf_section_parse(CONF_SECTION *cs, void *base, const CONF_PARSER *variables)
+int cf_item_parse(CONF_SECTION *cs, const char *name,
+ int type, void *data, const char *dflt)
{
- int i;
- int rcode;
+ int rcode = 0;
char **q;
- CONF_PAIR *cp;
- CONF_SECTION *subsection;
- uint32_t ipaddr;
- char buffer[1024];
const char *value;
- void *data;
+ lrad_ipaddr_t ipaddr;
+ const CONF_PAIR *cp;
+ char ipbuf[128];
- /*
- * Handle the user-supplied variables.
- */
- for (i = 0; variables[i].name != NULL; i++) {
- value = variables[i].dflt;
- if (base) {
- data = ((char *)base) + variables[i].offset;
+ cp = cf_pair_find(cs, name);
+ if (cp) {
+ value = cp->value;
+
+ } else if (!dflt) {
+ return 1; /* nothing to parse, return default value */
+
+ } else {
+ rcode = 1;
+ value = dflt;
+ }
+
+ switch (type) {
+ 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 {
- data = variables[i].data;
+ *(int *)data = 0;
+ radlog(L_ERR, "Bad value \"%s\" for boolean variable %s", value, name);
+ return -1;
}
-
- cp = cf_pair_find(cs, variables[i].name);
- if (cp) {
- value = cp->value;
+ DEBUG2(" %s: %s = %s", cs->name1, name, value);
+ break;
+
+ case PW_TYPE_INTEGER:
+ *(int *)data = strtol(value, 0, 0);
+ DEBUG2(" %s: %s = %d",
+ cs->name1, name,
+ *(int *)data);
+ break;
+
+ case PW_TYPE_STRING_PTR:
+ q = (char **) data;
+ if (*q != NULL) {
+ free(*q);
}
- switch (variables[i].type)
- {
- case PW_TYPE_SUBSECTION:
- subsection = cf_section_sub_find(cs,variables[i].name);
+ /*
+ * Expand variables which haven't already been
+ * expanded automagically when the configuration
+ * file was read.
+ */
+ if (value == dflt) {
+ char buffer[8192];
+
+ int lineno = cs->item.lineno;
/*
- * 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.
+ * FIXME: sizeof(buffer)?
*/
- if (subsection == NULL) {
- break;
- }
+ value = cf_expand_variables("?",
+ &lineno,
+ cs, buffer, value);
+ if (!value) return -1;
+ }
+
+ DEBUG2(" %s: %s = \"%s\"",
+ cs->name1, 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
+ * cache the result.
+ */
+ case PW_TYPE_FILENAME:
+ q = (char **) data;
+ if (*q != NULL) {
+ free(*q);
+ }
+
+ /*
+ * Expand variables which haven't already been
+ * expanded automagically when the configuration
+ * file was read.
+ */
+ if (value == dflt) {
+ char buffer[8192];
- rcode = cf_section_parse(subsection, base,
- (CONF_PARSER *) data);
- if (rcode < 0) {
- return -1;
- }
- break;
+ int lineno = cs->item.lineno;
- case PW_TYPE_BOOLEAN:
/*
- * Allow yes/no and on/off
+ * FIXME: sizeof(buffer)?
*/
- 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);
- return -1;
- }
- DEBUG2(" %s: %s = %s",
- cs->name1,
- variables[i].name,
- value);
- break;
+ value = cf_expand_variables("?",
+ &lineno,
+ cs, buffer, value);
+ if (!value) return -1;
+ }
+
+ DEBUG2(" %s: %s = \"%s\"",
+ cs->name1, name,
+ value ? value : "(null)");
+ *q = value ? strdup(value) : NULL;
- 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 (base == NULL && *q != NULL) {
- free(*q);
- }
+ /*
+ * And now we "stat" the file.XXX
+ */
+ if (*q) {
+ struct stat buf;
- /*
- * Expand variables while parsing,
- * but ONLY expand ones which haven't already
- * been expanded.
- */
- if (value && (value == variables[i].dflt)) {
- value = cf_expand_variables(NULL, 0, cs, buffer,value);
- if (!value) {
- return -1;
- }
+ if (stat(*q, &buf) == 0) {
+ time_t *mtime;
+
+ mtime = rad_malloc(sizeof(*mtime));
+ *mtime = buf.st_mtime;
+ /* FIXME: error? */
+ cf_data_add_internal(cs, *q, mtime, free,
+ PW_TYPE_FILENAME);
}
+ }
+ break;
- DEBUG2(" %s: %s = \"%s\"",
- cs->name1,
- variables[i].name,
- value ? value : "(null)");
- *q = value ? strdup(value) : NULL;
+ case PW_TYPE_IPADDR:
+ /*
+ * Allow '*' as any address
+ */
+ if (strcmp(value, "*") == 0) {
+ *(uint32_t *) data = htonl(INADDR_ANY);
+ DEBUG2(" %s: %s = *", cs->name1, name);
break;
-
- case PW_TYPE_IPADDR:
- /*
- * Allow '*' as any address
- */
- if (strcmp(value, "*") == 0) {
- *(uint32_t *) data = 0;
- break;
- }
- ipaddr = ip_getaddr(value);
- if (ipaddr == 0) {
- radlog(L_ERR, "Can't find IP address for host %s", value);
+ }
+ if (ip_hton(value, AF_INET, &ipaddr) < 0) {
+ radlog(L_ERR, "Can't find IP address for host %s", value);
+ return -1;
+ }
+ DEBUG2(" %s: %s = %s IP address [%s]",
+ cs->name1, 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(" %s: %s = %s IPv6 address [%s]",
+ cs->name1, 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;
+}
+
+/*
+ * Parse a configuration section into user-supplied variables.
+ */
+int cf_section_parse(const CONF_SECTION *cs, void *base,
+ const CONF_PARSER *variables)
+{
+ int i;
+ void *data;
+
+ /*
+ * Handle the known configuration parameters.
+ */
+ for (i = 0; variables[i].name != NULL; i++) {
+ /*
+ * Handle subsections specially
+ */
+ if (variables[i].type == PW_TYPE_SUBSECTION) {
+ const CONF_SECTION *subcs;
+ subcs = 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 (!subcs) continue;
+
+ if (!variables[i].dflt) {
+ DEBUG2("Internal sanity check 1 failed in cf_section_parse");
return -1;
}
- DEBUG2(" %s: %s = %s IP address [%s]",
- cs->name1,
- variables[i].name,
- value, ip_ntoa(buffer, ipaddr));
- *(uint32_t *) data = ipaddr;
- break;
- default:
- radlog(L_ERR, "type %d not supported yet", variables[i].type);
+ if (cf_section_parse(subcs, base,
+ (const CONF_PARSER *) variables[i].dflt) < 0) {
+ return -1;
+ }
+ continue;
+ } /* else it's a CONF_PAIR */
+
+ if (variables[i].data) {
+ data = variables[i].data; /* prefer this. */
+ } else if (base) {
+ data = ((char *)base) + variables[i].offset;
+ } else {
+ DEBUG2("Internal sanity check 2 failed in cf_section_parse");
return -1;
- break;
- } /* switch over variable type */
+ }
+
+ /*
+ * Parse the pair we found, or a default value.
+ */
+ if (cf_item_parse(cs, variables[i].name, variables[i].type,
+ data, variables[i].dflt) < 0) {
+ return -1;
+ }
} /* for all variables in the configuration section */
-
+
return 0;
}
+
+/*
+ * Free strings we've parsed into data structures.
+ */
+void cf_section_parse_free_strings(void *base, const CONF_PARSER *variables)
+{
+ int i;
+
+ if (!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) &&
+ (variables[i].type != PW_TYPE_FILENAME)) {
+ continue;
+ }
+
+ /*
+ * Prefer the data, if it's there.
+ * Else use the base + offset.
+ */
+ if (variables[i].data) {
+ p = (char **) &(variables[i].data);
+ } else {
+ p = (char **) (((char *)base) + variables[i].offset);
+ }
+ free(*p);
+ *p = NULL;
+ }
+}
+
+
+/*
+ * Used in a few places, so in one function for clarity.
+ */
+static void cf_fixup_children(CONF_SECTION *cs, CONF_SECTION *is)
+{
+ /*
+ * Add the included conf
+ * to our CONF_SECTION
+ */
+ if (is->children != NULL) {
+ CONF_ITEM *ci;
+
+ /*
+ * Re-write the parent of the
+ * moved children to be the
+ * upper-layer section.
+ */
+ for (ci = is->children; ci; ci = ci->next) {
+ ci->parent = cs;
+ }
+
+ /*
+ * If there are children, then
+ * move them up a layer.
+ */
+ if (is->children) {
+ cf_item_add(cs, is->children);
+ }
+ is->children = NULL;
+ }
+ /*
+ * Always free the section for the
+ * $INCLUDEd file.
+ */
+ cf_section_free(&is);
+}
+
+
/*
* Read a part of the config file.
*/
static CONF_SECTION *cf_section_read(const char *cf, int *lineno, FILE *fp,
- const char *name1, const char *name2,
- CONF_SECTION *parent)
+ const char *name1, const char *name2,
+ CONF_SECTION *parent)
{
CONF_SECTION *cs, *css;
CONF_PAIR *cpn;
char *ptr;
const char *value;
char buf[8192];
- char buf1[1024];
- char buf2[1024];
- char buf3[1024];
+ char buf1[8192];
+ char buf2[8192];
+ char buf3[8192];
int t1, t2, t3;
-
+ char *cbuf = buf;
+ int len;
+
/*
* Ensure that the user can't add CONF_SECTIONs
* with 'internal' names;
cs->item.lineno = *lineno;
/*
- * Read.
+ * Read, checking for line continuations ('\\' at EOL)
*/
- while (fgets(buf, sizeof(buf), fp) != NULL) {
+ for (;;) {
+ int eof;
+
+ /*
+ * Get data, and remember if we are at EOF.
+ */
+ eof = (fgets(cbuf, sizeof(buf) - (cbuf - buf), fp) == NULL);
(*lineno)++;
- ptr = buf;
+ len = strlen(cbuf);
+
+ /*
+ * We've filled the buffer, and there isn't
+ * a CR in it. Die!
+ */
+ if ((len == sizeof(buf)) &&
+ (cbuf[len - 1] != '\n')) {
+ radlog(L_ERR, "%s[%d]: Line too long",
+ cf, *lineno);
+ cf_section_free(&cs);
+ return NULL;
+ }
+
+ /*
+ * Check for continuations.
+ */
+ if (cbuf[len - 1] == '\n') len--;
+
+ /*
+ * Last character is '\\'. Over-write it,
+ * and read another line.
+ */
+ if ((len > 0) && (cbuf[len - 1] == '\\')) {
+ cbuf[len - 1] = '\0';
+ cbuf += len - 1;
+ continue;
+ }
+
+ /*
+ * We're at EOF, and haven't read anything. Stop.
+ */
+ if (eof && (cbuf == buf)) {
+ break;
+ }
+
+ ptr = cbuf = buf;
t1 = gettoken(&ptr, buf1, sizeof(buf1));
/*
/*
* Allow for $INCLUDE files
*
- * This *SHOULD* work for any level include.
+ * This *SHOULD* work for any level include.
* I really really really hate this file. -cparker
*/
if (strcasecmp(buf1, "$INCLUDE") == 0) {
-
- CONF_SECTION *is;
+ CONF_SECTION *is;
t2 = getword(&ptr, buf2, sizeof(buf2));
return NULL;
}
- DEBUG2( "Config: including file: %s", value );
-
- if ((is = conf_read(cf, *lineno, value, parent)) == NULL) {
- cf_section_free(&cs);
- return NULL;
- }
-
+#ifdef HAVE_DIRENT_H
/*
- * Add the included conf to our CONF_SECTION
+ * $INCLUDE foo/
+ *
+ * Include ALL non-"dot" files in the directory.
+ * careful!
*/
- if (is != NULL) {
- if (is->children != NULL) {
- CONF_ITEM *ci;
-
- /*
- * Re-write the parent of the
- * moved children to be the
- * upper-layer section.
- */
- for (ci = is->children; ci; ci = ci->next) {
- ci->parent = cs;
- }
+ if (value[strlen(value) - 1] == '/') {
+ DIR *dir;
+ struct dirent *dp;
+ struct stat stat_buf;
+
+ DEBUG2( "Config: including files in directory: %s", value );
+ dir = opendir(value);
+ if (!dir) {
+ radlog(L_ERR, "%s[%d]: Error reading directory %s: %s",
+ cf, *lineno, value,
+ strerror(errno));
+ cf_section_free(&cs);
+ return NULL;
+ }
+
+ /*
+ * Read the directory, ignoring "." files.
+ */
+ while ((dp = readdir(dir)) != NULL) {
+ const char *p;
+
+ if (dp->d_name[0] == '.') continue;
/*
- * If there are children, then
- * move them up a layer.
+ * Check for valid characters
*/
- if (is->children) {
- cf_item_add(cs, is->children);
+ for (p = dp->d_name; *p != '\0'; p++) {
+ if (isalpha((int)*p) ||
+ isdigit((int)*p) ||
+ (*p == '_') ||
+ (*p == '.')) continue;
+ break;
}
- is->children = NULL;
+ if (*p != '\0') continue;
+
+ snprintf(buf2, sizeof(buf2), "%s%s",
+ value, dp->d_name);
+ if ((stat(buf2, &stat_buf) != 0) ||
+ S_ISDIR(stat_buf.st_mode)) continue;
+ if ((is = conf_read(cf, *lineno, buf2, cs)) == NULL) {
+ closedir(dir);
+ cf_section_free(&cs);
+ return NULL;
+ }
+
+ cf_fixup_children(cs, is);
}
- /*
- * Always free the section for the
- * $INCLUDEd file.
- */
- cf_section_free(&is);
+ closedir(dir);
+ } else
+#endif
+ { /* it was a normal file */
+ DEBUG2( "Config: including file: %s", value );
+ if ((is = conf_read(cf, *lineno, value, cs)) == NULL) {
+ cf_section_free(&cs);
+ return NULL;
+ }
+ cf_fixup_children(cs, is);
}
-
continue;
- }
+ } /* we were in an include */
/*
* No '=': must be a section or sub-section.
*/
if (t2 == T_LCBRACE || t3 == T_LCBRACE) {
css = cf_section_read(cf, lineno, fp, buf1,
- t2==T_LCBRACE ? NULL : buf2, cs);
+ t2==T_LCBRACE ? NULL : buf2, cs);
if (css == NULL) {
cf_section_free(&cs);
return NULL;
/*
* Ignore semi-colons.
*/
- if (*buf2 == ';')
+ if (*buf2 == ';')
*buf2 = '\0';
/*
*/
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)) {
+ } 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",
cf, *lineno);
cf_section_free(&cs);
cf_section_free(&cs);
return NULL;
}
-
+
/*
* Handle variable substitution via ${foo}
*/
/*
* Read the config file.
*/
-static CONF_SECTION *conf_read(const char *fromfile, int fromline,
- const char *conffile, CONF_SECTION *parent)
+CONF_SECTION *conf_read(const char *fromfile, int fromline,
+ const char *conffile, CONF_SECTION *parent)
{
FILE *fp;
int lineno = 0;
CONF_SECTION *cs;
-
+
if ((fp = fopen(conffile, "r")) == NULL) {
if (fromfile) {
radlog(L_ERR|L_CONS, "%s[%d]: Unable to open file \"%s\": %s",
return NULL;
}
- if(parent) {
- cs = cf_section_read(conffile, &lineno, fp, NULL, NULL, parent);
- } else {
- cs = cf_section_read(conffile, &lineno, fp, NULL, NULL, NULL);
- }
+ cs = cf_section_read(conffile, &lineno, fp, NULL, NULL, parent);
fclose(fp);
return cs;
}
+
/*
- * These are not used anywhere else..
+ * Return a CONF_PAIR within a CONF_SECTION.
*/
-static const char *localstatedir = NULL;
-static const char *prefix = NULL;
-
-static CONF_PARSER directory_config[] = {
- /*
- * FIXME: 'prefix' is the ONLY one which should be configured
- * at compile time. Hard-coding it here is bad. It will be cleaned
- * up once we clean up the hard-coded defines for the locations of
- * the various files.
- */
- { "prefix", PW_TYPE_STRING_PTR, 0, &prefix, "/usr/local"},
- { "localstatedir", PW_TYPE_STRING_PTR, 0, &localstatedir, "${prefix}/var"},
- { "logdir", PW_TYPE_STRING_PTR, 0, &radlog_dir, "${localstatedir}/log"},
- { "libdir", PW_TYPE_STRING_PTR, 0, &radlib_dir, "${prefix}/lib"},
- { "radacctdir", PW_TYPE_STRING_PTR, 0, &radacct_dir, "${logdir}/radacct" },
- { "hostname_lookups", PW_TYPE_BOOLEAN, 0, &librad_dodns, "no" },
-
- /*
- * We don't allow re-defining this, as doing so will cause
- * all sorts of confusion.
- */
-#if 0
- { "confdir", PW_TYPE_STRING_PTR, 0, &radius_dir, RADIUS_DIR },
-#endif
- { NULL, -1, 0, NULL, NULL }
-};
-
-
-int read_radius_conf_file(void)
+CONF_PAIR *cf_pair_find(const CONF_SECTION *cs, const char *name)
{
- char buffer[256];
- CONF_SECTION *cs;
-
- /* Lets go look for the new configuration files */
- snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_CONFIG);
- if ((cs = conf_read(NULL, 0, buffer, NULL)) == NULL) {
- return -1;
- }
-
- /*
- * Free the old configuration data, and replace it
- * with the new one.
- */
- cf_section_free(&config);
- config = cs;
-
- /*
- * And parse the directory configuration values.
- */
- cs = cf_section_find(NULL);
- if (cs == NULL)
- return -1;
-
- /*
- * This allows us to figure out where, relative to
- * radiusd.conf, the other configuration files exist.
- */
- cf_section_parse(cs, NULL, directory_config);
-
- /* Initialize the dictionary */
- DEBUG2("read_config_files: reading dictionary");
- if (dict_init(radius_dir, RADIUS_DICTIONARY) != 0) {
- radlog(L_ERR|L_CONS, "Errors reading dictionary: %s",
- librad_errstr);
- return -1;
- }
+ CONF_ITEM *ci;
- /* old-style clients file */
- snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_CLIENTS);
- DEBUG2("read_config_files: reading clients");
- if (read_clients_file(buffer) < 0) {
- radlog(L_ERR|L_CONS, "Errors reading clients");
- return -1;
- }
+ if (!cs) cs = mainconfig.config;
/*
- * Add to that, the *new* list of clients.
+ * Find the name in the tree, for speed.
*/
- snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_CONFIG);
- if (generate_clients(buffer) < 0) {
- return -1;
- }
+ if (name) {
+ CONF_PAIR mycp;
- /* old-style realms file */
- snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_REALMS);
- DEBUG2("read_config_files: reading realms");
- if (read_realms_file(buffer) < 0) {
- radlog(L_ERR|L_CONS, "Errors reading realms");
- return -1;
+ mycp.attr = name;
+ return rbtree_finddata(cs->pair_tree, &mycp);
}
/*
- * If there isn't any realms it isn't fatal..
+ * Else find the first one
*/
- snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_CONFIG);
- if (generate_realms(buffer) < 0) {
- return -1;
- }
-
- /* old-style naslist file */
- snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_NASLIST);
- DEBUG2("read_config_files: reading naslist");
- if (read_naslist_file(buffer) < 0) {
- radlog(L_ERR|L_CONS, "Errors reading naslist");
- return -1;
- }
-
- return 0;
-}
-
-/* JLN
- * Create the linked list of realms from the new configuration type
- * This way we don't have to change to much in the other source-files
- */
-
-static int generate_realms(const char *filename)
-{
- CONF_SECTION *cs;
- REALM *c;
- char *s, *authhost, *accthost;
-
- for (cs = cf_subsection_find_next(config, NULL, "realm"); cs != NULL;
- cs = cf_subsection_find_next(config, cs, "realm")) {
- if (!cs->name2) {
- radlog(L_CONS|L_ERR, "%s[%d]: Missing realm name", filename, cs->item.lineno);
- return -1;
- }
- /*
- * We've found a realm, allocate space for it
- */
- c = rad_malloc(sizeof(REALM));
- memset(c, 0, sizeof(REALM));
- /*
- * An authhost must exist in the configuration
- */
- if ((authhost = cf_section_value_find(cs, "authhost")) == NULL) {
- radlog(L_CONS|L_ERR,
- "%s[%d]: No authhost entry for realm: %s",
- filename, cs->item.lineno, cs->name2);
- return -1;
- }
- if ((s = strchr(authhost, ':')) != NULL) {
- *s++ = 0;
- c->auth_port = atoi(s);
- } else {
- c->auth_port = auth_port;
- }
- if ((accthost = cf_section_value_find(cs, "accthost")) == NULL) {
- radlog(L_CONS|L_ERR,
- "%s[%d]: No accthost entry for realm: %s",
- filename, cs->item.lineno, cs->name2);
- return -1;
- }
-
- if ((s =strchr(accthost, ':')) != NULL) {
- *s++ = 0;
- c->acct_port = atoi(s);
- } else {
- c->acct_port = acct_port;
- }
- if (strcmp(authhost, "LOCAL") != 0)
- c->ipaddr = ip_getaddr(authhost);
-
- /*
- * Double check length, just to be sure!
- */
- if (strlen(authhost) >= sizeof(c->server)) {
- radlog(L_ERR, "%s[%d]: Server name of length %d is greater that allowed: %d",
- filename, cs->item.lineno,
- strlen(authhost), sizeof(c->server) - 1);
- return -1;
- }
- if (strlen(cs->name2) >= sizeof(c->realm)) {
- radlog(L_ERR, "%s[%d]: Realm name of length %d is greater than allowed %d",
- filename, cs->item.lineno,
- strlen(cs->name2), sizeof(c->server) - 1);
- return -1;
- }
-
- strcpy(c->realm, cs->name2);
- strcpy(c->server, authhost);
-
- if ((s = cf_section_value_find(cs, "secret")) == NULL ) {
- radlog(L_ERR, "%s[%d]: No shared secret supplied for realm: %s",
- filename, cs->item.lineno, cs->name2);
- return -1;
- }
-
- if (strlen(s) >= sizeof(c->secret)) {
- radlog(L_ERR, "%s[%d]: Secret of length %d is greater than the allowed maximum of %d.",
- filename, cs->item.lineno,
- strlen(s), sizeof(c->secret) - 1);
- return -1;
- }
- strNcpy((char *)c->secret, s, sizeof(c->secret));
-
- c->striprealm = 1;
-
- if ((cf_section_value_find(cs, "nostrip")) != NULL)
- c->striprealm = 0;
- if ((cf_section_value_find(cs, "noacct")) != NULL)
- c->acct_port = 0;
- if ((cf_section_value_find(cs, "trusted")) != NULL)
- c->trusted = 1;
- if ((cf_section_value_find(cs, "notrealm")) != NULL)
- c->notrealm = 1;
- if ((cf_section_value_find(cs, "notsuffix")) != NULL)
- c->notrealm = 1;
-
-
- c->next = realms;
- realms = c;
-
- }
-
- return 0;
-}
-
-/* JLN
- * Create the linked list of realms from the new configuration type
- * This way we don't have to change to much in the other source-files
- */
-static int generate_clients(const char *filename)
-{
- CONF_SECTION *cs;
- RADCLIENT *c;
- char *hostnm, *secret, *shortnm, *netmask;
-
- for (cs = cf_subsection_find_next(config, NULL, "client"); cs != NULL;
- cs = cf_subsection_find_next(config, cs, "client")) {
- if (!cs->name2) {
- radlog(L_CONS|L_ERR, "%s[%d]: Missing client name", filename, cs->item.lineno);
- return -1;
- }
- /*
- * Check the lengths, we don't want any core dumps
- */
- hostnm = cs->name2;
-
- if((secret = cf_section_value_find(cs, "secret")) == NULL) {
- radlog(L_ERR, "%s[%d]: Missing secret for client: %s",
- filename, cs->item.lineno, cs->name2);
- return -1;
- }
-
- if((shortnm = cf_section_value_find(cs, "shortname")) == NULL) {
- radlog(L_ERR, "%s[%d]: Missing shortname for client: %s",
- filename, cs->item.lineno, cs->name2);
- return -1;
- }
-
- netmask = strchr(hostnm, '/');
-
- if (strlen(secret) >= sizeof(c->secret)) {
- radlog(L_ERR, "%s[%d]: Secret of length %d is greater than the allowed maximum of %d.",
- filename, cs->item.lineno,
- strlen(secret), sizeof(c->secret) - 1);
- return -1;
- }
- if (strlen(shortnm) > sizeof(c->shortname)) {
- radlog(L_ERR, "%s[%d]: Client short name of length %d is greater than the allowed maximum of %d.",
- filename, cs->item.lineno,
- strlen(shortnm), sizeof(c->shortname) - 1);
- return -1;
- }
- /*
- * The size is fine.. Let's create the buffer
- */
- c = rad_malloc(sizeof(RADCLIENT));
-
- /*
- * Look for netmasks.
- */
- c->netmask = ~0;
- if (netmask) {
- int i, mask_length;
-
- mask_length = atoi(netmask + 1);
- if ((mask_length <= 0) || (mask_length > 32)) {
- radlog(L_ERR, "%s[%d]: Invalid value '%s' for IP network mask.",
- filename, cs->item.lineno, netmask + 1);
- return -1;
- }
-
- c->netmask = (1 << 31);
- for (i = 1; i < mask_length; i++) {
- c->netmask |= (c->netmask >> 1);
- }
-
- *netmask = '\0';
- c->netmask = htonl(c->netmask);
- }
-
- c->ipaddr = ip_getaddr(hostnm);
- if (c->ipaddr == INADDR_NONE) {
- radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
- filename, cs->item.lineno, hostnm);
- return -1;
- }
-
- /*
- * Update the client name again...
- */
- if (netmask) {
- *netmask = '/';
- c->ipaddr &= c->netmask;
- strcpy(c->longname, hostnm);
- } else {
- ip_hostname(c->longname, sizeof(c->longname),
- c->ipaddr);
- }
-
- strcpy((char *)c->secret, secret);
- strcpy(c->shortname, shortnm);
-
- c->next = clients;
- clients = c;
- }
-
- return 0;
-}
-
-/*
- * Return a CONF_PAIR within a CONF_SECTION.
- */
-
-CONF_PAIR *cf_pair_find(CONF_SECTION *section, const char *name)
-{
- CONF_ITEM *ci;
-
- if (section == NULL) {
- section = config;
- }
-
- for (ci = section->children; ci; ci = ci->next) {
- if (ci->type != CONF_ITEM_PAIR)
- continue;
- if (name == NULL || strcmp(cf_itemtopair(ci)->attr, name) == 0)
- break;
+ for (ci = cs->children; ci; ci = ci->next) {
+ if (ci->type == CONF_ITEM_PAIR)
+ return cf_itemtopair(ci);
}
-
- return cf_itemtopair(ci);
+
+ return NULL;
}
/*
* Return the first label of a CONF_SECTION
*/
-char *cf_section_name1(CONF_SECTION *section)
+const char *cf_section_name1(const CONF_SECTION *cs)
{
- return (section ? section->name1 : NULL);
+ return (cs ? cs->name1 : NULL);
}
/*
* Return the second label of a CONF_SECTION
*/
-char *cf_section_name2(CONF_SECTION *section)
+const char *cf_section_name2(const CONF_SECTION *cs)
{
- return (section ? section->name2 : NULL);
+ return (cs ? cs->name2 : NULL);
}
-/*
+/*
* Find a value in a CONF_SECTION
*/
-char *cf_section_value_find(CONF_SECTION *section, const char *attr)
+char *cf_section_value_find(const CONF_SECTION *cs, const char *attr)
{
CONF_PAIR *cp;
- cp = cf_pair_find(section, attr);
+ cp = cf_pair_find(cs, attr);
return (cp ? cp->value : NULL);
}
* attr is NULL, any attr matches.
*/
-CONF_PAIR *cf_pair_find_next(CONF_SECTION *section, CONF_PAIR *pair, const char *attr)
+CONF_PAIR *cf_pair_find_next(const CONF_SECTION *cs,
+ const CONF_PAIR *pair, const char *attr)
{
CONF_ITEM *ci;
*/
if (pair == NULL){
- return cf_pair_find(section, attr);
+ return cf_pair_find(cs, attr);
}
ci = cf_pairtoitem(pair)->next;
CONF_SECTION *cf_section_find(const char *name)
{
if (name)
- return cf_section_sub_find(config, name);
+ return cf_section_sub_find(mainconfig.config, name);
else
- return config;
+ return mainconfig.config;
}
/*
* Find a sub-section in a section
*/
-CONF_SECTION *cf_section_sub_find(CONF_SECTION *section, const char *name)
+CONF_SECTION *cf_section_sub_find(const CONF_SECTION *cs, const char *name)
{
CONF_ITEM *ci;
- for (ci = section->children; ci; ci = ci->next) {
+ /*
+ * Do the fast lookup if possible.
+ */
+ if (name && cs->section_tree) {
+ CONF_SECTION mycs;
+
+ mycs.name1 = name;
+ mycs.name2 = NULL;
+ return rbtree_finddata(cs->section_tree, &mycs);
+ }
+
+ for (ci = cs->children; ci; ci = ci->next) {
if (ci->type != CONF_ITEM_SECTION)
continue;
if (strcmp(cf_itemtosection(ci)->name1, name) == 0)
}
+
+/*
+ * Find a CONF_SECTION with both names.
+ */
+CONF_SECTION *cf_section_sub_find_name2(const CONF_SECTION *cs,
+ const char *name1, const char *name2)
+{
+ 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);
+ }
+ }
+
+ /*
+ * Else do it the old-fashioned way.
+ */
+ for (ci = cs->children; ci; ci = ci->next) {
+ CONF_SECTION *subcs;
+
+ if (ci->type != CONF_ITEM_SECTION)
+ continue;
+
+ subcs = cf_itemtosection(ci);
+ if (!name1) {
+ if (!subcs->name2) {
+ if (strcmp(subcs->name1, name2) == 0) break;
+ } else {
+ if (strcmp(subcs->name2, name2) == 0) break;
+ }
+ continue; /* don't do the string comparisons below */
+ }
+
+ if ((strcmp(subcs->name1, name1) == 0) &&
+ (subcs->name2 != NULL) &&
+ (strcmp(subcs->name2, name2) == 0))
+ break;
+ }
+
+ return cf_itemtosection(ci);
+}
+
/*
* Return the next subsection after a CONF_SECTION
* with a certain name1 (char *name1). If the requested
*/
CONF_SECTION *cf_subsection_find_next(CONF_SECTION *section,
- CONF_SECTION *subsection,
- const char *name1)
+ CONF_SECTION *subsection,
+ const char *name1)
{
CONF_ITEM *ci;
for (; ci; ci = ci->next) {
if (ci->type != CONF_ITEM_SECTION)
continue;
- if ((name1 == NULL) ||
- (strcmp(cf_itemtosection(ci)->name1, name1) == 0))
+ if ((name1 == NULL) ||
+ (strcmp(cf_itemtosection(ci)->name1, name1) == 0))
break;
}
}
-/*
+static CONF_DATA *cf_data_alloc(CONF_SECTION *parent, const char *name,
+ void *data, void (*data_free)(void *))
+{
+ CONF_DATA *cd;
+
+ cd = rad_malloc(sizeof(*cd));
+ 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;
+
+ return cd;
+}
+
+
+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;
+
+ mycd.name = name;
+ mycd.flag = flag;
+ cd = rbtree_finddata(cs->data_tree, &mycd);
+ if (cd) return cd->data;
+ }
+
+ return NULL;
+}
+
+/*
+ * Find data from a particular section.
+ */
+void *cf_data_find(CONF_SECTION *cs, const char *name)
+{
+ return cf_data_find_internal(cs, name, 0);
+}
+
+
+/*
+ * Add named data to a configuration section.
+ */
+static int cf_data_add_internal(CONF_SECTION *cs, const char *name,
+ void *data, void (*data_free)(void *),
+ int flag)
+{
+ CONF_DATA *cd;
+
+ if (!cs || !name) return -1;
+
+ /*
+ * Already exists. Can't add it.
+ */
+ if (cf_data_find_internal(cs, name, flag) != NULL) return -1;
+
+ cd = cf_data_alloc(cs, name, data, data_free);
+ if (!cd) return -1;
+ cd->flag = flag;
+
+ cf_item_add(cs, cf_datatoitem(cd));
+
+ return 0;
+}
+
+/*
+ * Add named data to a configuration section.
+ */
+int cf_data_add(CONF_SECTION *cs, const char *name,
+ void *data, void (*data_free)(void *))
+{
+ return cf_data_add_internal(cs, name, data, data_free, 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;
+
+ /*
+ * Don't check if s->data_tree is NULL. It's child
+ * sections may have data, even if this section doesn't.
+ */
+
+ 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) {
+ cf_section_copy_data(s1, d1);
+ }
+ last = &(cd->next);
+ continue;
+ }
+
+ /*
+ * Not conf data, remember last ptr.
+ */
+ if (cd->type != CONF_ITEM_DATA) {
+ last = &(cd->next);
+ continue;
+ }
+
+ /*
+ * Remove it from the src list
+ */
+ *last = cd->next;
+ cd->next = NULL;
+
+ /*
+ * Add it to the dst list
+ */
+ if (!d->children) {
+ rad_assert(d->tail == NULL);
+ d->children = cd;
+ } else {
+ rad_assert(d->tail != NULL);
+ d->tail->next = cd;
+ }
+ d->tail = cd;
+ }
+}
+
+/*
+ * For a CONF_DATA element, stat the filename, if necessary.
+ */
+static int filename_stat(void *context, void *data)
+{
+ struct stat buf;
+ CONF_DATA *cd = data;
+
+ context = context; /* -Wunused */
+
+ if (cd->flag != PW_TYPE_FILENAME) return 0;
+
+ if (stat(cd->name, &buf) < 0) return -1;
+
+ if (buf.st_mtime != *(time_t *) cd->data) return -1;
+
+ return 0;
+}
+
+
+/*
+ * Compare two CONF_SECTIONS. The items MUST be in the same
+ * order.
+ */
+static int cf_section_cmp(CONF_SECTION *a, CONF_SECTION *b)
+{
+ CONF_ITEM *ca = a->children;
+ CONF_ITEM *cb = b->children;
+
+ while (1) {
+ CONF_PAIR *pa, *pb;
+
+ /*
+ * Done. Stop.
+ */
+ if (!ca && !cb) break;
+
+ /*
+ * Skip CONF_DATA.
+ */
+ if (ca && ca->type == CONF_ITEM_DATA) {
+ ca = ca->next;
+ continue;
+ }
+ if (cb && cb->type == CONF_ITEM_DATA) {
+ cb = cb->next;
+ continue;
+ }
+
+ /*
+ * One is smaller than the other. Exit.
+ */
+ if (!ca || !cb) return 0;
+
+ if (ca->type != cb->type) return 0;
+
+ /*
+ * Deal with subsections.
+ */
+ if (ca->type == CONF_ITEM_SECTION) {
+ CONF_SECTION *sa = cf_itemtosection(ca);
+ CONF_SECTION *sb = cf_itemtosection(cb);
+
+ if (!cf_section_cmp(sa, sb)) return 0;
+ goto next;
+ }
+
+ rad_assert(ca->type == CONF_ITEM_PAIR);
+
+ pa = cf_itemtopair(ca);
+ pb = cf_itemtopair(cb);
+
+ /*
+ * Different attr and/or value, Exit.
+ */
+ if ((strcmp(pa->attr, pb->attr) != 0) ||
+ (strcmp(pa->value, pb->value) != 0)) return 0;
+
+
+ /*
+ * And go to the next element.
+ */
+ next:
+ ca = ca->next;
+ cb = cb->next;
+ }
+
+ /*
+ * Walk over the CONF_DATA, stat'ing PW_TYPE_FILENAME.
+ */
+ if (a->data_tree &&
+ (rbtree_walk(a->data_tree, InOrder, filename_stat, NULL) != 0)) {
+ return 0;
+ }
+
+ /*
+ * They must be the same, say so.
+ */
+ return 1;
+}
+
+
+
+
+/*
+ * Migrate CONF_DATA from one section to another.
+ */
+int cf_section_migrate(CONF_SECTION *dst, CONF_SECTION *src)
+{
+ CONF_ITEM *ci;
+ CONF_SECTION *s, *d;
+
+ for (ci = src->children; ci != NULL; ci = ci->next) {
+ if (ci->type != CONF_ITEM_SECTION)
+ continue;
+
+ s = cf_itemtosection(ci);
+ d = cf_section_sub_find_name2(dst, s->name1, s->name2);
+
+ if (!d) continue; /* not in new one, don't migrate it */
+
+ /*
+ * A section of the same name is in BOTH src & dst,
+ * compare the CONF_PAIR's. If they're all the same,
+ * then copy the CONF_DATA from one to the other.
+ */
+ if (cf_section_cmp(s, d)) {
+ cf_section_copy_data(s, d);
+ }
+ }
+
+ return 1; /* rcode means anything? */
+}
+
+
+#if 0
+/*
* JMG dump_config tries to dump the config structure in a readable format
- *
+ *
*/
static int dump_config_section(CONF_SECTION *cs, int indent)
* so I had to get creative. --Pac. */
for (ci = cs->children; ci; ci = ci->next) {
- if (ci->type == CONF_ITEM_PAIR) {
+ switch (ci->type) {
+ case CONF_ITEM_PAIR:
cp=cf_itemtopair(ci);
DEBUG("%.*s%s = %s",
indent, "\t\t\t\t\t\t\t\t\t\t\t",
cp->attr, cp->value);
- } else {
+ break;
+
+ case CONF_ITEM_SECTION:
scs=cf_itemtosection(ci);
DEBUG("%.*s%s %s%s{",
indent, "\t\t\t\t\t\t\t\t\t\t\t",
dump_config_section(scs, indent+1);
DEBUG("%.*s}",
indent, "\t\t\t\t\t\t\t\t\t\t\t");
+ break;
+
+ default: /* FIXME: Do more! */
+ break;
}
}
int dump_config(void)
{
- return dump_config_section(config, 0);
+ return dump_config_section(mainconfig.config, 0);
}
+#endif