]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Use separate module lists for proto, process and rlm modules
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 9 Apr 2022 02:22:58 +0000 (21:22 -0500)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 9 Apr 2022 04:18:38 +0000 (23:18 -0500)
82 files changed:
raddb/sites-available/default
src/bin/dhcpclient.c
src/bin/fuzzer.c
src/bin/radclient.c
src/bin/radict.c
src/bin/radiusd.c
src/bin/radsniff.c
src/bin/radwho.c
src/bin/unit_test_attribute.c
src/bin/unit_test_map.c
src/bin/unit_test_module.c
src/lib/io/master.c
src/lib/server/base.c
src/lib/server/cf_file.c
src/lib/server/cf_parse.c
src/lib/server/cf_util.h
src/lib/server/dl_module.c
src/lib/server/dl_module.h
src/lib/server/main_config.c
src/lib/server/modpriv.h
src/lib/server/module.c
src/lib/server/module.h
src/lib/server/module_rlm.c
src/lib/server/module_rlm.h
src/lib/server/virtual_servers.c
src/lib/server/virtual_servers.h
src/lib/unlang/call.c
src/lib/unlang/compile.c
src/lib/unlang/module.c
src/lib/unlang/xlat_inst.c
src/lib/util/dict.h
src/lib/util/dict_priv.h
src/lib/util/dict_test.c
src/lib/util/dict_util.c
src/lib/util/log.c
src/listen/arp/proto_arp.c
src/listen/control/proto_control.c
src/listen/control/radmin.c
src/listen/cron/proto_cron.c
src/listen/detail/proto_detail.c
src/listen/dhcpv4/proto_dhcpv4.c
src/listen/dhcpv6/proto_dhcpv6.c
src/listen/dns/proto_dns.c
src/listen/load/proto_load.c
src/listen/radius/proto_radius.c
src/listen/tacacs/proto_tacacs.c
src/listen/vmps/proto_vmps.c
src/modules/proto_ldap_sync/sync_touch.c
src/modules/rlm_always/rlm_always.c
src/modules/rlm_cache/drivers/rlm_cache_memcached/rlm_cache_memcached.c
src/modules/rlm_cache/drivers/rlm_cache_rbtree/rlm_cache_rbtree.c
src/modules/rlm_cache/drivers/rlm_cache_redis/rlm_cache_redis.c
src/modules/rlm_cache/rlm_cache.c
src/modules/rlm_eap/rlm_eap.c
src/modules/rlm_ldap/rlm_ldap.c
src/modules/rlm_radius/rlm_radius.c
src/modules/rlm_sql/drivers/rlm_sql_cassandra/rlm_sql_cassandra.c
src/modules/rlm_sql/drivers/rlm_sql_db2/rlm_sql_db2.c
src/modules/rlm_sql/drivers/rlm_sql_firebird/rlm_sql_firebird.c
src/modules/rlm_sql/drivers/rlm_sql_freetds/rlm_sql_freetds.c
src/modules/rlm_sql/drivers/rlm_sql_mysql/rlm_sql_mysql.c
src/modules/rlm_sql/drivers/rlm_sql_null/rlm_sql_null.c
src/modules/rlm_sql/drivers/rlm_sql_oracle/rlm_sql_oracle.c
src/modules/rlm_sql/drivers/rlm_sql_postgresql/rlm_sql_postgresql.c
src/modules/rlm_sql/drivers/rlm_sql_sqlite/rlm_sql_sqlite.c
src/modules/rlm_sql/drivers/rlm_sql_unixodbc/rlm_sql_unixodbc.c
src/modules/rlm_sql/rlm_sql.c
src/modules/rlm_sqlippool/rlm_sqlippool.c
src/process/arp/base.c
src/process/control/base.c
src/process/dhcpv4/base.c
src/process/dhcpv6/base.c
src/process/dns/base.c
src/process/eap_aka/base.c
src/process/eap_aka_prime/base.c
src/process/eap_sim/base.c
src/process/radius/base.c
src/process/tacacs/base.c
src/process/tls/base.c
src/process/ttls/base.c
src/process/vmps/base.c
src/tests/radmin/show-server-list.out

index efe4dd6245c1ff702a86f70b09fac9604f487086..bc9f53ef82f94154440546d465f670558692ee5d 100644 (file)
@@ -540,7 +540,7 @@ server default {
                }
        }
 
