diff options
author | G. Paul Ziemba <p-fbsd-bugs@ziemba.us> | 2021-09-03 18:49:05 +0200 |
---|---|---|
committer | G. Paul Ziemba <p-fbsd-bugs@ziemba.us> | 2021-09-14 18:51:49 +0200 |
commit | 52fad8f6563080c64b5609f8b357ed47b1dfb78c (patch) | |
tree | 71f29fe8be8f7f7d68982c8e15abfdfbd08e3a18 /lib/module.c | |
parent | Merge pull request #9542 from pguibert6WIND/ssh_rpki_source (diff) | |
download | frr-52fad8f6563080c64b5609f8b357ed47b1dfb78c.tar.xz frr-52fad8f6563080c64b5609f8b357ed47b1dfb78c.zip |
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>
Diffstat (limited to 'lib/module.c')
-rw-r--r-- | lib/module.c | 92 |
1 files changed, 76 insertions, 16 deletions
diff --git a/lib/module.c b/lib/module.c index 1d51a6396..4037bfbeb 100644 --- a/lib/module.c +++ b/lib/module.c @@ -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; } |