mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 13:37:17 +02:00
lib: dynamic module loading
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>
This commit is contained in:
parent
4c0a782d47
commit
30771d65b2
20
configure.ac
20
configure.ac
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
32
lib/libfrr.c
32
lib/libfrr.c
|
@ -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();
|
||||
|
|
13
lib/libfrr.h
13
lib/libfrr.h
|
@ -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);
|
||||
|
|
159
lib/module.c
Normal file
159
lib/module.c
Normal file
|
@ -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
|
104
lib/module.h
Normal file
104
lib/module.h
Normal file
|
@ -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 */
|
Loading…
Reference in a new issue