diff options
| author | David Lamparter <equinox@opensourcerouting.org> | 2016-05-31 19:25:46 +0200 | 
|---|---|---|
| committer | David Lamparter <equinox@opensourcerouting.org> | 2017-03-24 13:02:05 +0100 | 
| commit | 30771d65b23e4ef15a8298357b70fc083c699750 (patch) | |
| tree | 3916bace6127d166780fdd187b4908f2f7e12e7b /lib/module.c | |
| parent | 4c0a782d477d527fb795640225c5f22767105f32 (diff) | |
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>
Diffstat (limited to 'lib/module.c')
| -rw-r--r-- | lib/module.c | 159 | 
1 files changed, 159 insertions, 0 deletions
diff --git a/lib/module.c b/lib/module.c new file mode 100644 index 0000000000..4ebe3c0da2 --- /dev/null +++ b/lib/module.c @@ -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  | 
