aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:03:50 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:03:50 +0000
commit489d411efe4dd1a01fd8d773e77c809971ffac0b (patch)
tree3f7f12667a3d263d8abcfd93248790afa16cf241
parent533b0e3abce87f32f77ad06b188047870a5b26f0 (diff)
parent3a9039f4c7102de17c2be5b9ba5bf09b08aa8dd9 (diff)
downloadkmod-android14-mainline-permission-release.tar.gz
Change-Id: Ib91db799c637fd611445092293c0349c35b7f2b9
-rw-r--r--.gitignore3
-rw-r--r--METADATA8
-rw-r--r--Makefile.am20
-rw-r--r--NEWS126
-rw-r--r--README131
-rw-r--r--README.md128
-rw-r--r--configure.ac4
-rw-r--r--libkmod/docs/libkmod-sections.txt2
-rw-r--r--libkmod/libkmod-builtin.c4
-rw-r--r--libkmod/libkmod-config.c127
-rw-r--r--libkmod/libkmod-internal.h2
-rw-r--r--libkmod/libkmod-module.c168
-rw-r--r--libkmod/libkmod-signature.c6
-rw-r--r--libkmod/libkmod.c10
-rw-r--r--libkmod/libkmod.h5
-rw-r--r--libkmod/libkmod.sym1
-rw-r--r--man/depmod.d.xml17
-rw-r--r--man/modprobe.d.xml3
-rw-r--r--man/modprobe.xml17
-rw-r--r--shared/util.c72
-rw-r--r--shared/util.h17
-rw-r--r--testsuite/module-playground/.gitignore2
-rw-r--r--testsuite/module-playground/mod-simple.c18
-rwxr-xr-xtestsuite/populate-modules.sh52
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/correct.txt5
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/correct.txt5
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/module-param-kcmdline7/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline7/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/correct.txt5
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/correct.txt5
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/module-param-kcmdline7/proc/cmdline1
-rw-r--r--testsuite/rootfs-pristine/test-modprobe/module-param-kcmdline8/proc/cmdline1
-rw-r--r--testsuite/test-blacklist.c3
-rw-r--r--testsuite/test-depmod.c14
-rw-r--r--testsuite/test-initstate.c2
-rw-r--r--testsuite/test-modprobe.c95
-rw-r--r--testsuite/test-util.c41
-rw-r--r--testsuite/testsuite.c23
-rw-r--r--testsuite/testsuite.h8
-rw-r--r--tools/depmod.c231
-rw-r--r--tools/modinfo.c38
-rw-r--r--tools/modprobe.c144
42 files changed, 1113 insertions, 453 deletions
diff --git a/.gitignore b/.gitignore
index 2347858..cad86ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/METADATA b/METADATA
index 6422324..f19e9ca 100644
--- a/METADATA
+++ b/METADATA
@@ -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
diff --git a/NEWS b/NEWS
index ae56657..fe95103 100644
--- a/NEWS
+++ b/NEWS
@@ -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
=======
diff --git a/README b/README
deleted file mode 100644
index a0226e3..0000000
--- a/README
+++ /dev/null
@@ -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
diff --git a/README.md b/README.md
index d3b84bd..590c8a8 100644
--- a/README.md
+++ b/README.md
@@ -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;