change minimum pool size to be 2K
[freeradius.git] / src / main / mainconfig.c
index 2d066e2..3046906 100644 (file)
@@ -25,33 +25,25 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
+#include <freeradius-devel/modpriv.h>
 #include <freeradius-devel/rad_assert.h>
 
 #include <sys/stat.h>
-
-#ifdef HAVE_PWD_H
 #include <pwd.h>
-#endif
-
-#ifdef HAVE_GRP_H
 #include <grp.h>
-#endif
 
 #ifdef HAVE_SYSLOG_H
-#      include <syslog.h>
-#endif
-
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
+#  include <syslog.h>
 #endif
 
 #ifdef HAVE_FCNTL_H
-#include <fcntl.h>
+#  include <fcntl.h>
 #endif
 
-struct main_config_t mainconfig;
-char *debug_condition = NULL;
-extern int log_dates_utc;
+main_config_t          main_config;                            //!< Main server configuration.
+extern fr_cond_t       *debug_condition;
+fr_cond_t              *debug_condition = NULL;                        //!< Condition used to mark packets up for checking.
+bool                   event_loop_started = false;             //!< Whether the main event loop has been started yet.
 
 typedef struct cached_config_t {
        struct cached_config_t *next;
@@ -83,68 +75,100 @@ static char const *radlog_dest = NULL;
  */
 static char const      *localstatedir = NULL;
 static char const      *prefix = NULL;
-static char            my_name;
+static char const      *my_name = NULL;
 static char const      *sbindir = NULL;
 static char const      *run_dir = NULL;
-static char            *syslog_facility = NULL;
+static char const      *syslog_facility = NULL;
 static bool            do_colourise = false;
 
 static char const      *radius_dir = NULL;     //!< Path to raddb directory
 
+/**********************************************************************
+ *
+ *     We need to figure out where the logs go, before doing anything
+ *     else.  This is so that the log messages go to the correct
+ *     place.
+ *
+ *     BUT, we want the settings from the command line to over-ride
+ *     the ones in the configuration file.  So, these items are
+ *     parsed ONLY if there is no "-l foo" on the command line.
+ *
+ **********************************************************************/
 
 /*
- *  Security configuration for the server.
+ *     Log destinations
  */
-static const CONF_PARSER security_config[] = {
-       { "max_attributes",  PW_TYPE_INTEGER, 0, &fr_max_attributes, STRINGIFY(0) },
-       { "reject_delay",  PW_TYPE_INTEGER, 0, &mainconfig.reject_delay, STRINGIFY(0) },
-       { "status_server", PW_TYPE_BOOLEAN, 0, &mainconfig.status_server, "no"},
-       { "allow_vulnerable_openssl", PW_TYPE_STRING_PTR, 0, &mainconfig.allow_vulnerable_openssl, "no"},
-       { NULL, -1, 0, NULL, NULL }
+static const CONF_PARSER startup_log_config[] = {
+       { "destination",  FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dest), "files" },
+       { "syslog_facility",  FR_CONF_POINTER(PW_TYPE_STRING, &syslog_facility), STRINGIFY(0) },
+
+       { "localstatedir", FR_CONF_POINTER(PW_TYPE_STRING, &localstatedir), "${prefix}/var"},
+       { "logdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dir), "${localstatedir}/log"},
+       { "file",  FR_CONF_POINTER(PW_TYPE_STRING, &main_config.log_file), "${logdir}/radius.log" },
+       { "requests",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &default_log.file), NULL },
+       CONF_PARSER_TERMINATOR
 };
 
 
 /*
- *     Logging configuration for the server.
+ *     Basic configuration for the server.
  */
-static const CONF_PARSER logdest_config[] = {
-       { "destination",  PW_TYPE_STRING_PTR, 0, &radlog_dest, "files" },
-       { "syslog_facility",  PW_TYPE_STRING_PTR, 0, &syslog_facility, STRINGIFY(0) },
-
-       { "file", PW_TYPE_STRING_PTR, 0, &mainconfig.log_file, "${logdir}/radius.log" },
-       { "requests", PW_TYPE_STRING_PTR, 0, &default_log.file, NULL },
-       { NULL, -1, 0, NULL, NULL }
-};
+static const CONF_PARSER startup_server_config[] = {
+       { "log",  FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) startup_log_config },
 
+       { "name", FR_CONF_POINTER(PW_TYPE_STRING, &my_name), "radiusd"},
+       { "prefix", FR_CONF_POINTER(PW_TYPE_STRING, &prefix), "/usr/local"},
 
-static const CONF_PARSER serverdest_config[] = {
-       { "log", PW_TYPE_SUBSECTION, 0, NULL, (void const *) logdest_config },
-       { "log_file", PW_TYPE_STRING_PTR, 0, &mainconfig.log_file, NULL },
-       { "log_destination", PW_TYPE_STRING_PTR, 0, &radlog_dest, NULL },
-       { "use_utc", PW_TYPE_BOOLEAN, 0, &log_dates_utc, NULL },
-       { NULL, -1, 0, NULL, NULL }
+       { "log_file",  FR_CONF_POINTER(PW_TYPE_STRING, &main_config.log_file), NULL },
+       { "log_destination", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dest), NULL },
+       { "use_utc", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_dates_utc), NULL },
+       CONF_PARSER_TERMINATOR
 };
 
 