-       listen {
+       listen tcp_auth {
                type = Access-Request
                type = Status-Server
 
@@ -597,7 +597,7 @@ server default {
        #
        #  ### Listen for Accounting-Request packets
        #
-       listen {
+       listen udp_acct {
                type = Accounting-Request
 
                transport = udp
index 516a4b57d09fb7814e210ec20355bf21df5f8d1d..304d5ea892c7e1dae78190ee4e65903a03ee4693 100644 (file)
@@ -551,8 +551,6 @@ int main(int argc, char **argv)
        fr_radius_packet_t      *reply = NULL;
        fr_pair_list_t          reply_vps;
 
-       TALLOC_CTX              *autofree;
-
        int                     ret;
 
        fr_pair_list_init(&packet_vps);
@@ -563,8 +561,6 @@ int main(int argc, char **argv)
         */
        fr_atexit_global_setup();
 
-       autofree = talloc_autofree_context();
-
        fr_debug_lvl = 1;
 
        while ((c = getopt(argc, argv, "d:D:f:hr:t:vxi:")) != -1) switch(c) {
@@ -611,7 +607,7 @@ int main(int argc, char **argv)
 
        if (argc < 2) usage();
 
-       if (!fr_dict_global_ctx_init(autofree, dict_dir)) {
+       if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
                fr_perror("dhcpclient");
                fr_exit(EXIT_FAILURE);
        }
index b851dfd07c9b8d1d4f3def6a768617949aa9dadb..7829c825d17270c9b1d43bae0f6668305f8bf242 100644 (file)
@@ -146,7 +146,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv)
                fr_exit_now(EXIT_FAILURE);
        }
 
-       dict_gctx = fr_dict_global_ctx_init(NULL, dict_dir);
+       dict_gctx = fr_dict_global_ctx_init(NULL, true, dict_dir);
        if (!dict_gctx) {
                fr_perror("dict_global");
                fr_exit_now(EXIT_FAILURE);
index 3d21453b50dafa9d6a08e09d9ccceae9756bd8c8..7fc7145f97d979e6e4bcc023665530d6fd361e4e 100644 (file)
@@ -1322,7 +1322,7 @@ int main(int argc, char **argv)
                fr_exit_now(EXIT_FAILURE);
        }
 
-       if (!fr_dict_global_ctx_init(autofree, dict_dir)) {
+       if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
                fr_perror("radclient");
                fr_exit_now(EXIT_FAILURE);
        }
index e784bce3119316674bf769bd3de14f930c1ba27b..2e3a37365d23fc8e662f323ebd8d3f0a4342ceb3 100644 (file)
@@ -312,7 +312,7 @@ int main(int argc, char *argv[])
                goto finish;
        }
 
-       our_dict_gctx = fr_dict_global_ctx_init(autofree, dict_dir);
+       our_dict_gctx = fr_dict_global_ctx_init(NULL, true, dict_dir);
        if (!our_dict_gctx) {
                fr_perror("radict");
                ret = 1;
index 7dcfd914d2d40ec01bd34c4b036a16011d086038..c737a0e43fd3b6f917f391cbee318ba57921e4b3 100644 (file)
@@ -129,7 +129,10 @@ static int talloc_config_set(main_config_t *config)
  */
 static int thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el, UNUSED void *uctx)
 {
-       if (modules_thread_instantiate(ctx, el) < 0) return -1;
+       if (modules_rlm_thread_instantiate(ctx, el) < 0) return -1;
+
+       if (virtual_servers_thread_instantiate(ctx, el) < 0) return -1;
+
        if (xlat_thread_instantiate(ctx, el) < 0) return -1;
 #ifdef WITH_TLS
        if (fr_openssl_thread_init(main_config->openssl_async_pool_init,
@@ -143,7 +146,10 @@ static int thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el, UNUSED void
  */
 static void thread_detach(UNUSED void *uctx)
 {
-       modules_thread_detach();
+       virtual_servers_thread_detach();
+
+       modules_rlm_thread_detach();
+
        xlat_thread_detach();
 }
 
@@ -234,10 +240,6 @@ int main(int argc, char *argv[])
        void                    *pool_page_start = NULL, *pool_page_end = NULL;
        bool                    do_mprotect;
 
-       fr_dict_gctx_t const    *dict_gctx = NULL;
-
-       dl_module_loader_t *dl_modules = NULL;
-
 #ifndef NDEBUG
        fr_time_delta_t exit_after = fr_time_delta_wrap(0);
 #endif
@@ -540,18 +542,13 @@ int main(int argc, char *argv[])
         *      config file parser.  Note that we pass an empty path
         *      here, as we haven't yet read the configuration file.
         */
-       dl_modules = dl_module_loader_init(NULL);
-       if (!dl_modules) {
-               fr_perror("%s", program);
-               EXIT_WITH_FAILURE;
-       }
+       modules_init(NULL);
 
        /*
         *      Initialise the top level dictionary hashes which hold
         *      the protocols.
         */
-       dict_gctx = fr_dict_global_ctx_init(global_ctx, config->dict_dir);
-       if (!dict_gctx) {
+       if (!fr_dict_global_ctx_init(NULL, true, config->dict_dir)) {
                fr_perror("%s", program);
                EXIT_WITH_FAILURE;
        }
@@ -563,6 +560,19 @@ int main(int argc, char *argv[])
        }
 #endif
 
+       /*
+        *      Setup the global structures for module lists
+        */
+       if (modules_rlm_init() < 0) {
+               fr_perror("%s", program);
+               EXIT_WITH_FAILURE;
+       }
+
+       if (virtual_servers_init() < 0) {
+               fr_perror("%s", program);
+               EXIT_WITH_FAILURE;
+       }
+
        /*
         *  Read the configuration files, BEFORE doing anything else.
         */
@@ -587,16 +597,6 @@ int main(int argc, char *argv[])
                }
        }
 
-       if (modules_init() < 0) {
-               fr_perror("%s", program);
-               EXIT_WITH_FAILURE;
-       }
-
-       if (virtual_servers_init(config->root_cs) < 0) {
-               fr_perror("%s", program);
-               EXIT_WITH_FAILURE;
-       }
-
        /*
         *  Set panic_action from the main config if one wasn't specified in the
         *  environment.
@@ -1083,26 +1083,24 @@ cleanup:
        if (config) talloc_memory_report = config->talloc_memory_report;        /* Grab this before we free the config */
 
        /*
-        *  And now nothing should be left anywhere except the
-        *  parsed configuration items.
+        *      Free modules, this needs to be done explicitly
+        *      because some libraries used by modules use atexit
+        *      handlers registered after ours, and they may deinit
+        *      themselves before we free the modules and cause
+        *      crashes on exit.
         */
-       main_config_free(&config);
+       modules_rlm_free();
 
        /*
-        *      Free the modules that we loaded.
+        *      Same with virtual servers and proto modules.
         */
-       if (dl_modules) talloc_free(dl_modules);
+       virtual_servers_free();
 
        /*
-        *      Complain in debug builds about dictionaries
-        *      that haven't been freed.
+        *  And now nothing should be left anywhere except the
+        *  parsed configuration items.
         */
-       if (fr_dict_global_ctx_free(dict_gctx) < 0) {
-#ifndef NDEBUG
-               fr_perror("radiusd");
-               ret = EXIT_FAILURE;
-#endif
-       }
+       main_config_free(&config);
 
        /*
         *  Cleanup everything else
index 03183af17764b59c17396dc18d12ec1c0cac973e..66aec78bbe4dc1b7120abfba80faec36354f4cc8 100644 (file)
@@ -2597,7 +2597,7 @@ int main(int argc, char *argv[])
                                                         conf->pcap_filter, conf->pcap_filter);
        }
 
-       dict_gctx = fr_dict_global_ctx_init(conf, dict_dir);
+       dict_gctx = fr_dict_global_ctx_init(NULL, true, dict_dir);
        if (!dict_gctx) {
                fr_perror("radsniff");
                fr_exit_now(EXIT_FAILURE);
index 9bdd53faf6d49cef46db0149c99e3e15b6f3704c..e1d57d7a16da9bf21f91c14e46c275fca8db18a3 100644 (file)
@@ -307,7 +307,7 @@ int main(int argc, char **argv)
                return 1;
        }
 
-       dict_gctx = fr_dict_global_ctx_init(autofree, config->dict_dir);
+       dict_gctx = fr_dict_global_ctx_init(NULL, true, config->dict_dir);
        if (!dict_gctx) {
                fr_perror("%s", main_config->name);
                fr_exit_now(EXIT_FAILURE);
index fb9686b18622df565fbd4902b301cd6b617f577f..39e810cc0b6386de5d2c590c6b6e241080da54b4 100644 (file)
@@ -3137,7 +3137,7 @@ static command_file_ctx_t *command_ctx_alloc(TALLOC_CTX *ctx,
         *      go in this context, and don't affect the main
         *      dictionary context.
         */
-       cc->test_gctx = fr_dict_global_ctx_init(cc, cc->config->dict_dir);
+       cc->test_gctx = fr_dict_global_ctx_init(cc, false, cc->config->dict_dir);
        if (!cc->test_gctx) {
                fr_perror("Failed allocating test dict_gctx");
                return NULL;
@@ -3169,7 +3169,7 @@ static void command_ctx_reset(command_file_ctx_t *cc, TALLOC_CTX *ctx)
 
        if (fr_dict_global_ctx_free(cc->test_gctx) < 0) fr_perror("unit_test_attribute");
 
-       cc->test_gctx = fr_dict_global_ctx_init(cc, cc->config->dict_dir);
+       cc->test_gctx = fr_dict_global_ctx_init(cc, false, cc->config->dict_dir);
        if (fr_dict_internal_afrom_file(&cc->test_internal_dict, FR_DICTIONARY_INTERNAL_DIR, __FILE__) < 0) {
                fr_perror("Failed loading test dict_gctx internal dictionary");
        }
@@ -3476,7 +3476,6 @@ int main(int argc, char *argv[])
        CONF_SECTION            *cs;
        int                     ret = EXIT_SUCCESS;
        TALLOC_CTX              *autofree;
-       dl_module_loader_t      *dl_modules = NULL;
        bool                    exit_now = false;
 
        command_config_t        config = {
@@ -3601,11 +3600,7 @@ int main(int argc, char *argv[])
        fr_openssl_init();
 #endif
 
-       dl_modules = dl_module_loader_init(NULL);
-       if (!dl_modules) {
-               fr_perror("unit_test_attribute");
-               EXIT_WITH_FAILURE;
-       }
+       modules_init(NULL);
 
        dl_loader = dl_loader_init(autofree, NULL, false, false);
        if (!dl_loader) {
@@ -3613,7 +3608,7 @@ int main(int argc, char *argv[])
                EXIT_WITH_FAILURE;
        }
 
-       config.dict_gctx = fr_dict_global_ctx_init(autofree, config.dict_dir);
+       config.dict_gctx = fr_dict_global_ctx_init(NULL, true, config.dict_dir);
        if (!config.dict_gctx) {
                fr_perror("unit_test_attribute");
                EXIT_WITH_FAILURE;
@@ -3736,11 +3731,13 @@ cleanup:
        fr_openssl_free();
 #endif
 
-       if (talloc_free(dl_modules) < 0) {
-               fr_perror("unit_test_attribute - dl_modules - ");       /* Print free order issues */
-       }
-       if (talloc_free(dl_loader) < 0) {
+       /*
+        *      dl_loader check needed as talloc_free
+        *      returns -1 on failure.
+        */
+       if (dl_loader && (talloc_free(dl_loader) < 0)) {
                fr_perror("unit_test_attribute - dl_loader - ");        /* Print free order issues */
+               ret = EXIT_FAILURE;
        }
        if (fr_dict_free(&config.dict, __FILE__) < 0) {
                fr_perror("unit_test_attribute");
@@ -3749,15 +3746,6 @@ cleanup:
 
        unlang_free_global();
 
-       /*
-        *      Dictionaries get freed towards the end
-        *      because it breaks "autofree".
-        */
-       if (fr_dict_global_ctx_free(config.dict_gctx) < 0) {
-               fr_perror("unit_test_attribute");       /* Print free order issues */
-               ret = EXIT_FAILURE;
-       }
-
        if (receipt_file && (ret == EXIT_SUCCESS) && (fr_touch(NULL, receipt_file, 0644, true, 0755) <= 0)) {
                fr_perror("unit_test_attribute");
                ret = EXIT_FAILURE;
index e12c0c7104d604ba44d9b3d10edc419abb2e894a..13300e7ba9c8619637f08adae1991542cced5111 100644 (file)
@@ -237,7 +237,7 @@ int main(int argc, char *argv[])
                EXIT_WITH_FAILURE;
        }
 
-       dict_gctx = fr_dict_global_ctx_init(autofree, dict_dir);
+       dict_gctx = fr_dict_global_ctx_init(NULL, true, dict_dir);
        if (!dict_gctx) {
                fr_perror("unit_test_map");
                EXIT_WITH_FAILURE;
index ee59421f0bb535fd0011122356f081c03fd911db..e0131bf6f04376774a16c36b88cd772e78472a99 100644 (file)
@@ -553,7 +553,6 @@ int main(int argc, char *argv[])
 
        char                    *p;
        main_config_t           *config;
-       dl_module_loader_t      *dl_modules = NULL;
 
        CONF_SECTION            *server_cs;
 
@@ -722,13 +721,9 @@ int main(int argc, char *argv[])
         *      Initialize the DL infrastructure, which is used by the
         *      config file parser.
         */
-       dl_modules = dl_module_loader_init(config->lib_dir);
-       if (!dl_modules) {
-               fr_perror("%s", config->name);
-               EXIT_WITH_FAILURE;
-       }
+       modules_init(config->lib_dir);
 
-       dict_gctx = fr_dict_global_ctx_init(autofree, config->dict_dir);
+       dict_gctx = fr_dict_global_ctx_init(NULL, true, config->dict_dir);
        if (!dict_gctx) {
                fr_perror("%s", config->name);
                EXIT_WITH_FAILURE;
@@ -785,18 +780,19 @@ int main(int argc, char *argv[])
                setenv("PROTOCOL", PROTOCOL_NAME, true);
        }
 
-       /*  Read the configuration files, BEFORE doing anything else.  */
-       if (main_config_init(config) < 0) {
+       /*
+        *      Setup the global structures for module lists
+        */
+       if (modules_rlm_init() < 0) {
+               fr_perror("%s", config->name);
                EXIT_WITH_FAILURE;
        }
-
-       if (modules_init() < 0) {
+       if (virtual_servers_init() < 0) {
                fr_perror("%s", config->name);
                EXIT_WITH_FAILURE;
        }
 
-       if (virtual_servers_init(config->root_cs) < 0) {
-               fr_perror("%s", config->name);
+       if (main_config_init(config) < 0) {
                EXIT_WITH_FAILURE;
        }
 
@@ -821,7 +817,7 @@ int main(int argc, char *argv[])
        /*
         *      Do some sanity checking.
         */
-       dict_check = virtual_server_namespace("default");
+       dict_check = virtual_server_dict_by_name("default");
        if (!dict_check || (dict_check != dict_protocol)) {
                ERROR("Virtual server namespace does not match requested namespace '%s'", PROTOCOL_NAME);
                EXIT_WITH_FAILURE;
@@ -836,7 +832,8 @@ int main(int argc, char *argv[])
        /*
         *      Simulate thread specific instantiation
         */
-       if (modules_thread_instantiate(thread_ctx, el) < 0) EXIT_WITH_FAILURE;
+       if (modules_rlm_thread_instantiate(thread_ctx, el) < 0) EXIT_WITH_FAILURE;
+       if (virtual_servers_thread_instantiate(thread_ctx, el) < 0) EXIT_WITH_FAILURE;
        if (xlat_thread_instantiate(thread_ctx, el) < 0) EXIT_WITH_FAILURE;
        unlang_thread_instantiate(thread_ctx);
 
@@ -994,6 +991,14 @@ cleanup:
         */
        talloc_free(thread_ctx);
 
+       /*
+        *      Ensure all thread local memory is cleaned up
+        *      at the appropriate time.  This emulates what's
+        *      done with worker/network threads in the
+        *      scheduler.
+        */
+       fr_atexit_thread_trigger_all();
+
        /*
         *      Give processes a chance to exit
         */
@@ -1024,6 +1029,20 @@ cleanup:
         */
        unlang_free_global();
 
+       /*
+        *      Free modules, this needs to be done explicitly
+        *      because some libraries used by modules use atexit
+        *      handlers registered after ours, and they may deinit
+        *      themselves before we free the modules and cause
+        *      crashes on exit.
+        */
+       modules_rlm_free();
+
+       /*
+        *      Same with virtual servers and proto modules.
+        */
+       virtual_servers_free();
+
        /*
         *      And now nothing should be left anywhere except the
         *      parsed configuration items.
@@ -1039,12 +1058,10 @@ cleanup:
         *      Free our explicitly loaded internal dictionary
         */
        if (fr_dict_free(&dict, __FILE__) < 0) {
-               fr_perror("unit_test_module");
+               fr_perror("unit_test_module - dict");
                ret = EXIT_FAILURE;
        }
 
-       if (dl_modules) talloc_free(dl_modules);
-
        /*
         *      Free any openssl resources and the TLS dictionary
         */
@@ -1052,22 +1069,13 @@ cleanup:
        fr_openssl_free();
 #endif
 
-       /*
-        *      Free all the dictionaries, and complain/fail if
-        *      they still have dependents.
-        */
-       if (fr_dict_global_ctx_free(dict_gctx) < 0) {
-               fr_perror("unit_test_module");
-               ret = EXIT_FAILURE;
-       }
-
        if (receipt_file && (ret == EXIT_SUCCESS) && (fr_touch(NULL, receipt_file, 0644, true, 0755) <= 0)) {
                fr_perror("unit_test_module");
                ret = EXIT_FAILURE;
        }
 
        if (talloc_free(autofree) < 0) {
-               fr_perror("unit_test_module");
+               fr_perror("unit_test_module - autofree");
                ret = EXIT_FAILURE;
        }
 
index 47e0150b67ac57ae6661730c63c68954e67e8c8a..ad1f6d57df0d398fe19b3c30f3d4136248f47879 100644 (file)
@@ -46,6 +46,7 @@ typedef struct {
        // @todo - count num_nak_clients, and num_nak_connections, too
        uint32_t                        num_connections;                //!< number of dynamic connections
        uint32_t                        num_pending_packets;            //!< number of pending packets
+       uint64_t                        client_id;                      //!< Unique client identifier.
 } fr_io_thread_t;
 
 /** A saved packet
@@ -475,6 +476,8 @@ static fr_io_connection_t *fr_io_connection_alloc(fr_io_instance_t const *inst,
         *      called when the instance data is freed.
         */
        if (!nak) {
+               char *inst_name;
+
                if (inst->max_connections || client->radclient->limit.max_connections) {
                        uint32_t max_connections = inst->max_connections ? inst->max_connections : client->radclient->limit.max_connections;
 
@@ -495,16 +498,24 @@ static fr_io_connection_t *fr_io_connection_alloc(fr_io_instance_t const *inst,
                        }
                }
 
-               if (dl_module_instance(NULL, &dl_inst, NULL, inst->dl_inst, inst->transport, DL_MODULE_TYPE_SUBMODULE) < 0) {
+               /*
+                *      FIXME - This is not at all thread safe
+                */
+               inst_name = talloc_asprintf(NULL, "%s%"PRIu64, inst->transport, thread->client_id++);
+               if (dl_module_instance(NULL, &dl_inst, inst->dl_inst,
+                                      DL_MODULE_TYPE_SUBMODULE, inst->transport, inst_name) < 0) {
+                       talloc_free(inst_name);
                        DEBUG("Failed to find proto_%s_%s", inst->app->common.name, inst->transport);
                        return NULL;
                }
+               talloc_free(inst_name);
 
-               if (dl_module_conf_parse(dl_inst) < 0) {
+/*
+               if (dl_module_conf_parse(dl_inst, inst->server_cs) < 0) {
                        TALLOC_FREE(dl_inst);
                        return NULL;
                }
-
+*/
                fr_assert(dl_inst != NULL);
        } else {
                dl_inst = talloc_init_const("nak");
@@ -2602,12 +2613,13 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
                 *      Load proto_dhcpv4_dynamic_client
                 */
                if (dl_module_instance(conf, &inst->dynamic_submodule,
-                                      conf, inst->dl_inst, "dynamic_client", DL_MODULE_TYPE_SUBMODULE) < 0) {
+                                      inst->dl_inst,
+                                      DL_MODULE_TYPE_SUBMODULE, inst->app->common.name, "dynamic_client") < 0) {
                        cf_log_err(conf, "Failed finding proto_%s_dynamic_client", inst->app->common.name);
                        return -1;
                }
 
-               if (dl_module_conf_parse(inst->dynamic_submodule) < 0) {
+               if (dl_module_conf_parse(inst->dynamic_submodule, conf) < 0) {
                        TALLOC_FREE(inst->dynamic_submodule);
                        return -1;
                }
@@ -2682,7 +2694,7 @@ static int mod_instantiate(module_inst_ctx_t const *mctx)
                if (app_process->compile_list) {
                        tmpl_rules_t    parse_rules = {
                                .attr = {
-                                       .dict_def = virtual_server_namespace(cf_section_name2(inst->server_cs))
+                                       .dict_def = virtual_server_dict_by_name(cf_section_name2(inst->server_cs))
                                }
                        };
 
index 2ddfe40c0eedc74697d356381ad409b4ebe9d7d5..8ad85d9a6fec8e667e17cd6753f8dd3a04be5970 100644 (file)
@@ -83,7 +83,7 @@ int server_init(CONF_SECTION *cs)
        /*
         *      Instantiate the modules
         */
-       if (modules_instantiate(cs) < 0) return -1;
+       if (modules_rlm_instantiate() < 0) return -1;
 
        /*
         *      Call xlat instantiation functions (after the xlats have been compiled)
@@ -105,11 +105,6 @@ void server_free(void)
         */
        xlat_instances_free();
 
-       /*
-        *      Detach modules, connection pools, registered xlats / paircmps / maps.
-        */
-       modules_free();
-
        /*
         *      The only paircmps remaining are the ones registered by the server core.
         */
@@ -130,11 +125,6 @@ void server_free(void)
         */
        map_proc_free();
 
-       /*
-        *      Free information associated with the virtual servers.
-        */
-       virtual_servers_free();
-
        /*
         *      Now we're sure no more triggers can fire, free the
         *      trigger tree.
index a482fe303ebf43a58570fc9e2ccc128065d49bdd..3d5612faba09d47395be40ed638844117e19a15d 100644 (file)
@@ -1165,7 +1165,7 @@ static CONF_ITEM *process_if(cf_stack_t *stack)
        buff[1] = stack->buff[1];
        buff[2] = stack->buff[2];
 
-       dict = virtual_server_namespace_by_ci(cf_section_to_item(parent));
+       dict = virtual_server_dict_by_child_ci(cf_section_to_item(parent));
 
        /*
         *      fr_cond_tokenize needs the current section, so we
index d1746bb8376ecdb390394626d794a147e64de533..652ba1704071c200214df404b0d5ab35071cb89c 100644 (file)
@@ -1222,9 +1222,10 @@ int cf_section_parse(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs)
                rule = cf_data_value(rule_cd);
 
                /*
-                *      Ignore ON_READ parse rules
+                *      Ignore ON_READ parse rules if there's no subsequent
+                *      parse functions.
                 */
-               if (rule->on_read) continue;
+               if (!rule->func && rule->on_read) continue;
 
                /*
                 *      Pre-allocate the config structure to hold default values
@@ -1467,7 +1468,7 @@ int cf_section_parse_pass2(void *base, CONF_SECTION *cs)
                 *      Search for dictionary data somewhere in the virtual
                 *      server.
                 */
-               dict = virtual_server_namespace_by_ci(cf_section_to_item(cs));
+               dict = virtual_server_dict_by_child_ci(cf_section_to_item(cs));
 
                /*
                 *      Parse (and throw away) the xlat string (for validation).
index a8b4771d9610fb74e8ad12a7213adda3f1fe8604..a507bd77fcee73aa46acdd0daf8fd530c32f9c5b 100644 (file)
@@ -211,7 +211,20 @@ CONF_DATA const *_cf_data_add(CONF_ITEM *ci, void const *data, char const *name,
 #define                cf_data_add_static(_cf, _data, _type, _name) _cf_data_add_static(CF_TO_ITEM(_cf), _data, #_type, _name, __FILE__, __LINE__)
 CONF_DATA const *_cf_data_add_static(CONF_ITEM *ci, void const *data, char const *type, char const *name, char const *filename, int lineno);
 
-#define                cf_data_remove(_cf, _cd) _cf_data_remove(CF_TO_ITEM(_cf), _cd);
+/** Remove an item from a parent by type and name
+ *
+ * @param[in] _cf conf section or pair to remove data from.
+ * @param[in] _type of data to remove.
+ * @param[in] _name of data to remove.
+ */
+#define                cf_data_remove(_cf, _type, _name) _cf_data_remove(CF_TO_ITEM(_cf), cf_data_find(_cf, _type, _name));
+
+/** Remove an item from a parent
+ *
+ * @param[in] _cf conf section or pair to remove data from.
+ * @param[in] _cd conf data to remove.
+ */
+#define                cf_data_remove_by_data(_cf, _cd) _cf_data_remove(CF_TO_ITEM(_cf), _cd);
 void           *_cf_data_remove(CONF_ITEM *ci, CONF_DATA const *_cd);
 
 #define                cf_data_walk(_cf, _type, _cb, _ctx) _cf_data_walk(CF_TO_ITEM(_cf), #_type, _cb, _ctx)
index a3709bfd346f4c746b7eed0f62d6e72e835e390a..4eeac4922beb0ed8fb035062826a1a413d1fff55 100644 (file)
@@ -150,7 +150,6 @@ static void dl_module_unload_func(dl_t const *dl, UNUSED void *symbol, UNUSED vo
  * This is used to detect potential ABI issues caused by running with modules which
  * were built for a different version of the server.
  *
- * @param[in] cs       being parsed.
  * @param[in] module   Common fields from module's exported interface struct.
  * @returns
  *     - 0 on success.
@@ -158,7 +157,7 @@ static void dl_module_unload_func(dl_t const *dl, UNUSED void *symbol, UNUSED vo
  *     - -2 if version mismatch.
  *     - -3 if commit mismatch.
  */
-static int dl_module_magic_verify(CONF_SECTION const *cs, dl_module_common_t const *module)
+static int dl_module_magic_verify(dl_module_common_t const *module)
 {
 #ifdef HAVE_DLADDR
        Dl_info dl_info;
@@ -167,10 +166,10 @@ static int dl_module_magic_verify(CONF_SECTION const *cs, dl_module_common_t con
 
        if (MAGIC_PREFIX(module->magic) != MAGIC_PREFIX(RADIUSD_MAGIC_NUMBER)) {
 #ifdef HAVE_DLADDR
-               cf_log_err(cs, "Failed loading module rlm_%s from file %s", module->name, dl_info.dli_fname);
+               ERROR("Failed loading module rlm_%s from file %s", module->name, dl_info.dli_fname);
 #endif
-               cf_log_err(cs, "Application and rlm_%s magic number (prefix) mismatch."
-                             "  application: %x module: %x", module->name,
+               ERROR("Application and rlm_%s magic number (prefix) mismatch."
+                     "  application: %x module: %x", module->name,
                              MAGIC_PREFIX(RADIUSD_MAGIC_NUMBER),
                              MAGIC_PREFIX(module->magic));
                return -1;
@@ -178,29 +177,37 @@ static int dl_module_magic_verify(CONF_SECTION const *cs, dl_module_common_t con
 
        if (MAGIC_VERSION(module->magic) != MAGIC_VERSION(RADIUSD_MAGIC_NUMBER)) {
 #ifdef HAVE_DLADDR
-               cf_log_err(cs, "Failed loading module rlm_%s from file %s", module->name, dl_info.dli_fname);
+               ERROR("Failed loading module rlm_%s from file %s", module->name, dl_info.dli_fname);
 #endif
-               cf_log_err(cs, "Application and rlm_%s magic number (version) mismatch."
-                             "  application: %lx module: %lx", module->name,
-                             (unsigned long) MAGIC_VERSION(RADIUSD_MAGIC_NUMBER),
-                             (unsigned long) MAGIC_VERSION(module->magic));
+               ERROR("Application and rlm_%s magic number (version) mismatch."
+                     "  application: %lx module: %lx", module->name,
+                     (unsigned long) MAGIC_VERSION(RADIUSD_MAGIC_NUMBER),
+                     (unsigned long) MAGIC_VERSION(module->magic));
                return -2;
        }
 
        if (MAGIC_COMMIT(module->magic) != MAGIC_COMMIT(RADIUSD_MAGIC_NUMBER)) {
 #ifdef HAVE_DLADDR
-               cf_log_err(cs, "Failed loading module rlm_%s from file %s", module->name, dl_info.dli_fname);
+               ERROR("Failed loading module rlm_%s from file %s", module->name, dl_info.dli_fname);
 #endif
-               cf_log_err(cs, "Application and rlm_%s magic number (commit) mismatch."
-                             "  application: %lx module: %lx", module->name,
-                             (unsigned long) MAGIC_COMMIT(RADIUSD_MAGIC_NUMBER),
-                             (unsigned long) MAGIC_COMMIT(module->magic));
+               ERROR("Application and rlm_%s magic number (commit) mismatch."
+                     "  application: %lx module: %lx", module->name,
+                     (unsigned long) MAGIC_COMMIT(RADIUSD_MAGIC_NUMBER),
+                     (unsigned long) MAGIC_COMMIT(module->magic));
                return -3;
        }
 
        return 0;
 }
 
+/** Lookup a module's parent
+ *
+ */
+dl_module_inst_t const *dl_module_parent_instance(dl_module_inst_t const *child)
+{
+       return child->parent;
+}
+
 /** Lookup a dl_module_inst_t via instance data
  *
  */
@@ -361,8 +368,6 @@ static int _dl_module_free(dl_module_t *dl_module)
  * When all references to the original dlhandle are freed, dlclose() will be called on the
  * dlhandle to unload the module.
  *
- * @param[in] conf     section describing the module's configuration.  This is only used
- *                     to give error messages context, and for initialization.
  * @param[in] parent   The dl_module_t of the parent module, e.g. rlm_sql for rlm_sql_postgresql.
  * @param[in] name     of the module e.g. sql for rlm_sql.
  * @param[in] type     Used to determine module name prefixes.  Must be one of:
@@ -373,7 +378,7 @@ static int _dl_module_free(dl_module_t *dl_module)
  *     - Module handle holding dlhandle, and module's public interface structure.
  *     - NULL if module couldn't be loaded, or some other error occurred.
  */
-dl_module_t const *dl_module(CONF_SECTION *conf, dl_module_t const *parent, char const *name, dl_module_type_t type)
+dl_module_t const *dl_module(dl_module_t const *parent, char const *name, dl_module_type_t type)
 {
        dl_module_t                     *dl_module = NULL;
        dl_t                            *dl = NULL;
@@ -402,7 +407,7 @@ dl_module_t const *dl_module(CONF_SECTION *conf, dl_module_t const *parent, char
         *      If the module's already been loaded, increment the reference count.
         */
        dl_module = fr_rb_find(dl_module_loader->module_tree,
-                                   &(dl_module_t){ .dl = &(dl_t){ .name = module_name }});
+                              &(dl_module_t){ .dl = &(dl_t){ .name = module_name }});
        if (dl_module) {
                talloc_free(module_name);
                talloc_increase_ref_count(dl_module);
@@ -420,9 +425,9 @@ dl_module_t const *dl_module(CONF_SECTION *conf, dl_module_t const *parent, char
         */
        dl = dl_by_name(dl_module_loader->dl_loader, module_name, dl_module, false);
        if (!dl) {
-               cf_log_perr(conf, "Failed to link to module \"%s\"", module_name);
-               cf_log_err(conf, "Make sure it (and all its dependent libraries!) are in the search path"
-                          " of your system's ld");
+               ERROR("Failed to link to module \"%s\"", module_name);
+               ERROR("Make sure it (and all its dependent libraries!) are in the search path"
+                     " of your system's ld");
        error:
                talloc_free(module_name);
                talloc_free(dl_module);         /* Do not free dl explicitly, it's handled by the destructor */
@@ -434,7 +439,7 @@ dl_module_t const *dl_module(CONF_SECTION *conf, dl_module_t const *parent, char
 
        common = dlsym(dl->handle, module_name);
        if (!common) {
-               cf_log_err(conf, "Could not find \"%s\" symbol in module: %s", module_name, dlerror());
+               ERROR("Could not find \"%s\" symbol in module: %s", module_name, dlerror());
                goto error;
        }
        dl_module->common = common;
@@ -442,23 +447,23 @@ dl_module_t const *dl_module(CONF_SECTION *conf, dl_module_t const *parent, char
        /*
         *      Before doing anything else, check if it's sane.
         */
-       if (dl_module_magic_verify(conf, common) < 0) goto error;
+       if (dl_module_magic_verify(common) < 0) goto error;
 
        DEBUG3("%s validated.  Handle address %p, symbol address %p", module_name, dl, common);
 
        if (dl_symbol_init(dl_module_loader->dl_loader, dl) < 0) {
-               cf_log_perr(conf, "Failed calling initializers for module \"%s\"", module_name);
+               ERROR("Failed calling initializers for module \"%s\"", module_name);
                goto error;
        }
 
-       cf_log_info(conf, "Loaded module \"%s\"", module_name);
+       DEBUG2("Loaded module %s", module_name);
 
        /*
         *      Add the module to the dl cache
         */
        dl_module->in_tree = fr_rb_insert(dl_module_loader->module_tree, dl_module);
        if (!dl_module->in_tree) {
-               cf_log_err(conf, "Failed caching module \"%s\"", module_name);
+               ERROR("Failed caching module \"%s\"", module_name);
                goto error;
        }
 
@@ -533,66 +538,67 @@ void *dl_module_instance_symbol(dl_module_inst_t const *dl_inst, char const *sym
  * @param[in] ctx      to allocate structures in.
  * @param[out] out     where to write our #dl_module_inst_t containing the module
  *                     handle and instance.
- * @param[in] conf     section to parse.
  * @param[in] parent   of module instance.
- * @param[in] name     of the module to load .e.g. 'udp' for 'proto_radius_udp'
- *                     if the parent were 'proto_radius'.
  * @param[in] type     of module to load.
+ * @param[in] mod_name of the module to load .e.g. 'udp' for 'proto_radius_udp'
+ *                     if the parent were 'proto_radius'.
+ * @param[in] inst_name        The name of the instance .e.g. 'sql_aws_dc01'
  *
  * @return
  *     - 0 on success.
  *     - -1 on failure.
  */
 int dl_module_instance(TALLOC_CTX *ctx, dl_module_inst_t **out,
-                      CONF_SECTION *conf, dl_module_inst_t const *parent,
-                      char const *name, dl_module_type_t type)
+                      dl_module_inst_t const *parent,
+                      dl_module_type_t type, char const *mod_name, char const *inst_name)
 {
        dl_module_inst_t        *dl_inst;
-       char const              *name2;
 
        DL_INIT_CHECK;
 
        MEM(dl_inst = talloc_zero(ctx, dl_module_inst_t));
 
-       /*
-        *      Find a section with the same name as the module
-        */
-       dl_inst->module = dl_module(conf, parent ? parent->module : NULL, name, type);
+       dl_inst->module = dl_module(parent ? parent->module : NULL, mod_name, type);
        if (!dl_inst->module) {
                talloc_free(dl_inst);
                return -1;
        }
+       dl_inst->name = talloc_typed_strdup(dl_inst, inst_name);
 
        /*
         *      ctx here is the main module's instance data
         */
        dl_module_instance_data_alloc(dl_inst, dl_inst->module);
-
        talloc_set_destructor(dl_inst, _dl_module_instance_free);
 
-       /*
-        *      Associate the module instance with the conf section
-        *      *before* executing any parse rules that might need it.
-        */
-       cf_data_add(conf, dl_inst, dl_inst->module->dl->name, false);
-
-       name2 = cf_section_name2(conf);
-       if (name2) {
-               dl_inst->name = talloc_typed_strdup(dl_inst, name2);
-       } else {
-               dl_inst->name = talloc_typed_strdup(dl_inst, cf_section_name1(conf));
-       }
-
-       dl_inst->conf = conf;
        dl_inst->parent = parent;
-
        *out = dl_inst;
 
        return 0;
 }
 
-int dl_module_conf_parse(dl_module_inst_t *dl_inst)
+/** Avoid boilerplate when setting the module instance name
+ *
+ */
+char const *dl_module_inst_name_from_conf(CONF_SECTION *conf)
 {
+       char const *name2;
+
+       name2 = cf_section_name2(conf);
+       if (name2) return name2;
+
+       return cf_section_name1(conf);
+}
+
+int dl_module_conf_parse(dl_module_inst_t *dl_inst, CONF_SECTION *conf)
+{
+       /*
+        *      Associate the module instance with the conf section
+        *      *before* executing any parse rules that might need it.
+        */
+       cf_data_add(conf, dl_inst, dl_inst->module->dl->name, false);
+       dl_inst->conf = conf;
+
        if (dl_inst->module->common->config && dl_inst->conf) {
                if ((cf_section_rules_push(dl_inst->conf, dl_inst->module->common->config)) < 0 ||
                    (cf_section_parse(dl_inst->data, dl_inst->data, dl_inst->conf) < 0)) {
@@ -601,6 +607,7 @@ int dl_module_conf_parse(dl_module_inst_t *dl_inst)
                        return -1;
                }
        }
+
        return 0;
 }
 
index 82396f6832d1e7816d7eb4a9dad515c407bfb416..9b36be5bfc387259553d656cb22c85e12cce6129 100644 (file)
@@ -170,8 +170,9 @@ struct dl_module_instance_s {
 extern fr_table_num_sorted_t const dl_module_type_prefix[];
 extern size_t dl_module_type_prefix_len;
 
-dl_module_t const      *dl_module(CONF_SECTION *conf, dl_module_t const *parent,
-                                  char const *name, dl_module_type_t type);
+dl_module_t const      *dl_module(dl_module_t const *parent, char const *name, dl_module_type_t type);
+
+dl_module_inst_t const *dl_module_parent_instance(dl_module_inst_t const *child);
 
 dl_module_inst_t const *dl_module_instance_by_data(void const *data);
 
@@ -184,10 +185,12 @@ void                      *dl_module_parent_data_by_child_data(void const *data);
 void                   *dl_module_instance_symbol(dl_module_inst_t const *instance, char const *sym_name);
 
 int                    dl_module_instance(TALLOC_CTX *ctx, dl_module_inst_t **out,
-                                          CONF_SECTION *conf, dl_module_inst_t const *parent,
-                                          char const *name, dl_module_type_t type);
+                                          dl_module_inst_t const *parent,
+                                          dl_module_type_t type, char const *name, char const *inst_name);
+
+char const             *dl_module_inst_name_from_conf(CONF_SECTION *conf);
 
-int                    dl_module_conf_parse(dl_module_inst_t *dl_inst);
+int                    dl_module_conf_parse(dl_module_inst_t *dl_inst, CONF_SECTION *conf);
 
 char const             *dl_module_search_path(void);
 
index 7de02bbf3dca57f34a53d433359ed04899ed2a4a..4599f247933d5c3da1833de7217e47193b8d7bc8 100644 (file)
@@ -1250,7 +1250,7 @@ do {\
        if (cf_section_rules_push(cs, server_config) < 0) goto failure;
        if (cf_section_rules_push(cs, virtual_servers_config) < 0) goto failure;
 
-       DEBUG("Parsing main configuration.");
+       DEBUG("Parsing main configuration");
        if (cf_section_parse(config, config, cs) < 0) goto failure;
 
        /*
index e1e655ba2ce1caf783c5697247d0cc6b101247fd..49528c94352c56afb0f15209f813bd795c893aee 100644 (file)
@@ -38,8 +38,6 @@ extern fr_cmd_table_t module_cmd_table[];
 
 extern fr_cmd_table_t  module_cmd_list_table[];
 
-int                    module_instantiate(void *instance);
-
 int                    module_rlm_sibling_section_find(CONF_SECTION **out, CONF_SECTION *module, char const *name);
 
 int                    unlang_fixup_update(map_t *map, void *ctx);
index ce4a23df7274499d4e540457068fb8bd48c8908a..4a5565c099a25a0d3e403b4a8416d63f44aacb63 100644 (file)
@@ -36,23 +36,36 @@ RCSID("$Id$")
 #include <freeradius-devel/server/radmin.h>
 #include <freeradius-devel/server/request_data.h>
 
-static TALLOC_CTX *instance_ctx = NULL;
-static size_t instance_num = 1;
+/** Heap of all lists/modules used to get a common index with module_thread_inst_list
+ *
+ */
+static fr_heap_t *module_global_inst_list;
 
-/*
- *     For simplicity, this is just array[instance_num].  Once we
- *     finish with modules_rlm_bootstrap(), the "instance_num" above MUST
- *     NOT change.
+/** An array of thread-local module lists
+ *
+ * The indexes in this array are identical to module_list_global, allowing
+ * O(1) lookups.  Arrays are used here as there's no performance penalty
+ * once they're populated.
  */
-static _Thread_local module_thread_instance_t **module_thread_inst_array;
+static _Thread_local module_thread_instance_t **module_thread_inst_list;
 
-/** Lookup module instances by name and lineage
+/** Toggle used to determine if it's safe to use index based lookups
+ *
+ * Index based heap lookups are significantly more efficient than binary
+ * searches, but they can only be performed when all module data is inserted
+ * into both the global module list and the thread local module list.
+ *
+ * When we start removing module lists or modules from the thread local
+ * heap those heaps no longer have a common index with the global module
+ * list so we need to revert back to doing binary searches instead of using
+ * common indexes.
  */
-static fr_rb_tree_t *module_instance_name_tree;
+static _Thread_local bool module_list_in_sync = true;
 
-/** Lookup module by instance data
+/** dl module tracking
+ *
  */
-static fr_rb_tree_t *module_instance_data_tree;
+static dl_module_loader_t      *dl_modules = NULL;
 
 static int cmd_show_module_config(FILE *fp, UNUSED FILE *fp_err, void *ctx, UNUSED fr_cmd_info_t const *info);
 static int module_name_tab_expand(UNUSED TALLOC_CTX *talloc_ctx, UNUSED void *uctx, fr_cmd_info_t *info, int max_expansions, char const **expansions);
@@ -134,21 +147,18 @@ static int cmd_show_module_config(FILE *fp, UNUSED FILE *fp_err, void *ctx, UNUS
        return 0;
 }
 
-static int module_name_tab_expand(UNUSED TALLOC_CTX *talloc_ctx, UNUSED void *uctx, fr_cmd_info_t *info, int max_expansions, char const **expansions)
+static int module_name_tab_expand(UNUSED TALLOC_CTX *talloc_ctx, UNUSED void *uctx,
+                                 fr_cmd_info_t *info, int max_expansions, char const **expansions)
 {
-       fr_rb_iter_inorder_t    iter;
-       void                            *instance;
-       char const                      *text;
-       int                             count;
+       char const              *text;
+       int                     count;
 
        if (info->argc <= 0) return 0;
 
        text = info->argv[info->argc - 1];
        count = 0;
 
-       for (instance = fr_rb_iter_init_inorder(&iter, module_instance_name_tree);
-            instance;
-            instance = fr_rb_iter_next_inorder(&iter)) {
+       fr_heap_foreach(module_global_inst_list, module_instance_t, instance) {
                module_instance_t       *mi = talloc_get_type_abort(instance, module_instance_t);
 
                if (count >= max_expansions) {
@@ -158,24 +168,18 @@ static int module_name_tab_expand(UNUSED TALLOC_CTX *talloc_ctx, UNUSED void *uc
                        expansions[count] = strdup(mi->name);
                        count++;
                }
-       }
+       }}
 
        return count;
 }
 
-
 static int cmd_show_module_list(FILE *fp, UNUSED FILE *fp_err, UNUSED void *uctx, UNUSED fr_cmd_info_t const *info)
 {
-       fr_rb_iter_inorder_t    iter;
-       void                            *instance;
-
-       for (instance = fr_rb_iter_init_inorder(&iter, module_instance_name_tree);
-            instance;
-            instance = fr_rb_iter_next_inorder(&iter)) {
+       fr_heap_foreach(module_global_inst_list, module_instance_t, instance) {
                module_instance_t *mi = talloc_get_type_abort(instance, module_instance_t);
 
                fprintf(fp, "\t%s\n", mi->name);
-       }
+       }}
 
        return 0;
 }
@@ -216,6 +220,26 @@ static int cmd_set_module_status(UNUSED FILE *fp, FILE *fp_err, void *ctx, fr_cm
        return 0;
 }
 
+/** Sort module instance data first by list then by number
+ *
+ * The module's position in the global instance heap informs of us
+ * of its position in the thread-specific heap, which allows for
+ * O(1) lookups.
+ */
+static int8_t _module_instance_global_cmp(void const *one, void const *two)
+{
+       module_instance_t const *a = talloc_get_type_abort(one, module_instance_t);
+       module_instance_t const *b = talloc_get_type_abort(two, module_instance_t);
+       int8_t ret;
+
+       fr_assert(a->ml && b->ml);
+
+       ret = CMP(a->ml, b->ml);
+       if (ret != 0) return 0;
+
+       return CMP(a->number, b->number);
+}
+
 /** Compare module instances by parent and name
  *
  * The reason why we need parent, is because we could have submodules with names
@@ -276,18 +300,20 @@ static int8_t module_instance_data_cmp(void const *one, void const *two)
  * @param[in] parent   This _must_ point to the instance data of the parent
  *                     module.
  * @param[in] ci       The CONF_PAIR containing the name of the submodule to load.
- * @param[in] rule     unused.
+ * @param[in] rule     uctx pointer must be a pointer to a module_list_t **
+ *                     containing the list to search in.
  * @return
  *     - 0 on success.
  *     - -1 if we failed to load the submodule.
  */
 int module_submodule_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent,
-                          CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
+                          CONF_ITEM *ci, CONF_PARSER const *rule)
 {
        char const              *name = cf_pair_value(cf_item_to_pair(ci));
        CONF_SECTION            *cs = cf_item_to_section(cf_parent(ci));
        CONF_SECTION            *submodule_cs;
        module_instance_t       *mi;
+       module_list_t           *ml = talloc_get_type_abort(*((void * const *)rule->uctx), module_list_t);
 
        /*
         *      We assume the submodule's config is the
@@ -302,20 +328,38 @@ int module_submodule_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent,
         */
        if (!submodule_cs) submodule_cs = cf_section_alloc(cs, cs, name, NULL);
 
-       mi = module_bootstrap(DL_MODULE_TYPE_SUBMODULE, module_by_data(parent), submodule_cs);
+       /*
+        *      The submodule name dictates the module loaded
+        *      the instance name is always the submodule name
+        *      and will be appended to the parent's instance
+        *      name.
+        */
+       mi = module_alloc(ml, module_by_data(ml, parent), DL_MODULE_TYPE_SUBMODULE, name, name);
        if (unlikely(mi == NULL)) {
                cf_log_err(submodule_cs, "Failed loading submodule");
                return -1;
        }
 
+       if (unlikely(module_conf_parse(mi, submodule_cs) < 0)) {
+               cf_log_err(submodule_cs, "Failed parsing submodule config");
+       error:
+               talloc_free(mi);
+               return -1;
+       }
+
+       if (unlikely(module_bootstrap(mi) < 0)) {
+               cf_log_err(submodule_cs, "Failed bootstrapping submodule");
+               goto error;
+
+       }
        *((module_instance_t **)out) = mi;
 
        return 0;
 }
 
-
 /** Find an existing module instance by its name and parent
  *
+ * @param[in] ml               to search in.
  * @param[in] parent           to qualify search with.
  * @param[in] asked_name       The name of the module we're attempting to find.
  *                             May include '-' which indicates that it's ok for
@@ -324,12 +368,12 @@ int module_submodule_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent,
  *     - Module instance matching name.
  *     - NULL if no such module exists.
  */
-module_instance_t *module_by_name(module_instance_t const *parent, char const *asked_name)
+module_instance_t *module_by_name(module_list_t const *ml, module_instance_t const *parent, char const *asked_name)
 {
        char const              *inst_name;
        void                    *inst;
 
-       if (!module_instance_name_tree) return NULL;
+       if (!ml->name_tree) return NULL;
 
        /*
         *      Look for the real name.  Ignore the first character,
@@ -339,28 +383,46 @@ module_instance_t *module_by_name(module_instance_t const *parent, char const *a
        inst_name = asked_name;
        if (inst_name[0] == '-') inst_name++;
 
-       inst = fr_rb_find(module_instance_name_tree,
-                              &(module_instance_t){
-                                       .dl_inst = &(dl_module_inst_t){ .parent = parent ? parent->dl_inst : NULL },
-                                       .name = inst_name
-                              });
+       inst = fr_rb_find(ml->name_tree,
+                         &(module_instance_t){
+                               .dl_inst = &(dl_module_inst_t){ .parent = parent ? parent->dl_inst : NULL },
+                               .name = inst_name
+                         });
        if (!inst) return NULL;
 
        return talloc_get_type_abort(inst, module_instance_t);
 }
 
+/** Find the module's parent (if any)
+ *
+ * @param[in] child    to locate the parent for.
+ * @return
+ *     - The module's parent.
+ *     - NULL on error.
+ */
+module_instance_t *module_parent(module_instance_t const *child)
+{
+       dl_module_inst_t const *parent;
+
+       parent = dl_module_parent_instance(child->dl_inst);
+       if (!parent) return NULL;
+
+       return module_by_data(child->ml, parent->data);
+}
+
 /** Find an existing module instance by its private instance data
  *
+ * @param[in] ml       to search in.
  * @param[in] data     to resolve to module_instance_t.
  * @return
  *     - Module instance matching data.
  *     - NULL if no such module exists.
  */
-module_instance_t *module_by_data(void const *data)
+module_instance_t *module_by_data(module_list_t const *ml, void const *data)
 {
        module_instance_t *mi;
 
-       mi = fr_rb_find(module_instance_data_tree,
+       mi = fr_rb_find(ml->data_tree,
                        &(module_instance_t){
                                .dl_inst = &(dl_module_inst_t){ .data = UNCONST(void *, data) },
                        });
@@ -379,17 +441,23 @@ module_instance_t *module_by_data(void const *data)
  */
 module_thread_instance_t *module_thread(module_instance_t *mi)
 {
-       module_thread_instance_t **array = module_thread_inst_array;
-
-       if (!mi) return NULL;
-
-       fr_assert(mi->number < talloc_array_length(array));
-
-       return array[mi->number];
+       module_thread_instance_t         *ti;
+
+       fr_assert(mi->number < talloc_array_length(module_thread_inst_list));
+       fr_assert(module_list_in_sync);
+       fr_assert_msg(fr_heap_num_elements(module_global_inst_list) == talloc_array_length(module_thread_inst_list),
+                     "mismatch between global module heap (%u entries) and thread local (%zu entries)",
+                     fr_heap_num_elements(module_global_inst_list), talloc_array_length(module_thread_inst_list));
+
+       ti = talloc_get_type_abort(module_thread_inst_list[mi->inst_idx - 1], module_thread_instance_t);
+       fr_assert_msg(ti->mi == mi, "thread/module mismatch thread %s (%p), module %s (%p)",
+                     ti->mi->name, ti->mi, mi->name, mi);
+       return ti;
 }
 
 /** Retrieve module/thread specific instance data for a module
  *
+ * @param[in] ml       Module list module belongs to.
  * @param[in] data     Private instance data of the module.
  *                     Same as what would be provided by
  *                     #module_by_data.
@@ -397,16 +465,22 @@ module_thread_instance_t *module_thread(module_instance_t *mi)
  *     - Thread specific instance data on success.
  *     - NULL if module has no thread instance data.
  */
-module_thread_instance_t *module_thread_by_data(void const *data)
+module_thread_instance_t *module_thread_by_data(module_list_t const *ml, void const *data)
 {
-       module_thread_instance_t        **array = module_thread_inst_array;
-       module_instance_t               *mi = module_by_data(data);
-
+       module_instance_t               *mi = module_by_data(ml, data);
+       module_thread_instance_t        *ti;
        if (!mi) return NULL;
 
-       fr_assert(mi->number < talloc_array_length(array));
+       fr_assert(mi->number < ml->last_number);
+       fr_assert(module_list_in_sync);
+       fr_assert_msg(fr_heap_num_elements(module_global_inst_list) == talloc_array_length(module_thread_inst_list),
+                     "mismatch between global module heap (%u entries) and thread local (%zu entries)",
+                     fr_heap_num_elements(module_global_inst_list), talloc_array_length(module_thread_inst_list));
 
-       return array[mi->number];
+       ti = talloc_get_type_abort(module_thread_inst_list[mi->inst_idx - 1], module_thread_instance_t);
+       fr_assert_msg(ti->mi == mi, "thread/module mismatch thread %s (%p), module %s (%p)",
+                     ti->mi->name, ti->mi, mi->name, mi);
+       return ti;
 }
 
 /** Explicitly free a module if a fatal error occurs during bootstrap
@@ -418,81 +492,119 @@ void module_free(module_instance_t *mi)
        talloc_free(mi);
 }
 
-/** Destructor for module_thread_instance_t array
+/** Remove thread-specific data for a given module list
+ *
+ * Removes all module thread data for the
  */
-static int _module_thread_inst_array_free(module_thread_instance_t **array)
+void modules_thread_detach(module_list_t const *ml)
 {
-       size_t i, len;
+       fr_rb_iter_inorder_t            iter;
+       void                            *instance;
 
-       len = talloc_array_length(array);
-       for (i = 0; i < len; i++) {
-               module_thread_instance_t *ti;
+       /*
+        *      Loop over all the modules in the module list
+        *      finding and extracting their thread specific
+        *      data, and calling their detach methods.
+        */
+       for (instance = fr_rb_iter_init_inorder(&iter, ml->name_tree);
+            instance;
+            instance = fr_rb_iter_next_inorder(&iter)) {
+               module_instance_t               *mi = talloc_get_type_abort(instance, module_instance_t);
 
-               if (!array[i]) continue;
+               talloc_free(module_thread_inst_list[mi->inst_idx - 1]);
+       }
+}
 
-               ti = talloc_get_type_abort(array[i], module_thread_instance_t);
+static int _module_thread_inst_free(module_thread_instance_t *ti)
+{
+       module_instance_t const *mi = ti->mi;
 
-               if (ti->mi) DEBUG4("Worker cleaning up %s thread instance data (%p/%p)",
-                                  ti->mi->module->name, ti, ti->data);
+       module_list_in_sync = false;    /* Help catch anything attempting to do lookups */
 
-               /*
-                *      Check for ti->module is a hack
-                *      and should be removed along with
-                *      starting the instance number at 0
-                */
-               if (ti->mi && ti->mi->module->thread_detach) {
-                       ti->mi->module->thread_detach(&(module_thread_inst_ctx_t const ){
-                                                       .inst = ti->mi->dl_inst,
-                                                       .thread = ti->data,
-                                                       .el = ti->el
-                                                     });
-               }
-               talloc_free(ti);
+       DEBUG4("Worker cleaning up %s thread instance data (%p/%p)",
+              mi->module->name, ti, ti->data);
+
+       if (mi->module->thread_detach) {
+               mi->module->thread_detach(&(module_thread_inst_ctx_t const ){
+                                               .inst = ti->mi->dl_inst,
+                                               .thread = ti->data,
+                                               .el = ti->el
+                                         });
        }
 
+       /*
+        *      Pull the thread instance out of the tree
+        */
+       module_thread_inst_list[ti->mi->inst_idx - 1] = NULL;
        return 0;
 }
 
+/** Free the thread local heap on exit
+ *
+ * All thread local module lists should have been destroyed by this point
+ */
+static void _module_thread_inst_list_free(void *tilp)
+{
+       module_thread_instance_t **til = talloc_get_type_abort(tilp, module_thread_instance_t *);
+       size_t i, len = talloc_array_length(til);
+       unsigned int found = 0;
+
+       for (i = 0; i < len; i++) if (til[i]) found++;
+
+
+       if (!fr_cond_assert_msg(found == 0,
+                               "Thread local array has %u non-null elements remaining on exit.  This is a leak",
+                               found)) {
+               return;
+       }
+
+       talloc_free(til);
+}
+
 /** Creates per-thread instance data for modules which need it
  *
  * Must be called by any new threads before attempting to execute unlang sections.
  *
- * @param[in] ctx      to bind instance tree lifetime to.  Must not be
- *                     shared between multiple threads.
+ * @param[in] ctx      Talloc ctx to bind thread specific data to.
+ * @param[in] ml       Module list to perform thread instantiation for.
  * @param[in] el       Event list servived by this thread.
  * @return
  *     - 0 on success.
  *     - -1 on failure.
  */
-int modules_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el)
+int modules_thread_instantiate(TALLOC_CTX *ctx, module_list_t const *ml, fr_event_list_t *el)
 {
        void                    *instance;
        fr_rb_iter_inorder_t    iter;
 
        /*
-        *      Initialise the thread specific tree if this is the first time through
+        *      Initialise the thread specific tree if this is the
+        *      first time through or if everything else was
+        *      de-initialised.
         */
-       if (!module_thread_inst_array) {
-               MEM(module_thread_inst_array = talloc_zero_array(ctx, module_thread_instance_t *, instance_num + 1));
-               talloc_set_destructor(module_thread_inst_array, _module_thread_inst_array_free);
-       }
+       if (!module_thread_inst_list) {
+               module_thread_instance_t **arr;
 
-       /*
-        *      Index 0 is populated with a catchall entry
-        *      FIXME - This is only required so we can
-        *      fake out module instance data.  As soon
-        *      as we have multiple module lists this can
-        *      be removed.
-        */
-       MEM(module_thread_inst_array[0] = talloc_zero(module_thread_inst_array, module_thread_instance_t));
+               arr = talloc_zero_array(NULL, module_thread_instance_t *,
+                                       fr_heap_num_elements(module_global_inst_list));
+
+               fr_atexit_thread_local(module_thread_inst_list, _module_thread_inst_list_free, arr);
+       }
 
-       for (instance = fr_rb_iter_init_inorder(&iter, module_instance_name_tree);
+       for (instance = fr_rb_iter_init_inorder(&iter, ml->name_tree);
             instance;
             instance = fr_rb_iter_next_inorder(&iter)) {
                module_instance_t               *mi = talloc_get_type_abort(instance, module_instance_t);
                module_thread_instance_t        *ti;
+               TALLOC_CTX                      *our_ctx = ctx;
 
-               MEM(ti = talloc_zero(module_thread_inst_array, module_thread_instance_t));
+               /*
+                *      Check the list pointers are ok
+                */
+               (void)talloc_get_type_abort(mi->ml, module_list_t);
+
+               MEM(ti = talloc_zero(our_ctx, module_thread_instance_t));
+               talloc_set_destructor(ti, _module_thread_inst_free);
                ti->el = el;
                ti->mi = mi;
 
@@ -504,7 +616,10 @@ int modules_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el)
                         *      talloc_get_type_abort() on it...
                         */
                        if (!mi->module->thread_inst_type) {
-                               talloc_set_name(ti->data, "rlm_%s_thread_t", mi->module->name);
+                               talloc_set_name(ti->data, "%s_%s_thread_t",
+                                               fr_table_str_by_value(dl_module_type_prefix,
+                                                                     mi->dl_inst->module->type, "<INVALID>"),
+                                               mi->module->name);
                        } else {
                                talloc_set_name_const(ti->data, mi->module->thread_inst_type);
                        }
@@ -514,40 +629,33 @@ int modules_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el)
                if (mi->module->thread_instantiate &&
                    mi->module->thread_instantiate(MODULE_THREAD_INST_CTX(mi->dl_inst, ti->data, el)) < 0) {
                        PERROR("Thread instantiation failed for module \"%s\"", mi->name);
-                       TALLOC_FREE(module_thread_inst_array);
+                       modules_thread_detach(ml);
+                       TALLOC_FREE(module_thread_inst_list);
                        return -1;
                }
 
-               fr_assert(mi->number < talloc_array_length(module_thread_inst_array));
-               module_thread_inst_array[mi->number] = ti;
+               module_thread_inst_list[ti->mi->inst_idx - 1] = ti;
        }
 
        return 0;
 }
 
-/** Explicitly call thread_detach and free any module thread instances
- *
- * Call this function if the module thread instances need to be free explicitly before
- * another resource like the even loop is freed.
- */
-void modules_thread_detach(void)
-{
-       if (!module_thread_inst_array) return;
-       TALLOC_FREE(module_thread_inst_array);
-}
-
-/** Complete module setup by calling its instantiate function
+/** Manually complete module setup by calling its instantiate function
  *
  * @param[in] instance of module to complete instantiation for.
  * @return
  *     - 0 on success.
  *     - -1 on failure.
  */
-int module_instantiate(void *instance)
+int module_instantiate(module_instance_t *instance)
 {
        module_instance_t *mi = talloc_get_type_abort(instance, module_instance_t);
+       CONF_SECTION *cs = mi->dl_inst->conf;
 
-       if (mi->instantiated) return 0;
+       /*
+        *      We only instantiate modules in the bootstrapped state
+        */
+       if (mi->state != MODULE_INSTANCE_BOOTSTRAPPED) return 0;
 
        if (fr_command_register_hook(NULL, mi->name, mi, module_cmd_table) < 0) {
                PERROR("Failed registering radmin commands for module %s", mi->name);
@@ -565,7 +673,10 @@ int module_instantiate(void *instance)
         *      Call the instantiate method, if any.
         */
        if (mi->module->instantiate) {
-               cf_log_debug(mi->dl_inst->conf, "Instantiating module \"%s\"", mi->name);
+               cf_log_debug(cs, "Instantiating %s_%s \"%s\"",
+                            fr_table_str_by_value(dl_module_type_prefix, mi->dl_inst->module->type, "<INVALID>"),
+                            mi->dl_inst->module->common->name,
+                            mi->name);
 
                /*
                 *      Call the module's instantiation routine.
@@ -580,18 +691,10 @@ int module_instantiate(void *instance)
        /*
         *      If we're threaded, check if the module is thread-safe.
         *
-        *      If it isn't, we create a mutex.
+        *      If it isn't, we init the mutex.
         */
-       if ((mi->module->type & MODULE_TYPE_THREAD_UNSAFE) != 0) {
-               mi->mutex = talloc_zero(mi, pthread_mutex_t);
-
-               /*
-                *      Initialize the mutex.
-                */
-               pthread_mutex_init(mi->mutex, NULL);
-       }
-
-       mi->instantiated = true;
+       if ((mi->module->type & MODULE_TYPE_THREAD_UNSAFE) != 0) pthread_mutex_init(&mi->mutex, NULL);
+       mi->state = MODULE_INSTANCE_INSTANTIATED;
 
        return 0;
 }
@@ -601,95 +704,129 @@ int module_instantiate(void *instance)
  * Allows the module to initialise connection pools, and complete any registrations that depend on
  * attributes created during the bootstrap phase.
  *
- * @param[in] root of the server configuration.
+ * @param[in] ml containing modules ot instantiate.
  * @return
  *     - 0 on success.
  *     - -1 on failure.
  */
-int modules_instantiate(UNUSED CONF_SECTION *root)
+int modules_instantiate(module_list_t const *ml)
 {
        void                    *instance;
        fr_rb_iter_inorder_t    iter;
 
-       DEBUG2("#### Instantiating modules ####");
+       DEBUG2("#### Instantiating %s modules ####", ml->name);
 
-       for (instance = fr_rb_iter_init_inorder(&iter, module_instance_name_tree);
+       for (instance = fr_rb_iter_init_inorder(&iter, ml->name_tree);
             instance;
             instance = fr_rb_iter_next_inorder(&iter)) {
-               if (module_instantiate(instance) < 0) return -1;
+               module_instance_t *mi = talloc_get_type_abort(instance, module_instance_t);
+               if (mi->state != MODULE_INSTANCE_BOOTSTRAPPED) continue;
+
+               if (module_instantiate(mi) < 0) return -1;
        }
 
        return 0;
 }
 
-/** Recursive component of module_instance_name
+/** Manually complete module bootstrap by calling its instantiate function
+ *
+ * - Parse the module configuration.
+ * - Call the modules "bootstrap" method.
  *
+ * @param[in] mi       Module instance to bootstrap.
+ * @return
+ *      - 0 on success.
+ *     - -1 on failure.
  */
-static size_t _module_instance_name(TALLOC_CTX *ctx, char **out, module_instance_t const *parent, size_t need)
+int module_bootstrap(module_instance_t *mi)
 {
-       if (parent) {
-               size_t  our_len = talloc_array_length(parent->name) - 1;
-               char    *p, *end;
-               size_t  used;
-
-               used = _module_instance_name(ctx, out,
-                                            parent->dl_inst->parent ?
-                                            module_by_data(parent->dl_inst->parent->data) : NULL,
-                                            (need + our_len + 1));     /* +1 for '.' */
-               p = (*out) + used;
-               end = (*out) + talloc_array_length(*out);
+       /*
+        *      We only bootstrap modules in the init state
+        */
+       if (mi->state != MODULE_INSTANCE_INIT) return 0;
 
-               strlcpy(p, parent->name, end - p);
-               p += our_len;
+       /*
+        *      Bootstrap the module.
+        *      This must be done last so that the
+        *      module can find its module_instance_t
+        *      in the trees if it needs to bootstrap
+        *      submodules.
+        */
+       if (mi->module->bootstrap) {
+               CONF_SECTION *cs = mi->dl_inst->conf;
 
-               *p++ = '.';     /* Add the separator */
+               cf_log_debug(cs, "Bootstrapping %s_%s \"%s\"",
+                            fr_table_str_by_value(dl_module_type_prefix, mi->dl_inst->module->type, "<INVALID>"),
+                            mi->dl_inst->module->common->name,
+                            mi->name);
 
-               return (p - (*out));
+               if (mi->module->bootstrap(MODULE_INST_CTX(mi->dl_inst)) < 0) {
+                       cf_log_err(cs, "Bootstrap failed for module \"%s\"", mi->name);
+                       return -1;
+               }
        }
+       mi->state = MODULE_INSTANCE_BOOTSTRAPPED;
 
-       /*
-        *      Head on back up the stack
-        */
-       *out = talloc_array(ctx, char, need + 1);
+       return 0;
+}
+
+/** Bootstrap any modules which have not been bootstrapped already
+ *
+ * Allows the module to initialise connection pools, and complete any registrations that depend on
+ * attributes created during the bootstrap phase.
+ *
+ * @param[in] ml containing modules to bootstrap.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int modules_bootstrap(module_list_t const *ml)
+{
+       void                    *instance;
+       fr_rb_iter_inorder_t    iter;
+
+       DEBUG2("#### Bootstrapping %s modules ####", ml->name);
+
+       for (instance = fr_rb_iter_init_inorder(&iter, ml->name_tree);
+            instance;
+            instance = fr_rb_iter_next_inorder(&iter)) {
+               module_instance_t *mi = talloc_get_type_abort(instance, module_instance_t);
+               if (mi->state != MODULE_INSTANCE_INIT) continue;
+
+               if (module_bootstrap(mi) < 0) return -1;
+       }
 
        return 0;
 }
 
-/** Generate a module name from the module's section name and its parents
+/** Generate a module name from the module's name and its parents
  *
- * @param[in] ctx      Where to allocate the module name.
- * @param[out] out     Where to write a pointer to the instance name.
- * @param[in] parent   of the module.
- * @param[in] cs       module's configuration section.
+ * @param[in] ctx              Where to allocate the module name.
+ * @param[out] out             Where to write a pointer to the instance name.
+ * @param[in] parent           of the module.
+ * @param[in] inst_name                module's instance name.
  */
-static size_t module_instance_name(TALLOC_CTX *ctx, char **out, module_instance_t const *parent, CONF_SECTION *cs)
+static fr_slen_t module_instance_name(TALLOC_CTX *ctx, char **out, module_list_t const *ml,
+                                     module_instance_t const *parent, char const *inst_name)
 {
-       char const      *name1, *inst_name;
-       size_t          our_len;
-       char            *p, *end;
-       size_t          used;
+       fr_sbuff_t *agg;
 
-       name1 = cf_section_name1(cs);
-       inst_name = cf_section_name2(cs);
-       if (!inst_name) inst_name = name1;
+       FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 64, 256);
 
-       our_len = talloc_array_length(inst_name) - 1;
+       while (parent) {
+               FR_SBUFF_IN_STRCPY_RETURN(agg, parent->name);
+               FR_SBUFF_IN_CHAR_RETURN(agg, '.');
 
-       used = _module_instance_name(ctx, out, parent, our_len);
-       p = (*out) + used;
-       end = (*out) + talloc_array_length(*out);
+               if (!parent->dl_inst->parent) break;
 
-       strlcpy(p, inst_name, end - p); /* \0 terminates */
-       p += our_len;
+               parent = module_by_data(ml, parent->dl_inst->parent->data);
+       }
 
-       /*
-        *      Check we used the entire buffer
-        *      ...because recursive code still makes
-        *      my head hurt.
-        */
-       fr_assert((size_t)(p - (*out)) == (talloc_array_length(*out) - 1));
+       FR_SBUFF_IN_STRCPY_RETURN(agg, inst_name);
 
-       return (p - (*out));
+       MEM(*out = talloc_bstrndup(ctx, fr_sbuff_start(agg), fr_sbuff_used(agg)));
+
+       return fr_sbuff_used(agg);
 
 }
 
@@ -700,17 +837,30 @@ static size_t module_instance_name(TALLOC_CTX *ctx, char **out, module_instance_
  */
 static int _module_instance_free(module_instance_t *mi)
 {
+       module_list_t *ml = mi->ml;
+
        DEBUG3("Freeing %s (%p)", mi->name, mi);
 
-       if (mi->in_name_tree) if (!fr_cond_assert(fr_rb_delete(module_instance_name_tree, mi))) return 1;
-       if (mi->in_data_tree) if (!fr_cond_assert(fr_rb_delete(module_instance_data_tree, mi))) return 1;
-       if (mi->mutex) {
+       if (fr_heap_entry_inserted(mi->inst_idx) && !fr_cond_assert(fr_heap_extract(&module_global_inst_list, mi) == 0)) return 1;
+       if (fr_rb_node_inline_in_tree(&mi->name_node) && !fr_cond_assert(fr_rb_delete(ml->name_tree, mi))) return 1;
+       if (fr_rb_node_inline_in_tree(&mi->data_node) && !fr_cond_assert(fr_rb_delete(ml->data_tree, mi))) return 1;
+
+       /*
+        *      mi->module may be NULL if we failed loading the module
+        */
+       if (mi->module && ((mi->module->type & MODULE_TYPE_THREAD_UNSAFE) != 0)) {
+#ifndef NDEBUG
                /*
-                *      FIXME
-                *      The mutex MIGHT be locked...
-                *      we'll check for that later, I guess.
+                *      If the mutex is locked that means
+                *      the server exited without cleaning
+                *      up requests.
+                *
+                *      Assert that the mutex is not held.
                 */
-               pthread_mutex_destroy(mi->mutex);
+               fr_assert(pthread_mutex_trylock(&mi->mutex) == 0);
+               pthread_mutex_unlock(&mi->mutex);
+#endif
+               pthread_mutex_destroy(&mi->mutex);
        }
 
        /*
@@ -738,82 +888,118 @@ static int _module_instance_free(module_instance_t *mi)
        return 0;
 }
 
-/** Bootstrap a module
+/** Parse the configuration associated with a module
+ *
+ * @param[in] mi       To parse the configuration for.
+ * @param[in] mod_conf To parse.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int module_conf_parse(module_instance_t *mi, CONF_SECTION *mod_conf)
+{
+       if (dl_module_conf_parse(mi->dl_inst, mod_conf) < 0) return -1;
+
+       return 0;
+}
+
+/** Allocate a new module and add it to a module list for later bootstrap/instantiation
  *
- * Load the module shared library, allocate instance data for it,
- * parse the module configuration, and call the modules "bootstrap" method.
+ * - Load the module shared library.
+ * - Allocate instance data for it.
  *
+ * @param[in] ml       To add module to.
+ * @param[in] parent   of the module being bootstrapped, if this is a submodule.
+ *                     If this is not a submodule parent must be NULL.
  * @param[in] type     What type of module we're loading.  Determines the prefix
  *                     added to the library name.  Should be one of:
  *                     - DL_MODULE_TYPE_MODULE - Standard backend module.
  *                     - DL_MODULE_TYPE_SUBMODULE - Usually a driver for a backend module.
+ *                     - DL_MODULE_TYPE_PROTO - A module associated with a listen section.
  *                     - DL_MODULE_TYPE_PROCESS - Protocol state machine bound to a virtual server.
- * @param[in] parent   of the module being bootstrapped, if this is a submodule.
- *                     If this is not a submodule parent must be NULL.
- * @param[in] cs       containing the configuration for this module or submodule.
+ * @param[in] mod_name The name of this module, i.e. 'redis' for 'rlm_redis'.
+ * @param[in] inst_name        Instance name for this module, i.e. "aws_redis_01".
+ *                     The notable exception is if this is a submodule, in which case
+ *                     inst_name is usually the mod_name.
  * @return
  *     - A new module instance handle, containing the module's public interface,
  *       and private instance data.
  *     - NULL on error.
  */
-module_instance_t *module_bootstrap(dl_module_type_t type, module_instance_t const *parent, CONF_SECTION *cs)
+module_instance_t *module_alloc(module_list_t *ml,
+                               module_instance_t const *parent,
+                               dl_module_type_t type, char const *mod_name, char const *inst_name)
 {
-       char                    *inst_name = NULL;
+       char                    *qual_inst_name = NULL;
        module_instance_t       *mi;
-       char const              *name1 = cf_section_name1(cs);
 
        fr_assert((type == DL_MODULE_TYPE_MODULE) ||
                  (parent && (type == DL_MODULE_TYPE_SUBMODULE)) ||
+                 (type == DL_MODULE_TYPE_PROTO) ||
                  (type == DL_MODULE_TYPE_PROCESS));
 
-       module_instance_name(NULL, &inst_name, parent, cs);
+       /*
+        *      Takes the inst_name and adds qualifiers
+        *      if this is a submodule.
+        */
+       module_instance_name(NULL, &qual_inst_name, ml, parent, inst_name);
 
        /*
         *      See if the module already exists.
         */
-       mi = module_by_name(parent, inst_name);
+       mi = module_by_name(ml, parent, qual_inst_name);
        if (mi) {
-               ERROR("Duplicate module \"%s\" in file %s[%d] and file %s[%d]",
-                     inst_name,
-                     cf_filename(cs),
-                     cf_lineno(cs),
-                     cf_filename(mi->dl_inst->conf),
-                     cf_lineno(mi->dl_inst->conf));
-               talloc_free(inst_name);
+               /*
+                *      We may not have configuration data yet
+                *      for the duplicate module.
+                */
+               if (mi->dl_inst->conf) {
+                       ERROR("Duplicate %s_%s instance \"%s\", previous instance defined at %s[%d]",
+                             fr_table_str_by_value(dl_module_type_prefix, mi->dl_inst->module->type, "<INVALID>"),
+                             mi->dl_inst->module->common->name,
+                             qual_inst_name,
+                             cf_filename(mi->dl_inst->conf),
+                             cf_lineno(mi->dl_inst->conf));
+
+               } else {
+                       ERROR("Duplicate %s_%s instance \"%s\"",
+                             fr_table_str_by_value(dl_module_type_prefix, mi->dl_inst->module->type, "<INVALID>"),
+                             mi->dl_inst->module->common->name,
+                             qual_inst_name);
+               }
+               talloc_free(qual_inst_name);
                return NULL;
        }
 
-       MEM(mi = talloc_zero(parent ? parent : instance_ctx, module_instance_t));
+       MEM(mi = talloc_zero(parent ? (void const *)parent : (void const *)ml, module_instance_t));
        talloc_set_destructor(mi, _module_instance_free);
 
-       if (dl_module_instance(mi, &mi->dl_inst, cs,
-                              parent ? parent->dl_inst : NULL,
-                              name1,
-                              type) < 0) {
+       if (dl_module_instance(mi, &mi->dl_inst, parent ? parent->dl_inst : NULL,
+                              type, mod_name, qual_inst_name) < 0) {
        error:
-               mi->name = inst_name;   /* Assigned purely for debug log output when mi is freed */
+               mi->name = qual_inst_name;      /* Assigned purely for debug log output when mi is freed */
                talloc_free(mi);
-               talloc_free(inst_name);
+               talloc_free(qual_inst_name);
                return NULL;
        }
        fr_assert(mi->dl_inst);
 
-       mi->name = talloc_typed_strdup(mi, inst_name);
-       talloc_free(inst_name); /* Avoid stealing */
+       mi->name = talloc_typed_strdup(mi, qual_inst_name);
+       talloc_free(qual_inst_name);    /* Avoid stealing */
 
        mi->module = (module_t const *)mi->dl_inst->module->common;
        if (!mi->module) {
-               cf_log_err(cs, "Missing public structure for \"%s\"", inst_name);
+               ERROR("Missing public structure for \"%s\"", qual_inst_name);
                talloc_free(mi);
                return NULL;
        }
-       mi->number = instance_num++;
+       mi->number = ml->last_number++;
+       mi->ml = ml;
 
        /*
         *      Remember the module for later.
         */
-       if (!fr_cond_assert(fr_rb_insert(module_instance_name_tree, mi))) goto error;
-       mi->in_name_tree = true;
+       if (!fr_cond_assert(fr_rb_insert(ml->name_tree, mi))) goto error;
 
        /*
         *      Allow modules to get at their own
@@ -821,78 +1007,91 @@ module_instance_t *module_bootstrap(dl_module_type_t type, module_instance_t con
         *      looking up thread specific data
         *      and for bootstrapping submodules.
         */
-       if (mi->dl_inst->data) {
-               if (!fr_cond_assert(fr_rb_insert(module_instance_data_tree, mi))) goto error;
-               mi->in_data_tree = true;
-       }
+       if (mi->dl_inst->data && !fr_cond_assert(fr_rb_insert(ml->data_tree, mi))) goto error;
 
        /*
-        *      Do this after inserting the module instance into the tree
+        *      ...and finally insert the module
+        *      into the global heap so we can
+        *      get common thread-local indexes.
         */
-       if (dl_module_conf_parse(mi->dl_inst) < 0) {
-               TALLOC_FREE(mi);
-               return NULL;
-       }
+       if (fr_heap_insert(&module_global_inst_list, mi) < 0) goto error;
+
+       return mi;
+}
+
+/** Free all modules loaded by the server
+ *
+ * @param[in] ml       Module list being freed.
+ * @return 0
+ */
+static int _module_list_free(module_list_t *ml)
+{
+       fr_rb_iter_inorder_t    iter;
+       module_instance_t       *mi;
 
        /*
-        *      Bootstrap the module.
-        *      This must be done last so that the
-        *      module can find its module_instance_t
-        *      in the trees if it needs to bootstrap
-        *      submodules.
+        *      We explicitly free modules so that
+        *      they're done in a stable order.
         */
-       if (mi->module->bootstrap) {
-               cf_log_debug(mi->dl_inst->conf, "Bootstrapping module \"%s\"", mi->name);
-
-               if (mi->module->bootstrap(MODULE_INST_CTX(mi->dl_inst)) < 0) {
-                       cf_log_err(cs, "Bootstrap failed for module \"%s\"", mi->name);
-                       talloc_free(mi);
-                       return NULL;
-               }
+       for (mi = fr_rb_iter_init_inorder(&iter, ml->name_tree);
+            mi;
+            mi = fr_rb_iter_next_inorder(&iter)) {
+               fr_rb_iter_delete_inorder(&iter);       /* Keeps the iterator sane */
+               talloc_free(mi);
        }
 
-       return mi;
+       return 0;
 }
 
-/** Free all modules loaded by the server
+/** Allocate a new module list
+ *
+ * This is used to instantiate and destroy modules in distinct phases
+ * for example, we may need to load all proto modules before rlm modules.
+ *
+ * If the list is freed all module instance data will be freed.
+ * If no more instances of the module exist the module be unloaded.
+ *
+ * @param[in] ctx      To allocate the list in.
+ * @return A new module list.
  */
-void modules_free(void)
+module_list_t *module_list_alloc(TALLOC_CTX *ctx, char const *name)
 {
-       if (module_instance_name_tree) {
-               fr_rb_iter_inorder_t    iter;
-               module_instance_t       *mi;
+       module_list_t *ml;
 
-               for (mi = fr_rb_iter_init_inorder(&iter, module_instance_name_tree);
-                    mi;
-                    mi = fr_rb_iter_next_inorder(&iter)) {
-                       mi->in_name_tree = false; /* about to be deleted */
-                       mi->in_data_tree = false;
+       MEM(ml = talloc_zero(ctx, module_list_t));
+       talloc_set_destructor(ml, _module_list_free);
 
-                       fr_rb_iter_delete_inorder(&iter);
-                       fr_rb_remove(module_instance_data_tree, mi);
+       MEM(ml->name = talloc_typed_strdup(ml, name));
+       MEM(ml->name_tree = fr_rb_inline_alloc(ml, module_instance_t, name_node, module_instance_name_cmp, NULL));
+       MEM(ml->data_tree = fr_rb_inline_alloc(ml, module_instance_t, data_node, module_instance_data_cmp, NULL));
 
-                       talloc_free(mi);
-               }
-               TALLOC_FREE(module_instance_name_tree);
-       }
-       TALLOC_FREE(module_instance_data_tree);
-       modules_rlm_free();
-       TALLOC_FREE(instance_ctx);
+       return ml;
 }
 
-/** Allocate the global module tree
+static void _module_global_list_init(void *uctx)
+{
+       dl_modules = dl_module_loader_init(uctx);
+       MEM(module_global_inst_list = fr_heap_alloc(NULL, _module_instance_global_cmp, module_instance_t, inst_idx, 256));
+}
+
+static void _module_global_list_free(UNUSED void *uctx)
+{
+       if (!fr_cond_assert_msg(fr_heap_num_elements(module_global_inst_list) == 0,
+                               "Global module heap has %u elements remaining on exit.  This is a leak",
+                               fr_heap_num_elements(module_global_inst_list))) return;
+       TALLOC_FREE(module_global_inst_list);
+       TALLOC_FREE(dl_modules);
+}
+
+/** Perform global initialisation for modules
  *
- * This allocates all the trees necessary to hold module name and module instance data,
- * as well as the main ctx all module data gets allocated in.
  */
-int modules_init(void)
+void modules_init(char const *lib_dir)
 {
-       MEM(module_instance_name_tree = fr_rb_inline_alloc(NULL, module_instance_t, name_node,
-                                                          module_instance_name_cmp, NULL));
-       MEM(module_instance_data_tree = fr_rb_inline_alloc(NULL, module_instance_t, data_node,
-                                                          module_instance_data_cmp, NULL));
-       modules_rlm_init();
-       instance_ctx = talloc_init("module instance context");
-
-       return 0;
+       /*
+        *      Create the global module heap we use for
+        *      common indexes in the thread-specific
+        *      heaps.
+        */
+       fr_atexit_global_once(_module_global_list_init, _module_global_list_free, UNCONST(char *, lib_dir));
 }
index def8bd30188c145406a420c3d9112808b51e109b..41f0efdb68564a751950e1b21f75eb5b94d3f2c6 100644 (file)
@@ -19,8 +19,9 @@
  * $Id$
  *
  * @file lib/server/module.h
- * @brief Interface to the RADIUS module system.
+ * @brief Interface to the FreeRADIUS module system.
  *
+ * @copyright 2022 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
  * @copyright 2013 The FreeRADIUS server project
  */
 RCSIDH(modules_h, "$Id$")
@@ -40,6 +41,7 @@ typedef struct module_s                               module_t;
 typedef struct module_method_names_s           module_method_names_t;
 typedef struct module_instance_s               module_instance_t;
 typedef struct module_thread_instance_s                module_thread_instance_t;
+typedef struct module_list_t                   module_list_t;
 
 #define MODULE_TYPE_THREAD_SAFE                (0 << 0)        //!< Module is threadsafe.
 #define MODULE_TYPE_THREAD_UNSAFE      (1 << 0)        //!< Module is not threadsafe.
@@ -143,6 +145,15 @@ struct module_s {
        size_t                          thread_inst_size;
 };
 
+/** What state the module instance is currently in
+ *
+ */
+typedef enum {
+       MODULE_INSTANCE_INIT = 0,
+       MODULE_INSTANCE_BOOTSTRAPPED,
+       MODULE_INSTANCE_INSTANTIATED
+} module_instance_state_t;
+
 /** Per instance data
  *
  * Per-instance data structure, to correlate the modules with the
@@ -150,9 +161,17 @@ struct module_s {
  * data structures.
  */
 struct module_instance_s {
+       fr_heap_index_t                 inst_idx;       //!< Entry in the bootstrap/instantiation heap.
+                                                       //!< should be an identical value to the thread-specific
+                                                       ///< data for this module.
+
        fr_rb_node_t                    name_node;      //!< Entry in the name tree.
        fr_rb_node_t                    data_node;      //!< Entry in the data tree.
 
+       module_list_t                   *ml;            //!< Module list this instance belongs to.
+
+       uint32_t                        number;         //!< Unique module number.
+
        char const                      *name;          //!< Instance name e.g. user_database.
 
        dl_module_inst_t                *dl_inst;       //!< Structure containing the module's instance data,
@@ -164,13 +183,10 @@ struct module_instance_s {
                                                        ///< This exports module methods, i.e. the functions
                                                        ///< which allow the module to perform actions.
 
-       pthread_mutex_t                 *mutex;         //!< Used prevent multiple threads entering a thread
+       pthread_mutex_t                 mutex;          //!< Used prevent multiple threads entering a thread
                                                        ///< unsafe module simultaneously.
 
-       uint32_t                        number;         //!< unique module number.  Used as a lookup into the
-                                                       ///< thread instance array.
-
-       bool                            instantiated;   //!< Whether the module has been instantiated yet.
+       module_instance_state_t         state;          //!< What's been done with this module so far.
 
        /** @name Return code overrides
         * @{
@@ -184,13 +200,6 @@ struct module_instance_s {
        unlang_actions_t                actions;        //!< default actions and retries.
 
        /** @} */
-
-       /** @name Tree insertion tracking
-        * @{
-        */
-       bool                            in_name_tree;   //!< Whether this is in the name lookup tree.
-       bool                            in_data_tree;   //!< Whether this is in the data lookup tree.
-       /** @} */
 };
 
 /** Per thread per instance data
@@ -198,6 +207,10 @@ struct module_instance_s {
  * Stores module and thread specific data.
  */
 struct module_thread_instance_s {
+       fr_heap_index_t                 inst_idx;       //!< Entry in the thread-specific bootstrap heap.
+                                                       ///< Should be an identical value to the global
+                                                       ///< instance data for the same module.
+
        void                            *data;          //!< Thread specific instance data.
 
        fr_event_list_t                 *el;            //!< Event list associated with this thread.
@@ -208,6 +221,18 @@ struct module_thread_instance_s {
        uint64_t                        active_callers; //! number of active callers.  i.e. number of current yields
 };
 
+/** A list of modules
+ *
+ * This allows modules to be instantiated and freed in phases,
+ * i.e. proto modules before rlm modules.
+ */
+struct module_list_t {
+       uint32_t                        last_number;    //!< Last identifier assigned to a module instance.
+       char const                      *name;          //!< Friendly list identifier.
+       fr_rb_tree_t                    *name_tree;     //!< Modules indexed by name.
+       fr_rb_tree_t                    *data_tree;     //!< Modules indexed by data.
+};
+
 /** Map string values to module state method
  *
  */
@@ -216,7 +241,6 @@ typedef struct {
        module_method_t                 func;           //!< State function.
 } module_state_func_table_t;
 
-
 /** @name Callbacks for the CONF_PARSER
  *
  * @{
@@ -229,14 +253,16 @@ int               module_submodule_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent,
  *
  * @{
  */
-module_instance_t      *module_by_name(module_instance_t const *parent, char const *asked_name);
+module_instance_t      *module_parent(module_instance_t const *child);
 
-module_instance_t      *module_by_data(void const *data);
+module_instance_t      *module_by_name(module_list_t const *ml, module_instance_t const *parent, char const *asked_name)
+                       CC_HINT(nonnull(1,3));
 
-module_thread_instance_t *module_thread(module_instance_t *mi);
+module_instance_t      *module_by_data(module_list_t const *ml, void const *data);
 
-module_thread_instance_t *module_thread_by_data(void const *data);
+module_thread_instance_t *module_thread(module_instance_t *mi);
 
+module_thread_instance_t *module_thread_by_data(module_list_t const *ml, void const *data);
 /** @} */
 
 /** @name Module and module thread initialisation and instantiation
@@ -245,18 +271,28 @@ module_thread_instance_t *module_thread_by_data(void const *data);
  */
 void           module_free(module_instance_t *mi);
 
-int            modules_init(void);
+void           modules_thread_detach(module_list_t const *ml);
+
+int            modules_thread_instantiate(TALLOC_CTX *ctx, module_list_t const *ml, fr_event_list_t *el) CC_HINT(nonnull);
+
+int            module_instantiate(module_instance_t *mi) CC_HINT(nonnull);
+
+int            modules_instantiate(module_list_t const *ml) CC_HINT(nonnull);
+
+int            module_bootstrap(module_instance_t *mi) CC_HINT(nonnull);
 
-void           modules_free(void);
+int            modules_bootstrap(module_list_t const *ml) CC_HINT(nonnull);
 
-int            modules_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el) CC_HINT(nonnull);
+int            module_conf_parse(module_instance_t *mi, CONF_SECTION *mod_cs) CC_HINT(nonnull);
 
-void           modules_thread_detach(void);
+module_instance_t *module_alloc(module_list_t *ml,
+                               module_instance_t const *parent,
+                               dl_module_type_t type, char const *mod_name, char const *inst_name)
+                               CC_HINT(nonnull(1));
 
-int            modules_instantiate(CONF_SECTION *root) CC_HINT(nonnull);
+module_list_t  *module_list_alloc(TALLOC_CTX *ctx, char const *name);
 
-module_instance_t *module_bootstrap(dl_module_type_t type, module_instance_t const *parent, CONF_SECTION *cs)
-               CC_HINT(nonnull(3));
+void           modules_init(char const *lib_dir);
 /** @} */
 
 #ifdef __cplusplus
index 8b2559d73a0fd2cb9cc04da6bf4d6e134e55217c..b25829c5802339978b51550b8976c275e7da1e95 100644 (file)
@@ -33,6 +33,7 @@ RCSID("$Id$")
 #include <freeradius-devel/server/module_rlm.h>
 #include <freeradius-devel/server/pair.h>
 #include <freeradius-devel/server/virtual_servers.h>
+#include <freeradius-devel/util/atexit.h>
 
 /** Lookup virtual module by name
  */
@@ -65,6 +66,11 @@ char const *section_type_value[MOD_COUNT] = {
        "post-auth"
 };
 
+/** Global module list for all backend modules
+ *
+ */
+static module_list_t *rlm_modules;
+
 /** Initialise a module specific exfile handle
  *
  * @see exfile_init
@@ -175,14 +181,14 @@ int module_rlm_sibling_section_find(CONF_SECTION **out, CONF_SECTION *module, ch
         *      instantiation order issues.
         */
        inst_name = cf_pair_value(cp);
-       mi = module_by_name(NULL, inst_name);
+       mi = module_by_name(rlm_modules, NULL, inst_name);
        if (!mi) {
                cf_log_err(cp, "Unknown module instance \"%s\"", inst_name);
 
                return -1;
        }
 
-       if (!mi->instantiated) {
+       if (mi->state != MODULE_INSTANCE_INSTANTIATED) {
                CONF_SECTION *parent = module;
 
                /*
@@ -197,14 +203,14 @@ int module_rlm_sibling_section_find(CONF_SECTION **out, CONF_SECTION *module, ch
                        parent = tmp;
                } while (true);
 
-               module_instantiate(module_by_name(NULL, inst_name));
+               module_instantiate(module_by_name(rlm_modules, NULL, inst_name));
        }
 
        /*
         *      Remove the config data we added for loop
         *      detection.
         */
-       cf_data_remove(module, cd);
+       cf_data_remove_by_data(module, cd);
 
        /*
         *      Check the module instances are of the same type.
@@ -450,7 +456,7 @@ module_instance_t *module_rlm_by_name_and_method(module_method_t *method, rlm_co
         *      Module names are allowed to contain '.'
         *      so we search for the bare module name first.
         */
-       mi = module_by_name(NULL, name);
+       mi = module_by_name(rlm_modules, NULL, name);
        if (mi) {
                virtual_server_method_t const   *allowed_list;
 
@@ -610,7 +616,7 @@ module_instance_t *module_rlm_by_name_and_method(module_method_t *method, rlm_co
        do {
                *p = '\0';
 
-               mi = module_by_name(NULL, inst_name);
+               mi = module_by_name(rlm_modules, NULL, inst_name);
                if (mi) break;
 
                /*
@@ -808,6 +814,16 @@ CONF_SECTION *module_rlm_by_name_virtual(char const *asked_name)
        return inst->cs;
 }
 
+module_thread_instance_t *module_rlm_thread_by_data(void const *data)
+{
+       return module_thread_by_data(rlm_modules, data);
+}
+
+module_instance_t *module_rlm_by_name(module_instance_t const *parent, char const *asked_name)
+{
+       return module_by_name(rlm_modules, parent, asked_name);
+}
+
 /** Create a virtual module.
  *
  * @param[in] cs       that defines the virtual module.
@@ -852,7 +868,7 @@ static int module_rlm_bootstrap_virtual(CONF_SECTION *cs)
        /*
         *      Ensure that the module doesn't exist.
         */
-       mi = module_by_name(NULL, name);
+       mi = module_by_name(rlm_modules, NULL, name);
        if (mi) {
                ERROR("Duplicate module \"%s\" in file %s[%d] and file %s[%d]",
                      name,
@@ -928,6 +944,51 @@ static int module_rlm_bootstrap_virtual(CONF_SECTION *cs)
        return 0;
 }
 
+/** Generic CONF_PARSER func for loading drivers
+ *
+ */
+int module_rlm_submodule_parse(TALLOC_CTX *ctx, void *out, void *parent,
+                              CONF_ITEM *ci, CONF_PARSER const *rule)
+{
+       CONF_PARSER our_rule = *rule;
+
+       our_rule.uctx = &rlm_modules;
+
+       return module_submodule_parse(ctx, out, parent, ci, &our_rule);
+}
+
+/** Frees thread-specific data for all registered backend modules
+ *
+ */
+void modules_rlm_thread_detach(void)
+{
+       modules_thread_detach(rlm_modules);
+}
+
+/** Allocates thread-specific data for all registered backend modules
+ *
+ * @param[in] ctx      To allocate any thread-specific data in.
+ * @param[in] el       to register events.
+ * @return
+ *     - 0 if all modules were instantiated successfully.
+ *     - -1 if a module failed instantiation.
+ */
+int modules_rlm_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el)
+{
+       return modules_thread_instantiate(ctx, rlm_modules, el);
+}
+
+/** Performs the instantiation phase for all backend modules
+ *
+ * @return
+ *     - 0 if all modules were instantiated successfully.
+ *     - -1 if a module failed instantiation.
+ */
+int modules_rlm_instantiate(void)
+{
+       return modules_instantiate(rlm_modules);
+}
+
 /** Bootstrap modules and virtual modules
  *
  * Parse the module config sections, and load and call each module's init() function.
@@ -1009,8 +1070,24 @@ int modules_rlm_bootstrap(CONF_SECTION *root)
                        continue;
                }
 
-               mi = module_bootstrap(DL_MODULE_TYPE_MODULE, NULL, subcs);
-               if (!mi) return -1;
+               mi = module_alloc(rlm_modules, NULL, DL_MODULE_TYPE_MODULE, name, dl_module_inst_name_from_conf(subcs));
+               if (unlikely(mi == NULL)) {
+                       cf_log_perr(subcs, "Failed loading module");
+                       return -1;
+
+               }
+
+               if (module_conf_parse(mi, subcs) < 0) {
+                       cf_log_perr(subcs, "Failed parsing module config");
+               error:
+                       talloc_free(mi);
+                       return -1;
+               }
+
+               if (module_bootstrap(mi) < 0) {
+                       cf_log_perr(subcs, "Failed bootstrapping module");
+                       goto error;
+               }
 
                /*
                 *      Compile the default "actions" subsection, which includes retries.
@@ -1018,7 +1095,7 @@ int modules_rlm_bootstrap(CONF_SECTION *root)
                actions = cf_section_find(subcs, "actions", NULL);
                if (actions && unlang_compile_actions(&mi->actions, actions, (mi->module->type & MODULE_TYPE_RETRY) != 0)) {
                        talloc_free(mi);
-                       return -1;
+                       goto error;
                }
        }
 
@@ -1082,14 +1159,30 @@ int modules_rlm_bootstrap(CONF_SECTION *root)
        return 0;
 }
 
+/** Cleanup all global structures
+ *
+ * Automatically called on exit.
+ */
 void modules_rlm_free(void)
 {
+       TALLOC_FREE(rlm_modules);
        TALLOC_FREE(module_rlm_virtual_name_tree);
 }
 
+static void _modules_rlm_free_atexit(UNUSED void *uctx)
+{
+       modules_rlm_free();
+}
+
+/** Initialise the module list structure
+ *
+ */
 int modules_rlm_init(void)
 {
+       MEM(rlm_modules = module_list_alloc(NULL, "rlm"));
        MEM(module_rlm_virtual_name_tree = fr_rb_inline_alloc(NULL, module_rlm_virtual_t, name_node,
                                                              module_rlm_virtual_name_cmp, NULL));
+       fr_atexit_global(_modules_rlm_free_atexit, NULL);
+
        return 0;
 }
index 5a3a9de4ef9e60df91b475321648f72f6d236042..904b9e06cb35b39d78a1ebb0c667b707eddcadfe 100644 (file)
@@ -90,20 +90,43 @@ module_instance_t   *module_rlm_by_name_and_method(module_method_t *method, rlm_co
                                                   char const **name1, char const **name2,
                                                   char const *asked_name);
 
+module_thread_instance_t *module_rlm_thread_by_data(void const *data);
+
+module_instance_t      *module_rlm_by_name(module_instance_t const *parent, char const *asked_name);
+
 CONF_SECTION           *module_rlm_by_name_virtual(char const *asked_name);
 
 /** @} */
 
+/** @name Support functions
+ *
+ * @{
+ */
+int                    module_rlm_submodule_parse(TALLOC_CTX *ctx, void *out, void *parent,
+                                                  CONF_ITEM *ci, CONF_PARSER const *rule);
+/** @} */
+
 /** @name Module and module thread initialisation and instantiation
  *
  * @{
  */
+void           modules_rlm_thread_detach(void);
+
+int            modules_rlm_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el) CC_HINT(nonnull(2));
+
+int            modules_rlm_instantiate(void);
+
 int            modules_rlm_bootstrap(CONF_SECTION *root) CC_HINT(nonnull);
 /** @} */
 
-void   modules_rlm_free(void);
+/** @name Global initialisation and free functions
+ *
+ * @{
+ */
+void           modules_rlm_free(void);
 
-int    modules_rlm_init(void);
+int            modules_rlm_init(void);
+/** @} */
 
 #ifdef __cplusplus
 }
index abebb7420140a5907ed180d69bbc0333a866ceac..ff68831f26b04847385c2ced93357443a8c97521 100644 (file)
@@ -42,16 +42,20 @@ RCSID("$Id$")
 #include <freeradius-devel/io/listen.h>
 
 typedef struct {
-       dl_module_inst_t        *proto_module;          //!< The proto_* module for a listen section.
-       fr_app_t const          *app;                   //!< Easy access to the exported struct.
+       module_instance_t               *proto_mi;              //!< The proto_* module for a listen section.
+       fr_app_t const                  *proto_module;          //!< Public interface to the proto_mi.
+                                                               ///< cached for convenience.
 } fr_virtual_listen_t;
 
 typedef struct {
-       CONF_SECTION            *server_cs;             //!< The server section.
-       char const              *namespace;             //!< Protocol namespace
-       fr_virtual_listen_t     **listener;             //!< Listeners in this virtual server.
-       dl_module_inst_t        *process_module;        //!< the process_* module for a virtual server
-       dl_module_inst_t        *dynamic_client_module; //!< the process_* module for a dynamic client
+       CONF_SECTION                    *server_cs;             //!< The server section.
+       fr_virtual_listen_t             **listeners;            //!< Listeners in this virtual server.
+
+       module_instance_t               *process_mi;            //!< The process_* module for a virtual server.
+                                                               ///< Contains the dictionary used by the virtual
+                                                               ///< server and the entry point for the state machine.
+       fr_process_module_t const       *process_module;        //!< Public interface to the process_mi.
+                                                               ///< cached for convenience.
 } fr_virtual_server_t;
 
 static fr_dict_t const *dict_freeradius;
@@ -71,6 +75,20 @@ fr_dict_attr_autoload_t virtual_server_dict_attr_autoload[] = {
        { NULL }
 };
 
+/** List of process modules we've loaded
+ *
+ * This is global for all virtual servers.  Must be initialised
+ * _before_ the configuration is loaded.
+ */
+static module_list_t   *process_modules;
+
+/** List of proto modules we've loaded
+ *
+ * This is global for all virtual servers.  Must be initialised
+ * _before_ the configuration is loaded.
+ */
+static module_list_t   *proto_modules;
+
 /** Top level structure holding all virtual servers
  *
  */
@@ -92,7 +110,6 @@ static fr_rb_tree_t *server_section_name_tree = NULL;
 static int8_t server_section_name_cmp(void const *one, void const *two);
 
 static int namespace_on_read(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, CONF_PARSER const *rule);
-static int listen_on_read(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, CONF_PARSER const *rule);
 static int server_on_read(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule);
 
 static int namespace_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, CONF_PARSER const *rule);
@@ -100,15 +117,9 @@ static int listen_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_IT
 static int server_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule);
 
 static const CONF_PARSER server_on_read_config[] = {
-       { FR_CONF_OFFSET("namespace", FR_TYPE_STRING | FR_TYPE_REQUIRED, fr_virtual_server_t, namespace),
+       { FR_CONF_OFFSET("namespace", FR_TYPE_VOID | FR_TYPE_REQUIRED, fr_virtual_server_t, process_mi),
                        .on_read = namespace_on_read },
 
-       { FR_CONF_OFFSET("listen", FR_TYPE_SUBSECTION | FR_TYPE_MULTI | FR_TYPE_OK_MISSING,
-                        fr_virtual_server_t, listener),
-                        .ident2 = CF_IDENT_ANY,
-                        .subcs_size = sizeof(fr_virtual_listen_t), .subcs_type = "fr_virtual_listen_t",
-                        .on_read = listen_on_read },
-
        CONF_PARSER_TERMINATOR
 };
 
@@ -126,11 +137,11 @@ const CONF_PARSER virtual_servers_on_read_config[] = {
 };
 
 static const CONF_PARSER server_config[] = {
-       { FR_CONF_OFFSET("namespace", FR_TYPE_STRING | FR_TYPE_REQUIRED, fr_virtual_server_t, namespace),
+       { FR_CONF_OFFSET("namespace", FR_TYPE_VOID | FR_TYPE_REQUIRED, fr_virtual_server_t, process_mi),
                         .func = namespace_parse },
 
        { FR_CONF_OFFSET("listen", FR_TYPE_SUBSECTION | FR_TYPE_MULTI | FR_TYPE_OK_MISSING,
-                        fr_virtual_server_t, listener),
+                        fr_virtual_server_t, listeners),
                         .ident2 = CF_IDENT_ANY,
                         .subcs_size = sizeof(fr_virtual_listen_t), .subcs_type = "fr_virtual_listen_t",
                         .func = listen_parse },
@@ -151,60 +162,13 @@ const CONF_PARSER virtual_servers_config[] = {
        CONF_PARSER_TERMINATOR
 };
 
-typedef struct {
-       fr_dict_t const         *dict;
-       char const              *server;
-} virtual_server_dict_t;
-
-/** Decrement references on dictionaries as the config sections are freed
+/** Parse a "namespace" parameter
  *
- */
-static int _virtual_server_dict_free(virtual_server_dict_t *cd)
-{
-       fr_dict_const_free(&cd->dict, cd->server);
-       return 0;
-}
-
-
-void virtual_server_dict_set(CONF_SECTION *server_cs, fr_dict_t const *dict, bool reference)
-{
-       virtual_server_dict_t *p;
-       CONF_DATA const *cd;
-
-       cd = cf_data_find(server_cs, virtual_server_dict_t, "dictionary");
-       if (cd) {
-               p = (virtual_server_dict_t *) cf_data_value(cd);
-               if (p->dict == dict) return;
-
-               cf_log_warn(server_cs, "Attempt to add multiple different dictionaries %s and %s",
-                           fr_dict_root(p->dict)->name, fr_dict_root(dict)->name);
-               return;
-       }
-
-       p = talloc_zero(server_cs, virtual_server_dict_t);
-       p->dict = dict;
-       p->server = talloc_strdup(p, cf_section_name2(server_cs));
-       talloc_set_destructor(p, _virtual_server_dict_free);
-
-       if (reference) fr_dict_dependent_add(dict, p->server);
-
-       cf_data_add(server_cs, p, "dictionary", true);
-}
-
-fr_dict_t const *virtual_server_dict(CONF_SECTION *server_cs)
-{
-       virtual_server_dict_t *p;
-       CONF_DATA const *cd;
-
-       cd = cf_data_find(server_cs, virtual_server_dict_t, "dictionary");
-       if (cd) {
-               p = (virtual_server_dict_t *) cf_data_value(cd);
-               return p->dict;
-       }
-       return NULL;
-}
-
-/** Parse a "namespace" parameter.
+ * We need to load the process module before continuing to parse the virtual server contents
+ * as we need to know the namespace so that we can resolve attribute names.
+ *
+ * We also need the compilation list from the proto module to figure out which sections we
+ * need to compile.
  *
  * @param[in] ctx      to allocate data in.
  * @param[out] out     always NULL
@@ -218,17 +182,17 @@ fr_dict_t const *virtual_server_dict(CONF_SECTION *server_cs)
 static int namespace_on_read(TALLOC_CTX *ctx, UNUSED void *out, UNUSED void *parent,
                             CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
 {
-       CONF_PAIR               *cp = cf_item_to_pair(ci);
-       CONF_SECTION            *server_cs = cf_item_to_section(cf_parent(ci));
-       char const              *namespace = cf_pair_value(cp);
-       dl_module_t const       *module;
-       char                    *module_name, *p, *end;
+       CONF_PAIR                       *cp = cf_item_to_pair(ci);
+       CONF_SECTION                    *server_cs = cf_item_to_section(cf_parent(ci));
+       module_instance_t               *mi;
+       char const                      *namespace;
+       char                            *module_name, *p, *end;
+       fr_process_module_t const       *process;
 
-       if (!namespace || !*namespace) {
-               cf_log_err(ci, "Missing value for 'namespace'");
-               return -1;
-       }
+       fr_cond_assert_msg(process_modules,
+                          "virtual_servers_init MUST be called before reading virtual server config");
 
+       namespace = cf_pair_value(cp);
        module_name = talloc_strdup(ctx, namespace);
 
        /*
@@ -238,89 +202,31 @@ static int namespace_on_read(TALLOC_CTX *ctx, UNUSED void *out, UNUSED void *par
             p < end;
             p++) if (*p == '-') *p = '_';
 
+
        /*
-        *      Pass server_cs, even though it's wrong.  We don't have
-        *      anything else to pass, and the dl_module() function
-        *      only uses the CONF_SECTION for printing.
+        *      The module being loaded is the namespace with all '-'
+        *      transformed to '_'.
+        *
+        *      The instance name is the virtual server name.
         */
-       module = dl_module(server_cs, NULL, module_name, DL_MODULE_TYPE_PROCESS);
+       mi = module_alloc(process_modules, NULL, DL_MODULE_TYPE_PROCESS,
+                         module_name, dl_module_inst_name_from_conf(server_cs));
        talloc_free(module_name);
-       if (module) {
-               fr_process_module_t const *process = (fr_process_module_t const *) module->common;
-
-               if (*process->dict) {
-                       virtual_server_dict_set(server_cs, *process->dict, false);
-               }
-       }
-
-       if (!module) {
+       if (mi == NULL) {
                cf_log_perr(ci, "Failed loading process module");
                return -1;
        }
-
-       cf_data_add(server_cs, module, "process module", true);
-
-       return 0;
-}
-
-/** dl_open a proto_* module
- *
- * @param[in] ctx      to allocate data in.
- * @param[out] out     always NULL
- * @param[in] parent   Base structure address.
- * @param[in] ci       #CONF_SECTION containing the listen section.
- * @param[in] rule     unused.
- * @return
- *     - 0 on success.
- *     - -1 on failure.
- */
-static int listen_on_read(UNUSED TALLOC_CTX *ctx, UNUSED void *out, UNUSED void *parent,
-                         CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
-{
-       CONF_SECTION            *listen_cs = cf_item_to_section(ci);
-       CONF_SECTION            *server_cs = cf_item_to_section(cf_parent(ci));
-       CONF_PAIR               *namespace = cf_pair_find(server_cs, "namespace");
-       char const              *value;
-       dl_module_t const       *module;
-       fr_app_t const          *app;
-       bool                    set_dict;
-
-       if (!namespace) {
-               cf_log_err(server_cs, "No 'namespace' set for virtual server");
-               cf_log_err(server_cs, "Please add 'namespace = <protocol>' inside of the 'server %s { ... }' section",
-                          cf_section_name2(server_cs));
-               return -1;
-       }
-
-       value = cf_section_name2(listen_cs);
-       if (value) {
-               set_dict = false;
-
-
-       } else {
-               value = cf_pair_value(namespace);
-               fr_assert(value);
-               set_dict = true;
-
-       }
-
-       if (DEBUG_ENABLED4) cf_log_debug(ci, "Loading proto_%s", value);
-       module = dl_module(listen_cs, NULL, value, DL_MODULE_TYPE_PROTO);
-       if (!module) {
-               cf_log_err(listen_cs, "Failed loading proto_%s module", value);
+       process = (fr_process_module_t const *)mi->dl_inst->module->common;
+       if (!*(process->dict)) {
+               cf_log_err(ci, "Process module is invalid - missing namespace dictionary");
+               talloc_free(mi);
                return -1;
        }
-       cf_data_add(listen_cs, module, "proto module", true);
-
-       if (!set_dict) return 0;
-
-       app = (fr_app_t const *) module->common;
-       if (app->dict) virtual_server_dict_set(server_cs, *app->dict, true);
+       cf_data_add(server_cs, mi, "process_module", false);
 
        return 0;
 }
 
-
 /** Callback when a "server" section is created.
  *
  *  This callback exists only as a place-holder to ensure that the
@@ -343,6 +249,28 @@ static int server_on_read(UNUSED TALLOC_CTX *ctx, UNUSED void *out, UNUSED void
        return 0;
 }
 
+
+static inline CC_HINT(always_inline)
+int add_compile_list(CONF_SECTION *cs, virtual_server_compile_t const *compile_list, char const *name)
+{
+       int i;
+       virtual_server_compile_t const *list = compile_list;
+
+       if (!compile_list) return 0;
+
+       for (i = 0; list[i].name != NULL; i++) {
+               if (list[i].name == CF_IDENT_ANY) continue;
+
+               if (virtual_server_section_register(&list[i]) < 0) {
+                       cf_log_err(cs, "Failed registering processing section name %s for %s",
+                                  list[i].name, name);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
 /** dl_open a process_* module
  *
  * @param[in] ctx      to allocate data in.
@@ -354,22 +282,24 @@ static int server_on_read(UNUSED TALLOC_CTX *ctx, UNUSED void *out, UNUSED void
  *     - 0 on success.
  *     - -1 on failure.
  */
-static int namespace_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
+static int namespace_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
 {
+       CONF_PAIR               *cp = cf_item_to_pair(ci);
        CONF_SECTION            *server_cs = cf_item_to_section(cf_parent(ci));
        CONF_SECTION            *process_cs;
-       char const              *namespace = cf_pair_value(cf_item_to_pair(ci));
-       char                    *module_name = talloc_strdup(ctx, namespace);
-       char                    *p, *end;
-       fr_virtual_server_t     *server = (fr_virtual_server_t *) (((uint8_t *) out) - offsetof(fr_virtual_server_t, namespace));
-       int                     ret;
-
-       (void) talloc_get_type_abort(server, fr_virtual_server_t);
-       fr_assert(namespace != NULL);
+       fr_virtual_server_t     *server = talloc_get_type_abort(((uint8_t *) out) - offsetof(fr_virtual_server_t, process_mi), fr_virtual_server_t);
+       char const              *namespace = cf_pair_value(cp);
+       module_instance_t       *mi = cf_data_value(cf_data_find(server_cs, module_instance_t, "process_module"));
 
-       server->namespace = namespace;
+       /*
+        *      We don't have access to fr_virtual_server_t
+        *      in the onread callback, so we need to do the
+        *      fixups here.
+        */
+       server->process_mi = mi;
+       server->process_module = (fr_process_module_t const *)mi->dl_inst->module->common;
 
-       if (DEBUG_ENABLED4) cf_log_debug(ci, "Loading process %s into %p", namespace, out);
+       *(module_instance_t const **)out = mi;
 
        /*
         *      Enforce that the protocol process configuration is in
@@ -380,27 +310,26 @@ static int namespace_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF
                process_cs = cf_section_alloc(server_cs, server_cs, namespace, NULL);
        }
 
+       if (module_conf_parse(mi, process_cs) < 0) {
+       error:
+               cf_log_perr(ci, "Failed bootstrapping process module");
+               cf_data_remove(server_cs, mi, "process_module");
+               TALLOC_FREE(server->process_mi);
+               return -1;
+       }
+
        /*
-        *      Smush all hyphens to underscores for module names
+        *      Pass server_cs, even though it's wrong.  We don't have
+        *      anything else to pass, and the dl_module() function
+        *      only uses the CONF_SECTION for printing.
         */
-       for (p = module_name, end = module_name + talloc_array_length(module_name) - 1;
-            p < end;
-            p++) if (*p == '-') *p = '_';
+       if (module_bootstrap(server->process_mi) < 0) goto error;
 
        /*
-        *      We now require a process module for everything.
+        *      Pull the list of sections we need to compile out of
+        *      the process module's public struct.
         */
-       ret = dl_module_instance(ctx, &server->process_module, process_cs, NULL, module_name, DL_MODULE_TYPE_PROCESS);
-       talloc_free(module_name);
-       if (ret < 0) {
-               cf_log_warn(server_cs, "Failed loading process module");
-               return -1;
-       }
-
-       if (dl_module_conf_parse(server->process_module) < 0) {
-               TALLOC_FREE(server->process_module);
-               return -1;
-       }
+       add_compile_list(server->process_mi->dl_inst->conf, server->process_module->compile_list, namespace);
 
        return 0;
 }
@@ -416,29 +345,122 @@ static int namespace_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF
  *     - 0 on success.
  *     - -1 on failure.
  */
-static int listen_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
+static int listen_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
 {
-       fr_virtual_listen_t     *listen = talloc_get_type_abort(out, fr_virtual_listen_t); /* Pre-allocated for us */
-       CONF_SECTION            *listen_cs = cf_item_to_section(ci);
+       fr_virtual_listen_t     *listener = talloc_get_type_abort(out, fr_virtual_listen_t); /* Pre-allocated for us */
+       CONF_SECTION            *listener_cs = cf_item_to_section(ci);
        CONF_SECTION            *server_cs = cf_item_to_section(cf_parent(ci));
        CONF_PAIR               *namespace = cf_pair_find(server_cs, "namespace");
-       char const              *value;
 
-       value = cf_section_name2(listen_cs);
-       if (!value) value = cf_pair_value(namespace);
+       CONF_PAIR               *proto;
+       char const              *mod_name;
+       char const              *inst_name;
+       char                    *qual_inst_name;
 
-       if (DEBUG_ENABLED4) cf_log_debug(ci, "Loading %s listener into %p", value, out);
+       module_instance_t       *mi;
 
-       if (dl_module_instance(ctx, &listen->proto_module, listen_cs, NULL, value, DL_MODULE_TYPE_PROTO) < 0) {
-               cf_log_err(listen_cs, "Failed loading proto module");
+       fr_cond_assert_msg(proto_modules,
+                          "virtual_servers_init MUST be called before reading virtual server config");
+
+       if (!namespace) {
+               cf_log_err(server_cs, "No 'namespace' set for virtual server");
+               cf_log_err(server_cs, "Please add 'namespace = <protocol>' inside of the 'server %s { ... }' section",
+                          cf_section_name2(server_cs));
                return -1;
        }
 
-       if (dl_module_conf_parse(listen->proto_module) < 0) {
-               TALLOC_FREE(listen->proto_module);
+       /*
+        *      Module name comes from the 'proto' pair if the
+        *      listen section has one else it comes from the
+        *      namespace of the virtual server.
+        *
+        *      The following results in proto_radius being loaded:
+        *
+        *      server foo {
+        *              namespace = radius
+        *              listen {
+        *
+        *              }
+        *      }
+        *
+        *      The following results in proto_load being loaded:
+        *
+        *      server foo {
+        *              namespace = radius
+        *              listen {
+        *                      proto = load
+        *
+        *              }
+        *      }
+        *
+        *      In this way the server behaves reasonably out
+        *      of the box, but allows foreign or generic listeners
+        *      to be included in the server.
+        *
+        */
+       proto = cf_pair_find(listener_cs, "proto");
+       if (proto) {
+               mod_name = cf_pair_value(proto);
+       } else {
+               mod_name = cf_pair_value(namespace);
+       }
+
+       /*
+        *      Inst name comes from the 'listen' name2
+        *      or from the module name.
+        *
+        *      The inst name is qualified with the name
+        *      of the server the listener appears in.
+        *
+        *      The following results in the instance name of 'foo.radius':
+        *
+        *      server foo {
+        *              namespace = radius
+        *              listen {
+        *
+        *              }
+        *      }
+        *
+        *      The following results in the instance name 'foo.my_network':
+        *
+        *      server foo {
+        *              namespace = radius
+        *              listen my_network {
+        *
+        *              }
+        *      }
+        */
+       inst_name = cf_section_name2(listener_cs);
+       if (!inst_name) inst_name = mod_name;
+
+       qual_inst_name = talloc_asprintf(NULL, "%s.%s", cf_section_name2(server_cs), inst_name);
+       mi = module_alloc(proto_modules, NULL, DL_MODULE_TYPE_PROTO, mod_name, qual_inst_name);
+       talloc_free(qual_inst_name);
+       if (mi == NULL) return -1;
+
+       if (DEBUG_ENABLED4) cf_log_debug(ci, "Loading %s listener into %p", inst_name, out);
+
+       if (module_conf_parse(mi, listener_cs) < 0) {
+               cf_log_perr(ci, "Failed parsing config for listener");
+       error:
+               talloc_free(mi);
                return -1;
        }
 
+       /*
+        *      Pass server_cs, even though it's wrong.  We don't have
+        *      anything else to pass, and the dl_module() function
+        *      only uses the CONF_SECTION for printing.
+        */
+       if (module_bootstrap(mi) < 0) {
+               cf_log_perr(ci, "Failed bootstrapping listener");
+               goto error;
+       }
+
+       listener->proto_mi = mi;
+       listener->proto_module = (fr_app_t const *)listener->proto_mi->dl_inst->module->common;
+       cf_data_add(listener_cs, mi, "proto_module", false);
+
        return 0;
 }
 
@@ -468,7 +490,6 @@ static int server_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
        }
 
        server->server_cs = server_cs;
-       server->namespace = cf_pair_value(namespace);
 
        /*
         *      Now parse the listeners
@@ -483,122 +504,137 @@ static int server_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
        return 0;
 }
 
-/** Set the request processing function.
+/** Return the namespace for the named virtual server
  *
- *     Short-term hack
+ * @param[in] virtual_server   to look for namespace in.
+ * @return
+ *     - NULL on error.
+ *     - Namespace on success.
  */
-unlang_action_t virtual_server_push(request_t *request, CONF_SECTION *server_cs, bool top_frame)
+fr_dict_t const *virtual_server_dict_by_name(char const *virtual_server)
 {
-       fr_virtual_server_t *server;
-       fr_process_module_t const *process;
-       module_instance_t *mi = talloc_zero(request, module_instance_t);
+       CONF_SECTION const *server_cs;
 
-       server = cf_data_value(cf_data_find(server_cs, fr_virtual_server_t, "vs"));
-       if (!server) {
-               REDEBUG("server_cs does not contain virtual server data");
-               return UNLANG_ACTION_FAIL;
-       }
+       server_cs = virtual_server_find(virtual_server);
+       if (!server_cs) return NULL;
 
-       mi->name = server->process_module->name;
-       mi->module = (module_t *)server->process_module;
-       mi->number = 0; /* Hacky hack hack */
+       return virtual_server_dict_by_cs(server_cs);
+}
 
-       process = (fr_process_module_t const *) server->process_module->module->common;
-       mi->dl_inst = server->process_module;
-       mi->instantiated = true;
+/** Return the namespace for the virtual server specified by a config section
+ *
+ * @param[in] server_cs                to look for namespace in.
+ * @return
+ *     - NULL on error.
+ *     - Namespace on success.
+ */
+fr_dict_t const *virtual_server_dict_by_cs(CONF_SECTION const *server_cs)
+{
+       CONF_DATA const *cd;
 
-       /*
-        *      Bootstrap the stack with a module instance.
-        *
-        *      @todo - src/lib/unlang/module.c calls module_thread(),
-        *      which looks up mi->number in various data structures.
-        *      It's probably best to initialize instance_num=1 in
-        *      src/lib/server/module.c, that reserves 0 for "nothing
-        *      is initialized".
-        */
-       if (unlang_module_push(&request->rcode, request, mi, process->process, top_frame) < 0) return UNLANG_ACTION_FAIL;
+       cd = cf_data_find(server_cs, module_instance_t, "process_module");
+       if (cd) {
+               module_instance_t const *mi = cf_data_value(cd);
+               fr_process_module_t const *process = (fr_process_module_t const *)mi->module;
 
-       return UNLANG_ACTION_PUSHED_CHILD;
+               return *(process->dict);
+       }
+       return NULL;
 }
 
-/** Allow dynamic clients in this virtual server.
+/** Return the namespace for a given virtual server specified by a CONF_ITEM within the virtual server
  *
- *     Short-term hack
-*/
-int virtual_server_dynamic_clients_allow(CONF_SECTION *server_cs)
+ * @param[in] ci               to look for namespace in.
+ * @return
+ *     - NULL on error.
+ *     - Namespace on success.
+ */
+fr_dict_t const *virtual_server_dict_by_child_ci(CONF_ITEM const *ci)
 {
-       fr_virtual_server_t *server;
-
-       server = cf_data_value(cf_data_find(server_cs, fr_virtual_server_t, "vs"));
-       if (server->dynamic_client_module) return 0;
+       CONF_DATA const *cd;
 
-       if (dl_module_instance(server_cs, &server->dynamic_client_module, server_cs, NULL, "dynamic_client", DL_MODULE_TYPE_PROCESS) < 0) {
-               cf_log_err(server_cs, "Failed loading dynamic client module");
-               return -1;
-       }
+       cd = cf_data_find_in_parent(ci, module_instance_t, "process_module");
+       if (cd) {
+               module_instance_t const *mi = cf_data_value(cd);
+               fr_process_module_t const *process = (fr_process_module_t const *)mi->module;
 
-       if (dl_module_conf_parse(server->dynamic_client_module) < 0) {
-               TALLOC_FREE(server->dynamic_client_module);
-               return -1;
+               return *(process->dict);
        }
-
-       return 0;
+       return NULL;
 }
 
-/** Define a values for Auth-Type attributes by the sections present in a virtual-server
+/** Verify that a given virtual_server exists and is of a particular namespace
  *
- * The ident2 value of any sections found will be converted into values of the specified da.
+ * Mostly used by modules to check virtual servers specified by their configs.
  *
- * @param[in] server_cs                The virtual server containing the sections.
- * @param[in] subcs_name       of the subsection to search for.
- * @param[in] da               to add enumeration values for.
+ * @param[out] out             we found. May be NULL if just checking for existence.
+ * @param[in] virtual_server   to check.
+ * @param[in] namespace                the virtual server must belong to.
+ * @param[in] ci               to log errors against. May be NULL if caller
+ *                             doesn't want errors logged.
  * @return
- *     - 0 all values added successfully.
- *     - -1 an error occurred.
+ *     - 0 on success.
+ *     - -1 if no virtual server could be found.
+ *     - -2 if virtual server is not of the correct namespace.
  */
-int virtual_server_section_attribute_define(CONF_SECTION *server_cs, char const *subcs_name, fr_dict_attr_t const *da)
+int virtual_server_has_namespace(CONF_SECTION **out,
+                                char const *virtual_server, fr_dict_t const *namespace, CONF_ITEM *ci)
 {
-       int                     rcode = 0;
-       CONF_SECTION            *subcs = NULL;
-
-       fr_assert(strcmp(cf_section_name1(server_cs), "server") == 0);
-
-       while ((subcs = cf_section_find_next(server_cs, subcs, subcs_name, CF_IDENT_ANY))) {
-               char const      *name2;
-               fr_dict_enum_value_t    *dv;
+       CONF_SECTION    *server_cs;
+       fr_dict_t const *dict;
 
-               name2 = cf_section_name2(subcs);
-               if (!name2) {
-                       cf_log_err(subcs, "Invalid '%s { ... }' section, it must have a name", subcs_name);
-                       return -1;
-               }
-
-               /*
-                *      If the value already exists, don't
-                *      create it again.
-                */
-               dv = fr_dict_enum_by_name(da, name2, -1);
-               if (dv) continue;
-
-               cf_log_debug(subcs, "Creating %s = %s", da->name, name2);
+       if (out) *out = NULL;
 
+       server_cs = virtual_server_find(virtual_server);
+       if (!server_cs) {
+               if (ci) cf_log_err(ci, "Can't find virtual server \"%s\"", virtual_server);
+               return -1;
+       }
+       dict = virtual_server_dict_by_name(virtual_server);
+       if (!dict) {
                /*
-                *      Create a new unique value with a meaningless
-                *      number.  You can't look at it from outside of
-                *      this code, so it doesn't matter.  The only
-                *      requirement is that it's unique.
+                *      Not sure this is even a valid state?
                 */
-               if (fr_dict_enum_add_name_next(fr_dict_attr_unconst(da), name2) < 0) {
-                       PERROR("Failed adding section value");
-                       return -1;
-               }
+               if (ci) cf_log_err(ci, "No namespace found in virtual server \"%s\"", virtual_server);
+               return -2;
+       }
 
-               rcode = 1;
+       if (dict != namespace) {
+               if (ci) {
+                       cf_log_err(ci,
+                                  "Expected virtual server \"%s\" to be of namespace \"%s\", got namespace \"%s\"",
+                                  virtual_server, fr_dict_root(namespace)->name, fr_dict_root(dict)->name);
+               }
+               return -2;
        }
 
-       return rcode;
+       if (out) *out = server_cs;
+
+       return 0;
 }
 
+/** Set the request processing function.
+ *
+ *     Short-term hack
+ */
+unlang_action_t virtual_server_push(request_t *request, CONF_SECTION *server_cs, bool top_frame)
+{
+       fr_virtual_server_t *server;
+
+       server = cf_data_value(cf_data_find(server_cs, fr_virtual_server_t, "vs"));
+       if (!server) {
+               REDEBUG("server_cs does not contain virtual server data");
+               return UNLANG_ACTION_FAIL;
+       }
+
+       /*
+        *      Bootstrap the stack with a module instance.
+        */
+       if (unlang_module_push(&request->rcode, request, server->process_mi,
+                              server->process_module->process, top_frame) < 0) return UNLANG_ACTION_FAIL;
+
+       return UNLANG_ACTION_PUSHED_CHILD;
+}
 
 static int cmd_show_server_list(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
 {
@@ -608,7 +644,7 @@ static int cmd_show_server_list(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx,
 
        for (i = 0; i < server_cnt; i++) {
                fprintf(fp, "%-30snamespace = %s\n", cf_section_name2(virtual_servers[i]->server_cs),
-                       virtual_servers[i]->namespace);
+                       fr_dict_root(*(virtual_servers[i]->process_module->dict))->name);
        }
 
        return 0;
@@ -725,343 +761,6 @@ bool listen_record(fr_listen_t *li)
        return fr_rb_insert(listen_addr_root, li);
 }
 
-static int process_instantiate(CONF_SECTION *server_cs, dl_module_inst_t *dl_inst, fr_dict_t const *dict)
-{
-       fr_process_module_t const *process = (fr_process_module_t const *) dl_inst->module->common;
-
-       if (process->common.instantiate &&
-           (process->common.instantiate(MODULE_INST_CTX(dl_inst)) < 0)) {
-               cf_log_err(dl_inst->conf, "Instantiate failed");
-               return -1;
-       }
-
-       /*
-        *      Compile the processing sections.
-        */
-       if (process->compile_list) {
-               tmpl_rules_t            parse_rules;
-
-               memset(&parse_rules, 0, sizeof(parse_rules));
-               parse_rules.attr.dict_def = dict;
-               fr_assert(parse_rules.attr.dict_def != NULL);
-
-               if (virtual_server_compile_sections(server_cs, process->compile_list, &parse_rules,
-                                                   dl_inst->data) < 0) {
-                       return -1;
-               }
-       }
-
-       return 0;
-}
-
-
-/** Instantiate all the virtual servers
- *
- * @return
- *     - 0 on success.
- *     - -1 on failure.
- */
-int virtual_servers_instantiate(void)
-{
-       size_t          i, server_cnt = virtual_servers ? talloc_array_length(virtual_servers) : 0;
-
-       fr_assert(virtual_servers);
-
-       DEBUG2("#### Instantiating listeners ####");
-
-       if (fr_command_register_hook(NULL, NULL, virtual_server_root, cmd_table) < 0) {
-               PERROR("Failed registering radmin commands for virtual servers");
-               return -1;
-       }
-
-       for (i = 0; i < server_cnt; i++) {
-               fr_virtual_listen_t     **listener;
-               size_t                  j, listen_cnt;
-               CONF_ITEM               *ci = NULL;
-               CONF_SECTION            *server_cs = virtual_servers[i]->server_cs;
-               CONF_DATA const         *cd;
-               virtual_server_dict_t   *dict = NULL;
-
-               cd = cf_data_find(server_cs, virtual_server_dict_t, "dictionary");
-               if (cd) {       /* only NULl for the control socket */
-                       dict = (virtual_server_dict_t *) cf_data_value(cd);
-                       fr_assert(dict != NULL);
-               }
-
-               listener = virtual_servers[i]->listener;
-               listen_cnt = talloc_array_length(listener);
-
-               DEBUG("Compiling policies in server %s { ... }", cf_section_name2(server_cs));
-
-               fr_assert(virtual_servers[i]->namespace != NULL);
-
-               for (j = 0; j < listen_cnt; j++) {
-                       fr_virtual_listen_t *listen = listener[j];
-
-                       fr_assert(listen != NULL);
-                       fr_assert(listen->proto_module != NULL);
-                       fr_assert(listen->app != NULL);
-
-                       if (listen->app->common.instantiate &&
-                           listen->app->common.instantiate(MODULE_INST_CTX(listen->proto_module)) < 0) {
-                               cf_log_err(listen->proto_module->conf, "Could not load virtual server \"%s\".",
-                                           cf_section_name2(server_cs));
-                               return -1;
-                       }
-               }
-
-               fr_assert(virtual_servers[i]->process_module);
-
-               if (!dict) {
-                       cf_log_err(server_cs, "No dictionary for namespace %s", virtual_servers[i]->namespace);
-                       return -1; /* should never happen */
-               }
-
-               if (process_instantiate(server_cs, virtual_servers[i]->process_module, dict->dict) < 0) return -1;
-
-               if (virtual_servers[i]->dynamic_client_module &&
-                   (process_instantiate(server_cs, virtual_servers[i]->dynamic_client_module, dict->dict) < 0)) return -1;
-
-               /*
-                *      Print out warnings for unused "recv" and
-                *      "send" sections.
-                *
-                *      @todo - check against the "compile_list"
-                *      registered for this virtual server, instead of hard-coding stuff.
-                */
-               while ((ci = cf_item_next(server_cs, ci))) {
-                       char const      *name;
-                       CONF_SECTION    *subcs;
-
-                       if (!cf_item_is_section(ci)) continue;
-
-                       subcs = cf_item_to_section(ci);
-                       name = cf_section_name1(subcs);
-
-                       /*
-                        *      Skip known "other" sections
-                        */
-                       if ((strcmp(name, "listen") == 0) || (strcmp(name, "client") == 0)) continue;
-
-                       /*
-                        *      For every other section, warn if it hasn't
-                        *      been compiled.
-                        */
-                       if (!cf_data_find(subcs, unlang_group_t, NULL)) {
-                               char const *name2;
-
-                               name2 = cf_section_name2(subcs);
-                               if (!name2) name2 = "";
-
-                               cf_log_warn(subcs, "%s %s { ... } section is unused", name, name2);
-                       }
-               }
-       }
-
-       return 0;
-}
-
-static int add_compile_list(CONF_SECTION *cs, virtual_server_compile_t const *compile_list, char const *name)
-{
-       int i;
-       virtual_server_compile_t const *list = compile_list;
-
-       if (!compile_list) return 0;
-
-       for (i = 0; list[i].name != NULL; i++) {
-               if (list[i].name == CF_IDENT_ANY) continue;
-
-               if (virtual_server_section_register(&list[i]) < 0) {
-                       cf_log_err(cs, "Failed registering processing section name %s for %s",
-                                  list[i].name, name);
-                       return -1;
-               }
-       }
-
-       return 0;
-}
-
-static int process_bootstrap(dl_module_inst_t *dl_inst, char const *namespace)
-{
-       fr_process_module_t const *process = (fr_process_module_t const *) dl_inst->module->common;
-
-       if (process->common.bootstrap &&
-           (process->common.bootstrap(MODULE_INST_CTX(dl_inst)) < 0)) {
-               cf_log_err(dl_inst->conf, "Bootstrap failed");
-               return -1;
-       }
-
-       return add_compile_list(dl_inst->conf, process->compile_list, namespace);
-}
-
-/** Load protocol modules and call their bootstrap methods
- *
- * @return
- *     - 0 on success.
- *     - -1 on failure.
- */
-int virtual_servers_bootstrap(CONF_SECTION *config)
-{
-       size_t i, server_cnt = 0;
-       CONF_SECTION *cs = NULL;
-
-       if (!virtual_servers) {
-               ERROR("No server { ... } sections found");
-               return -1;
-       }
-
-       /*
-        *      Check the talloc hierarchy is sane
-        */
-       talloc_get_type_abort(virtual_servers, fr_virtual_server_t *);
-       server_cnt = talloc_array_length(virtual_servers);
-
-       DEBUG2("#### Bootstrapping listeners ####");
-
-       /*
-        *      Load all of the virtual servers.
-        */
-       while ((cs = cf_section_find_next(config, cs, "server", CF_IDENT_ANY))) {
-               char const *server_name;
-               CONF_SECTION *bad;
-
-               server_name = cf_section_name2(cs);
-               if (!server_name) {
-                       cf_log_err(cs, "server sections must have a name");
-                       return -1;
-               }
-
-               bad = cf_section_find_next(config, cs, "server", server_name);
-               if (bad) {
-                       cf_log_err(bad, "Duplicate virtual servers are forbidden.");
-                       cf_log_err(cs, "Previous definition occurs here.");
-                       return -1;
-               }
-
-               /*
-                *      Ignore internally generated "server" sections,
-                *      they're for the unit tests.
-                */
-               if (!cf_filename(cs)) continue;
-
-               /*
-                *      Forbid old-style virtual servers.
-                */
-               if (!cf_pair_find(cs, "namespace")) {
-                       cf_log_err(cs, "server %s { ...} section must set 'namespace = ...' to define the server protocol", server_name);
-                       return -1;
-               }
-       }
-
-       for (i = 0; i < server_cnt; i++) {
-               fr_virtual_listen_t     **listener;
-               size_t                  j, listen_cnt;
-
-               fr_assert(virtual_servers[i] != NULL);
-
-               /*
-                *      The only listener which can't have a dictionary is "control".
-                */
-               fr_assert((cf_data_find(virtual_servers[i]->server_cs, virtual_server_dict_t, "dictionary") != NULL) ||
-                         (virtual_servers[i]->listener &&
-                          (strcmp(((fr_virtual_listen_t *) virtual_servers[i]->listener[0])->proto_module->module->common->name, "control") == 0)));
-
-               if (!virtual_servers[i]->listener) goto bootstrap;
-
-               listener = talloc_get_type_abort(virtual_servers[i]->listener, fr_virtual_listen_t *);
-               listen_cnt = talloc_array_length(listener);
-
-               for (j = 0; j < listen_cnt; j++) {
-                       fr_virtual_listen_t *listen = listener[j];
-
-                       fr_assert(listen != NULL);
-                       fr_assert(listen->proto_module != NULL);
-
-                       (void) talloc_get_type_abort(listen, fr_virtual_listen_t);
-
-                       talloc_get_type_abort(listen->proto_module, dl_module_inst_t);
-                       listen->app = (fr_app_t const *)listen->proto_module->module->common;
-
-                       if (listen->app->common.bootstrap &&
-                           listen->app->common.bootstrap(MODULE_INST_CTX(listen->proto_module)) < 0) {
-                               cf_log_err(listen->proto_module->conf, "Bootstrap failed");
-                               return -1;
-                       }
-               }
-
-       bootstrap:
-               fr_assert(virtual_servers[i]->process_module);
-
-               if (process_bootstrap(virtual_servers[i]->process_module,
-                                     virtual_servers[i]->namespace) < 0) return -1;
-
-               if (virtual_servers[i]->dynamic_client_module &&
-                   (process_bootstrap(virtual_servers[i]->process_module,
-                                      virtual_servers[i]->namespace) < 0)) return -1;
-       }
-
-       return 0;
-}
-
-/** Open all the listen sockets
- *
- * @param[in] sc       Scheduler to add I/O paths to.
- * @return
- *     - 0 on success.
- *     - -1 on failure.
- */
-int virtual_servers_open(fr_schedule_t *sc)
-{
-       size_t i, server_cnt = virtual_servers ? talloc_array_length(virtual_servers) : 0;
-
-       fr_assert(virtual_servers);
-
-       DEBUG2("#### Opening listener interfaces ####");
-       fr_strerror_clear();
-
-       for (i = 0; i < server_cnt; i++) {
-               fr_virtual_listen_t     **listener;
-               size_t                  j, listen_cnt;
-
-               listener = virtual_servers[i]->listener;
-               listen_cnt = talloc_array_length(listener);
-
-               for (j = 0; j < listen_cnt; j++) {
-                       fr_virtual_listen_t *listen = listener[j];
-
-                       fr_assert(listen != NULL);
-                       fr_assert(listen->proto_module != NULL);
-                       fr_assert(listen->app != NULL);
-
-                       /*
-                        *      The socket is opened with app_instance,
-                        *      but all subsequent calls (network.c, etc.) use app_io_instance.
-                        *
-                        *      The reason is that we call (for example) proto_radius to
-                        *      open the socket, and proto_radius is responsible for setting up
-                        *      proto_radius_udp, and then calling proto_radius_udp->open.
-                        *
-                        *      Even then, proto_radius usually calls fr_master_io_listen() in order
-                        *      to create the fr_listen_t structure.
-                        */
-                       if (listen->app->open &&
-                           listen->app->open(listen->proto_module->data, sc, listen->proto_module->conf) < 0) {
-                               cf_log_perr(listen->proto_module->conf, "Opening %s I/O interface failed",
-                                           listen->app->common.name);
-                               return -1;
-                       }
-
-                       /*
-                        *      Socket information is printed out by
-                        *      the socket handlers.  e.g. proto_radius_udp
-                        */
-                       DEBUG3("Opened listener for %s", listen->app->common.name);
-               }
-       }
-
-       return 0;
-}
-
 /** Return virtual server matching the specified name
  *
  * @note May be called in bootstrap or instantiate as all servers should be present.
@@ -1088,146 +787,21 @@ CONF_SECTION *virtual_server_by_child(CONF_SECTION *section)
        return cf_section_find_in_parent(section, "server", CF_IDENT_ANY);
 }
 
-/** Wrapper for the config parser to allow pass1 resolution of virtual servers
- *
- */
-int virtual_server_cf_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
-                           CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
-{
-       CONF_SECTION    *server_cs;
-
-       server_cs = virtual_server_find(cf_pair_value(cf_item_to_pair(ci)));
-       if (!server_cs) {
-               cf_log_err(ci, "virtual-server \"%s\" not found", cf_pair_value(cf_item_to_pair(ci)));
-               return -1;
-       }
-
-       *((CONF_SECTION **)out) = server_cs;
-
-       return 0;
-}
-
-
-/** Return the namespace for a given virtual server
- *
- * @param[in] virtual_server   to look for namespace in.
- * @return
- *     - NULL on error.
- *     - Namespace on success.
- */
-fr_dict_t const *virtual_server_namespace(char const *virtual_server)
-{
-       CONF_SECTION const *server_cs;
-       CONF_DATA const *cd;
-       virtual_server_dict_t *dict;
-
-       server_cs = virtual_server_find(virtual_server);
-       if (!server_cs) return NULL;
-
-       cd = cf_data_find(server_cs, virtual_server_dict_t, "dictionary");
-       if (!cd) return NULL;
-
-       dict = (virtual_server_dict_t *) cf_data_value(cd);
-
-       return dict->dict;
-}
-
-/** Return the namespace for a given virtual server specified by a CONF_ITEM within the virtual server
- *
- * @param[in] ci               to look for namespace in.
- * @return
- *     - NULL on error.
- *     - Namespace on success.
- */
-fr_dict_t const *virtual_server_namespace_by_ci(CONF_ITEM *ci)
-{
-       CONF_DATA const *cd;
-       virtual_server_dict_t *dict;
-
-       cd = cf_data_find_in_parent(ci, virtual_server_dict_t, "dictionary");
-       if (!cd) return NULL;
-
-       dict = (virtual_server_dict_t *) cf_data_value(cd);
-
-       return dict->dict;
-}
-
-/** Verify that a given virtual_server exists and is of a particular namespace
- *
- * Mostly used by modules to check virtual servers specified by their configs.
- *
- * @param[out] out             we found. May be NULL if just checking for existence.
- * @param[in] virtual_server   to check.
- * @param[in] namespace                the virtual server must belong to.
- * @param[in] ci               to log errors against. May be NULL if caller
- *                             doesn't want errors logged.
- * @return
- *     - 0 on success.
- *     - -1 if no virtual server could be found.
- *     - -2 if virtual server is not of the correct namespace.
- */
-int virtual_server_has_namespace(CONF_SECTION **out,
-                                char const *virtual_server, fr_dict_t const *namespace, CONF_ITEM *ci)
-{
-       CONF_SECTION    *server_cs;
-       fr_dict_t const *dict;
-
-       if (out) *out = NULL;
-
-       server_cs = virtual_server_find(virtual_server);
-       if (!server_cs) {
-               if (ci) cf_log_err(ci, "Can't find virtual server \"%s\"", virtual_server);
-               return -1;
-       }
-       dict = virtual_server_namespace(virtual_server);
-       if (!dict) {
-               /*
-                *      Not sure this is even a valid state?
-                */
-               if (ci) cf_log_err(ci, "No namespace found in virtual server \"%s\"", virtual_server);
-               return -2;
-       }
-
-       if (dict != namespace) {
-               if (ci) {
-                       cf_log_err(ci,
-                                  "Expected virtual server \"%s\" to be of namespace \"%s\", got namespace \"%s\"",
-                                  virtual_server, fr_dict_root(namespace)->name, fr_dict_root(dict)->name);
-               }
-               return -2;
-       }
-
-       if (out) *out = server_cs;
-
-       return 0;
-}
-
-int virtual_servers_init(CONF_SECTION *config)
-{
-       virtual_server_root = config;
-
-       if (fr_dict_autoload(virtual_server_dict_autoload) < 0) {
-               PERROR("%s", __FUNCTION__);
-               return -1;
-       }
-       if (fr_dict_attr_autoload(virtual_server_dict_attr_autoload) < 0) {
-               PERROR("%s", __FUNCTION__);
-               fr_dict_autofree(virtual_server_dict_autoload);
-               return -1;
-       }
-
-       MEM(listen_addr_root = fr_rb_inline_alloc(NULL, fr_listen_t, virtual_server_node, listen_addr_cmp, NULL));
-       MEM(server_section_name_tree = fr_rb_alloc(NULL, server_section_name_cmp, NULL));
-
-       return 0;
-}
-
-int virtual_servers_free(void)
+/** Wrapper for the config parser to allow pass1 resolution of virtual servers
+ *
+ */
+int virtual_server_cf_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
+                           CONF_ITEM *ci, UNUSED CONF_PARSER const *rule)
 {
-       TALLOC_FREE(listen_addr_root);
-       TALLOC_FREE(server_section_name_tree);
+       CONF_SECTION    *server_cs;
 
-       fr_dict_autofree(virtual_server_dict_autoload);
+       server_cs = virtual_server_find(cf_pair_value(cf_item_to_pair(ci)));
+       if (!server_cs) {
+               cf_log_err(ci, "virtual-server \"%s\" not found", cf_pair_value(cf_item_to_pair(ci)));
+               return -1;
+       }
+
+       *((CONF_SECTION **)out) = server_cs;
 
        return 0;
 }
@@ -1279,7 +853,6 @@ unlang_action_t process_authenticate(rlm_rcode_t *p_result, int auth_type, reque
        request->module = NULL;
        request->component = "authenticate";
 
-
        if (unlang_interpret_push_section(request, subcs, RLM_MODULE_REJECT, UNLANG_TOP_FRAME) < 0) {
                RETURN_MODULE_FAIL;
        }
@@ -1555,3 +1128,318 @@ virtual_server_method_t const *virtual_server_section_methods(char const *name1,
 
        return entry->methods;
 }
+
+/** Define a values for Auth-Type attributes by the sections present in a virtual-server
+ *
+ * The ident2 value of any sections found will be converted into values of the specified da.
+ *
+ * @param[in] server_cs                The virtual server containing the sections.
+ * @param[in] subcs_name       of the subsection to search for.
+ * @param[in] da               to add enumeration values for.
+ * @return
+ *     - 0 all values added successfully.
+ *     - -1 an error occurred.
+ */
+int virtual_server_section_attribute_define(CONF_SECTION *server_cs, char const *subcs_name, fr_dict_attr_t const *da)
+{
+       int                     rcode = 0;
+       CONF_SECTION            *subcs = NULL;
+
+       fr_assert(strcmp(cf_section_name1(server_cs), "server") == 0);
+
+       while ((subcs = cf_section_find_next(server_cs, subcs, subcs_name, CF_IDENT_ANY))) {
+               char const      *name2;
+               fr_dict_enum_value_t    *dv;
+
+               name2 = cf_section_name2(subcs);
+               if (!name2) {
+                       cf_log_err(subcs, "Invalid '%s { ... }' section, it must have a name", subcs_name);
+                       return -1;
+               }
+
+               /*
+                *      If the value already exists, don't
+                *      create it again.
+                */
+               dv = fr_dict_enum_by_name(da, name2, -1);
+               if (dv) continue;
+
+               cf_log_debug(subcs, "Creating %s = %s", da->name, name2);
+
+               /*
+                *      Create a new unique value with a meaningless
+                *      number.  You can't look at it from outside of
+                *      this code, so it doesn't matter.  The only
+                *      requirement is that it's unique.
+                */
+               if (fr_dict_enum_add_name_next(fr_dict_attr_unconst(da), name2) < 0) {
+                       PERROR("Failed adding section value");
+                       return -1;
+               }
+
+               rcode = 1;
+       }
+
+       return rcode;
+}
+
+/** Open all the listen sockets
+ *
+ * @param[in] sc       Scheduler to add I/O paths to.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int virtual_servers_open(fr_schedule_t *sc)
+{
+       size_t i, server_cnt = virtual_servers ? talloc_array_length(virtual_servers) : 0;
+
+       fr_assert(virtual_servers);
+
+       DEBUG2("#### Opening listener interfaces ####");
+       fr_strerror_clear();
+
+       for (i = 0; i < server_cnt; i++) {
+               fr_virtual_listen_t     **listeners;
+               size_t                  j, listener_cnt;
+
+               listeners = virtual_servers[i]->listeners;
+               listener_cnt = talloc_array_length(listeners);
+
+               for (j = 0; j < listener_cnt; j++) {
+                       fr_virtual_listen_t *listener = listeners[j];
+
+                       fr_assert(listener != NULL);
+                       fr_assert(listener->proto_mi != NULL);
+                       fr_assert(listener->proto_module != NULL);
+
+                       /*
+                        *      The socket is opened with app_instance,
+                        *      but all subsequent calls (network.c, etc.) use app_io_instance.
+                        *
+                        *      The reason is that we call (for example) proto_radius to
+                        *      open the socket, and proto_radius is responsible for setting up
+                        *      proto_radius_udp, and then calling proto_radius_udp->open.
+                        *
+                        *      Even then, proto_radius usually calls fr_master_io_listen() in order
+                        *      to create the fr_listen_t structure.
+                        */
+                       if (listener->proto_module->open &&
+                           listener->proto_module->open(listener->proto_mi->dl_inst->data, sc,
+                                                        listener->proto_mi->dl_inst->conf) < 0) {
+                               cf_log_err(listener->proto_mi->dl_inst->conf,
+                                          "Opening %s I/O interface failed",
+                                          listener->proto_module->common.name);
+                               return -1;
+                       }
+
+                       /*
+                        *      Socket information is printed out by
+                        *      the socket handlers.  e.g. proto_radius_udp
+                        */
+                       DEBUG3("Opened listener for %s", listener->proto_module->common.name);
+               }
+       }
+
+       return 0;
+}
+
+/** Free thread-specific data for all process modules and listeners
+ *
+ */
+void virtual_servers_thread_detach(void)
+{
+       modules_thread_detach(proto_modules);
+       modules_thread_detach(process_modules);
+}
+
+/** Perform thread instantiation for all process modules and listeners
+ *
+ */
+int virtual_servers_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el)
+{
+       if (modules_thread_instantiate(ctx, process_modules, el) < 0) return -1;
+       if (modules_thread_instantiate(ctx, proto_modules, el) < 0) {
+               modules_thread_detach(process_modules);
+               return -1;
+       }
+       return 0;
+}
+
+/** Instantiate all the virtual servers
+ *
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int virtual_servers_instantiate(void)
+{
+       size_t  i, server_cnt = virtual_servers ? talloc_array_length(virtual_servers) : 0;
+
+       fr_assert(virtual_servers);
+
+       DEBUG2("#### Instantiating listeners ####");
+
+       if (fr_command_register_hook(NULL, NULL, virtual_server_root, cmd_table) < 0) {
+               PERROR("Failed registering radmin commands for virtual servers");
+               return -1;
+       }
+
+       for (i = 0; i < server_cnt; i++) {
+               fr_virtual_listen_t             **listeners;
+               size_t                          j, listener_cnt;
+               CONF_ITEM                       *ci = NULL;
+               CONF_SECTION                    *server_cs = virtual_servers[i]->server_cs;
+               fr_virtual_server_t const       *vs = virtual_servers[i];
+               fr_process_module_t const       *process = (fr_process_module_t const *)
+                                                           vs->process_mi->dl_inst->module->common;
+               listeners = virtual_servers[i]->listeners;
+               listener_cnt = talloc_array_length(listeners);
+
+               DEBUG("Compiling policies in server %s { ... }", cf_section_name2(server_cs));
+
+               for (j = 0; j < listener_cnt; j++) {
+                       fr_virtual_listen_t *listener = listeners[j];
+
+                       fr_assert(listener != NULL);
+                       fr_assert(listener->proto_mi != NULL);
+                       fr_assert(listener->proto_module != NULL);
+
+                       if (module_instantiate(listener->proto_mi) < 0) {
+                               cf_log_perr(listener->proto_mi->dl_inst->conf,
+                                           "Failed instantiating listener");
+                               return -1;
+                       }
+               }
+
+               fr_assert(virtual_servers[i]->process_mi);
+
+               /*
+                *      Complete final instantiation of the process module
+                */
+               if (module_instantiate(virtual_servers[i]->process_mi) < 0) {
+                       cf_log_perr(virtual_servers[i]->process_mi->dl_inst->conf,
+                                   "Failed instantiating process module");
+                       return -1;
+               }
+
+               /*
+                *      Compile the processing sections indicated by
+                *      the process module.
+                */
+               if (process->compile_list) {
+                       tmpl_rules_t            parse_rules;
+
+                       memset(&parse_rules, 0, sizeof(parse_rules));
+                       parse_rules.attr.dict_def = *(process->dict);
+                       fr_assert(parse_rules.attr.dict_def != NULL);
+
+                       if (virtual_server_compile_sections(server_cs, process->compile_list, &parse_rules,
+                                                           vs->process_mi->dl_inst->data) < 0) {
+                               return -1;
+                       }
+               }
+
+               /*
+                *      Print out warnings for unused "recv" and
+                *      "send" sections.
+                *
+                *      @todo - check against the "compile_list"
+                *      registered for this virtual server, instead of hard-coding stuff.
+                */
+               while ((ci = cf_item_next(server_cs, ci))) {
+                       char const      *name;
+                       CONF_SECTION    *subcs;
+
+                       if (!cf_item_is_section(ci)) continue;
+
+                       subcs = cf_item_to_section(ci);
+                       name = cf_section_name1(subcs);
+
+                       /*
+                        *      Skip known "other" sections
+                        */
+                       if ((strcmp(name, "listen") == 0) || (strcmp(name, "client") == 0)) continue;
+
+                       /*
+                        *      For every other section, warn if it hasn't
+                        *      been compiled.
+                        */
+                       if (!cf_data_find(subcs, unlang_group_t, NULL)) {
+                               char const *name2;
+
+                               name2 = cf_section_name2(subcs);
+                               if (!name2) name2 = "";
+
+                               cf_log_warn(subcs, "%s %s { ... } section is unused", name, name2);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/** Load protocol modules and call their bootstrap methods
+ *
+ * @param[in] config   section containing the virtual servers to bootstrap.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int virtual_servers_bootstrap(CONF_SECTION *config)
+{
+       virtual_server_root = config;
+
+       if (modules_bootstrap(process_modules) < 0) return -1;
+       if (modules_bootstrap(proto_modules) < 0) return -1;
+
+       return 0;
+}
+
+void virtual_servers_free(void)
+{
+       TALLOC_FREE(listen_addr_root);
+       TALLOC_FREE(server_section_name_tree);
+       TALLOC_FREE(process_modules);
+       TALLOC_FREE(proto_modules);
+       fr_dict_autofree(virtual_server_dict_autoload);
+}
+
+static void _virtual_servers_atexit(UNUSED void *uctx)
+{
+       virtual_servers_free();
+}
+
+/** Performs global initialisation for the virtual server code
+ *
+ * This has to be done separately and explicitly, because the above code makes
+ * use of "onread" callbacks.
+ *
+ * Will automatically free module lists on exit, but all modules should have
+ * been removed from this list by the point that happens.
+ */
+int virtual_servers_init(void)
+{
+       if (fr_dict_autoload(virtual_server_dict_autoload) < 0) {
+               PERROR("%s", __FUNCTION__);
+               return -1;
+       }
+       if (fr_dict_attr_autoload(virtual_server_dict_attr_autoload) < 0) {
+               PERROR("%s", __FUNCTION__);
+               fr_dict_autofree(virtual_server_dict_autoload);
+               return -1;
+       }
+
+       MEM(process_modules = module_list_alloc(NULL, "process"));
+       MEM(proto_modules = module_list_alloc(NULL, "protocol"));
+       MEM(listen_addr_root = fr_rb_inline_alloc(NULL, fr_listen_t, virtual_server_node, listen_addr_cmp, NULL));
+       MEM(server_section_name_tree = fr_rb_alloc(NULL, server_section_name_cmp, NULL));
+
+       /*
+        *      Create a list to hold all the proto_* modules
+        *      that get loaded during startup.
+        */
+       fr_atexit_global(_virtual_servers_atexit, NULL);
+
+       return 0;
+}
index 7e712d79f7241c44c3c027af1a3590f8eb253395..27c12ad377afb5e7417300e4e8e34fcfb087372a 100644 (file)
@@ -36,32 +36,19 @@ extern "C" {
 extern const CONF_PARSER virtual_servers_config[];
 extern const CONF_PARSER virtual_servers_on_read_config[];
 
-/** @name Parsing, bootstrap and instantiation
+/** @name Namespace management
  *
  * @{
  */
-void           virtual_server_dict_set(CONF_SECTION *server_cs, fr_dict_t const *dict, bool do_free) CC_HINT(nonnull);
-
-fr_dict_t const *virtual_server_dict(CONF_SECTION *server_cs) CC_HINT(nonnull);
-
-int            virtual_server_section_attribute_define(CONF_SECTION *server_cs, char const *subcs_name,
-                                                       fr_dict_attr_t const *da) CC_HINT(nonnull);
+fr_dict_t const        *virtual_server_dict_by_name(char const *virtual_server) CC_HINT(nonnull);
 
-int            virtual_servers_instantiate(void);
+fr_dict_t const *virtual_server_dict_by_cs(CONF_SECTION const *server_cs) CC_HINT(nonnull);
 
-int            virtual_servers_bootstrap(CONF_SECTION *config) CC_HINT(nonnull);
-
-int            virtual_servers_init(CONF_SECTION *config) CC_HINT(nonnull);
-
-int            virtual_servers_free(void);
-
-/** @} */
+fr_dict_t const *virtual_server_dict_by_child_ci(CONF_ITEM const *ci) CC_HINT(nonnull);
 
-/** @name Runtime management
- *
- * @{
- */
-int            virtual_servers_open(fr_schedule_t *sc);
+int            virtual_server_has_namespace(CONF_SECTION **out,
+                                            char const *virtual_server, fr_dict_t const *namespace,
+                                            CONF_ITEM *ci) CC_HINT(nonnull(2,3));
 /** @} */
 
 /** @name Lookup and namespace management
@@ -74,14 +61,6 @@ CONF_SECTION *virtual_server_by_child(CONF_SECTION *section) CC_HINT(nonnull);
 
 int            virtual_server_cf_parse(TALLOC_CTX *ctx, void *out, void *parent,
                                        CONF_ITEM *ci, CONF_PARSER const *rule) CC_HINT(nonnull(2,4));
-
-fr_dict_t const        *virtual_server_namespace(char const *virtual_server) CC_HINT(nonnull);
-
-fr_dict_t const *virtual_server_namespace_by_ci(CONF_ITEM *ci) CC_HINT(nonnull);
-
-int            virtual_server_has_namespace(CONF_SECTION **out,
-                                            char const *virtual_server, fr_dict_t const *namespace,
-                                            CONF_ITEM *ci) CC_HINT(nonnull(2,3));
 /** @} */
 unlang_action_t process_authenticate(rlm_rcode_t *p_result, int auth_type,
                                     request_t *request, CONF_SECTION *server_cs) CC_HINT(nonnull);
@@ -128,7 +107,27 @@ virtual_server_method_t const *virtual_server_section_methods(char const *name1,
 
 unlang_action_t        virtual_server_push(request_t *request, CONF_SECTION *server_cs, bool top_frame) CC_HINT(nonnull);
 
-int            virtual_server_dynamic_clients_allow(CONF_SECTION *server_cs) CC_HINT(nonnull);
+/** @name Parsing, bootstrap and instantiation
+ *
+ * @{
+ */
+int            virtual_server_section_attribute_define(CONF_SECTION *server_cs, char const *subcs_name,
+                                                       fr_dict_attr_t const *da) CC_HINT(nonnull);
+
+int            virtual_servers_open(fr_schedule_t *sc);
+
+void           virtual_servers_thread_detach(void);
+
+int            virtual_servers_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el) CC_HINT(nonnull);
+
+int            virtual_servers_instantiate(void) CC_HINT(nonnull);
+
+int            virtual_servers_bootstrap(CONF_SECTION *config) CC_HINT(nonnull);
+
+void           virtual_servers_free(void);
+
+int            virtual_servers_init(void) CC_HINT(nonnull);
+/** @} */
 
 #ifdef __cplusplus
 }
index bf6586e2fe20afe280a2947f5550e33ad4fb20c1..6bd86da28d6d17841e93c3af4f2658bb4d04798b 100644 (file)
@@ -164,7 +164,7 @@ unlang_action_t unlang_call_push(request_t *request, CONF_SECTION *server_cs, bo
        /*
         *      Temporary hack until packet->code is removed
         */
-       dict = virtual_server_dict(server_cs);
+       dict = virtual_server_dict_by_cs(server_cs);
        if (!dict) {
                REDEBUG("Virtual server \"%s\" not compiled", cf_section_name2(server_cs));
                return UNLANG_ACTION_FAIL;
index 5e129c7a3e65e18c5165295fcb7386cd83cddd1d..24d849022d05f23448795eb0fb40a922c99d1018 100644 (file)
@@ -3678,7 +3678,7 @@ static unlang_t *compile_call(unlang_t *parent, unlang_compile_t *unlang_ctx, CO
        /*
         *      The dictionaries are not compatible, forbid it.
         */
-       dict = virtual_server_namespace(server);
+       dict = virtual_server_dict_by_name(server);
        if (!dict) {
                cf_log_err(cs, "Cannot call virtual server '%s', failed retrieving its namespace",
                           server);
index c8f431bca169c8e65bc8d1661411b1d9900612fe..06a3d75a070b56255ea8ef4238794021abea8ccc 100644 (file)
@@ -590,17 +590,17 @@ unlang_action_t unlang_module_yield(request_t *request,
 /*
  *     Lock the mutex for the module
  */
-static inline CC_HINT(always_inline) void safe_lock(module_instance_t *instance)
+static inline CC_HINT(always_inline) void safe_lock(module_instance_t *mi)
 {
-       if (instance->mutex) pthread_mutex_lock(instance->mutex);
+       if ((mi->module->type & MODULE_TYPE_THREAD_UNSAFE) != 0) pthread_mutex_lock(&mi->mutex);
 }
 
 /*
  *     Unlock the mutex for the module
  */
-static inline CC_HINT(always_inline) void safe_unlock(module_instance_t *instance)
+static inline CC_HINT(always_inline) void safe_unlock(module_instance_t *mi)
 {
-       if (instance->mutex) pthread_mutex_unlock(instance->mutex);
+       if ((mi->module->type & MODULE_TYPE_THREAD_UNSAFE) != 0) pthread_mutex_unlock(&mi->mutex);
 }
 
 /** Send a signal (usually stop) to a request
index 8c16142c55ec043f684db2b22f4370dc3898ef21..2115ec8e61d40c78f4461605f0e96455087aa0e5 100644 (file)
@@ -158,7 +158,7 @@ static xlat_thread_inst_t *xlat_thread_inst_alloc(TALLOC_CTX *ctx, fr_event_list
                module_ctx_t *mctx;
 
                mctx = module_ctx_from_inst(xt, call->func->mctx);
-               mctx->thread = module_thread_by_data(mctx->inst->data)->data;
+               mctx->thread = module_rlm_thread_by_data(mctx->inst->data)->data;
 
                xt->mctx = mctx;
        }
index 47dd352677e3ffedc292dc9702ca6f44891bb18d..8e2020c475faba21df9858d1139bf5b6c3dac7e9 100644 (file)
@@ -629,7 +629,7 @@ int                 fr_dict_const_free(fr_dict_t const **dict, char const *dependent) CC_HINT(
  *
  * @{
  */
-fr_dict_gctx_t const   *fr_dict_global_ctx_init(TALLOC_CTX *ctx, char const *dict_dir);
+fr_dict_gctx_t const   *fr_dict_global_ctx_init(TALLOC_CTX *ctx, bool free_at_exit, char const *dict_dir);
 
 void                   fr_dict_global_ctx_set(fr_dict_gctx_t const *gctx);
 
@@ -639,7 +639,7 @@ int                 fr_dict_global_ctx_dir_set(char const *dict_dir);
 
 void                   fr_dict_global_ctx_read_only(void);
 
-void                   fr_dict_global_ctx_debug(void);
+void                   fr_dict_global_ctx_debug(fr_dict_gctx_t const *gctx);
 
 char const             *fr_dict_global_ctx_dir(void);
 
index 68a25fa9667c513ee2b7cb87b71c696df5a4b66b..585e68759f32d57d02205c7a42204cf00693e567 100644 (file)
@@ -111,7 +111,10 @@ struct fr_dict {
 };
 
 struct fr_dict_gctx_s {
+       bool                    free_at_exit;           //!< This gctx will be freed on exit.
+
        bool                    read_only;
+
        char                    *dict_dir_default;      //!< The default location for loading dictionaries if one
                                                        ///< wasn't provided.
 
index 54918f165a7216b0998b8f534ca2ac4250c0866b..9a9c04903beeed0dc69d7f0877ae2aeea5be41c4 100644 (file)
@@ -234,7 +234,7 @@ int fr_dict_test_init(TALLOC_CTX *ctx, fr_dict_t **dict_p, fr_dict_test_attr_t c
        fr_dict_gctx_t const    *our_dict_gctx;
        fr_dict_t               *dict;
 
-       our_dict_gctx = fr_dict_global_ctx_init(ctx, "share/dictionary");
+       our_dict_gctx = fr_dict_global_ctx_init(ctx, false, "share/dictionary");
        if (!our_dict_gctx) return -1;
 
        if (!test_defs) test_defs = fr_dict_test_attrs;
index 93b9a271e7f6706934f1ba0326aa31436e45a346..4e3b1501bd7dc703734ff60baf2cb4969f7c7d53 100644 (file)
@@ -27,6 +27,7 @@ RCSID("$Id$")
 #include <freeradius-devel/util/proto.h>
 #include <freeradius-devel/util/rand.h>
 #include <freeradius-devel/util/syserror.h>
+#include <freeradius-devel/util/atexit.h>
 
 #ifdef HAVE_SYS_STAT_H
 #  include <sys/stat.h>
@@ -3617,12 +3618,23 @@ static int _dict_validation_onload(dl_t const *dl, void *symbol, UNUSED void *us
        return 0;
 }
 
+static void _dict_global_free_at_exit(void *uctx)
+{
+       talloc_free(uctx);
+}
+
 static int _dict_global_free(fr_dict_gctx_t *gctx)
 {
        fr_hash_iter_t  iter;
        fr_dict_t       *dict;
        bool            still_loaded = false;
 
+       /*
+        *      Make sure this doesn't fire later and mess
+        *      things up...
+        */
+       if (gctx->free_at_exit) fr_atexit_global_disarm(true, _dict_global_free_at_exit, gctx);
+
        if (gctx->internal) {
                dict_dependent_remove(gctx->internal, "global");        /* remove our dependency */
 
@@ -3638,8 +3650,12 @@ static int _dict_global_free(fr_dict_gctx_t *gctx)
                if (talloc_free(dict) < 0) still_loaded = true;
        }
 
-       if (still_loaded) return -1;
-
+       if (still_loaded) {
+#ifndef NDEBUG
+               fr_dict_global_ctx_debug(gctx);
+#endif
+               return -1;
+       }
        /*
         *      Set this to NULL just in case the caller tries to use
         *      dict_global_init() again.
@@ -3653,13 +3669,16 @@ static int _dict_global_free(fr_dict_gctx_t *gctx)
  *
  * @note Must be called before any other dictionary functions.
  *
- * @param[in] ctx      to allocate global resources in.
- * @param[in] dict_dir the default location for the dictionaries.
+ * @param[in] ctx              to allocate global resources in.
+ * @param[in] free_at_exit     Install an at_exit handler to free the global ctx.
+ *                             This is useful when dictionaries are held by other
+ *                             libraries which free them using atexit handlers.
+ * @param[in] dict_dir         the default location for the dictionaries.
  * @return
  *     - A pointer to the new global context on success.
  *     - NULL on failure.
  */
-fr_dict_gctx_t const *fr_dict_global_ctx_init(TALLOC_CTX *ctx, char const *dict_dir)
+fr_dict_gctx_t const *fr_dict_global_ctx_init(TALLOC_CTX *ctx, bool free_at_exit, char const *dict_dir)
 {
        fr_dict_gctx_t *new_ctx;
 
@@ -3696,11 +3715,14 @@ fr_dict_gctx_t const *fr_dict_global_ctx_init(TALLOC_CTX *ctx, char const *dict_
 
        if (dl_symbol_init_cb_register(new_ctx->dict_loader, 0, "dict_protocol",
                                       _dict_validation_onload, NULL) < 0) goto error;
+       new_ctx->free_at_exit = free_at_exit;
 
        talloc_set_destructor(new_ctx, _dict_global_free);
 
        if (!dict_gctx) dict_gctx = new_ctx;    /* Set as the default */
 
+       if (free_at_exit) fr_atexit_global(_dict_global_free_at_exit, new_ctx);
+
        return new_ctx;
 }
 
@@ -3783,22 +3805,24 @@ void fr_dict_global_ctx_read_only(void)
  *
  * Intended to be called from a debugger
  */
-void fr_dict_global_ctx_debug(void)
+void fr_dict_global_ctx_debug(fr_dict_gctx_t const *gctx)
 {
        fr_hash_iter_t                  dict_iter;
        fr_dict_t                       *dict;
-       fr_rb_iter_inorder_t    dep_iter;
+       fr_rb_iter_inorder_t            dep_iter;
        fr_dict_dependent_t             *dep;
 
-       if (!dict_gctx) {
+       if (gctx == NULL) gctx = dict_gctx;
+
+       if (!gctx) {
                FR_FAULT_LOG("gctx not initialised");
                return;
        }
 
        FR_FAULT_LOG("gctx %p report", dict_gctx);
-       for (dict = fr_hash_table_iter_init(dict_gctx->protocol_by_num, &dict_iter);
+       for (dict = fr_hash_table_iter_init(gctx->protocol_by_num, &dict_iter);
             dict;
-            dict = fr_hash_table_iter_next(dict_gctx->protocol_by_num, &dict_iter)) {
+            dict = fr_hash_table_iter_next(gctx->protocol_by_num, &dict_iter)) {
                for (dep = fr_rb_iter_init_inorder(&dep_iter, dict->dependents);
                     dep;
                     dep = fr_rb_iter_next_inorder(&dep_iter)) {
@@ -3806,11 +3830,11 @@ void fr_dict_global_ctx_debug(void)
                }
        }
 
-       if (dict_gctx->internal) {
-               for (dep = fr_rb_iter_init_inorder(&dep_iter, dict_gctx->internal->dependents);
+       if (gctx->internal) {
+               for (dep = fr_rb_iter_init_inorder(&dep_iter, gctx->internal->dependents);
                     dep;
                     dep = fr_rb_iter_next_inorder(&dep_iter)) {
-                       FR_FAULT_LOG("\t%s refs %s (%u)", dict_gctx->internal->root->name, dep->dependent, dep->count);
+                       FR_FAULT_LOG("\t%s refs %s (%u)", gctx->internal->root->name, dep->dependent, dep->count);
                }
        }
 }
index 5580a2e7dd9a3a152fc047c9401d3989e09827df..fba985a2129debfd7d396089778ee215da6da482 100644 (file)
@@ -317,6 +317,8 @@ TALLOC_CTX *fr_log_pool_init(void)
 
        pool = fr_log_pool;
        if (unlikely(!pool)) {
+               if (fr_atexit_is_exiting()) return NULL;        /* No new pools if we're exiting */
+
                pool = talloc_pool(NULL, 16384);
                if (!pool) {
                        fr_perror("Failed allocating memory for vlog_request_pool");
index 03327e7a7d328597386ea40ee413e682666df4b0..24d246fe659a55d5285634462bb299f706ed7f9d 100644 (file)
@@ -259,13 +259,14 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
        parent_inst = cf_data_value(cf_data_find(inst->cs, dl_module_inst_t, "proto_arp"));
        fr_assert(parent_inst);
 
-       if (dl_module_instance(inst->cs, &inst->io_submodule, inst->cs,
-                              parent_inst, "ethernet", DL_MODULE_TYPE_SUBMODULE) < 0) {
+       if (dl_module_instance(inst->cs, &inst->io_submodule,
+                              parent_inst,
+                              DL_MODULE_TYPE_SUBMODULE, "ethernet", dl_module_inst_name_from_conf(inst->cs)) < 0) {
                cf_log_perr(inst->cs, "Failed to load proto_arp_ethernet");
                return -1;
        }
 
-       if (dl_module_conf_parse(inst->io_submodule) < 0) {
+       if (dl_module_conf_parse(inst->io_submodule, inst->cs) < 0) {
                TALLOC_FREE(inst->io_submodule);
                return -1;
        }
index 9021f8e65fd5a5419095c1a211189bf9ef97a435..56caeb5811babc6b6c7cd2fd64e5fc9f4d31ba58 100644 (file)
@@ -103,8 +103,9 @@ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF
        inst = talloc_get_type_abort(parent_inst->data, proto_control_t);
        inst->io.transport = name;
 
-       if (dl_module_instance(ctx, &dl_mod_inst, transport_cs, parent_inst, name, DL_MODULE_TYPE_SUBMODULE) < 0) return -1;
-       if (dl_module_conf_parse(dl_mod_inst) < 0) {
+       if (dl_module_instance(ctx, &dl_mod_inst, parent_inst,
+                              DL_MODULE_TYPE_SUBMODULE, name, dl_module_inst_name_from_conf(transport_cs)) < 0) return -1;
+       if (dl_module_conf_parse(dl_mod_inst, transport_cs) < 0) {
                talloc_free(dl_mod_inst);
                return -1;
        }
index 32da21adb666e911fa827dcd91b6a48c34bf43d2..aaebe9039699a2f6c2a77466d1664903f0f7abf4 100644 (file)
@@ -975,7 +975,7 @@ int main(int argc, char **argv)
                 *      Need to read in the dictionaries, else we may get
                 *      validation errors when we try and parse the config.
                 */
-               dict_gctx = fr_dict_global_ctx_init(autofree, dict_dir);
+               dict_gctx = fr_dict_global_ctx_init(NULL, true, dict_dir);
                if (!dict_gctx) {
                        fr_perror("radmin");
                        fr_exit_now(64);
index d14a3d4c88b06c9b1443e5b26ee4c5a6d7abf960..dfb472fee7d49eab3517e8ae72902d0eb2f3c6ba 100644 (file)
@@ -110,7 +110,7 @@ static int type_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM
 
        *((char const **) out) = value;
 
-       inst->dict = virtual_server_namespace_by_ci(ci);
+       inst->dict = virtual_server_dict_by_child_ci(ci);
        if (!inst->dict) {
                cf_log_err(ci, "Please define 'namespace' in this virtual server");
                return -1;
@@ -168,8 +168,9 @@ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
        parent_inst = cf_data_value(cf_data_find(listen_cs, dl_module_inst_t, "proto_cron"));
        fr_assert(parent_inst);
 
-       if (dl_module_instance(ctx, &dl_mod_inst, transport_cs, parent_inst, name, DL_MODULE_TYPE_SUBMODULE) < 0) return -1;
-       if (dl_module_conf_parse(dl_mod_inst) < 0) {
+       if (dl_module_instance(ctx, &dl_mod_inst, parent_inst,
+                              DL_MODULE_TYPE_SUBMODULE, name, dl_module_inst_name_from_conf(transport_cs)) < 0) return -1;
+       if (dl_module_conf_parse(dl_mod_inst, transport_cs) < 0) {
                talloc_free(dl_mod_inst);
                return -1;
        }
index 085e37354780c032bd52429085dea7be0082ea0b..3ebe8d91094512eb726a7561808216b2ef7dc773 100644 (file)
@@ -121,7 +121,7 @@ static int type_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM
 
        *((char const **) out) = value;
 
-       inst->dict = virtual_server_namespace_by_ci(ci);
+       inst->dict = virtual_server_dict_by_child_ci(ci);
        if (!inst->dict) {
                cf_log_err(ci, "Please define 'namespace' in this virtual server");
                return -1;
@@ -179,8 +179,9 @@ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
        parent_inst = cf_data_value(cf_data_find(listen_cs, dl_module_inst_t, "proto_detail"));
        fr_assert(parent_inst);
 
-       if (dl_module_instance(ctx, &dl_mod_inst, transport_cs, parent_inst, name, DL_MODULE_TYPE_SUBMODULE) < 0) return -1;
-       if (dl_module_conf_parse(dl_mod_inst) < 0) {
+       if (dl_module_instance(ctx, &dl_mod_inst, parent_inst,
+                              DL_MODULE_TYPE_SUBMODULE, name, dl_module_inst_name_from_conf(transport_cs)) < 0) return -1;
+       if (dl_module_conf_parse(dl_mod_inst, transport_cs) < 0) {
                talloc_free(dl_mod_inst);
                return -1;
        }
@@ -547,13 +548,14 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
                        }
                }
 
-               if (dl_module_instance(inst->cs, &inst->work_submodule, transport_cs,
-                               parent_inst, "work", DL_MODULE_TYPE_SUBMODULE) < 0) {
+               if (dl_module_instance(inst->cs, &inst->work_submodule,
+                                      parent_inst,
+                                      DL_MODULE_TYPE_SUBMODULE, "work", dl_module_inst_name_from_conf(transport_cs)) < 0) {
                        cf_log_perr(inst->cs, "Failed to load proto_detail_work");
                        return -1;
                }
 
-               if (dl_module_conf_parse(inst->work_submodule) < 0) {
+               if (dl_module_conf_parse(inst->work_submodule, transport_cs) < 0) {
                        TALLOC_FREE(inst->work_submodule);
                        return -1;
                }
index 305b35a96628404e2fde4e680a780e6007e72f13..8146e64caa8e38276036f3f3d6c89a042fcf48dd 100644 (file)
@@ -176,8 +176,9 @@ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
        inst = talloc_get_type_abort(parent_inst->data, proto_dhcpv4_t);
        inst->io.transport = name;
 
-       if (dl_module_instance(ctx, &dl_mod_inst, transport_cs, parent_inst, name, DL_MODULE_TYPE_SUBMODULE) < 0) return -1;
-       if (dl_module_conf_parse(dl_mod_inst) < 0) {
+       if (dl_module_instance(ctx, &dl_mod_inst, parent_inst,
+                              DL_MODULE_TYPE_SUBMODULE, name, dl_module_inst_name_from_conf(transport_cs)) < 0) return -1;
+       if (dl_module_conf_parse(dl_mod_inst, transport_cs) < 0) {
                talloc_free(dl_mod_inst);
                return -1;
        }
index 5f35c47d93dfdb658f0b52c8c919689e1c9e35ba..f301737a63bf1a40e1de009fb5d85d7c4e558a30 100644 (file)
@@ -176,8 +176,9 @@ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
        inst = talloc_get_type_abort(parent_inst->data, proto_dhcpv6_t);
        inst->io.transport = name;
 
-       if (dl_module_instance(ctx, &dl_mod_inst, transport_cs, parent_inst, name, DL_MODULE_TYPE_SUBMODULE) < 0) return -1;
-       if (dl_module_conf_parse(dl_mod_inst) < 0) {
+       if (dl_module_instance(ctx, &dl_mod_inst, parent_inst,
+                              DL_MODULE_TYPE_SUBMODULE, name, dl_module_inst_name_from_conf(transport_cs)) < 0) return -1;
+       if (dl_module_conf_parse(dl_mod_inst, transport_cs) < 0) {
                talloc_free(dl_mod_inst);
                return -1;
        }
index 94f19a6645bb852fdd7ae034d146a2e0cf494150..5d3621c780ccc2ca2f85f40ecc47a2ca7c892cb3 100644 (file)
@@ -157,8 +157,9 @@ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
        inst = talloc_get_type_abort(parent_inst->data, proto_dns_t);
        inst->io.transport = name;
 
-       if (dl_module_instance(ctx, &dl_mod_inst, transport_cs, parent_inst, name, DL_MODULE_TYPE_SUBMODULE) < 0) return -1;
-       if (dl_module_conf_parse(dl_mod_inst) < 0) {
+       if (dl_module_instance(ctx, &dl_mod_inst, parent_inst,
+                              DL_MODULE_TYPE_SUBMODULE, name, dl_module_inst_name_from_conf(transport_cs)) < 0) return -1;
+       if (dl_module_conf_parse(dl_mod_inst, transport_cs) < 0) {
                talloc_free(dl_mod_inst);
                return -1;
        }
index 481e77f8209ad31223f6b711d516d6b8a5a819c6..ca12d6de824a88280a762b54960281069bcdf48b 100644 (file)
@@ -110,7 +110,7 @@ static int type_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM
 
        *((char const **) out) = value;
 
-       inst->dict = virtual_server_namespace_by_ci(ci);
+       inst->dict = virtual_server_dict_by_child_ci(ci);
        if (!inst->dict) {
                cf_log_err(ci, "Please define 'namespace' in this virtual server");
                return -1;
@@ -168,8 +168,9 @@ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
        parent_inst = cf_data_value(cf_data_find(listen_cs, dl_module_inst_t, "proto_load"));
        fr_assert(parent_inst);
 
-       if (dl_module_instance(ctx, &dl_mod_inst, transport_cs, parent_inst, name, DL_MODULE_TYPE_SUBMODULE) < 0) return -1;
-       if (dl_module_conf_parse(dl_mod_inst) < 0) {
+       if (dl_module_instance(ctx, &dl_mod_inst, parent_inst,
+                              DL_MODULE_TYPE_SUBMODULE, name, dl_module_inst_name_from_conf(transport_cs)) < 0) return -1;
+       if (dl_module_conf_parse(dl_mod_inst, transport_cs) < 0) {
                talloc_free(dl_mod_inst);
                return -1;
        }
index c1e36a7a281dee9612ff65cd309cbcf1b1609051..7c2e4576f86ce6994ed6b3484edfa12b5b59ce33 100644 (file)
@@ -183,8 +183,9 @@ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF
                inst->io.app_io_conf = transport_cs;
        }
 
-       if (dl_module_instance(ctx, &dl_mod_inst, transport_cs, parent_inst, name, DL_MODULE_TYPE_SUBMODULE) < 0) return -1;
-       if (dl_module_conf_parse(dl_mod_inst) < 0) {
+       if (dl_module_instance(ctx, &dl_mod_inst, parent_inst,
+                              DL_MODULE_TYPE_SUBMODULE, name, dl_module_inst_name_from_conf(transport_cs)) < 0) return -1;
+       if (dl_module_conf_parse(dl_mod_inst, transport_cs) < 0) {
                talloc_free(dl_mod_inst);
                return -1;
        }
index 2cf64ea61731c23e8740341cc12dfa603bb92f14..af1569d22bf67a06e0a2d85c24f02dad62eafc9b 100644 (file)
@@ -156,8 +156,9 @@ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF
                inst->io.app_io_conf = transport_cs;
        }
 
-       if (dl_module_instance(ctx, &dl_mod_inst, transport_cs, parent_inst, name, DL_MODULE_TYPE_SUBMODULE) < 0) return -1;
-       if (dl_module_conf_parse(dl_mod_inst) < 0) {
+       if (dl_module_instance(ctx, &dl_mod_inst, parent_inst,
+                              DL_MODULE_TYPE_SUBMODULE, name, dl_module_inst_name_from_conf(transport_cs)) < 0) return -1;
+       if (dl_module_conf_parse(dl_mod_inst, transport_cs) < 0) {
                talloc_free(dl_mod_inst);
                return -1;
        }
index 5f88ecf4524e77fd751eb0fd36871819dc250af0..1d9ce7ffbd4aae56cd12a675ba950ff3ba6453b3 100644 (file)
@@ -165,8 +165,9 @@ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
        inst = talloc_get_type_abort(parent_inst->data, proto_vmps_t);
        inst->io.transport = name;
 
-       if (dl_module_instance(ctx, &dl_mod_inst, transport_cs, parent_inst, name, DL_MODULE_TYPE_SUBMODULE) < 0) return -1;
-       if (dl_module_conf_parse(dl_mod_inst) < 0) {
+       if (dl_module_instance(ctx, &dl_mod_inst, parent_inst,
+                              DL_MODULE_TYPE_SUBMODULE, name, dl_module_inst_name_from_conf(transport_cs)) < 0) return -1;
+       if (dl_module_conf_parse(dl_mod_inst, transport_cs) < 0) {
                talloc_free(dl_mod_inst);
                return -1;
        }
index 70dfe2d255bc02c740265e1a24570624d1360639..c3215168ca67607be9913eb082d077197ff06402 100644 (file)
@@ -133,7 +133,7 @@ int main(int argc, char **argv)
                fr_exit_now(EXIT_FAILURE);
        }
 
-       if (!fr_dict_global_ctx_init(autofree, dict_dir)) {
+       if (!fr_dict_global_ctx_init(NULL, true, dict_dir)) {
                fr_perror("sync_touch");
                fr_exit_now(EXIT_FAILURE);
        }
index 7964948fc111e748634e98d8b7990f6b8e196ba8..da1939670949321707173853bf5becd52eac137f 100644 (file)
@@ -119,7 +119,7 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
        rlm_always_t    *inst = talloc_get_type_abort(mctx->inst->data, rlm_always_t);
        xlat_t          *xlat;
 
-       inst->mi = module_by_name(NULL, mctx->inst->name);
+       inst->mi = module_rlm_by_name(NULL, mctx->inst->name);
        if (!inst->mi) {
                cf_log_err(mctx->inst->conf, "Can't find the module instance data for this module: %s", mctx->inst->name);
                return -1;
index 787646016f7580a21161060318164b58856b048d..7145c2458f0d0765cecb797b266457ef059d6cae 100644 (file)
@@ -318,7 +318,7 @@ extern rlm_cache_driver_t rlm_cache_memcached;
 rlm_cache_driver_t rlm_cache_memcached = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "rlm_cache_memcached",
+               .name           = "cache_memcached",
                .inst_size      = sizeof(rlm_cache_memcached_t),
                .config         = driver_config,
 
index e0ed9d692817698516c88856a7f36d3be33cd2b1..a03ece0fa03f6745e14f0fe922e1075577863962 100644 (file)
@@ -336,7 +336,7 @@ extern rlm_cache_driver_t rlm_cache_rbtree;
 rlm_cache_driver_t rlm_cache_rbtree = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "rlm_cache_rbtree",
+               .name           = "cache_rbtree",
                .instantiate    = mod_instantiate,
                .detach         = mod_detach,
                .inst_size      = sizeof(rlm_cache_rbtree_t),
index 29dd1cfc67b504f0590c0207ee4ca67375a0f0c5..d8ee23b7d60ee1b38437fddab71697b9a3a180db 100644 (file)
@@ -473,7 +473,7 @@ extern rlm_cache_driver_t rlm_cache_redis;
 rlm_cache_driver_t rlm_cache_redis = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "rlm_cache_redis",
+               .name           = "cache_redis",
                .onload         = mod_load,
                .instantiate    = mod_instantiate,
                .inst_size      = sizeof(rlm_cache_redis_t),
index a995efec8884ed983f470d3f95ccae54949aa49c..65383f71370ddfd9fcfdc90611ad7dadd62cf9da 100644 (file)
@@ -37,7 +37,7 @@ extern module_rlm_t rlm_cache;
 
 static const CONF_PARSER module_config[] = {
        { FR_CONF_OFFSET("driver", FR_TYPE_VOID, rlm_cache_t, driver_submodule), .dflt = "rbtree",
-                        .func = module_submodule_parse },
+                        .func = module_rlm_submodule_parse },
        { FR_CONF_OFFSET("key", FR_TYPE_TMPL | FR_TYPE_REQUIRED, rlm_cache_config_t, key) },
        { FR_CONF_OFFSET("ttl", FR_TYPE_TIME_DELTA, rlm_cache_config_t, ttl), .dflt = "500s" },
        { FR_CONF_OFFSET("max_entries", FR_TYPE_UINT32, rlm_cache_config_t, max_entries), .dflt = "0" },
index 6aff01df626b49364266d0f261bc0c3a0c50516f..febe5529fd5e526bd3c560fc25d7a5aa5fad999b 100644 (file)
@@ -191,7 +191,7 @@ static int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent,
                break;
        }
 #endif
-       return module_submodule_parse(ctx, out, parent, ci, rule);
+       return module_rlm_submodule_parse(ctx, out, parent, ci, rule);
 }
 
 /** Convert EAP type strings to eap_type_t values
index 8ec4a2133e82369728814eb3decc1022357af0af..431b24ad4077344590f5539479dc5638e6477769 100644 (file)
@@ -733,7 +733,7 @@ static rlm_rcode_t mod_map_proc(void *mod_inst, UNUSED void *proc_inst, request_
 {
        rlm_rcode_t             rcode = RLM_MODULE_UPDATED;
        rlm_ldap_t              *inst = talloc_get_type_abort(mod_inst, rlm_ldap_t);
-       fr_ldap_thread_t        *thread = talloc_get_type_abort(module_thread_by_data(inst)->data, fr_ldap_thread_t);
+       fr_ldap_thread_t        *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t);
 
        LDAPURLDesc             *ldap_url;
 
@@ -902,7 +902,7 @@ free_urldesc:
 static int rlm_ldap_groupcmp(void *instance, request_t *request, UNUSED fr_pair_list_t *request_list, fr_pair_t const *check)
 {
        rlm_ldap_t const        *inst = talloc_get_type_abort_const(instance, rlm_ldap_t);
-       fr_ldap_thread_t        *thread = talloc_get_type_abort(module_thread_by_data(inst)->data, fr_ldap_thread_t);
+       fr_ldap_thread_t        *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t);
        rlm_rcode_t             rcode;
 
        bool                    found = false;
@@ -1032,7 +1032,7 @@ cleanup:
 static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
 {
        rlm_ldap_t const        *inst = talloc_get_type_abort_const(mctx->inst->data, rlm_ldap_t);
-       fr_ldap_thread_t        *thread = talloc_get_type_abort(module_thread_by_data(inst)->data, fr_ldap_thread_t);
+       fr_ldap_thread_t        *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t);
        rlm_rcode_t             rcode;
        char const              *dn;
        fr_ldap_thread_trunk_t  *ttrunk = NULL;
@@ -1214,7 +1214,7 @@ static unlang_action_t rlm_ldap_map_profile(rlm_rcode_t *p_result, rlm_ldap_t co
 static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
 {
        rlm_ldap_t const        *inst = talloc_get_type_abort_const(mctx->inst->data, rlm_ldap_t);
-       fr_ldap_thread_t        *thread = talloc_get_type_abort(module_thread_by_data(inst)->data, fr_ldap_thread_t);
+       fr_ldap_thread_t        *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t);
        rlm_rcode_t             rcode = RLM_MODULE_OK;
        int                     ldap_errno;
        int                     i;
@@ -1442,7 +1442,7 @@ static unlang_action_t user_modify(rlm_rcode_t *p_result, rlm_ldap_t const *inst
                                   request_t *request, ldap_acct_section_t *section)
 {
        rlm_rcode_t     rcode = RLM_MODULE_OK;
-       fr_ldap_thread_t        *thread = talloc_get_type_abort(module_thread_by_data(inst)->data, fr_ldap_thread_t);
+       fr_ldap_thread_t        *thread = talloc_get_type_abort(module_rlm_thread_by_data(inst)->data, fr_ldap_thread_t);
 
        fr_ldap_thread_trunk_t  *ttrunk = NULL;
        fr_ldap_query_t         *query = NULL;
index 19b32c704e6829c8402d842519835b730bbf6025..98819eddbbc24aed59d38e972c37d87adf04acf5 100644 (file)
@@ -100,7 +100,7 @@ static CONF_PARSER disconnect_config[] = {
  */
 static CONF_PARSER const module_config[] = {
        { FR_CONF_OFFSET("transport", FR_TYPE_VOID, rlm_radius_t, io_submodule),
-         .func = module_submodule_parse },
+         .func = module_rlm_submodule_parse },
 
        { FR_CONF_OFFSET("type", FR_TYPE_UINT32 | FR_TYPE_MULTI | FR_TYPE_NOT_EMPTY | FR_TYPE_REQUIRED, rlm_radius_t, types),
          .func = type_parse },
index c689b4724b512cf11c9f362fa122ec47347edca7..13ede0cf0feadac89a1e7fe189fca3ffb7117e8e 100644 (file)
@@ -963,7 +963,7 @@ static int mod_load(void)
 extern rlm_sql_driver_t rlm_sql_cassandra;
 rlm_sql_driver_t rlm_sql_cassandra = {
        .common = {
-               .name                           = "rlm_sql_cassandra",
+               .name                           = "sql_cassandra",
                .magic                          = MODULE_MAGIC_INIT,
                .inst_size                      = sizeof(rlm_sql_cassandra_t),
                .onload                         = mod_load,
index 381da7ea92b984b84129a0c922ea288dbe50ba52..a0860efd6603a0cb1c5c3134b252e9d29d8c2c27 100644 (file)
@@ -289,7 +289,7 @@ extern rlm_sql_driver_t rlm_sql_db2;
 rlm_sql_driver_t rlm_sql_db2 = {
        .common = {
                .magic                          = MODULE_MAGIC_INIT,
-               .name                           = "rlm_sql_db2",
+               .name                           = "sql_db2",
        },
        .sql_socket_init                = sql_socket_init,
        .sql_query                      = sql_query,
index be6ef061024430c56368410d28bc7ecd56d57f50..7032454fdc182c712c4b1ab39e213e910cdf20c1 100644 (file)
@@ -294,7 +294,7 @@ static int sql_affected_rows(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t c
 extern rlm_sql_driver_t rlm_sql_firebird;
 rlm_sql_driver_t rlm_sql_firebird = {
        .common = {
-               .name                           = "rlm_sql_firebird",
+               .name                           = "sql_firebird",
                .magic                          = MODULE_MAGIC_INIT
        },
        .sql_socket_init                = sql_socket_init,
index 2440aa30e4e9740049b9a092be97d9f93e381a20..e62883e09918c8c1d4371453e60dbb4b95cb84ca 100644 (file)
@@ -817,7 +817,7 @@ extern rlm_sql_driver_t rlm_sql_freetds;
 rlm_sql_driver_t rlm_sql_freetds = {
        .common = {
                .magic                          = MODULE_MAGIC_INIT,
-               .name                           = "rlm_sql_freetds"
+               .name                           = "sql_freetds"
        },
        .sql_socket_init                = sql_socket_init,
        .sql_query                      = sql_query,
index fa127408fb5248504960673e4bfd1e4a4f1cb676..e3b2766f9d8ee0c2af7124b0ab38d92f5a7fadbc 100644 (file)
@@ -845,7 +845,7 @@ static size_t sql_escape_func(UNUSED request_t *request, char *out, size_t outle
 extern rlm_sql_driver_t rlm_sql_mysql;
 rlm_sql_driver_t rlm_sql_mysql = {
        .common = {
-               .name                           = "rlm_sql_mysql",
+               .name                           = "sql_mysql",
                .magic                          = MODULE_MAGIC_INIT,
                .inst_size                      = sizeof(rlm_sql_mysql_t),
                .onload                         = mod_load,
index 7e15b0854da4e829126ce91f385d0f5fb58287fd..b4f4e43a049f69eb1918c5e0c980cdcc5754860a 100644 (file)
@@ -100,7 +100,7 @@ extern rlm_sql_driver_t rlm_sql_null;
 rlm_sql_driver_t rlm_sql_null = {
        .common = {
                .magic                          = MODULE_MAGIC_INIT,
-               .name                           = "rlm_sql_null"
+               .name                           = "sql_null"
        },
        .sql_socket_init                = sql_socket_init,
        .sql_query                      = sql_query,
index 4bbfded94514b7325e9585e1d7fec12d9f81c6d2..843ca33c26acfcf085fb5866564a6be0e480b2d7 100644 (file)
@@ -613,7 +613,7 @@ static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t const *c
 extern rlm_sql_driver_t rlm_sql_oracle;
 rlm_sql_driver_t rlm_sql_oracle = {
        .common = {
-               .name                           = "rlm_sql_oracle",
+               .name                           = "sql_oracle",
                .magic                          = MODULE_MAGIC_INIT,
                .inst_size                      = sizeof(rlm_sql_oracle_t),
                .config                         = driver_config,
index 95aeb368561a18d0bfcddc56007bc90c880aa799..aa788718fbb1a6cb33b2a679ab8569b654e57720 100644 (file)
@@ -671,7 +671,7 @@ extern rlm_sql_driver_t rlm_sql_postgresql;
 rlm_sql_driver_t rlm_sql_postgresql = {
        .common = {
                .magic                          = MODULE_MAGIC_INIT,
-               .name                           = "rlm_sql_postgresql",
+               .name                           = "sql_postgresql",
                .inst_size                      = sizeof(rlm_sql_postgresql_t),
                .onload                         = mod_load,
                .config                         = driver_config,
index 46de6365d071046c760974452b3adc4bbf37a491..56ce5c5e63f62b4972067dcf3263766df1eb3490 100644 (file)
@@ -815,7 +815,7 @@ static int mod_load(void)
 extern rlm_sql_driver_t rlm_sql_sqlite;
 rlm_sql_driver_t rlm_sql_sqlite = {
        .common = {
-               .name                           = "rlm_sql_sqlite",
+               .name                           = "sql_sqlite",
                .magic                          = MODULE_MAGIC_INIT,
                .inst_size                      = sizeof(rlm_sql_sqlite_t),
                .config                         = driver_config,
index 855a9b850a2cf15eadae710126feb6e15c52466f..2bbaca58ac3b18eed66e25e347b283e4a1b7fea2 100644 (file)
@@ -391,7 +391,7 @@ extern rlm_sql_driver_t rlm_sql_unixodbc;
 rlm_sql_driver_t rlm_sql_unixodbc = {
        .common = {
                .magic                          = MODULE_MAGIC_INIT,
-               .name                           = "rlm_sql_unixodbc"
+               .name                           = "sql_unixodbc"
        },
        .sql_socket_init                = sql_socket_init,
        .sql_query                      = sql_query,
index 1cd997cd6506070bf0f56bc6f0702192685ac5f6..48ae1c15bc8f65dfa8562cd421c80ec21adae162 100644 (file)
@@ -83,7 +83,7 @@ static const CONF_PARSER postauth_config[] = {
 
 static const CONF_PARSER module_config[] = {
        { FR_CONF_OFFSET("driver", FR_TYPE_VOID, rlm_sql_t, driver_submodule), .dflt = "null",
-                        .func = module_submodule_parse },
+                        .func = module_rlm_submodule_parse },
        { FR_CONF_OFFSET("server", FR_TYPE_STRING, rlm_sql_config_t, sql_server), .dflt = "" }, /* Must be zero length so drivers can determine if it was set */
        { FR_CONF_OFFSET("port", FR_TYPE_UINT32, rlm_sql_config_t, sql_port), .dflt = "0" },
        { FR_CONF_OFFSET("login", FR_TYPE_STRING, rlm_sql_config_t, sql_login), .dflt = "" },
index 87375e6db4800019c8673293b04d8775037ebb1c..0883692c4ed0f4301ad69c83b9beb6f357b4811d 100644 (file)
@@ -428,7 +428,7 @@ static int mod_instantiate(module_inst_ctx_t const *mctx)
        } else {
                inst->pool_name = talloc_typed_strdup(inst, "ippool");
        }
-       sql_inst = module_by_name(NULL, inst->sql_instance_name);
+       sql_inst = module_rlm_by_name(NULL, inst->sql_instance_name);
        if (!sql_inst) {
                cf_log_err(conf, "failed to find sql instance named %s",
                           inst->sql_instance_name);
index 651fea3f222de3f3fb2fda3fa690cc377c9b430f..a947ced041b0f456467d3fd3f1b81c14d1810988 100644 (file)
@@ -244,7 +244,7 @@ extern fr_process_module_t process_arp;
 fr_process_module_t process_arp = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_arp",
+               .name           = "arp",
                .inst_size      = sizeof(process_arp_t)
        },
        .process        = mod_process,
index c303446cb31c2e13605450eed2953aae06e19454..6c4ece5fce01a1ebfc4e83bb7f69444447d8a7f5 100644 (file)
@@ -53,7 +53,7 @@ extern fr_process_module_t process_control;
 fr_process_module_t process_control = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_control"
+               .name           = "control"
        },
        .process        = mod_process,
        .dict           = &dict_freeradius
index 4d8e2eabd59b679705cbb33ca7112fa99b5a762b..b4970a462d37b81748d15de5f6761f071962f9b5 100644 (file)
@@ -453,7 +453,7 @@ extern fr_process_module_t process_dhcpv4;
 fr_process_module_t process_dhcpv4 = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_dhcpv4",
+               .name           = "dhcpv4",
                .inst_size      = sizeof(process_dhcpv4_t)
        },
        .process        = mod_process,
index 6b95f731d7d90b88e37e966f4b2f8f3ae67ca38b..bff44364664823ed1a8a6e3158bbf5274d528911 100644 (file)
@@ -1233,7 +1233,7 @@ extern fr_process_module_t process_dhcpv6;
 fr_process_module_t process_dhcpv6 = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_dhcpv6",
+               .name           = "dhcpv6",
                .config         = dhcpv6_process_config,
                .inst_size      = sizeof(process_dhcpv6_t),
 
index a28ca562cf66ef9a0c4dbb7b1ae922f061e917ce..332a6af8ac62e36126dcb89e397df88d36f0fa7f 100644 (file)
@@ -192,7 +192,7 @@ extern fr_process_module_t process_dns;
 fr_process_module_t process_dns = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_dns",
+               .name           = "dns",
                .inst_size      = sizeof(process_dns_t)
        },
        .process        = mod_process,
index d9e6c51d4dd53f5b35250b10a1771e483ce24671..0d9a08b2f3cc9fbaf83acb2ce924b7566abbd203 100644 (file)
@@ -267,7 +267,7 @@ extern fr_process_module_t process_eap_aka;
 fr_process_module_t process_eap_aka = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_eap_aka",
+               .name           = "eap_aka",
                .onload         = mod_load,
                .unload         = mod_unload,
                .config         = submodule_config,
index e0b6d5bd437b4a67044fdfd9b9ecb13f8c9ee579..4f665a1d034d49690bc9388a080853879b44e66d 100644 (file)
@@ -268,7 +268,7 @@ extern fr_process_module_t process_eap_aka_prime;
 fr_process_module_t process_eap_aka_prime = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_eap_aka_prime",
+               .name           = "eap_aka_prime",
                .onload         = mod_load,
                .unload         = mod_unload,
                .config         = submodule_config,
index afa97c75b20d73be18b5184514e3c32f7cc2de37..d98ec6d44e7f7969469d24bf65101e761f70e642 100644 (file)
@@ -255,7 +255,7 @@ extern fr_process_module_t process_eap_sim;
 fr_process_module_t process_eap_sim = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_eap_sim",
+               .name           = "eap_sim",
                .onload         = mod_load,
                .unload         = mod_unload,
                .config         = submodule_config,
index efd365c0202477f4b23775fd7892b27d1e8c28c1..db91d208451032b6d42a3f1990926f6e190361d5 100644 (file)
@@ -1127,7 +1127,7 @@ extern fr_process_module_t process_radius;
 fr_process_module_t process_radius = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_radius",
+               .name           = "radius",
                .config         = config,
                .inst_size      = sizeof(process_radius_t),
 
index 8a633d7cc613154b6ed19363b2f1d7d77c14ea82..5d1e7e7fbb7e8c86c48f0a3aa0e7282ceecdcfda 100644 (file)
@@ -707,7 +707,7 @@ extern fr_process_module_t process_tacacs;
 fr_process_module_t process_tacacs = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_tacacs",
+               .name           = "tacacs",
                .config         = config,
                .inst_size      = sizeof(process_tacacs_t),
                .bootstrap      = mod_bootstrap,
index 025aeff734fae5853c10d24f0a4d9382546d845f..85a68830b1e9e73cda6866ca2a1391a2cebfa5ac 100644 (file)
@@ -183,7 +183,7 @@ extern fr_process_module_t process_tls;
 fr_process_module_t process_tls = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_tls",
+               .name           = "tls",
                .inst_size      = sizeof(process_tls_t)
        },
        .process        = mod_process,
index ebf275eb53546dfd216f0b92b19f65699edbd32c..305de933ce1f4314db3c7eb65ed20a96263264cf 100644 (file)
@@ -821,7 +821,7 @@ extern fr_process_module_t process_ttls;
 fr_process_module_t process_ttls = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_ttls",
+               .name           = "ttls",
                .config         = config,
                .inst_size      = sizeof(process_ttls_t),
 
index 450ce25c102cc359bf9c9ea4bc3568345dc88017..1d76482820df0226936354a327b7ee5d10d63303 100644 (file)
@@ -261,7 +261,7 @@ extern fr_process_module_t process_vmps;
 fr_process_module_t process_vmps = {
        .common = {
                .magic          = MODULE_MAGIC_INIT,
-               .name           = "process_vmps",
+               .name           = "vmps",
                .inst_size      = sizeof(process_vmps_t)
        },
        .process        = mod_process,
index a15458814dedaa3df719869c5eeef7c8df4005a5..4731316e2efdf889d6fbc2b2f6df8ea78076e08a 100644 (file)
@@ -1 +1 @@
-control                       namespace = control
+control                       namespace = internal