diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | libkmod/docs/libkmod-sections.txt | 2 | ||||
-rw-r--r-- | libkmod/libkmod-config.c | 212 | ||||
-rw-r--r-- | libkmod/libkmod-file.c | 209 | ||||
-rw-r--r-- | libkmod/libkmod-internal.h | 5 | ||||
-rw-r--r-- | libkmod/libkmod-module.c | 64 | ||||
-rw-r--r-- | libkmod/libkmod.h | 3 | ||||
-rw-r--r-- | libkmod/libkmod.sym | 2 | ||||
-rw-r--r-- | man/modprobe.d.5.xml | 24 | ||||
-rw-r--r-- | testsuite/module-playground/mod-fake-scsi-mod.c | 2 | ||||
-rw-r--r-- | testsuite/module-playground/mod-foo-a.c | 2 | ||||
-rw-r--r-- | testsuite/module-playground/mod-foo-b.c | 2 | ||||
-rw-r--r-- | testsuite/module-playground/mod-foo-c.c | 2 | ||||
-rw-r--r-- | tools/depmod.c | 25 | ||||
-rw-r--r-- | tools/modprobe.c | 1 |
17 files changed, 433 insertions, 133 deletions
@@ -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 = ®_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; |