-static const CONF_PARSER log_config_nodest[] = {
-       { "stripped_names", PW_TYPE_BOOLEAN, 0, &log_stripped_names,"no" },
-       { "auth", PW_TYPE_BOOLEAN, 0, &mainconfig.log_auth, "no" },
-       { "auth_badpass", PW_TYPE_BOOLEAN, 0, &mainconfig.log_auth_badpass, "no" },
-       { "auth_goodpass", PW_TYPE_BOOLEAN, 0, &mainconfig.log_auth_goodpass, "no" },
-       { "msg_badpass", PW_TYPE_STRING_PTR, 0, &mainconfig.auth_badpass_msg, NULL},
-       { "msg_goodpass", PW_TYPE_STRING_PTR, 0, &mainconfig.auth_goodpass_msg, NULL},
-       { "colourise", PW_TYPE_BOOLEAN, 0, &do_colourise, NULL },
-       { "use_utc", PW_TYPE_BOOLEAN, 0, &log_dates_utc, NULL },
-       { "msg_denied", PW_TYPE_STRING_PTR, 0, &mainconfig.denied_msg,
-         "You are already logged in - access denied" },
-
-       { NULL, -1, 0, NULL, NULL }
+/**********************************************************************
+ *
+ *     Now that we've parsed the log destination, AND the security
+ *     items, we can parse the rest of the configuration items.
+ *
+ **********************************************************************/
+static const CONF_PARSER log_config[] = {
+       { "stripped_names", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_stripped_names),"no" },
+       { "auth", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_auth), "no" },
+       { "auth_badpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_auth_badpass), "no" },
+       { "auth_goodpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_auth_goodpass), "no" },
+       { "msg_badpass", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.auth_badpass_msg), NULL},
+       { "msg_goodpass", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.auth_goodpass_msg), NULL},
+       { "colourise",FR_CONF_POINTER(PW_TYPE_BOOLEAN, &do_colourise), NULL },
+       { "use_utc", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_dates_utc), NULL },
+       { "msg_denied", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.denied_msg), "You are already logged in - access denied" },
+       CONF_PARSER_TERMINATOR
 };
 
 
 /*
- *  A mapping of configuration file names to internal variables
+ *  Security configuration for the server.
  */
+static const CONF_PARSER security_config[] = {
+       { "max_attributes",  FR_CONF_POINTER(PW_TYPE_INTEGER, &fr_max_attributes), STRINGIFY(0) },
+       { "reject_delay",  FR_CONF_POINTER(PW_TYPE_TIMEVAL, &main_config.reject_delay), STRINGIFY(0) },
+       { "status_server", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.status_server), "no"},
+#ifdef ENABLE_OPENSSL_VERSION_CHECK
+       { "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"},
+#endif
+       CONF_PARSER_TERMINATOR
+};
+
+static const CONF_PARSER resources[] = {
+       /*
+        *      Don't set a default here.  It's set in the code, below.  This means that
+        *      the config item will *not* get printed out in debug mode, so that no one knows
+        *      it exists.
+        */
+       { "talloc_pool_size", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.talloc_pool_size), NULL },
+       CONF_PARSER_TERMINATOR
+};
+
 static const CONF_PARSER server_config[] = {
        /*
         *      FIXME: 'prefix' is the ONLY one which should be
@@ -153,31 +177,30 @@ static const CONF_PARSER server_config[] = {
         *      hard-coded defines for the locations of the various
         *      files.
         */
-       { "name",              PW_TYPE_STRING_PTR, 0, &my_name,   "radiusd"},
-       { "prefix",          PW_TYPE_STRING_PTR, 0, &prefix,        "/usr/local"},
-       { "localstatedir",      PW_TYPE_STRING_PTR, 0, &localstatedir,     "${prefix}/var"},
-       { "sbindir",        PW_TYPE_STRING_PTR, 0, &sbindir,        "${prefix}/sbin"},
-       { "logdir",          PW_TYPE_STRING_PTR, 0, &radlog_dir,        "${localstatedir}/log"},
-       { "run_dir",        PW_TYPE_STRING_PTR, 0, &run_dir,       "${localstatedir}/run/${name}"},
-       { "libdir",          PW_TYPE_STRING_PTR, 0, &radlib_dir,        "${prefix}/lib"},
-       { "radacctdir",  PW_TYPE_STRING_PTR, 0, &radacct_dir,       "${logdir}/radacct" },
-       { "panic_action", PW_TYPE_STRING_PTR, 0, &mainconfig.panic_action, NULL},
-       { "hostname_lookups",   PW_TYPE_BOOLEAN,    0, &fr_dns_lookups,      "no" },
-       { "max_request_time", PW_TYPE_INTEGER, 0, &mainconfig.max_request_time, STRINGIFY(MAX_REQUEST_TIME) },
-       { "cleanup_delay", PW_TYPE_INTEGER, 0, &mainconfig.cleanup_delay, STRINGIFY(CLEANUP_DELAY) },
-       { "max_requests", PW_TYPE_INTEGER, 0, &mainconfig.max_requests, STRINGIFY(MAX_REQUESTS) },
-#ifdef DELETE_BLOCKED_REQUESTS
-       { "delete_blocked_requests", PW_TYPE_INTEGER, 0, &mainconfig.kill_unresponsive_children, STRINGIFY(false) },
-#endif
-       { "pidfile", PW_TYPE_STRING_PTR, 0, &mainconfig.pid_file, "${run_dir}/radiusd.pid"},
-       { "checkrad", PW_TYPE_STRING_PTR, 0, &mainconfig.checkrad, "${sbindir}/checkrad" },
-
-       { "debug_level", PW_TYPE_INTEGER, 0, &mainconfig.debug_level, "0"},
+       { "name", FR_CONF_POINTER(PW_TYPE_STRING, &my_name), "radiusd"},
+       { "prefix", FR_CONF_POINTER(PW_TYPE_STRING, &prefix), "/usr/local"},
+       { "localstatedir", FR_CONF_POINTER(PW_TYPE_STRING, &localstatedir), "${prefix}/var"},
+       { "sbindir", FR_CONF_POINTER(PW_TYPE_STRING, &sbindir), "${prefix}/sbin"},
+       { "logdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dir), "${localstatedir}/log"},
+       { "run_dir", FR_CONF_POINTER(PW_TYPE_STRING, &run_dir), "${localstatedir}/run/${name}"},
+       { "libdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlib_dir), "${prefix}/lib"},
+       { "radacctdir", FR_CONF_POINTER(PW_TYPE_STRING, &radacct_dir), "${logdir}/radacct" },
+       { "panic_action", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.panic_action), NULL},
+       { "hostname_lookups", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &fr_dns_lookups), "no" },
+       { "max_request_time", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.max_request_time), STRINGIFY(MAX_REQUEST_TIME) },
+       { "cleanup_delay", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.cleanup_delay), STRINGIFY(CLEANUP_DELAY) },
+       { "max_requests", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.max_requests), STRINGIFY(MAX_REQUESTS) },
+       { "pidfile", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.pid_file), "${run_dir}/radiusd.pid"},
+       { "checkrad", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.checkrad), "${sbindir}/checkrad" },
+
+       { "debug_level", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.debug_level), "0"},
 
 #ifdef WITH_PROXY
