From: Nick Porter Date: Thu, 2 Jun 2022 16:50:31 +0000 (+0100) Subject: v4: Framework for configuring, initialising and freeing libraries (#4541) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b31418bf01197a5db1e2cd34cbf45fb173a14078;p=thirdparty%2Ffreeradius-server.git v4: Framework for configuring, initialising and freeing libraries (#4541) * Add endforeach macro to close code blocks opened by foreach macros * Define framework for initialising and freeing libraries Reading library configuration from a subsection of a global {} config section. * Initialise global list of libraries * Register handlers for "lib" symbols to auto initialise and free libraries --- diff --git a/src/bin/radiusd.c b/src/bin/radiusd.c index a9e502a2d57..97712cadfc1 100644 --- a/src/bin/radiusd.c +++ b/src/bin/radiusd.c @@ -573,11 +573,21 @@ int main(int argc, char *argv[]) EXIT_WITH_FAILURE; } + /* + * Setup the global structure for libraries + */ + if (global_lib_init() < 0) EXIT_WITH_FAILURE; + /* * Read the configuration files, BEFORE doing anything else. */ if (main_config_init(config) < 0) EXIT_WITH_FAILURE; + /* + * Instantiate any libraries loaded by config parsing + */ + if (global_lib_instantiate() < 0) EXIT_WITH_FAILURE; + /* * Check we're the only process using this config. */ diff --git a/src/include/build.h b/src/include/build.h index 8453270d1a4..c949f4e88cd 100644 --- a/src/include/build.h +++ b/src/include/build.h @@ -378,3 +378,8 @@ do { \ #ifdef __cplusplus } #endif + +/* + * For closing macros which open a code block e.g. fr_rb_inorder_foreach + */ +#define endforeach } diff --git a/src/lib/server/base.h b/src/lib/server/base.h index 8f02ecab77c..75d325ba8aa 100644 --- a/src/lib/server/base.h +++ b/src/lib/server/base.h @@ -39,6 +39,7 @@ RCSIDH(base_h, "$Id$") #include #include #include +#include #include #include #include diff --git a/src/lib/server/dl_module.c b/src/lib/server/dl_module.c index 5424aedbad3..61511de95b0 100644 --- a/src/lib/server/dl_module.c +++ b/src/lib/server/dl_module.c @@ -28,6 +28,7 @@ RCSID("$Id$") #include #include +#include #include #include @@ -732,6 +733,14 @@ dl_module_loader_t *dl_module_loader_init(char const *lib_dir) dl_symbol_free_cb_register(dl_module_loader->dl_loader, DL_PRIORITY_DICT, "dict", fr_dl_dict_autofree, NULL); + /* + * Register library autoload callbacks + */ + dl_symbol_init_cb_register(dl_module_loader->dl_loader, + DL_PRIORITY_LIB, "lib", global_lib_auto_instantiate, NULL); + dl_symbol_free_cb_register(dl_module_loader->dl_loader, + DL_PRIORITY_LIB, "lib", global_lib_autofree, NULL); + talloc_set_destructor(dl_module_loader, _dl_module_loader_free); DEBUG4("Module linker search path(s)"); diff --git a/src/lib/server/dl_module.h b/src/lib/server/dl_module.h index 9b36be5bfc3..1695276714a 100644 --- a/src/lib/server/dl_module.h +++ b/src/lib/server/dl_module.h @@ -78,6 +78,7 @@ typedef enum { #define DL_PRIORITY_DICT 30 //!< Callback priority for dictionary autoloading #define DL_PRIORITY_DICT_ATTR 29 //!< Callback priority for attribute resolution #define DL_PRIORITY_DICT_ENUM 28 //!< Callback priority for enum resolution +#define DL_PRIORITY_LIB 20 //!< Callback priority for library config #define DL_PRIORITY_BOOTSTRAP 10 //!< Callback priority for bootstrap callback typedef struct dl_module_loader_s dl_module_loader_t; diff --git a/src/lib/server/global_lib.c b/src/lib/server/global_lib.c new file mode 100644 index 00000000000..a4d11eb2516 --- /dev/null +++ b/src/lib/server/global_lib.c @@ -0,0 +1,233 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * $Id$ + * + * @file global_lib.c + * @brief Handle global configuration, initialisation and freeing for libraries + * + * @copyright 2022 The FreeRADIUS server project + */ +#include +#include +#include + +/* + * Terminator for array of global_lib_autoinit_t + */ +global_lib_autoinit_t const lib_autoinit_terminator = { .name = NULL }; + +/* + * Global list of libraries + */ +typedef struct { + fr_rb_tree_t libs; +} global_lib_list_t; + +static global_lib_list_t *lib_list; + +/** Structure to track use of libraries. + * + */ +typedef struct { + fr_rb_node_t entry; //!< Entry in tree of libraries + global_lib_autoinit_t const *autoinit; //!< Autoinit structure used to manage this library + uint32_t instance_count; //!< Number of current uses of this library + bool initialised; //!< Has the init callback been run for this library +} global_lib_inst_t; + +/** Parse the global config section for a library and call its init function + * + * @param[in] lib to configure and initialise + * @return + * - 0 on success + * - -1 on failure + */ +static int lib_init_call(global_lib_inst_t *lib) +{ + CONF_SECTION *global_cs, *cs; + + /* + * Find relevant config section + * If the section does not exist allocate an empty one + * so default rules can be evaluated. + */ + global_cs = cf_section_find(cf_root(main_config->root_cs), "global", NULL); + if (!global_cs) global_cs = cf_section_alloc(main_config->root_cs, main_config->root_cs, "global", NULL); + + cs = cf_section_find(global_cs, lib->autoinit->name, NULL); + if (!cs) cs = cf_section_alloc(global_cs, global_cs, lib->autoinit->name, NULL); + + if ((cf_section_rules_push(cs, lib->autoinit->config)) < 0 || + (cf_section_parse(lib, lib->autoinit->inst, cs) < 0)) { + cf_log_err(cs, "Failed evaluating configuration for libldap"); + return -1; + } + + /* + * Call the init callback if defined + */ + if (lib->autoinit->init && (lib->autoinit->init()) < 0) return -1; + + lib->initialised = true; + + return 0; +} + +/** Instantiate a list of libraries + * + * @param to_init Array of autoinit structures detailing libraries to initialise + * @return + * - 0 on succcess + * - -1 on failure + */ +static int lib_auto_instantiate(global_lib_autoinit_t * const *to_init) +{ + global_lib_autoinit_t * const *p; + + for (p = to_init; *p != &lib_autoinit_terminator; p++) { + global_lib_inst_t *lib = NULL; + + lib = fr_rb_find(&lib_list->libs, &(global_lib_inst_t){ .autoinit = *p }); + + /* + * If the library is already initialised, just increase the reference count + */ + if ((lib) && (lib->initialised)) { + lib->instance_count++; + return 0; + } + + if (!lib) { + MEM(lib = talloc_zero(lib_list, global_lib_inst_t)); + lib->autoinit = *p; + fr_rb_insert(&lib_list->libs, lib); + } + lib->instance_count++; + + /* + * If the main config parsing is not complete we can't initialise the library yet + */ + if (!main_config->root_cs) continue; + + DEBUG2("Instantiating %s", lib->autoinit->name); + if (lib_init_call(lib) < 0) return -1; + } + + return 0; +} + +/** Callback for creation of "lib" symbols + * + */ +int global_lib_auto_instantiate(UNUSED dl_t const *module, void *symbol, UNUSED void *user_ctx) +{ + if (lib_auto_instantiate((global_lib_autoinit_t **)symbol) < 0) return -1; + + return 0; +} + +/** Run free callbacks for external libraries no-longer in use + * + * @param[in] to_free Array of autoinit structures detailing libraries to free + */ +static void lib_autofree(global_lib_autoinit_t * const *to_free) +{ + global_lib_autoinit_t * const *p; + + for (p = to_free; *p != &lib_autoinit_terminator; p++) { + global_lib_inst_t *lib = NULL; + + lib = fr_rb_find(&lib_list->libs, &(global_lib_inst_t){ .autoinit = *p }); + + fr_assert_msg(lib, "Library %s already freed", (*p)->name); + + if (--lib->instance_count > 0) return; + + /* + * Only run the free callback if the library was successfully initialised + */ + if (lib->initialised && ((*p)->free)) (*p)->free(); + + fr_rb_remove(&lib_list->libs, lib); + talloc_free(lib); + } +} + +/** Callback for freeing of "lib" symbols + * + */ +void global_lib_autofree(UNUSED dl_t const *module, void *symbol, UNUSED void *user_ctx) +{ + lib_autofree((global_lib_autoinit_t **)symbol); +} + +/** Compare two fr_lib_t + * + */ +static int8_t _lib_cmp(void const *one, void const *two) +{ + global_lib_inst_t const *a = one; + global_lib_inst_t const *b = two; + + return CMP(a->autoinit, b->autoinit); +} + +/** Free global list of libraries + * + * Called as an atexit function + */ +static int _lib_list_free_atexit(UNUSED void *uctx) +{ + if (talloc_free(lib_list) < 0) return -1; + lib_list = NULL; + return 0; +} + +/** Initialise the global list of external libraries + * + */ +int global_lib_init(void) +{ + MEM(lib_list = talloc_zero(NULL, global_lib_list_t)); + fr_rb_inline_init(&lib_list->libs, global_lib_inst_t, entry, _lib_cmp, NULL); + + fr_atexit_global(_lib_list_free_atexit, NULL); + return 0; +} + +/** Walk the tree of libraries and instantiate any which are pending + * + */ +int global_lib_instantiate(void) +{ + /* + * Must be called after main config has been parsed + */ + fr_assert(main_config->root_cs); + + DEBUG2("#### Instantiating libraries ####"); + fr_rb_inorder_foreach(&lib_list->libs, global_lib_inst_t, lib) { + if (lib->initialised) continue; + + DEBUG2("Instantiating %s", lib->autoinit->name); + if (lib_init_call(lib) < 0) return -1; + + } + endforeach + return 0; +} diff --git a/src/lib/server/global_lib.h b/src/lib/server/global_lib.h new file mode 100644 index 00000000000..bb7ea55515e --- /dev/null +++ b/src/lib/server/global_lib.h @@ -0,0 +1,57 @@ +#pragma once +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * + * @file lib/server/global_lib.h + * @brief API for initialising and freeing libraries. + * + * @copyright 2022 The FreeRADIUS server project + */ +RCSIDH(lib_h, "$Id$") + +#include +#include + +typedef int (*lib_init_t)(void); + +typedef void (*lib_free_t)(void); + +/** Structure to define how to initialise libraries with global configuration + * + */ +typedef struct { + char const *name; //!< Name of library and section within global config + CONF_PARSER const *config; //!< Config parser for this library's global options + void *inst; //!< Module data to parse global config into + lib_init_t init; //!< Callback to initialise library + lib_free_t free; //!< Callback to free library +} global_lib_autoinit_t; + +/* + * To be used as terminator in an array of fr_lib_autoinit_t + */ +extern const global_lib_autoinit_t lib_autoinit_terminator; + +int global_lib_auto_instantiate(dl_t const *module, void *symbol, void *user_ctx); + +void global_lib_autofree(dl_t const *module, void *symbol, void *user_ctx); + +int global_lib_init(void); + +int global_lib_instantiate(void); diff --git a/src/lib/server/libfreeradius-server.mk b/src/lib/server/libfreeradius-server.mk index 28595187735..0e948d46eb2 100644 --- a/src/lib/server/libfreeradius-server.mk +++ b/src/lib/server/libfreeradius-server.mk @@ -16,6 +16,7 @@ SOURCES := \ exec.c \ exec_legacy.c \ exfile.c \ + global_lib.c \ log.c \ main_config.c \ main_loop.c \