diff options
author | Elliott Hughes <enh@google.com> | 2022-10-04 04:00:15 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-10-04 04:00:15 +0000 |
commit | 3a9039f4c7102de17c2be5b9ba5bf09b08aa8dd9 (patch) | |
tree | 3f7f12667a3d263d8abcfd93248790afa16cf241 | |
parent | b748b53013964743132f5296d7f21377af939383 (diff) | |
parent | adcf6b87fbe2fd45aac8f24cb174f9b7a499be04 (diff) | |
download | kmod-3a9039f4c7102de17c2be5b9ba5bf09b08aa8dd9.tar.gz |
Upgrade kmod to v30 am: 65d4b95025 am: d9c006ff3c am: 3eb265e5e7 am: dfd13c6f51 am: adcf6b87fbHEADandroid-wear-14.0.0-gpl_r1android-u-rb-dp-10-gplandroid-u-qpr3-beta-2-gplandroid-u-qpr3-beta-1-gplandroid-u-qpr2-beta-3-gplandroid-u-qpr2-beta-2-gplandroid-u-qpr2-beta-1-gplandroid-u-qpr1-beta-2.2-gplandroid-u-qpr1-beta-1-gplandroid-15-dp-2-gplandroid-15-beta-2-gplandroid-14.0.0_r51android-14.0.0_r50android-14.0.0_r45android-14.0.0_r44android-14.0.0_r43android-14.0.0_r42android-14.0.0_r41android-14.0.0_r40android-14.0.0_r39android-14.0.0_r38android-14.0.0_r37android-14.0.0_r36android-14.0.0_r35android-14.0.0_r34android-14.0.0_r33android-14.0.0_r32android-14.0.0_r31android-14.0.0_r30android-14.0.0_r29android-14.0.0_r27android-14.0.0_r26android-14.0.0_r25android-14.0.0_r24android-14.0.0_r23android-14.0.0_r22android-14.0.0_r21android-14.0.0_r20android-14.0.0_r19android-14.0.0_r18android-14.0.0_r17android-14.0.0_r16aml_rkp_341510000aml_rkp_341311000aml_rkp_341114000aml_rkp_341015010aml_rkp_341012000aml_hef_341717050aml_hef_341613000aml_hef_341512030aml_hef_341415040aml_hef_341311010aml_hef_341114030aml_cfg_341510000mastermainandroid14-qpr3-releaseandroid14-qpr2-s5-releaseandroid14-qpr2-s4-releaseandroid14-qpr2-s3-releaseandroid14-qpr2-s2-releaseandroid14-qpr2-s1-releaseandroid14-qpr2-releaseandroid14-qpr1-s2-releaseandroid14-qpr1-releaseandroid14-mainline-healthfitness-releaseandroid14-devandroid14-d2-s5-releaseandroid14-d2-s4-releaseandroid14-d2-s3-releaseandroid14-d2-s2-releaseandroid14-d2-s1-releaseandroid14-d2-releaseandroid-u-rb-dp-10-gpl
Original change: https://android-review.googlesource.com/c/platform/external/kmod/+/2240916
Change-Id: Iea563ec646f9fbfb49440d3b749425a4f4f346c4
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
42 files changed, 1113 insertions, 453 deletions
@@ -3,6 +3,8 @@ *.gcno /*.tar.xz /*.md5sum +/*.mbx +/*.cover .deps/ .libs/ /Makefile @@ -18,6 +20,7 @@ /configure /cov-int /coverage +/gtk-doc.make /kmod-*.tar.* /libtool /stamp-h1 @@ -5,11 +5,11 @@ third_party { type: GIT value: "https://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git" } - version: "v28" + version: "v30" license_type: RESTRICTED last_upgrade_date { - year: 2021 - month: 1 - day: 7 + year: 2022 + month: 10 + day: 3 } } diff --git a/Makefile.am b/Makefile.am index acde92b..0e48770 100644 --- a/Makefile.am +++ b/Makefile.am @@ -57,9 +57,9 @@ SED_PROCESS = \ # increment age. # 6. If any interfaces have been removed or changed since the last public # release, then set age to 0. -LIBKMOD_CURRENT=5 -LIBKMOD_REVISION=6 -LIBKMOD_AGE=3 +LIBKMOD_CURRENT=6 +LIBKMOD_REVISION=0 +LIBKMOD_AGE=4 noinst_LTLIBRARIES = shared/libshared.la shared_libshared_la_SOURCES = \ @@ -249,7 +249,7 @@ CREATE_ROOTFS = $(AM_V_GEN) ( $(RM) -rf $(ROOTFS) && mkdir -p $(dir $(ROOTFS)) & find $(ROOTFS) -type d -exec chmod +w {} \; && \ find $(ROOTFS) -type f -name .gitignore -exec rm -f {} \; && \ $(top_srcdir)/testsuite/populate-modules.sh \ - $(MODULE_PLAYGROUND) $(ROOTFS) ) && \ + $(MODULE_PLAYGROUND) $(ROOTFS) $(top_builddir)/config.h ) && \ touch testsuite/stamp-rootfs build-module-playground: @@ -280,13 +280,7 @@ TESTSUITE_OVERRIDE_LIBS = \ TESTSUITE_OVERRIDE_LIBS_LDFLAGS = \ avoid-version -module -shared -export-dynamic -rpath /nowhere -ldl -check-sysconfdir: - $(AM_V_at)if test "$(sysconfdir)" != "/etc" -a "$(sysconfdir)" != "/etc/"; then \ - echo "warning: Some tests will fail without --sysconfdir=/etc" >&2; \ - fi -.PHONY: check-sysconfdir - -check-am: rootfs check-sysconfdir +check-am: rootfs EXTRA_DIST += \ @@ -341,6 +335,10 @@ TESTSUITE_LDADD = \ testsuite/libtestsuite.la libkmod/libkmod-internal.la \ shared/libshared.la +if KMOD_SYSCONFDIR_NOT_ETC +TESTSUITE_CPPFLAGS += -DKMOD_SYSCONFDIR_NOT_ETC +endif + check_LTLIBRARIES += testsuite/libtestsuite.la testsuite_libtestsuite_la_SOURCES = \ testsuite/testsuite.c testsuite/testsuite.h @@ -1,3 +1,129 @@ +kmod 30 +======= + +- Improvements + - Stop adding duplicate information on modules.builtin.alias.bin, just use + the modules.builtin.bin index + + - Speedup depmod, particularly under qemu with emulated arch, by + avoiding a lot of open/read/close of modules.alias.bin. On an + emulated ARM rootfs, depmod with only 2 modules was taking ~32s + vs ~0.07s now. + + - Add kmod_module_new_from_name_lookup() which allows doing a lookup by + module name, without considering the aliases. Other than that search + order is similar to kmod_module_new_from_lookup(). + + - modinfo learned the --modname option to explicitely show information + about the module, even if there is an alias with the same name. This + allows showing information about e.g. kernel/lib/crc32.ko, even if + kernel also exports a crc32 alias in modules.alias: + + alias crc32 crc32_pclmul + alias crc32 crc32_generic + + Same behavior will be used to other modules and to aliases provided + by user/distro. + + - depmod.conf learned a new "excludedir" directive so distro/user can + configure more directories to be excluded from its search, besides + the hardcoded values "build" and "source". + + - Better group modprobe options on help output under "Management, Query and General". + + - modprobe learned a --wait <MSEC> option to be used together with -r + when removing a module. This allows modprobe to keep trying the + removal if it fails because the module is still in use. An exponential backoff + time is used for further retries. + + The wait behavior provided by the kernel when not passing O_NONBLOCK + to delete_module() was removed in v3.13 due to not be used and the + consequences of having to support it in the kernel. However there may + be some users, particularly on testsuites for individual susbsystems, that + would want that. So provide a userspace implementation inside modprobe for + such users. "rmmod" doesn't have a --wait as it remains a bare minimal over + the API provided by the kernel. In future the --wait behavior can be added + to libkmod for testsuites not exec'ing modprobe for module removal. + + - kmod_module_remove_module() learned a new flag to silence output when + caller wants to handle them - this is particularly important for the + --wait flag to modprobe, as it's not desired to keep seeing error messages + while waiting for the module to be unused. + + - Add SM3 hash algo support to modinfo output, as already available in the kernel. + +- Bug Fixes + - Fix modinfo output when showing information for a .ko module when running + on a kernel that has that module as builtin. + + - Fix kmod_module_new_from_lookup() returning > 0 rather than 0 + when it matches an alias. + + - Fix modinfo segfault when module doesn't exist. + + - Add missing function in the html documentation: kmod_get_dirname(). + + - Fix modprobe incorrectly handling number of arguments when prepending values from + MODPROBE_OPTIONS environment variable. + + - Fix modprobe -r --remove-dependencies and since "dependencies" was a + misnomer, add the preferred argument option: "--remove-holders". This + is the same name used by the kernel. It allows users to also remove + other modules holding the one that is being removed. + + - Fix off-by-one in max module name length in depmod. + +- Infra/internal + - Start some changes in the out-of-tree test modules in kmod so they are useful + for being really inserted in the kernel rather than relying on kmod's mock + interface. This helps manual testing and may be used to exercise to test + changes in the kernel. + +kmod 29 +======= + +- Improvements + - Add support to use /usr/local as a place for configuration files. This makes it easier + to install locally without overriding distro files. + +- Bug fixes + - Fix `modinfo -F` when module is builtin: when we asked by a specific field from modinfo, + it was not working correctly if the module was builtin + + - Documentation fixes on precedence order of /etc and /run: the correct order is + /etc/modprobe.d, /run/modprobe.d, /lib/modprobe.d + + - Fix the priority order that we use for searching configuration files. The + correct one is /etc, /run, /usr/local/lib, /lib, for both modprobe.d + and depmo.d + + - Fix kernel command line parsing when there are quotes present. Grub + mangles the command line and changes it from 'module.option="val with + spaces"' to '"module.option=val with spaces"'. Although this is weird + behavior and grub could have been fixed, the kernel understands it + correctly for builtin modules. So change libkmod to also parse it + correctly. This also brings another hidden behavior from the kernel: + newline in the kernel command line is also allowed and can be used to + separate options. + + - Fix a memory leak, overflow and double free on error path + + - Fix documentation for return value from kmod_module_get_info(): we + return the number of entries we added to the list + + - Fix output of modules.builtin.alias.bin index: we were writing an empty file due to + the misuse of kmod_module_get_info() + +- Infra/internal + - Retire integration with semaphoreci + + - Declare the github mirror also as an official upstream source: now besides accepting + patches via mailing list, PRs on github are also acceptable + + - Misc improvements to testsuite, so we can use it reliably regardless + of the configuration used: now tests will skip if we don't have the + build dependencies) + kmod 28 ======= @@ -1,131 +0,0 @@ -kmod - Linux kernel module handling - -Information -=========== - -Build Status: - https://lucasdemarchi.semaphoreci.com/projects/kmod - -Mailing list: - linux-modules@vger.kernel.org (no subscription needed) - https://lore.kernel.org/linux-modules/ - -Patchwork: - https://patchwork.kernel.org/project/linux-modules/ - -Signed packages: - http://www.kernel.org/pub/linux/utils/kernel/kmod/ - -Git: - git://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git - http://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git - https://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git - -Gitweb: - http://git.kernel.org/?p=utils/kernel/kmod/kmod.git - -Irc: - #kmod on irc.freenode.org - -License: - LGPLv2.1+ for libkmod, testsuite and helper libraries - GPLv2+ for tools/* - - -OVERVIEW -======== - -kmod is a set of tools to handle common tasks with Linux kernel modules like -insert, remove, list, check properties, resolve dependencies and aliases. - -These tools are designed on top of libkmod, a library that is shipped with -kmod. See libkmod/README for more details on this library and how to use it. -The aim is to be compatible with tools, configurations and indexes from -module-init-tools project. - -Compilation and installation -============================ - -In order to compiler the source code you need following software packages: - - GCC compiler - - GNU C library - -Optional dependencies: - - ZLIB library - - LZMA library - -Typical configuration: - ./configure CFLAGS="-g -O2" --prefix=/usr \ - --sysconfdir=/etc --libdir=/usr/lib - -Configure automatically searches for all required components and packages. - -To compile and install run: - make && make install - -Hacking -======= - -Run 'autogen.sh' script before configure. If you want to accept the recommended -flags, you just need to run 'autogen.sh c'. Note that the recommended -flags require cython be installed to compile successfully. - -Make sure to read the CODING-STYLE file and the other READMEs: libkmod/README -and testsuite/README. - -Compatibility with module-init-tools -==================================== - -kmod replaces module-init-tools, which is end-of-life. Most of its tools are -rewritten on top of libkmod so it can be used as a drop in replacements. -Somethings however were changed. Reasons vary from "the feature was already -long deprecated on module-init-tools" to "it would be too much trouble to -support it". - -There are several features that are being added in kmod, but we don't -keep track of them here. - -modprobe --------- - -* 'modprobe -l' was marked as deprecated and does not exist anymore - -* 'modprobe -t' is gone, together with 'modprobe -l' - -* modprobe doesn't parse configuration files with names not ending in - '.alias' or '.conf'. modprobe used to warn about these files. - -* modprobe doesn't parse 'config' and 'include' commands in configuration - files. - -* modprobe from m-i-t does not honour softdeps for install commands. E.g.: - config: - - install bli "echo bli" - install bla "echo bla" - softdep bla pre: bli - - With m-i-t, the output of 'modprobe --show-depends bla' will be: - install "echo bla" - - While with kmod: - install "echo bli" - install "echo bla" - -* kmod doesn't dump the configuration as is in the config files. Instead it - dumps the configuration as it was parsed. Therefore, comments and file names - are not dumped, but on the good side we know what the exact configuration - kmod is using. We did this because if we only want to know the entire content - of configuration files, it's enough to use find(1) in modprobe.d directories - -depmod ------- - -* there's no 'depmod -m' option: legacy modules.*map files are gone - -lsmod ------ - -* module-init-tools used /proc/modules to parse module info. kmod uses - /sys/module/*, but there's a fallback to /proc/modules if the latter isn't - available @@ -2,4 +2,130 @@ [![Coverity Scan Status](https://scan.coverity.com/projects/2096/badge.svg)](https://scan.coverity.com/projects/2096) -This is a ***mirror only***. Please see [README](../master/README) file for more information. + +Information +=========== + +Mailing list: + linux-modules@vger.kernel.org (no subscription needed) + https://lore.kernel.org/linux-modules/ + +Signed packages: + http://www.kernel.org/pub/linux/utils/kernel/kmod/ + +Git: + git://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git + http://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git + https://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git + +Gitweb: + http://git.kernel.org/?p=utils/kernel/kmod/kmod.git + https://github.com/kmod-project/kmod + +Irc: + #kmod on irc.freenode.org + +License: + LGPLv2.1+ for libkmod, testsuite and helper libraries + GPLv2+ for tools/* + + +OVERVIEW +======== + +kmod is a set of tools to handle common tasks with Linux kernel modules like +insert, remove, list, check properties, resolve dependencies and aliases. + +These tools are designed on top of libkmod, a library that is shipped with +kmod. See libkmod/README for more details on this library and how to use it. +The aim is to be compatible with tools, configurations and indexes from +module-init-tools project. + +Compilation and installation +============================ + +In order to compiler the source code you need following software packages: + - GCC compiler + - GNU C library + +Optional dependencies: + - ZLIB library + - LZMA library + - ZSTD library + - OPENSSL library (signature handling in modinfo) + +Typical configuration: + ./configure CFLAGS="-g -O2" --prefix=/usr \ + --sysconfdir=/etc --libdir=/usr/lib + +Configure automatically searches for all required components and packages. + +To compile and install run: + make && make install + +Hacking +======= + +Run 'autogen.sh' script before configure. If you want to accept the recommended +flags, you just need to run 'autogen.sh c'. Note that the recommended +flags require cython be installed to compile successfully. + +Make sure to read the CODING-STYLE file and the other READMEs: libkmod/README +and testsuite/README. + +Compatibility with module-init-tools +==================================== + +kmod replaces module-init-tools, which is end-of-life. Most of its tools are +rewritten on top of libkmod so it can be used as a drop in replacements. +Somethings however were changed. Reasons vary from "the feature was already +long deprecated on module-init-tools" to "it would be too much trouble to +support it". + +There are several features that are being added in kmod, but we don't +keep track of them here. + +modprobe +-------- + +* 'modprobe -l' was marked as deprecated and does not exist anymore + +* 'modprobe -t' is gone, together with 'modprobe -l' + +* modprobe doesn't parse configuration files with names not ending in + '.alias' or '.conf'. modprobe used to warn about these files. + +* modprobe doesn't parse 'config' and 'include' commands in configuration + files. + +* modprobe from m-i-t does not honour softdeps for install commands. E.g.: + config: + + install bli "echo bli" + install bla "echo bla" + softdep bla pre: bli + + With m-i-t, the output of 'modprobe --show-depends bla' will be: + install "echo bla" + + While with kmod: + install "echo bli" + install "echo bla" + +* kmod doesn't dump the configuration as is in the config files. Instead it + dumps the configuration as it was parsed. Therefore, comments and file names + are not dumped, but on the good side we know what the exact configuration + kmod is using. We did this because if we only want to know the entire content + of configuration files, it's enough to use find(1) in modprobe.d directories + +depmod +------ + +* there's no 'depmod -m' option: legacy modules.*map files are gone + +lsmod +----- + +* module-init-tools used /proc/modules to parse module info. kmod uses + /sys/module/*, but there's a fallback to /proc/modules if the latter isn't + available diff --git a/configure.ac b/configure.ac index 0cf2eda..6989e93 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ(2.64) AC_INIT([kmod], - [28], + [30], [linux-modules@vger.kernel.org], [kmod], [http://git.kernel.org/?p=utils/kernel/kmod/kmod.git]) @@ -224,6 +224,8 @@ GTK_DOC_CHECK([1.14],[--flavour no-tmpl-flat]) ], [ AM_CONDITIONAL([ENABLE_GTK_DOC], false)]) +# Some tests are skipped when sysconfdir != /etc. +AM_CONDITIONAL([KMOD_SYSCONFDIR_NOT_ETC], [test "x$sysconfdir" != "x/etc"]) ##################################################################### # Default CFLAGS and LDFLAGS diff --git a/libkmod/docs/libkmod-sections.txt b/libkmod/docs/libkmod-sections.txt index e59ab7a..33d9eec 100644 --- a/libkmod/docs/libkmod-sections.txt +++ b/libkmod/docs/libkmod-sections.txt @@ -15,6 +15,7 @@ kmod_get_log_priority kmod_set_log_fn kmod_get_userdata kmod_set_userdata +kmod_get_dirname </SECTION> <SECTION> @@ -46,6 +47,7 @@ kmod_config_iter_free_iter <FILE>libkmod-module</FILE> kmod_module kmod_module_new_from_lookup +kmod_module_new_from_name_lookup kmod_module_new_from_name kmod_module_new_from_path diff --git a/libkmod/libkmod-builtin.c b/libkmod/libkmod-builtin.c index fc9a376..a002cb5 100644 --- a/libkmod/libkmod-builtin.c +++ b/libkmod/libkmod-builtin.c @@ -246,7 +246,7 @@ bool kmod_builtin_iter_get_modname(struct kmod_builtin_iter *iter, len = dot - line; - if (len > PATH_MAX) { + if (len >= PATH_MAX) { sv_errno = ENAMETOOLONG; goto fail; } @@ -313,7 +313,7 @@ ssize_t kmod_builtin_get_modinfo(struct kmod_ctx *ctx, const char *modname, while (offset < iter->next) { offset = get_string(iter, pos, &line, &linesz); if (offset <= 0) { - count = (offset) ? -errno : -EOF; + count = (offset) ? -errno : -EINVAL; free(*modinfo); goto fail; } diff --git a/libkmod/libkmod-config.c b/libkmod/libkmod-config.c index 971f20b..e83621b 100644 --- a/libkmod/libkmod-config.c +++ b/libkmod/libkmod-config.c @@ -498,8 +498,15 @@ static int kmod_config_parse_kcmdline(struct kmod_config *config) { char buf[KCMD_LINE_SIZE]; int fd, err; - char *p, *modname, *param = NULL, *value = NULL; - bool is_quoted = false, is_module = true; + char *p, *p_quote_start, *modname, *param = NULL, *value = NULL; + bool is_quoted = false, iter = true; + enum state { + STATE_IGNORE, + STATE_MODNAME, + STATE_PARAM, + STATE_VALUE, + STATE_COMPLETE, + } state; fd = open("/proc/cmdline", O_RDONLY|O_CLOEXEC); if (fd < 0) { @@ -516,54 +523,102 @@ static int kmod_config_parse_kcmdline(struct kmod_config *config) return err; } - for (p = buf, modname = buf; *p != '\0' && *p != '\n'; p++) { - if (*p == '"') { + state = STATE_MODNAME; + p_quote_start = NULL; + for (p = buf, modname = buf; iter; p++) { + switch (*p) { + case '"': is_quoted = !is_quoted; - if (is_quoted) { - /* don't consider a module until closing quotes */ - is_module = false; - } else if (param != NULL && value != NULL) { - /* - * If we are indeed expecting a value and - * closing quotes, then this can be considered - * a valid option for a module - */ - is_module = true; + /* + * only allow starting quote as first char when looking + * for a modname: anything else is considered ill-formed + */ + if (is_quoted && state == STATE_MODNAME && p == modname) { + p_quote_start = p; + modname = p + 1; + } else if (state != STATE_VALUE) { + state = STATE_IGNORE; } - continue; - } - if (is_quoted) - continue; - - switch (*p) { + break; + case '\0': + iter = false; + /* fall-through */ case ' ': - *p = '\0'; - if (is_module) - kcmdline_parse_result(config, modname, param, value); - param = value = NULL; - modname = p + 1; - is_module = true; + case '\n': + case '\t': + case '\v': + case '\f': + case '\r': + if (is_quoted && state == STATE_VALUE) { + /* no state change*/; + } else if (is_quoted) { + /* spaces are only allowed in the value part */ + state = STATE_IGNORE; + } else if (state == STATE_VALUE || state == STATE_PARAM) { + *p = '\0'; + state = STATE_COMPLETE; + } else { + /* + * go to next option, ignoring any possible + * partial match we have + */ + modname = p + 1; + state = STATE_MODNAME; + p_quote_start = NULL; + } break; case '.': - if (param == NULL) { + if (state == STATE_MODNAME) { *p = '\0'; param = p + 1; + state = STATE_PARAM; + } else if (state == STATE_PARAM) { + state = STATE_IGNORE; } break; case '=': - if (param != NULL) + if (state == STATE_PARAM) { + /* + * Don't set *p to '\0': the value var shadows + * param + */ value = p + 1; - else - is_module = false; + state = STATE_VALUE; + } else if (state == STATE_MODNAME) { + state = STATE_IGNORE; + } break; } - } - *p = '\0'; - if (is_module) - kcmdline_parse_result(config, modname, param, value); + if (state == STATE_COMPLETE) { + /* + * We may need to re-quote to unmangle what the + * bootloader passed. Example: grub passes the option as + * "parport.dyndbg=file drivers/parport/ieee1284_ops.c +mpf" + * instead of + * parport.dyndbg="file drivers/parport/ieee1284_ops.c +mpf" + */ + if (p_quote_start && p_quote_start < modname) { + /* + * p_quote_start + * | + * |modname param value + * || | | + * vv v v + * "parport\0dyndbg=file drivers/parport/ieee1284_ops.c +mpf" */ + memmove(p_quote_start, modname, value - modname); + value--; modname--; param--; + *value = '"'; + } + kcmdline_parse_result(config, modname, param, value); + /* start over on next iteration */ + modname = p + 1; + state = STATE_MODNAME; + p_quote_start = NULL; + } + } return 0; } @@ -854,8 +909,10 @@ int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **p_config, memcpy(cf->path, path, pathlen); tmp = kmod_list_append(path_list, cf); - if (tmp == NULL) + if (tmp == NULL) { + free(cf); goto oom; + } path_list = tmp; } diff --git a/libkmod/libkmod-internal.h b/libkmod/libkmod-internal.h index 398af9c..c22644a 100644 --- a/libkmod/libkmod-internal.h +++ b/libkmod/libkmod-internal.h @@ -25,10 +25,12 @@ static _always_inline_ _printf_format_(2, 3) void # else # define DBG(ctx, arg...) kmod_log_null(ctx, ## arg) # endif +# define NOTICE(ctx, arg...) kmod_log_cond(ctx, LOG_NOTICE, ## arg) # define INFO(ctx, arg...) kmod_log_cond(ctx, LOG_INFO, ## arg) # define ERR(ctx, arg...) kmod_log_cond(ctx, LOG_ERR, ## arg) #else # define DBG(ctx, arg...) kmod_log_null(ctx, ## arg) +# define NOTICE(ctx, arg...) kmod_log_null(ctx, ## arg) # define INFO(ctx, arg...) kmod_log_null(ctx, ## arg) # define ERR(ctx, arg...) kmod_log_null(ctx, ## arg) #endif diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c index 76a6dc3..12d8ed1 100644 --- a/libkmod/libkmod-module.c +++ b/libkmod/libkmod-module.c @@ -431,17 +431,18 @@ KMOD_EXPORT int kmod_module_new_from_path(struct kmod_ctx *ctx, return -EEXIST; } - *mod = kmod_module_ref(m); - return 0; - } + kmod_module_ref(m); + } else { + err = kmod_module_new(ctx, name, name, namelen, NULL, 0, &m); + if (err < 0) { + free(abspath); + return err; + } - err = kmod_module_new(ctx, name, name, namelen, NULL, 0, &m); - if (err < 0) { - free(abspath); - return err; + m->path = abspath; } - m->path = abspath; + m->builtin = KMOD_MODULE_BUILTIN_NO; *mod = m; return 0; @@ -498,13 +499,26 @@ KMOD_EXPORT struct kmod_module *kmod_module_ref(struct kmod_module *mod) return mod; } -#define CHECK_ERR_AND_FINISH(_err, _label_err, _list, label_finish) \ - do { \ - if ((_err) < 0) \ - goto _label_err; \ - if (*(_list) != NULL) \ - goto finish; \ - } while (0) +typedef int (*lookup_func)(struct kmod_ctx *ctx, const char *name, struct kmod_list **list) __attribute__((nonnull(1, 2, 3))); + +static int __kmod_module_new_from_lookup(struct kmod_ctx *ctx, const lookup_func lookup[], + size_t lookup_count, const char *s, + struct kmod_list **list) +{ + unsigned int i; + + for (i = 0; i < lookup_count; i++) { + int err; + + err = lookup[i](ctx, s, list); + if (err < 0 && err != -ENOSYS) + return err; + else if (*list != NULL) + return 0; + } + + return 0; +} /** * kmod_module_new_from_lookup: @@ -520,7 +534,7 @@ KMOD_EXPORT struct kmod_module *kmod_module_ref(struct kmod_module *mod) * * The search order is: 1. aliases in configuration file; 2. module names in * modules.dep index; 3. symbol aliases in modules.symbols index; 4. aliases - * in modules.alias index. + * from install commands; 5. builtin indexes from kernel. * * The initial refcount is 1, and needs to be decremented to release the * resources of the kmod_module. The returned @list must be released by @@ -537,8 +551,17 @@ KMOD_EXPORT int kmod_module_new_from_lookup(struct kmod_ctx *ctx, const char *given_alias, struct kmod_list **list) { - int err; + const lookup_func lookup[] = { + kmod_lookup_alias_from_config, + kmod_lookup_alias_from_moddep_file, + kmod_lookup_alias_from_symbols_file, + kmod_lookup_alias_from_commands, + kmod_lookup_alias_from_aliases_file, + kmod_lookup_alias_from_builtin_file, + kmod_lookup_alias_from_kernel_builtin_file, + }; char alias[PATH_MAX]; + int err; if (ctx == NULL || given_alias == NULL) return -ENOENT; @@ -555,46 +578,75 @@ KMOD_EXPORT int kmod_module_new_from_lookup(struct kmod_ctx *ctx, DBG(ctx, "input alias=%s, normalized=%s\n", given_alias, alias); - /* Aliases from config file override all the others */ - err = kmod_lookup_alias_from_config(ctx, alias, list); - CHECK_ERR_AND_FINISH(err, fail, list, finish); + err = __kmod_module_new_from_lookup(ctx, lookup, ARRAY_SIZE(lookup), + alias, list); + + DBG(ctx, "lookup=%s found=%d\n", alias, err >= 0 && *list); + + if (err < 0) { + kmod_module_unref_list(*list); + *list = NULL; + } + + return err; +} + +/** + * kmod_module_new_from_name_lookup: + * @ctx: kmod library context + * @modname: module name to look for + * @mod: returned module on success + * + * Lookup by module name, without considering possible aliases. This is similar + * to kmod_module_new_from_lookup(), but don't consider as source indexes and + * configurations that work with aliases. When succesful, this always resolves + * to one and only one module. + * + * The search order is: 1. module names in modules.dep index; + * 2. builtin indexes from kernel. + * + * The initial refcount is 1, and needs to be decremented to release the + * resources of the kmod_module. Since libkmod keeps track of all + * kmod_modules created, they are all released upon @ctx destruction too. Do + * not unref @ctx before all the desired operations with the returned list are + * completed. + * + * Returns: 0 on success or < 0 otherwise. It fails if any of the lookup + * methods failed, which is basically due to memory allocation failure. If + * module is not found, it still returns 0, but @mod is left untouched. + */ +KMOD_EXPORT int kmod_module_new_from_name_lookup(struct kmod_ctx *ctx, + const char *modname, + struct kmod_module **mod) +{ + const lookup_func lookup[] = { + kmod_lookup_alias_from_moddep_file, + kmod_lookup_alias_from_builtin_file, + kmod_lookup_alias_from_kernel_builtin_file, + }; + char name_norm[PATH_MAX]; + struct kmod_list *list = NULL; + int err; + + if (ctx == NULL || modname == NULL || mod == NULL) + return -ENOENT; - DBG(ctx, "lookup modules.dep %s\n", alias); - err = kmod_lookup_alias_from_moddep_file(ctx, alias, list); - CHECK_ERR_AND_FINISH(err, fail, list, finish); + modname_normalize(modname, name_norm, NULL); - DBG(ctx, "lookup modules.symbols %s\n", alias); - err = kmod_lookup_alias_from_symbols_file(ctx, alias, list); - CHECK_ERR_AND_FINISH(err, fail, list, finish); + DBG(ctx, "input modname=%s, normalized=%s\n", modname, name_norm); - DBG(ctx, "lookup install and remove commands %s\n", alias); - err = kmod_lookup_alias_from_commands(ctx, alias, list); - CHECK_ERR_AND_FINISH(err, fail, list, finish); + err = __kmod_module_new_from_lookup(ctx, lookup, ARRAY_SIZE(lookup), + name_norm, &list); - DBG(ctx, "lookup modules.aliases %s\n", alias); - err = kmod_lookup_alias_from_aliases_file(ctx, alias, list); - CHECK_ERR_AND_FINISH(err, fail, list, finish); + DBG(ctx, "lookup=%s found=%d\n", name_norm, err >= 0 && list); - DBG(ctx, "lookup modules.builtin.modinfo %s\n", alias); - err = kmod_lookup_alias_from_kernel_builtin_file(ctx, alias, list); - if (err == -ENOSYS) { - /* Optional index missing, try the old one */ - DBG(ctx, "lookup modules.builtin %s\n", alias); - err = kmod_lookup_alias_from_builtin_file(ctx, alias, list); - } - CHECK_ERR_AND_FINISH(err, fail, list, finish); + if (err >= 0 && list != NULL) + *mod = kmod_module_get_module(list); + kmod_module_unref_list(list); -finish: - DBG(ctx, "lookup %s=%d, list=%p\n", alias, err, *list); - return err; -fail: - DBG(ctx, "Failed to lookup %s\n", alias); - kmod_module_unref_list(*list); - *list = NULL; return err; } -#undef CHECK_ERR_AND_FINISH /** * kmod_module_unref_list: @@ -771,11 +823,13 @@ extern long delete_module(const char *name, unsigned int flags); /** * kmod_module_remove_module: * @mod: kmod module - * @flags: flags to pass to Linux kernel when removing the module. The only valid flag is + * @flags: flags used when removing the module. * KMOD_REMOVE_FORCE: force remove module regardless if it's still in - * use by a kernel subsystem or other process; - * KMOD_REMOVE_NOWAIT is always enforced, causing us to pass O_NONBLOCK to + * use by a kernel subsystem or other process; passed directly to Linux kernel + * KMOD_REMOVE_NOWAIT: is always enforced, causing us to pass O_NONBLOCK to * delete_module(2). + * KMOD_REMOVE_NOLOG: when module removal fails, do not log anything as the + * caller may want to handle retries and log when appropriate. * * Remove a module from Linux kernel. * @@ -784,6 +838,8 @@ extern long delete_module(const char *name, unsigned int flags); KMOD_EXPORT int kmod_module_remove_module(struct kmod_module *mod, unsigned int flags) { + unsigned int libkmod_flags = flags & 0xff; + int err; if (mod == NULL) @@ -796,7 +852,8 @@ KMOD_EXPORT int kmod_module_remove_module(struct kmod_module *mod, err = delete_module(mod->name, flags); if (err != 0) { err = -errno; - ERR(mod->ctx, "could not remove '%s': %m\n", mod->name); + if (!(libkmod_flags & KMOD_REMOVE_NOLOG)) + ERR(mod->ctx, "could not remove '%s': %m\n", mod->name); } return err; @@ -2277,7 +2334,7 @@ list_error: * * After use, free the @list by calling kmod_module_info_free_list(). * - * Returns: 0 on success or < 0 otherwise. + * Returns: number of entries in @list on success or < 0 otherwise. */ KMOD_EXPORT int kmod_module_get_info(const struct kmod_module *mod, struct kmod_list **list) { @@ -2912,7 +2969,10 @@ int kmod_module_get_builtin(struct kmod_ctx *ctx, struct kmod_list **list) goto fail; } - kmod_module_new_from_name(ctx, modname, &mod); + err = kmod_module_new_from_name(ctx, modname, &mod); + if (err < 0) + goto fail; + kmod_module_set_builtin(mod, true); *list = kmod_list_append(*list, mod); diff --git a/libkmod/libkmod-signature.c b/libkmod/libkmod-signature.c index 9877cf3..47aedd0 100644 --- a/libkmod/libkmod-signature.c +++ b/libkmod/libkmod-signature.c @@ -55,6 +55,7 @@ enum pkey_hash_algo { PKEY_HASH_SHA384, PKEY_HASH_SHA512, PKEY_HASH_SHA224, + PKEY_HASH_SM3, PKEY_HASH__LAST }; @@ -67,6 +68,7 @@ const char *const pkey_hash_algo[PKEY_HASH__LAST] = { [PKEY_HASH_SHA384] = "sha384", [PKEY_HASH_SHA512] = "sha512", [PKEY_HASH_SHA224] = "sha224", + [PKEY_HASH_SM3] = "sm3", }; enum pkey_id_type { @@ -160,6 +162,10 @@ static int obj_to_hash_algo(const ASN1_OBJECT *o) return PKEY_HASH_SHA512; case NID_sha224: return PKEY_HASH_SHA224; +# ifndef OPENSSL_NO_SM3 + case NID_sm3: + return PKEY_HASH_SM3; +# endif default: return -1; } diff --git a/libkmod/libkmod.c b/libkmod/libkmod.c index 43423d6..7c2b889 100644 --- a/libkmod/libkmod.c +++ b/libkmod/libkmod.c @@ -64,6 +64,7 @@ static struct _index_files { static const char *default_config_paths[] = { SYSCONFDIR "/modprobe.d", "/run/modprobe.d", + "/usr/local/lib/modprobe.d", "/lib/modprobe.d", NULL }; @@ -234,10 +235,11 @@ static char *get_kernel_release(const char *dirname) * Otherwise, give an absolute dirname. * @config_paths: ordered array of paths (directories or files) where * to load from user-defined configuration parameters such as - * alias, blacklists, commands (install, remove). If - * NULL defaults to /run/modprobe.d, /etc/modprobe.d and - * /lib/modprobe.d. Give an empty vector if configuration should - * not be read. This array must be null terminated. + * alias, blacklists, commands (install, remove). If NULL + * defaults to /etc/modprobe.d, /run/modprobe.d, + * /usr/local/lib/modprobe.d and /lib/modprobe.d. Give an empty + * vector if configuration should not be read. This array must + * be null terminated. * * Create kmod library context. This reads the kmod configuration * and fills in the default values. diff --git a/libkmod/libkmod.h b/libkmod/libkmod.h index 3cab2e5..7251aa7 100644 --- a/libkmod/libkmod.h +++ b/libkmod/libkmod.h @@ -129,6 +129,9 @@ int kmod_module_new_from_path(struct kmod_ctx *ctx, const char *path, struct kmod_module **mod); int kmod_module_new_from_lookup(struct kmod_ctx *ctx, const char *given_alias, struct kmod_list **list); +int kmod_module_new_from_name_lookup(struct kmod_ctx *ctx, + const char *modname, + struct kmod_module **mod); int kmod_module_new_from_loaded(struct kmod_ctx *ctx, struct kmod_list **list); @@ -142,6 +145,8 @@ struct kmod_module *kmod_module_get_module(const struct kmod_list *entry); enum kmod_remove { KMOD_REMOVE_FORCE = O_TRUNC, KMOD_REMOVE_NOWAIT = O_NONBLOCK, /* always set */ + /* libkmod-only defines, not passed to kernel */ + KMOD_REMOVE_NOLOG = 1, }; /* Insertion flags */ diff --git a/libkmod/libkmod.sym b/libkmod/libkmod.sym index 5f5e1fb..0c04fda 100644 --- a/libkmod/libkmod.sym +++ b/libkmod/libkmod.sym @@ -30,6 +30,7 @@ global: kmod_module_new_from_name; kmod_module_new_from_path; kmod_module_new_from_lookup; + kmod_module_new_from_name_lookup; kmod_module_new_from_loaded; kmod_module_ref; kmod_module_unref; diff --git a/man/depmod.d.xml b/man/depmod.d.xml index 4341a56..76548e9 100644 --- a/man/depmod.d.xml +++ b/man/depmod.d.xml @@ -40,8 +40,9 @@ <refsynopsisdiv> <para><filename>/usr/lib/depmod.d/*.conf</filename></para> - <para><filename>/etc/depmod.d/*.conf</filename></para> + <para><filename>/usr/local/lib/depmod.d/*.conf</filename></para> <para><filename>/run/depmod.d/*.conf</filename></para> + <para><filename>/etc/depmod.d/*.conf</filename></para> </refsynopsisdiv> <refsect1><title>DESCRIPTION</title> @@ -130,6 +131,20 @@ </para> </listitem> </varlistentry> + <varlistentry> + <term>exclude <replaceable>excludedir</replaceable> + </term> + <listitem> + <para> + This specifies the trailing directories that will be excluded + during the search for kernel modules. + </para> + <para> + The <replaceable>excludedir</replaceable> is the trailing directory + to exclude + </para> + </listitem> + </varlistentry> </variablelist> </refsect1> diff --git a/man/modprobe.d.xml b/man/modprobe.d.xml index 211af84..0ab3e91 100644 --- a/man/modprobe.d.xml +++ b/man/modprobe.d.xml @@ -41,8 +41,9 @@ <refsynopsisdiv> <para><filename>/lib/modprobe.d/*.conf</filename></para> - <para><filename>/etc/modprobe.d/*.conf</filename></para> + <para><filename>/usr/local/lib/modprobe.d/*.conf</filename></para> <para><filename>/run/modprobe.d/*.conf</filename></para> + <para><filename>/etc/modprobe.d/*.conf</filename></para> </refsynopsisdiv> <refsect1><title>DESCRIPTION</title> diff --git a/man/modprobe.xml b/man/modprobe.xml index 0372b6b..db39c7a 100644 --- a/man/modprobe.xml +++ b/man/modprobe.xml @@ -390,6 +390,23 @@ </varlistentry> <varlistentry> <term> + <option>-w</option> + </term> + <term> + <option>--wait=</option>TIMEOUT_MSEC + </term> + <listitem> + <para> + This option causes <command>modprobe -r</command> to continue trying to + remove a module if it fails due to the module being busy, i.e. its refcount + is not 0 at the time the call is made. Modprobe tries to remove the module + with an incremental sleep time between each tentative up until the maximum + wait time in milliseconds passed in this option. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> <option>-S</option> </term> <term> diff --git a/shared/util.c b/shared/util.c index b487b5f..4b547ff 100644 --- a/shared/util.c +++ b/shared/util.c @@ -466,6 +466,78 @@ unsigned long long ts_usec(const struct timespec *ts) (unsigned long long) ts->tv_nsec / NSEC_PER_USEC; } +unsigned long long ts_msec(const struct timespec *ts) +{ + return (unsigned long long) ts->tv_sec * MSEC_PER_SEC + + (unsigned long long) ts->tv_nsec / NSEC_PER_MSEC; +} + +static struct timespec msec_ts(unsigned long long msec) +{ + struct timespec ts = { + .tv_sec = msec / MSEC_PER_SEC, + .tv_nsec = (msec % MSEC_PER_SEC) * NSEC_PER_MSEC, + }; + + return ts; +} + +int sleep_until_msec(unsigned long long msec) +{ + struct timespec ts = msec_ts(msec); + + if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL) < 0 && + errno != EINTR) + return -errno; + + return 0; +} + +/* + * Exponential retry backoff with tail + */ +unsigned long long get_backoff_delta_msec(unsigned long long t0, + unsigned long long tend, + unsigned long long *delta) +{ + unsigned long long t; + + t = now_msec(); + + if (!*delta) + *delta = 1; + else + *delta <<= 1; + + while (t + *delta > tend) + *delta >>= 1; + + if (!*delta && tend > t) + *delta = tend - t; + + return t + *delta; +} + +unsigned long long now_usec(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + return 0; + + return ts_usec(&ts); +} + +unsigned long long now_msec(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + return 0; + + return ts_msec(&ts); +} + unsigned long long stat_mstamp(const struct stat *st) { #ifdef HAVE_STRUCT_STAT_ST_MTIM diff --git a/shared/util.h b/shared/util.h index c6a31df..7030653 100644 --- a/shared/util.h +++ b/shared/util.h @@ -7,6 +7,7 @@ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> +#include <time.h> #include <shared/macro.h> @@ -42,7 +43,23 @@ char *path_make_absolute_cwd(const char *p) _must_check_ __attribute__((nonnull( int mkdir_p(const char *path, int len, mode_t mode); int mkdir_parents(const char *path, mode_t mode); unsigned long long stat_mstamp(const struct stat *st); + +/* time-related functions + * ************************************************************************ */ +#define USEC_PER_SEC 1000000ULL +#define USEC_PER_MSEC 1000ULL +#define MSEC_PER_SEC 1000ULL +#define NSEC_PER_MSEC 1000000ULL + unsigned long long ts_usec(const struct timespec *ts); +unsigned long long ts_msec(const struct timespec *ts); +unsigned long long now_usec(void); +unsigned long long now_msec(void); +int sleep_until_msec(unsigned long long msec); +unsigned long long get_backoff_delta_msec(unsigned long long t0, + unsigned long long tend, + unsigned long long *delta); + /* endianess and alignments */ /* ************************************************************************ */ diff --git a/testsuite/module-playground/.gitignore b/testsuite/module-playground/.gitignore index fca12f3..6d9c7b1 100644 --- a/testsuite/module-playground/.gitignore +++ b/testsuite/module-playground/.gitignore @@ -1,4 +1,3 @@ -*o.cmd *.ko !mod-simple-*.ko !cache/*.ko @@ -8,6 +7,7 @@ *.mod *.a *.cmd +*.o.d modules.order Module.symvers diff --git a/testsuite/module-playground/mod-simple.c b/testsuite/module-playground/mod-simple.c index cb38580..503e4d8 100644 --- a/testsuite/module-playground/mod-simple.c +++ b/testsuite/module-playground/mod-simple.c @@ -1,16 +1,32 @@ +#include <linux/debugfs.h> #include <linux/init.h> #include <linux/module.h> +static struct dentry *debugfs_dir; + +static int test_show(struct seq_file *s, void *data) +{ + seq_puts(s, "test"); + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(test); + static int __init test_module_init(void) { + debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); + debugfs_create_file("test", 0444, debugfs_dir, NULL, &test_fops); + return 0; } static void test_module_exit(void) { + debugfs_remove_recursive(debugfs_dir); } + module_init(test_module_init); module_exit(test_module_exit); MODULE_AUTHOR("Lucas De Marchi <lucas.demarchi@intel.com>"); -MODULE_LICENSE("LGPL"); +MODULE_LICENSE("GPL"); diff --git a/testsuite/populate-modules.sh b/testsuite/populate-modules.sh index 358e740..099f026 100755 --- a/testsuite/populate-modules.sh +++ b/testsuite/populate-modules.sh @@ -4,6 +4,12 @@ set -e MODULE_PLAYGROUND=$1 ROOTFS=$2 +CONFIG_H=$3 + +feature_enabled() { + local feature=$1 + grep KMOD_FEATURES $CONFIG_H | head -n 1 | grep -q \+$feature +} declare -A map map=( @@ -66,6 +72,9 @@ map=( gzip_array=( "test-depmod/modules-order-compressed/lib/modules/4.4.4/kernel/drivers/block/cciss.ko" + ) + +xz_array=( "test-depmod/modules-order-compressed/lib/modules/4.4.4/kernel/drivers/scsi/scsi_mod.ko" ) @@ -85,38 +94,47 @@ attach_pkcs7_array=( "test-modinfo/mod-simple-pkcs7.ko" ) -for k in ${!map[@]}; do +for k in "${!map[@]}"; do dst=${ROOTFS}/$k src=${MODULE_PLAYGROUND}/${map[$k]} - if test "${dst: -1}" = "/"; then - install -d $dst - install -t $dst $src + if [[ $dst = */ ]]; then + install -d "$dst" + install -t "$dst" "$src" else - install -D $src $dst + install -D "$src" "$dst" fi done # start poking the final rootfs... -# gzip these modules -for m in "${gzip_array[@]}"; do - gzip $ROOTFS/$m -done - -# zstd-compress these modules -for m in "${zstd_array[@]}"; do - zstd --rm $ROOTFS/$m -done +# compress modules with each format if feature is enabled +if feature_enabled ZLIB; then + for m in "${gzip_array[@]}"; do + gzip "$ROOTFS/$m" + done +fi + +if feature_enabled XZ; then + for m in "${xz_array[@]}"; do + xz "$ROOTFS/$m" + done +fi + +if feature_enabled ZSTD; then + for m in "${zstd_array[@]}"; do + zstd --rm $ROOTFS/$m + done +fi for m in "${attach_sha1_array[@]}"; do - cat ${MODULE_PLAYGROUND}/dummy.sha1 >> ${ROOTFS}/$m + cat "${MODULE_PLAYGROUND}/dummy.sha1" >>"${ROOTFS}/$m" done for m in "${attach_sha256_array[@]}"; do - cat ${MODULE_PLAYGROUND}/dummy.sha256 >> ${ROOTFS}/$m + cat "${MODULE_PLAYGROUND}/dummy.sha256" >>"${ROOTFS}/$m" done for m in "${attach_pkcs7_array[@]}"; do - cat ${MODULE_PLAYGROUND}/dummy.pkcs7 >> ${ROOTFS}/$m + cat "${MODULE_PLAYGROUND}/dummy.pkcs7" >>"${ROOTFS}/$m" done diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/correct.txt new file mode 100644 index 0000000..d80da6d --- /dev/null +++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/correct.txt @@ -0,0 +1,5 @@ +options psmouse foo +options parport dyndbg="file drivers/parport/ieee1284_ops.c +mpf" + +# End of configuration files. Dumping indexes now: + diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/correct.txt new file mode 100644 index 0000000..d80da6d --- /dev/null +++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/correct.txt @@ -0,0 +1,5 @@ +options psmouse foo +options parport dyndbg="file drivers/parport/ieee1284_ops.c +mpf" + +# End of configuration files. Dumping indexes now: + diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/proc/cmdline new file mode 100644 index 0000000..86f9052 --- /dev/null +++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/proc/cmdline @@ -0,0 +1 @@ +psmouse.foo parport.dyndbg="file drivers/parport/ieee1284_ops.c +mpf" quiet rw diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/proc/cmdline new file mode 100644 index 0000000..86f9052 --- /dev/null +++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/proc/cmdline @@ -0,0 +1 @@ +psmouse.foo parport.dyndbg="file drivers/parport/ieee1284_ops.c +mpf" quiet rw diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/correct.txt new file mode 100644 index 0000000..d80da6d --- /dev/null +++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/correct.txt @@ -0,0 +1,5 @@ +options psmouse foo +options parport dyndbg="file drivers/parport/ieee1284_ops.c +mpf" + +# End of configuration files. Dumping indexes now: + diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/correct.txt b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/correct.txt new file mode 100644 index 0000000..d80da6d --- /dev/null +++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/correct.txt @@ -0,0 +1,5 @@ +options psmouse foo +options parport dyndbg="file drivers/parport/ieee1284_ops.c +mpf" + +# End of configuration files. Dumping indexes now: + diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/proc/cmdline new file mode 100644 index 0000000..86f9052 --- /dev/null +++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/proc/cmdline @@ -0,0 +1 @@ +psmouse.foo parport.dyndbg="file drivers/parport/ieee1284_ops.c +mpf" quiet rw diff --git a/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/proc/cmdline b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/proc/cmdline new file mode 100644 index 0000000..eab04ad --- /dev/null +++ b/testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/proc/cmdline @@ -0,0 +1 @@ +psmouse.foo "parport.dyndbg=file drivers/parport/ieee1284_ops.c +mpf" quiet rw diff --git a/testsuite/test-blacklist.c b/testsuite/test-blacklist.c index 969567d..d03eedb 100644 --- a/testsuite/test-blacklist.c +++ b/testsuite/test-blacklist.c @@ -95,6 +95,9 @@ fail_lookup: } DEFINE_TEST(blacklist_1, +#if defined(KMOD_SYSCONFDIR_NOT_ETC) + .skip = true, +#endif .description = "check if modules are correctly blacklisted", .config = { [TC_ROOTFS] = TESTSUITE_ROOTFS "test-blacklist/", diff --git a/testsuite/test-depmod.c b/testsuite/test-depmod.c index 47dafb4..d7802d7 100644 --- a/testsuite/test-depmod.c +++ b/testsuite/test-depmod.c @@ -25,7 +25,6 @@ #include "testsuite.h" -#ifdef ENABLE_ZLIB #define MODULES_ORDER_UNAME "4.4.4" #define MODULES_ORDER_ROOTFS TESTSUITE_ROOTFS "test-depmod/modules-order-compressed" #define MODULES_ORDER_LIB_MODULES MODULES_ORDER_ROOTFS "/lib/modules/" MODULES_ORDER_UNAME @@ -42,6 +41,9 @@ static noreturn int depmod_modules_order_for_compressed(const struct test *t) } DEFINE_TEST(depmod_modules_order_for_compressed, +#if defined(KMOD_SYSCONFDIR_NOT_ETC) + .skip = true, +#endif .description = "check if depmod let aliases in right order when using compressed modules", .config = { [TC_UNAME_R] = MODULES_ORDER_UNAME, @@ -54,7 +56,6 @@ DEFINE_TEST(depmod_modules_order_for_compressed, { } }, }); -#endif #define SEARCH_ORDER_SIMPLE_ROOTFS TESTSUITE_ROOTFS "test-depmod/search-order-simple" static noreturn int depmod_search_order_simple(const struct test *t) @@ -121,6 +122,9 @@ static noreturn int depmod_detect_loop(const struct test *t) exit(EXIT_FAILURE); } DEFINE_TEST(depmod_detect_loop, +#if defined(KMOD_SYSCONFDIR_NOT_ETC) + .skip = true, +#endif .description = "check if depmod detects module loops correctly", .config = { [TC_UNAME_R] = "4.4.4", @@ -144,6 +148,9 @@ static noreturn int depmod_search_order_external_first(const struct test *t) exit(EXIT_FAILURE); } DEFINE_TEST(depmod_search_order_external_first, +#if defined(KMOD_SYSCONFDIR_NOT_ETC) + .skip = true, +#endif .description = "check if depmod honor external keyword with higher priority", .config = { [TC_UNAME_R] = "4.4.4", @@ -196,6 +203,9 @@ static noreturn int depmod_search_order_override(const struct test *t) exit(EXIT_FAILURE); } DEFINE_TEST(depmod_search_order_override, +#if defined(KMOD_SYSCONFDIR_NOT_ETC) + .skip = true, +#endif .description = "check if depmod honor override keyword", .config = { [TC_UNAME_R] = "4.4.4", diff --git a/testsuite/test-initstate.c b/testsuite/test-initstate.c index da2303a..9332e8f 100644 --- a/testsuite/test-initstate.c +++ b/testsuite/test-initstate.c @@ -45,7 +45,7 @@ static noreturn int test_initstate_from_lookup(const struct test *t) exit(EXIT_FAILURE); err = kmod_module_new_from_lookup(ctx, "fake-builtin", &list); - if (err != 0) { + if (err < 0) { ERR("could not create module from lookup: %s\n", strerror(-err)); exit(EXIT_FAILURE); } diff --git a/testsuite/test-modprobe.c b/testsuite/test-modprobe.c index f908d56..0255f1a 100644 --- a/testsuite/test-modprobe.c +++ b/testsuite/test-modprobe.c @@ -83,6 +83,9 @@ static noreturn int modprobe_show_alias_to_none(const struct test *t) exit(EXIT_FAILURE); } DEFINE_TEST(modprobe_show_alias_to_none, +#if defined(KMOD_SYSCONFDIR_NOT_ETC) + .skip = true, +#endif .description = "check if modprobe --show-depends doesn't explode with an alias to nothing", .config = { [TC_UNAME_R] = "4.4.4", @@ -172,6 +175,9 @@ static noreturn int modprobe_softdep_loop(const struct test *t) exit(EXIT_FAILURE); } DEFINE_TEST(modprobe_softdep_loop, +#if defined(KMOD_SYSCONFDIR_NOT_ETC) + .skip = true, +#endif .description = "check if modprobe breaks softdep loop", .config = { [TC_UNAME_R] = "4.4.4", @@ -207,7 +213,7 @@ DEFINE_TEST(modprobe_install_cmd_loop, .modules_loaded = "mod-loop-b,mod-loop-a", ); -static noreturn int modprobe_param_kcmdline(const struct test *t) +static noreturn int modprobe_param_kcmdline_show_deps(const struct test *t) { const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe"; const char *const args[] = { @@ -219,7 +225,7 @@ static noreturn int modprobe_param_kcmdline(const struct test *t) test_spawn_prog(progname, args); exit(EXIT_FAILURE); } -DEFINE_TEST(modprobe_param_kcmdline, +DEFINE_TEST(modprobe_param_kcmdline_show_deps, .description = "check if params from kcmdline are passed to (f)init_module call", .config = { [TC_UNAME_R] = "4.4.4", @@ -231,7 +237,7 @@ DEFINE_TEST(modprobe_param_kcmdline, .modules_loaded = "", ); -static noreturn int modprobe_param_kcmdline2(const struct test *t) +static noreturn int modprobe_param_kcmdline(const struct test *t) { const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe"; const char *const args[] = { @@ -243,7 +249,7 @@ static noreturn int modprobe_param_kcmdline2(const struct test *t) test_spawn_prog(progname, args); exit(EXIT_FAILURE); } -DEFINE_TEST(modprobe_param_kcmdline2, +DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline2, modprobe_param_kcmdline, .description = "check if params with no value are parsed correctly from kcmdline", .config = { [TC_UNAME_R] = "4.4.4", @@ -255,19 +261,7 @@ DEFINE_TEST(modprobe_param_kcmdline2, .modules_loaded = "", ); -static noreturn int modprobe_param_kcmdline3(const struct test *t) -{ - const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe"; - const char *const args[] = { - progname, - "-c", - NULL, - }; - - test_spawn_prog(progname, args); - exit(EXIT_FAILURE); -} -DEFINE_TEST(modprobe_param_kcmdline3, +DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline3, modprobe_param_kcmdline, .description = "check if unrelated strings in kcmdline are correctly ignored", .config = { [TC_UNAME_R] = "4.4.4", @@ -279,19 +273,7 @@ DEFINE_TEST(modprobe_param_kcmdline3, .modules_loaded = "", ); -static noreturn int modprobe_param_kcmdline4(const struct test *t) -{ - const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe"; - const char *const args[] = { - progname, - "-c", - NULL, - }; - - test_spawn_prog(progname, args); - exit(EXIT_FAILURE); -} -DEFINE_TEST(modprobe_param_kcmdline4, +DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline4, modprobe_param_kcmdline, .description = "check if unrelated strings in kcmdline are correctly ignored", .config = { [TC_UNAME_R] = "4.4.4", @@ -303,19 +285,7 @@ DEFINE_TEST(modprobe_param_kcmdline4, .modules_loaded = "", ); -static noreturn int modprobe_param_kcmdline5(const struct test *t) -{ - const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe"; - const char *const args[] = { - progname, - "-c", - NULL, - }; - - test_spawn_prog(progname, args); - exit(EXIT_FAILURE); -} -DEFINE_TEST(modprobe_param_kcmdline5, +DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline5, modprobe_param_kcmdline, .description = "check if params with spaces are parsed correctly from kcmdline", .config = { [TC_UNAME_R] = "4.4.4", @@ -327,20 +297,7 @@ DEFINE_TEST(modprobe_param_kcmdline5, .modules_loaded = "", ); - -static noreturn int modprobe_param_kcmdline6(const struct test *t) -{ - const char *progname = ABS_TOP_BUILDDIR "/tools/modprobe"; - const char *const args[] = { - progname, - "-c", - NULL, - }; - - test_spawn_prog(progname, args); - exit(EXIT_FAILURE); -} -DEFINE_TEST(modprobe_param_kcmdline6, +DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline6, modprobe_param_kcmdline, .description = "check if dots on other parts of kcmdline don't confuse our parser", .config = { [TC_UNAME_R] = "4.4.4", @@ -352,6 +309,30 @@ DEFINE_TEST(modprobe_param_kcmdline6, .modules_loaded = "", ); +DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline7, modprobe_param_kcmdline, + .description = "check if dots on other parts of kcmdline don't confuse our parser", + .config = { + [TC_UNAME_R] = "4.4.4", + [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline7", + }, + .output = { + .out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline7/correct.txt", + }, + .modules_loaded = "", + ); + +DEFINE_TEST_WITH_FUNC(modprobe_param_kcmdline8, modprobe_param_kcmdline, + .description = "check if dots on other parts of kcmdline don't confuse our parser", + .config = { + [TC_UNAME_R] = "4.4.4", + [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline8", + }, + .output = { + .out = TESTSUITE_ROOTFS "test-modprobe/module-param-kcmdline8/correct.txt", + }, + .modules_loaded = "", + ); + static noreturn int modprobe_force(const struct test *t) { diff --git a/testsuite/test-util.c b/testsuite/test-util.c index 621446b..fb8c9ef 100644 --- a/testsuite/test-util.c +++ b/testsuite/test-util.c @@ -229,4 +229,45 @@ DEFINE_TEST(test_addu64_overflow, ); +static int test_backoff_time(const struct test *t) +{ + unsigned long long delta; + + /* Check exponential increments */ + get_backoff_delta_msec(now_msec(), now_msec() + 10, &delta); + assert_return(delta == 1, EXIT_FAILURE); + get_backoff_delta_msec(now_msec(), now_msec() + 10, &delta); + assert_return(delta == 2, EXIT_FAILURE); + get_backoff_delta_msec(now_msec(), now_msec() + 10, &delta); + assert_return(delta == 4, EXIT_FAILURE); + get_backoff_delta_msec(now_msec(), now_msec() + 10, &delta); + assert_return(delta == 8, EXIT_FAILURE); + + { + unsigned long long t0, tend; + + /* Check tail */ + delta = 4; + tend = now_msec() + 3; + t0 = tend - 10; + get_backoff_delta_msec(t0, tend, &delta); + assert_return(delta == 2, EXIT_FAILURE); + tend = now_msec() + 1; + t0 = tend - 9; + get_backoff_delta_msec(t0, tend, &delta); + assert_return(delta == 1, EXIT_FAILURE); + tend = now_msec(); + t0 = tend - 10; + get_backoff_delta_msec(t0, tend, &delta); + assert_return(delta == 0, EXIT_FAILURE); + } + + return EXIT_SUCCESS; +} +DEFINE_TEST(test_backoff_time, + .description = "check implementation of get_backoff_delta_msec()", + .need_spawn = false, + ); + + TESTSUITE_MAIN(); diff --git a/testsuite/testsuite.c b/testsuite/testsuite.c index e46f3d8..6a2d296 100644 --- a/testsuite/testsuite.c +++ b/testsuite/testsuite.c @@ -37,6 +37,7 @@ #include "testsuite.h" static const char *ANSI_HIGHLIGHT_GREEN_ON = "\x1B[1;32m"; +static const char *ANSI_HIGHLIGHT_YELLOW_ON = "\x1B[1;33m"; static const char *ANSI_HIGHLIGHT_RED_ON = "\x1B[1;31m"; static const char *ANSI_HIGHLIGHT_OFF = "\x1B[0m"; @@ -50,6 +51,7 @@ static const struct option options[] = { }; #define OVERRIDE_LIBDIR ABS_TOP_BUILDDIR "/testsuite/.libs/" +#define TEST_TIMEOUT_USEC 2 * USEC_PER_SEC struct _env_config { const char *key; @@ -61,19 +63,6 @@ struct _env_config { [TC_DELETE_MODULE_RETCODES] = { S_TC_DELETE_MODULE_RETCODES, OVERRIDE_LIBDIR "delete_module.so" }, }; -#define USEC_PER_SEC 1000000ULL -#define USEC_PER_MSEC 1000ULL -#define TEST_TIMEOUT_USEC 2 * USEC_PER_SEC -static unsigned long long now_usec(void) -{ - struct timespec ts; - - if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) - return 0; - - return ts_usec(&ts); -} - static void help(void) { const struct option *itr; @@ -948,6 +937,14 @@ static inline int test_run_parent(const struct test *t, int fdout[2], int err; bool matchout, match_modules; + if (t->skip) { + LOG("%sSKIPPED%s: %s\n", + ANSI_HIGHLIGHT_YELLOW_ON, ANSI_HIGHLIGHT_OFF, + t->name); + err = EXIT_SUCCESS; + goto exit; + } + /* Close write-fds */ if (t->output.out != NULL) close(fdout[1]); diff --git a/testsuite/testsuite.h b/testsuite/testsuite.h index f190249..44d1730 100644 --- a/testsuite/testsuite.h +++ b/testsuite/testsuite.h @@ -109,6 +109,8 @@ struct test { const struct keyval *env_vars; bool need_spawn; bool expected_fail; + /* allow to skip tests that don't meet compile-time dependencies */ + bool skip; bool print_outputs; } __attribute__((aligned(8))); @@ -138,14 +140,16 @@ int test_run(const struct test *t); /* Test definitions */ -#define DEFINE_TEST(_name, ...) \ +#define DEFINE_TEST_WITH_FUNC(_name, _func, ...) \ static const struct test UNIQ(s##_name) \ __attribute__((used, section("kmod_tests"), aligned(8))) = { \ .name = #_name, \ - .func = _name, \ + .func = _func, \ ## __VA_ARGS__ \ }; +#define DEFINE_TEST(_name, ...) DEFINE_TEST_WITH_FUNC(_name, _name, __VA_ARGS__) + #define TESTSUITE_MAIN() \ extern struct test __start_kmod_tests[] __attribute__((weak, visibility("hidden"))); \ extern struct test __stop_kmod_tests[] __attribute__((weak, visibility("hidden"))); \ diff --git a/tools/depmod.c b/tools/depmod.c index 3f31cdf..364b7d4 100644 --- a/tools/depmod.c +++ b/tools/depmod.c @@ -51,8 +51,9 @@ static int verbose = DEFAULT_VERBOSE; static const char CFG_BUILTIN_KEY[] = "built-in"; static const char CFG_EXTERNAL_KEY[] = "external"; static const char *default_cfg_paths[] = { - "/run/depmod.d", SYSCONFDIR "/depmod.d", + "/run/depmod.d", + "/usr/local/lib/depmod.d", "/lib/depmod.d", NULL }; @@ -457,6 +458,11 @@ struct cfg_external { char path[]; }; +struct cfg_exclude { + struct cfg_exclude *next; + char exclude_dir[]; +}; + struct cfg { const char *kversion; char dirname[PATH_MAX]; @@ -468,6 +474,7 @@ struct cfg { struct cfg_override *overrides; struct cfg_search *searches; struct cfg_external *externals; + struct cfg_exclude *excludes; }; static enum search_type cfg_define_search_type(const char *path) @@ -579,6 +586,30 @@ static void cfg_external_free(struct cfg_external *ext) free(ext); } +static int cfg_exclude_add(struct cfg *cfg, const char *path) +{ + struct cfg_exclude *exc; + size_t len = strlen(path); + + exc = malloc(sizeof(struct cfg_exclude) + len + 1); + if (exc == NULL) { + ERR("exclude add: out of memory\n"); + return -ENOMEM; + } + memcpy(exc->exclude_dir, path, len + 1); + + DBG("exclude add: %s\n", path); + + exc->next = cfg->excludes; + cfg->excludes = exc; + return 0; +} + +static void cfg_exclude_free(struct cfg_exclude *exc) +{ + free(exc); +} + static int cfg_kernel_matches(const struct cfg *cfg, const char *pattern) { regex_t re; @@ -656,6 +687,11 @@ static int cfg_file_parse(struct cfg *cfg, const char *filename) } cfg_external_add(cfg, dir); + } else if (streq(cmd, "exclude")) { + const char *sp; + while ((sp = strtok_r(NULL, "\t ", &saveptr)) != NULL) { + cfg_exclude_add(cfg, sp); + } } else if (streq(cmd, "include") || streq(cmd, "make_map_files")) { INF("%s:%u: command %s not implemented yet\n", @@ -856,6 +892,12 @@ static void cfg_free(struct cfg *cfg) cfg->externals = cfg->externals->next; cfg_external_free(tmp); } + + while (cfg->excludes) { + struct cfg_exclude *tmp = cfg->excludes; + cfg->excludes = cfg->excludes->next; + cfg_exclude_free(tmp); + } } @@ -1228,6 +1270,25 @@ add: return 0; } +static bool should_exclude_dir(const struct cfg *cfg, const char *name) +{ + struct cfg_exclude *exc; + + if (name[0] == '.' && (name[1] == '\0' || + (name[1] == '.' && name[2] == '\0'))) + return true; + + if (streq(name, "build") || streq(name, "source")) + return true; + + for (exc = cfg->excludes; exc != NULL; exc = exc->next) { + if (streq(name, exc->exclude_dir)) + return true; + } + + return false; +} + static int depmod_modules_search_dir(struct depmod *depmod, DIR *d, size_t baselen, struct scratchbuf *s_path) { struct dirent *de; @@ -1239,11 +1300,9 @@ static int depmod_modules_search_dir(struct depmod *depmod, DIR *d, size_t basel size_t namelen; uint8_t is_dir; - if (name[0] == '.' && (name[1] == '\0' || - (name[1] == '.' && name[2] == '\0'))) - continue; - if (streq(name, "build") || streq(name, "source")) + if (should_exclude_dir(depmod->cfg, name)) continue; + namelen = strlen(name); if (scratchbuf_alloc(s_path, baselen + namelen + 2) < 0) { err = -ENOMEM; @@ -2345,6 +2404,103 @@ static int output_builtin_bin(struct depmod *depmod, FILE *out) return 0; } +static int flush_stream(FILE *in, int endchar) +{ + size_t i = 0; + int c; + + for (c = fgetc(in); + c != EOF && c != endchar && c != '\0'; + c = fgetc(in)) + ; + + return c == endchar ? i : 0; +} + +static int flush_stream_to(FILE *in, int endchar, char *dst, size_t dst_sz) +{ + size_t i = 0; + int c; + + for (c = fgetc(in); + c != EOF && c != endchar && c != '\0' && i < dst_sz; + c = fgetc(in)) + dst[i++] = c; + + if (i == dst_sz) { + WRN("Could not flush stream: %d. Partial content: %.*s\n", + ENOSPC, (int) dst_sz, dst); + i--; + } + + return c == endchar ? i : 0; +} + +static int output_builtin_alias_bin(struct depmod *depmod, FILE *out) +{ + FILE *in; + struct index_node *idx; + int ret; + + if (out == stdout) + return 0; + + in = dfdopen(depmod->cfg->dirname, "modules.builtin.modinfo", O_RDONLY, "r"); + if (in == NULL) + return 0; + + idx = index_create(); + if (idx == NULL) { + fclose(in); + return -ENOMEM; + } + + /* format: modname.key=value\0 */ + while (!feof(in) && !ferror(in)) { + char alias[PATH_MAX]; + char modname[PATH_MAX]; + char value[PATH_MAX]; + size_t len; + + len = flush_stream_to(in, '.', modname, sizeof(modname)); + modname[len] = '\0'; + if (!len) + continue; + + len = flush_stream_to(in, '=', value, sizeof(value)); + value[len] = '\0'; + if (!streq(value, "alias")) { + flush_stream(in, '\0'); + continue; + } + + len = flush_stream_to(in, '\0', value, sizeof(value)); + value[len] = '\0'; + if (!len) + continue; + + alias[0] = '\0'; + if (alias_normalize(value, alias, NULL) < 0) { + WRN("Unmatched bracket in %s\n", value); + continue; + } + + index_insert(idx, alias, modname, 0); + } + + if (ferror(in)) { + ret = -EINVAL; + } else { + index_write(idx, out); + ret = 0; + } + + index_destroy(idx); + fclose(in); + + return ret; +} + static int output_devname(struct depmod *depmod, FILE *out) { size_t i; @@ -2402,71 +2558,6 @@ static int output_devname(struct depmod *depmod, FILE *out) return 0; } -static int output_builtin_alias_bin(struct depmod *depmod, FILE *out) -{ - int ret = 0, count = 0; - struct index_node *idx; - struct kmod_list *l, *builtin = NULL; - - if (out == stdout) - return 0; - - idx = index_create(); - if (idx == NULL) - return -ENOMEM; - - ret = kmod_module_get_builtin(depmod->ctx, &builtin); - if (ret < 0) { - if (ret == -ENOENT) - ret = 0; - goto out; - } - - kmod_list_foreach(l, builtin) { - struct kmod_list *ll, *info_list = NULL; - struct kmod_module *mod = l->data; - const char *modname = kmod_module_get_name(mod); - - ret = kmod_module_get_info(mod, &info_list); - if (ret < 0) - goto out; - - kmod_list_foreach(ll, info_list) { - char alias[PATH_MAX]; - const char *key = kmod_module_info_get_key(ll); - const char *value = kmod_module_info_get_value(ll); - - if (!streq(key, "alias")) - continue; - - alias[0] = '\0'; - if (alias_normalize(value, alias, NULL) < 0) { - WRN("Unmatched bracket in %s\n", value); - continue; - } - - index_insert(idx, alias, modname, 0); - } - - kmod_module_info_free_list(info_list); - - index_insert(idx, modname, modname, 0); - count++; - } - -out: - /* do not bother writing the index if we are going to discard it */ - if (!ret) - index_write(idx, out); - - if (builtin) - kmod_module_unref_list(builtin); - - index_destroy(idx); - - return ret; -} - static int depmod_output(struct depmod *depmod, FILE *out) { static const struct depfile { diff --git a/tools/modinfo.c b/tools/modinfo.c index 0231bb0..d0aab20 100644 --- a/tools/modinfo.c +++ b/tools/modinfo.c @@ -178,7 +178,11 @@ static int modinfo_do(struct kmod_module *mod) is_builtin = (filename == NULL); if (is_builtin) { - printf("%-16s%s%c", "name:", kmod_module_get_name(mod), separator); + if (field == NULL) + printf("%-16s%s%c", "name:", + kmod_module_get_name(mod), separator); + else if (field != NULL && streq(field, "name")) + printf("%s%c", kmod_module_get_name(mod), separator); filename = "(builtin)"; } @@ -289,6 +293,24 @@ static int modinfo_path_do(struct kmod_ctx *ctx, const char *path) return err; } +static int modinfo_name_do(struct kmod_ctx *ctx, const char *name) +{ + struct kmod_module *mod = NULL; + int err; + + err = kmod_module_new_from_name_lookup(ctx, name, &mod); + if (err < 0 || mod == NULL) { + ERR("Module name %s not found.\n", name); + return err < 0 ? err : -ENOENT; + } + + err = modinfo_do(mod); + kmod_module_unref(mod); + + return err; +} + + static int modinfo_alias_do(struct kmod_ctx *ctx, const char *alias) { struct kmod_list *l, *list = NULL; @@ -314,7 +336,7 @@ static int modinfo_alias_do(struct kmod_ctx *ctx, const char *alias) return err; } -static const char cmdopts_s[] = "adlpn0F:k:b:Vh"; +static const char cmdopts_s[] = "adlpn0mF:k:b:Vh"; static const struct option cmdopts[] = { {"author", no_argument, 0, 'a'}, {"description", no_argument, 0, 'd'}, @@ -322,6 +344,7 @@ static const struct option cmdopts[] = { {"parameters", no_argument, 0, 'p'}, {"filename", no_argument, 0, 'n'}, {"null", no_argument, 0, '0'}, + {"modname", no_argument, 0, 'm'}, {"field", required_argument, 0, 'F'}, {"set-version", required_argument, 0, 'k'}, {"basedir", required_argument, 0, 'b'}, @@ -333,7 +356,7 @@ static const struct option cmdopts[] = { static void help(void) { printf("Usage:\n" - "\t%s [options] filename [args]\n" + "\t%s [options] <modulename|filename> [args]\n" "Options:\n" "\t-a, --author Print only 'author'\n" "\t-d, --description Print only 'description'\n" @@ -341,6 +364,7 @@ static void help(void) "\t-p, --parameters Print only 'parm'\n" "\t-n, --filename Print only 'filename'\n" "\t-0, --null Use \\0 instead of \\n\n" + "\t-m, --modname Handle argument as module name instead of alias or filename\n" "\t-F, --field=FIELD Print only provided FIELD\n" "\t-k, --set-version=VERSION Use VERSION instead of `uname -r`\n" "\t-b, --basedir=DIR Use DIR as filesystem root for /lib/modules\n" @@ -368,6 +392,7 @@ static int do_modinfo(int argc, char *argv[]) const char *kversion = NULL; const char *root = NULL; const char *null_config = NULL; + bool arg_is_modname = false; int i, err; for (;;) { @@ -394,6 +419,9 @@ static int do_modinfo(int argc, char *argv[]) case '0': separator = '\0'; break; + case 'm': + arg_is_modname = true; + break; case 'F': field = optarg; break; @@ -450,7 +478,9 @@ static int do_modinfo(int argc, char *argv[]) const char *name = argv[i]; int r; - if (is_module_filename(name)) + if (arg_is_modname) + r = modinfo_name_do(ctx, name); + else if (is_module_filename(name)) r = modinfo_path_do(ctx, name); else r = modinfo_alias_do(ctx, name); diff --git a/tools/modprobe.c b/tools/modprobe.c index 9387537..2a2ae21 100644 --- a/tools/modprobe.c +++ b/tools/modprobe.c @@ -32,6 +32,7 @@ #include <sys/wait.h> #include <shared/array.h> +#include <shared/util.h> #include <shared/macro.h> #include <libkmod/libkmod.h> @@ -54,14 +55,19 @@ static int use_blacklist = 0; static int force = 0; static int strip_modversion = 0; static int strip_vermagic = 0; -static int remove_dependencies = 0; +static int remove_holders = 0; +static unsigned long long wait_msec = 0; static int quiet_inuse = 0; -static const char cmdopts_s[] = "arRibfDcnC:d:S:sqvVh"; +static const char cmdopts_s[] = "arw:RibfDcnC:d:S:sqvVh"; static const struct option cmdopts[] = { {"all", no_argument, 0, 'a'}, + {"remove", no_argument, 0, 'r'}, {"remove-dependencies", no_argument, 0, 5}, + {"remove-holders", no_argument, 0, 5}, + {"wait", required_argument, 0, 'w'}, + {"resolve-alias", no_argument, 0, 'R'}, {"first-time", no_argument, 0, 3}, {"ignore-install", no_argument, 0, 'i'}, @@ -107,8 +113,11 @@ static void help(void) "\t be a module name to be inserted\n" "\t or removed (-r)\n" "\t-r, --remove Remove modules instead of inserting\n" - "\t --remove-dependencies Also remove modules depending on it\n" - "\t-R, --resolve-alias Only lookup and print alias and exit\n" + "\t --remove-dependencies Deprecated: use --remove-holders\n" + "\t --remove-holders Also remove module holders (use together with -r)\n" + "\t-w, --wait <MSEC> When removing a module, wait up to MSEC for\n" + "\t module's refcount to become 0 so it can be\n" + "\t removed (use together with -r)\n" "\t --first-time Fail if module already inserted or removed\n" "\t-i, --ignore-install Ignore install commands\n" "\t-i, --ignore-remove Ignore remove commands\n" @@ -120,6 +129,7 @@ static void help(void) "\t --force-vermagic Ignore module's version magic\n" "\n" "Query Options:\n" + "\t-R, --resolve-alias Only lookup and print alias and exit\n" "\t-D, --show-depends Only print module dependencies and exit\n" "\t-c, --showconfig Print out known configuration and exit\n" "\t-c, --show-config Same as --showconfig\n" @@ -320,10 +330,11 @@ end: static int rmmod_do_remove_module(struct kmod_module *mod) { const char *modname = kmod_module_get_name(mod); - struct kmod_list *deps, *itr; + unsigned long long interval_msec = 0, t0_msec = 0, + tend_msec = 0; int flags = 0, err; - SHOW("rmmod %s\n", kmod_module_get_name(mod)); + SHOW("rmmod %s\n", modname); if (dry_run) return 0; @@ -331,33 +342,55 @@ static int rmmod_do_remove_module(struct kmod_module *mod) if (force) flags |= KMOD_REMOVE_FORCE; - err = kmod_module_remove_module(mod, flags); - if (err == -EEXIST) { - if (!first_time) - err = 0; - else - LOG("Module %s is not in kernel.\n", modname); - } + if (wait_msec) + flags |= KMOD_REMOVE_NOLOG; - deps = kmod_module_get_dependencies(mod); - if (deps != NULL) { - kmod_list_foreach(itr, deps) { - struct kmod_module *dep = kmod_module_get_module(itr); - if (kmod_module_get_refcnt(dep) == 0) - rmmod_do_remove_module(dep); - kmod_module_unref(dep); + do { + err = kmod_module_remove_module(mod, flags); + if (err == -EEXIST) { + if (!first_time) + err = 0; + else + LOG("Module %s is not in kernel.\n", modname); + break; + } else if (err == -EAGAIN && wait_msec) { + unsigned long long until_msec; + + if (!t0_msec) { + t0_msec = now_msec(); + tend_msec = t0_msec + wait_msec; + interval_msec = 1; + } + + until_msec = get_backoff_delta_msec(t0_msec, tend_msec, + &interval_msec); + err = sleep_until_msec(until_msec); + + if (!t0_msec) + err = -ENOTSUP; + + if (err < 0) { + ERR("Failed to sleep: %s\n", strerror(-err)); + err = -EAGAIN; + break; + } + } else { + break; } - kmod_module_unref_list(deps); - } + } while (interval_msec); + + if (err < 0 && wait_msec) + ERR("could not remove '%s': %s\n", modname, strerror(-err)); return err; } -#define RMMOD_FLAG_DO_DEPENDENCIES 0x1 +#define RMMOD_FLAG_REMOVE_HOLDERS 0x1 #define RMMOD_FLAG_IGNORE_BUILTIN 0x2 static int rmmod_do_module(struct kmod_module *mod, int flags); -static int rmmod_do_deps_list(struct kmod_list *list, bool stop_on_errors) +/* Remove modules in reverse order */ +static int rmmod_do_modlist(struct kmod_list *list, bool stop_on_errors) { struct kmod_list *l; @@ -391,7 +424,8 @@ static int rmmod_do_module(struct kmod_module *mod, int flags) cmd = kmod_module_get_remove_commands(mod); } - if (cmd == NULL && !ignore_loaded) { + /* Quick check if module is loaded, otherwise there's nothing to do */ + if (!cmd && !ignore_loaded) { int state = kmod_module_get_initstate(mod); if (state < 0) { @@ -413,17 +447,20 @@ static int rmmod_do_module(struct kmod_module *mod, int flags) } } - rmmod_do_deps_list(post, false); + /* 1. @mod's post-softdeps in reverse order */ + rmmod_do_modlist(post, false); - if ((flags & RMMOD_FLAG_DO_DEPENDENCIES) && remove_dependencies) { - struct kmod_list *deps = kmod_module_get_dependencies(mod); + /* 2. Other modules holding @mod */ + if (flags & RMMOD_FLAG_REMOVE_HOLDERS) { + struct kmod_list *holders = kmod_module_get_holders(mod); - err = rmmod_do_deps_list(deps, true); + err = rmmod_do_modlist(holders, true); if (err < 0) goto error; } - if (!ignore_loaded && !cmd) { + /* 3. @mod itself, but check for refcnt first */ + if (!cmd && !ignore_loaded && !wait_msec) { int usage = kmod_module_get_refcnt(mod); if (usage > 0) { @@ -435,7 +472,7 @@ static int rmmod_do_module(struct kmod_module *mod, int flags) } } - if (cmd == NULL) + if (!cmd) err = rmmod_do_remove_module(mod); else err = command_do(mod, "remove", cmd, NULL); @@ -443,7 +480,22 @@ static int rmmod_do_module(struct kmod_module *mod, int flags) if (err < 0) goto error; - rmmod_do_deps_list(pre, false); + /* 4. Other modules that became unused: errors are non-fatal */ + if (!cmd) { + struct kmod_list *deps, *itr; + + deps = kmod_module_get_dependencies(mod); + kmod_list_foreach(itr, deps) { + struct kmod_module *dep = kmod_module_get_module(itr); + if (kmod_module_get_refcnt(dep) == 0) + rmmod_do_remove_module(dep); + kmod_module_unref(dep); + } + kmod_module_unref_list(deps); + } + + /* 5. @mod's pre-softdeps in reverse order: errors are non-fatal */ + rmmod_do_modlist(pre, false); error: kmod_module_unref_list(pre); @@ -468,7 +520,9 @@ static int rmmod(struct kmod_ctx *ctx, const char *alias) kmod_list_foreach(l, list) { struct kmod_module *mod = kmod_module_get_module(l); - err = rmmod_do_module(mod, RMMOD_FLAG_DO_DEPENDENCIES); + int flags = remove_holders ? RMMOD_FLAG_REMOVE_HOLDERS : 0; + + err = rmmod_do_module(mod, flags); kmod_module_unref(mod); if (err < 0) break; @@ -683,7 +737,7 @@ static int options_from_array(char **args, int nargs, char **output) static char **prepend_options_from_env(int *p_argc, char **orig_argv) { const char *p, *env = getenv("MODPROBE_OPTIONS"); - char **new_argv, *str_start, *str_end, *str, *s, *quote; + char **new_argv, *str_end, *str, *s, *quote; int i, argc = *p_argc; size_t envlen, space_count = 0; @@ -701,10 +755,10 @@ static char **prepend_options_from_env(int *p_argc, char **orig_argv) return NULL; new_argv[0] = orig_argv[0]; - str_start = str = (char *) (new_argv + argc + space_count + 3); + str = (char *) (new_argv + argc + space_count + 3); memcpy(str, env, envlen + 1); - str_end = str_start + envlen; + str_end = str + envlen; quote = NULL; for (i = 1, s = str; *s != '\0'; s++) { @@ -743,7 +797,7 @@ static char **prepend_options_from_env(int *p_argc, char **orig_argv) } memcpy(new_argv + i, orig_argv + 1, sizeof(char *) * (argc - 1)); - new_argv[i + argc] = NULL; + new_argv[i + argc - 1] = NULL; *p_argc = i + argc - 1; return new_argv; @@ -786,11 +840,18 @@ static int do_modprobe(int argc, char **orig_argv) do_remove = 1; break; case 5: - remove_dependencies = 1; + remove_holders = 1; break; - case 'R': - lookup_only = 1; + case 'w': { + char *endptr = NULL; + wait_msec = strtoul(optarg, &endptr, 0); + if (!*optarg || *endptr) { + ERR("unexpected wait value '%s'.\n", optarg); + err = -1; + goto done; + } break; + } case 3: first_time = 1; break; @@ -814,6 +875,9 @@ static int do_modprobe(int argc, char **orig_argv) dry_run = 1; do_show = 1; break; + case 'R': + lookup_only = 1; + break; case 'c': do_show_config = 1; break; |