diff --git a/Makefile.am b/Makefile.am index 504af5b2..4b9b0afa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,6 +32,8 @@ AM_CPPFLAGS = \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DDISTCONFDIR=\""$(distconfdir)"\" \ -DMODULE_DIRECTORY=\""$(module_directory)"\" \ + -DENABLE_ALTERNATIVE_DIR=\""$(enable_alternative_dir) + -DMODULE_ALTERNATIVE_DIRECTORY=\""$(module_alternative_directory)"\" \ ${zlib_CFLAGS} AM_CFLAGS = $(OUR_CFLAGS) @@ -198,7 +200,7 @@ MODULE_PLAYGROUND = testsuite/module-playground BUILD_MODULES = $(AM_V_GEN) $(top_srcdir)/scripts/setup-modules.sh $(top_srcdir) $(top_builddir) $(MODULE_PLAYGROUND) ROOTFS = testsuite/rootfs ROOTFS_PRISTINE = $(top_srcdir)/testsuite/rootfs-pristine -CREATE_ROOTFS = $(AM_V_GEN) $(top_srcdir)/scripts/setup-rootfs.sh $(ROOTFS_PRISTINE) $(ROOTFS) $(MODULE_PLAYGROUND) $(top_builddir)/config.h $(sysconfdir) $(module_directory) +CREATE_ROOTFS = $(AM_V_GEN) $(top_srcdir)/scripts/setup-rootfs.sh $(ROOTFS_PRISTINE) $(ROOTFS) $(MODULE_PLAYGROUND) $(top_builddir)/config.h $(sysconfdir) $(module_directory) $(module_alternative_directory) build-module-playground: $(BUILD_MODULES) diff --git a/configure.ac b/configure.ac index 7e35f1be..01d96463 100644 --- a/configure.ac +++ b/configure.ac @@ -89,8 +89,16 @@ AC_ARG_WITH([module_directory], [], [with_module_directory=/lib/modules]) AC_SUBST([module_directory], [$with_module_directory]) +AC_ARG_ENABLE([enable_alternative_dir], + AS_HELP_STRING([--enable-alternative-dir], [enable alternative module directory @<:@default=disabled@:>@]), + [], [enable_alternative_dir=no]) +AC_ARG_WITH([module_alternative_directory], + AS_HELP_STRING([--with-module-alternative_directory=DIR], [alternative directory in which to look for kernel modules @<:@default=/run/modules@:>@]), + [], [with_module_alternative_directory=/run/modules]) +AC_SUBST([module_alternative_directory], [$with_module_alternative_directory]) + # Check all directory arguments for consistency. -for ac_var in distconfdir module_directory +for ac_var in distconfdir module_directory module_alternative_directory do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -335,26 +343,28 @@ AC_MSG_RESULT([ $PACKAGE $VERSION ======= - module_directory: ${module_directory} - prefix: ${prefix} - sysconfdir: ${sysconfdir} - distconfdir: ${distconfdir} - libdir: ${libdir} - includedir: ${includedir} - bindir: ${bindir} - Bash completions dir: ${with_bashcompletiondir} - - compiler: ${CC} - cflags: ${with_cflags} ${CFLAGS} - ldflags: ${with_ldflags} ${LDFLAGS} - - tools: ${enable_tools} - logging: ${enable_logging} - compression: zstd=${with_zstd} xz=${with_xz} zlib=${with_zlib} - debug: ${enable_debug} - coverage: ${enable_coverage} - doc: ${enable_gtk_doc} - man: ${enable_manpages} - - features: ${with_features} + module_directory: ${module_directory} + enable_alternative_dir: ${enable_alternative_dir} + module_alternative_directory: ${module_alternative_directory} + prefix: ${prefix} + sysconfdir: ${sysconfdir} + distconfdir: ${distconfdir} + libdir: ${libdir} + includedir: ${includedir} + bindir: ${bindir} + Bash completions dir: ${with_bashcompletiondir} + + compiler: ${CC} + cflags: ${with_cflags} ${CFLAGS} + ldflags: ${with_ldflags} ${LDFLAGS} + + tools: ${enable_tools} + logging: ${enable_logging} + compression: zstd=${with_zstd} xz=${with_xz} zlib=${with_zlib} + debug: ${enable_debug} + coverage: ${enable_coverage} + doc: ${enable_gtk_doc} + man: ${enable_manpages} + + features: ${with_features} ]) diff --git a/man/Makefile.am b/man/Makefile.am index 6356d878..d72ba0a6 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -18,6 +18,7 @@ define generate_manpage sed -e 's|@SYSCONFDIR@|$(sysconfdir)|g' | \ sed -e 's|@DISTCONFDIR@|$(distconfdir)|g' | \ sed -e 's|@MODULE_DIRECTORY@|$(module_directory)|g' | \ + sed -e 's|@MODULE_ALTERNATIVE_DIRECTORY@|$(module_alternative_directory)|g' | \ $(SCDOC) > $@ endef diff --git a/man/insmod.8.scd b/man/insmod.8.scd index a5c42646..475f1910 100644 --- a/man/insmod.8.scd +++ b/man/insmod.8.scd @@ -18,6 +18,13 @@ Only the most general of error messages are reported: as the work of trying to link the module is now done inside the kernel, the *dmesg*(1) usually gives more information about errors. +*insmod* will look by default at modules stored in @MODULE_DIRECTORY@, but if +the folder is missing or the module is not found and kmod is compiled with +@enable-alternative-dir=true@, it will automatically look at the fallback +directory @MODULE_ALTERNATIVE_DIRECTORY@. +In that case, this program will only fail if the provided module is not found in +both directories. + # OPTIONS *-f*, *--force* diff --git a/man/lsmod.8.scd b/man/lsmod.8.scd index 339526df..519f51b0 100644 --- a/man/lsmod.8.scd +++ b/man/lsmod.8.scd @@ -13,6 +13,9 @@ lsmod - Show the status of modules in the Linux Kernel *lsmod* is a trivial program which nicely formats the contents of the _/proc/modules_, showing what kernel modules are currently loaded. +*lsmod* will look by default at modules stored in @MODULE_DIRECTORY@, but if +the folder is missing , it will automatically look at the fallback directory @MODULE_ALTERNATIVE_DIRECTORY@. + # OPTIONS *-s*, *--syslog* diff --git a/man/modinfo.8.scd b/man/modinfo.8.scd index c64e9c82..bff5a7aa 100644 --- a/man/modinfo.8.scd +++ b/man/modinfo.8.scd @@ -15,9 +15,19 @@ modinfo - Show information about a Linux Kernel module # DESCRIPTION *modinfo* extracts information from the Linux Kernel modules given on the -command line. If the module name is not a filename, then the @MODULE_DIRECTORY@/ -_version_ directory is searched, as is also done by *modprobe*(8) when loading -kernel modules. +command line. + +*modinfo* will search by default for modules stored in @MODULE_DIRECTORY@, but +if the folder is missing or the module is not found and kmod is compiled with +@enable-alternative-dir=true@, it will automatically look at the fallback +directory @MODULE_ALTERNATIVE_DIRECTORY@. +In that case, this program will only fail if the provided module is not found in +both directories. + +If the module name is not a filename, then the @MODULE_DIRECTORY@/ +_version_ directory is searched first, and then if enabled +@MODULE_ALTERNATIVE_DIRECTORY@/_version_, as is also done by *modprobe*(8) when +loading kernel modules. *modinfo* by default lists each attribute of the module in form _fieldname_ : _value_, for easy reading. The filename is listed the same way (although it's diff --git a/man/modprobe.8.scd b/man/modprobe.8.scd index 075704d8..2a18e496 100644 --- a/man/modprobe.8.scd +++ b/man/modprobe.8.scd @@ -44,6 +44,30 @@ is relative, it must explicitly start with "./". Note that this may fail when using a path to a module with dependencies not matching the installed *depmod* database. +*modprobe* will search by default for modules stored in @MODULE_DIRECTORY@, but +if the folder is missing or the module is not found and kmod is compiled with +@enable-alternative-dir=true@, it will automatically look at the fallback +directory @MODULE_ALTERNATIVE_DIRECTORY@. +In that case, this program will only fail if the provided module is not found in +both directories. + +The rationale for @MODULE_ALTERNATIVE_DIRECTORY@ is that this folder could be +used to reference pre-build or distro-provided default modules, and leave +@MODULE_DIRECTORY@ to point custom locally generated modules. In this way, the +first lookup always happens in @MODULE_DIRECTORY@ and will always reference the +local modules, but if for some reason some module is missing, +@MODULE_ALTERNATIVE_DIRECTORY@ could be used as fallback as source of default +standard modules. + +Another specific use case for this is with UKIs (Unified Kernel Image). +In this scenario, there is the possibility of not keeping two copies of the +kernel modules (one for initramfs and not for rootfs), but instead copy the ones +in initramfs to tmpfs, e.g. in /run/modules. Note that modules are not directly +copied in rootfs because it might also be read-only mounted. +This copy is feasible because an UKI ships kernel and initramfs together, so +there is no need to locally build anything, avoiding kernel-initramfs version +mismatches. + # OPTIONS *-a*, *--all* diff --git a/man/rmmod.8.scd b/man/rmmod.8.scd index 2f6bd56c..5a4b3b24 100644 --- a/man/rmmod.8.scd +++ b/man/rmmod.8.scd @@ -15,6 +15,13 @@ kernel (when module unloading support is provided). Most users will want to use *modprobe*(8) with the *-r* option instead since it removes unused dependent modules as well. +*rmmod* will look by default at modules stored in @MODULE_DIRECTORY@, but if +the folder is missing or the module is not found and kmod is compiled with +@enable-alternative-dir=true@, it will automatically look at the fallback +directory @MODULE_ALTERNATIVE_DIRECTORY@. +In that case, this program will only fail if the provided module is not found in +both directories. + When a list of modules is provided, the program will process them one at a time. If a module is not found, *rmmod* will immediately exit with an error code. Should the module removal fail, the program will log an error AND continue with diff --git a/meson.build b/meson.build index 7e7c0209..51b0762c 100644 --- a/meson.build +++ b/meson.build @@ -193,6 +193,9 @@ datadir = prefixdir / get_option('datadir') distconfdir = get_option('distconfdir') moduledir = get_option('moduledir') +enablealternativedir = get_option('enable-alternative-dir') +modulealternativedir = get_option('modulealternativedir') +cdata.set10('ENABLE_ALTERNATIVE_DIR', enablealternativedir) bashcompletiondir = get_option('bashcompletiondir') fishcompletiondir = get_option('fishcompletiondir') @@ -204,6 +207,7 @@ _customdirs = [ # The defaults are hard-coded due to historical reasons ['distconfdir', prefixdir / 'lib', 'DISTCONFDIR'], ['moduledir', '/lib/modules', 'MODULE_DIRECTORY'], + ['modulealternativedir', '/run/modules', 'MODULE_ALTERNATIVE_DIRECTORY'], ] foreach tuple : _customdirs @@ -472,6 +476,7 @@ _kmod_variables = [ 'sysconfdir=' + sysconfdir, 'distconfdir=' + distconfdir, 'module_directory=' + moduledir, + 'module_alternative_directory=' + modulealternativedir, ] # Don't (space) escape variables with space-separated lists, for consistency @@ -565,6 +570,7 @@ summary({ summary({ 'distconfdir' : distconfdir, 'moduledir' : moduledir, + 'modulealternativedir' : modulealternativedir, }, section : 'Kmod specific') summary({ @@ -574,13 +580,14 @@ summary({ }, section : 'Shell completions') summary({ - 'tools' : get_option('tools'), - 'logging' : get_option('logging'), - 'debug-messages' : get_option('debug-messages'), - 'build-tests' : get_option('build-tests'), - 'manpages' : get_option('manpages'), - 'docs' : get_option('docs'), - 'dlopen' : get_option('dlopen'), + 'tools' : get_option('tools'), + 'logging' : get_option('logging'), + 'debug-messages' : get_option('debug-messages'), + 'build-tests' : get_option('build-tests'), + 'enable-alternative-dir' : get_option('enable-alternative-dir'), + 'manpages' : get_option('manpages'), + 'docs' : get_option('docs'), + 'dlopen' : get_option('dlopen'), }, section : 'Options') summary({ diff --git a/meson_options.txt b/meson_options.txt index 3d41fae2..a1f06828 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -12,6 +12,19 @@ option( description : 'Directory to look for kernel modules. Default: /lib/modules', ) +option( + 'enable-alternative-dir', + type : 'boolean', + value : false, + description : 'Always use a fallback alternative directory to look for kernel modules. Default: false', +) + +option( + 'modulealternativedir', + type : 'string', + description : 'Alternative directory to look for kernel modules, if enable-alternative-dir is true. Otherwise unused. Default: /run/modules', +) + option( 'bashcompletiondir', type : 'string', diff --git a/scripts/setup-rootfs.sh b/scripts/setup-rootfs.sh index cbf94bc9..707fc87f 100755 --- a/scripts/setup-rootfs.sh +++ b/scripts/setup-rootfs.sh @@ -8,6 +8,7 @@ MODULE_PLAYGROUND=$3 CONFIG_H=$4 SYSCONFDIR=$5 MODULE_DIRECTORY=$6 +MODULE_ALTERNATIVE_DIRECTORY=$7 # create rootfs from rootfs-pristine diff --git a/testsuite/meson.build b/testsuite/meson.build index 1f49f698..e8851a75 100644 --- a/testsuite/meson.build +++ b/testsuite/meson.build @@ -28,6 +28,7 @@ create_rootfs = custom_target( meson.project_build_root() / 'config.h', sysconfdir, moduledir, + modulealternativedir, ], output : 'stamp-rootfs', console : true, diff --git a/testsuite/rootfs-pristine/test-modinfo/builtin/run/modules/6.11.0 b/testsuite/rootfs-pristine/test-modinfo/builtin/run/modules/6.11.0 new file mode 120000 index 00000000..7baa5694 --- /dev/null +++ b/testsuite/rootfs-pristine/test-modinfo/builtin/run/modules/6.11.0 @@ -0,0 +1 @@ +../../../external/lib/modules/4.4.4 \ No newline at end of file diff --git a/testsuite/rootfs-pristine/test-modinfo/external/run/modules/4.4.4 b/testsuite/rootfs-pristine/test-modinfo/external/run/modules/4.4.4 new file mode 120000 index 00000000..206db607 --- /dev/null +++ b/testsuite/rootfs-pristine/test-modinfo/external/run/modules/4.4.4 @@ -0,0 +1 @@ +../../../builtin/lib/modules/6.11.0 \ No newline at end of file diff --git a/testsuite/test-modinfo.c b/testsuite/test-modinfo.c index b936e2fa..c3e79acb 100644 --- a/testsuite/test-modinfo.c +++ b/testsuite/test-modinfo.c @@ -124,4 +124,49 @@ DEFINE_TEST(test_modinfo_builtin, .out = TESTSUITE_ROOTFS "test-modinfo/correct-builtin.txt", }); +static noreturn int test_modinfo_alternative(const struct test *t) +{ + const char *const args[] = { + // clang-format off + progname, + "-F", "filename", + "mod-simple", + NULL, + // clang-format on + }; + test_spawn_prog(progname, args); + exit(EXIT_FAILURE); +} +DEFINE_TEST(test_modinfo_alternative, + .description = "check if modinfo finds module in alternative directory", + .config = { + [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modinfo/builtin", + [TC_UNAME_R] = "6.11.0", + }, + .output = { + .out = TESTSUITE_ROOTFS "test-modinfo/correct-external.txt", + }); + +static noreturn int test_modinfo_alternative_inv(const struct test *t) +{ + const char *const args[] = { + // clang-format off + progname, + "intel_uncore", + NULL, + // clang-format on + }; + test_spawn_prog(progname, args); + exit(EXIT_FAILURE); +} +DEFINE_TEST(test_modinfo_alternative_inv, + .description = "check if modinfo finds module in alternative directory", + .config = { + [TC_ROOTFS] = TESTSUITE_ROOTFS "test-modinfo/external", + [TC_UNAME_R] = "4.4.4", + }, + .output = { + .out = TESTSUITE_ROOTFS "test-modinfo/correct-builtin.txt", + }); + TESTSUITE_MAIN(); diff --git a/tools/insmod.c b/tools/insmod.c index f7916b5f..ff65991d 100644 --- a/tools/insmod.c +++ b/tools/insmod.c @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -16,6 +17,9 @@ #include "kmod.h" +LOG_PTR_INIT(error_log) +#define SET_ERR(...) SET_LOG_PTR(error_log, __VA_ARGS__) + static const char cmdopts_s[] = "fsvVh"; static const struct option cmdopts[] = { // clang-format off @@ -58,16 +62,70 @@ static const char *mod_strerror(int err) } } -static int do_insmod(int argc, char *argv[]) +static int get_module_dirname(char *dirname_buf, size_t dirname_size, + const char *module_directory) +{ + struct utsname u; + int n; + + if (uname(&u) < 0) + return EXIT_FAILURE; + + n = snprintf(dirname_buf, dirname_size, "%s/%s", module_directory, + u.release); + if (n >= (int)dirname_size) { + SET_ERR("bad directory %s/%s: path too long\n", + module_directory, u.release); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static int _do_insmod(const char *dirname, const char *filename, + unsigned int flags, char *opts, int verbose) { struct kmod_ctx *ctx = NULL; + const char *null_config = NULL; struct kmod_module *mod; + int r; + + ctx = kmod_new(dirname, &null_config); + if (!ctx) { + SET_ERR("kmod_new() failed!\n"); + r = EXIT_FAILURE; + goto finish; + } + + log_setup_kmod_log(ctx, verbose); + + r = kmod_module_new_from_path(ctx, filename, &mod); + if (r < 0) { + SET_ERR("could not load module %s: %s\n", filename, strerror(-r)); + goto finish; + } + + r = kmod_module_insert_module(mod, flags, opts); + if (r < 0) + SET_ERR("could not insert module %s: %s\n", filename, mod_strerror(-r)); + + kmod_module_unref(mod); + +finish: + kmod_unref(ctx); + return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static int do_insmod(int argc, char *argv[]) +{ const char *filename; + char dirname_buf[PATH_MAX]; char *opts = NULL; + char *module_dir_error = NULL; + char *module_alt_dir_error = NULL; int verbose = LOG_ERR; int use_syslog = 0; int c, r = 0; - const char *null_config = NULL; unsigned int flags = 0; while ((c = getopt_long(argc, argv, cmdopts_s, cmdopts, NULL)) != -1) { @@ -115,33 +173,44 @@ static int do_insmod(int argc, char *argv[]) if (r < 0) goto end; - ctx = kmod_new(NULL, &null_config); - if (!ctx) { - ERR("kmod_new() failed!\n"); - r = EXIT_FAILURE; + /* Try first with MODULE_DIRECTORY */ + r = get_module_dirname(dirname_buf, sizeof(dirname_buf), + MODULE_DIRECTORY); + if (!r) + r = _do_insmod(dirname_buf, filename, flags, opts, verbose); + + if (r) + /* Store the error and print it *if* + * MODULE_ALTERNATIVE_DIRECTORY fails too */ + module_dir_error = pop_log_str(&error_log); + else + /* MODULE_DIRECTORY was succesful */ goto end; - } - log_setup_kmod_log(ctx, verbose); - - r = kmod_module_new_from_path(ctx, filename, &mod); - if (r < 0) { - ERR("could not load module %s: %s\n", filename, strerror(-r)); - goto end; + #if ENABLE_ALTERNATIVE_DIR + /* If not found, look at MODULE_ALTERNATIVE_DIRECTORY */ + r = get_module_dirname(dirname_buf, sizeof(dirname_buf), + MODULE_ALTERNATIVE_DIRECTORY); + if (!r) + r = _do_insmod(dirname_buf, filename, flags, opts, verbose); + + if (r) + /* Store the error and print it after MODULE_DIRECTORY */ + module_alt_dir_error = pop_log_str(&error_log); + else { + /* MODULE_ALTERNATIVE_DIRECTORY was succesful, no need to print + * module_dir_error */ + free(module_dir_error); + module_dir_error = NULL; } + #endif - r = kmod_module_insert_module(mod, flags, opts); - if (r < 0) - ERR("could not insert module %s: %s\n", filename, mod_strerror(-r)); - - kmod_module_unref(mod); - + PRINT_LOG_PTR(LOG_ERR, module_dir_error, module_alt_dir_error); end: - kmod_unref(ctx); free(opts); log_close(); - return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + return r; } const struct kmod_cmd kmod_cmd_compat_insmod = { diff --git a/tools/log.h b/tools/log.h index 72836be7..f1a91b12 100644 --- a/tools/log.h +++ b/tools/log.h @@ -21,3 +21,36 @@ void log_printf(int prio, const char *fmt, ...) _printf_format_(2, 3); struct kmod_ctx; void log_setup_kmod_log(struct kmod_ctx *ctx, int priority); + +#define LOG_PTR_INIT(name) static char *name = NULL; + +/* SET_LOG_PTR: try to allocate the error message into a string pointed by + * msg_ptr, but if it fails it will just print it as ERR(). + * msg_ptr must be defined by the caller and initialized to NULL */ +#define SET_LOG_PTR(msg_ptr, ...) \ + do { \ + if (msg_ptr) \ + free(msg_ptr); \ + if (asprintf(&msg_ptr, __VA_ARGS__) < 0) \ + ERR(__VA_ARGS__); \ + } while (0); + +static inline char *pop_log_str(char **msg_ptr) +{ + char *ret = *msg_ptr; + *msg_ptr = NULL; + return ret; +} + +#define PRINT_LOG_PTR(prio, module_dir, module_alt_dir) \ + do { \ + if (module_dir) { \ + log_printf(prio, "%s", module_dir); \ + if (module_alt_dir && \ + strcmp(module_dir, module_alt_dir) != 0) { \ + log_printf(prio, "%s", module_alt_dir); \ + free(module_alt_dir); \ + } \ + free(module_dir); \ + } \ + } while (0); diff --git a/tools/lsmod.c b/tools/lsmod.c index ea68d1dc..c8213cd6 100644 --- a/tools/lsmod.c +++ b/tools/lsmod.c @@ -10,11 +10,17 @@ #include #include #include +#include + +#include #include #include "kmod.h" +LOG_PTR_INIT(error_log) +#define SET_ERR(...) SET_LOG_PTR(error_log, __VA_ARGS__) + static const char cmdopts_s[] = "svVh"; static const struct option cmdopts[] = { // clang-format off @@ -38,59 +44,47 @@ static void help(void) program_invocation_short_name); } -static int do_lsmod(int argc, char *argv[]) +static int get_module_dirname(char *dirname_buf, size_t dirname_size, + const char *module_directory) { - struct kmod_ctx *ctx = NULL; - const char *null_config = NULL; - struct kmod_list *list, *itr; - int verbose = LOG_ERR; - int use_syslog = 0; - int err, c, r = 0; - - while ((c = getopt_long(argc, argv, cmdopts_s, cmdopts, NULL)) != -1) { - switch (c) { - case 's': - use_syslog = 1; - break; - case 'v': - verbose++; - break; - case 'h': - help(); - return EXIT_SUCCESS; - case 'V': - kmod_version(); - return EXIT_SUCCESS; - case '?': - return EXIT_FAILURE; - default: - ERR("unexpected getopt_long() value '%c'.\n", c); - return EXIT_FAILURE; - } + struct utsname u; + int n; + + if (uname(&u) < 0) + return EXIT_FAILURE; + + n = snprintf(dirname_buf, dirname_size, + "%s/%s", module_directory, u.release); + if (n >= (int)dirname_size) { + SET_ERR("bad directory %s/%s: path too long\n", + module_directory, u.release); + return EXIT_FAILURE; } - log_open(use_syslog); + return EXIT_SUCCESS; +} - if (optind < argc) { - ERR("too many arguments provided.\n"); - r = EXIT_FAILURE; - goto done; - } +static int _do_lsmod(const char *dirname, int verbose) +{ + struct kmod_ctx *ctx = NULL; + const char *null_config = NULL; + struct kmod_list *list, *itr; + int r, err; - ctx = kmod_new(NULL, &null_config); + ctx = kmod_new(dirname, &null_config); if (!ctx) { - ERR("kmod_new() failed!\n"); + SET_ERR("kmod_new() failed!\n"); r = EXIT_FAILURE; - goto done; + goto finish; } log_setup_kmod_log(ctx, verbose); err = kmod_module_new_from_loaded(ctx, &list); if (err < 0) { - ERR("could not get list of modules: %s\n", strerror(-err)); + SET_ERR("could not get list of modules: %s\n", strerror(-err)); r = EXIT_FAILURE; - goto done; + goto finish; } puts("Module Size Used by"); @@ -120,11 +114,87 @@ static int do_lsmod(int argc, char *argv[]) } kmod_module_unref_list(list); -done: +finish: kmod_unref(ctx); + return r; +} + +static int do_lsmod(int argc, char *argv[]) +{ + char dirname_buf[PATH_MAX]; + int verbose = LOG_ERR; + char *module_dir_error = NULL; + char *module_alt_dir_error = NULL; + int use_syslog = 0; + int c, r = 0; + + while ((c = getopt_long(argc, argv, cmdopts_s, cmdopts, NULL)) != -1) { + switch (c) { + case 's': + use_syslog = 1; + break; + case 'v': + verbose++; + break; + case 'h': + help(); + return EXIT_SUCCESS; + case 'V': + kmod_version(); + return EXIT_SUCCESS; + case '?': + return EXIT_FAILURE; + default: + ERR("unexpected getopt_long() value '%c'.\n", c); + return EXIT_FAILURE; + } + } + + log_open(use_syslog); + + if (optind < argc) { + ERR("too many arguments provided.\n"); + r = EXIT_FAILURE; + goto done; + } + + /* Try first with MODULE_DIRECTORY */ + r = get_module_dirname(dirname_buf, sizeof(dirname_buf), + MODULE_DIRECTORY); + if (!r) + r = _do_lsmod(dirname_buf, verbose); + + if (r) + /* Store the error and print it *if* + * MODULE_ALTERNATIVE_DIRECTORY fails too */ + module_dir_error = pop_log_str(&error_log); + else + /* MODULE_DIRECTORY was succesful */ + goto done; + + #if ENABLE_ALTERNATIVE_DIR + /* If not found, look at MODULE_ALTERNATIVE_DIRECTORY */ + r = get_module_dirname(dirname_buf, sizeof(dirname_buf), + MODULE_ALTERNATIVE_DIRECTORY); + if (!r) + r = _do_lsmod(dirname_buf, verbose); + + if (r) + /* Store the error and print it after MODULE_DIRECTORY */ + module_alt_dir_error = pop_log_str(&error_log); + else { + /* MODULE_ALTERNATIVE_DIRECTORY was succesful, no need to print + * module_dir_error */ + free(module_dir_error); + module_dir_error = NULL; + } + #endif + + PRINT_LOG_PTR(LOG_ERR, module_dir_error, module_alt_dir_error); +done: log_close(); - return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + return r; } const struct kmod_cmd kmod_cmd_compat_lsmod = { diff --git a/tools/modinfo.c b/tools/modinfo.c index 6f8147ea..3536e9e9 100644 --- a/tools/modinfo.c +++ b/tools/modinfo.c @@ -24,6 +24,9 @@ static char separator = '\n'; static const char *field = NULL; +LOG_PTR_INIT(error_log) +#define SET_ERR(...) SET_LOG_PTR(error_log, __VA_ARGS__) + struct param { struct param *next; const char *name; @@ -84,7 +87,7 @@ static int process_parm(const char *key, const char *value, struct param **param struct param *it; const char *colon = strchr(value, ':'); if (colon == NULL) { - ERR("Found invalid \"%s=%s\": missing ':'\n", key, value); + SET_ERR("Found invalid \"%s=%s\": missing ':'\n", key, value); return 0; } @@ -104,7 +107,7 @@ static int process_parm(const char *key, const char *value, struct param **param it = add_param(name, namelen, param, paramlen, type, typelen, params); if (it == NULL) { - ERR("Unable to add parameter: %m\n"); + SET_ERR("Unable to add parameter: %m\n"); return -ENOMEM; } @@ -188,8 +191,8 @@ static int modinfo_do(struct kmod_module *mod) */ return 0; } - ERR("could not get modinfo from '%s': %s\n", kmod_module_get_name(mod), - strerror(-err)); + SET_ERR("could not get modinfo from '%s': %s\n", + kmod_module_get_name(mod), strerror(-err)); return err; } @@ -265,7 +268,8 @@ static int modinfo_path_do(struct kmod_ctx *ctx, const char *path) struct kmod_module *mod; int err = kmod_module_new_from_path(ctx, path, &mod); if (err < 0) { - ERR("Module file %s not found.\n", path); + SET_ERR("Module file %s not found in %s.\n", path, + kmod_get_dirname(ctx)); return err; } err = modinfo_do(mod); @@ -280,7 +284,8 @@ static int modinfo_name_do(struct kmod_ctx *ctx, const char *name) err = kmod_module_new_from_name_lookup(ctx, name, &mod); if (err < 0 || mod == NULL) { - ERR("Module name %s not found.\n", name); + SET_ERR("Module name %s not found in %s.\n", name, + kmod_get_dirname(ctx)); return err < 0 ? err : -ENOENT; } @@ -295,12 +300,14 @@ static int modinfo_alias_do(struct kmod_ctx *ctx, const char *alias) struct kmod_list *l, *list = NULL; int err = kmod_module_new_from_lookup(ctx, alias, &list); if (err < 0) { - ERR("Module alias %s not found.\n", alias); + SET_ERR("Module alias %s not found in %s.\n", alias, + kmod_get_dirname(ctx)); return err; } if (list == NULL) { - ERR("Module %s not found.\n", alias); + SET_ERR("Module %s not found in %s.\n", alias, + kmod_get_dirname(ctx)); return -ENOENT; } @@ -364,16 +371,66 @@ static bool is_module_filename(const char *name) return false; } -static int do_modinfo(int argc, char *argv[]) +static int get_module_dirname(char *dirname_buf, size_t dirname_size, + const char *root, const char *module_directory, + const char *kversion) +{ + int n; + + n = snprintf(dirname_buf, dirname_size, + "%s%s/%s", root, module_directory, kversion); + if (n >= (int)dirname_size) { + SET_ERR("bad directory %s%s/%s: path too long\n", root, + module_directory, kversion); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static int _do_modinfo(const char *dirname, int argc, char *argv[], + bool arg_is_modname) { struct kmod_ctx *ctx; + const char *null_config = NULL; + int i, err; + + ctx = kmod_new(dirname, &null_config); + if (!ctx) { + SET_ERR("kmod_new() failed!\n"); + return EXIT_FAILURE; + } + + err = EXIT_SUCCESS; + for (i = optind; i < argc; i++) { + const char *name = argv[i]; + int r; + + 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); + + if (r < 0) + err = r; + } + + kmod_unref(ctx); + return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static int do_modinfo(int argc, char *argv[]) +{ char dirname_buf[PATH_MAX]; - const char *dirname = NULL; const char *kversion = NULL; const char *root = NULL; - const char *null_config = NULL; bool arg_is_modname = false; - int i, err; + char *module_dir_error = NULL; + char *module_alt_dir_error = NULL; + struct utsname u; + int err; for (;;) { int c, idx = 0; @@ -430,53 +487,50 @@ static int do_modinfo(int argc, char *argv[]) return EXIT_FAILURE; } - if (root != NULL || kversion != NULL) { - struct utsname u; - int n; - if (root == NULL) - root = ""; - if (kversion == NULL) { - if (uname(&u) < 0) { - ERR("uname() failed: %m\n"); - return EXIT_FAILURE; - } - kversion = u.release; - } - - n = snprintf(dirname_buf, sizeof(dirname_buf), - "%s" MODULE_DIRECTORY "/%s", root, kversion); - if (n >= (int)sizeof(dirname_buf)) { - ERR("bad directory %s" MODULE_DIRECTORY "/%s: path too long\n", - root, kversion); + if (root == NULL) + root = ""; + if (kversion == NULL){ + if (uname(&u) < 0) { + ERR("uname() failed: %m\n"); return EXIT_FAILURE; } - dirname = dirname_buf; + kversion = u.release; } - ctx = kmod_new(dirname, &null_config); - if (!ctx) { - ERR("kmod_new() failed!\n"); - return EXIT_FAILURE; + /* Try first with MODULE_DIRECTORY */ + err = get_module_dirname(dirname_buf, sizeof(dirname_buf), root, + MODULE_DIRECTORY, kversion); + if (!err) + err = _do_modinfo(dirname_buf, argc, argv, arg_is_modname); + + if (err) + /* Store the error and print it *if* + * MODULE_ALTERNATIVE_DIRECTORY fails too */ + module_dir_error = pop_log_str(&error_log); + else + /* MODULE_DIRECTORY was succesful */ + return EXIT_SUCCESS; + + #if ENABLE_ALTERNATIVE_DIR + /* If not found, look at MODULE_ALTERNATIVE_DIRECTORY */ + err = get_module_dirname(dirname_buf, sizeof(dirname_buf), root, + MODULE_ALTERNATIVE_DIRECTORY, kversion); + if (!err) + err = _do_modinfo(dirname_buf, argc, argv, arg_is_modname); + + if (err) + /* Store the error and print it after MODULE_DIRECTORY */ + module_alt_dir_error = pop_log_str(&error_log); + else { + /* MODULE_ALTERNATIVE_DIRECTORY was succesful, no need to print + * module_dir_error */ + free(module_dir_error); + module_dir_error = NULL; } + #endif - err = 0; - for (i = optind; i < argc; i++) { - const char *name = argv[i]; - int r; - - 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); - - if (r < 0) - err = r; - } - - kmod_unref(ctx); - return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; + PRINT_LOG_PTR(LOG_ERR, module_dir_error, module_alt_dir_error); + return err ? EXIT_FAILURE : EXIT_SUCCESS; } const struct kmod_cmd kmod_cmd_compat_modinfo = { diff --git a/tools/modprobe.c b/tools/modprobe.c index ec66e6fc..1404a472 100644 --- a/tools/modprobe.c +++ b/tools/modprobe.c @@ -27,9 +27,16 @@ #include "kmod.h" -static int log_priority = LOG_CRIT; +/* + * Do NOT increase priority higher than LOG_ERR, because the main logic is + * called twice and if the first time it errors (expected), the log function + * will call exit(EXIT_FAILURE) and the second call to the main function will + * not be executed. + */ +static int log_priority = LOG_ERR; static int use_syslog = 0; -#define LOG(...) log_printf(log_priority, __VA_ARGS__) +LOG_PTR_INIT(error_log) +#define LOG(...) SET_LOG_PTR(error_log, __VA_ARGS__) #define DEFAULT_VERBOSE LOG_WARNING static int verbose = DEFAULT_VERBOSE; @@ -210,7 +217,8 @@ static int show_modversions(struct kmod_ctx *ctx, const char *filename) struct kmod_module *mod; int err = kmod_module_new_from_path(ctx, filename, &mod); if (err < 0) { - LOG("Module %s not found.\n", filename); + LOG("Module %s not found in %s.\n", filename, + kmod_get_dirname(ctx)); return err; } @@ -237,7 +245,8 @@ static int show_exports(struct kmod_ctx *ctx, const char *filename) struct kmod_module *mod; int err = kmod_module_new_from_path(ctx, filename, &mod); if (err < 0) { - LOG("Module %s not found.\n", filename); + LOG("Module %s not found in %s.\n", filename, + kmod_get_dirname(ctx)); return err; } @@ -500,7 +509,7 @@ static int rmmod(struct kmod_ctx *ctx, const char *alias) return err; if (list == NULL) { - LOG("Module %s not found.\n", alias); + LOG("Module %s not found in %s.\n", alias, kmod_get_dirname(ctx)); err = -ENOENT; } @@ -748,14 +757,70 @@ static char **prepend_options_from_env(int *p_argc, char **orig_argv) return new_argv; } -static int do_modprobe(int argc, char **orig_argv) +static int get_module_dirname(char *dirname_buf, size_t dirname_size, + const char *root, const char *module_directory, + const char *kversion) +{ + int n; + + n = snprintf(dirname_buf, dirname_size, + "%s%s/%s", root, module_directory, kversion); + if (n >= (int)dirname_size) { + ERR("bad directory %s%s/%s: path too long\n", + root, module_directory, kversion); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static int _do_modprobe(const char *dirname, const char **config_paths, + char **args, int nargs, int do_show_config, + int do_show_modversions, int do_show_exports, + int do_remove, int use_all) { struct kmod_ctx *ctx; + int err; + + ctx = kmod_new(dirname, config_paths); + if (!ctx) { + ERR("kmod_new() failed!\n"); + return EXIT_FAILURE; + } + + log_setup_kmod_log(ctx, verbose); + + kmod_load_resources(ctx); + + if (do_show_config) + err = show_config(ctx); + else if (do_show_modversions) + err = show_modversions(ctx, args[0]); + else if (do_show_exports) + err = show_exports(ctx, args[0]); + else if (do_remove) + err = rmmod_all(ctx, args, nargs); + else if (use_all) + err = insmod_all(ctx, args, nargs); + else { + char *opts; + err = options_from_array(args + 1, nargs - 1, &opts); + if (err == 0) { + err = insmod(ctx, args[0], opts); + free(opts); + } + } + + kmod_unref(ctx); + return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static int do_modprobe(int argc, char **orig_argv) +{ char **args = NULL, **argv; const char **config_paths = NULL; int nargs = 0, n_config_paths = 0; char dirname_buf[PATH_MAX]; - const char *dirname = NULL; const char *root = NULL; const char *kversion = NULL; int use_all = 0; @@ -763,7 +828,10 @@ static int do_modprobe(int argc, char **orig_argv) int do_show_config = 0; int do_show_modversions = 0; int do_show_exports = 0; + char *module_dir_error = NULL; + char *module_alt_dir_error = NULL; int err; + struct utsname u; struct stat stat_buf; argv = prepend_options_from_env(&argc, orig_argv); @@ -906,63 +974,55 @@ static int do_modprobe(int argc, char **orig_argv) } } - if (root != NULL || kversion != NULL) { - struct utsname u; - int n; - if (root == NULL) - root = ""; - if (kversion == NULL) { - if (uname(&u) < 0) { - ERR("uname() failed: %m\n"); - err = -1; - goto done; - } - kversion = u.release; - } - n = snprintf(dirname_buf, sizeof(dirname_buf), - "%s" MODULE_DIRECTORY "/%s", root, kversion); - if (n >= (int)sizeof(dirname_buf)) { - ERR("bad directory %s" MODULE_DIRECTORY "/%s: path too long\n", - root, kversion); + if (root == NULL) + root = ""; + if (kversion == NULL) { + if (uname(&u) < 0) { + ERR("uname() failed: %m\n"); err = -1; goto done; } - dirname = dirname_buf; + kversion = u.release; } - ctx = kmod_new(dirname, config_paths); - if (!ctx) { - ERR("kmod_new() failed!\n"); - err = -1; + /* Try first with MODULE_DIRECTORY */ + err = get_module_dirname(dirname_buf, sizeof(dirname_buf), root, + MODULE_DIRECTORY, kversion); + if (!err) + err = _do_modprobe(dirname_buf, config_paths, args, nargs, + do_show_config, do_show_modversions, + do_show_exports, do_remove, use_all); + + if (err) + /* Store the error and print it *if* + * MODULE_ALTERNATIVE_DIRECTORY fails too */ + module_dir_error = pop_log_str(&error_log); + else + /* MODULE_DIRECTORY was succesful */ goto done; - } - - log_setup_kmod_log(ctx, verbose); - - kmod_load_resources(ctx); - if (do_show_config) - err = show_config(ctx); - else if (do_show_modversions) - err = show_modversions(ctx, args[0]); - else if (do_show_exports) - err = show_exports(ctx, args[0]); - else if (do_remove) - err = rmmod_all(ctx, args, nargs); - else if (use_all) - err = insmod_all(ctx, args, nargs); + #if ENABLE_ALTERNATIVE_DIR + /* If not found, look at MODULE_ALTERNATIVE_DIRECTORY */ + err = get_module_dirname(dirname_buf, sizeof(dirname_buf), root, + MODULE_ALTERNATIVE_DIRECTORY, kversion); + if (!err) + err = _do_modprobe(dirname_buf, config_paths, args, nargs, + do_show_config, do_show_modversions, + do_show_exports, do_remove, use_all); + + if (err) + /* Store the error and print it after MODULE_DIRECTORY */ + module_alt_dir_error = pop_log_str(&error_log); else { - char *opts; - err = options_from_array(args + 1, nargs - 1, &opts); - if (err == 0) { - err = insmod(ctx, args[0], opts); - free(opts); - } + /* MODULE_ALTERNATIVE_DIRECTORY was succesful, no need to print + * module_dir_error */ + free(module_dir_error); + module_dir_error = NULL; } - - kmod_unref(ctx); + #endif done: + PRINT_LOG_PTR(log_priority, module_dir_error, module_alt_dir_error); log_close(); if (argv != orig_argv) @@ -970,7 +1030,7 @@ static int do_modprobe(int argc, char **orig_argv) free(config_paths); - return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; + return err; } const struct kmod_cmd kmod_cmd_compat_modprobe = { diff --git a/tools/rmmod.c b/tools/rmmod.c index 0e00edec..9102d021 100644 --- a/tools/rmmod.c +++ b/tools/rmmod.c @@ -13,13 +13,18 @@ #include #include #include +#include +#include #include #include #include "kmod.h" +LOG_PTR_INIT(error_log) +#define SET_ERR(...) SET_LOG_PTR(error_log, __VA_ARGS__) + static const char cmdopts_s[] = "fsvVh"; static const struct option cmdopts[] = { // clang-format off @@ -54,25 +59,33 @@ static int check_module_inuse(struct kmod_module *mod) state = kmod_module_get_initstate(mod); if (state == KMOD_MODULE_BUILTIN) { - ERR("Module %s is builtin.\n", kmod_module_get_name(mod)); + SET_ERR("Module %s is builtin.\n", kmod_module_get_name(mod)); return -ENOENT; } else if (state < 0) { - ERR("Module %s is not currently loaded\n", kmod_module_get_name(mod)); + SET_ERR("Module %s is not currently loaded\n", + kmod_module_get_name(mod)); return -ENOENT; } holders = kmod_module_get_holders(mod); if (holders != NULL) { struct kmod_list *itr; + char *mod_list = NULL, *old_mod; - ERR("Module %s is in use by:", kmod_module_get_name(mod)); + if (asprintf(&mod_list, "Module %s is in use by:", kmod_module_get_name(mod)) < 0) + return -ENOMEM; kmod_list_foreach(itr, holders) { struct kmod_module *hm = kmod_module_get_module(itr); - ERR(" %s", kmod_module_get_name(hm)); + old_mod = mod_list; + ret = asprintf(&mod_list, "%s %s", old_mod, kmod_module_get_name(hm)); + free(old_mod); kmod_module_unref(hm); + if (ret < 0) + return -ENOMEM; } - ERR("\n"); + SET_ERR("%s\n", mod_list); + free(mod_list); kmod_module_unref_list(holders); return -EBUSY; @@ -80,62 +93,47 @@ static int check_module_inuse(struct kmod_module *mod) ret = kmod_module_get_refcnt(mod); if (ret > 0) { - ERR("Module %s is in use\n", kmod_module_get_name(mod)); + SET_ERR("Module %s is in use\n", kmod_module_get_name(mod)); return -EBUSY; } else if (ret == -ENOENT) { - ERR("Module unloading is not supported\n"); + SET_ERR("Module unloading is not supported\n"); } return ret; } -static int do_rmmod(int argc, char *argv[]) +static int get_module_dirname(char *dirname_buf, size_t dirname_size, + const char *module_directory) { - struct kmod_ctx *ctx; - const char *null_config = NULL; - int verbose = LOG_ERR; - int use_syslog = 0; - int flags = 0; - int i, c, r = 0; + struct utsname u; + int n; - while ((c = getopt_long(argc, argv, cmdopts_s, cmdopts, NULL)) != -1) { - switch (c) { - case 'f': - flags |= KMOD_REMOVE_FORCE; - break; - case 's': - use_syslog = 1; - break; - case 'v': - verbose++; - break; - case 'h': - help(); - return EXIT_SUCCESS; - case 'V': - kmod_version(); - return EXIT_SUCCESS; - case '?': - return EXIT_FAILURE; - default: - ERR("unexpected getopt_long() value '%c'.\n", c); - return EXIT_FAILURE; - } + if (uname(&u) < 0) + return EXIT_FAILURE; + + n = snprintf(dirname_buf, dirname_size, + "%s/%s", module_directory, u.release); + if (n >= (int)dirname_size) { + SET_ERR("bad directory %s/%s: path too long\n", + module_directory, u.release); + return EXIT_FAILURE; } - log_open(use_syslog); + return EXIT_SUCCESS; +} - if (optind >= argc) { - ERR("missing module name.\n"); - r = EXIT_FAILURE; - goto done; - } +static int _do_rmmod(const char *dirname, int argc, char *argv[], int flags, + int verbose) +{ + struct kmod_ctx *ctx = NULL; + const char *null_config = NULL; + int r, i; - ctx = kmod_new(NULL, &null_config); + ctx = kmod_new(dirname, &null_config); if (!ctx) { - ERR("kmod_new() failed!\n"); + SET_ERR("kmod_new() failed!\n"); r = EXIT_FAILURE; - goto done; + goto finish; } log_setup_kmod_log(ctx, verbose); @@ -152,7 +150,7 @@ static int do_rmmod(int argc, char *argv[]) err = kmod_module_new_from_name(ctx, arg, &mod); if (err < 0) { - ERR("could not use module %s: %s\n", arg, strerror(-err)); + SET_ERR("could not use module %s: %s\n", arg, strerror(-err)); r = EXIT_FAILURE; break; } @@ -165,19 +163,98 @@ static int do_rmmod(int argc, char *argv[]) err = kmod_module_remove_module(mod, flags); if (err < 0) { - ERR("could not remove module %s: %s\n", arg, strerror(-err)); + SET_ERR("could not remove module %s: %s\n", arg, strerror(-err)); r = EXIT_FAILURE; } kmod_module_unref(mod); } +finish: kmod_unref(ctx); + return r; +} + +static int do_rmmod(int argc, char *argv[]) +{ + char dirname_buf[PATH_MAX]; + int verbose = LOG_ERR; + char *module_dir_error = NULL; + char *module_alt_dir_error = NULL; + int use_syslog = 0; + int flags = 0; + int c, r = 0; + + while ((c = getopt_long(argc, argv, cmdopts_s, cmdopts, NULL)) != -1) { + switch (c) { + case 'f': + flags |= KMOD_REMOVE_FORCE; + break; + case 's': + use_syslog = 1; + break; + case 'v': + verbose++; + break; + case 'h': + help(); + return EXIT_SUCCESS; + case 'V': + kmod_version(); + return EXIT_SUCCESS; + case '?': + return EXIT_FAILURE; + default: + ERR("unexpected getopt_long() value '%c'.\n", c); + return EXIT_FAILURE; + } + } + + log_open(use_syslog); + + if (optind >= argc) { + ERR("missing module name.\n"); + r = EXIT_FAILURE; + goto done; + } + + /* Try first with MODULE_DIRECTORY */ + r = get_module_dirname(dirname_buf, sizeof(dirname_buf), + MODULE_DIRECTORY); + if (!r) + r = _do_rmmod(dirname_buf, argc, argv, flags, verbose); + + if (r) + /* Store the error and print it *if* + * MODULE_ALTERNATIVE_DIRECTORY fails too */ + module_dir_error = pop_log_str(&error_log); + else + /* MODULE_DIRECTORY was succesful */ + goto done; + + #if ENABLE_ALTERNATIVE_DIR + /* If not found, look at MODULE_ALTERNATIVE_DIRECTORY */ + r = get_module_dirname(dirname_buf, sizeof(dirname_buf), + MODULE_ALTERNATIVE_DIRECTORY); + if (!r) + r = _do_rmmod(dirname_buf, argc, argv, flags, verbose); + + if (r) + /* Store the error and print it after MODULE_DIRECTORY */ + module_alt_dir_error = pop_log_str(&error_log); + else { + /* MODULE_ALTERNATIVE_DIRECTORY was succesful, no need to print + * module_dir_error */ + free(module_dir_error); + module_dir_error = NULL; + } + #endif + PRINT_LOG_PTR(LOG_ERR, module_dir_error, module_alt_dir_error); done: log_close(); - return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + return r; } const struct kmod_cmd kmod_cmd_compat_rmmod = { diff --git a/tools/static-nodes.c b/tools/static-nodes.c index 0ecb9030..9c89d41c 100644 --- a/tools/static-nodes.c +++ b/tools/static-nodes.c @@ -141,12 +141,74 @@ static void help(void) } } +static int get_module_dirname(char *dirname_buf, size_t dirname_size, + const char *module_directory, + const char *kversion) +{ + int n; + + n = snprintf(dirname_buf, dirname_size, + "%s/%s/modules.devname", module_directory, kversion); + if (n >= (int)dirname_size) { + ERR("could not open %s/%s/modules.devname: path too long\n", + module_directory, kversion); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +static int _do_static_nodes(char *modules, const char *path, char *kver, + const struct static_nodes_format *format, FILE *out) +{ + char buf[PATH_MAX]; + FILE *in = NULL; + int ret = EXIT_SUCCESS; + + in = fopen(modules, "re"); + if (in == NULL) { + if (errno == ENOENT) { + WRN("%s/%s/modules.devname not found - ignoring\n", + path, kver); + } else { + ERR("could not open %s/%s/modules.devname - %m\n", + path, kver); + } + return EXIT_FAILURE; + } + + while (fgets(buf, sizeof(buf), in) != NULL) { + char modname[PATH_MAX]; + char devname[PATH_MAX]; + char type; + unsigned int maj, min; + int matches; + + if (buf[0] == '#') + continue; + + matches = + sscanf(buf, "%s %s %c%u:%u", modname, devname, &type, &maj, &min); + if (matches != 5 || (type != 'c' && type != 'b')) { + ERR("invalid devname entry: %s", buf); + ret = EXIT_FAILURE; + continue; + } + + format->write(out, modname, devname, type, maj, min); + } + + fclose(in); + + return ret; +} + static int do_static_nodes(int argc, char *argv[]) { struct utsname kernel; - char modules[PATH_MAX], buf[PATH_MAX]; + char modules[PATH_MAX]; const char *output = "/dev/stdout"; - FILE *in = NULL, *out = NULL; + FILE *out = NULL; const struct static_nodes_format *format = &static_nodes_format_human; int r, ret = EXIT_SUCCESS; @@ -199,73 +261,36 @@ static int do_static_nodes(int argc, char *argv[]) goto finish; } - r = snprintf(modules, sizeof(modules), MODULE_DIRECTORY "/%s/modules.devname", - kernel.release); - if (r >= (int)sizeof(modules)) { - fprintf(stderr, - "Error: could not open " MODULE_DIRECTORY - "/%s/modules.devname - path too long\n", - kernel.release); - ret = EXIT_FAILURE; - goto finish; - } - in = fopen(modules, "re"); - if (in == NULL) { - if (errno == ENOENT) { - fprintf(stderr, - "Warning: " MODULE_DIRECTORY - "/%s/modules.devname not found - ignoring\n", - kernel.release); - ret = EXIT_SUCCESS; - } else { - fprintf(stderr, - "Error: could not open " MODULE_DIRECTORY - "/%s/modules.devname - %m\n", - kernel.release); - ret = EXIT_FAILURE; - } - goto finish; - } - r = mkdir_parents(output, 0755); if (r < 0) { - fprintf(stderr, "Error: could not create parent directory for %s - %m.\n", + ERR("could not create parent directory for %s - %m.\n", output); ret = EXIT_FAILURE; - goto finish; } - out = fopen(output, "we"); if (out == NULL) { - fprintf(stderr, "Error: could not create %s - %m\n", output); + ERR("could not create %s - %m\n", output); ret = EXIT_FAILURE; goto finish; } - while (fgets(buf, sizeof(buf), in) != NULL) { - char modname[PATH_MAX]; - char devname[PATH_MAX]; - char type; - unsigned int maj, min; - int matches; - - if (buf[0] == '#') - continue; - - matches = - sscanf(buf, "%s %s %c%u:%u", modname, devname, &type, &maj, &min); - if (matches != 5 || (type != 'c' && type != 'b')) { - fprintf(stderr, "Error: invalid devname entry: %s", buf); - ret = EXIT_FAILURE; - continue; - } - - format->write(out, modname, devname, type, maj, min); - } - + /* Try first with MODULE_DIRECTORY */ + ret = get_module_dirname(modules, sizeof(modules), MODULE_DIRECTORY, + kernel.release); + if (!ret) + ret = _do_static_nodes(modules, MODULE_DIRECTORY, kernel.release, + format, out); + + #if ENABLE_ALTERNATIVE_DIR + /* Whether MODULE_DIRECTORY went well or not, look at + * MODULE_ALTERNATIVE_DIRECTORY */ + ret = get_module_dirname(modules, sizeof(modules), + MODULE_ALTERNATIVE_DIRECTORY, kernel.release); + if (!ret) + ret = _do_static_nodes(modules, MODULE_ALTERNATIVE_DIRECTORY, + kernel.release, format, out); + #endif finish: - if (in) - fclose(in); if (out) fclose(out); return ret;