lib/module.c and callers of frrmod_load(): fix error messages

frrmod_load() attempts to dlopen() several possible paths
    (constructed from its basename argument) until one succeeds.

    Each dlopen() attempt may fail for a different reason, and
    the important one might not be the last one. Example:

	dlopen(a/foo): file not found
	dlopen(b/foo): symbol "bar" missing
	dlopen(c/foo): file not found

    Previous code reported only the most recent error. Now frrmod_load()
    describes each dlopen() failure.

Signed-off-by: G. Paul Ziemba <paulz@labn.net>
This commit is contained in:
G. Paul Ziemba 2021-09-03 09:49:05 -07:00
parent 53b08a373d
commit 52fad8f656
5 changed files with 99 additions and 32 deletions

View file

@ -94,10 +94,9 @@ static void ldp_load_module(const char *name)
{
const char *dir;
dir = ldpd_di.module_path ? ldpd_di.module_path : frr_moduledir;
char moderr[256];
struct frrmod_runtime *module;
module = frrmod_load(name, dir, moderr, sizeof(moderr));
module = frrmod_load(name, dir, NULL,NULL);
if (!module) {
fprintf(stderr, "%s: failed to load %s", __func__, name);
log_warnx("%s: failed to load %s", __func__, name);

View file

@ -674,13 +674,19 @@ static void frr_mkdir(const char *path, bool strip)
strerror(errno));
}
static void _err_print(const void *cookie, const char *errstr)
{
const char *prefix = (const char *)cookie;
fprintf(stderr, "%s: %s\n", prefix, errstr);
}
static struct thread_master *master;
struct thread_master *frr_init(void)
{
struct option_chain *oc;
struct frrmod_runtime *module;
struct zprivs_ids_t ids;
char moderr[256];
char p_instance[16] = "", p_pathspace[256] = "";
const char *dir;
dir = di->module_path ? di->module_path : frr_moduledir;
@ -734,11 +740,9 @@ struct thread_master *frr_init(void)
frrmod_init(di->module);
while (modules) {
modules = (oc = modules)->next;
module = frrmod_load(oc->arg, dir, moderr, sizeof(moderr));
if (!module) {
fprintf(stderr, "%s\n", moderr);
module = frrmod_load(oc->arg, dir, _err_print, __func__);
if (!module)
exit(1);
}
XFREE(MTYPE_TMP, oc);
}

View file

@ -26,9 +26,11 @@
#include "module.h"
#include "memory.h"
#include "lib/version.h"
#include "printfrr.h"
DEFINE_MTYPE_STATIC(LIB, MODULE_LOADNAME, "Module loading name");
DEFINE_MTYPE_STATIC(LIB, MODULE_LOADARGS, "Module loading arguments");
DEFINE_MTYPE_STATIC(LIB, MODULE_LOAD_ERR, "Module loading error");
static struct frrmod_info frrmod_default_info = {
.name = "libfrr",
@ -67,14 +69,64 @@ void frrmod_init(struct frrmod_runtime *modinfo)
execname = modinfo->info->name;
}
struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, char *err,
size_t err_len)
/*
* If caller wants error strings, it should define non-NULL pFerrlog
* which will be called with 0-terminated error messages. These
* messages will NOT contain newlines, and the (*pFerrlog)() function
* could be called multiple times for a single call to frrmod_load().
*
* The (*pFerrlog)() function may copy these strings if needed, but
* should expect them to be freed by frrmod_load() before frrmod_load()
* returns.
*
* frrmod_load() is coded such that (*pFerrlog)() will be called only
* in the case where frrmod_load() returns an error.
*/
struct frrmod_runtime *frrmod_load(const char *spec, const char *dir,
void (*pFerrlog)(const void *, const char *),
const void *pErrlogCookie)
{
void *handle = NULL;
char name[PATH_MAX], fullpath[PATH_MAX * 2], *args;
struct frrmod_runtime *rtinfo, **rtinfop;
const struct frrmod_info *info;
#define FRRMOD_LOAD_N_ERRSTR 10
char *aErr[FRRMOD_LOAD_N_ERRSTR];
unsigned int iErr = 0;
memset(aErr, 0, sizeof(aErr));
#define ERR_RECORD(...) \
do { \
if (pFerrlog && (iErr < FRRMOD_LOAD_N_ERRSTR)) { \
aErr[iErr++] = asprintfrr(MTYPE_MODULE_LOAD_ERR, \
__VA_ARGS__); \
} \
} while (0)
#define ERR_REPORT \
do { \
if (pFerrlog) { \
unsigned int i; \
\
for (i = 0; i < iErr; ++i) { \
(*pFerrlog)(pErrlogCookie, aErr[i]); \
} \
} \
} while (0)
#define ERR_FREE \
do { \
unsigned int i; \
\
for (i = 0; i < iErr; ++i) { \
XFREE(MTYPE_MODULE_LOAD_ERR, aErr[i]); \
aErr[i] = 0; \
} \
iErr = 0; \
} while (0)
snprintf(name, sizeof(name), "%s", spec);
args = strchr(name, ':');
if (args)
@ -85,32 +137,41 @@ struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, char *err,
snprintf(fullpath, sizeof(fullpath), "%s/%s_%s.so", dir,
execname, name);
handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
if (!handle)
ERR_RECORD("loader error: dlopen(%s): %s",
fullpath, dlerror());
}
if (!handle) {
snprintf(fullpath, sizeof(fullpath), "%s/%s.so", dir,
name);
handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
if (!handle)
ERR_RECORD("loader error: dlopen(%s): %s",
fullpath, dlerror());
}
}
if (!handle) {
snprintf(fullpath, sizeof(fullpath), "%s", name);
handle = dlopen(fullpath, RTLD_NOW | RTLD_GLOBAL);
if (!handle)
ERR_RECORD("loader error: dlopen(%s): %s", fullpath,
dlerror());
}
if (!handle) {
if (err)
snprintf(err, err_len,
"loading module \"%s\" failed: %s", name,
dlerror());
ERR_REPORT;
ERR_FREE;
return NULL;
}
/* previous dlopen() errors are no longer relevant */
ERR_FREE;
rtinfop = dlsym(handle, "frr_module");
if (!rtinfop) {
dlclose(handle);
if (err)
snprintf(err, err_len,
"\"%s\" is not an FRR module: %s", name,
dlerror());
ERR_RECORD("\"%s\" is not an FRR module: %s", name, dlerror());
ERR_REPORT;
ERR_FREE;
return NULL;
}
rtinfo = *rtinfop;
@ -122,17 +183,13 @@ struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, char *err,
if (rtinfo->finished_loading) {
dlclose(handle);
if (err)
snprintf(err, err_len, "module \"%s\" already loaded",
name);
ERR_RECORD("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);
ERR_RECORD("module \"%s\" initialisation failed", name);
goto out_fail;
}
@ -140,11 +197,14 @@ struct frrmod_runtime *frrmod_load(const char *spec, const char *dir, char *err,
*frrmod_last = rtinfo;
frrmod_last = &rtinfo->next;
ERR_FREE;
return rtinfo;
out_fail:
XFREE(MTYPE_MODULE_LOADARGS, rtinfo->load_args);
XFREE(MTYPE_MODULE_LOADNAME, rtinfo->load_name);
ERR_REPORT;
ERR_FREE;
return NULL;
}

View file

@ -91,7 +91,9 @@ extern struct frrmod_runtime *frrmod_list;
extern void frrmod_init(struct frrmod_runtime *modinfo);
extern struct frrmod_runtime *frrmod_load(const char *spec, const char *dir,
char *err, size_t err_len);
void (*pFerrlog)(const void *,
const char *),
const void *pErrlogCookie);
#if 0
/* not implemented yet */
extern void frrmod_unload(struct frrmod_runtime *module);

