Merge pull request #31131 from poettering/dlopen-kmod

turn libkmod into a dlopen() dependency, too
This commit is contained in:
Luca Boccassi 2024-04-06 13:19:27 +01:00 committed by GitHub
commit 3abc3671f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 202 additions and 92 deletions

1
TODO
View file

@ -1436,7 +1436,6 @@ Features:
* make us use dynamically fewer deps for containers in general purpose distros:
o turn into dlopen() deps:
- kmod-libs (only when called from PID 1)
- libblkid (only in RootImage= handling in PID 1, but not elsewhere)
- libpam (only when called from PID 1)

View file

@ -1209,6 +1209,7 @@ libkmod = dependency('libkmod',
version : '>= 15',
required : get_option('kmod'))
conf.set10('HAVE_KMOD', libkmod.found())
libkmod_cflags = libkmod.partial_dependency(includes: true, compile_args: true)
libxenctrl = dependency('xencontrol',
version : '>= 4.9',

View file

@ -37,7 +37,9 @@ Packages=
grep
gzip
kernel-kvmsmall
kmod
libasan8
libkmod2
libubsan1
openssh-clients
openssh-server
@ -55,4 +57,6 @@ Packages=
InitrdPackages=
btrfs-progs
kmod
libkmod2
tpm2.0-tools

View file

@ -9,28 +9,13 @@
#include "fileio.h"
#include "kmod-setup.h"
#include "macro.h"
#include "module-util.h"
#include "recurse-dir.h"
#include "string-util.h"
#include "strv.h"
#include "virt.h"
#if HAVE_KMOD
#include "module-util.h"
static void systemd_kmod_log(
void *data,
int priority,
const char *file, int line,
const char *fn,
const char *format,
va_list args) {
/* library logging is enabled at debug only */
DISABLE_WARNING_FORMAT_NONLITERAL;
log_internalv(LOG_DEBUG, 0, file, line, fn, format, args);
REENABLE_WARNING;
}
static int match_modalias_recurse_dir_cb(
RecurseDirEvent event,
const char *path,
@ -166,11 +151,12 @@ int kmod_setup(void) {
#endif
};
int r;
if (have_effective_cap(CAP_SYS_MODULE) <= 0)
return 0;
_cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
_cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
FOREACH_ARRAY(kmod, kmod_table, ELEMENTSOF(kmod_table)) {
if (kmod->path && access(kmod->path, F_OK) >= 0)
continue;
@ -184,12 +170,9 @@ int kmod_setup(void) {
"this by loading the module...", kmod->module);
if (!ctx) {
ctx = kmod_new(NULL, NULL);
if (!ctx)
return log_oom();
kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
kmod_load_resources(ctx);
r = module_setup_context(&ctx);
if (r < 0)
return log_error_errno(r, "Failed to initialize kmod context: %m");
}
(void) module_load_and_warn(ctx, kmod->module, kmod->warn_if_unavailable);

View file

@ -125,7 +125,7 @@ libcore = shared_library(
libaudit,
libblkid,
libdl,
libkmod,
libkmod_cflags,
libm,
libmount,
libpam,

View file

@ -5,7 +5,7 @@ executables += [
'name' : 'systemd-modules-load',
'conditions' : ['HAVE_KMOD'],
'sources' : files('modules-load.c'),
'dependencies' : libkmod,
'dependencies' : libkmod_cflags,
},
]

View file

@ -23,14 +23,6 @@ static const char conf_file_dirs[] = CONF_PATHS_NULSTR("modules-load.d");
STATIC_DESTRUCTOR_REGISTER(arg_proc_cmdline_modules, strv_freep);
static void systemd_kmod_log(void *data, int priority, const char *file, int line,
const char *fn, const char *format, va_list args) {
DISABLE_WARNING_FORMAT_NONLITERAL;
log_internalv(priority, 0, file, line, fn, format, args);
REENABLE_WARNING;
}
static int add_modules(const char *p) {
_cleanup_strv_free_ char **k = NULL;
@ -156,7 +148,7 @@ static int parse_argv(int argc, char *argv[]) {
}
static int run(int argc, char *argv[]) {
_cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
_cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
int r, k;
r = parse_argv(argc, argv);
@ -171,12 +163,9 @@ static int run(int argc, char *argv[]) {
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
ctx = kmod_new(NULL, NULL);
if (!ctx)
return log_oom();
kmod_load_resources(ctx);
kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
r = module_setup_context(&ctx);
if (r < 0)
return log_error_errno(r, "Failed to initialize libkmod context: %m");
r = 0;

View file

@ -118,6 +118,7 @@ shared_sources = files(
'macvlan-util.c',
'mkdir-label.c',
'mkfs-util.c',
'module-util.c',
'mount-setup.c',
'mount-util.c',
'net-condition.c',
@ -236,10 +237,6 @@ if conf.get('HAVE_LIBBPF') == 1
shared_sources += files('bpf-link.c')
endif
if conf.get('HAVE_KMOD') == 1
shared_sources += files('module-util.c')
endif
if conf.get('HAVE_PAM') == 1
shared_sources += files('pam-util.c')
endif
@ -324,7 +321,7 @@ libshared_deps = [threads,
libdl,
libgcrypt,
libiptc_cflags,
libkmod,
libkmod_cflags,
liblz4_cflags,
libmount,
libopenssl,

View file

@ -6,6 +6,44 @@
#include "proc-cmdline.h"
#include "strv.h"
#if HAVE_KMOD
static void *libkmod_dl = NULL;
DLSYM_FUNCTION(kmod_list_next);
DLSYM_FUNCTION(kmod_load_resources);
DLSYM_FUNCTION(kmod_module_get_initstate);
DLSYM_FUNCTION(kmod_module_get_module);
DLSYM_FUNCTION(kmod_module_get_name);
DLSYM_FUNCTION(kmod_module_new_from_lookup);
DLSYM_FUNCTION(kmod_module_probe_insert_module);
DLSYM_FUNCTION(kmod_module_unref);
DLSYM_FUNCTION(kmod_module_unref_list);
DLSYM_FUNCTION(kmod_new);
DLSYM_FUNCTION(kmod_set_log_fn);
DLSYM_FUNCTION(kmod_unref);
DLSYM_FUNCTION(kmod_validate_resources);
int dlopen_libkmod(void) {
return dlopen_many_sym_or_warn(
&libkmod_dl,
"libkmod.so.2",
LOG_DEBUG,
DLSYM_ARG(kmod_list_next),
DLSYM_ARG(kmod_load_resources),
DLSYM_ARG(kmod_module_get_initstate),
DLSYM_ARG(kmod_module_get_module),
DLSYM_ARG(kmod_module_get_name),
DLSYM_ARG(kmod_module_new_from_lookup),
DLSYM_ARG(kmod_module_probe_insert_module),
DLSYM_ARG(kmod_module_unref),
DLSYM_ARG(kmod_module_unref_list),
DLSYM_ARG(kmod_new),
DLSYM_ARG(kmod_set_log_fn),
DLSYM_ARG(kmod_unref),
DLSYM_ARG(kmod_validate_resources));
}
static int denylist_modules(const char *p, char ***denylist) {
_cleanup_strv_free_ char **k = NULL;
int r;
@ -41,19 +79,21 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
}
int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) {
const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST;
struct kmod_list *itr;
_cleanup_(kmod_module_unref_listp) struct kmod_list *modlist = NULL;
_cleanup_(sym_kmod_module_unref_listp) struct kmod_list *modlist = NULL;
_cleanup_strv_free_ char **denylist = NULL;
bool denylist_parsed = false;
struct kmod_list *itr;
int r;
assert(ctx);
assert(module);
/* verbose==true means we should log at non-debug level if we
* fail to find or load the module. */
log_debug("Loading module: %s", module);
r = kmod_module_new_from_lookup(ctx, module, &modlist);
r = sym_kmod_module_new_from_lookup(ctx, module, &modlist);
if (r < 0)
return log_full_errno(verbose ? LOG_ERR : LOG_DEBUG, r,
"Failed to look up module alias '%s': %m", module);
@ -63,32 +103,37 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
SYNTHETIC_ERRNO(ENOENT),
"Failed to find module '%s'", module);
kmod_list_foreach(itr, modlist) {
_cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
sym_kmod_list_foreach(itr, modlist) {
_cleanup_(sym_kmod_module_unrefp) struct kmod_module *mod = NULL;
int state, err;
mod = kmod_module_get_module(itr);
state = kmod_module_get_initstate(mod);
mod = sym_kmod_module_get_module(itr);
state = sym_kmod_module_get_initstate(mod);
switch (state) {
case KMOD_MODULE_BUILTIN:
log_full(verbose ? LOG_INFO : LOG_DEBUG,
"Module '%s' is built in", kmod_module_get_name(mod));
"Module '%s' is built in", sym_kmod_module_get_name(mod));
break;
case KMOD_MODULE_LIVE:
log_debug("Module '%s' is already loaded", kmod_module_get_name(mod));
log_debug("Module '%s' is already loaded", sym_kmod_module_get_name(mod));
break;
default:
err = kmod_module_probe_insert_module(mod, probe_flags,
NULL, NULL, NULL, NULL);
err = sym_kmod_module_probe_insert_module(
mod,
KMOD_PROBE_APPLY_BLACKLIST,
/* extra_options= */ NULL,
/* run_install= */ NULL,
/* data= */ NULL,
/* print_action= */ NULL);
if (err == 0)
log_full(verbose ? LOG_INFO : LOG_DEBUG,
"Inserted module '%s'", kmod_module_get_name(mod));
"Inserted module '%s'", sym_kmod_module_get_name(mod));
else if (err == KMOD_PROBE_APPLY_BLACKLIST)
log_full(verbose ? LOG_INFO : LOG_DEBUG,
"Module '%s' is deny-listed (by kmod)", kmod_module_get_name(mod));
"Module '%s' is deny-listed (by kmod)", sym_kmod_module_get_name(mod));
else {
assert(err < 0);
@ -102,9 +147,9 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
denylist_parsed = true;
}
if (strv_contains(denylist, kmod_module_get_name(mod))) {
if (strv_contains(denylist, sym_kmod_module_get_name(mod))) {
log_full(verbose ? LOG_INFO : LOG_DEBUG,
"Module '%s' is deny-listed (by kernel)", kmod_module_get_name(mod));
"Module '%s' is deny-listed (by kernel)", sym_kmod_module_get_name(mod));
continue;
}
}
@ -115,7 +160,7 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
LOG_ERR,
err,
"Failed to insert module '%s': %m",
kmod_module_get_name(mod));
sym_kmod_module_get_name(mod));
if (!IN_SET(err, -ENODEV, -ENOENT))
r = err;
}
@ -124,3 +169,38 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
return r;
}
_printf_(6,0) static void systemd_kmod_log(
void *data,
int priority,
const char *file,
int line,
const char *fn,
const char *format,
va_list args) {
log_internalv(priority, 0, file, line, fn, format, args);
}
int module_setup_context(struct kmod_ctx **ret) {
_cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
int r;
assert(ret);
r = dlopen_libkmod();
if (r < 0)
return r;
ctx = sym_kmod_new(NULL, NULL);
if (!ctx)
return -ENOMEM;
(void) sym_kmod_load_resources(ctx);
sym_kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
*ret = TAKE_PTR(ctx);
return 0;
}
#endif

View file

@ -1,12 +1,56 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "dlfcn-util.h"
#if HAVE_KMOD
#include <libkmod.h>
#include "macro.h"
DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_ctx*, kmod_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_module*, kmod_module_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct kmod_list*, kmod_module_unref_list, NULL);
DLSYM_PROTOTYPE(kmod_list_next);
DLSYM_PROTOTYPE(kmod_load_resources);
DLSYM_PROTOTYPE(kmod_module_get_initstate);
DLSYM_PROTOTYPE(kmod_module_get_module);
DLSYM_PROTOTYPE(kmod_module_get_name);
DLSYM_PROTOTYPE(kmod_module_new_from_lookup);
DLSYM_PROTOTYPE(kmod_module_probe_insert_module);
DLSYM_PROTOTYPE(kmod_module_unref);
DLSYM_PROTOTYPE(kmod_module_unref_list);
DLSYM_PROTOTYPE(kmod_new);
DLSYM_PROTOTYPE(kmod_set_log_fn);
DLSYM_PROTOTYPE(kmod_unref);
DLSYM_PROTOTYPE(kmod_validate_resources);
int dlopen_libkmod(void);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_ctx*, sym_kmod_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct kmod_module*, sym_kmod_module_unref);
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(struct kmod_list*, sym_kmod_module_unref_list, NULL);
#define sym_kmod_list_foreach(list_entry, first_entry) \
for (list_entry = first_entry; \
list_entry != NULL; \
list_entry = sym_kmod_list_next(first_entry, list_entry))
int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose);
int module_setup_context(struct kmod_ctx **ret);
#else
struct kmod_ctx;
static inline int dlopen_libkmod(void) {
return -EOPNOTSUPP;
}
static inline int module_setup_context(struct kmod_ctx **ret) {
return -EOPNOTSUPP;
}
static inline int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose) {
return -EOPNOTSUPP;
}
#endif

