]> git.ipfire.org Git - thirdparty/dracut.git/blobdiff - install/dracut-install.c
install/dracut-install.c: install module dependencies of dependencies
[thirdparty/dracut.git] / install / dracut-install.c
index 86c32dbb90f947d0480bbd0e8335af2eac1b1a63..d64de545a0acad5859691e96e501febeafaff190 100644 (file)
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
 /* dracut-install.c  -- install files and executables
 
    Copyright (C) 2012 Harald Hoyer
    along with this program; If not, see <http://www.gnu.org/licenses/>.
 */
 
-#define PROGRAM_VERSION_STRING "1"
+#define PROGRAM_VERSION_STRING "2"
 
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
-
+#undef _FILE_OFFSET_BITS
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
+#include <libkmod.h>
+#include <fts.h>
+#include <regex.h>
+#include <sys/utsname.h>
 
 #include "log.h"
 #include "hashmap.h"
 #include "util.h"
+#include "strv.h"
 
 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_modalias = 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 size_t kerneldirlen = 0;
+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 Hashmap *modules_loaded = 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);
+
+
+static inline void kmod_module_unrefp(struct kmod_module **p) {
+        if (*p)
+                kmod_module_unref(*p);
+}
+#define _cleanup_kmod_module_unref_ _cleanup_(kmod_module_unrefp)
+
+static inline void kmod_module_unref_listp(struct kmod_list **p) {
+        if (*p)
+                kmod_module_unref_list(*p);
+}
+#define _cleanup_kmod_module_unref_list_ _cleanup_(kmod_module_unref_listp)
+
+static inline void kmod_module_info_free_listp(struct kmod_list **p) {
+        if (*p)
+                kmod_module_info_free_list(*p);
+}
+#define _cleanup_kmod_module_info_free_list_ _cleanup_(kmod_module_info_free_listp)
+
+static inline void kmod_unrefp(struct kmod_ctx **p) {
+        kmod_unref(*p);
+}
+#define _cleanup_kmod_unref_ _cleanup_(kmod_unrefp)
+
+static inline void kmod_module_dependency_symbols_free_listp(struct kmod_list **p) {
+        if (*p)
+                kmod_module_dependency_symbols_free_list(*p);
+}
+#define _cleanup_kmod_module_dependency_symbols_free_list_ _cleanup_(kmod_module_dependency_symbols_free_listp)
+
+static inline void fts_closep(FTS **p) {
+        if (*p)
+                fts_close(*p);
+}
+#define _cleanup_fts_close_ _cleanup_(fts_closep)
+
 
-static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps);
 
 static size_t dir_len(char const *file)
 {
         size_t length;
+
+        if (!file)
+                return 0;
+
         /* Strip the basename and any redundant slashes before it.  */
-        for (length = strlen(file); 0 < length; length--)
-                if (file[length] == '/')
+        for (length = strlen(file) - 1; 0 < length; length--)
+                if (file[length] == '/' && file[length - 1] != '/')
                         break;
         return length;
 }
