From 794b2d2c753489635b922457a5ccb06669f37268 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Thu, 23 Jul 2015 10:35:06 +0200 Subject: [PATCH] Use dracut-install to install kernel modules dracut-install can now install kernel modules and their corresponding firmware files. --- Makefile | 1 + dracut-init.sh | 178 +------ dracut.sh | 8 +- install/dracut-install.c | 536 +++++++++++++++++++-- modules.d/50drm/module-setup.sh | 63 +-- modules.d/90crypt/module-setup.sh | 4 +- modules.d/90dm/module-setup.sh | 3 +- modules.d/90kernel-modules/module-setup.sh | 53 +- modules.d/90kernel-network-modules/module-setup.sh | 45 +- modules.d/90multipath/module-setup.sh | 39 +- modules.d/95iscsi/module-setup.sh | 41 +- modules.d/95nfs/module-setup.sh | 2 +- 12 files changed, 580 insertions(+), 393 deletions(-) diff --git a/Makefile b/Makefile index 8281f90..0a1ae6c 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,7 @@ install/util.o: install/util.c install/util.h install/macro.h install/log.h install/strv.o: install/strv.c install/strv.h install/util.h install/macro.h install/log.h install/dracut-install: $(DRACUT_INSTALL_OBJECTS) + $(CC) $(LDFLAGS) -o $@ $(DRACUT_INSTALL_OBJECTS) $(LDLIBS) -lkmod dracut-install: install/dracut-install ln -fs $< $@ diff --git a/dracut-init.sh b/dracut-init.sh index a8b78ce..e26d97a 100644 --- a/dracut-init.sh +++ b/dracut-init.sh @@ -218,6 +218,13 @@ dracut_install() { inst_multiple "$@" } +dracut_instmods() { + [[ $no_kernel = yes ]] && return + $DRACUT_INSTALL \ + ${initdir:+-D "$initdir"} ${loginstall:+-L "$loginstall"} ${hostonly:+-H} ${omit_drivers:+-N "$omit_drivers"} ${srcmods:+--kerneldir "$srcmods"} -m "$@" + (($? != 0)) && derror FAILED: $DRACUT_INSTALL ${initdir:+-D "$initdir"} ${loginstall:+-L "$loginstall"} ${hostonly:+-H} ${omit_drivers:+-N "$omit_drivers"} ${srcmods:+--kerneldir "$srcmods"} -m "$@" || : +} + inst_library() { local _hostonly_install if [[ "$1" == "-H" ]]; then @@ -847,11 +854,6 @@ install_kmod_with_fw() { [[ -e "${initdir}/lib/modules/$kernel/${1##*/lib/modules/$kernel/}" ]] \ && return 0 - if [[ $DRACUT_KERNEL_LAZY_HASHDIR ]] && [[ -e "$DRACUT_KERNEL_LAZY_HASHDIR/${1##*/}" ]]; then - read ret < "$DRACUT_KERNEL_LAZY_HASHDIR/${1##*/}" - return $ret - fi - if [[ $omit_drivers ]]; then local _kmod=${1##*/} _kmod=${_kmod%.ko*} @@ -876,9 +878,6 @@ install_kmod_with_fw() { inst_simple "$1" "/lib/modules/$kernel/${1##*/lib/modules/$kernel/}" ret=$? - [[ $DRACUT_KERNEL_LAZY_HASHDIR ]] && \ - [[ -d "$DRACUT_KERNEL_LAZY_HASHDIR" ]] && \ - echo $ret > "$DRACUT_KERNEL_LAZY_HASHDIR/${1##*/}" (($ret != 0)) && return $ret local _modname=${1##*/} _fwdir _found _fw @@ -925,51 +924,6 @@ dracut_kernel_post() { local _moddirname=${srcmods%%/lib/modules/*} local _pid - if [[ $DRACUT_KERNEL_LAZY_HASHDIR ]] && [[ -f "$DRACUT_KERNEL_LAZY_HASHDIR/lazylist" ]]; then - xargs -r modprobe -a ${_moddirname:+-d ${_moddirname}/} \ - --ignore-install --show-depends --set-version $kernel \ - < "$DRACUT_KERNEL_LAZY_HASHDIR/lazylist" 2>/dev/null \ - | sort -u \ - | while read _cmd _modpath _options || [ -n "$_cmd" ]; do - [[ $_cmd = insmod ]] || continue - echo "$_modpath" - done > "$DRACUT_KERNEL_LAZY_HASHDIR/lazylist.dep" - - ( - if [[ $DRACUT_INSTALL ]] && [[ -z $_moddirname ]]; then - xargs -r $DRACUT_INSTALL ${initdir:+-D "$initdir"} ${loginstall:+-L "$loginstall"} -a < "$DRACUT_KERNEL_LAZY_HASHDIR/lazylist.dep" - else - while read _modpath || [ -n "$_modpath" ]; do - local _destpath=$_modpath - [[ $_moddirname ]] && _destpath=${_destpath##$_moddirname/} - _destpath=${_destpath##*/lib/modules/$kernel/} - inst_simple "$_modpath" "/lib/modules/$kernel/${_destpath}" || exit $? - done < "$DRACUT_KERNEL_LAZY_HASHDIR/lazylist.dep" - fi - ) & - _pid=$(jobs -p | while read a || [ -n "$a" ]; do printf ":$a";done) - _pid=${_pid##*:} - - if [[ $DRACUT_INSTALL ]]; then - xargs -r modinfo -k $kernel -F firmware < "$DRACUT_KERNEL_LAZY_HASHDIR/lazylist.dep" \ - | while read line || [ -n "$line" ]; do - for _fwdir in $fw_dir; do - echo $_fwdir/$line; - done; - done | xargs -r $DRACUT_INSTALL ${initdir:+-D "$initdir"} ${loginstall:+-L "$loginstall"} -a -o - else - for _fw in $(xargs -r modinfo -k $kernel -F firmware < "$DRACUT_KERNEL_LAZY_HASHDIR/lazylist.dep"); do - for _fwdir in $fw_dir; do - [[ -d $_fwdir && -f $_fwdir/$_fw ]] || continue - inst_simple "$_fwdir/$_fw" "/lib/firmware/$_fw" - break - done - done - fi - - wait $_pid - fi - for _f in modules.builtin.bin modules.builtin modules.order; do [[ $srcmods/$_f ]] && inst_simple "$srcmods/$_f" "/lib/modules/$kernel/$_f" done @@ -981,7 +935,6 @@ dracut_kernel_post() { exit 1 fi - [[ $DRACUT_KERNEL_LAZY_HASHDIR ]] && rm -fr -- "$DRACUT_KERNEL_LAZY_HASHDIR" } [[ "$kernel_current" ]] || export kernel_current=$(uname -r) @@ -1038,113 +991,32 @@ find_kernel_modules () { find_kernel_modules_by_path drivers } -# instmods [-c [-s]] [ ... ] -# instmods [-c [-s]] -# install kernel modules along with all their dependencies. -# can be e.g. "=block" or "=drivers/usb/storage" instmods() { + # instmods [-c [-s]] [ ... ] + # instmods [-c [-s]] + # install kernel modules along with all their dependencies. + # can be e.g. "=block" or "=drivers/usb/storage" + # -c check + # -s silent + local _optional="-o" + local _silent + local _ret [[ $no_kernel = yes ]] && return - # called [sub]functions inherit _fderr - local _fderr=9 - local _check=no - local _silent=no if [[ $1 = '-c' ]]; then - _check=yes + _optional="" shift fi - if [[ $1 = '-s' ]]; then - _silent=yes + _silent=1 shift fi - - function inst1mod() { - local _ret=0 _mod="$1" - case $_mod in - =*) - ( [[ "$_mpargs" ]] && echo $_mpargs - find_kernel_modules_by_path "${_mod#=}" ) \ - | instmods - ((_ret+=$?)) - ;; - --*) _mpargs+=" $_mod" ;; - *) - _mod=${_mod##*/} - # Check for aliased modules - _modalias=$(modinfo -k $kernel -F filename $_mod 2> /dev/null) - _modalias=${_modalias%.ko*} - if [[ $_modalias ]] && [ "${_modalias##*/}" != "${_mod%.ko*}" ] ; then - _mod=${_modalias##*/} - fi - - # if we are already installed, skip this module and go on - # to the next one. - if [[ $DRACUT_KERNEL_LAZY_HASHDIR ]] && \ - [[ -f "$DRACUT_KERNEL_LAZY_HASHDIR/${_mod%.ko*}" ]]; then - read _ret <"$DRACUT_KERNEL_LAZY_HASHDIR/${_mod%.ko*}" - return $_ret - fi - - _mod=${_mod/-/_} - if [[ $omit_drivers ]] && [[ "$_mod" =~ $omit_drivers ]]; then - dinfo "Omitting driver ${_mod##$srcmods}" - return 0 - fi - - # If we are building a host-specific initramfs and this - # module is not already loaded, move on to the next one. - [[ $hostonly ]] \ - && ! module_is_host_only "$_mod" \ - && return 0 - - if [[ "$_check" = "yes" ]] || ! [[ $DRACUT_KERNEL_LAZY_HASHDIR ]]; then - # We use '-d' option in modprobe only if modules prefix path - # differs from default '/'. This allows us to use dracut with - # old version of modprobe which doesn't have '-d' option. - local _moddirname=${srcmods%%/lib/modules/*} - [[ -n ${_moddirname} ]] && _moddirname="-d ${_moddirname}/" - - # ok, load the module, all its dependencies, and any firmware - # it may require - for_each_kmod_dep install_kmod_with_fw $_mod \ - --set-version $kernel ${_moddirname} $_mpargs - ((_ret+=$?)) - else - [[ $DRACUT_KERNEL_LAZY_HASHDIR ]] && \ - echo ${_mod%.ko*} >> "$DRACUT_KERNEL_LAZY_HASHDIR/lazylist" - fi - ;; - esac - return $_ret - } - - function instmods_1() { - local _mod _mpargs - if (($# == 0)); then # filenames from stdin - while read _mod || [ -n "$_mod" ]; do - inst1mod "${_mod%.ko*}" || { - if [[ "$_check" == "yes" ]] && [[ "$_silent" == "no" ]]; then - dfatal "Failed to install module $_mod" - fi - } - done - fi - while (($# > 0)); do # filenames as arguments - inst1mod ${1%.ko*} || { - if [[ "$_check" == "yes" ]] && [[ "$_silent" == "no" ]]; then - dfatal "Failed to install module $1" - fi - } - shift - done - return 0 - } - - local _ret _filter_not_found='FATAL: Module .* not found.' - # Capture all stderr from modprobe to _fderr. We could use {var}>... - # redirections, but that would make dracut require bash4 at least. - eval "( instmods_1 \"\$@\" ) ${_fderr}>&1" \ - | while read line || [ -n "$line" ]; do [[ "$line" =~ $_filter_not_found ]] || echo $line;done | derror + if (($# == 0)); then + read -r -d '' -a args + set -- "${args[@]}" + fi + $DRACUT_INSTALL ${initdir:+-D "$initdir"} ${loginstall:+-L "$loginstall"} ${hostonly:+-H} ${omit_drivers:+-N "$omit_drivers"} ${_optional:+-o} ${_silent:+--silent} ${srcmods:+--kerneldir "$srcmods"} -m "$@" _ret=$? + (($_ret != 0)) && [[ -z "$_silent" ]] && derror FAILED: $DRACUT_INSTALL ${initdir:+-D "$initdir"} ${loginstall:+-L "$loginstall"} ${hostonly:+-H} ${omit_drivers:+-N "$omit_drivers"} ${_optional:+-o} ${_silent:+--silent} ${srcmods:+--kerneldir "$srcmods"} -m "$@" || : + [[ "$optional" ]] && return 0 return $_ret } diff --git a/dracut.sh b/dracut.sh index 37ae350..f6d0439 100755 --- a/dracut.sh +++ b/dracut.sh @@ -733,7 +733,7 @@ stdloglvl=$((stdloglvl + verbosity_mod_l)) [[ $mdadmconf_l ]] && mdadmconf=$mdadmconf_l [[ $lvmconf_l ]] && lvmconf=$lvmconf_l [[ $dracutbasedir ]] || dracutbasedir=/usr/lib/dracut -[[ $fw_dir ]] || fw_dir="/lib/firmware/updates /lib/firmware /lib/firmware/$kernel" +[[ $fw_dir ]] || fw_dir="/lib/firmware/updates:/lib/firmware:/lib/firmware/$kernel" [[ $tmpdir_l ]] && tmpdir="$tmpdir_l" [[ $tmpdir ]] || tmpdir=/var/tmp [[ $INITRD_COMPRESS ]] && compress=$INITRD_COMPRESS @@ -750,6 +750,7 @@ stdloglvl=$((stdloglvl + verbosity_mod_l)) [[ $kernel_image_l ]] && kernel_image="$kernel_image_l" # eliminate IFS hackery when messing with fw_dir +export DRACUT_FIRMWARE_PATH=${fw_dir// /:} fw_dir=${fw_dir//:/ } # check for logfile and try to create one if it doesn't exist @@ -839,7 +840,6 @@ trap ' # clean up after ourselves no matter how we die. trap 'exit 1;' SIGINT -export DRACUT_KERNEL_LAZY="1" export DRACUT_RESOLVE_LAZY="1" if [[ $print_cmdline ]]; then @@ -1436,9 +1436,9 @@ if [[ $no_kernel != yes ]]; then hostonly='' instmods -c $filesystems fi - dinfo "*** Installing kernel module dependencies and firmware ***" + dinfo "*** Installing kernel module dependencies ***" dracut_kernel_post - dinfo "*** Installing kernel module dependencies and firmware done ***" + dinfo "*** Installing kernel module dependencies done ***" if [[ $noimageifnotneeded == yes ]] && [[ $hostonly ]]; then if [[ ! -f "$initdir/lib/dracut/need-initqueue" ]] && \ diff --git a/install/dracut-install.c b/install/dracut-install.c index 3b48ba8..a20e06c 100644 --- a/install/dracut-install.c +++ b/install/dracut-install.c @@ -22,7 +22,7 @@ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif - +#undef _FILE_OFFSET_BITS #include #include #include @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include "log.h" #include "hashmap.h" @@ -48,16 +51,31 @@ static bool arg_hmac = false; static bool arg_createdir = false; static int arg_loglevel = -1; static bool arg_optional = false; +static bool arg_silent = false; static bool arg_all = false; +static bool arg_module = false; static bool arg_resolvelazy = false; static bool arg_resolvedeps = false; static bool arg_hostonly = false; static char *destrootdir = NULL; +static char *kerneldir = NULL; +static char **firmwaredirs = NULL; +static char **pathdirs; static char *logdir = NULL; static char *logfile = NULL; FILE *logfile_f = NULL; static Hashmap *items = NULL; static Hashmap *items_failed = NULL; +static regex_t mod_filter_path; +static regex_t mod_filter_nopath; +static regex_t mod_filter_symbol; +static regex_t mod_filter_nosymbol; +static regex_t mod_filter_noname; +static bool arg_mod_filter_path = false; +static bool arg_mod_filter_nopath = false; +static bool arg_mod_filter_symbol = false; +static bool arg_mod_filter_nosymbol = false; +static bool arg_mod_filter_noname = false; static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps, bool hashdst); @@ -516,6 +534,18 @@ void dracut_log_cp(const char *path) log_error("Could not append '%s' to logfile '%s': %m", path, logfile); } +static bool check_hashmap(Hashmap *hm, const char *item) +{ + char *existing; + existing = hashmap_get(hm, item); + if (existing) { + if (strcmp(existing, item) == 0) { + return true; + } + } + return false; +} + static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps, bool hashdst) { struct stat sb, db; @@ -524,26 +554,17 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res int ret; bool src_exists = true; char *i = NULL; - char *existing; log_debug("dracut_install('%s', '%s')", src, dst); - existing = hashmap_get(items_failed, src); - if (existing) { - if (strcmp(existing, src) == 0) { - log_debug("hash hit items_failed for '%s'", src); - return 1; - } + if (check_hashmap(items_failed, src)) { + log_debug("hash hit items_failed for '%s'", src); + return 1; } - if (hashdst) { - existing = hashmap_get(items, dst); - if (existing) { - if (strcmp(existing, dst) == 0) { - log_debug("hash hit items for '%s'", dst); - return 0; - } - } + if (hashdst && check_hashmap(items, dst)) { + log_debug("hash hit items for '%s'", dst); + return 0; } if (lstat(src, &sb) < 0) { @@ -678,7 +699,7 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res log_debug("dracut_install ret = %d", ret); log_info("cp '%s' '%s'", src, fulldstpath); - if (arg_hostonly) + if (arg_hostonly && !arg_module) mark_hostonly(dst); ret += cp(src, fulldstpath); @@ -751,6 +772,9 @@ static int parse_argv(int argc, char *argv[]) enum { ARG_VERSION = 0x100, + ARG_SILENT, + ARG_KERNELDIR, + ARG_FIRMWAREDIRS, ARG_DEBUG }; @@ -765,13 +789,22 @@ static int parse_argv(int argc, char *argv[]) {"optional", no_argument, NULL, 'o'}, {"hostonly", no_argument, NULL, 'H'}, {"all", no_argument, NULL, 'a'}, + {"module", no_argument, NULL, 'm'}, {"fips", no_argument, NULL, 'f'}, {"destrootdir", required_argument, NULL, 'D'}, {"logdir", required_argument, NULL, 'L'}, + {"mod-filter-path", required_argument, NULL, 'p'}, + {"mod-filter-nopath", required_argument, NULL, 'P'}, + {"mod-filter-symbol", required_argument, NULL, 's'}, + {"mod-filter-nosymbol", required_argument, NULL, 'S'}, + {"mod-filter-noname", required_argument, NULL, 'N'}, + {"silent", no_argument, NULL, ARG_SILENT}, + {"kerneldir", required_argument, NULL, ARG_KERNELDIR}, + {"firmwaredirs", required_argument, NULL, ARG_FIRMWAREDIRS}, {NULL, 0, NULL, 0} }; - while ((c = getopt_long(argc, argv, "adfhlL:oD:HR", options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "madfhlL:oD:HRp:P:s:S:N:", options, NULL)) != -1) { switch (c) { case ARG_VERSION: puts(PROGRAM_VERSION_STRING); @@ -782,6 +815,9 @@ static int parse_argv(int argc, char *argv[]) case ARG_DEBUG: arg_loglevel = LOG_DEBUG; break; + case ARG_SILENT: + arg_silent = true; + break; case 'v': arg_loglevel = LOG_INFO; break; @@ -797,12 +833,56 @@ static int parse_argv(int argc, char *argv[]) case 'a': arg_all = true; break; + case 'm': + arg_module = true; + break; case 'D': destrootdir = strdup(optarg); break; + case 'p': + if (regcomp(&mod_filter_path, optarg, REG_NOSUB|REG_EXTENDED) != 0) { + log_error("Module path filter %s is not a regular expression", optarg); + exit(EXIT_FAILURE); + } + arg_mod_filter_path = true; + break; + case 'P': + if (regcomp(&mod_filter_nopath, optarg, REG_NOSUB|REG_EXTENDED) != 0) { + log_error("Module path filter %s is not a regular expression", optarg); + exit(EXIT_FAILURE); + } + arg_mod_filter_nopath = true; + break; + case 's': + if (regcomp(&mod_filter_symbol, optarg, REG_NOSUB|REG_EXTENDED) != 0) { + log_error("Module symbol filter %s is not a regular expression", optarg); + exit(EXIT_FAILURE); + } + arg_mod_filter_symbol = true; + break; + case 'S': + if (regcomp(&mod_filter_nosymbol, optarg, REG_NOSUB|REG_EXTENDED) != 0) { + log_error("Module symbol filter %s is not a regular expression", optarg); + exit(EXIT_FAILURE); + } + arg_mod_filter_nosymbol = true; + break; + case 'N': + if (regcomp(&mod_filter_noname, optarg, REG_NOSUB|REG_EXTENDED) != 0) { + log_error("Module symbol filter %s is not a regular expression", optarg); + exit(EXIT_FAILURE); + } + arg_mod_filter_noname = true; + break; case 'L': logdir = strdup(optarg); break; + case ARG_KERNELDIR: + kerneldir = strdup(optarg); + break; + case ARG_FIRMWAREDIRS: + firmwaredirs = strv_split(optarg, ":"); + break; case 'f': arg_hmac = true; break; @@ -817,6 +897,22 @@ static int parse_argv(int argc, char *argv[]) } } + if (arg_module) { + if (!firmwaredirs) { + char *path = NULL; + + path = getenv("DRACUT_FIRMWARE_PATH"); + + if (path == NULL) { + log_error("Environment variable DRACUT_FIRMWARE_PATH is not set"); + exit(EXIT_FAILURE); + } + + log_debug("DRACUT_FIRMWARE_PATH=%s", path); + + firmwaredirs = strv_split(path, ":"); + } + } if (!optind || optind == argc) { log_error("No SOURCE argument given"); usage(EXIT_FAILURE); @@ -858,24 +954,11 @@ static int resolve_lazy(int argc, char **argv) static char **find_binary(const char *src) { - char *path = NULL; - _cleanup_strv_free_ char **p = NULL; char **ret = NULL; char **q; char *newsrc = NULL; - path = getenv("PATH"); - - if (path == NULL) { - log_error("PATH is not set"); - exit(EXIT_FAILURE); - } - - log_debug("PATH=%s", path); - - p = strv_split(path, ":"); - - STRV_FOREACH(q, p) { + STRV_FOREACH(q, pathdirs) { struct stat sb; int r; @@ -976,10 +1059,381 @@ static int install_all(int argc, char **argv) return r; } +static int install_firmware(struct kmod_module *mod) +{ + struct kmod_list *l, *list = NULL; + int ret; + + char **q; + + ret = kmod_module_get_info(mod, &list); + if (ret < 0) { + log_error("could not get modinfo from '%s': %s\n", + kmod_module_get_name(mod), strerror(-ret)); + return ret; + } + kmod_list_foreach(l, list) { + const char *key = kmod_module_info_get_key(l); + const char *value = NULL; + char *fwpath = NULL; + + if (!streq("firmware", key)) + continue; + + value = kmod_module_info_get_value(l); + log_debug("Firmware %s", value); + ret = -1; + STRV_FOREACH(q, firmwaredirs) { + struct stat sb; + int r; + + r = asprintf(&fwpath, "%s/%s", *q, value); + if (r < 0) { + log_error("Out of memory!"); + exit(EXIT_FAILURE); + } + + if (stat(fwpath, &sb) != 0) { + log_debug("stat(%s) != 0", fwpath); + free(fwpath); + fwpath = NULL; + continue; + } + + ret = dracut_install(fwpath, fwpath, false, false, true); + if (ret == 0) + log_debug("dracut_install '%s' OK", fwpath); + } + + if (ret != 0) { + log_info("Possible missing firmware %s for kernel module %s", value, kmod_module_get_name(mod)); + } + } + return 0; +} + +static bool check_module_symbols(struct kmod_module *mod) +{ + struct kmod_list *itr, *deplist = NULL; + + if (!arg_mod_filter_symbol && !arg_mod_filter_nosymbol) + return true; + + if (kmod_module_get_dependency_symbols(mod, &deplist) < 0) { + log_debug("kmod_module_get_dependency_symbols failed"); + if (arg_mod_filter_symbol) + return false; + return true; + } + + if (arg_mod_filter_nosymbol) { + kmod_list_foreach(itr, deplist) { + const char *symbol = kmod_module_symbol_get_symbol(itr); + // log_debug("Checking symbol %s", symbol); + if (regexec(&mod_filter_nosymbol, symbol, 0, NULL, 0) == 0) { + kmod_module_dependency_symbols_free_list(deplist); + log_debug("Module %s: symbol %s matched exclusion filter", kmod_module_get_name(mod), symbol); + return false; + } + } + } + + if (arg_mod_filter_symbol) { + kmod_list_foreach(itr, deplist) { + const char *symbol = kmod_module_dependency_symbol_get_symbol(itr); + // log_debug("Checking symbol %s", symbol); + if (regexec(&mod_filter_symbol, symbol, 0, NULL, 0) == 0) { + kmod_module_dependency_symbols_free_list(deplist); + log_debug("Module %s: symbol %s matched inclusion filter", kmod_module_get_name(mod), symbol); + return true; + } + } + kmod_module_dependency_symbols_free_list(deplist); + return false; + } + + kmod_module_dependency_symbols_free_list(deplist); + return true; +} + + +static bool check_module_path(const char *path) +{ + if (arg_mod_filter_nopath && (regexec(&mod_filter_nopath, path, 0, NULL, 0) == 0)) { + log_debug("Path %s matched exclusion filter", path); + return false; + } + + if (arg_mod_filter_path && (regexec(&mod_filter_path, path, 0, NULL, 0) != 0)) { + log_debug("Path %s matched inclusion filter", path); + return false; + } + return true; +} + +static int install_module(struct kmod_module *mod) +{ + int ret = 0; + int state; + struct kmod_list *itr, *modlist = NULL; + const char *path = NULL; + const char *name = NULL; + state = kmod_module_get_initstate(mod); + + name = kmod_module_get_name(mod); + if (arg_mod_filter_noname && (regexec(&mod_filter_noname, name, 0, NULL, 0) == 0)) + return 0; + + if (arg_hostonly && (state != KMOD_MODULE_BUILTIN) && (state != KMOD_MODULE_LIVE)) { + log_debug("dracut_install '%s' not hostonly", name); + return 0; + } + + path = kmod_module_get_path(mod); + if (!path) + return -ENOENT; + + if (check_hashmap(items_failed, path)) + return 1; + + if (check_hashmap(items, path)) + return 0; + + if (!check_module_path(path) || !check_module_symbols(mod)) { + log_debug("No symbol or patch match for '%s'", path); + return 0; + } + + log_debug("dracut_install '%s'", path); + ret = dracut_install(path, path, false, false, true); + if (ret == 0) { + log_debug("dracut_install '%s' OK", kmod_module_get_name(mod)); + } else if (!arg_optional) { + if (!arg_silent) + log_error("dracut_install '%s' ERROR", kmod_module_get_name(mod)); + return ret; + } + install_firmware(mod); + + modlist = kmod_module_get_dependencies(mod); + kmod_list_foreach(itr, modlist) { + mod = kmod_module_get_module(itr); + path = kmod_module_get_path(mod); + name = kmod_module_get_name(mod); + if (arg_mod_filter_noname && (regexec(&mod_filter_noname, name, 0, NULL, 0) == 0)) { + kmod_module_unref(mod); + continue; + } + ret = dracut_install(path, path, false, false, true); + if (ret == 0) { + log_debug("dracut_install '%s' OK", kmod_module_get_name(mod)); + install_firmware(mod); + } else { + log_error("dracut_install '%s' ERROR", kmod_module_get_name(mod)); + } + kmod_module_unref(mod); + } + kmod_module_unref_list(modlist); + + return ret; +} + +static int install_modules(int argc, char **argv) +{ + struct kmod_ctx *ctx = NULL; + struct kmod_list *itr, *modlist = NULL; + struct kmod_module *mod = NULL, *mod_o = NULL; + + const char *modname = NULL; + int i; + + ctx = kmod_new(kerneldir, NULL); + + for (i = 0; i < argc; i++) { + int r = 0; + int ret = 0; + log_debug("Handle module '%s'", argv[i]); + + if (argv[i][0] == '/') { + r = kmod_module_new_from_path(ctx, argv[i], &mod_o); + if (r < 0) { + log_debug("Failed to lookup modules path '%s': %m", argv[i]); + if (!arg_optional) + return -ENOENT; + continue; + } + /* Check, if we have to load another module with that name instead */ + modname = kmod_module_get_name(mod_o); + if (!modname) { + if (!arg_optional) { + if (!arg_silent) + log_error("Failed to get name for module '%s'", argv[i]); + return -ENOENT; + } + log_info("Failed to get name for module '%s'", argv[i]); + continue; + } + r = kmod_module_new_from_lookup(ctx, modname, &modlist); + kmod_module_unref(mod_o); + if (r < 0) { + if (!arg_optional) { + if (!arg_silent) + log_error("3 Failed to lookup alias '%s': %d", modname, r); + return -ENOENT; + } + log_info("3 Failed to lookup alias '%s': %d", modname, r); + continue; + } + if (!modlist) { + if (!arg_optional) { + if (!arg_silent) + log_error("Failed to find module '%s' %s", modname, argv[i]); + return -ENOENT; + } + log_info("Failed to find module '%s' %s", modname, argv[i]); + continue; + } + kmod_list_foreach(itr, modlist) { + mod = kmod_module_get_module(itr); + ret = install_module(mod); + } + kmod_module_unref_list(modlist); + modlist = 0; + } else if (argv[i][0] == '=') { + char *path1, *path2, *path3; + FTS *fts; + log_debug("Handling =%s", &argv[i][1]); + /* FIXME and add more paths*/ + { + int r; + r = asprintf(&path2, "%s/kernel/%s", kerneldir, &argv[i][1]); + if (r < 0) { + log_error("Out of memory!"); + exit(EXIT_FAILURE); + } + + r = asprintf(&path1, "%s/extra/%s", kerneldir, &argv[i][1]); + if (r < 0) { + log_error("Out of memory!"); + exit(EXIT_FAILURE); + } + + r = asprintf(&path3, "%s/updates/%s", kerneldir, &argv[i][1]); + if (r < 0) { + log_error("Out of memory!"); + exit(EXIT_FAILURE); + } + + char *paths[] = { path1, path2, path3, NULL }; + fts = fts_open(paths, FTS_COMFOLLOW|FTS_NOCHDIR|FTS_NOSTAT|FTS_LOGICAL, NULL); + } + for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) { + if((ftsent->fts_info == FTS_D) && !check_module_path(ftsent->fts_accpath)) { + fts_set(fts, ftsent, FTS_SKIP); + log_debug("Skipping %s", ftsent->fts_accpath); + continue; + } + if((ftsent->fts_info != FTS_F) && (ftsent->fts_info != FTS_SL)) { + log_debug("Ignoring %s", ftsent->fts_accpath); + continue; + } + log_debug("Handling %s", ftsent->fts_accpath); + r = kmod_module_new_from_path(ctx, ftsent->fts_accpath, &mod_o); + if (r < 0) { + log_debug("Failed to lookup modules path '%s': %m", ftsent->fts_accpath); + continue; + } +#if 1 + /* Check, if we have to load another module with that name instead */ + modname = kmod_module_get_name(mod_o); + if (!modname) { + log_error("Failed to get name for module '%s'", ftsent->fts_accpath); + continue; + } + r = kmod_module_new_from_lookup(ctx, modname, &modlist); + kmod_module_unref(mod_o); + if (r < 0) { + log_error("2 Failed to lookup alias '%s': %m", modname); + kmod_module_unref_list(modlist); + continue; + } + if (!modlist) { + log_error("Failed to find module '%s' %s", modname, ftsent->fts_accpath); + kmod_module_unref_list(modlist); + continue; + } + kmod_list_foreach(itr, modlist) { + mod = kmod_module_get_module(itr); + ret = install_module(mod); + kmod_module_unref(mod); + } + kmod_module_unref_list(modlist); + modlist = 0; +#else + ret = install_module(mod_o); + kmod_module_unref(mod_o); +#endif + + } + if (errno) { + log_error("FTS ERROR: %m"); + } + fts_close(fts); + free(path1); path1 = NULL; + free(path2); path2 = NULL; + free(path3); path3 = NULL; + } else { + char *modname = argv[i]; + if (endswith(modname, ".ko")) { + int len = strlen(modname); + modname[len-3]=0; + } + if (endswith(modname, ".ko.xz") || endswith(modname, ".ko.gz")) { + int len = strlen(modname); + modname[len-6]=0; + } + r = kmod_module_new_from_lookup(ctx, modname, &modlist); + if (r < 0) { + if (!arg_optional) { + if (!arg_silent) + log_error("Failed to lookup alias '%s': %m", modname); + return -ENOENT; + } + log_info("Failed to lookup alias '%s': %m", modname); + continue; + } + if (!modlist) { + if (!arg_optional) { + if (!arg_silent) + log_error("Failed to find module '%s'", modname); + return -ENOENT; + } + log_info("Failed to find module '%s'", modname); + continue; + } + kmod_list_foreach(itr, modlist) { + mod = kmod_module_get_module(itr); + ret = install_module(mod); + kmod_module_unref(mod); + } + kmod_module_unref_list(modlist); + modlist = 0; + } + + if ((ret != 0) && (!arg_optional)) { + if (!arg_silent) + log_error("ERROR: installing '%s'", argv[i]); + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; +} + int main(int argc, char **argv) { int r; char *i; + char *path = NULL; r = parse_argv(argc, argv); if (r <= 0) @@ -993,6 +1447,17 @@ int main(int argc, char **argv) log_open(); + path = getenv("PATH"); + + if (path == NULL) { + log_error("PATH is not set"); + exit(EXIT_FAILURE); + } + + log_debug("PATH=%s", path); + + pathdirs = strv_split(path, ":"); + umask(0022); if (destrootdir == NULL || strlen(destrootdir) == 0) { @@ -1059,7 +1524,9 @@ int main(int argc, char **argv) } } - if (arg_resolvelazy) { + if (arg_module) { + r = install_modules(argc - optind, &argv[optind]); + } else if (arg_resolvelazy) { r = resolve_lazy(argc - optind, &argv[optind]); } else if (arg_all || (argc - optind > 2) || ((argc - optind) == 1)) { r = install_all(argc - optind, &argv[optind]); @@ -1085,6 +1552,7 @@ int main(int argc, char **argv) hashmap_free(items_failed); free(destrootdir); - + strv_free(firmwaredirs); + strv_free(pathdirs); return r; } diff --git a/modules.d/50drm/module-setup.sh b/modules.d/50drm/module-setup.sh index 55a214e..e0b2959 100755 --- a/modules.d/50drm/module-setup.sh +++ b/modules.d/50drm/module-setup.sh @@ -15,40 +15,6 @@ installkernel() { local _modname # Include KMS capable drm drivers - drm_module_filter() { - local _drm_drivers='drm_crtc_init' - local _ret - # subfunctions inherit following FDs - local _merge=8 _side2=9 - function nmf1() { - local _fname _fcont - while read _fname || [ -n "$_fname" ]; do - case "$_fname" in - *.ko) _fcont="$(< $_fname)" ;; - *.ko.gz) _fcont="$(gzip -dc $_fname)" ;; - *.ko.xz) _fcont="$(xz -dc $_fname)" ;; - esac - [[ $_fcont =~ $_drm_drivers - && ! $_fcont =~ iw_handler_get_spy ]] \ - && echo "$_fname" - done - } - function rotor() { - local _f1 _f2 - while read _f1 || [ -n "$_f1" ]; do - echo "$_f1" - if read _f2; then - echo "$_f2" 1>&${_side2} - fi - done | nmf1 1>&${_merge} - } - # Use two parallel streams to filter alternating modules. - set +x - eval "( ( rotor ) ${_side2}>&1 | nmf1 ) ${_merge}>&1" - [[ $debug ]] && set -x - return 0 - } - if [[ "$(uname -p)" == arm* ]]; then # arm specific modules needed by drm instmods \ @@ -62,20 +28,19 @@ installkernel() { instmods amdkfd hyperv_fb - for _modname in $(find_kernel_modules_by_path drivers/gpu/drm \ - | drm_module_filter) ; do - # if the hardware is present, include module even if it is not currently loaded, - # as we could e.g. be in the installer; nokmsboot boot parameter will disable - # loading of the driver if needed - if [[ $hostonly ]] && modinfo -F alias $_modname | sed -e 's,\?,\.,g' -e 's,\*,\.\*,g' \ - | grep -qxf - /sys/bus/{pci/devices,soc/devices/soc?}/*/modalias 2>/dev/null; then - hostonly='' instmods $_modname - # if radeon.ko is installed, we want amdkfd also - if strstr "$_modname" radeon.ko; then - hostonly='' instmods amdkfd + # if the hardware is present, include module even if it is not currently loaded, + # as we could e.g. be in the installer; nokmsboot boot parameter will disable + # loading of the driver if needed + if [[ $hostonly ]]; then + for i in /sys/bus/{pci/devices,soc/devices/soc?}/*/modalias; do + [[ -e $i ]] || continue + if hostonly="" dracut_instmods -s "drm_crtc_init" $(<$i) 2>/dev/null; then + if strstr $(modinfo -F filename $(<$i) 2>/dev/null) radeon.ko; then + hostonly='' instmods amdkfd + fi fi - continue - fi - instmods $_modname - done + done + else + dracut_instmods -s "drm_crtc_init" "=drivers/gpu/drm" + fi } diff --git a/modules.d/90crypt/module-setup.sh b/modules.d/90crypt/module-setup.sh index 5d964f4..6c377ef 100755 --- a/modules.d/90crypt/module-setup.sh +++ b/modules.d/90crypt/module-setup.sh @@ -24,8 +24,10 @@ depends() { # called by dracut installkernel() { - instmods dm_crypt =crypto hostonly="" instmods drbg + arch=$(arch) + [[ $arch == x86_64 ]] && arch=x86 + instmods dm_crypt =crypto =drivers/crypto =arch/$arch/crypto } # called by dracut diff --git a/modules.d/90dm/module-setup.sh b/modules.d/90dm/module-setup.sh index 419e8b1..2b8e39f 100755 --- a/modules.d/90dm/module-setup.sh +++ b/modules.d/90dm/module-setup.sh @@ -13,8 +13,7 @@ depends() { # called by dracut installkernel() { - instmods =drivers/md - instmods dm_mod dm-cache dm-cache-mq dm-cache-cleaner + instmods =drivers/md dm_mod dm-cache dm-cache-mq dm-cache-cleaner } # called by dracut diff --git a/modules.d/90kernel-modules/module-setup.sh b/modules.d/90kernel-modules/module-setup.sh index 300adc7..e97d598 100755 --- a/modules.d/90kernel-modules/module-setup.sh +++ b/modules.d/90kernel-modules/module-setup.sh @@ -2,54 +2,25 @@ # called by dracut installkernel() { - if [[ -z $drivers ]]; then - block_module_filter() { - local _blockfuncs='ahci_platform_get_resources|ata_scsi_ioctl|scsi_add_host|blk_cleanup_queue|register_mtd_blktrans|scsi_esp_register|register_virtio_device|usb_stor_disconnect|mmc_add_host|sdhci_add_host' - # subfunctions inherit following FDs - local _merge=8 _side2=9 - function bmf1() { - local _f - while read _f || [ -n "$_f" ]; do case "$_f" in - *.ko) [[ $(< $_f) =~ $_blockfuncs ]] && echo "$_f" ;; - *.ko.gz) [[ $(gzip -dc <$_f) =~ $_blockfuncs ]] && echo "$_f" ;; - *.ko.xz) [[ $(xz -dc <$_f) =~ $_blockfuncs ]] && echo "$_f" ;; - esac - done - return 0 - } - function rotor() { - local _f1 _f2 - while read _f1 || [ -n "$_f1" ]; do - echo "$_f1" - if read _f2; then - echo "$_f2" 1>&${_side2} - fi - done | bmf1 1>&${_merge} - return 0 - } - # Use two parallel streams to filter alternating modules. - set +x - eval "( ( rotor ) ${_side2}>&1 | bmf1 ) ${_merge}>&1" - [[ $debug ]] && set -x - return 0 - } + local _blockfuncs='ahci_platform_get_resources|ata_scsi_ioctl|scsi_add_host|blk_cleanup_queue|register_mtd_blktrans|scsi_esp_register|register_virtio_device|usb_stor_disconnect|mmc_add_host|sdhci_add_host|scsi_add_host_with_dma' + if [[ -z $drivers ]]; then hostonly='' instmods \ sr_mod sd_mod scsi_dh ata_piix hid_generic unix \ - ehci-hcd ehci-pci ehci-platform \ + ehci-hcd ehci-pci \ ohci-hcd ohci-pci \ uhci-hcd \ xhci-hcd xhci-pci xhci-plat-hcd \ + ehci-platform + + instmods \ "=drivers/hid" \ "=drivers/input/serio" \ "=drivers/input/keyboard" \ - "=drivers/usb/storage" - - instmods \ yenta_socket scsi_dh_rdac scsi_dh_emc scsi_dh_alua \ atkbd i8042 usbhid firewire-ohci pcmcia hv-vmbus \ virtio virtio_blk virtio_ring virtio_pci virtio_scsi \ - "=drivers/pcmcia" =ide + "=drivers/pcmcia" =ide "=drivers/usb/storage" if [[ "$(uname -p)" == arm* ]]; then # arm specific modules @@ -62,20 +33,16 @@ installkernel() { ${NULL} fi - - find_kernel_modules | block_module_filter | instmods + dracut_instmods -s "${_blockfuncs}" "=drivers" # if not on hostonly mode, install all known filesystems, # if the required list is not set via the filesystems variable if ! [[ $hostonly ]]; then if [[ -z $filesystems ]]; then - silent_omit_drivers="kernel/fs/nfs|kernel/fs/nfsd|kernel/fs/lockd" \ - instmods '=fs' + dracut_instmods -P ".*/(kernel/fs/nfs|kernel/fs/nfsd|kernel/fs/lockd)/.*" '=fs' fi else - for i in "${host_fs_types[@]}"; do - hostonly='' instmods $i - done + hostonly='' instmods "${host_fs_types[@]}" fi fi : diff --git a/modules.d/90kernel-network-modules/module-setup.sh b/modules.d/90kernel-network-modules/module-setup.sh index 18d7d96..c004ff8 100755 --- a/modules.d/90kernel-network-modules/module-setup.sh +++ b/modules.d/90kernel-network-modules/module-setup.sh @@ -14,47 +14,14 @@ depends() { installkernel() { # Include wired net drivers, excluding wireless local _arch=$(uname -m) + local _net_drivers='eth_type_trans|register_virtio_device|usbnet_open' + local _unwanted_drivers='/(wireless|isdn|uwb|net/ethernet|net/phy|net/team)/' - net_module_filter() { - local _net_drivers='eth_type_trans|register_virtio_device|usbnet_open' - local _unwanted_drivers='/(wireless|isdn|uwb|net/ethernet|net/phy|net/team)/' - local _ret - # subfunctions inherit following FDs - local _merge=8 _side2=9 - function nmf1() { - local _fname _fcont - while read _fname; do - [[ $_fname =~ $_unwanted_drivers ]] && continue - case "$_fname" in - *.ko) _fcont="$(< $_fname)" ;; - *.ko.gz) _fcont="$(gzip -dc $_fname)" ;; - *.ko.xz) _fcont="$(xz -dc $_fname)" ;; - esac - [[ $_fcont =~ $_net_drivers - && ! $_fcont =~ iw_handler_get_spy ]] \ - && echo "$_fname" - done - return 0 - } - function rotor() { - local _f1 _f2 - while read _f1; do - echo "$_f1" - if read _f2; then - echo "$_f2" 1>&${_side2} - fi - done | nmf1 1>&${_merge} - return 0 - } - # Use two parallel streams to filter alternating modules. - set +x - eval "( ( rotor ) ${_side2}>&1 | nmf1 ) ${_merge}>&1" - [[ $debug ]] && set -x - return 0 - } + if [ "$_arch" = "s390" -o "$_arch" = "s390x" ]; then + _s390drivers="=drivers/s390/net" + fi - { find_kernel_modules_by_path drivers/net; if [ "$_arch" = "s390" -o "$_arch" = "s390x" ]; then find_kernel_modules_by_path drivers/s390/net; fi; } \ - | net_module_filter | instmods + dracut_instmods -P ".*${_unwanted_drivers}.*" -s "$_net_drivers" "=drivers/net" ${_s390drivers:+"$_s390drivers"} #instmods() will take care of hostonly instmods \ diff --git a/modules.d/90multipath/module-setup.sh b/modules.d/90multipath/module-setup.sh index a808678..27817bd 100755 --- a/modules.d/90multipath/module-setup.sh +++ b/modules.d/90multipath/module-setup.sh @@ -51,41 +51,14 @@ cmdline() { installkernel() { local _ret local _arch=$(uname -m) - mp_mod_filter() { - local _funcs='scsi_register_device_handler|dm_dirty_log_type_register|dm_register_path_selector|dm_register_target' - # subfunctions inherit following FDs - local _merge=8 _side2=9 - function bmf1() { - local _f - while read _f || [ -n "$_f" ]; do - case "$_f" in - *.ko) [[ $(< $_f) =~ $_funcs ]] && echo "$_f" ;; - *.ko.gz) [[ $(gzip -dc <$_f) =~ $_funcs ]] && echo "$_f" ;; - *.ko.xz) [[ $(xz -dc <$_f) =~ $_funcs ]] && echo "$_f" ;; - esac - done - return 0 - } + local _funcs='scsi_register_device_handler|dm_dirty_log_type_register|dm_register_path_selector|dm_register_target' + local _s390 - function rotor() { - local _f1 _f2 - while read _f1 || [ -n "$_f1" ]; do - echo "$_f1" - if read _f2; then - echo "$_f2" 1>&${_side2} - fi - done | bmf1 1>&${_merge} - return 0 - } - # Use two parallel streams to filter alternating modules. - set +x - eval "( ( rotor ) ${_side2}>&1 | bmf1 ) ${_merge}>&1" - [[ $debug ]] && set -x - return 0 - } + if [ "$_arch" = "s390" -o "$_arch" = "s390x" ]; then + _s390drivers="=drivers/s390/scsi" + fi - ( find_kernel_modules_by_path drivers/scsi; if [ "$_arch" = "s390" -o "$_arch" = "s390x" ]; then find_kernel_modules_by_path drivers/s390/scsi; fi; - find_kernel_modules_by_path drivers/md ) | mp_mod_filter | hostonly='' instmods + hostonly='' dracut_instmods -s "$_funcs" "=drivers/scsi" "=drivers/md" ${_s390drivers:+"$_s390drivers"} } # called by dracut diff --git a/modules.d/95iscsi/module-setup.sh b/modules.d/95iscsi/module-setup.sh index beb80e3..203e313 100755 --- a/modules.d/95iscsi/module-setup.sh +++ b/modules.d/95iscsi/module-setup.sh @@ -157,44 +157,17 @@ depends() { # called by dracut installkernel() { local _arch=$(uname -m) + local _funcs='iscsi_register_transport' instmods bnx2i qla4xxx cxgb3i cxgb4i be2iscsi hostonly="" instmods iscsi_tcp iscsi_ibft crc32c iscsi_boot_sysfs - iscsi_module_filter() { - local _funcs='iscsi_register_transport' - # subfunctions inherit following FDs - local _merge=8 _side2=9 - function bmf1() { - local _f - while read _f || [ -n "$_f" ]; do - case "$_f" in - *.ko) [[ $(< $_f) =~ $_funcs ]] && echo "$_f" ;; - *.ko.gz) [[ $(gzip -dc <$_f) =~ $_funcs ]] && echo "$_f" ;; - *.ko.xz) [[ $(xz -dc <$_f) =~ $_funcs ]] && echo "$_f" ;; - esac - done - return 0 - } - - function rotor() { - local _f1 _f2 - while read _f1 || [ -n "$_f1" ]; do - echo "$_f1" - if read _f2; then - echo "$_f2" 1>&${_side2} - fi - done | bmf1 1>&${_merge} - return 0 - } - # Use two parallel streams to filter alternating modules. - set +x - eval "( ( rotor ) ${_side2}>&1 | bmf1 ) ${_merge}>&1" - [[ $debug ]] && set -x - return 0 - } - { find_kernel_modules_by_path drivers/scsi; if [ "$_arch" = "s390" -o "$_arch" = "s390x" ]; then find_kernel_modules_by_path drivers/s390/scsi; fi;} \ - | iscsi_module_filter | instmods + if [ "$_arch" = "s390" -o "$_arch" = "s390x" ]; then + _s390drivers="=drivers/s390/scsi" + fi + + dracut_instmods -s "$_funcs" "=drivers/scsi" ${_s390drivers:+"$_s390drivers"} + } # called by dracut diff --git a/modules.d/95nfs/module-setup.sh b/modules.d/95nfs/module-setup.sh index 6f039bd..9767e57 100755 --- a/modules.d/95nfs/module-setup.sh +++ b/modules.d/95nfs/module-setup.sh @@ -25,7 +25,7 @@ depends() { # called by dracut installkernel() { - instmods nfs sunrpc ipv6 nfsv2 nfsv3 nfsv4 nfs_acl nfs_layout_nfsv41_files + instmods =net/sunrpc =fs/nfs ipv6 nfs_acl nfs_layout_nfsv41_files } cmdline() {