]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
v4: Framework for configuring, initialising and freeing libraries (#4541)
authorNick Porter <nick@portercomputing.co.uk>
Thu, 2 Jun 2022 16:50:31 +0000 (17:50 +0100)
committerGitHub <noreply@github.com>
Thu, 2 Jun 2022 16:50:31 +0000 (12:50 -0400)
* 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

src/bin/radiusd.c
src/include/build.h
src/lib/server/base.h
src/lib/server/dl_module.c
src/lib/server/dl_module.h
src/lib/server/global_lib.c [new file with mode: 0644]
src/lib/server/global_lib.h [new file with mode: 0644]
src/lib/server/libfreeradius-server.mk

index a9e502a2d57f789d1982be175276b87404a9fe40..97712cadfc1d09ebbb183be954e744aa76a81efc 100644 (file)
@@ -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.
         */
index 8453270d1a4e6c33c956398480bc2ec1f3f673ab..c949f4e88cd5731b101f41460b7db40c35d425c8 100644 (file)
@@ -378,3 +378,8 @@ do { \
 #ifdef __cplusplus
 }
 #endif
+
+/*
+ *     For closing macros which open a code block e.g. fr_rb_inorder_foreach
+ */
+#define endforeach }
index 8f02ecab77c1f78acb1380ce59b3fe9cc1b765d3..75d325ba8aa9843c55f7e2587a67feadb271f65c 100644 (file)
@@ -39,6 +39,7 @@ RCSIDH(base_h, "$Id$")
 #include <freeradius-devel/server/dl_module.h>
 #include <freeradius-devel/server/exec.h>
 #include <freeradius-devel/server/exfile.h>
+#include <freeradius-devel/server/global_lib.h>
 #include <freeradius-devel/server/listen.h>
 #include <freeradius-devel/server/log.h>
 #include <freeradius-devel/server/main_config.h>
index 5424aedbad3f748e81b1621c5c5207eb771c183d..61511de95b0b894af96e75e1bcd43ed86f0a5b88 100644 (file)
@@ -28,6 +28,7 @@ RCSID("$Id$")
 #include <freeradius-devel/server/dl_module.h>
 
 #include <freeradius-devel/server/log.h>
+#include <freeradius-devel/server/global_lib.h>
 #include <freeradius-devel/util/debug.h>
 
 #include <freeradius-devel/util/dl.h>
@@ -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)");
index 9b36be5bfc387259553d656cb22c85e12cce6129..1695276714a518fed95c6c0edc4b0114cf91a75d 100644 (file)
@@ -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 (file)
index 0000000..a4d11eb
--- /dev/null
@@ -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 <freeradius-devel/server/global_lib.h>
+#include <freeradius-devel/server/main_config.h>
+#include <freeradius-devel/util/atexit.h>
+
+/*
+ *  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 (file)
index 0000000..bb7ea55
--- /dev/null
@@ -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 <freeradius-devel/server/cf_parse.h>
+#include <freeradius-devel/util/dl.h>
+
+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);
index 2859518773535d9bf19bd877d612195eafadaa60..0e948d46eb2cccce47adfdee86a3bc0d54127d85 100644 (file)
@@ -16,6 +16,7 @@ SOURCES       := \
        exec.c \
        exec_legacy.c \
        exfile.c \
+       global_lib.c \
        log.c \
        main_config.c \
        main_loop.c \