@@ -73,27 +139,38 @@ static char *convert_abs_rel(const char *from, const char *target)
 {
         /* we use the 4*MAXPATHLEN, which should not overrun */
         char relative_from[MAXPATHLEN * 4];
-        char *realtarget = NULL;
-        char *p, *q;
+        _cleanup_free_ char *realtarget = NULL;
+        _cleanup_free_ char *target_dir_p = NULL, *realpath_p = NULL;
         const char *realfrom = from;
-        int level = 0, fromlevel = 0, targetlevel = 0;
-        int l, i, rl;
-        int dirlen;
+        size_t level = 0, fromlevel = 0, targetlevel = 0;
+        int l;
+        size_t i, rl, dirlen;
+        int ret;
+
+        target_dir_p = strdup(target);
+        if (!target_dir_p)
+                return strdup(from);
 
-        p = strdup(target);
-        dirlen = dir_len(p);
-        p[dirlen] = '\0';
-        q = realpath(p, NULL);
+        dirlen = dir_len(target_dir_p);
+        target_dir_p[dirlen] = '\0';
+        realpath_p = realpath(target_dir_p, NULL);
 
-        if (q == NULL) {
-                free(p);
+        if (realpath_p == NULL) {
                 log_warning("convert_abs_rel(): target '%s' directory has no realpath.", target);
                 return strdup(from);
         }
 
-        asprintf(&realtarget, "%s/%s", q, &p[dirlen + 1]);
-        free(p);
-        free(q);
+        /* dir_len() skips double /'s e.g. //lib64, so we can't skip just one
+         * character - need to skip all leading /'s */
+        rl = strlen(target);
+        for (i = dirlen + 1; i < rl; ++i)
+                if (target_dir_p[i] != '/')
+                        break;
+        ret = asprintf(&realtarget, "%s/%s", realpath_p, &target_dir_p[i]);
+        if (ret < 0) {
+                log_error("Out of memory!");
+                exit(EXIT_FAILURE);
+        }
 
         /* now calculate the relative path from <from> to <target> and
            store it in <relative_from>
@@ -116,8 +193,6 @@ static char *convert_abs_rel(const char *from, const char *target)
                 if (realtarget[i] == '/')
                         level++;
 
-        free(realtarget);
-
         /* add "../" to the relative_from path, until the common pathname is
            reached */
         for (i = level; i < targetlevel; i++) {
@@ -149,18 +224,16 @@ static char *convert_abs_rel(const char *from, const char *target)
 static int ln_r(const char *src, const char *dst)
 {
         int ret;
-        const char *points_to = convert_abs_rel(src, dst);
+        _cleanup_free_ const char *points_to = convert_abs_rel(src, dst);
+
         log_info("ln -s '%s' '%s'", points_to, dst);
         ret = symlink(points_to, dst);
 
         if (ret != 0) {
                 log_error("ERROR: ln -s '%s' '%s': %m", points_to, dst);
-                free((char *)points_to);
                 return 1;
         }
 
-        free((char *)points_to);
-
         return 0;
 }
 
@@ -180,11 +253,11 @@ static bool use_clone = true;
 static int cp(const char *src, const char *dst)
 {
         int pid;
-        int ret;
+        int ret = 0;
 
-        if(use_clone) {
+        if (use_clone) {
                 struct stat sb;
-                int dest_desc, source_desc;
+                _cleanup_close_ int dest_desc = -1, source_desc = -1;
 
                 if (lstat(src, &sb) != 0)
                         goto normal_copy;
@@ -197,24 +270,34 @@ static int cp(const char *src, const char *dst)
                         goto normal_copy;
 
                 dest_desc =
-                        open(dst, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC,
-                             (sb.st_mode) & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO));
+                    open(dst, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC,
+                         (sb.st_mode) & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO));
 
                 if (dest_desc < 0) {
-                        close(source_desc);
                         goto normal_copy;
                 }
 
                 ret = clone_file(dest_desc, source_desc);
-                close(source_desc);
+
                 if (ret == 0) {
+                        struct timeval tv[2];
                         if (fchown(dest_desc, sb.st_uid, sb.st_gid) != 0)
-                                fchown(dest_desc, -1, sb.st_gid);
-                        close(dest_desc);
+                                if(fchown(dest_desc, (uid_t) - 1, sb.st_gid) != 0) {
+                                        if (geteuid() == 0)
+                                                log_error("Failed to chown %s: %m", dst);
+                                        else
+                                                log_info("Failed to chown %s: %m", dst);
+                                }
+
+                        tv[0].tv_sec = sb.st_atime;
+                        tv[0].tv_usec = 0;
+                        tv[1].tv_sec = sb.st_mtime;
+                        tv[1].tv_usec = 0;
+                        futimes(dest_desc, tv);
                         return ret;
                 }
                 close(dest_desc);
-
+                dest_desc = -1;
                 /* clone did not work, remove the file */
                 unlink(dst);
                 /* do not try clone again */
@@ -224,16 +307,93 @@ static int cp(const char *src, const char *dst)
  normal_copy:
         pid = fork();
         if (pid == 0) {
-                execlp("cp", "cp", "--reflink=auto", "--sparse=auto", "--preserve=mode", "-fL", src, dst, NULL);
+                if (geteuid() == 0)
+                        execlp("cp", "cp", "--reflink=auto", "--sparse=auto", "--preserve=mode,xattr,timestamps", "-fL", src, dst,
+                               NULL);
+                else
+                        execlp("cp", "cp", "--reflink=auto", "--sparse=auto", "--preserve=mode,timestamps", "-fL", src, dst,
+                               NULL);
                 _exit(EXIT_FAILURE);
         }
 
         while (waitpid(pid, &ret, 0) < 0) {
                 if (errno != EINTR) {
                         ret = -1;
+                        if (geteuid() == 0)
+                                log_error("Failed: cp --reflink=auto --sparse=auto --preserve=mode,xattr,timestamps -fL %s %s", src,
+                                          dst);
+                        else
+                                log_error("Failed: cp --reflink=auto --sparse=auto --preserve=mode,timestamps -fL %s %s", src,
+                                          dst);
                         break;
                 }
         }
+        log_debug("cp ret = %d", ret);
+        return ret;
+}
+
+static int library_install(const char *src, const char *lib)
+{
+        _cleanup_free_ char *p = NULL;
+        _cleanup_free_ char *pdir = NULL, *ppdir = NULL, *clib = NULL;
+        char *q;
+        int r, ret = 0;
+
+        p = strdup(lib);
+
+        r = dracut_install(p, p, false, false, true);
+        if (r != 0)
+                log_error("ERROR: failed to install '%s' for '%s'", p, src);
+        else
+                log_debug("Lib install: '%s'", p);
+        ret += r;
+
+        /* also install lib.so for lib.so.* files */
+        q = strstr(p, ".so.");
+        if (q) {
+                q[3] = '\0';
+
+                /* ignore errors for base lib symlink */
+                if (dracut_install(p, p, false, false, true) == 0)
+                        log_debug("Lib install: '%s'", p);
+        }
+
+        /* Also try to install the same library from one directory above.
+           This fixes the case, where only the HWCAP lib would be installed
+           # ldconfig -p|grep -F libc.so
+           libc.so.6 (libc6,64bit, hwcap: 0x0000001000000000, OS ABI: Linux 2.6.32) => /lib64/power6/libc.so.6
+           libc.so.6 (libc6,64bit, hwcap: 0x0000000000000200, OS ABI: Linux 2.6.32) => /lib64/power6x/libc.so.6
+           libc.so.6 (libc6,64bit, OS ABI: Linux 2.6.32) => /lib64/libc.so.6
+         */
+
+        free(p);
+        p = strdup(lib);
+
+        pdir = dirname(p);
+        if (!pdir)
+                return ret;
+
+        pdir = strdup(pdir);
+        ppdir = dirname(pdir);
+        if (!ppdir)
+                return ret;
+
+        ppdir = strdup(ppdir);
+
+        strcpy(p, lib);
+
+        clib = strjoin(ppdir, "/", basename(p), NULL);
+        if (dracut_install(clib, clib, false, false, true) == 0)
+                log_debug("Lib install: '%s'", clib);
+        /* also install lib.so for lib.so.* files */
+        q = strstr(clib, ".so.");
+        if (q) {
+                q[3] = '\0';
+
+                /* ignore errors for base lib symlink */
+                if (dracut_install(clib, clib, false, false, true) == 0)
+                        log_debug("Lib install: '%s'", p);
+        }
 
         return ret;
 }
@@ -242,17 +402,26 @@ static int resolve_deps(const char *src)
 {
         int ret = 0;
 
-        char *buf = malloc(LINE_MAX);
+        _cleanup_free_ char *buf = NULL;
         size_t linesize = LINE_MAX;
-        FILE *fptr;
-        char *cmd;
+        _cleanup_pclose_ FILE *fptr = NULL;
+        _cleanup_free_ char *cmd = NULL;
+
+        buf = malloc(LINE_MAX);
+        if (buf == NULL)
+                return -errno;
 
         if (strstr(src, ".so") == 0) {
-                int fd;
+                _cleanup_close_ int fd = -1;
                 fd = open(src, O_RDONLY | O_CLOEXEC);
-                read(fd, buf, LINE_MAX);
+                if (fd < 0)
+                        return -errno;
+
+                ret = read(fd, buf, LINE_MAX);
+                if (ret == -1)
+                        return -errno;
+
                 buf[LINE_MAX - 1] = '\0';
-                close(fd);
                 if (buf[0] == '#' && buf[1] == '!') {
                         /* we have a shebang */
                         char *p, *q;
@@ -260,7 +429,7 @@ static int resolve_deps(const char *src)
                         for (q = p; *q && (!isspace(*q)); q++) ;
                         *q = '\0';
                         log_debug("Script install: '%s'", p);
-                        ret = dracut_install(p, p, false, true);
+                        ret = dracut_install(p, p, false, true, false);
                         if (ret != 0)
                                 log_error("ERROR: failed to install '%s'", p);
                         return ret;
@@ -268,100 +437,185 @@ static int resolve_deps(const char *src)
         }
 
         /* run ldd */
-        asprintf(&cmd, "ldd %s", src);
+        ret = asprintf(&cmd, "ldd %s 2>&1", src);
+        if (ret < 0) {
+                log_error("Out of memory!");
+                exit(EXIT_FAILURE);
+        }
+
+        ret = 0;
+
         fptr = popen(cmd, "r");
 
         while (!feof(fptr)) {
-                char *p, *q;
+                char *p;
 
                 if (getline(&buf, &linesize, fptr) <= 0)
                         continue;
 
                 log_debug("ldd: '%s'", buf);
 
+                if (strstr(buf, "you do not have execution permission")) {
+                        log_error("%s", buf);
+                        ret += 1;
+                        break;
+                }
+
+                /* musl ldd */
+                if (strstr(buf, "Not a valid dynamic program"))
+                        break;
+
+                /* glibc */
+                if (strstr(buf, "cannot execute binary file"))
+                        break;
+
                 if (strstr(buf, "not a dynamic executable"))
                         break;
 
-                p = strstr(buf, "/");
+                if (strstr(buf, "loader cannot load itself"))
+                        break;
+
+                if (strstr(buf, "not regular file"))
+                        break;
+
+                if (strstr(buf, "cannot read header"))
+                        break;
+
+                if (strstr(buf, destrootdir))
+                        break;
+
+                p = strchr(buf, '/');
                 if (p) {
-                        int r;
+                        char *q;
+
                         for (q = p; *q && *q != ' ' && *q != '\n'; q++) ;
                         *q = '\0';
-                        r = dracut_install(p, p, false, false);
-                        if (r != 0)
-                                log_error("ERROR: failed to install '%s' for '%s'", p, src);
-                        else
-                                log_debug("Lib install: '%s'", p);
-                        ret += r;
 
-                        /* also install lib.so for lib.so.* files */
-                        q = strstr(p, ".so.");
-                        if (q) {
-                                q += 3;
-                                *q = '\0';
+                        ret += library_install(src, p);
 
-                                /* ignore errors for base lib symlink */
-                                if (dracut_install(p, p, false, false) == 0)
-                                        log_debug("Lib install: '%s'", p);
-                        }
                 }
         }
-        pclose(fptr);
 
         return ret;
 }
 
 /* Install ".<filename>.hmac" file for FIPS self-checks */
-static int hmac_install(const char *src, const char *dst)
+static int hmac_install(const char *src, const char *dst, const char *hmacpath)
 {
-        char *srcpath = strdup(src);
-        char *dstpath = strdup(dst);
-        char *srchmacname = NULL;
-        char *dsthmacname = NULL;
+        _cleanup_free_ char *srcpath = strdup(src);
+        _cleanup_free_ char *dstpath = strdup(dst);
+        _cleanup_free_ char *srchmacname = NULL;
+        _cleanup_free_ char *dsthmacname = NULL;
+        int ret;
+
+        if (!(srcpath && dstpath))
+                return -ENOMEM;
+
         size_t dlen = dir_len(src);
 
         if (endswith(src, ".hmac"))
                 return 0;
 
+        if (!hmacpath) {
+                hmac_install(src, dst, "/lib/fipscheck");
+                hmac_install(src, dst, "/lib64/fipscheck");
+                hmac_install(src, dst, "/lib/hmaccalc");
+                hmac_install(src, dst, "/lib64/hmaccalc");
+        }
+
         srcpath[dlen] = '\0';
         dstpath[dir_len(dst)] = '\0';
-        asprintf(&srchmacname, "%s/.%s.hmac", srcpath, &src[dlen + 1]);
-        asprintf(&dsthmacname, "%s/.%s.hmac", dstpath, &src[dlen + 1]);
+        if (hmacpath) {
+                ret = asprintf(&srchmacname, "%s/%s.hmac", hmacpath, &src[dlen + 1]);
+                if (ret < 0) {
+                        log_error("Out of memory!");
+                        exit(EXIT_FAILURE);
+                }
+
+                ret = asprintf(&dsthmacname, "%s/%s.hmac", hmacpath, &src[dlen + 1]);
+                if (ret < 0) {
+                        log_error("Out of memory!");
+                        exit(EXIT_FAILURE);
+                }
+        } else {
+                ret = asprintf(&srchmacname, "%s/.%s.hmac", srcpath, &src[dlen + 1]);
+                if (ret < 0) {
+                        log_error("Out of memory!");
+                        exit(EXIT_FAILURE);
+                }
+
+                ret = asprintf(&dsthmacname, "%s/.%s.hmac", dstpath, &src[dlen + 1]);
+                if (ret < 0) {
+                        log_error("Out of memory!");
+                        exit(EXIT_FAILURE);
+                }
+        }
         log_debug("hmac cp '%s' '%s')", srchmacname, dsthmacname);
-        dracut_install(srchmacname, dsthmacname, false, false);
-        free(dsthmacname);
-        free(srchmacname);
-        free(srcpath);
-        free(dstpath);
+        dracut_install(srchmacname, dsthmacname, false, false, true);
         return 0;
 }
 
-static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps)
+void mark_hostonly(const char *path)
+{
+        _cleanup_free_ char *fulldstpath = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        int ret;
+
+        ret = asprintf(&fulldstpath, "%s/lib/dracut/hostonly-files", destrootdir);
+        if (ret < 0) {
+                log_error("Out of memory!");
+                exit(EXIT_FAILURE);
+        }
+
+        f = fopen(fulldstpath, "a");
+
+        if (f == NULL) {
+                log_error("Could not open '%s' for writing.", fulldstpath);
+                return;
+        }
+
+        fprintf(f, "%s\n", path);
+}
+
+void dracut_log_cp(const char *path)
+{
+        int ret;
+        ret = fprintf(logfile_f, "%s\n", path);
+        if (ret < 0)
+                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;
-        char *dname = NULL;
-        char *fulldstpath = NULL;
-        char *fulldstdir = NULL;
+        _cleanup_free_ char *fulldstpath = NULL;
+        _cleanup_free_ char *fulldstdir = NULL;
         int ret;
         bool src_exists = true;
-        char *i, *existing;
+        char *i = NULL;
 
         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;
         }
 
-        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) {
@@ -375,9 +629,16 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
         }
 
         i = strdup(dst);
+        if (!i)
+                return -ENOMEM;
+
         hashmap_put(items, i, i);
 
-        asprintf(&fulldstpath, "%s%s", destrootdir, dst);
+        ret = asprintf(&fulldstpath, "%s/%s", destrootdir, (dst[0]=='/' ? (dst+1) : dst));
+        if (ret < 0) {
+                log_error("Out of memory!");
+                exit(EXIT_FAILURE);
+        }
 
         ret = stat(fulldstpath, &sb);
 
@@ -387,10 +648,14 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
         }
 
         if (ret == 0) {
-                log_debug("'%s' already exists", fulldstpath);
-                free(fulldstpath);
+                if (resolvedeps && S_ISREG(sb.st_mode) && (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+                        log_debug("'%s' already exists, but checking for any deps", fulldstpath);
+                        ret = resolve_deps(src);
+                } else
+                        log_debug("'%s' already exists", fulldstpath);
+
                 /* dst does already exist */
-                return 0;
+                return ret;
         }
 
         /* check destination directory */
@@ -400,6 +665,8 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
         ret = stat(fulldstdir, &db);
 
         if (ret < 0) {
+                _cleanup_free_ char *dname = NULL;
+
                 if (errno != ENOENT) {
                         log_error("ERROR: stat '%s': %m", fulldstdir);
                         return 1;
@@ -407,42 +674,41 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
                 /* create destination directory */
                 log_debug("dest dir '%s' does not exist", fulldstdir);
                 dname = strdup(dst);
-                dname[dir_len(dname)] = '\0';
-                ret = dracut_install(dname, dname, true, false);
+                if (!dname)
+                        return 1;
 
-                free(dname);
+                dname[dir_len(dname)] = '\0';
+                ret = dracut_install(dname, dname, true, false, true);
 
                 if (ret != 0) {
                         log_error("ERROR: failed to create directory '%s'", fulldstdir);
-                        free(fulldstdir);
                         return 1;
                 }
         }
 
-        free(fulldstdir);
-
         if (isdir && !src_exists) {
                 log_info("mkdir '%s'", fulldstpath);
-                return mkdir(fulldstpath, 0755);
+                ret = mkdir(fulldstpath, 0755);
+                return ret;
         }
 
         /* ready to install src */
 
         if (S_ISDIR(sb.st_mode)) {
                 log_info("mkdir '%s'", fulldstpath);
-                return mkdir(fulldstpath, sb.st_mode | S_IWUSR);
+                ret = mkdir(fulldstpath, sb.st_mode | S_IWUSR);
+                return ret;
         }
 
         if (S_ISLNK(sb.st_mode)) {
-                char *abspath;
-                char *absdestpath = NULL;
+                _cleanup_free_ char *abspath = NULL;
 
                 abspath = realpath(src, NULL);
 
                 if (abspath == NULL)
                         return 1;
 
-                if (dracut_install(abspath, abspath, false, resolvedeps)) {
+                if (dracut_install(abspath, abspath, false, resolvedeps, hashdst)) {
                         log_debug("'%s' install error", abspath);
                         return 1;
                 }
@@ -453,18 +719,20 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
                 }
 
                 if (lstat(fulldstpath, &sb) != 0) {
+                        _cleanup_free_ char *absdestpath = NULL;
 
-                        asprintf(&absdestpath, "%s%s", destrootdir, abspath);
+                        ret = asprintf(&absdestpath, "%s/%s", destrootdir, (abspath[0]=='/' ? (abspath+1) : abspath));
+                        if (ret < 0) {
+                                log_error("Out of memory!");
+                                exit(EXIT_FAILURE);
+                        }
 
                         ln_r(absdestpath, fulldstpath);
-
-                        free(absdestpath);
                 }
 
-                free(abspath);
                 if (arg_hmac) {
                         /* copy .hmac files also */
-                        hmac_install(src, dst);
+                        hmac_install(src, dst, NULL);
                 }
 
                 return 0;
@@ -475,12 +743,22 @@ static int dracut_install(const char *src, const char *dst, bool isdir, bool res
                         ret += resolve_deps(src);
                 if (arg_hmac) {
                         /* copy .hmac files also */
-                        hmac_install(src, dst);
+                        hmac_install(src, dst, NULL);
                 }
         }
 
+        log_debug("dracut_install ret = %d", ret);
         log_info("cp '%s' '%s'", src, fulldstpath);
+
+        if (arg_hostonly && !arg_module)
+                mark_hostonly(dst);
+
         ret += cp(src, fulldstpath);
+        if (ret == 0 && logfile_f)
+                dracut_log_cp(src);
+
+        log_debug("dracut_install ret = %d", ret);
+
         return ret;
 }
 
@@ -492,47 +770,48 @@ static void item_free(char *i)
 
 static void usage(int status)
 {
-        /*                                                                     */
-        printf("\
-Usage: %s -D DESTROOTDIR [OPTION]... -a SOURCE...\n\
-   or: %s -D DESTROOTDIR [OPTION]... SOURCE DEST\n\
-\n\
-Install SOURCE to DEST in DESTROOTDIR with all needed dependencies.\n\
-\n\
-  -D --destrootdir    Install all files to DESTROOTDIR as the root\n\
-  -a --all            Install all SOURCE arguments to DESTROOTDIR\n\
-  -o --optional       If SOURCE does not exist, do not fail\n\
-  -d --dir            SOURCE is a directory\n\
-  -l --ldd            Also install shebang executables and libraries\n\
-  -R --resolvelazy    Only install shebang executables and libraries for all SOURCE files\n\
-  -f --fips           Also install all '.SOURCE.hmac' files\n\
-  -v --verbose        Show more output\n\
-     --debug          Show debug output\n\
-     --version        Show package version\n\
-  -h --help           Show this help\n\
-\n\
-Example:\n\
-# %s -D /var/tmp/test-root --ldd -a sh tr\n\
-# tree /var/tmp/test-root\n\
-/var/tmp/test-root\n\
-|-- lib64 -> usr/lib64\n\
-`-- usr\n\
-    |-- bin\n\
-    |   |-- bash\n\
-    |   |-- sh -> bash\n\
-    |   `-- tr\n\
-    `-- lib64\n\
-        |-- ld-2.15.90.so\n\
-        |-- ld-linux-x86-64.so.2 -> ld-2.15.90.so\n\
-        |-- libc-2.15.90.so\n\
-        |-- libc.so\n\
-        |-- libc.so.6 -> libc-2.15.90.so\n\
-        |-- libdl-2.15.90.so\n\
-        |-- libdl.so -> libdl-2.15.90.so\n\
-        |-- libdl.so.2 -> libdl-2.15.90.so\n\
-        |-- libtinfo.so.5 -> libtinfo.so.5.9\n\
-        `-- libtinfo.so.5.9\n\
-", program_invocation_short_name, program_invocation_short_name, program_invocation_short_name);
+              /*                                                                                */
+        printf("Usage: %s -D DESTROOTDIR [OPTION]... -a SOURCE...\n"
+               "or: %s -D DESTROOTDIR [OPTION]... SOURCE DEST\n"
+               "or: %s -D DESTROOTDIR [OPTION]... -m KERNELMODULE [KERNELMODULE â€¦]\n"
+               "\n"
+               "Install SOURCE to DEST in DESTROOTDIR with all needed dependencies.\n"
+               "\n"
+               "  KERNELMODULE can have the format:\n"
+               "     <absolute path> with a leading /\n"
+               "     =<kernel subdir>[/<kernel subdir>…] like '=drivers/hid'\n"
+               "     <module name>\n"
+               "\n"
+               "  -D --destrootdir  Install all files to DESTROOTDIR as the root\n"
+               "  -a --all          Install all SOURCE arguments to DESTROOTDIR\n"
+               "  -o --optional     If SOURCE does not exist, do not fail\n"
+               "  -d --dir          SOURCE is a directory\n"
+               "  -l --ldd          Also install shebang executables and libraries\n"
+               "  -L --logdir <DIR> Log files, which were installed from the host to <DIR>\n"
+               "  -R --resolvelazy  Only install shebang executables and libraries\n"
+               "                     for all SOURCE files\n"
+               "  -H --hostonly     Mark all SOURCE files as hostonly\n\n"
+               "  -f --fips         Also install all '.SOURCE.hmac' files\n"
+               "\n"
+               "  --module,-m       Install kernel modules, instead of files\n"
+               "  --kerneldir       Specify the kernel module directory\n"
+               "  --firmwaredirs    Specify the firmware directory search path with : separation\n"
+               "  --silent          Don't display error messages for kernel module install\n"
+               "  --modalias        Only generate module list from /sys/devices modalias list\n"
+               "  -o --optional     If kernel module does not exist, do not fail\n"
+               "  -p --mod-filter-path      Filter kernel modules by path regexp\n"
+               "  -P --mod-filter-nopath    Exclude kernel modules by path regexp\n"
+               "  -s --mod-filter-symbol    Filter kernel modules by symbol regexp\n"
+               "  -S --mod-filter-nosymbol  Exclude kernel modules by symbol regexp\n"
+               "  -N --mod-filter-noname    Exclude kernel modules by name regexp\n"
+               "\n"
+               "  -v --verbose      Show more output\n"
+               "     --debug        Show debug output\n"
+               "     --version      Show package version\n"
+               "  -h --help         Show this help\n"
+               "\n",
+               program_invocation_short_name, program_invocation_short_name,
+               program_invocation_short_name);
         exit(status);
 }
 
@@ -542,10 +821,14 @@ static int parse_argv(int argc, char *argv[])
 
         enum {
                 ARG_VERSION = 0x100,
+                ARG_SILENT,
+                ARG_MODALIAS,
+                ARG_KERNELDIR,
+                ARG_FIRMWAREDIRS,
                 ARG_DEBUG
         };
 
-        static const struct option const options[] = {
+        static struct option const options[] = {
                 {"help", no_argument, NULL, 'h'},
                 {"version", no_argument, NULL, ARG_VERSION},
                 {"dir", no_argument, NULL, 'd'},
@@ -554,13 +837,25 @@ static int parse_argv(int argc, char *argv[])
                 {"ldd", no_argument, NULL, 'l'},
                 {"resolvelazy", no_argument, NULL, 'R'},
                 {"optional", no_argument, NULL, 'o'},
+                {"hostonly", no_argument, NULL, 'H'},
                 {"all", no_argument, NULL, 'a'},
-                {"fips", no_argument, NULL, 'H'},
+                {"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'},
+                {"modalias", no_argument, NULL, ARG_MODALIAS},
+                {"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, "adhloD:DHILR", 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);
@@ -571,6 +866,13 @@ static int parse_argv(int argc, char *argv[])
                 case ARG_DEBUG:
                         arg_loglevel = LOG_DEBUG;
                         break;
+                case ARG_SILENT:
+                        arg_silent = true;
+                        break;
+                case ARG_MODALIAS:
+                        arg_modalias = true;
+                        return 1;
+                        break;
                 case 'v':
                         arg_loglevel = LOG_INFO;
                         break;
@@ -586,12 +888,62 @@ 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 'H':
+                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;
+                case 'H':
+                        arg_hostonly = true;
+                        break;
                 case 'h':
                         usage(EXIT_SUCCESS);
                         break;
@@ -600,7 +952,35 @@ static int parse_argv(int argc, char *argv[])
                 }
         }
 
+        if (!kerneldir) {
+                struct utsname buf;
+                uname(&buf);
+                kerneldir = strdup(buf.version);
+        }
+
+        if (arg_modalias) {
+                return 1;
+        }
+
+        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);
         }
 
@@ -610,7 +990,7 @@ static int parse_argv(int argc, char *argv[])
 static int resolve_lazy(int argc, char **argv)
 {
         int i;
-        int destrootdirlen = strlen(destrootdir);
+        size_t destrootdirlen = strlen(destrootdir);
         int ret = 0;
         char *item;
         for (i = 0; i < argc; i++) {
@@ -638,77 +1018,685 @@ static int resolve_lazy(int argc, char **argv)
         return ret;
 }
 
+static char **find_binary(const char *src)
+{
+        char **ret = NULL;
+        char **q;
+        char *newsrc = NULL;
+
+        STRV_FOREACH(q, pathdirs) {
+                struct stat sb;
+                int r;
+
+                r = asprintf(&newsrc, "%s/%s", *q, src);
+                if (r < 0) {
+                        log_error("Out of memory!");
+                        exit(EXIT_FAILURE);
+                }
+
+                if (stat(newsrc, &sb) != 0) {
+                        log_debug("stat(%s) != 0", newsrc);
+                        free(newsrc);
+                        newsrc = NULL;
+                        continue;
+                }
+
+                strv_push(&ret, newsrc);
+
+        };
+
+        if (ret) {
+                STRV_FOREACH(q, ret) {
+                        log_debug("find_binary(%s) == %s", src, *q);
+                }
+        }
+
+        return ret;
+}
+
+static int install_one(const char *src, const char *dst)
+{
+        int r = EXIT_SUCCESS;
+        int ret = 0;
+
+        if (strchr(src, '/') == NULL) {
+                char **p = find_binary(src);
+                if (p) {
+                        char **q = NULL;
+                        STRV_FOREACH(q, p) {
+                                char *newsrc = *q;
+                                log_debug("dracut_install '%s' '%s'", newsrc, dst);
+                                ret = dracut_install(newsrc, dst, arg_createdir, arg_resolvedeps, true);
+                                if (ret == 0) {
+                                        log_debug("dracut_install '%s' '%s' OK", newsrc, dst);
+                                }
+                        }
+                        strv_free(p);
+                } else {
+                        ret = -1;
+                }
+        } else {
+                ret = dracut_install(src, dst, arg_createdir, arg_resolvedeps, true);
+        }
+
+        if ((ret != 0) && (!arg_optional)) {
+                log_error("ERROR: installing '%s' to '%s'", src, dst);
+                r = EXIT_FAILURE;
+        }
+
+        return r;
+}
+
 static int install_all(int argc, char **argv)
 {
-        int r = 0;
+        int r = EXIT_SUCCESS;
         int i;
         for (i = 0; i < argc; i++) {
-                int ret;
+                int ret = 0;
                 log_debug("Handle '%s'", argv[i]);
 
                 if (strchr(argv[i], '/') == NULL) {
-                        char *path;
-                        char *p, *q;
-                        bool end = false;
-                        path = getenv("PATH");
-                        if (path == NULL) {
-                                log_error("PATH is not set");
+                        char **p = find_binary(argv[i]);
+                        if (p) {
+                                char **q = NULL;
+                                STRV_FOREACH(q, p) {
+                                        char *newsrc = *q;
+                                        log_debug("dracut_install '%s'", newsrc);
+                                        ret = dracut_install(newsrc, newsrc, arg_createdir, arg_resolvedeps, true);
+                                        if (ret == 0) {
+                                                log_debug("dracut_install '%s' OK", newsrc);
+                                        }
+                                }
+                                strv_free(p);
+                        } else {
+                                ret = -1;
+                        }
+
+                } else {
+                        _cleanup_free_ char *dest = strdup(argv[i]);
+                        ret = dracut_install(argv[i], dest, arg_createdir, arg_resolvedeps, true);
+                }
+
+                if ((ret != 0) && (!arg_optional)) {
+                        log_error("ERROR: installing '%s'", argv[i]);
+                        r = EXIT_FAILURE;
+                }
+        }
+        return r;
+}
+
+static int install_firmware(struct kmod_module *mod)
+{
+        struct kmod_list *l;
+        _cleanup_kmod_module_info_free_list_ struct kmod_list *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;
+
+                if (!streq("firmware", key))
+                        continue;
+
+                value = kmod_module_info_get_value(l);
+                log_debug("Firmware %s", value);
+                ret = -1;
+                STRV_FOREACH(q, firmwaredirs) {
+                        _cleanup_free_ char *fwpath = NULL;
+                        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);
+                                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;
+        _cleanup_kmod_module_dependency_symbols_free_list_ struct kmod_list *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) {
+                                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) {
+                                log_debug("Module %s: symbol %s matched inclusion filter", kmod_module_get_name(mod), symbol);
+                                return true;
+                        }
+                }
+                return false;
+        }
+
+        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_dependent_modules(struct kmod_list *modlist)
+{
+        struct kmod_list *itr;
+        const char *path = NULL;
+        const char *name = NULL;
+        int ret = 0;
+
+        kmod_list_foreach(itr, modlist) {
+               _cleanup_kmod_module_unref_ struct kmod_module *mod = NULL;
+                mod = kmod_module_get_module(itr);
+                path = kmod_module_get_path(mod);
+
+               if (check_hashmap(items_failed, path))
+                       return -1;
+
+               if (check_hashmap(items, path)) {
+                       continue;
+               }
+
+                name = kmod_module_get_name(mod);
+
+                if ((path == NULL) || (arg_mod_filter_noname && (regexec(&mod_filter_noname, name, 0, NULL, 0) == 0))) {
+                        continue;
+                }
+
+                ret = dracut_install(path, &path[kerneldirlen], false, false, true);
+                if (ret == 0) {
+                       _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
+                       _cleanup_kmod_module_unref_list_ struct kmod_list *modpre = NULL;
+                       _cleanup_kmod_module_unref_list_ struct kmod_list *modpost = NULL;
+                        log_debug("dracut_install '%s' '%s' OK", path, &path[kerneldirlen]);
+                        install_firmware(mod);
+                       modlist = kmod_module_get_dependencies(mod);
+                       ret = install_dependent_modules(modlist);
+                       if (ret == 0) {
+                               ret = kmod_module_get_softdeps(mod, &modpre, &modpost);
+                               if (ret == 0)
+                                       ret = install_dependent_modules(modpre);
+                       }
+                } else {
+                        log_error("dracut_install '%s' '%s' ERROR", path, &path[kerneldirlen]);
+                }
+        }
+
+        return ret;
+}
+
+static int install_module(struct kmod_module *mod)
+{
+        int ret = 0;
+        _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
+        _cleanup_kmod_module_unref_list_ struct kmod_list *modpre = NULL;
+        _cleanup_kmod_module_unref_list_ struct kmod_list *modpost = NULL;
+        const char *path = NULL;
+        const char *name = NULL;
+
+        name = kmod_module_get_name(mod);
+        if (arg_mod_filter_noname && (regexec(&mod_filter_noname, name, 0, NULL, 0) == 0)) {
+                log_debug("dracut_install '%s' is excluded", name);
+                return 0;
+        }
+
+        if (arg_hostonly && !check_hashmap(modules_loaded, name)) {
+                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 path match for '%s'", path);
+                return 1;
+        }
+
+        log_debug("dracut_install '%s' '%s'", path, &path[kerneldirlen]);
+
+        ret = dracut_install(path, &path[kerneldirlen], 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);
+        ret = install_dependent_modules(modlist);
+
+        if (ret == 0) {
+                ret = kmod_module_get_softdeps(mod, &modpre, &modpost);
+                if (ret == 0)
+                        ret = install_dependent_modules(modpre);
+        }
+
+        return ret;
+}
+
+static int modalias_list(struct kmod_ctx *ctx)
+{
+        int err;
+        struct kmod_list *loaded_list = NULL;
+        struct kmod_list *itr, *l;
+        _cleanup_fts_close_ FTS *fts = NULL;
+
+        {
+                char *paths[] = { "/sys/devices", NULL };
+                fts = fts_open(paths, FTS_NOCHDIR|FTS_NOSTAT, NULL);
+        }
+        for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
+                _cleanup_fclose_ FILE *f = NULL;
+                _cleanup_kmod_module_unref_list_ struct kmod_list *list = NULL;
+                struct kmod_list *l;
+
+                int err;
+
+                char alias[2048];
+                size_t len;
+
+                if (strncmp("modalias", ftsent->fts_name, 8) != 0)
+                        continue;
+                if (!(f = fopen(ftsent->fts_accpath, "r")))
+                        continue;
+
+                if(!fgets(alias, sizeof(alias), f))
+                        continue;
+
+                len = strlen(alias);
+
+                if (len == 0)
+                        continue;
+
+                if (alias[len-1] == '\n')
+                        alias[len-1] = 0;
+
+                err = kmod_module_new_from_lookup(ctx, alias, &list);
+                if (err < 0)
+                        continue;
+
+                kmod_list_foreach(l, list) {
+                        struct kmod_module *mod = kmod_module_get_module(l);
+                        char *name = strdup(kmod_module_get_name(mod));
+                        kmod_module_unref(mod);
+                        hashmap_put(modules_loaded, name, name);
+                }
+        }
+
+        err = kmod_module_new_from_loaded(ctx, &loaded_list);
+        if (err < 0) {
+                errno = err;
+                log_error("Could not get list of loaded modules: %m. Switching to non-hostonly mode.");
+                arg_hostonly = false;
+        } else {
+                kmod_list_foreach(itr, loaded_list) {
+                        _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
+
+                        struct kmod_module *mod = kmod_module_get_module(itr);
+                        char *name = strdup(kmod_module_get_name(mod));
+                        hashmap_put(modules_loaded, name, name);
+                        kmod_module_unref(mod);
+
+                        /* also put the modules from the new kernel in the hashmap,
+                         * which resolve the name as an alias, in case a kernel module is
+                         * renamed.
+                         */
+                        err = kmod_module_new_from_lookup(ctx, name, &modlist);
+                        if (err < 0)
+                                continue;
+                        if (!modlist)
+                                continue;
+                        kmod_list_foreach(l, modlist) {
+                                mod = kmod_module_get_module(l);
+                                char *name = strdup(kmod_module_get_name(mod));
+                                hashmap_put(modules_loaded, name, name);
+                                kmod_module_unref(mod);
+                        }
+                }
+                kmod_module_unref_list(loaded_list);
+        }
+        return 0;
+}
+
+static int install_modules(int argc, char **argv)
+{
+        _cleanup_kmod_unref_ struct kmod_ctx *ctx = NULL;
+        struct kmod_list *itr;
+
+        struct kmod_module *mod = NULL, *mod_o = NULL;
+
+        const char *abskpath = NULL;
+        char *p;
+        int i;
+        int modinst = 0;
+
+        ctx = kmod_new(kerneldir, NULL);
+        abskpath = kmod_get_dirname(ctx);
+
+        p = strstr(abskpath, "/lib/modules/");
+        if (p != NULL)
+                kerneldirlen = p - abskpath;
+
+        if (arg_hostonly) {
+                char *modalias_file;
+                modalias_file = getenv("DRACUT_KERNEL_MODALIASES");
+
+                if (modalias_file == NULL) {
+                        modalias_list(ctx);
+                } else {
+                        _cleanup_fclose_ FILE *f = NULL;
+                        if ((f = fopen(modalias_file, "r"))) {
+                                char name[2048];
+
+                                while (!feof(f)) {
+                                        size_t len;
+                                        char *dupname = NULL;
+
+                                        if(!(fgets(name, sizeof(name), f)))
+                                                continue;
+                                        len = strlen(name);
+
+                                        if (len == 0)
+                                                continue;
+
+                                        if (name[len-1] == '\n')
+                                                name[len-1] = 0;
+
+                                        log_debug("Adding module '%s' to hostonly module list", name);
+                                        dupname = strdup(name);
+                                        hashmap_put(modules_loaded, dupname, dupname);
+                                }
+                        }
+                }
+
+        }
+
+        for (i = 0; i < argc; i++) {
+                int r = 0;
+                int ret = -1;
+                log_debug("Handle module '%s'", argv[i]);
+
+                if (argv[i][0] == '/') {
+                        _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
+                        _cleanup_free_ const char *modname = NULL;
+
+                        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 = strdup(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);
+                        mod_o = NULL;
+
+                        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);
+                                r = install_module(mod);
+                                kmod_module_unref(mod);
+                                if ((r < 0) && !arg_optional) {
+                                        if (!arg_silent)
+                                                log_error("ERROR: installing module '%s'", modname);
+                                        return -ENOENT;
+                                };
+                                ret = ( ret == 0 ? 0 : r );
+                                modinst = 1;
+                        }
+                } else if (argv[i][0] == '=') {
+                        _cleanup_free_ char *path1 = NULL, *path2 = NULL, *path3 = NULL;
+                        _cleanup_fts_close_ FTS *fts = NULL;
+
+                        log_debug("Handling =%s", &argv[i][1]);
+                        /* FIXME and add more paths*/
+                        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);
                         }
-                        path = strdup(path);
-                        p = path;
-                        log_debug("PATH=%s", path);
-                        do {
-                                char *newsrc = NULL;
-                                char *dest;
-                                struct stat sb;
 
-                                for (q = p; *q && *q != ':'; q++) ;
+                        r = asprintf(&path3, "%s/updates/%s", kerneldir, &argv[i][1]);
+                        if (r < 0) {
+                                log_error("Out of memory!");
+                                exit(EXIT_FAILURE);
+                        }
 
-                                if (*q == '\0')
-                                        end = true;
-                                else
-                                        *q = '\0';
+                        {
+                                char *paths[] = { path1, path2, path3, NULL };
+                                fts = fts_open(paths, FTS_COMFOLLOW|FTS_NOCHDIR|FTS_NOSTAT|FTS_LOGICAL, NULL);
+                        }
 
-                                asprintf(&newsrc, "%s/%s", p, argv[i]);
-                                p = q + 1;
+                        for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
+                                _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
+                                _cleanup_free_ const char *modname = NULL;
 
-                                if (stat(newsrc, &sb) != 0) {
-                                        free(newsrc);
-                                        ret = -1;
+                                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);
+                                        if (!arg_optional) {
+                                                return -ENOENT;
+                                        }
                                         continue;
                                 }
 
-                                dest = strdup(newsrc);
+                                /* Check, if we have to load another module with that name instead */
+                                modname = strdup(kmod_module_get_name(mod_o));
 
-                                log_debug("dracut_install '%s'", newsrc);
-                                ret = dracut_install(newsrc, dest, arg_createdir, arg_resolvedeps);
-                                if (ret == 0) {
-                                        end = true;
-                                        log_debug("dracut_install '%s' OK", newsrc);
+                                if (!modname) {
+                                        log_error("Failed to get name for module '%s'", ftsent->fts_accpath);
+                                        if (!arg_optional) {
+                                                return -ENOENT;
+                                        }
+                                        continue;
+                                }
+                                r = kmod_module_new_from_lookup(ctx, modname, &modlist);
+                                kmod_module_unref(mod_o);
+                                mod_o = NULL;
+
+                                if (r < 0) {
+                                        log_error("Failed to lookup alias '%s': %m", modname);
+                                        if (!arg_optional) {
+                                                return -ENOENT;
+                                        }
+                                        continue;
+                                }
+
+                                if (!modlist) {
+                                        log_error("Failed to find module '%s' %s", modname,
+                                                  ftsent->fts_accpath);
+                                        if (!arg_optional) {
+                                                return -ENOENT;
+                                        }
+                                        continue;
+                                }
+                                kmod_list_foreach(itr, modlist) {
+                                        mod = kmod_module_get_module(itr);
+                                        r = install_module(mod);
+                                        kmod_module_unref(mod);
+                                        if ((r < 0) && !arg_optional) {
+                                                if (!arg_silent)
+                                                        log_error("ERROR: installing module '%s'", modname);
+                                                return -ENOENT;
+                                        };
+                                        ret = ( ret == 0 ? 0 : r );
+                                        modinst = 1;
                                 }
-                                free(newsrc);
-                                free(dest);
-                        } while (!end);
-                        free(path);
+                        }
+                        if (errno) {
+                                log_error("FTS ERROR: %m");
+                        }
                 } else {
-                        char *dest = strdup(argv[i]);
-                        ret = dracut_install(argv[i], dest, arg_createdir, arg_resolvedeps);
-                        free(dest);
+                        _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
+                        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);
+                                r = install_module(mod);
+                                kmod_module_unref(mod);
+                                if ((r < 0) && !arg_optional) {
+                                        if (!arg_silent)
+                                                log_error("ERROR: installing '%s'", argv[i]);
+                                        return -ENOENT;
+                                };
+                                ret = ( ret == 0 ? 0 : r );
+                                modinst = 1;
+                        }
                 }
 