View file

@ -265,7 +265,10 @@ executables += [
},
test_template + {
'sources' : files('test-dlopen-so.c'),
'dependencies' : libp11kit_cflags
'dependencies' : [
libp11kit_cflags,
libkmod_cflags,
],
},
test_template + {
# only static linking apart from libdl, to make sure that the
@ -335,7 +338,7 @@ executables += [
},
test_template + {
'sources' : files('test-netlink-manual.c'),
'dependencies' : libkmod,
'dependencies' : libkmod_cflags,
'conditions' : ['HAVE_KMOD'],
'type' : 'manual',
},

View file

@ -13,6 +13,7 @@
#include "libfido2-util.h"
#include "macro.h"
#include "main-func.h"
#include "module-util.h"
#include "password-quality-util-passwdqc.h"
#include "password-quality-util-pwquality.h"
#include "pcre2-util.h"
@ -93,6 +94,10 @@ static int run(int argc, char **argv) {
assert_se(initialize_libgcrypt(/* secmem= */ false) >= 0);
#endif
#if HAVE_KMOD
assert_se(dlopen_libkmod() >= 0);
#endif
return 0;
}

View file

@ -13,25 +13,29 @@
#include "tests.h"
static int load_module(const char *mod_name) {
_cleanup_(kmod_unrefp) struct kmod_ctx *ctx = NULL;
_cleanup_(kmod_module_unref_listp) struct kmod_list *list = NULL;
_cleanup_(sym_kmod_unrefp) struct kmod_ctx *ctx = NULL;
_cleanup_(sym_kmod_module_unref_listp) struct kmod_list *list = NULL;
struct kmod_list *l;
int r;
ctx = kmod_new(NULL, NULL);
r = dlopen_libkmod();
if (r < 0)
return log_error_errno(r, "Failed to load libkmod: %m");
ctx = sym_kmod_new(NULL, NULL);
if (!ctx)
return log_oom();
r = kmod_module_new_from_lookup(ctx, mod_name, &list);
r = sym_kmod_module_new_from_lookup(ctx, mod_name, &list);
if (r < 0)
return r;
kmod_list_foreach(l, list) {
_cleanup_(kmod_module_unrefp) struct kmod_module *mod = NULL;
sym_kmod_list_foreach(l, list) {
_cleanup_(sym_kmod_module_unrefp) struct kmod_module *mod = NULL;
mod = kmod_module_get_module(l);
mod = sym_kmod_module_get_module(l);
r = kmod_module_probe_insert_module(mod, 0, NULL, NULL, NULL, NULL);
r = sym_kmod_module_probe_insert_module(mod, 0, NULL, NULL, NULL, NULL);
if (r > 0)
r = -EINVAL;
}

View file

@ -114,7 +114,7 @@ libudevd_core = static_library(
include_directories : includes + include_directories('net'),
link_with : udev_link_with,
dependencies : [libblkid,
libkmod,
libkmod_cflags,
userspace],
build_by_default : false)

View file

@ -18,10 +18,6 @@
static struct kmod_ctx *ctx = NULL;
_printf_(6,0) static void udev_kmod_log(void *data, int priority, const char *file, int line, const char *fn, const char *format, va_list args) {
log_internalv(priority, 0, file, line, fn, format, args);
}
static int builtin_kmod(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
int r;
@ -44,7 +40,7 @@ static int builtin_kmod(UdevEvent *event, int argc, char *argv[]) {
r = sd_device_get_property_value(dev, "MODALIAS", &modalias);
if (r < 0)
return log_device_warning_errno(dev, r, "Failed to read property \"MODALIAS\".");
return log_device_warning_errno(dev, r, "Failed to read property \"MODALIAS\": %m");
(void) module_load_and_warn(ctx, modalias, /* verbose = */ false);
} else
@ -56,23 +52,28 @@ static int builtin_kmod(UdevEvent *event, int argc, char *argv[]) {
/* called at udev startup and reload */
static int builtin_kmod_init(void) {
int r;
if (ctx)
return 0;
ctx = kmod_new(NULL, NULL);
if (!ctx)
return -ENOMEM;
log_debug("Loading kernel module index.");
kmod_set_log_fn(ctx, udev_kmod_log, NULL);
kmod_load_resources(ctx);
r = module_setup_context(&ctx);
if (r < 0)
return log_error_errno(r, "Failed to initialize libkmod context: %m");
return 0;
}
/* called on udev shutdown and reload request */
static void builtin_kmod_exit(void) {
log_debug("Unload kernel module index.");
ctx = kmod_unref(ctx);
if (!ctx)
return;
ctx = sym_kmod_unref(ctx);
}
/* called every couple of seconds during event activity; 'true' if config has changed */
@ -80,7 +81,7 @@ static bool builtin_kmod_should_reload(void) {
if (!ctx)
return false;
if (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) {
if (sym_kmod_validate_resources(ctx) != KMOD_RESOURCES_OK) {
log_debug("Kernel module index needs reloading.");
return true;
}

View file

@ -1555,7 +1555,7 @@ install_missing_libraries() {
local lib path
# 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 tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1 libarchive libgcrypt; do
for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw xkbcommon p11-kit-1 libarchive libgcrypt libkmod; do
ddebug "Searching for $lib via pkg-config"
if pkg-config --exists "$lib"; then
path="$(pkg-config --variable=libdir "$lib")"