-       { "proxy_requests", PW_TYPE_BOOLEAN, 0, &mainconfig.proxy_requests, "yes" },
+       { "proxy_requests", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.proxy_requests), "yes" },
 #endif
-       { "log", PW_TYPE_SUBSECTION, 0, NULL, (void const *) log_config_nodest },
+       { "log", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) log_config },
+
+       { "resources", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) resources },
 
        /*
         *      People with old configs will have these.  They are listed
@@ -187,52 +210,66 @@ static const CONF_PARSER server_config[] = {
         *      DON'T exist in radiusd.conf, then the previously parsed
         *      values for "log { foo = bar}" will be used.
         */
-       { "log_auth", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &mainconfig.log_auth, NULL },
-       { "log_auth_badpass", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &mainconfig.log_auth_badpass, NULL },
-       { "log_auth_goodpass", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &mainconfig.log_auth_goodpass, NULL },
-       { "log_stripped_names", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &log_stripped_names, NULL },
-
-       {  "security", PW_TYPE_SUBSECTION, 0, NULL, (void const *) security_config },
+       { "log_auth", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &main_config.log_auth), NULL },
+       { "log_auth_badpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &main_config.log_auth_badpass), NULL },
+       { "log_auth_goodpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &main_config.log_auth_goodpass), NULL },
+       { "log_stripped_names", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &log_stripped_names), NULL },
 
-       { NULL, -1, 0, NULL, NULL }
+       {  "security", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) security_config },
+       CONF_PARSER_TERMINATOR
 };
 
+
+/**********************************************************************
+ *
+ *     The next few items are here as a "bootstrap" for security.
+ *     They allow the server to switch users, chroot, while still
+ *     opening the various output files with the correct permission.
+ *
+ *     It's rare (or impossible) to have parse errors here, so we
+ *     don't worry too much about that.  In contrast, when we parse
+ *     the rest of the configuration, we CAN get parse errors.  We
+ *     want THOSE parse errors to go to the log file, and we want the
+ *     log file to have the correct permissions.
+ *
+ **********************************************************************/
 static const CONF_PARSER bootstrap_security_config[] = {
 #ifdef HAVE_SETUID
-       { "user",  PW_TYPE_STRING_PTR, 0, &uid_name, NULL },
-       { "group",  PW_TYPE_STRING_PTR, 0, &gid_name, NULL },
+       { "user",  FR_CONF_POINTER(PW_TYPE_STRING, &uid_name), NULL },
+       { "group", FR_CONF_POINTER(PW_TYPE_STRING, &gid_name), NULL },
 #endif
-       { "chroot",  PW_TYPE_STRING_PTR, 0, &chroot_dir, NULL },
-       { "allow_core_dumps", PW_TYPE_BOOLEAN, 0, &allow_core_dumps, "no" },
-
-       { NULL, -1, 0, NULL, NULL }
+       { "chroot",  FR_CONF_POINTER(PW_TYPE_STRING, &chroot_dir), NULL },
+       { "allow_core_dumps", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &allow_core_dumps), "no" },
+       CONF_PARSER_TERMINATOR
 };
 
 static const CONF_PARSER bootstrap_config[] = {
-       {  "security", PW_TYPE_SUBSECTION, 0, NULL, (void const *) bootstrap_security_config },
+       {  "security", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) bootstrap_security_config },
+
+       { "name", FR_CONF_POINTER(PW_TYPE_STRING, &my_name), "radiusd"},
+       { "prefix", FR_CONF_POINTER(PW_TYPE_STRING, &prefix), "/usr/local"},
+       { "localstatedir", FR_CONF_POINTER(PW_TYPE_STRING, &localstatedir), "${prefix}/var"},
+
+       { "logdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dir), "${localstatedir}/log"},
+       { "run_dir", FR_CONF_POINTER(PW_TYPE_STRING, &run_dir), "${localstatedir}/run/${name}"},
 
        /*
         *      For backwards compatibility.
         */
 #ifdef HAVE_SETUID
-       { "user",  PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, 0, &uid_name, NULL },
-       { "group",  PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, 0, &gid_name, NULL },
+       { "user",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &uid_name), NULL },
+       { "group",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &gid_name), NULL },
 #endif
-       { "chroot",  PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, 0, &chroot_dir, NULL },
-       { "allow_core_dumps", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &allow_core_dumps, NULL },
-
-       { NULL, -1, 0, NULL, NULL }
+       { "chroot",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &chroot_dir), NULL },
+       { "allow_core_dumps", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &allow_core_dumps), NULL },
+       CONF_PARSER_TERMINATOR
 };
 
 