View file

@ -81,11 +81,16 @@ static const struct frr_yang_module_info *const staticd_yang_modules[] = {
static int grpc_thread_stop(struct thread *thread);
static void _err_print(const void *cookie, const char *errstr)
{
std::cout << "Failed to load grpc module:" << errstr << std::endl;
}
static void static_startup(void)
{
// struct frrmod_runtime module;
// static struct option_chain *oc;
char moderr[256] = {};
cmd_init(1);
zlog_aux_init("NONE: ", LOG_DEBUG);
@ -94,17 +99,14 @@ static void static_startup(void)
/* Load the server side module -- check libtool path first */
std::string modpath = std::string(binpath) + std::string("../../../lib/.libs");
grpc_module = frrmod_load("grpc:50051", modpath.c_str(), moderr, sizeof(moderr));
grpc_module = frrmod_load("grpc:50051", modpath.c_str(), 0, 0);
if (!grpc_module) {
modpath = std::string(binpath) + std::string("../../lib");
grpc_module = frrmod_load("grpc:50051", modpath.c_str(), moderr,
sizeof(moderr));
grpc_module = frrmod_load("grpc:50051", modpath.c_str(),
_err_print, 0);
}
if (!grpc_module) {
std::cout << "Failed to load grpc module:" << moderr
<< std::endl;
if (!grpc_module)
exit(1);
}
static_debug_init();