-                if ((ret != 0) && (!arg_optional)) {
-                        log_error("ERROR: installing '%s'", argv[i]);
-                        r = EXIT_FAILURE;
+                if ((modinst != 0) && (ret != 0) && (!arg_optional)) {
+                        if (!arg_silent)
+                                log_error("ERROR: installing '%s'", argv[i]);
+                        return EXIT_FAILURE;
                 }
         }
-        return r;
+
+        return EXIT_SUCCESS;
 }
 
 int main(int argc, char **argv)
 {
         int r;
         char *i;
+        char *path = NULL;
 
         r = parse_argv(argc, argv);
         if (r <= 0)
@@ -722,26 +1710,82 @@ int main(int argc, char **argv)
 
         log_open();
 
+        modules_loaded = hashmap_new(string_hash_func, string_compare_func);
+        if (arg_modalias) {
+                Iterator i;
+                char *name;
+                _cleanup_kmod_unref_ struct kmod_ctx *ctx = NULL;
+                ctx = kmod_new(kerneldir, NULL);
+
+                modalias_list(ctx);
+                HASHMAP_FOREACH(name, modules_loaded, i) {
+                        printf("%s\n", name);
+                }
+                exit(0);
+        }
+
+        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) {
+        if (destrootdir == NULL || strlen(destrootdir) == 0) {
                 destrootdir = getenv("DESTROOTDIR");
-                if (destrootdir == NULL) {
+                if (destrootdir == NULL || strlen(destrootdir) == 0) {
                         log_error("Environment DESTROOTDIR or argument -D is not set!");
                         usage(EXIT_FAILURE);
                 }
                 destrootdir = strdup(destrootdir);
         }
 
+        if (strcmp(destrootdir, "/") == 0) {
+                log_error("Environment DESTROOTDIR or argument -D is set to '/'!");
+                usage(EXIT_FAILURE);
+        }
+
+        i = destrootdir;
+        destrootdir = realpath(destrootdir, NULL);
+        if (!destrootdir) {
+                log_error("Environment DESTROOTDIR or argument -D is set to '%s': %m", i);
+                r = EXIT_FAILURE;
+                goto finish;
+        }
+        free(i);
+
         items = hashmap_new(string_hash_func, string_compare_func);
         items_failed = hashmap_new(string_hash_func, string_compare_func);
 
-        if (!items || !items_failed) {
+        if (!items || !items_failed || !modules_loaded) {
                 log_error("Out of memory");
                 r = EXIT_FAILURE;
                 goto finish;
         }
 
+        if (logdir) {
+                int ret;
+
+                ret = asprintf(&logfile, "%s/%d.log", logdir, getpid());
+                if (ret < 0) {
+                        log_error("Out of memory!");
+                        exit(EXIT_FAILURE);
+                }
+
+                logfile_f = fopen(logfile, "a");
+                if (logfile_f == NULL) {
+                        log_error("Could not open %s for logging: %m", logfile);
+                        r = EXIT_FAILURE;
+                        goto finish;
+                }
+        }
+
         r = EXIT_SUCCESS;
 
         if (((optind + 1) < argc) && (strcmp(argv[optind + 1], destrootdir) == 0)) {
@@ -757,23 +1801,26 @@ 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]);
         } else {
                 /* simple "inst src dst" */
-                r = dracut_install(argv[optind], argv[optind + 1], arg_createdir, arg_resolvedeps);
-                if ((r != 0) && (!arg_optional)) {
-                        log_error("ERROR: installing '%s' to '%s'", argv[optind], argv[optind + 1]);
-                        r = EXIT_FAILURE;
-                }
+                r = install_one(argv[optind], argv[optind + 1]);
         }
 
         if (arg_optional)
                 r = EXIT_SUCCESS;
 
  finish:
+        if (logfile_f)
+                fclose(logfile_f);
+
+        while ((i = hashmap_steal_first(modules_loaded)))
+                item_free(i);
 
         while ((i = hashmap_steal_first(items)))
                 item_free(i);
@@ -783,8 +1830,10 @@ int main(int argc, char **argv)
 
         hashmap_free(items);
         hashmap_free(items_failed);
+        hashmap_free(modules_loaded);
 
         free(destrootdir);
-
+        strv_free(firmwaredirs);
+        strv_free(pathdirs);
         return r;
 }