-
-#define MAX_ARGV (256)
-
-
 static size_t config_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg)
 {
        size_t len = 0;
-       static char const *disallowed = "%{}\\'\"`";
+       static char const disallowed[] = "%{}\\'\"`";
 
        while (in[0]) {
                /*
@@ -306,7 +343,7 @@ static ssize_t xlat_config(UNUSED void *instance, REQUEST *request, char const *
                return -1;
        }
 
-       cp = cf_itemtopair(ci);
+       cp = cf_item_to_pair(ci);
 
        /*
         *  Ensure that we only copy what's necessary.
@@ -339,7 +376,7 @@ static ssize_t xlat_client(UNUSED void *instance, REQUEST *request, char const *
 
        if (!fmt || !out || (outlen < 1)) return 0;
 
-       if (!request || !request->client) {
+       if (!request->client) {
                RWDEBUG("No client associated with this request");
                *out = '\0';
                return 0;
@@ -351,7 +388,6 @@ static ssize_t xlat_client(UNUSED void *instance, REQUEST *request, char const *
                        strlcpy(out, request->client->shortname, outlen);
                        return strlen(out);
                }
-               RDEBUG("Client does not contain config item \"%s\"", fmt);
                *out = '\0';
                return 0;
        }
@@ -382,7 +418,7 @@ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char cons
        }
 
        strlcpy(buffer, p, (q + 1) - p);
-       if (ip_ptonx(buffer, &ip) <= 0) {
+       if (fr_pton(&ip, buffer, -1, AF_UNSPEC, false) < 0) {
                REDEBUG("\"%s\" is not a valid IPv4 or IPv6 address", buffer);
                goto error;
        }
@@ -402,7 +438,6 @@ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char cons
                        strlcpy(out, request->client->shortname, outlen);
                        return strlen(out);
                }
-               RDEBUG("Client does not contain config item \"%s\"", fmt);
                *out = '\0';
                return 0;
        }
@@ -415,108 +450,36 @@ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char cons
        return -1;
 }
 
-#ifdef HAVE_SETUID
-static bool doing_setuid = false;
-
-#  if defined(HAVE_SETRESUID) && defined (HAVE_GETRESUID)
-void fr_suid_up(void)
-{
-       uid_t ruid, euid, suid;
-
-       if (getresuid(&ruid, &euid, &suid) < 0) {
-               ERROR("Failed getting saved UID's");
-               fr_exit_now(1);
-       }
-
-       if (setresuid(-1, suid, -1) < 0) {
-               ERROR("Failed switching to privileged user");
-               fr_exit_now(1);
-       }
-
-       if (geteuid() != suid) {
-               ERROR("Switched to unknown UID");
-               fr_exit_now(1);
-       }
-}
-
-void fr_suid_down(void)
-{
-       if (!doing_setuid) return;
-
-       if (setresuid(-1, server_uid, geteuid()) < 0) {
-               fprintf(stderr, "%s: Failed switching to uid %s: %s\n",
-                       progname, uid_name, fr_syserror(errno));
-               fr_exit_now(1);
-       }
-
-       if (geteuid() != server_uid) {
-               fprintf(stderr, "%s: Failed switching uid: UID is incorrect\n",
-                       progname);
-               fr_exit_now(1);
-       }
-
-       fr_set_dumpable(allow_core_dumps);
-}
-
-void fr_suid_down_permanent(void)
-{
-       if (!doing_setuid) return;
-
-       if (setresuid(server_uid, server_uid, server_uid) < 0) {
-               ERROR("Failed in permanent switch to uid %s: %s",
-                      uid_name, fr_syserror(errno));
-               fr_exit_now(1);
-       }
-
-       if (geteuid() != server_uid) {
-               ERROR("Switched to unknown uid");
-               fr_exit_now(1);
-       }
-
-       fr_set_dumpable(allow_core_dumps);
-}
-#  else
 /*
- *     Much less secure...
+ *     Xlat for %{listen:foo}
  */
-void fr_suid_up(void)
+static ssize_t xlat_listen(UNUSED void *instance, REQUEST *request,
+                          char const *fmt, char *out, size_t outlen)
 {
-}
+       char const *value = NULL;
+       CONF_PAIR *cp;
 
-void fr_suid_down(void)
-{
-       if (!uid_name) return;
+       if (!fmt || !out || (outlen < 1)) return 0;
 
-       if (setuid(server_uid) < 0) {
-               fprintf(stderr, "%s: Failed switching to uid %s: %s\n",
-                       progname, uid_name, fr_syserror(errno));
-               fr_exit(1);
+       if (!request->listener) {
+               RWDEBUG("No listener associated with this request");
+               *out = '\0';
+               return 0;
        }
 
-       fr_set_dumpable(allow_core_dumps);
-}
+       cp = cf_pair_find(request->listener->cs, fmt);
+       if (!cp || !(value = cf_pair_value(cp))) {
+               RDEBUG("Listener does not contain config item \"%s\"", fmt);
+               *out = '\0';
+               return 0;
+       }
 
-void fr_suid_down_permanent(void)
-{
-       fr_set_dumpable(allow_core_dumps);
-}
-#  endif /* HAVE_SETRESUID && HAVE_GETRESUID */
-#else  /* HAVE_SETUID */
-void fr_suid_up(void)
-{
-}
-void fr_suid_down(void)
-{
-       fr_set_dumpable(allow_core_dumps);
-}
-void fr_suid_down_permanent(void)
-{
-       fr_set_dumpable(allow_core_dumps);
+       strlcpy(out, value, outlen);
+
+       return strlen(out);
 }
-#endif /* HAVE_SETUID */
 
 #ifdef HAVE_SETUID
-
 /*
  *  Do chroot, if requested.
  *
@@ -524,6 +487,9 @@ void fr_suid_down_permanent(void)
  */
 static int switch_users(CONF_SECTION *cs)
 {
+       bool do_suid = false;
+       bool do_sgid = false;
+
        /*
         *      Get the current maximum for core files.  Do this
         *      before anything else so as to ensure it's properly
@@ -538,61 +504,72 @@ static int switch_users(CONF_SECTION *cs)
         *      Don't do chroot/setuid/setgid if we're in debugging
         *      as non-root.
         */
-       if (debug_flag && (getuid() != 0)) return 1;
+       if (rad_debug_lvl && (getuid() != 0)) return 1;
 
        if (cf_section_parse(cs, NULL, bootstrap_config) < 0) {
                fprintf(stderr, "radiusd: Error: Failed to parse user/group information.\n");
                return 0;
        }
 
-
 #ifdef HAVE_GRP_H
-       /*  Set GID.  */
+       /*
+        *      Get the correct GID for the server.
+        */
+       server_gid = getgid();
+
        if (gid_name) {
                struct group *gr;
 
                gr = getgrnam(gid_name);
-               if (gr == NULL) {
+               if (!gr) {
                        fprintf(stderr, "%s: Cannot get ID for group %s: %s\n",
                                progname, gid_name, fr_syserror(errno));
                        return 0;
                }
-               server_gid = gr->gr_gid;
-       } else {
-               server_gid = getgid();
+
+               if (server_gid != gr->gr_gid) {
+                       server_gid = gr->gr_gid;
+                       do_sgid = true;
+               }
        }
 #endif
 
-#ifdef HAVE_PWD_H
-       /*  Set UID.  */
+       /*
+        *      Get the correct UID for the server.
+        */
+       server_uid = getuid();
+
        if (uid_name) {
-               struct passwd *pw;
+               struct passwd *user;
 
-               pw = getpwnam(uid_name);
-               if (pw == NULL) {
+               if (rad_getpwnam(cs, &user, uid_name) < 0) {
                        fprintf(stderr, "%s: Cannot get passwd entry for user %s: %s\n",
-                               progname, uid_name, fr_syserror(errno));
+                               progname, uid_name, fr_strerror());
                        return 0;
                }
 
-               if (getuid() == pw->pw_uid) {
-                       uid_name = NULL;
-               } else {
-
-                       server_uid = pw->pw_uid;
+               /*
+                *      We're not the correct user.  Go set that.
+                */
+               if (server_uid != user->pw_uid) {
+                       server_uid = user->pw_uid;
+                       do_suid = true;
 #ifdef HAVE_INITGROUPS
                        if (initgroups(uid_name, server_gid) < 0) {
                                fprintf(stderr, "%s: Cannot initialize supplementary group list for user %s: %s\n",
                                        progname, uid_name, fr_syserror(errno));
+                               talloc_free(user);
                                return 0;
                        }
 #endif
                }
-       } else {
-               server_uid = getuid();
+
+               talloc_free(user);
        }
-#endif
 
+       /*
+        *      Do chroot BEFORE changing UIDs.
+        */
        if (chroot_dir) {
                if (chroot(chroot_dir) < 0) {
                        fprintf(stderr, "%s: Failed to perform chroot %s: %s",
@@ -618,46 +595,89 @@ static int switch_users(CONF_SECTION *cs)
        }
 
 #ifdef HAVE_GRP_H
-       /*  Set GID.  */
-       if (gid_name && (setgid(server_gid) < 0)) {
-               fprintf(stderr, "%s: Failed setting group to %s: %s",
-                       progname, gid_name, fr_syserror(errno));
-               return 0;
+       /*
+        *      Set the GID.  Don't bother checking it.
+        */
+       if (do_sgid) {
+               if (setgid(server_gid) < 0){
+                       fprintf(stderr, "%s: Failed setting group to %s: %s",
+                               progname, gid_name, fr_syserror(errno));
+                       return 0;
+               }
        }
 #endif
 
-#ifdef HAVE_SETUID
        /*
-        *      Just before losing root permissions, ensure that the
-        *      log files have the correct owner && group.
+        *      The directories for PID files and logs must exist.  We
+        *      need to create them if we're told to write files to
+        *      those directories.
+        *
+        *      Because this creation is new in 3.0.9, it's a soft
+        *      fail.
         *
-        *      We have to do this because the log file MAY have been
-        *      specified on the command-line.
         */
-       if (uid_name || gid_name) {
-               if ((default_log.dst == L_DST_FILES) &&
-                   (default_log.fd < 0)) {
-                       default_log.fd = open(mainconfig.log_file,
-                                             O_WRONLY | O_APPEND | O_CREAT, 0640);
-                       if (default_log.fd < 0) {
-                               fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, fr_syserror(errno));
-                               return 0;
-                       }
+       if (main_config.write_pid) {
+               char *my_dir;
 
-                       if (chown(mainconfig.log_file, server_uid, server_gid) < 0) {
-                               fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n",
-                                       progname, mainconfig.log_file, fr_syserror(errno));
-                               return 0;
-                       }
+               my_dir = talloc_strdup(NULL, run_dir);
+               if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) {
+                       DEBUG("Failed to create run_dir %s: %s",
+                             my_dir, strerror(errno));
                }
+               talloc_free(my_dir);
        }
 
-       if (uid_name) {
-               doing_setuid = true;
+       if (default_log.dst == L_DST_FILES) {
+               char *my_dir;
 
-               fr_suid_down();
+               my_dir = talloc_strdup(NULL, radlog_dir);
+               if (rad_mkdir(my_dir, 0750, server_uid, server_gid) < 0) {
+                       DEBUG("Failed to create logdir %s: %s",
+                             my_dir, strerror(errno));
+               }
+               talloc_free(my_dir);
+       }
+
+       /*
+        *      Once we're done with all of the privileged work,
+        *      permanently change the UID.
+        */
+       if (do_suid) {
+               rad_suid_set_down_uid(server_uid);
+               rad_suid_down();
+       }
+
+       /*
+        *      If we don't already have a log file open, open one
+        *      now.  We may not have been logging anything yet.  The
+        *      server normally starts up fairly quietly.
+        */
+       if ((default_log.dst == L_DST_FILES) &&
+           (default_log.fd < 0)) {
+               default_log.fd = open(main_config.log_file,
+                                     O_WRONLY | O_APPEND | O_CREAT, 0640);
+               if (default_log.fd < 0) {
+                       fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", main_config.log_file, fr_syserror(errno));
+                       return 0;
+               }
+       }
+
+       /*
+        *      If we need to change UID, ensure that the log files
+        *      have the correct owner && group.
+        *
+        *      We have to do this because some log files MAY already
+        *      have been written as root.  We need to change them to
+        *      have the correct ownership before proceeding.
+        */
+       if ((do_suid || do_sgid) &&
+           (default_log.dst == L_DST_FILES)) {
+               if (fchown(default_log.fd, server_uid, server_gid) < 0) {
+                       fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n",
+                               progname, main_config.log_file, fr_syserror(errno));
+                       return 0;
+               }
        }
-#endif
 
        /*
         *      This also clears the dumpable flag if core dumps
@@ -668,7 +688,7 @@ static int switch_users(CONF_SECTION *cs)
        }
 
        if (allow_core_dumps) {
-               INFO("Core dumps are enabled.");
+               INFO("Core dumps are enabled");
        }
 
        return 1;
@@ -706,10 +726,10 @@ char const *get_radius_dir(void)
  *
  *     This function can ONLY be called from the main server process.
  */
-int mainconfig_init(void)
+int main_config_init(void)
 {
        char const *p = NULL;
-       CONF_SECTION *cs;
+       CONF_SECTION *cs, *subcs;
        struct stat statbuf;
        cached_config_t *cc;
        char buffer[1024];
@@ -728,8 +748,8 @@ int mainconfig_init(void)
        }
 #endif
 
-#ifdef S_IROTH
-       if (0 && (statbuf.st_mode & S_IROTH) != 0) {
+#if 0 && defined(S_IROTH)
+       if (statbuf.st_mode & S_IROTH != 0) {
                ERROR("Configuration directory %s is globally readable.  Refusing to start due to insecure configuration.",
                       radius_dir);
                return -1;
@@ -743,16 +763,23 @@ int mainconfig_init(void)
         *      pre-compilation in conffile.c.  That should probably
         *      be fixed to be done as a second stage.
         */
-       if (!mainconfig.dictionary_dir) {
-               mainconfig.dictionary_dir = DICTDIR;
+       if (!main_config.dictionary_dir) {
+               main_config.dictionary_dir = DICTDIR;
        }
 
        /*
+        *      About sizeof(REQUEST) + sizeof(RADIUS_PACKET) * 2 + sizeof(VALUE_PAIR) * 400
+        *
+        *      Which should be enough for many configurations.
+        */
+       main_config.talloc_pool_size = 8 * 1024; /* default */
+
+       /*
         *      Read the distribution dictionaries first, then
         *      the ones in raddb.
         */
-       DEBUG2("including dictionary file %s/%s", mainconfig.dictionary_dir, RADIUS_DICTIONARY);
-       if (dict_init(mainconfig.dictionary_dir, RADIUS_DICTIONARY) != 0) {
+       DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY);
+       if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) {
                ERROR("Errors reading dictionary: %s",
                      fr_strerror());
                return -1;
@@ -777,11 +804,11 @@ do {\
         *      if they don't exist.
         */
 #ifdef WITH_DHCP
-       DICT_READ_OPTIONAL(mainconfig.dictionary_dir, "dictionary.dhcp");
+       DICT_READ_OPTIONAL(main_config.dictionary_dir, "dictionary.dhcp");
 #endif
 
 #ifdef WITH_VMPS
-       DICT_READ_OPTIONAL(mainconfig.dictionary_dir, "dictionary.vqp");
+       DICT_READ_OPTIONAL(main_config.dictionary_dir, "dictionary.vqp");
 #endif
 
        /*
@@ -789,11 +816,43 @@ do {\
         */
        DICT_READ_OPTIONAL(radius_dir, RADIUS_DICTIONARY);
 
+       cs = cf_section_alloc(NULL, "main", NULL);
+       if (!cs) return -1;
+
+       /*
+        *      Add a 'feature' subsection off the main config
+        *      We check if it's defined first, as the user may
+        *      have defined their own feature flags, or want
+        *      to manually override the ones set by modules
+        *      or the server.
+        */
+       subcs = cf_section_sub_find(cs, "feature");
+       if (!subcs) {
+               subcs = cf_section_alloc(cs, "feature", NULL);
+               if (!subcs) return -1;
+
+               cf_section_add(cs, subcs);
+       }
+       version_init_features(subcs);
+
+       /*
+        *      Add a 'version' subsection off the main config
+        *      We check if it's defined first, this is for
+        *      backwards compatibility.
+        */
+       subcs = cf_section_sub_find(cs, "version");
+       if (!subcs) {
+               subcs = cf_section_alloc(cs, "version", NULL);
+               if (!subcs) return -1;
+               cf_section_add(cs, subcs);
+       }
+       version_init_numbers(subcs);
+
        /* Read the configuration file */
-       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
-                radius_dir, mainconfig.name);
-       if ((cs = cf_file_read(buffer)) == NULL) {
+       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, main_config.name);
+       if (cf_file_read(cs, buffer) < 0) {
                ERROR("Errors reading or parsing %s", buffer);
+               talloc_free(cs);
                return -1;
        }
 
@@ -802,7 +861,7 @@ do {\
         *      set it now.
         */
        if (default_log.dst == L_DST_NULL) {
-               if (cf_section_parse(cs, NULL, serverdest_config) < 0) {
+               if (cf_section_parse(cs, NULL, startup_server_config) < 0) {
                        fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n");
                        cf_file_free(cs);
                        return -1;
@@ -833,8 +892,8 @@ do {\
                                cf_file_free(cs);
                                return -1;
                        }
-                       mainconfig.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1);
-                       if (mainconfig.syslog_facility < 0) {
+                       main_config.syslog_facility = fr_str2int(syslog_facility_table, syslog_facility, -1);
+                       if (main_config.syslog_facility < 0) {
                                fprintf(stderr, "radiusd: Error: Unknown syslog_facility %s\n",
                                        syslog_facility);
                                cf_file_free(cs);
@@ -846,11 +905,11 @@ do {\
                         *      Call openlog only once, when the
                         *      program starts.
                         */
-                       openlog(progname, LOG_PID, mainconfig.syslog_facility);
+                       openlog(progname, LOG_PID, main_config.syslog_facility);
 #endif
 
                } else if (default_log.dst == L_DST_FILES) {
-                       if (!mainconfig.log_file) {
+                       if (!main_config.log_file) {
                                fprintf(stderr, "radiusd: Error: Specified \"files\" as a log destination, but no log filename was given!\n");
                                cf_file_free(cs);
                                return -1;
@@ -866,28 +925,10 @@ do {\
 #endif
 
        /*
-        *      Open the log file AFTER switching uid / gid.  If we
-        *      did switch uid/gid, then the code in switch_users()
-        *      took care of setting the file permissions correctly.
-        */
-       if ((default_log.dst == L_DST_FILES) &&
-           (default_log.fd < 0)) {
-               default_log.fd = open(mainconfig.log_file,
-                                           O_WRONLY | O_APPEND | O_CREAT, 0640);
-               if (default_log.fd < 0) {
-                       fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, fr_syserror(errno));
-                       cf_file_free(cs);
-                       return -1;
-               }
-       }
-
-       /*
         *      This allows us to figure out where, relative to
         *      radiusd.conf, the other configuration files exist.
         */
-       if (cf_section_parse(cs, NULL, server_config) < 0) {
-               return -1;
-       }
+       if (cf_section_parse(cs, NULL, server_config) < 0) return -1;
 
        /*
         *      We ignore colourization of output until after the
@@ -900,9 +941,39 @@ do {\
                default_log.colourise = false;
        }
 
-       if (mainconfig.max_request_time == 0) mainconfig.max_request_time = 100;
-       if (mainconfig.reject_delay > 5) mainconfig.reject_delay = 5;
-       if (mainconfig.cleanup_delay > 5) mainconfig.cleanup_delay =5;
+       /*
+        *      Starting the server, WITHOUT "-x" on the
+        *      command-line: use whatever is in the config
+        *      file.
+        */
+       if (rad_debug_lvl == 0) {
+               rad_debug_lvl = main_config.debug_level;
+       }
+       fr_debug_lvl = rad_debug_lvl;
+
+       FR_INTEGER_COND_CHECK("max_request_time", main_config.max_request_time,
+                             (main_config.max_request_time != 0), 100);
+
+       /*
+        *      reject_delay can be zero.  OR 1 though 10.
+        */
+       if ((main_config.reject_delay.tv_sec != 0) || (main_config.reject_delay.tv_usec != 0)) {
+               FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, >=, 1, 0);
+       }
+       FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, <=, 10, 0);
+
+       FR_INTEGER_BOUND_CHECK("cleanup_delay", main_config.cleanup_delay, <=, 10);
+
+       FR_INTEGER_BOUND_CHECK("resources.talloc_pool_size", main_config.talloc_pool_size, >=, 2 * 1024);
+       FR_INTEGER_BOUND_CHECK("resources.talloc_pool_size", main_config.talloc_pool_size, <=, 1024 * 1024);
+
+       /*
+        * Set default initial request processing delay to 1/3 of a second.
+        * Will be updated by the lowest response window across all home servers,
+        * if it is less than this.
+        */
+       main_config.init_delay.tv_sec = 0;
+       main_config.init_delay.tv_usec = 2* (1000000 / 3);
 
        /*
         *      Free the old configuration items, and replace them
@@ -911,35 +982,26 @@ do {\
         *      Note that where possible, we do atomic switch-overs,
         *      to ensure that the pointers are always valid.
         */
-       rad_assert(mainconfig.config == NULL);
-       root_config = mainconfig.config = cs;
+       rad_assert(main_config.config == NULL);
+       root_config = main_config.config = cs;
 
-       DEBUG2("%s: #### Loading Realms and Home Servers ####", mainconfig.name);
+       DEBUG2("%s: #### Loading Realms and Home Servers ####", main_config.name);
        if (!realms_init(cs)) {
                return -1;
        }
 
-       DEBUG2("%s: #### Loading Clients ####", mainconfig.name);
-       if (!clients_parse_section(cs, false)) {
+       DEBUG2("%s: #### Loading Clients ####", main_config.name);
+       if (!client_list_parse_section(cs, false)) {
                return -1;
        }
 
        /*
-        *  Register the %{config:section.subsection} xlat function.
+        *      Register the %{config:section.subsection} xlat function.
         */
        xlat_register("config", xlat_config, NULL, NULL);
        xlat_register("client", xlat_client, NULL, NULL);
        xlat_register("getclient", xlat_getclient, NULL, NULL);
-
-       /*
-        *      Starting the server, WITHOUT "-x" on the
-        *      command-line: use whatever is in the config
-        *      file.
-        */
-       if (debug_flag == 0) {
-               debug_flag = mainconfig.debug_level;
-       }
-       fr_debug_flag = debug_flag;
+       xlat_register("listen", xlat_listen, NULL, NULL);
 
        /*
         *  Go update our behaviour, based on the configuration
@@ -950,10 +1012,7 @@ do {\
         *      Sanity check the configuration for internal
         *      consistency.
         */
-       if (mainconfig.reject_delay > mainconfig.cleanup_delay) {
-               mainconfig.reject_delay = mainconfig.cleanup_delay;
-       }
-       if (mainconfig.reject_delay < 0) mainconfig.reject_delay = 0;
+       FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, <=, main_config.cleanup_delay, 0);
 
        if (chroot_dir) {
                if (chdir(radlog_dir) < 0) {
@@ -979,7 +1038,7 @@ do {\
 /*
  *     Free the configuration.  Called only when the server is exiting.
  */
-int mainconfig_free(void)
+int main_config_free(void)
 {
        virtual_servers_free(0);
 
@@ -987,9 +1046,9 @@ int mainconfig_free(void)
         *      Clean up the configuration data
         *      structures.
         */
-       clients_free(NULL);
+       client_list_free(NULL);
        realms_free();
-       listen_free(&mainconfig.listen);
+       listen_free(&main_config.listen);
 
        /*
         *      Frees current config and any previous configs.
@@ -1002,40 +1061,129 @@ int mainconfig_free(void)
 
 void hup_logfile(void)
 {
-               int fd, old_fd;
+       int fd, old_fd;
 
-               if (default_log.dst != L_DST_FILES) return;
+       if (default_log.dst != L_DST_FILES) return;
 
-               fd = open(mainconfig.log_file,
-                         O_WRONLY | O_APPEND | O_CREAT, 0640);
-               if (fd >= 0) {
-                       /*
-                        *      Atomic swap. We'd like to keep the old
-                        *      FD around so that callers don't
-                        *      suddenly find the FD closed, and the
-                        *      writes go nowhere.  But that's hard to
-                        *      do.  So... we have the case where a
-                        *      log message *might* be lost on HUP.
-                        */
-                       old_fd = default_log.fd;
-                       default_log.fd = fd;
-                       close(old_fd);
-               }
+       fd = open(main_config.log_file,
+                 O_WRONLY | O_APPEND | O_CREAT, 0640);
+       if (fd >= 0) {
+               /*
+                *      Atomic swap. We'd like to keep the old
+                *      FD around so that callers don't
+                *      suddenly find the FD closed, and the
+                *      writes go nowhere.  But that's hard to
+                *      do.  So... we have the case where a
+                *      log message *might* be lost on HUP.
+                */
+               old_fd = default_log.fd;
+               default_log.fd = fd;
+               close(old_fd);
+       }
 }
 
-void mainconfig_hup(void)
+static int hup_callback(void *ctx, void *data)
 {
+       CONF_SECTION *modules = ctx;
+       CONF_SECTION *cs = data;
+       CONF_SECTION *parent;
+       char const *name;
+       module_instance_t *mi;
+
+       /*
+        *      Files may be defined in sub-sections of a module
+        *      config.  Walk up the tree until we find the module
+        *      definition.
+        */
+       parent = cf_item_parent(cf_section_to_item(cs));
+       while (parent != modules) {
+               cs = parent;
+               parent = cf_item_parent(cf_section_to_item(cs));
+
+               /*
+                *      Something went wrong.  Oh well...
+                */
+               if (!parent) return 0;
+       }
+
+       name = cf_section_name2(cs);
+       if (!name) name = cf_section_name1(cs);
+
+       mi = module_find(modules, name);
+       if (!mi) return 0;
+
+       if ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) return 0;
+
+       if (!module_hup_module(mi->cs, mi, time(NULL))) return 0;
+
+       return 0;
+}
+
+void main_config_hup(void)
+{
+       int rcode;
        cached_config_t *cc;
        CONF_SECTION *cs;
+       time_t when;
        char buffer[1024];
 
-       INFO("HUP - Re-reading configuration files");
+       static time_t last_hup = 0;
+
+       /*
+        *      Re-open the log file.  If we can't, then keep logging
+        *      to the old log file.
+        *
+        *      The "open log file" code is here rather than in log.c,
+        *      because it makes that function MUCH simpler.
+        */
+       hup_logfile();
+
+       /*
+        *      Only check the config files every few seconds.
+        */
+       when = time(NULL);
+       if ((last_hup + 2) >= when) {
+               INFO("HUP - Last HUP was too recent.  Ignoring");
+               return;
+       }
+       last_hup = when;
+
+       rcode = cf_file_changed(cs_cache->cs, hup_callback);
+       if (rcode == CF_FILE_NONE) {
+               INFO("HUP - No files changed.  Ignoring");
+               return;
+       }
+
+       if (rcode == CF_FILE_ERROR) {
+               INFO("HUP - Cannot read configuration files.  Ignoring");
+               return;
+       }
+
+       /*
+        *      No config files have changed.
+        */
+       if ((rcode & CF_FILE_CONFIG) == 0) {
+               if ((rcode & CF_FILE_MODULE) != 0) {
+                       INFO("HUP - Files loaded by a module have changed.");
+
+                       /*
+                        *      FIXME: reload the module.
+                        */
+
+               }
+               return;
+       }
+
+       cs = cf_section_alloc(NULL, "main", NULL);
+       if (!cs) return;
 
        /* Read the configuration file */
-       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
-                radius_dir, mainconfig.name);
-       if ((cs = cf_file_read(buffer)) == NULL) {
+       snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, main_config.name);
+
+       INFO("HUP - Re-reading configuration files");
+       if (cf_file_read(cs, buffer) < 0) {
                ERROR("Failed to re-read or parse %s", buffer);
+               talloc_free(cs);
                return;
        }
 
@@ -1059,15 +1207,6 @@ void mainconfig_hup(void)
        cc->next = cs_cache;
        cs_cache = cc;
 
-       /*
-        *      Re-open the log file.  If we can't, then keep logging
-        *      to the old log file.
-        *
-        *      The "open log file" code is here rather than in log.c,
-        *      because it makes that function MUCH simpler.
-        */
-       hup_logfile();
-
        INFO("HUP - loading modules");
 
        /*
@@ -1080,5 +1219,5 @@ void mainconfig_hup(void)
         */
        virtual_servers_load(cs);
 
-       virtual_servers_free(cc->created - mainconfig.max_request_time * 4);
+       virtual_servers_free(cc->created - (main_config.max_request_time * 4));
 }