diff options
Diffstat (limited to 'src/basic/dlfcn-util.c')
-rw-r--r-- | src/basic/dlfcn-util.c | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/src/basic/dlfcn-util.c b/src/basic/dlfcn-util.c new file mode 100644 index 0000000..8022f55 --- /dev/null +++ b/src/basic/dlfcn-util.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "dlfcn-util.h" + +static int dlsym_many_or_warnv(void *dl, int log_level, va_list ap) { + void (**fn)(void); + + /* Tries to resolve a bunch of function symbols, and logs an error about if it cannot resolve one of + * them. Note that this function possibly modifies the supplied function pointers if the whole + * operation fails. */ + + while ((fn = va_arg(ap, typeof(fn)))) { + void (*tfn)(void); + const char *symbol; + + symbol = va_arg(ap, typeof(symbol)); + + tfn = (typeof(tfn)) dlsym(dl, symbol); + if (!tfn) + return log_full_errno(log_level, + SYNTHETIC_ERRNO(ELIBBAD), + "Can't find symbol %s: %s", symbol, dlerror()); + *fn = tfn; + } + + return 0; +} + +int dlsym_many_or_warn_sentinel(void *dl, int log_level, ...) { + va_list ap; + int r; + + va_start(ap, log_level); + r = dlsym_many_or_warnv(dl, log_level, ap); + va_end(ap); + + return r; +} + +int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_level, ...) { + _cleanup_(dlclosep) void *dl = NULL; + int r; + + if (*dlp) + return 0; /* Already loaded */ + + dl = dlopen(filename, RTLD_LAZY); + if (!dl) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "%s is not installed: %s", filename, dlerror()); + + log_debug("Loaded '%s' via dlopen()", filename); + + va_list ap; + va_start(ap, log_level); + r = dlsym_many_or_warnv(dl, log_level, ap); + va_end(ap); + + if (r < 0) + return r; + + /* Note that we never release the reference here, because there's no real reason to. After all this + * was traditionally a regular shared library dependency which lives forever too. */ + *dlp = TAKE_PTR(dl); + return 1; +} |