From: Arran Cudbard-Bell Date: Fri, 10 May 2024 04:21:14 +0000 (-0600) Subject: Allow variants of module lists X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7384aac65066dc9763edab0cb39ba422c7384b33;p=thirdparty%2Ffreeradius-server.git Allow variants of module lists For now we support global module lists and thread-local lists --- diff --git a/src/lib/server/module.c b/src/lib/server/module.c index 2a9624a2a66..a82b02fabcf 100644 --- a/src/lib/server/module.c +++ b/src/lib/server/module.c @@ -18,10 +18,10 @@ * $Id$ * * @file src/lib/server/module.c - * @brief Defines functions for module (re-)initialisation. + * @brief Defines functions for module initialisation * - * @copyright 2003,2006,2016 The FreeRADIUS server project * @copyright 2016,2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org) + * @copyright 2003,2006,2016 The FreeRADIUS server project * @copyright 2000 Alan DeKok (aland@freeradius.org) * @copyright 2000 Alan Curry (pacman@world.std.com) */ @@ -36,20 +36,142 @@ RCSID("$Id$") #include #include #include +#include #include -/** Heap of all lists/modules used to get a common index with module_thread_inst_list +#include + +/** dl module tracking * + * This is used by all module lists, irrespecitve of their type, and is thread safe. */ -static fr_heap_t *module_global_inst_list; +static dl_module_loader_t *dl_modules = NULL; -/** An array of thread-local module lists +/** Callback to initialise any global structures required for the module list + * + * @param[in] ml to initialise global data for. + * @return + * - 0 on success. + * - -1 on failure. + */ +typedef int (*module_list_init_t)(module_list_t *ml); + +/** Callback to free any global structures associated with the module list * - * 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. + * @param[in] ml to free. */ -static _Thread_local module_thread_instance_t **module_thread_inst_list; +typedef void (*module_list_free_t)(module_list_t *ml); + +/** Callback to add data for a module + * + * @param[in] mi to add data for. + * Use mi->ml for the module list. + * Use mi->data to access the data. + * @return + * - 0 on success. + * - -1 on failure. + */ +typedef int (*module_list_data_add_t)(module_instance_t *mi); + +/** Callback to del data for a module + * + * @param[in] mi to add data to (use mi->ml for the module list). + * + */ +typedef void (*module_list_data_del_t)(module_instance_t *mi); + +/** Callback to initialise a list for thread-local data, called once per thread + * + * @param[in] ctx talloc context for thread-local data. + * May be modified by the init function if the + * module_thread_instance_t need to be parented + * by another ctx. + * @param[in] ml to initialise thread-local data for. + * @return + * - 0 on success. + * - -1 on failure. + */ +typedef int (*module_list_thread_init_t)(TALLOC_CTX **ctx, module_list_t const *ml); + +/** Callback to free thread-local structures, called once per thread as the thread is being destroyed + * + * @param[in] ml to free thread-local data for. + */ +typedef void (*module_list_thread_free_t)(module_list_t *ml); + +/** Callback to add thread-local data for a module + * + * @param[in] ti to add data for. + * Use `ti->mi->ml` for the module list. + * Use `ti->mi` for the module instance. + * Use `ti->data` for the thread specific data. + * @return + * - 0 on success. + * - -1 on failure. + */ +typedef int (*module_list_thread_data_add_t)(module_thread_instance_t *ti); + +/** Callback to remove thread-local data for a module + * + * @param[in] ti to del data for. + * Use `ti->mi->ml` for the module list. + * Use `ti->mi` for the module instance. + * Use `ti->data` for the thread specific data. + */ +typedef void (*module_list_thread_data_del_t)(module_thread_instance_t *ti); + +/** Structure to hold callbacks for a module list type + * + * We care about performance for module lists, as they're used heavily at runtime. + * + * As much as possible we try to avoid jumping through unecessary functions and + * unecessary switch statements. + * + * This structure contains callbacks which change how the module list operates, + * making it either a global module list, or a thread-local module list, i.e. one + * which only be used by a single thread. + * + * Instances of this structure are created in this compilation unit, and exported + * for the caller to pass into module_list_alloc(). + */ +struct module_list_type_s { + size_t list_size; //!< Size of talloc_chunk to allocate for the module_list_t. + + module_list_init_t init; //!< Initialise any global structures required for thread-local lookups. + module_list_free_t free; //!< Free any global structures required for thread-local lookups. + + size_t inst_size; //!< Size of talloc chunk to allocate for the module_instance_t. + ///< allows over-allocation if list types want to append fields. + module_list_data_add_t data_add; //!< Record that module data has been added. + module_list_data_del_t data_del; //!< Record that module data has been removed. + + /** Callbacks to manage thread-local data + */ + struct { + module_list_thread_init_t init; //!< Initialise any thread-local structures required for thread-local lookups. + module_list_thread_free_t free; //!< Free any thread-local structures. + + module_list_thread_data_add_t data_add; //!< Add thread-local data for a module. + module_list_thread_data_get_t data_get; //!< Retrieve thread local-data for a module. + module_list_thread_data_del_t data_del; //!< Remove (but not free) thread-local data for a module. + + void *data; //!< Pointer to hold any global resources for the thread-local implementation. + } thread; +}; + +static void module_thread_detach(module_thread_instance_t *ti); + +/** Heap of all lists/modules used to get a common index with mlg_thread->inst_list + */ +static fr_heap_t *mlg_index; + +/** 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 **mlg_thread_inst_list; /** Toggle used to determine if it's safe to use index based lookups * @@ -62,12 +184,254 @@ static _Thread_local module_thread_instance_t **module_thread_inst_list; * list so we need to revert back to doing binary searches instead of using * common indexes. */ -static _Thread_local bool module_list_in_sync = true; +static _Thread_local bool mlg_in_sync; -/** dl module tracking +typedef struct { + module_instance_t mi; //!< Common module instance fields. Must come first. + + 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. +} mlg_module_instance_t; + +/** 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 dl_module_loader_t *dl_modules = NULL; +static int8_t _mlg_module_instance_cmp(void const *one, void const *two) +{ + module_instance_t const *a = talloc_get_type_abort_const(one, module_instance_t); + module_instance_t const *b = talloc_get_type_abort_const(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); +} + +/** Free the global module index + * + */ +static int _mlg_global_free(UNUSED void *uctx) +{ + return talloc_free(mlg_index); +} + +/** Initialise the global module index + * + */ +static int _mlg_global_init(UNUSED void *uctx) +{ + MEM(mlg_index = fr_heap_alloc(NULL, _mlg_module_instance_cmp, mlg_module_instance_t, inst_idx, 256)); + return 0; +} + +/** Global initialisation for index heap and module array + * + */ +static int mlg_init(UNUSED module_list_t *ml) +{ + /* + * Create the global module heap we use for + * common indexes in the thread-specific + * heaps. + */ + fr_atexit_global_once(_mlg_global_init, _mlg_global_free, NULL); + + return 0; +} + +/** Add the unique index value so we can do thread local lookups + * + */ +static int mlg_data_add(module_instance_t *mi) +{ + /* + * Insert the module into the global heap so + * we can get common thread-local indexes. + */ + if (fr_heap_insert(&mlg_index, mi) < 0) { + ERROR("Failed inserting into global module index"); + return -1; + } + + return 0; +} + +static void mlg_data_del(module_instance_t *mi) +{ + mlg_module_instance_t *mlg_mi = (mlg_module_instance_t *)talloc_get_type_abort(mi, module_instance_t); + + if (fr_heap_entry_inserted(mlg_mi->inst_idx) && !fr_cond_assert(fr_heap_extract(&mlg_index, mi) == 0)) return; + return; +} + +/** Free the thread local heap on exit + * + * All thread local module lists should have been destroyed by this point + */ +static int _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 -1; + } + + return talloc_free(til); +} + +/** Allocate a thread-local array to hold thread data for each module thats been instantiated + * + * @param[in] ctx Talloc context for the thread-local data. + * Mutated by this function so that thread local data is allocated + * beneath the array. + * @param[in] ml Module list to initialise the thread-local data for. + */ +static int mlg_thread_init(UNUSED TALLOC_CTX **ctx, UNUSED module_list_t const *ml) +{ + /* + * Initialise the thread specific tree if this is the + * first time through or if everything else was + * de-initialised. + */ + if (!mlg_thread_inst_list) { + module_thread_instance_t **arr; + + MEM(arr = talloc_zero_array(NULL, module_thread_instance_t *, fr_heap_num_elements(mlg_index))); + + fr_atexit_thread_local(mlg_thread_inst_list, _module_thread_inst_list_free, arr); + + mlg_in_sync = true; + } + + return 0; +} + +/** Retrieve the thread-specific data for a module from the thread-local array of instance data + * + * This looks complex, but it's just asserts for sanity. This is really only returning an array offset. + * + * @param[in] mi Module instance to get the thread-specific data for. + */ +static module_thread_instance_t *mlg_thread_data_get(module_instance_t *mi) +{ + mlg_module_instance_t *mlg_mi = (mlg_module_instance_t *)talloc_get_type_abort(mi, module_instance_t); + module_thread_instance_t *ti; + + fr_assert_msg(mlg_mi->inst_idx <= talloc_array_length(mlg_thread_inst_list), + "module instance index %u must be <= thread local array %zu", + mlg_mi->inst_idx, talloc_array_length(mlg_thread_inst_list)); + fr_assert(mlg_in_sync); + + fr_assert_msg(fr_heap_num_elements(mlg_index) == talloc_array_length(mlg_thread_inst_list), + "mismatch between global module heap (%u entries) and thread local (%zu entries)", + fr_heap_num_elements(mlg_index), talloc_array_length(mlg_thread_inst_list)); + + ti = talloc_get_type_abort(mlg_thread_inst_list[mlg_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; +} + +static int mlg_thread_data_add(module_thread_instance_t *ti) +{ + mlg_module_instance_t *mlg_mi = (mlg_module_instance_t *)talloc_get_type_abort(ti->mi, module_instance_t); + mlg_thread_inst_list[mlg_mi->inst_idx - 1] = ti; + return 0; +} + +static void mlg_thread_data_del(module_thread_instance_t *ti) +{ + mlg_module_instance_t *mlg_mi = (mlg_module_instance_t *)talloc_get_type_abort(ti->mi, module_instance_t); + mlg_thread_inst_list[mlg_mi->inst_idx - 1] = NULL; + mlg_in_sync = false; +} + +/** Callbacks for a global module list + */ +module_list_type_t const module_list_type_global = { + .init = mlg_init, + + .inst_size = sizeof(mlg_module_instance_t), + .data_add = mlg_data_add, + .data_del = mlg_data_del, + + .thread = { + .init = mlg_thread_init, + .data_add = mlg_thread_data_add, + .data_get = mlg_thread_data_get, + .data_del = mlg_thread_data_del + } +}; + +/** A slightly larger module_instance structure to hold the module instance and thread instance + */ +typedef struct { + module_instance_t mi; //!< Common module instance fields. Must come first. + module_thread_instance_t *ti; //!< Thread-specific data. Still in its own structure + ///< for talloc reasons. +} mltl_module_instance_t; + +/** This causes the module_list code to skip bootstrapping for thread-local modules + */ +static int mltl_mlg_data_add(module_instance_t *mi) +{ + mi->state |= MODULE_INSTANCE_BOOTSTRAPPED; + return 0; +} + +static void mltl_mlg_data_del(module_instance_t *mi) +{ + mltl_module_instance_t *mltl_mi = (mltl_module_instance_t *)talloc_get_type_abort(mi, module_instance_t); + module_thread_detach(mltl_mi->ti); +} + +static module_thread_instance_t *mltl_thread_data_get(module_instance_t *mi) +{ + mltl_module_instance_t *mltl_mi = (mltl_module_instance_t *)talloc_get_type_abort(mi, module_instance_t); + return mltl_mi->ti; +} + +static int mltl_thread_data_add(module_thread_instance_t *ti) +{ + mltl_module_instance_t *mltl_mi = (mltl_module_instance_t *)talloc_get_type_abort(ti->mi, module_instance_t); + mltl_mi->ti = ti; + return 0; +} + +static void mltl_thread_data_del(module_thread_instance_t *ti) +{ + mltl_module_instance_t *mltl_mi = (mltl_module_instance_t *)talloc_get_type_abort(ti->mi, module_instance_t); + mltl_mi->ti = NULL; +} + +/** Callbacks for a thread local list + */ +module_list_type_t const module_list_type_thread_local = { + .inst_size = sizeof(mltl_module_instance_t), + + .data_add = mltl_mlg_data_add, + .data_del = mltl_mlg_data_del, + + .thread = { + .data_add = mltl_thread_data_add, + .data_get = mltl_thread_data_get, + .data_del = mltl_thread_data_del + } +}; 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); @@ -160,7 +524,7 @@ static int module_name_tab_expand(UNUSED TALLOC_CTX *talloc_ctx, UNUSED void *uc text = info->argv[info->argc - 1]; count = 0; - fr_heap_foreach(module_global_inst_list, module_instance_t, instance) { + fr_heap_foreach(mlg_index, module_instance_t, instance) { module_instance_t *mi = talloc_get_type_abort(instance, module_instance_t); if (count >= max_expansions) { @@ -177,7 +541,7 @@ static int module_name_tab_expand(UNUSED TALLOC_CTX *talloc_ctx, UNUSED void *uc static int cmd_show_module_list(FILE *fp, UNUSED FILE *fp_err, UNUSED void *uctx, UNUSED fr_cmd_info_t const *info) { - fr_heap_foreach(module_global_inst_list, module_instance_t, instance) { + fr_heap_foreach(mlg_index, module_instance_t, instance) { module_instance_t *mi = talloc_get_type_abort(instance, module_instance_t); fprintf(fp, "\t%s\n", mi->name); @@ -238,21 +602,6 @@ char const *module_instance_root_prefix_str(module_instance_t const *mi) return fr_table_str_by_value(dl_module_type_prefix, root->module->type, ""); } -/** Detach the shallowest parent first - * - */ -static void module_detach_parent(module_instance_t *mi) -{ - if (mi->detached) return; - - if (mi->parent) module_detach_parent(UNCONST(module_instance_t *, mi->parent)); - - if (mi->exported->detach) { - mi->exported->detach(&(module_detach_ctx_t){ .mi = mi }); - mi->detached = true; - } -} - /** Allocate module instance data * * @param[in] mi to allocate instance data for @@ -318,26 +667,6 @@ int module_instance_conf_parse(module_instance_t *mi, CONF_SECTION *conf) 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_const(one, module_instance_t); - module_instance_t const *b = talloc_get_type_abort_const(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 @@ -432,7 +761,7 @@ int module_submodule_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, * and will be appended to the parent's instance * name. */ - mi = module_instance_alloc(ml, module_instance_by_data(ml, parent), DL_MODULE_TYPE_SUBMODULE, name, name); + mi = module_instance_alloc(ml, module_instance_by_data(ml, parent), DL_MODULE_TYPE_SUBMODULE, name, name, 0); if (unlikely(mi == NULL)) { cf_log_err(submodule_cs, "Failed loading submodule"); return -1; @@ -527,29 +856,6 @@ module_instance_t *module_instance_by_data(module_list_t const *ml, void const * return talloc_get_type_abort(mi, module_instance_t); } -/** Retrieve module/thread specific instance for a module - * - * @param[in] mi to find thread specific data for. - * @return - * - Thread specific instance data on success. - * - NULL if module has no thread instance data. - */ -module_thread_instance_t *module_thread(module_instance_t *mi) -{ - 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. @@ -563,58 +869,66 @@ 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) { module_instance_t *mi = module_instance_by_data(ml, data); - module_thread_instance_t *ti; - if (!mi) return NULL; - 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)); + if (!mi) return NULL; - 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; + return module_thread(mi); } -/** Explicitly free a module if a fatal error occurs during bootstrap - * - * @param[in] mi to free. - */ -void module_free(module_instance_t *mi) +static void module_thread_detach(module_thread_instance_t *ti) { - talloc_free(mi); + module_list_t *ml = ti->mi->ml; + ml->type->thread.data_del(ti); + talloc_free(ti); } /** Remove thread-specific data for a given module list * * Removes all module thread data for the */ -void modules_thread_detach(module_list_t const *ml) +void modules_thread_detach(module_list_t *ml) { fr_rb_iter_inorder_t iter; - void *instance; + void *inst; /* * 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); + for (inst = fr_rb_iter_init_inorder(&iter, ml->name_tree); + inst; + inst = fr_rb_iter_next_inorder(&iter)) { + module_instance_t *mi = talloc_get_type_abort(inst, module_instance_t); + module_thread_instance_t *ti = module_thread(mi); - talloc_free(module_thread_inst_list[mi->inst_idx - 1]); + module_thread_detach(ti); } + + /* + * Cleanup any lists the module list added to this thread + */ + if (ml->type->thread.free) ml->type->thread.free(ml); } +/** Callback to free thread local data + * + * ti->data is allocated in the context of ti, so will be freed too. + * + * Calls the detach function for thread local data, and removes the data from the + * thread local list. + * + * @param[in] ti to free. + */ static int _module_thread_inst_free(module_thread_instance_t *ti) { module_instance_t const *mi = ti->mi; - module_list_in_sync = false; /* Help catch anything attempting to do lookups */ + /* + * Never allocated a thread instance, so we don't need + * to clean it up... + */ + if (mi->state & MODULE_INSTANCE_NO_THREAD_INSTANCE) return 0; DEBUG4("Worker cleaning up %s thread instance data (%p/%p)", mi->exported->name, ti, ti->data); @@ -627,33 +941,9 @@ static int _module_thread_inst_free(module_thread_instance_t *ti) }); } - /* - * 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 int _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 -1; - } + ti->mi->ml->type->thread.data_del(ti); - return talloc_free(til); + return 0; } /** Allocate thread-local instance data for a module @@ -664,35 +954,41 @@ static int _module_thread_inst_list_free(void *tilp) * a specific dynamic use of that module. * * @param[in] ctx Talloc ctx to bind thread specific data to. - * @param[out] out Where to write the allocated #module_thread_instance_t. - * @param[in] ml Module list to perform thread instantiation for. * @param[in] mi Module instance to perform thread instantiation for. * @param[in] el Event list serviced by this thread. * @return * - 0 on success. * - -1 on failure. */ -static int module_thread_instantiate(TALLOC_CTX *ctx, module_thread_instance_t **out, module_list_t const *ml, - module_instance_t *mi, fr_event_list_t *el) +int module_thread_instantiate(TALLOC_CTX *ctx, module_instance_t *mi, fr_event_list_t *el) { + module_list_t *ml = mi->ml; module_thread_instance_t *ti; - TALLOC_CTX *our_ctx = ctx; + + /* + * Allows the caller of module_instance_alloc to + * skip thread instantiation for certain modules instances + * whilst allowing modules to still register thread + * instantiation callbacks. + * + * This is mainly there for the single global instance of + * a module, which will only have run-time thread-specific + * instances, like dynamic/keyed modules. + */ + if (mi->state & MODULE_INSTANCE_NO_THREAD_INSTANCE) return 0; /* * 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)); + MEM(ti = talloc_zero(ctx, module_thread_instance_t)); talloc_set_destructor(ti, _module_thread_inst_free); ti->el = el; ti->mi = mi; if (mi->exported->thread_inst_size) { - module_instance_t *rmi; - MEM(ti->data = talloc_zero_array(ti, uint8_t, mi->exported->thread_inst_size)); - rmi = module_instance_root(mi); /* * Fixup the type name, in case something calls @@ -700,16 +996,21 @@ static int module_thread_instantiate(TALLOC_CTX *ctx, module_thread_instance_t * */ if (!mi->exported->thread_inst_type) { talloc_set_name(ti->data, "%s_%s_thread_t", - fr_table_str_by_value(dl_module_type_prefix, - rmi ? rmi->module->type : - mi->module->type, - ""), + module_instance_root_prefix_str(mi), mi->exported->name); } else { talloc_set_name_const(ti->data, mi->exported->thread_inst_type); } } + if (ml->type->thread.data_add(ti) < 0) { + PERROR("Failed adding thread data for module \"%s\"", mi->name); + error: + ml->type->thread.data_del(ti); + talloc_free(ti); + return -1; + } + /* * So we don't get spurious errors */ @@ -717,13 +1018,10 @@ static int module_thread_instantiate(TALLOC_CTX *ctx, module_thread_instance_t * DEBUG4("Alloced %s thread instance data (%p/%p)", ti->mi->exported->name, ti, ti->data); if (mi->exported->thread_instantiate && - mi->exported->thread_instantiate(MODULE_THREAD_INST_CTX(mi, ti->data, el)) < 0) { + mi->exported->thread_instantiate(MODULE_THREAD_INST_CTX(mi, ti->data, el)) < 0) { PERROR("Thread instantiation failed for module \"%s\"", mi->name); - /* Leave module_thread_inst_list intact, other modules may need to clean up */ - modules_thread_detach(ml); - return -1; + goto error; } - *out = ti; return 0; } @@ -741,31 +1039,27 @@ static int module_thread_instantiate(TALLOC_CTX *ctx, module_thread_instance_t * */ int modules_thread_instantiate(TALLOC_CTX *ctx, module_list_t const *ml, fr_event_list_t *el) { - void *instance; + void *inst; fr_rb_iter_inorder_t iter; + int ret; /* - * Initialise the thread specific tree if this is the - * first time through or if everything else was - * de-initialised. + * Do any thread-local instantiation necessary */ - if (!module_thread_inst_list) { - module_thread_instance_t **arr; - - 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); + if (ml->type->thread.init) { + ret = ml->type->thread.init(&ctx, ml); + if (unlikely(ret < 0)) return ret; } - 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; + for (inst = fr_rb_iter_init_inorder(&iter, ml->name_tree); + inst; + inst = fr_rb_iter_next_inorder(&iter)) { + module_instance_t *mi = talloc_get_type_abort(inst, module_instance_t); /* Sanity check*/ - if (module_thread_instantiate(ctx, &ti, ml, mi, el) < 0) return -1; - module_thread_inst_list[ti->mi->inst_idx - 1] = ti; + if (module_thread_instantiate(ctx, mi, el) < 0) { + modules_thread_detach(UNCONST(module_list_t *, ml)); + return -1; + } } return 0; @@ -786,7 +1080,8 @@ int module_instantiate(module_instance_t *instance) /* * We only instantiate modules in the bootstrapped state */ - if (mi->state != MODULE_INSTANCE_BOOTSTRAPPED) return 0; + if ((!fr_cond_assert(mi->state & MODULE_INSTANCE_BOOTSTRAPPED)) || + (mi->state & MODULE_INSTANCE_INSTANTIATED)) return 0; if (mi->module->type == DL_MODULE_TYPE_MODULE) { if (fr_command_register_hook(NULL, mi->name, mi, module_cmd_table) < 0) { @@ -820,7 +1115,7 @@ int module_instantiate(module_instance_t *instance) return -1; } } - mi->state = MODULE_INSTANCE_INSTANTIATED; + mi->state |= MODULE_INSTANCE_INSTANTIATED; return 0; } @@ -846,8 +1141,6 @@ int modules_instantiate(module_list_t const *ml) 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_BOOTSTRAPPED) continue; - if (module_instantiate(mi) < 0) return -1; } @@ -869,7 +1162,7 @@ int module_bootstrap(module_instance_t *mi) /* * We only bootstrap modules in the init state */ - if (mi->state != MODULE_INSTANCE_INIT) return 0; + if (mi->state & MODULE_INSTANCE_BOOTSTRAPPED) return 0; /* * Bootstrap the module. @@ -891,7 +1184,7 @@ int module_bootstrap(module_instance_t *mi) return -1; } } - mi->state = MODULE_INSTANCE_BOOTSTRAPPED; + mi->state |= MODULE_INSTANCE_BOOTSTRAPPED; return 0; } @@ -917,8 +1210,6 @@ int modules_bootstrap(module_list_t const *ml) 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; } @@ -936,15 +1227,17 @@ static fr_slen_t module_instance_name(TALLOC_CTX *ctx, char **out, module_instance_t const *parent, char const *inst_name) { fr_sbuff_t *agg; - module_instance_t const *mi; FR_SBUFF_TALLOC_THREAD_LOCAL(&agg, 64, 256); - for (mi = parent; mi; mi = mi->parent) { - FR_SBUFF_IN_STRCPY_RETURN(agg, mi->name); + /* + * Parent has all of the qualifiers of its ancestors + * already in the name, so we just need to concatenate. + */ + if (parent) { + FR_SBUFF_IN_STRCPY_RETURN(agg, parent->name); FR_SBUFF_IN_CHAR_RETURN(agg, '.'); } - FR_SBUFF_IN_STRCPY_RETURN(agg, inst_name); MEM(*out = talloc_bstrndup(ctx, fr_sbuff_start(agg), fr_sbuff_used(agg))); @@ -952,6 +1245,33 @@ static fr_slen_t module_instance_name(TALLOC_CTX *ctx, char **out, return fr_sbuff_used(agg); } +/** Detach the shallowest parent first + * + * This ensures that the module's parent is detached before it is. + * + * Generally parents reach into their children and not the other way + * around. Calling the parent's detach method first ensures that + * there's no code that access the child module's instance data or + * reach into its symbol space if it's being unloaded. + * + * @note If you don't want to detach the parent, maybe because its children + * are ephemeral, consider using a seaprate thread-local module list + * to hold the children instead. + * + * @param[in] mi to detach. + */ +static void module_detach_parent(module_instance_t *mi) +{ + if (mi->detached) return; + + if (mi->parent) module_detach_parent(UNCONST(module_instance_t *, mi->parent)); + + if (mi->exported && mi->exported->detach) { + mi->exported->detach(&(module_detach_ctx_t){ .mi = mi }); + mi->detached = true; + } +} + /** Free module's instance data, and any xlats or paircmps * * @param[in] mi to free. @@ -963,9 +1283,9 @@ static int _module_instance_free(module_instance_t *mi) DEBUG3("Freeing %s (%p)", mi->name, mi); - 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; + if (ml->type->data_del) ml->type->data_del(mi); /* * mi->exported may be NULL if we failed loading the module @@ -991,7 +1311,7 @@ static int _module_instance_free(module_instance_t *mi) /* * Remove all xlat's registered to module instance. */ - if (mi && mi->data) { + if (mi->data) { xlat_func_unregister(mi->name); xlat_func_unregister_module(mi); } @@ -1013,24 +1333,44 @@ static int _module_instance_free(module_instance_t *mi) return 0; } +/** Duplicate a module instance, placing it in a new module list + * + * @param[in] dst list to place the new module instance in. + * @param[in] src to duplicate. + * @param[in] inst_name new instance name. If null, src->name will be used. + */ +module_instance_t *module_instance_copy(module_list_t *dst, module_instance_t const *src, char const *inst_name) +{ + module_instance_t *mi = module_instance_alloc(dst, src->parent, src->module->type, + src->module->name, + inst_name ? inst_name : src->name, 0); + if (!mi) return NULL; + + return mi; +} + /** 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. * - * @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] 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. + * @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] 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. + * @param[in] init_state The state the module "starts" in. Can be used to prevent + * bootstrapping, instantiation, or thread instantiation of the module, + * by passing one or more of the MODULE_INSTANCE_* flags. + * Should usually be 0, unless special behaviour is required. * @return * - A new module instance handle, containing the module's public interface, * and private instance data. @@ -1038,7 +1378,8 @@ static int _module_instance_free(module_instance_t *mi) */ module_instance_t *module_instance_alloc(module_list_t *ml, module_instance_t const *parent, - dl_module_type_t type, char const *mod_name, char const *inst_name) + dl_module_type_t type, char const *mod_name, char const *inst_name, + module_instance_state_t init_state) { char *qual_inst_name = NULL; module_instance_t *mi; @@ -1084,12 +1425,18 @@ module_instance_t *module_instance_alloc(module_list_t *ml, return NULL; } - MEM(mi = talloc_zero(parent ? (void const *)parent : (void const *)ml, module_instance_t)); + /* + * Overallocate the module instance, so we can add + * some module list type specific data to it. + */ + MEM(mi = (module_instance_t *)talloc_zero_array(parent ? (void const *)parent : (void const *)ml, uint8_t, ml->type->inst_size)); + talloc_set_name_const(mi, "module_instance_t"); mi->name = talloc_typed_strdup(mi, qual_inst_name); talloc_free(qual_inst_name); /* Avoid stealing */ mi->ml = ml; mi->parent = parent; + mi->state = init_state; /* * Increment the reference count on an already loaded module, @@ -1126,25 +1473,15 @@ module_instance_t *module_instance_alloc(module_list_t *ml, */ if ((mi->exported->flags & MODULE_TYPE_THREAD_UNSAFE) != 0) pthread_mutex_init(&mi->mutex, NULL); talloc_set_destructor(mi, _module_instance_free); /* Set late intentionally */ - - mi->number = ml->last_number++; mi->ml = ml; + mi->number = ml->last_number++; /* * Remember the module for later. */ if (!fr_cond_assert(fr_rb_insert(ml->name_tree, mi))) goto error; if (!fr_cond_assert(fr_rb_insert(ml->data_tree, mi))) goto error; - - /* - * ...and finally insert the module - * into the global heap so we can - * get common thread-local indexes. - */ - if (fr_heap_insert(&module_global_inst_list, mi) < 0) { - ERROR("Failed inserting into global module index"); - goto error; - } + if (ml->type->data_add && unlikely(ml->type->data_add(mi) < 0)) goto error; return mi; } @@ -1170,6 +1507,8 @@ static int _module_list_free(module_list_t *ml) talloc_free(mi); } + if (ml->type->free) ml->type->free(ml); + return 0; } @@ -1182,27 +1521,43 @@ static int _module_list_free(module_list_t *ml) * If no more instances of the module exist the module be unloaded. * * @param[in] ctx To allocate the list in. - * @param[in] name of the list. + * @param[in] type of the list. Controls whether this is a global + * module list, or a per-thread list containing + * variants of an existing module. + * @param[in] name of the list. Used for debugging. * @return A new module list. */ -module_list_t *module_list_alloc(TALLOC_CTX *ctx, char const *name) +module_list_t *module_list_alloc(TALLOC_CTX *ctx, module_list_type_t const *type, char const *name) { module_list_t *ml; + /* + * These callbacks are NOT optional, the rest are. + */ + fr_assert(type->thread.data_add); + fr_assert(type->thread.data_get); + fr_assert(type->thread.data_del); + MEM(ml = talloc_zero(ctx, module_list_t)); - talloc_set_destructor(ml, _module_list_free); + ml->type = type; + ml->thread_data_get = type->thread.data_get; /* Cache for access outside of the compilation unit */ 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_set_destructor(ml, _module_list_free); + + if (ml->type->init && (ml->type->init(ml) < 0)) { + talloc_free(ml); + return NULL; + } return ml; } -static int _module_global_list_init(void *uctx) +static int _module_dl_loader_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)); /* * Ensure the common library tracking @@ -1213,15 +1568,8 @@ static int _module_global_list_init(void *uctx) return 0; } -static int _module_global_list_free(UNUSED void *uctx) +static int _module_dl_loader_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 -1; - if (talloc_free(module_global_inst_list) < 0) return -1; - module_global_inst_list = NULL; - if (talloc_free(dl_modules) < 0) return -1; dl_modules = NULL; return 0; @@ -1237,5 +1585,5 @@ void modules_init(char const *lib_dir) * common indexes in the thread-specific * heaps. */ - fr_atexit_global_once(_module_global_list_init, _module_global_list_free, UNCONST(char *, lib_dir)); + fr_atexit_global_once(_module_dl_loader_init, _module_dl_loader_free, UNCONST(char *, lib_dir)); } diff --git a/src/lib/server/module.h b/src/lib/server/module.h index f3448202e6e..ae4e4be5c7e 100644 --- a/src/lib/server/module.h +++ b/src/lib/server/module.h @@ -42,7 +42,8 @@ typedef struct module_s module_t; typedef struct module_method_name_s module_method_name_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; +typedef struct module_list_type_s module_list_type_t; +typedef struct module_list_s module_list_t; DIAG_OFF(attributes) typedef enum CC_HINT(flag_enum) { @@ -144,7 +145,7 @@ struct module_method_name_s { char const *name2; //!< The packet type i.e Access-Request, Access-Reject. module_method_t method; //!< Module method to call - call_env_method_t const *method_env; //!< Call specific conf parsing. + call_env_method_t const * const method_env; //!< Call specific conf parsing. }; #define MODULE_NAME_TERMINATOR { NULL } @@ -184,14 +185,16 @@ struct module_s { /** What state the module instance is currently in * */ -typedef enum { - MODULE_INSTANCE_INIT = 0, //!< Module instance has been allocated, but not - ///< yet bootstrapped. - MODULE_INSTANCE_BOOTSTRAPPED, //!< Module instance has been bootstrapped, but not - ///< yet instantiated. - MODULE_INSTANCE_INSTANTIATED //!< Module instance has been bootstrapped and - ///< instantiated. +DIAG_OFF(attributes) +typedef enum CC_HINT(flag_enum) { + MODULE_INSTANCE_BOOTSTRAPPED = (1 << 1), //!< Module instance has been bootstrapped, but not + ///< yet instantiated. + MODULE_INSTANCE_INSTANTIATED = (1 << 2), //!< Module instance has been bootstrapped and + ///< instantiated. + MODULE_INSTANCE_NO_THREAD_INSTANCE = (1 << 3) //!< Not set internally, but can be used to prevent + ///< thread instantiation for certain modules. } module_instance_state_t; +DIAG_ON(attributes) /** Per instance data * @@ -200,9 +203,6 @@ typedef enum { * 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. module_instance_state_t state; //!< What's been done with this module so far. fr_rb_node_t name_node; //!< Entry in the name tree. @@ -210,9 +210,6 @@ struct module_instance_s { module_list_t *ml; //!< Module list this instance belongs to. - uint32_t number; //!< Unique module number. This is used to access the - ///< thread-specific module instance. - char const *name; //!< Instance name e.g. user_database. dl_module_t *module; //!< dynamic loader handle. Contains the module's @@ -236,6 +233,10 @@ struct module_instance_s { pthread_mutex_t mutex; //!< Used prevent multiple threads entering a thread ///< unsafe module simultaneously. + + uint32_t number; //!< Unique module number. Used to assign a stable + ///< number to each module instance. + /** @name Return code overrides * @{ */ @@ -271,32 +272,60 @@ struct module_thread_instance_s { uint64_t active_callers; //! number of active callers. i.e. number of current yields }; +/** Callback to retrieve thread-local data for a module + * + * @param[in] mi to add data to (use mi->ml for the module list). + * @return + * - NULL if no data exists. + * - Pointer to the data on success. + */ +typedef module_thread_instance_t *(*module_list_thread_data_get_t)(module_instance_t *mi); + /** 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. +struct module_list_s { + 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. + fr_heap_t *inst_heap; //!< Heap of module instances. + + /** @name Callbacks to manage thread-specific data + * + * In "child" lists, which are only operating in a single thread, we don't need + * to use true thread-local data, because the module list itself is thread-local. + * + * In that case these callbacks hang memory off of the list itself. + * + * In the main module list, which is shared between threads, these callbacks + * do use true thread-local data, to manage the module_thread_instance_t + * on a per thread-basis. + * + * @{ + */ + module_list_type_t const *type; //!< Type of module list. + module_list_thread_data_get_t thread_data_get; //!< Callback to get thread-specific data. + ///< Copy of type->thread_data_get. + /** @} */ }; /** Map string values to module state method * */ typedef struct { - char const *name; //!< String identifier for state. - module_method_t func; //!< State function. + char const *name; //!< String identifier for state. + module_method_t func; //!< State function. } module_state_func_table_t; /** @name Callbacks for the conf_parser_t * * @{ */ -int module_submodule_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, - CONF_ITEM *ci, UNUSED conf_parser_t const *rule) CC_HINT(warn_unused_result); +int module_submodule_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, + CONF_ITEM *ci, UNUSED conf_parser_t const *rule) CC_HINT(warn_unused_result); /** @} */ /** @name Module and module thread lookup @@ -316,7 +345,18 @@ module_instance_t *module_instance_by_name(module_list_t const *ml, module_insta module_instance_t *module_instance_by_data(module_list_t const *ml, void const *data) CC_HINT(warn_unused_result); -module_thread_instance_t *module_thread(module_instance_t *mi) CC_HINT(warn_unused_result); +/** Retrieve module/thread specific instance for a module + * + * @param[in] mi to find thread specific data for. + * @return + * - Thread specific instance data on success. + * - NULL if module has no thread instance data. + */ +static inline CC_HINT(warn_unused_result) CC_HINT(always_inline) +module_thread_instance_t *module_thread(module_instance_t *mi) +{ + return mi->ml->thread_data_get(mi); +} module_thread_instance_t *module_thread_by_data(module_list_t const *ml, void const *data) CC_HINT(warn_unused_result); /** @} */ @@ -325,28 +365,51 @@ module_thread_instance_t *module_thread_by_data(module_list_t const *ml, void co * * @{ */ -void module_free(module_instance_t *mi); +void modules_thread_detach(module_list_t *ml); + +int module_thread_instantiate(TALLOC_CTX *ctx, module_instance_t *mi, fr_event_list_t *el); + CC_HINT(nonnull) CC_HINT(warn_unused_result); -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) CC_HINT(warn_unused_result); -int modules_thread_instantiate(TALLOC_CTX *ctx, module_list_t const *ml, fr_event_list_t *el) CC_HINT(nonnull) CC_HINT(warn_unused_result); +int module_instantiate(module_instance_t *mi) CC_HINT(nonnull) CC_HINT(warn_unused_result); -int module_instantiate(module_instance_t *mi) CC_HINT(nonnull) CC_HINT(warn_unused_result); +int modules_instantiate(module_list_t const *ml) CC_HINT(nonnull) CC_HINT(warn_unused_result); -int modules_instantiate(module_list_t const *ml) CC_HINT(nonnull) CC_HINT(warn_unused_result); +int module_bootstrap(module_instance_t *mi) CC_HINT(nonnull) CC_HINT(warn_unused_result); -int module_bootstrap(module_instance_t *mi) CC_HINT(nonnull) CC_HINT(warn_unused_result); +int modules_bootstrap(module_list_t const *ml) CC_HINT(nonnull) CC_HINT(warn_unused_result); -int modules_bootstrap(module_list_t const *ml) CC_HINT(nonnull) CC_HINT(warn_unused_result); +module_instance_t *module_instance_copy(module_list_t *dst, module_instance_t const *src, char const *inst_name) + CC_HINT(nonnull(1,2)) CC_HINT(warn_unused_result); -module_instance_t *module_instance_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)) CC_HINT(warn_unused_result); +module_instance_t *module_instance_alloc(module_list_t *ml, + module_instance_t const *parent, + dl_module_type_t type, char const *mod_name, char const *inst_name, + module_instance_state_t init_state) + CC_HINT(nonnull(1)) CC_HINT(warn_unused_result); + +/** @name Module list variants + * + * These are passed to the module_list_alloc function to allocate lists of different types + * + * Global module lists are used for backend modules, listeners, and process state machines. + * + * Thread-local lists are usually runtime instantiated variants of modules, or modules that represent client connections. + * + * One major difference (from the module's perspective) is that bootstrap is not called for thread-local modules. + * + * @{ + */ +extern module_list_type_t const module_list_type_global; //!< Initialise a global module, with thread-specific data. +extern module_list_type_t const module_list_type_thread_local; //!< Initialise a thread-local module, which is only used in a single thread. +/** @} */ -module_list_t *module_list_alloc(TALLOC_CTX *ctx, char const *name) CC_HINT(warn_unused_result); +module_list_t *module_list_alloc(TALLOC_CTX *ctx, module_list_type_t const *type, char const *name) + CC_HINT(nonnull(2,3)) CC_HINT(warn_unused_result); -void modules_init(char const *lib_dir); +void modules_init(char const *lib_dir); /** @} */ #ifdef __cplusplus diff --git a/src/lib/server/module_rlm.c b/src/lib/server/module_rlm.c index e9983a3555f..a0f9efb9889 100644 --- a/src/lib/server/module_rlm.c +++ b/src/lib/server/module_rlm.c @@ -1039,7 +1039,7 @@ int modules_rlm_bootstrap(CONF_SECTION *root) continue; } - mi = module_instance_alloc(rlm_modules, NULL, DL_MODULE_TYPE_MODULE, name, module_instance_name_from_conf(subcs)); + mi = module_instance_alloc(rlm_modules, NULL, DL_MODULE_TYPE_MODULE, name, module_instance_name_from_conf(subcs), 0); if (unlikely(mi == NULL)) { cf_log_perr(subcs, "Failed loading module"); return -1; @@ -1157,7 +1157,7 @@ static int _modules_rlm_free_atexit(UNUSED void *uctx) */ int modules_rlm_init(void) { - MEM(rlm_modules = module_list_alloc(NULL, "rlm")); + MEM(rlm_modules = module_list_alloc(NULL, &module_list_type_global, "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); diff --git a/src/lib/server/virtual_servers.c b/src/lib/server/virtual_servers.c index b1b056ae459..97b3cc18649 100644 --- a/src/lib/server/virtual_servers.c +++ b/src/lib/server/virtual_servers.c @@ -223,7 +223,8 @@ static int namespace_on_read(TALLOC_CTX *ctx, UNUSED void *out, UNUSED void *par * The instance name is the virtual server name. */ mi = module_instance_alloc(process_modules, NULL, DL_MODULE_TYPE_PROCESS, - module_name, module_instance_name_from_conf(server_cs)); + module_name, module_instance_name_from_conf(server_cs), + 0); talloc_free(module_name); if (mi == NULL) { error: @@ -444,7 +445,7 @@ static int listen_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, if (!inst_name) inst_name = mod_name; MEM(qual_inst_name = talloc_asprintf(NULL, "%s.%s", cf_section_name2(server_cs), inst_name)); - mi = module_instance_alloc(proto_modules, NULL, DL_MODULE_TYPE_PROTO, mod_name, qual_inst_name); + mi = module_instance_alloc(proto_modules, NULL, DL_MODULE_TYPE_PROTO, mod_name, qual_inst_name, 0); talloc_free(qual_inst_name); if (!mi) { error: @@ -1594,8 +1595,8 @@ int virtual_servers_init(void) return -1; } - MEM(process_modules = module_list_alloc(NULL, "process")); - MEM(proto_modules = module_list_alloc(NULL, "protocol")); + MEM(process_modules = module_list_alloc(NULL, &module_list_type_global, "process")); + MEM(proto_modules = module_list_alloc(NULL, &module_list_type_global, "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)); diff --git a/src/listen/detail/proto_detail.c b/src/listen/detail/proto_detail.c index b4a1b4d6814..41bc7c25ba9 100644 --- a/src/listen/detail/proto_detail.c +++ b/src/listen/detail/proto_detail.c @@ -572,7 +572,7 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx) * of that list. */ inst->work_submodule = module_instance_alloc(parent_inst->ml, parent_inst, DL_MODULE_TYPE_SUBMODULE, - "work", module_instance_name_from_conf(transport_cs)); + "work", module_instance_name_from_conf(transport_cs), 0); if (inst->work_submodule == NULL) { error: cf_log_perr(inst->cs, "Failed to load proto_detail_work");