* instance names (may NOT be the module names!), and the per-instance
* data structures.
*/
-typedef struct module_instance {
+typedef struct {
char const *name; //!< Instance name e.g. user_database.
rad_module_t const *module; //!< Module this is an instance of.
//!< has been set to true.
} module_instance_t;
+/** Per thread per instance data
+ *
+ * Stores module and thread specific data.
+ */
+typedef struct {
+ module_instance_t *inst; //!< Non-thread local instance of this
+
+ void *data; //!< Thread specific instance data.
+} module_thread_instance_t;
+
module_instance_t *module_find_with_method(rlm_components_t *method,
CONF_SECTION *modules, char const *asked_name);
module_instance_t *module_find(CONF_SECTION *modules, char const *asked_name);
*/
typedef int (*module_thread_t)(CONF_SECTION *mod_cs, void *instance, void *thread);
+/** Module thread destruction callback
+ *
+ * Destroy a module/thread instance.
+ *
+ * @param[in] instance data, specific to an instantiated module.
+ * Pre-allocated, and populated during the
+ * bootstrap and instantiate calls.
+ * @param[in] thread data specific to this module instance.
+ * @return
+ * - 0 on success.
+ * - -1 if instantiation failed.
+ */
+typedef int (*module_thread_detach_t)(void *instance, void *thread);
+
/** Struct exported by a rlm_* module
*
* Determines the capabilities of the module, and maps internal functions
module_instantiate_t bootstrap; //!< Callback to register dynamic attrs, xlats, etc.
module_instantiate_t instantiate; //!< Callback to configure a new module instance.
- module_thread_t thread; //!< Callback to configure a module's instance for
+
+ module_thread_t thread_instantiate; //!< Callback to configure a module's instance for
//!< a new worker thread.
+ module_thread_detach_t thread_detach; //!< Destroy thread specific data.
+ size_t thread_inst_size; //!< Size of data to allocate to the thread instance.
module_method_t methods[MOD_COUNT]; //!< Pointers to the various section callbacks.
} rad_module_t;
/*
* Create free and destroy module instances
*/
-int modules_bootstrap(CONF_SECTION *root) CC_HINT(nonnull);
+void *module_thread_instance_find(void *inst);
+int modules_thread_instantiate(CONF_SECTION *root) CC_HINT(nonnull);
int modules_instantiate(CONF_SECTION *root) CC_HINT(nonnull);
+int modules_bootstrap(CONF_SECTION *root) CC_HINT(nonnull);
int modules_free(void);
int module_instance_read_only(TALLOC_CTX *ctx, char const *name);
#include <freeradius-devel/interpreter.h>
#include <freeradius-devel/parser.h>
+fr_thread_local_setup(rbtree_t *, module_thread_inst_tree);
+
static TALLOC_CTX *instance_ctx = NULL;
/*
return talloc_get_type_abort(inst, module_instance_t);
}
+/** Free all modules loaded by the server
+ *
+ * @return 0.
+ */
+int modules_free(void)
+{
+ /*
+ * Free instances first, then dynamic libraries.
+ */
+ TALLOC_FREE(instance_ctx);
+
+ return 0;
+}
+
/** Find an existing module instance and verify it implements the specified method
*
* Extracts the method from the module name where the format is @verbatim <module>.<method> @endverbatim
return inst;
}
-/** Free all modules loaded by the server
+/** Retrieve module/thread specific instance data for a module
*
- * @return 0.
+ * @param[in] instance to find thread specific data for.
+ * @return
+ * - Thread specific instance data on success.
+ * - NULL if module has no thread instance data.
*/
-int modules_free(void)
+void *module_thread_instance_find(void *instance)
{
- /*
- * Free instances first, then dynamic libraries.
- */
- TALLOC_FREE(instance_ctx);
+ module_instance_t *inst = instance;
+ rbtree_t *tree = fr_thread_local_get(module_thread_inst_tree);
+ module_thread_instance_t find, *found;
+
+ if (!inst->module->thread_instantiate || !inst->module->thread_inst_size) return NULL;
+
+ memset(&find, 0, sizeof(find));
+ find.inst = inst;
+
+ found = rbtree_finddata(tree, &find);
+ if (!found) return NULL;
+
+ return found->data;
+}
+
+/** Destructor for module_thread_instance_t
+ *
+ */
+static int _module_thread_instance_free(void *thread_inst_data)
+{
+ module_thread_instance_t *thread_inst = talloc_parent(thread_inst_data);
+
+ if (thread_inst->inst->module->thread_detach) {
+ (void) thread_inst->inst->module->thread_detach(thread_inst->inst, thread_inst->data);
+ }
+
+ return 0;
+}
+
+/** Frees the thread local instance free and any thread local instance data
+ *
+ * @param[in] to_free Thread specific module instance tree to free.
+ */
+static void _module_thread_inst_tree_free(void *to_free)
+{
+ rbtree_t *thread_inst_tree = talloc_get_type_abort(to_free, rbtree_t);
+ rbtree_free(thread_inst_tree);
+}
+
+/** Compare two thread instances based on inst pointer
+ *
+ * @param[in] a First thread specific module instance.
+ * @param[in] b Second thread specific module instance.
+ * @return
+ * - +1 if a > b.
+ * - -1 if a < b.
+ * - 0 if a == b.
+ */
+static int _module_thread_inst_tree_cmp(void const *a, void const *b)
+{
+ module_thread_instance_t const *my_a = a, *my_b = b;
+
+ if (my_a->inst > my_b->inst) return +1;
+ if (my_a->inst < my_b->inst) return -1;
+
+ return 0;
+}
+
+/** Setup thread specific instance data for a module
+ *
+ * @param[in] instance of module to perform thread instantiation for.
+ * @param[in] ctx modules section, containing instance data.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static int _module_thread_instantiate(void *instance, UNUSED void *ctx)
+{
+ module_instance_t *inst = talloc_get_type_abort(instance, module_instance_t);
+ module_thread_instance_t *thread_inst;
+ rbtree_t *thread_inst_tree = talloc_get_type_abort(ctx, rbtree_t);
+ int ret;
+
+ if (!inst->module->thread_instantiate) return 0;
+
+ MEM(thread_inst = talloc_zero(NULL, module_thread_instance_t));
+ thread_inst->inst = inst;
+
+ if (inst->module->thread_inst_size) {
+ MEM(thread_inst->data = talloc_zero_array(thread_inst, uint8_t, inst->module->thread_inst_size));
+ talloc_set_name(thread_inst->data, "%s", inst->name);
+ talloc_set_destructor(thread_inst->data, _module_thread_instance_free);
+ rbtree_insert(thread_inst_tree, thread_inst);
+ }
+
+ ret = inst->module->thread_instantiate(inst->cs, inst, thread_inst->data);
+ if (ret < 0) {
+ ERROR("Thread instantiation failed for module \"%s\"", inst->name);
+ return -1;
+ }
+
+ return 0;
+}
+
+/** 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] root Configuration root.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+int modules_thread_instantiate(CONF_SECTION *root)
+{
+ CONF_SECTION *modules;
+ rbtree_t *thread_inst_tree;
+
+ modules = cf_section_sub_find(root, "modules");
+ if (!modules) return 0;
+
+ thread_inst_tree = fr_thread_local_init(module_thread_inst_tree, _module_thread_inst_tree_free);
+ if (!thread_inst_tree) {
+ MEM(thread_inst_tree = rbtree_create(NULL, _module_thread_inst_tree_cmp, rbtree_node_talloc_free, 0));
+ fr_thread_local_set(module_thread_inst_tree, thread_inst_tree);
+ }
+
+ if (cf_data_walk(modules, CF_DATA_TYPE_MODULE_INSTANCE, _module_thread_instantiate, thread_inst_tree) < 0) {
+ _module_thread_inst_tree_free(thread_inst_tree); /* make re-entrant */
+ fr_thread_local_set(module_thread_inst_tree, NULL);
+ return -1;
+ }
return 0;
}
#include <freeradius-devel/process.h>
#include <freeradius-devel/heap.h>
#include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/modules.h>
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
goto done;
}
+ /*
+ * Perform thread specific module instantiation
+ */
+ if (modules_thread_instantiate(main_config.config) < 0) {
+ ERROR("Thread instantiation failed");
+ goto done;
+ }
+
thread->status = THREAD_ACTIVE;
/*
if (modules_bootstrap(main_config.config) < 0) goto exit_failure;
/*
- * Load the modules
+ * Instantiate the modules
*/
if (modules_instantiate(main_config.config) < 0) goto exit_failure;
+ /*
+ * Perform any thread specific instantiation
+ */
+ if (modules_thread_instantiate(main_config.config) < 0) goto exit_failure;
+
/*
* And then load the virtual servers.
*/
_timeval_t *timeval_m;
} rlm_test_t;
+typedef struct {
+ pthread_t value;
+} rlm_test_thread_t;
+
/*
* A mapping of configuration file names to internal variables.
*/
return 1;
}
+static int mod_thread_instantiate(UNUSED CONF_SECTION *conf, UNUSED void *instance, void *thread)
+{
+ rlm_test_thread_t *t = thread;
+
+ t->value = pthread_self();
+ INFO("Performing instantiation for thread %p", t->value);
+
+ return 0;
+}
+
+static int mod_thread_detach(UNUSED void *instance, void *thread)
+{
+ rlm_test_thread_t *t = thread;
+
+ INFO("Performing detach for thread %p", t->value);
+
+ if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
+
+ return 0;
+}
+
/*
* Do any per-module initialization that is separate to each
* configured instance of the module. e.g. set up connections
*/
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, UNUSED void *thread, REQUEST *request)
{
+ rlm_test_thread_t *t = thread;
+
RINFO("RINFO message");
RDEBUG("RDEBUG message");
RDEBUG2("RDEBUG2 message");
REXDENT();
REDEBUG4("RDEBUG4 error message");
+ if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
+
return RLM_MODULE_OK;
}
*/
static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, UNUSED void *thread, UNUSED REQUEST *request)
{
+ rlm_test_thread_t *t = thread;
+
+ if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
+
return RLM_MODULE_OK;
}
*/
static rlm_rcode_t CC_HINT(nonnull) mod_preacct(UNUSED void *instance, UNUSED void *thread, UNUSED REQUEST *request)
{
+ rlm_test_thread_t *t = thread;
+
+ if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
+
return RLM_MODULE_OK;
}
*/
static rlm_rcode_t CC_HINT(nonnull) mod_accounting(UNUSED void *instance, UNUSED void *thread, UNUSED REQUEST *request)
{
+ rlm_test_thread_t *t = thread;
+
+ if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
+
return RLM_MODULE_OK;
}
*/
static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(UNUSED void *instance, UNUSED void *thread, REQUEST *request)
{
- request->simul_count=0;
+ rlm_test_thread_t *t = thread;
+
+ request->simul_count = 0;
+
+ if (!rad_cond_assert(t->value == pthread_self())) return RLM_MODULE_FAIL;
return RLM_MODULE_OK;
}
*/
extern rad_module_t rlm_test;
rad_module_t rlm_test = {
- .magic = RLM_MODULE_INIT,
- .name = "test",
- .type = RLM_TYPE_THREAD_SAFE,
- .inst_size = sizeof(rlm_test_t),
- .config = module_config,
- .instantiate = mod_instantiate,
- .detach = mod_detach,
+ .magic = RLM_MODULE_INIT,
+ .name = "test",
+ .type = RLM_TYPE_THREAD_SAFE,
+ .inst_size = sizeof(rlm_test_t),
+ .thread_inst_size = sizeof(rlm_test_thread_t),
+ .config = module_config,
+ .instantiate = mod_instantiate,
+ .thread_instantiate = mod_thread_instantiate,
+ .thread_detach = mod_thread_detach,
+ .detach = mod_detach,
.methods = {
[MOD_AUTHENTICATE] = mod_authenticate,
[MOD_AUTHORIZE] = mod_authorize,
return NULL;
}
+#if 0
+ /*
+ * Perform thread specific module instantiation
+ */
+ if (modules_thread_instantiate(main_config.config) < 0) {
+ ERROR("Thread instantiation failed");
+ talloc_free(ctx);
+ return NULL;
+ }
+#endif
+
while (true) {
bool wait_for_event;
int num_events;