diff --git a/meson.build b/meson.build index 5d9413a5bb..513b841ac5 100644 --- a/meson.build +++ b/meson.build @@ -1117,6 +1117,7 @@ conf.set10('HAVE_LIBIPTC', have) want_qrencode = get_option('qrencode') if want_qrencode != 'false' and not skip_deps libqrencode = dependency('libqrencode', + version : '>= 4', required : want_qrencode == 'true') have = libqrencode.found() else diff --git a/src/shared/qrcode-util.c b/src/shared/qrcode-util.c index f7d2d984c9..6b9ff8531b 100644 --- a/src/shared/qrcode-util.c +++ b/src/shared/qrcode-util.c @@ -5,12 +5,45 @@ #if HAVE_QRENCODE #include +#include "alloc-util.h" #include "dlfcn-util.h" #include "locale-util.h" #include "terminal-util.h" #define ANSI_WHITE_ON_BLACK "\033[40;37;1m" +static void *qrcode_dl = NULL; + +static QRcode* (*sym_QRcode_encodeString)(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive) = NULL; +static void (*sym_QRcode_free)(QRcode *qrcode) = NULL; + +int dlopen_qrencode(void) { + _cleanup_(dlclosep) void *dl = NULL; + int r; + + if (qrcode_dl) + return 0; /* Already loaded */ + + dl = dlopen("libqrencode.so.4", RTLD_LAZY); + if (!dl) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "libqrcode support is not installed: %s", dlerror()); + + r = dlsym_many_and_warn( + dl, + LOG_DEBUG, + DLSYM_ARG(QRcode_encodeString), + DLSYM_ARG(QRcode_free), + NULL); + 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. */ + qrcode_dl = TAKE_PTR(dl); + return 1; +} + static void print_border(FILE *output, unsigned width) { /* Four rows of border */ for (unsigned y = 0; y < 4; y += 2) { @@ -65,9 +98,6 @@ static void write_qrcode(FILE *output, QRcode *qr) { } int print_qrcode(FILE *out, const char *header, const char *string) { - QRcode* (*sym_QRcode_encodeString)(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive); - void (*sym_QRcode_free)(QRcode *qrcode); - _cleanup_(dlclosep) void *dl = NULL; QRcode* qr; int r; @@ -76,17 +106,7 @@ int print_qrcode(FILE *out, const char *header, const char *string) { if (!is_locale_utf8() || !colors_enabled()) return -EOPNOTSUPP; - dl = dlopen("libqrencode.so.4", RTLD_LAZY); - if (!dl) - return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), - "QRCODE support is not installed: %s", dlerror()); - - r = dlsym_many_and_warn( - dl, - LOG_DEBUG, - DLSYM_ARG(QRcode_encodeString), - DLSYM_ARG(QRcode_free), - NULL); + r = dlopen_qrencode(); if (r < 0) return r; diff --git a/src/shared/qrcode-util.h b/src/shared/qrcode-util.h index 6fc45c93d1..b64ecce80a 100644 --- a/src/shared/qrcode-util.h +++ b/src/shared/qrcode-util.h @@ -5,6 +5,8 @@ #include #if HAVE_QRENCODE +int dlopen_qrencode(void); + int print_qrcode(FILE *out, const char *header, const char *string); #else static inline int print_qrcode(FILE *out, const char *header, const char *string) { diff --git a/src/test/meson.build b/src/test/meson.build index 9e781f88dc..3afe5d58cb 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -71,6 +71,10 @@ tests += [ libshared], []], + [['src/test/test-dlopen-so.c'], + [libshared], + []], + [['src/test/test-job-type.c'], [libcore, libshared], diff --git a/src/test/test-dlopen-so.c b/src/test/test-dlopen-so.c new file mode 100644 index 0000000000..6436dc600f --- /dev/null +++ b/src/test/test-dlopen-so.c @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "cryptsetup-util.h" +#include "idn-util.h" +#include "macro.h" +#include "main-func.h" +#include "pwquality-util.h" +#include "qrcode-util.h" +#include "tests.h" + +static int run(int argc, char **argv) { + test_setup_logging(LOG_DEBUG); + + /* Try to load each of our weak library dependencies once. This is supposed to help finding cases + * where .so versions change and distributions update, but systemd doesn't have the new so names + * around yet. */ + +#if HAVE_LIBIDN2 || HAVE_LIBIDN + assert_se(dlopen_idn() >= 0); +#endif + +#if HAVE_LIBCRYPTSETUP + assert_se(dlopen_cryptsetup() >= 0); +#endif + +#if HAVE_PWQUALITY + assert_se(dlopen_pwquality() >= 0); +#endif + +#if HAVE_QRENCODE + assert_se(dlopen_qrencode() >= 0); +#endif + + return 0; +} + +DEFINE_MAIN_FUNCTION(run); diff --git a/test/test-functions b/test/test-functions index d5da8e0ea5..91cd7312d6 100644 --- a/test/test-functions +++ b/test/test-functions @@ -676,6 +676,19 @@ install_missing_libraries() { for i in $initdir{,/usr}/{sbin,bin}/* $initdir{,/usr}/lib/systemd/{,tests/{,manual/,unsafe/}}*; do LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$(get_ldpath $i):$(get_ldpath $i)/src/udev" inst_libs $i done + + # A number of dependencies is now optional via dlopen, so the install + # script will not pick them up, since it looks at linkage. + for lib in libcryptsetup libidn libidn2 pwquality libqrencode; do + if pkg-config --exists ${lib}; then + path=$(pkg-config --variable=libdir ${lib}) + if ! [[ ${lib} =~ ^lib ]]; then + lib="lib${lib}" + fi + inst_libs "${path}/${lib}.so" + inst_library "${path}/${lib}.so" + fi + done } cleanup_loopdev() {