summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitignore2
-rw-r--r--Makefile.am6
-rw-r--r--configure.ac3
-rw-r--r--libkmod/docs/libkmod-sections.txt2
-rw-r--r--libkmod/libkmod-config.c212
-rw-r--r--libkmod/libkmod-file.c209
-rw-r--r--libkmod/libkmod-internal.h5
-rw-r--r--libkmod/libkmod-module.c64
-rw-r--r--libkmod/libkmod.h3
-rw-r--r--libkmod/libkmod.sym2
-rw-r--r--man/modprobe.d.5.xml24
-rw-r--r--testsuite/module-playground/mod-fake-scsi-mod.c2
-rw-r--r--testsuite/module-playground/mod-foo-a.c2
-rw-r--r--testsuite/module-playground/mod-foo-b.c2
-rw-r--r--testsuite/module-playground/mod-foo-c.c2
-rw-r--r--tools/depmod.c25
-rw-r--r--tools/modprobe.c1
17 files changed, 433 insertions, 133 deletions
diff --git a/.gitignore b/.gitignore
index 29b3102..799897e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,8 @@
/libtool
/stamp-h1
/test-suite.log
+/build
+/build-*
*~
.*.swp
diff --git a/Makefile.am b/Makefile.am
index d37b56d..e2e2411 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -96,8 +96,8 @@ libkmod_libkmod_internal_la_LDFLAGS = $(AM_LDFLAGS) \
libkmod_libkmod_internal_la_DEPENDENCIES = $(libkmod_libkmod_la_DEPENDENCIES)
libkmod_libkmod_internal_la_LIBADD = $(libkmod_libkmod_la_LIBADD)
-pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libkmod/libkmod.pc tools/kmod.pc
+pkgconfig_DATA = libkmod/libkmod.pc
+noarch_pkgconfig_DATA = tools/kmod.pc
bashcompletiondir=@bashcompletiondir@
dist_bashcompletion_DATA = \
@@ -113,7 +113,7 @@ install-exec-hook:
fi
if BUILD_TOOLS
for tool in insmod lsmod rmmod depmod modprobe modinfo; do \
- $(LN_S) kmod $(DESTDIR)$(bindir)/$$tool; \
+ $(LN_S) -f kmod $(DESTDIR)$(bindir)/$$tool; \
done
endif
diff --git a/configure.ac b/configure.ac
index b651b5f..a80780e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -231,6 +231,9 @@ GTK_DOC_CHECK([1.14],[--flavour no-tmpl-flat])
], [
AM_CONDITIONAL([ENABLE_GTK_DOC], false)])
+PKG_INSTALLDIR
+PKG_NOARCH_INSTALLDIR
+
#####################################################################
# Default CFLAGS and LDFLAGS
#####################################################################
diff --git a/libkmod/docs/libkmod-sections.txt b/libkmod/docs/libkmod-sections.txt
index 33d9eec..978b064 100644
--- a/libkmod/docs/libkmod-sections.txt
+++ b/libkmod/docs/libkmod-sections.txt
@@ -37,6 +37,7 @@ kmod_config_get_remove_commands
kmod_config_get_aliases
kmod_config_get_options
kmod_config_get_softdeps
+kmod_config_get_weakdeps
kmod_config_iter_get_key
kmod_config_iter_get_value
kmod_config_iter_next
@@ -62,6 +63,7 @@ kmod_module_remove_module
kmod_module_get_module
kmod_module_get_dependencies
kmod_module_get_softdeps
+kmod_module_get_weakdeps
kmod_module_apply_filter
kmod_module_get_filtered_blacklist
kmod_module_get_install_commands
diff --git a/libkmod/libkmod-config.c b/libkmod/libkmod-config.c
index e83621b..a571b6b 100644
--- a/libkmod/libkmod-config.c
+++ b/libkmod/libkmod-config.c
@@ -58,6 +58,12 @@ struct kmod_softdep {
unsigned int n_post;
};
+struct kmod_weakdep {
+ char *name;
+ const char **weak;
+ unsigned int n_weak;
+};
+
const char *kmod_blacklist_get_modname(const struct kmod_list *l)
{
return l->data;
@@ -110,6 +116,16 @@ const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned in
return dep->post;
}
+const char *kmod_weakdep_get_name(const struct kmod_list *l) {
+ const struct kmod_weakdep *dep = l->data;
+ return dep->name;
+}
+
+const char * const *kmod_weakdep_get_weak(const struct kmod_list *l, unsigned int *count) {
+ const struct kmod_weakdep *dep = l->data;
+ *count = dep->n_weak;
+ return dep->weak;
+}
static int kmod_config_add_command(struct kmod_config *config,
const char *modname,
const char *command,
@@ -392,6 +408,112 @@ static int kmod_config_add_softdep(struct kmod_config *config,
return 0;
}
+static int kmod_config_add_weakdep(struct kmod_config *config,
+ const char *modname,
+ const char *line)
+{
+ struct kmod_list *list;
+ struct kmod_weakdep *dep;
+ const char *s, *p;
+ char *itr;
+ unsigned int n_weak = 0;
+ size_t modnamelen = strlen(modname) + 1;
+ size_t buflen = 0;
+ bool was_space = false;
+
+ DBG(config->ctx, "modname=%s\n", modname);
+
+ /* analyze and count */
+ for (p = s = line; ; s++) {
+ size_t plen;
+
+ if (*s != '\0') {
+ if (!isspace(*s)) {
+ was_space = false;
+ continue;
+ }
+
+ if (was_space) {
+ p = s + 1;
+ continue;
+ }
+ was_space = true;
+
+ if (p >= s)
+ continue;
+ }
+ plen = s - p;
+
+ if (*s != '\0' || (*s == '\0' && !was_space)) {
+ buflen += plen + 1;
+ n_weak++;
+ }
+ p = s + 1;
+ if (*s == '\0')
+ break;
+ }
+
+ DBG(config->ctx, "%u weak\n", n_weak);
+
+ dep = malloc(sizeof(struct kmod_weakdep) + modnamelen +
+ n_weak * sizeof(const char *) +
+ buflen);
+ if (dep == NULL) {
+ ERR(config->ctx, "out-of-memory modname=%s\n", modname);
+ return -ENOMEM;
+ }
+ dep->n_weak = n_weak;
+ dep->weak = (const char **)((char *)dep + sizeof(struct kmod_weakdep));
+ dep->name = (char *)(dep->weak + n_weak);
+
+ memcpy(dep->name, modname, modnamelen);
+
+ /* copy strings */
+ itr = dep->name + modnamelen;
+ n_weak = 0;
+ was_space = false;
+ for (p = s = line; ; s++) {
+ size_t plen;
+
+ if (*s != '\0') {
+ if (!isspace(*s)) {
+ was_space = false;
+ continue;
+ }
+
+ if (was_space) {
+ p = s + 1;
+ continue;
+ }
+ was_space = true;
+
+ if (p >= s)
+ continue;
+ }
+ plen = s - p;
+
+ if (*s != '\0' || (*s == '\0' && !was_space)) {
+ dep->weak[n_weak] = itr;
+ memcpy(itr, p, plen);
+ itr[plen] = '\0';
+ itr += plen + 1;
+ n_weak++;
+ }
+ p = s + 1;
+ if (*s == '\0')
+ break;
+ }
+
+ list = kmod_list_append(config->weakdeps, dep);
+ if (list == NULL) {
+ free(dep);
+ return -ENOMEM;
+ }
+ config->weakdeps = list;
+
+ return 0;
+}
+
static char *softdep_to_char(struct kmod_softdep *dep) {
const size_t sz_preprefix = sizeof("pre: ") - 1;
const size_t sz_postprefix = sizeof("post: ") - 1;
@@ -461,6 +583,44 @@ static char *softdep_to_char(struct kmod_softdep *dep) {
return s;
}
+static char *weakdep_to_char(struct kmod_weakdep *dep) {
+ size_t sz;
+ const char *start, *end;
+ char *s, *itr;
+
+ /*
+ * Rely on the fact that dep->weak[] and are strv's that point to a
+ * contiguous buffer
+ */
+ if (dep->n_weak > 0) {
+ start = dep->weak[0];
+ end = dep->weak[dep->n_weak - 1]
+ + strlen(dep->weak[dep->n_weak - 1]);
+ sz = end - start;
+ } else
+ sz = 0;
+
+ itr = s = malloc(sz);
+ if (s == NULL)
+ return NULL;
+
+ if (sz) {
+ char *p;
+
+ /* include last '\0' */
+ memcpy(itr, dep->weak[0], sz + 1);
+ for (p = itr; p < itr + sz; p++) {
+ if (*p == '\0')
+ *p = ' ';
+ }
+ itr = p;
+ }
+
+ *itr = '\0';
+
+ return s;
+}
+
static void kmod_config_free_softdep(struct kmod_config *config,
struct kmod_list *l)
{
@@ -468,6 +628,13 @@ static void kmod_config_free_softdep(struct kmod_config *config,
config->softdeps = kmod_list_remove(l);
}
+static void kmod_config_free_weakdep(struct kmod_config *config,
+ struct kmod_list *l)
+{
+ free(l->data);
+ config->weakdeps = kmod_list_remove(l);
+}
+
static void kcmdline_parse_result(struct kmod_config *config, char *modname,
char *param, char *value)
{
@@ -703,6 +870,14 @@ static int kmod_config_parse(struct kmod_config *config, int fd,
goto syntax_error;
kmod_config_add_softdep(config, modname, softdeps);
+ } else if (streq(cmd, "weakdep")) {
+ char *modname = strtok_r(NULL, "\t ", &saveptr);
+ char *weakdeps = strtok_r(NULL, "\0", &saveptr);
+
+ if (underscores(modname) < 0 || weakdeps == NULL)
+ goto syntax_error;
+
+ kmod_config_add_weakdep(config, modname, weakdeps);
} else if (streq(cmd, "include")
|| streq(cmd, "config")) {
ERR(ctx, "%s: command %s is deprecated and not parsed anymore\n",
@@ -746,6 +921,9 @@ void kmod_config_free(struct kmod_config *config)
while (config->softdeps)
kmod_config_free_softdep(config, config->softdeps);
+ while (config->weakdeps)
+ kmod_config_free_weakdep(config, config->weakdeps);
+
for (; config->paths != NULL;
config->paths = kmod_list_remove(config->paths))
free(config->paths->data);
@@ -889,6 +1067,7 @@ int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config,
size_t i;
conf_files_insert_sorted(ctx, &list, kmod_get_dirname(ctx), "modules.softdep");
+ conf_files_insert_sorted(ctx, &list, kmod_get_dirname(ctx), "modules.weakdep");
for (i = 0; config_paths[i] != NULL; i++) {
const char *path = config_paths[i];
@@ -973,6 +1152,7 @@ enum config_type {
CONFIG_TYPE_ALIAS,
CONFIG_TYPE_OPTION,
CONFIG_TYPE_SOFTDEP,
+ CONFIG_TYPE_WEAKDEP,
};
struct kmod_config_iter {
@@ -991,6 +1171,12 @@ static const char *softdep_get_plain_softdep(const struct kmod_list *l)
return s;
}
+static const char *weakdep_get_plain_weakdep(const struct kmod_list *l)
+{
+ char *s = weakdep_to_char(l->data);
+ return s;
+}
+
static struct kmod_config_iter *kmod_config_iter_new(const struct kmod_ctx* ctx,
enum config_type type)
{
@@ -1033,6 +1219,12 @@ static struct kmod_config_iter *kmod_config_iter_new(const struct kmod_ctx* ctx,
iter->get_value = softdep_get_plain_softdep;
iter->intermediate = true;
break;
+ case CONFIG_TYPE_WEAKDEP:
+ iter->list = config->weakdeps;
+ iter->get_key = kmod_weakdep_get_name;
+ iter->get_value = weakdep_get_plain_weakdep;
+ iter->intermediate = true;
+ break;
}
return iter;
@@ -1164,6 +1356,26 @@ KMOD_EXPORT struct kmod_config_iter *kmod_config_get_softdeps(const struct kmod_
}
/**
+ * kmod_config_get_weakdeps:
+ * @ctx: kmod library context
+ *
+ * Retrieve an iterator to deal with the weakdeps maintained inside the
+ * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value() and
+ * kmod_config_iter_next(). At least one call to kmod_config_iter_next() must
+ * be made to initialize the iterator and check if it's valid.
+ *
+ * Returns: a new iterator over the weakdeps or NULL on failure. Free it with
+ * kmod_config_iter_free_iter().
+ */
+KMOD_EXPORT struct kmod_config_iter *kmod_config_get_weakdeps(const struct kmod_ctx *ctx)
+{
+ if (ctx == NULL)
+ return NULL;;
+
+ return kmod_config_iter_new(ctx, CONFIG_TYPE_WEAKDEP);
+}
+
+/**
* kmod_config_iter_get_key:
* @iter: iterator over a certain configuration
*
diff --git a/libkmod/libkmod-file.c b/libkmod/libkmod-file.c
index b138e7e..8baf12d 100644
--- a/libkmod/libkmod-file.c
+++ b/libkmod/libkmod-file.c
@@ -41,27 +41,12 @@
#include "libkmod.h"
#include "libkmod-internal.h"
-struct kmod_file;
-struct file_ops {
- int (*load)(struct kmod_file *file);
- void (*unload)(struct kmod_file *file);
-};
-
struct kmod_file {
-#ifdef ENABLE_ZSTD
- bool zstd_used;
-#endif
-#ifdef ENABLE_XZ
- bool xz_used;
-#endif
-#ifdef ENABLE_ZLIB
- gzFile gzf;
-#endif
int fd;
enum kmod_file_compression_type compression;
off_t size;
void *memory;
- const struct file_ops *ops;
+ int (*load)(struct kmod_file *file);
const struct kmod_ctx *ctx;
struct kmod_elf *elf;
};
@@ -179,7 +164,6 @@ static int load_zstd(struct kmod_file *file)
ZSTD_freeDStream(dstr);
free((void *)zst_inb.src);
- file->zstd_used = true;
file->memory = zst_outb.dst;
file->size = zst_outb.pos;
return 0;
@@ -190,16 +174,14 @@ out:
free((void *)zst_outb.dst);
return ret;
}
-
-static void unload_zstd(struct kmod_file *file)
+#else
+static int load_zstd(struct kmod_file *file)
{
- if (!file->zstd_used)
- return;
- free(file->memory);
+ return -ENOSYS;
}
+#endif
static const char magic_zstd[] = {0x28, 0xB5, 0x2F, 0xFD};
-#endif
#ifdef ENABLE_XZ
static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
@@ -272,7 +254,6 @@ static int xz_uncompress(lzma_stream *strm, struct kmod_file *file)
goto out;
}
}
- file->xz_used = true;
file->memory = p;
file->size = total;
return 0;
@@ -299,16 +280,14 @@ static int load_xz(struct kmod_file *file)
lzma_end(&strm);
return ret;
}
-
-static void unload_xz(struct kmod_file *file)
+#else
+static int load_xz(struct kmod_file *file)
{
- if (!file->xz_used)
- return;
- free(file->memory);
+ return -ENOSYS;
}
+#endif
static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
-#endif
#ifdef ENABLE_ZLIB
#define READ_STEP (4 * 1024 * 1024)
@@ -317,12 +296,19 @@ static int load_zlib(struct kmod_file *file)
int err = 0;
off_t did = 0, total = 0;
_cleanup_free_ unsigned char *p = NULL;
+ gzFile gzf;
+ int gzfd;
errno = 0;
- file->gzf = gzdopen(file->fd, "rb");
- if (file->gzf == NULL)
+ gzfd = fcntl(file->fd, F_DUPFD_CLOEXEC, 3);
+ if (gzfd < 0)
return -errno;
- file->fd = -1; /* now owned by gzf due gzdopen() */
+
+ gzf = gzdopen(gzfd, "rb"); /* takes ownership of the fd */
+ if (gzf == NULL) {
+ close(gzfd);
+ return -errno;
+ }
for (;;) {
int r;
@@ -337,12 +323,12 @@ static int load_zlib(struct kmod_file *file)
p = tmp;
}
- r = gzread(file->gzf, p + did, total - did);
+ r = gzread(gzf, p + did, total - did);
if (r == 0)
break;
else if (r < 0) {
int gzerr;
- const char *gz_errmsg = gzerror(file->gzf, &gzerr);
+ const char *gz_errmsg = gzerror(gzf, &gzerr);
ERR(file->ctx, "gzip: %s\n", gz_errmsg);
@@ -356,41 +342,21 @@ static int load_zlib(struct kmod_file *file)
file->memory = p;
file->size = did;
p = NULL;
+ gzclose(gzf);
return 0;
error:
- gzclose(file->gzf);
+ gzclose(gzf); /* closes the gzfd */
return err;
}
-
-static void unload_zlib(struct kmod_file *file)
+#else
+static int load_zlib(struct kmod_file *file)
{
- if (file->gzf == NULL)
- return;
- free(file->memory);
- gzclose(file->gzf); /* closes file->fd */
+ return -ENOSYS;
}
-
-static const char magic_zlib[] = {0x1f, 0x8b};
#endif
-static const struct comp_type {
- size_t magic_size;
- enum kmod_file_compression_type compression;
- const char *magic_bytes;
- const struct file_ops ops;
-} comp_types[] = {
-#ifdef ENABLE_ZSTD
- {sizeof(magic_zstd), KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, {load_zstd, unload_zstd}},
-#endif
-#ifdef ENABLE_XZ
- {sizeof(magic_xz), KMOD_FILE_COMPRESSION_XZ, magic_xz, {load_xz, unload_xz}},
-#endif
-#ifdef ENABLE_ZLIB
- {sizeof(magic_zlib), KMOD_FILE_COMPRESSION_ZLIB, magic_zlib, {load_zlib, unload_zlib}},
-#endif
- {0, KMOD_FILE_COMPRESSION_NONE, NULL, {NULL, NULL}}
-};
+static const char magic_zlib[] = {0x1f, 0x8b};
static int load_reg(struct kmod_file *file)
{
@@ -402,27 +368,39 @@ static int load_reg(struct kmod_file *file)
file->size = st.st_size;
file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE,
file->fd, 0);
- if (file->memory == MAP_FAILED)
+ if (file->memory == MAP_FAILED) {
+ file->memory = NULL;
return -errno;
+ }
return 0;
}
-static void unload_reg(struct kmod_file *file)
-{
- munmap(file->memory, file->size);
-}
-
-static const struct file_ops reg_ops = {
- load_reg, unload_reg
+static const struct comp_type {
+ size_t magic_size;
+ enum kmod_file_compression_type compression;
+ const char *magic_bytes;
+ int (*load)(struct kmod_file *file);
+} comp_types[] = {
+ {sizeof(magic_zstd), KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, load_zstd},
+ {sizeof(magic_xz), KMOD_FILE_COMPRESSION_XZ, magic_xz, load_xz},
+ {sizeof(magic_zlib), KMOD_FILE_COMPRESSION_ZLIB, magic_zlib, load_zlib},
+ {0, KMOD_FILE_COMPRESSION_NONE, NULL, load_reg}
};
struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
{
+ int err;
+
if (file->elf)
return file->elf;
- kmod_file_load_contents(file);
+ err = kmod_file_load_contents(file);
+ if (err) {
+ errno = err;
+ return NULL;
+ }
+
file->elf = kmod_elf_new(file->memory, file->size);
return file->elf;
}
@@ -430,81 +408,63 @@ struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
const char *filename)
{
- struct kmod_file *file = calloc(1, sizeof(struct kmod_file));
- const struct comp_type *itr;
- size_t magic_size_max = 0;
- int err = 0;
+ struct kmod_file *file;
+ char buf[7];
+ ssize_t sz;
+ assert_cc(sizeof(magic_zstd) < sizeof(buf));
+ assert_cc(sizeof(magic_xz) < sizeof(buf));
+ assert_cc(sizeof(magic_zlib) < sizeof(buf));
+
+ file = calloc(1, sizeof(struct kmod_file));
if (file == NULL)
return NULL;
file->fd = open(filename, O_RDONLY|O_CLOEXEC);
if (file->fd < 0) {
- err = -errno;
- goto error;
+ free(file);
+ return NULL;
}
- for (itr = comp_types; itr->ops.load != NULL; itr++) {
- if (magic_size_max < itr->magic_size)
- magic_size_max = itr->magic_size;
- }
+ sz = read_str_safe(file->fd, buf, sizeof(buf));
+ lseek(file->fd, 0, SEEK_SET);
+ if (sz != (sizeof(buf) - 1)) {
+ if (sz < 0)
+ errno = -sz;
+ else
+ errno = EINVAL;
- if (magic_size_max > 0) {
- char *buf = alloca(magic_size_max + 1);
- ssize_t sz;
+ close(file->fd);
+ free(file);
+ return NULL;
+ }
- if (buf == NULL) {
- err = -errno;
- goto error;
- }
- sz = read_str_safe(file->fd, buf, magic_size_max + 1);
- lseek(file->fd, 0, SEEK_SET);
- if (sz != (ssize_t)magic_size_max) {
- if (sz < 0)
- err = sz;
- else
- err = -EINVAL;
- goto error;
- }
+ for (unsigned int i = 0; i < ARRAY_SIZE(comp_types); i++) {
+ const struct comp_type *itr = &comp_types[i];
- for (itr = comp_types; itr->ops.load != NULL; itr++) {
- if (memcmp(buf, itr->magic_bytes, itr->magic_size) == 0) {
- file->ops = &itr->ops;
- file->compression = itr->compression;
- break;
- }
+ file->load = itr->load;
+ file->compression = itr->compression;
+ if (itr->magic_size &&
+ memcmp(buf, itr->magic_bytes, itr->magic_size) == 0) {
+ break;
}
}
- if (file->ops == NULL) {
- file->ops = &reg_ops;
- file->compression = KMOD_FILE_COMPRESSION_NONE;
- }
-
file->ctx = ctx;
-error:
- if (err < 0) {
- if (file->fd >= 0)
- close(file->fd);
- free(file);
- errno = -err;
- return NULL;
- }
-
return file;
}
/*
* Callers should just check file->memory got updated
*/
-void kmod_file_load_contents(struct kmod_file *file)
+int kmod_file_load_contents(struct kmod_file *file)
{
if (file->memory)
- return;
+ return 0;
/* The load functions already log possible errors. */
- file->ops->load(file);
+ return file->load(file);
}
void *kmod_file_get_contents(const struct kmod_file *file)
@@ -532,10 +492,13 @@ void kmod_file_unref(struct kmod_file *file)
if (file->elf)
kmod_elf_unref(file->elf);
- if (file->memory)
- file->ops->unload(file);
+ if (file->compression == KMOD_FILE_COMPRESSION_NONE) {
+ if (file->memory)
+ munmap(file->memory, file->size);
+ } else {
+ free(file->memory);
+ }
- if (file->fd >= 0)
- close(file->fd);
+ close(file->fd);
free(file);
}
diff --git a/libkmod/libkmod-internal.h b/libkmod/libkmod-internal.h
index 26a7e28..4e1cc20 100644
--- a/libkmod/libkmod-internal.h
+++ b/libkmod/libkmod-internal.h
@@ -128,6 +128,7 @@ struct kmod_config {
struct kmod_list *remove_commands;
struct kmod_list *install_commands;
struct kmod_list *softdeps;
+ struct kmod_list *weakdeps;
struct kmod_list *paths;
};
@@ -146,6 +147,8 @@ const char *kmod_softdep_get_name(const struct kmod_list *l) __attribute__((nonn
const char * const *kmod_softdep_get_pre(const struct kmod_list *l, unsigned int *count) __attribute__((nonnull(1, 2)));
const char * const *kmod_softdep_get_post(const struct kmod_list *l, unsigned int *count);
+const char *kmod_weakdep_get_name(const struct kmod_list *l) __attribute__((nonnull(1)));
+const char * const *kmod_weakdep_get_weak(const struct kmod_list *l, unsigned int *count) __attribute__((nonnull(1, 2)));
/* libkmod-module.c */
int kmod_module_new_from_alias(struct kmod_ctx *ctx, const char *alias, const char *name, struct kmod_module **mod);
@@ -160,7 +163,7 @@ bool kmod_module_is_builtin(struct kmod_module *mod) __attribute__((nonnull(1)))
/* libkmod-file.c */
struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx, const char *filename) _must_check_ __attribute__((nonnull(1,2)));
struct kmod_elf *kmod_file_get_elf(struct kmod_file *file) __attribute__((nonnull(1)));
-void kmod_file_load_contents(struct kmod_file *file) __attribute__((nonnull(1)));
+int kmod_file_load_contents(struct kmod_file *file) __attribute__((nonnull(1)));
void *kmod_file_get_contents(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
off_t kmod_file_get_size(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
enum kmod_file_compression_type kmod_file_get_compression(const struct kmod_file *file) _must_check_ __attribute__((nonnull(1)));
diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c
index 585da41..5c26e03 100644
--- a/libkmod/libkmod-module.c
+++ b/libkmod/libkmod-module.c
@@ -903,8 +903,6 @@ static int do_init_module(struct kmod_module *mod, unsigned int flags,
off_t size;
int err;
- kmod_file_load_contents(mod->file);
-
if (flags & (KMOD_INSERT_FORCE_VERMAGIC | KMOD_INSERT_FORCE_MODVERSION)) {
elf = kmod_file_get_elf(mod->file);
if (elf == NULL) {
@@ -926,6 +924,10 @@ static int do_init_module(struct kmod_module *mod, unsigned int flags,
mem = kmod_elf_get_memory(elf);
} else {
+ err = kmod_file_load_contents(mod->file);
+ if (err)
+ return err;
+
mem = kmod_file_get_contents(mod->file);
}
size = kmod_file_get_size(mod->file);
@@ -1589,7 +1591,7 @@ void kmod_module_set_install_commands(struct kmod_module *mod, const char *cmd)
mod->install_commands = cmd;
}
-static struct kmod_list *lookup_softdep(struct kmod_ctx *ctx, const char * const * array, unsigned int count)
+static struct kmod_list *lookup_dep(struct kmod_ctx *ctx, const char * const * array, unsigned int count)
{
struct kmod_list *ret = NULL;
unsigned i;
@@ -1601,7 +1603,7 @@ static struct kmod_list *lookup_softdep(struct kmod_ctx *ctx, const char * const
err = kmod_module_new_from_lookup(ctx, depname, &lst);
if (err < 0) {
- ERR(ctx, "failed to lookup soft dependency '%s', continuing anyway.\n", depname);
+ ERR(ctx, "failed to lookup dependency '%s', continuing anyway.\n", depname);
continue;
} else if (lst != NULL)
ret = kmod_list_append_list(ret, lst);
@@ -1650,9 +1652,59 @@ KMOD_EXPORT int kmod_module_get_softdeps(const struct kmod_module *mod,
continue;
array = kmod_softdep_get_pre(l, &count);
- *pre = lookup_softdep(mod->ctx, array, count);
+ *pre = lookup_dep(mod->ctx, array, count);
array = kmod_softdep_get_post(l, &count);
- *post = lookup_softdep(mod->ctx, array, count);
+ *post = lookup_dep(mod->ctx, array, count);
+
+ /*
+ * find only the first command, as modprobe from
+ * module-init-tools does
+ */
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * kmod_module_get_weakdeps:
+ * @mod: kmod module
+ * @weak: where to save the list of weak dependencies.
+ *
+ * Get weak dependencies for this kmod module. Weak dependencies come
+ * from configuration file and are not cached in @mod because it may include
+ * dependency cycles that would make we leak kmod_module. Any call
+ * to this function will search for this module in configuration, allocate a
+ * list and return the result.
+ *
+ * @weak is newly created list of kmod_module and
+ * should be unreferenced with kmod_module_unref_list().
+ *
+ * Returns: 0 on success or < 0 otherwise.
+ */
+KMOD_EXPORT int kmod_module_get_weakdeps(const struct kmod_module *mod,
+ struct kmod_list **weak)
+{
+ const struct kmod_list *l;
+ const struct kmod_config *config;
+
+ if (mod == NULL || weak == NULL)
+ return -ENOENT;
+
+ assert(*weak == NULL);
+
+ config = kmod_get_config(mod->ctx);
+
+ kmod_list_foreach(l, config->weakdeps) {
+ const char *modname = kmod_weakdep_get_name(l);
+ const char * const *array;
+ unsigned count;
+
+ if (fnmatch(modname, mod->name, 0) != 0)
+ continue;
+
+ array = kmod_weakdep_get_weak(l, &count);
+ *weak = lookup_dep(mod->ctx, array, count);
/*
* find only the first command, as modprobe from
diff --git a/libkmod/libkmod.h b/libkmod/libkmod.h
index 7251aa7..fce66d1 100644
--- a/libkmod/libkmod.h
+++ b/libkmod/libkmod.h
@@ -112,6 +112,7 @@ struct kmod_config_iter *kmod_config_get_remove_commands(const struct kmod_ctx *
struct kmod_config_iter *kmod_config_get_aliases(const struct kmod_ctx *ctx);
struct kmod_config_iter *kmod_config_get_options(const struct kmod_ctx *ctx);
struct kmod_config_iter *kmod_config_get_softdeps(const struct kmod_ctx *ctx);
+struct kmod_config_iter *kmod_config_get_weakdeps(const struct kmod_ctx *ctx);
const char *kmod_config_iter_get_key(const struct kmod_config_iter *iter);
const char *kmod_config_iter_get_value(const struct kmod_config_iter *iter);
bool kmod_config_iter_next(struct kmod_config_iter *iter);
@@ -196,6 +197,8 @@ const char *kmod_module_get_remove_commands(const struct kmod_module *mod);
struct kmod_list *kmod_module_get_dependencies(const struct kmod_module *mod);
int kmod_module_get_softdeps(const struct kmod_module *mod,
struct kmod_list **pre, struct kmod_list **post);
+int kmod_module_get_weakdeps(const struct kmod_module *mod,
+ struct kmod_list **weak);
int kmod_module_get_filtered_blacklist(const struct kmod_ctx *ctx,
const struct kmod_list *input,
struct kmod_list **output) __attribute__ ((deprecated));
diff --git a/libkmod/libkmod.sym b/libkmod/libkmod.sym
index 0c04fda..0d6d338 100644
--- a/libkmod/libkmod.sym
+++ b/libkmod/libkmod.sym
@@ -21,6 +21,7 @@ global:
kmod_config_get_aliases;
kmod_config_get_options;
kmod_config_get_softdeps;
+ kmod_config_get_weakdeps;
kmod_config_iter_get_key;
kmod_config_iter_get_value;
kmod_config_iter_next;
@@ -42,6 +43,7 @@ global:
kmod_module_get_dependencies;
kmod_module_get_softdeps;
+ kmod_module_get_weakdeps;
kmod_module_get_filtered_blacklist;
kmod_module_get_name;
diff --git a/man/modprobe.d.5.xml b/man/modprobe.d.5.xml
index 2bf6537..cc90da6 100644
--- a/man/modprobe.d.5.xml
+++ b/man/modprobe.d.5.xml
@@ -212,6 +212,30 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>weakdep <replaceable>modulename</replaceable> <replaceable>modules...</replaceable>
+ </term>
+ <listitem>
+ <para>
+ The <command>weakdep</command> command allows you to specify weak module
+ dependencies. Those are similar to pre softdep, with the
+ difference that userspace doesn't attempt to load that
+ dependency before the specified module. Instead the kernel
+ may request one or multiple of them during module probe,
+ depending on the hardware it's binding to. The purpose of
+ weak module is to allow a driver to specify that a certain
+ dependency may be needed, so it should be present in the
+ filesystem (e.g. in initramfs) when that module is probed.
+ </para>
+ <para>
+ Example: Assume "weakdep c a b". A program creating an
+ initramfs knows it should add a, b, and c to the filesystem
+ since a and b may be required/desired at runtime. When c is
+ loaded and is being probed, it may issue calls to
+ request_module() causing a or b to also be loaded.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<refsect1><title>COMPATIBILITY</title>
diff --git a/testsuite/module-playground/mod-fake-scsi-mod.c b/testsuite/module-playground/mod-fake-scsi-mod.c
index 916a04d..f335b6f 100644
--- a/testsuite/module-playground/mod-fake-scsi-mod.c
+++ b/testsuite/module-playground/mod-fake-scsi-mod.c
@@ -1,6 +1,8 @@
#include <linux/init.h>
#include <linux/module.h>
+void dummy_export(void);
+
static int __init test_module_init(void)
{
return 0;
diff --git a/testsuite/module-playground/mod-foo-a.c b/testsuite/module-playground/mod-foo-a.c
index bc65f66..4eeeb0b 100644
--- a/testsuite/module-playground/mod-foo-a.c
+++ b/testsuite/module-playground/mod-foo-a.c
@@ -4,6 +4,8 @@
#include <linux/module.h>
#include <linux/printk.h>
+void print_fooA(void);
+
static int __init foo_init(void)
{
return 0;
diff --git a/testsuite/module-playground/mod-foo-b.c b/testsuite/module-playground/mod-foo-b.c
index 09079f6..0c92b1b 100644
--- a/testsuite/module-playground/mod-foo-b.c
+++ b/testsuite/module-playground/mod-foo-b.c
@@ -4,6 +4,8 @@
#include <linux/module.h>
#include <linux/printk.h>
+void print_fooB(void);
+
static int __init foo_init(void)
{
return 0;
diff --git a/testsuite/module-playground/mod-foo-c.c b/testsuite/module-playground/mod-foo-c.c
index 3afd35d..a8917c8 100644
--- a/testsuite/module-playground/mod-foo-c.c
+++ b/testsuite/module-playground/mod-foo-c.c
@@ -4,6 +4,8 @@
#include <linux/module.h>
#include <linux/printk.h>
+void print_fooC(void);
+
static int __init foo_init(void)
{
return 0;
diff --git a/tools/depmod.c b/tools/depmod.c
index 43fc354..06618fa 100644
--- a/tools/depmod.c
+++ b/tools/depmod.c
@@ -2296,6 +2296,30 @@ static int output_softdeps(struct depmod *depmod, FILE *out)
return 0;
}
+static int output_weakdeps(struct depmod *depmod, FILE *out)
+{
+ size_t i;
+
+ fputs("# Weak dependencies extracted from modules themselves.\n", out);
+
+ for (i = 0; i < depmod->modules.count; i++) {
+ const struct mod *mod = depmod->modules.array[i];
+ struct kmod_list *l;
+
+ kmod_list_foreach(l, mod->info_list) {
+ const char *key = kmod_module_info_get_key(l);
+ const char *value = kmod_module_info_get_value(l);
+
+ if (!streq(key, "weakdep"))
+ continue;
+
+ fprintf(out, "weakdep %s %s\n", mod->modname, value);
+ }
+ }
+
+ return 0;
+}
+
static int output_symbols(struct depmod *depmod, FILE *out)
{
struct hash_iter iter;
@@ -2574,6 +2598,7 @@ static int depmod_output(struct depmod *depmod, FILE *out)
{ "modules.alias", output_aliases },
{ "modules.alias.bin", output_aliases_bin },
{ "modules.softdep", output_softdeps },
+ { "modules.weakdep", output_weakdeps },
{ "modules.symbols", output_symbols },
{ "modules.symbols.bin", output_symbols_bin },
{ "modules.builtin.bin", output_builtin_bin },
diff --git a/tools/modprobe.c b/tools/modprobe.c
index 5306bef..4328da6 100644
--- a/tools/modprobe.c
+++ b/tools/modprobe.c
@@ -182,6 +182,7 @@ static int show_config(struct kmod_ctx *ctx)
{ "alias", kmod_config_get_aliases },
{ "options", kmod_config_get_options },
{ "softdep", kmod_config_get_softdeps },
+ { "weakdep", kmod_config_get_weakdeps },
};
size_t i;