}
}
- listen {
+ listen tcp_auth {
type = Access-Request
type = Status-Server
#
# ### Listen for Accounting-Request packets
#
- listen {
+ listen udp_acct {
type = Accounting-Request
transport = udp
fr_radius_packet_t *reply = NULL;
fr_pair_list_t reply_vps;
- TALLOC_CTX *autofree;
-
int ret;
fr_pair_list_init(&packet_vps);
*/
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) {
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);
}
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);
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);
}
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;
*/
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,
*/
static void thread_detach(UNUSED void *uctx)
{
- modules_thread_detach();
+ virtual_servers_thread_detach();
+
+ modules_rlm_thread_detach();
+
xlat_thread_detach();
}
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
* 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;
}
}
#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.
*/
}
}
- 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.
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
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);
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);
* 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;
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");
}
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 = {
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) {
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;
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");
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;
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;
char *p;
main_config_t *config;
- dl_module_loader_t *dl_modules = NULL;
CONF_SECTION *server_cs;
* 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;
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;
}
/*
* 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;
/*
* 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);
*/
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
*/
*/
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.
* 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
*/
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;
}
// @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
* 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;
}
}
- 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");
* 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;
}
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))
}
};
/*
* 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)
*/
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.
*/
*/
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.
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
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
* 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).
#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)
* 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.
* - -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;
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;
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
*
*/
* 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:
* - 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;
* 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);
*/
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 */
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;
/*
* 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;
}
* @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)) {
return -1;
}
}
+
return 0;
}
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);
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);
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;
/*
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);
#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);
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) {
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;
}
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
* @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
*/
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
* - 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,
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) },
});
*/
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.
* - 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
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;
* 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);
}
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);
* 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.
/*
* 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;
}
* 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);
}
*/
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);
}
/*
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
* 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));
}
* $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$")
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.
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
* 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,
///< 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
* @{
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
* 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.
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
*
*/
module_method_t func; //!< State function.
} module_state_func_table_t;
-
/** @name Callbacks for the CONF_PARSER
*
* @{
*
* @{
*/
-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
*/
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
#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
*/
"post-auth"
};
+/** Global module list for all backend modules
+ *
+ */
+static module_list_t *rlm_modules;
+
/** Initialise a module specific exfile handle
*
* @see exfile_init
* 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;
/*
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.
* 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;
do {
*p = '\0';
- mi = module_by_name(NULL, inst_name);
+ mi = module_by_name(rlm_modules, NULL, inst_name);
if (mi) break;
/*
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.
/*
* 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,
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.
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.
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;
}
}
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;
}
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
}
#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;
{ 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
*
*/
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);
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
};
};
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 },
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
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);
/*
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
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.
* - 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
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;
}
* - 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;
}
}
server->server_cs = server_cs;
- server->namespace = cf_pair_value(namespace);
/*
* Now parse the listeners
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)
{
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;
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.
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;
}
request->module = NULL;
request->component = "authenticate";
-
if (unlang_interpret_push_section(request, subcs, RLM_MODULE_REJECT, UNLANG_TOP_FRAME) < 0) {
RETURN_MODULE_FAIL;
}
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;
+}
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
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);
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
}
/*
* 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;
/*
* 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);
/*
* 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
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;
}
*
* @{
*/
-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);
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);
};
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.
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;
#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>
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 */
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.
*
* @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;
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;
}
*
* 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)) {
}
}
- 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);
}
}
}
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");
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;
}
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;
}
* 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);
*((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;
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;
}
*((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;
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;
}
}
}
- 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;
}
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;
}
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;
}
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;
}
*((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;
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;
}
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;
}
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;
}
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;
}
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);
}
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;
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,
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),
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),
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" },
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
{
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;
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;
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;
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;
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;
*/
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 },
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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 = "" },
} 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);
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,
fr_process_module_t process_control = {
.common = {
.magic = MODULE_MAGIC_INIT,
- .name = "process_control"
+ .name = "control"
},
.process = mod_process,
.dict = &dict_freeradius
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,
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),
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,
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,
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,
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,
fr_process_module_t process_radius = {
.common = {
.magic = MODULE_MAGIC_INIT,
- .name = "process_radius",
+ .name = "radius",
.config = config,
.inst_size = sizeof(process_radius_t),
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,
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,
fr_process_module_t process_ttls = {
.common = {
.magic = MODULE_MAGIC_INIT,
- .name = "process_ttls",
+ .name = "ttls",
.config = config,
.inst_size = sizeof(process_ttls_t),
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,
-control namespace = control
+control namespace = internal