]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: dynamic module loading
authorDavid Lamparter <equinox@opensourcerouting.org>
Tue, 31 May 2016 17:25:46 +0000 (19:25 +0200)
committerDavid Lamparter <equinox@opensourcerouting.org>
Fri, 24 Mar 2017 12:02:05 +0000 (13:02 +0100)
This adds a "-M" option to each daemon, to load dynamic modules at
startup.  Modules are by default located in /usr/lib/frr/modules (lib64
if appropriate).  Unloading or loading at runtime is not supported at
this point to keep things simple.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
configure.ac
lib/Makefile.am
lib/libfrr.c
lib/libfrr.h
lib/module.c [new file with mode: 0644]
lib/module.h [new file with mode: 0644]

index e46e44a8b751f8aa8559923fbe94edc1ca4a74cb..2a8cab34f122ff515eb9da8ae75e03b493bd5d89 100755 (executable)
@@ -55,6 +55,13 @@ dnl XXX add --pkgsrcrcdir to autoconf standard directory list somehow
 AC_SUBST(pkgsrcdir)
 AC_SUBST(pkgsrcrcdir)
 
+AC_ARG_WITH([moduledir], [AS_HELP_STRING([--with-moduledir=DIR], [module directory (${libdir}/frr/modules)])], [
+       moduledir="$withval"
+], [
+       moduledir="\${libdir}/frr/modules"
+])
+AC_SUBST([moduledir], [$moduledir])
+
 AC_ARG_ENABLE(tcmalloc,
        AS_HELP_STRING([--enable-tcmalloc], [Turn on tcmalloc]),
 [case "${enableval}" in
@@ -1347,6 +1354,14 @@ int main(void);
    AC_DEFINE_UNQUOTED(AS_TR_CPP(SNMP_${SNMP_METHOD}),,SNMP method to interface with snmpd)
 fi
 
+dnl ------
+dnl dlopen
+dnl ------
+AC_SEARCH_LIBS(dlopen, [dl dld], [], [
+  AC_MSG_ERROR([unable to find the dlopen()])
+])
+
+
 dnl ---------------------------
 dnl sockaddr and netinet checks
 dnl ---------------------------
@@ -1646,14 +1661,18 @@ AC_DEFINE_UNQUOTED(VTYSH_BIN_PATH, "$vtysh_bin",path to vtysh binary)
 CFG_SYSCONF="$sysconfdir"
 CFG_SBIN="$sbindir"
 CFG_STATE="$frr_statedir"
+CFG_MODULE="$moduledir"
 for I in 1 2 3 4 5 6 7 8 9 10; do
        eval CFG_SYSCONF="\"$CFG_SYSCONF\""
        eval CFG_SBIN="\"$CFG_SBIN\""
        eval CFG_STATE="\"$CFG_STATE\""
+       eval CFG_MODULE="\"$CFG_MODULE\""
 done
 AC_SUBST(CFG_SYSCONF)
 AC_SUBST(CFG_SBIN)
 AC_SUBST(CFG_STATE)
+AC_SUBST(CFG_MODULE)
+AC_DEFINE_UNQUOTED(MODULE_PATH, "$CFG_MODULE", path to modules)
 
 dnl ---------------------------
 dnl Check htonl works correctly
@@ -1728,6 +1747,7 @@ linker flags            : ${LDFLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM}
 state file directory    : ${frr_statedir}
 config file directory   : `eval echo \`echo ${sysconfdir}\``
 example directory       : `eval echo \`echo ${exampledir}\``
+module directory        : ${CFG_MODULE}
 user to run as          : ${enable_user}
 group to run as         : ${enable_group}
 group for vty sockets   : ${enable_vty_group}
index 1a8c7af42b78e41230168eccbbff92cc60b99bba..5731440640085a5c2cb8f7f549502bb72a9b0e65 100644 (file)
@@ -31,7 +31,9 @@ libfrr_la_SOURCES = \
        spf_backoff.c \
        libfrr.c \
        strlcpy.c \
-       strlcat.c
+       strlcat.c \
+       module.c \
+       # end
 
 BUILT_SOURCES = route_types.h gitversion.h command_parse.h command_lex.h
 
@@ -54,6 +56,7 @@ pkginclude_HEADERS = \
        monotime.h \
        spf_backoff.h \
        srcdest_table.h \
+       module.h \
        libfrr.h \
        # end
 
index b7ce0679c3a007d5bfb0c10b187cde7c145694ec..5c047040ae29de806f1811872611509ade025d13 100644 (file)
@@ -28,6 +28,7 @@
 #include "memory_vty.h"
 #include "zclient.h"
 #include "log_int.h"
+#include "module.h"
 
 const char frr_sysconfdir[] = SYSCONFDIR;
 const char frr_vtydir[] = DAEMON_VTY_DIR;
@@ -64,14 +65,16 @@ static const struct option lo_always[] = {
        { "help",        no_argument,       NULL, 'h' },
        { "version",     no_argument,       NULL, 'v' },
        { "daemon",      no_argument,       NULL, 'd' },
+       { "module",      no_argument,       NULL, 'M' },
        { "vty_socket",  required_argument, NULL, OPTION_VTYSOCK },
        { NULL }
 };
 static const struct optspec os_always = {
-       "hvdi:",
+       "hvdM:",
        "  -h, --help         Display this help and exit\n"
        "  -v, --version      Print program version\n"
        "  -d, --daemon       Runs in daemon mode\n"
+       "  -M, --module       Load specified module\n"
        "      --vty_socket   Override vty socket path\n",
        lo_always
 };
@@ -184,12 +187,18 @@ void frr_help_exit(int status)
        exit(status);
 }
 
+struct option_chain {
+       struct option_chain *next;
+       const char *arg;
+};
+static struct option_chain *modules = NULL, **modnext = &modules;
 static int errors = 0;
 
 static int frr_opt(int opt)
 {
        static int vty_port_set = 0;
        static int vty_addr_set = 0;
+       struct option_chain *oc;
        char *err;
 
        switch (opt) {
@@ -203,6 +212,13 @@ static int frr_opt(int opt)
        case 'd':
                di->daemon_mode = 1;
                break;
+       case 'M':
+               oc = XMALLOC(MTYPE_TMP, sizeof(*oc));
+               oc->arg = optarg;
+               oc->next = NULL;
+               *modnext = oc;
+               modnext = &oc->next;
+               break;
        case 'i':
                if (di->flags & FRR_NO_CFG_PID_DRY)
                        return 1;
@@ -298,6 +314,9 @@ int frr_getopt(int argc, char * const argv[], int *longindex)
 struct thread_master *frr_init(void)
 {
        struct thread_master *master;
+       struct option_chain *oc;
+       struct frrmod_runtime *module;
+       char moderr[256];
 
        srandom(time(NULL));
 
@@ -307,6 +326,17 @@ struct thread_master *frr_init(void)
        zlog_set_level (ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
 #endif
 
+       frrmod_init(di->module);
+       while (modules) {
+               modules = (oc = modules)->next;
+               module = frrmod_load(oc->arg, moderr, sizeof(moderr));
+               if (!module) {
+                       fprintf(stderr, "%s\n", moderr);
+                       exit(1);
+               }
+               XFREE(MTYPE_TMP, oc);
+       }
+
        zprivs_init(di->privs);
 
        master = thread_master_create();
index d37f406f5b601881bcf22792df39abd0d1f99b48..71225fad384a7fdb9ba9bc37da1a0e9fca06a31d 100644 (file)
@@ -26,6 +26,7 @@
 #include "thread.h"
 #include "log.h"
 #include "getopt.h"
+#include "module.h"
 
 #define FRR_NO_PRIVSEP         (1 << 0)
 #define FRR_NO_TCPVTY          (1 << 1)
@@ -40,6 +41,7 @@ struct frr_daemon_info {
        const char *name;
        const char *logname;
        unsigned short instance;
+       struct frrmod_runtime *module;
 
        char *vty_addr;
        int vty_port;
@@ -67,15 +69,22 @@ struct frr_daemon_info {
  * i.e. "ZEBRA" or "BGP"
  *
  * note that this macro is also a latch-on point for other changes (e.g.
- * upcoming plugin support) that need to place some per-daemon things.  Each
+ * upcoming module support) that need to place some per-daemon things.  Each
  * daemon should have one of these.
  */
 #define FRR_DAEMON_INFO(execname, constname, ...) \
        static struct frr_daemon_info execname ##_di = { \
                .name = # execname, \
                .logname = # constname, \
+               .module = THIS_MODULE, \
                __VA_ARGS__ \
-       };
+       }; \
+       FRR_COREMOD_SETUP( \
+               .name = # execname, \
+               .description = # execname " daemon", \
+                .version = FRR_VERSION, \
+       ) \
+       /* end */
 
 extern void frr_preinit(struct frr_daemon_info *daemon,
                int argc, char **argv);
diff --git a/lib/module.c b/lib/module.c
new file mode 100644 (file)
index 0000000..4ebe3c0
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2015-16  David Lamparter, for NetDEF, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dlfcn.h>
+
+#include "module.h"
+#include "memory.h"
+#include "version.h"
+
+DEFINE_MTYPE_STATIC(LIB, MODULE_LOADNAME, "Module loading name")
+DEFINE_MTYPE_STATIC(LIB, MODULE_LOADARGS, "Module loading arguments")
+
+static struct frrmod_info frrmod_default_info = {
+       .name = "libfrr",
+       .version = FRR_VERSION,
+       .description = "libfrr core module",
+};
+union _frrmod_runtime_u frrmod_default = {
+       .r.info = &frrmod_default_info,
+       .r.finished_loading = 1,
+};
+
+// if defined(HAVE_SYS_WEAK_ALIAS_ATTRIBUTE)
+// union _frrmod_runtime_u _frrmod_this_module
+//     __attribute__((weak, alias("frrmod_default")));
+// elif defined(HAVE_SYS_WEAK_ALIAS_PRAGMA)
+#pragma weak _frrmod_this_module = frrmod_default
+// else
+// error need weak symbol support
+// endif
+
+struct frrmod_runtime *frrmod_list = &frrmod_default.r;
+static struct frrmod_runtime **frrmod_last = &frrmod_default.r.next;
+static const char *execname = NULL;
+
+void frrmod_init(struct frrmod_runtime *modinfo)
+{
+       modinfo->finished_loading = 1;
+       *frrmod_last = modinfo;
+       frrmod_last = &modinfo->next;
+
+       execname = modinfo->info->name;
+}
+
+struct frrmod_runtime *frrmod_load(const char *spec,
+               char *err, size_t err_len)
+{
+       void *handle = NULL;
+       char name[PATH_MAX], fullpath[PATH_MAX], *args;
+       struct frrmod_runtime *rtinfo, **rtinfop;
+       const struct frrmod_info *info;
+
+       snprintf(name, sizeof(name), "%s", spec);
+       args = strchr(name, ':');
+       if (args)
+               *args++ = '\0';
+
+       if (!strchr(name, '/')) {
+               if (!handle && execname) {
+                       snprintf(fullpath, sizeof(fullpath), "%s/%s_%s.so",
+                                       MODULE_PATH, execname, name);
+                       handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
+               }
+               if (!handle) {
+                       snprintf(fullpath, sizeof(fullpath), "%s/%s.so",
+                                       MODULE_PATH, name);
+                       handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
+               }
+       }
+       if (!handle) {
+               snprintf(fullpath, sizeof(fullpath), "%s", name);
+               handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
+       }
+       if (!handle) {
+               if (err)
+                       snprintf(err, err_len,
+                                       "loading module \"%s\" failed: %s",
+                                       name, dlerror());
+               return NULL;
+       }
+
+       rtinfop = dlsym(handle, "frr_module");
+       if (!rtinfop) {
+               dlclose(handle);
+               if (err)
+                       snprintf(err, err_len,
+                                       "\"%s\" is not a Quagga module: %s",
+                                       name, dlerror());
+               return NULL;
+       }
+       rtinfo = *rtinfop;
+       rtinfo->load_name = XSTRDUP(MTYPE_MODULE_LOADNAME, name);
+       rtinfo->dl_handle = handle;
+       if (args)
+               rtinfo->load_args = XSTRDUP(MTYPE_MODULE_LOADARGS, args);
+       info = rtinfo->info;
+
+       if (rtinfo->finished_loading) {
+               dlclose(handle);
+               if (err)
+                       snprintf(err, err_len,
+                                       "module \"%s\" already loaded",
+                                       name);
+               goto out_fail;
+       }
+
+       if (info->init && info->init()) {
+               dlclose(handle);
+               if (err)
+                       snprintf(err, err_len,
+                                       "module \"%s\" initialisation failed",
+                                       name);
+               goto out_fail;
+       }
+
+       rtinfo->finished_loading = 1;
+
+       *frrmod_last = rtinfo;
+       frrmod_last = &rtinfo->next;
+       return rtinfo;
+
+out_fail:
+       if (rtinfo->load_args)
+               XFREE(MTYPE_MODULE_LOADARGS, rtinfo->load_args);
+       XFREE(MTYPE_MODULE_LOADNAME, rtinfo->load_name);
+       return NULL;
+}
+
+#if 0
+void frrmod_unload(struct frrmod_runtime *module)
+{
+}
+#endif
diff --git a/lib/module.h b/lib/module.h
new file mode 100644 (file)
index 0000000..cb66e60
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015-16  David Lamparter, for NetDEF, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _FRR_MODULE_H
+#define _FRR_MODULE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#if !defined(__GNUC__)
+# error module code needs GCC visibility extensions
+#elif __GNUC__ < 4
+# error module code needs GCC visibility extensions
+#else
+# define DSO_PUBLIC __attribute__ ((visibility ("default")))
+# define DSO_SELF   __attribute__ ((visibility ("protected")))
+# define DSO_LOCAL  __attribute__ ((visibility ("hidden")))
+#endif
+
+struct frrmod_runtime;
+
+struct frrmod_info {
+       /* single-line few-word title */
+       const char *name;
+       /* human-readable version number, should not contain spaces */
+       const char *version;
+       /* one-paragraph description */
+       const char *description;
+
+       int (*init)(void);
+};
+
+/* primary entry point structure to be present in loadable module under
+ * "_frrmod_this_module" dlsym() name
+ *
+ * note space for future extensions is reserved below, so other modules
+ * (e.g. memory management, hooks) can add fields
+ *
+ * const members/info are in frrmod_info.
+ */
+struct frrmod_runtime {
+       struct frrmod_runtime *next;
+
+       const struct frrmod_info *info;
+       void *dl_handle;
+       bool finished_loading;
+
+       char *load_name;
+       char *load_args;
+};
+
+/* space-reserving foo */
+struct _frrmod_runtime_size {
+       struct frrmod_runtime r;
+       /* this will barf if frrmod_runtime exceeds 1024 bytes ... */
+       uint8_t space[1024 - sizeof(struct frrmod_runtime)];
+};
+union _frrmod_runtime_u {
+       struct frrmod_runtime r;
+       struct _frrmod_runtime_size s;
+};
+
+extern union _frrmod_runtime_u _frrmod_this_module;
+#define THIS_MODULE (&_frrmod_this_module.r)
+
+#define FRR_COREMOD_SETUP(...) \
+       static const struct frrmod_info _frrmod_info = { __VA_ARGS__ }; \
+       DSO_LOCAL union _frrmod_runtime_u _frrmod_this_module = { \
+               .r.info = &_frrmod_info, \
+       };
+#define FRR_MODULE_SETUP(...) \
+       FRR_COREMOD_SETUP(__VA_ARGS__) \
+       DSO_SELF struct frrmod_runtime *frr_module = &_frrmod_this_module.r;
+
+extern struct frrmod_runtime *frrmod_list;
+
+extern void frrmod_init(struct frrmod_runtime *modinfo);
+extern struct frrmod_runtime *frrmod_load(const char *spec,
+               char *err, size_t err_len);
+#if 0
+/* not implemented yet */
+extern void frrmod_unload(struct frrmod_runtime *module);
+#endif
+
+#endif /* _FRR_MODULE_H */