summaryrefslogtreecommitdiffstats
path: root/src/install
diff options
context:
space:
mode:
Diffstat (limited to 'src/install')
-rw-r--r--src/install/Makefile7
-rw-r--r--src/install/dracut-install.c2281
-rw-r--r--src/install/hashmap.c658
-rw-r--r--src/install/hashmap.h88
-rw-r--r--src/install/log.c303
-rw-r--r--src/install/log.h98
-rw-r--r--src/install/macro.h299
-rw-r--r--src/install/strv.c616
-rw-r--r--src/install/strv.h118
-rw-r--r--src/install/util.c574
-rw-r--r--src/install/util.h609
11 files changed, 5651 insertions, 0 deletions
diff --git a/src/install/Makefile b/src/install/Makefile
new file mode 100644
index 0000000..5332f25
--- /dev/null
+++ b/src/install/Makefile
@@ -0,0 +1,7 @@
+all:
+ $(MAKE) -C ..
+
+clean:
+ $(MAKE) -C .. clean
+
+.PHONY: all clean
diff --git a/src/install/dracut-install.c b/src/install/dracut-install.c
new file mode 100644
index 0000000..485143a
--- /dev/null
+++ b/src/install/dracut-install.c
@@ -0,0 +1,2281 @@
+/* dracut-install.c -- install files and executables
+
+ Copyright (C) 2012 Harald Hoyer
+ Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+
+ This program is free software: you can redistribute it and/or modify
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#define PROGRAM_VERSION_STRING "2"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <glob.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.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"
+
+#define _asprintf(strp, fmt, ...) \
+ do { \
+ if (dracut_asprintf(strp, fmt, __VA_ARGS__) < 0) { \
+ log_error("Out of memory\n"); \
+ exit(EXIT_FAILURE); \
+ } \
+ } while (0)
+
+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 bool no_xattr = false;
+static char *destrootdir = NULL;
+static char *sysrootdir = NULL;
+static size_t sysrootdirlen = 0;
+static char *kerneldir = NULL;
+static size_t kerneldirlen = 0;
+static char **firmwaredirs = NULL;
+static char **pathdirs;
+static char *ldd = NULL;
+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 Hashmap *modules_suppliers = NULL;
+static Hashmap *processed_suppliers = 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 int install_dependent_modules(struct kmod_ctx *ctx, struct kmod_list *modlist, Hashmap *suppliers_paths);
+
+static void item_free(char *i)
+{
+ assert(i);
+ free(i);
+}
+
+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)
+
+#define _cleanup_globfree_ _cleanup_(globfree)
+
+static inline void destroy_hashmap(Hashmap **hashmap)
+{
+ void *i = NULL;
+
+ while ((i = hashmap_steal_first(*hashmap)))
+ item_free(i);
+
+ hashmap_free(*hashmap);
+}
+
+#define _cleanup_destroy_hashmap_ _cleanup_(destroy_hashmap)
+
+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) - 1; 0 < length; length--)
+ if (file[length] == '/' && file[length - 1] != '/')
+ break;
+ return length;
+}
+
+static char *convert_abs_rel(const char *from, const char *target)
+{
+ /* we use the 4*MAXPATHLEN, which should not overrun */
+ char buf[MAXPATHLEN * 4];
+ _cleanup_free_ char *realtarget = NULL, *realfrom = NULL, *from_dir_p = NULL;
+ _cleanup_free_ char *target_dir_p = NULL;
+ size_t level = 0, fromlevel = 0, targetlevel = 0;
+ int l;
+ size_t i, rl, dirlen;
+
+ dirlen = dir_len(from);
+ from_dir_p = strndup(from, dirlen);
+ if (!from_dir_p)
+ return strdup(from + strlen(destrootdir));
+ if (realpath(from_dir_p, buf) == NULL) {
+ log_warning("convert_abs_rel(): from '%s' directory has no realpath: %m", from);
+ return strdup(from + strlen(destrootdir));
+ }
+ /* dir_len() skips double /'s e.g. //lib64, so we can't skip just one
+ * character - need to skip all leading /'s */
+ for (i = dirlen + 1; from[i] == '/'; ++i)
+ ;
+ _asprintf(&realfrom, "%s/%s", buf, from + i);
+
+ dirlen = dir_len(target);
+ target_dir_p = strndup(target, dirlen);
+ if (!target_dir_p)
+ return strdup(from + strlen(destrootdir));
+ if (realpath(target_dir_p, buf) == NULL) {
+ log_warning("convert_abs_rel(): target '%s' directory has no realpath: %m", target);
+ return strdup(from + strlen(destrootdir));
+ }
+
+ for (i = dirlen + 1; target[i] == '/'; ++i)
+ ;
+ _asprintf(&realtarget, "%s/%s", buf, target + i);
+
+ /* now calculate the relative path from <from> to <target> and
+ store it in <buf>
+ */
+ rl = 0;
+
+ /* count the pathname elements of realtarget */
+ for (targetlevel = 0, i = 0; realtarget[i]; i++)
+ if (realtarget[i] == '/')
+ targetlevel++;
+
+ /* count the pathname elements of realfrom */
+ for (fromlevel = 0, i = 0; realfrom[i]; i++)
+ if (realfrom[i] == '/')
+ fromlevel++;
+
+ /* count the pathname elements, which are common for both paths */
+ for (level = 0, i = 0; realtarget[i] && (realtarget[i] == realfrom[i]); i++)
+ if (realtarget[i] == '/')
+ level++;
+
+ /* add "../" to the buf path, until the common pathname is
+ reached */
+ for (i = level; i < targetlevel; i++) {
+ if (i != level)
+ buf[rl++] = '/';
+ buf[rl++] = '.';
+ buf[rl++] = '.';
+ }
+
+ /* set l to the next uncommon pathname element in realfrom */
+ for (l = 1, i = 1; i < level; i++)
+ for (l++; realfrom[l] && realfrom[l] != '/'; l++) ;
+ /* skip next '/' */
+ l++;
+
+ /* append the uncommon rest of realfrom to the buf path */
+ for (i = level; i <= fromlevel; i++) {
+ if (rl)
+ buf[rl++] = '/';
+ while (realfrom[l] && realfrom[l] != '/')
+ buf[rl++] = realfrom[l++];
+ l++;
+ }
+
+ buf[rl] = 0;
+ return strdup(buf);
+}
+
+static int ln_r(const char *src, const char *dst)
+{
+ int ret;
+ _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);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Perform the O(1) btrfs clone operation, if possible.
+ Upon success, return 0. Otherwise, return -1 and set errno. */
+static inline int clone_file(int dest_fd, int src_fd)
+{
+#undef BTRFS_IOCTL_MAGIC
+#define BTRFS_IOCTL_MAGIC 0x94
+#undef BTRFS_IOC_CLONE
+#define BTRFS_IOC_CLONE _IOW (BTRFS_IOCTL_MAGIC, 9, int)
+ return ioctl(dest_fd, BTRFS_IOC_CLONE, src_fd);
+}
+
+static bool use_clone = true;
+
+static int cp(const char *src, const char *dst)
+{
+ int pid;
+ int ret = 0;
+
+ if (use_clone) {
+ struct stat sb;
+ _cleanup_close_ int dest_desc = -1, source_desc = -1;
+
+ if (lstat(src, &sb) != 0)
+ goto normal_copy;
+
+ if (S_ISLNK(sb.st_mode))
+ goto normal_copy;
+
+ source_desc = open(src, O_RDONLY | O_CLOEXEC);
+ if (source_desc < 0)
+ 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));
+
+ if (dest_desc < 0) {
+ goto normal_copy;
+ }
+
+ ret = clone_file(dest_desc, source_desc);
+
+ if (ret == 0) {
+ struct timeval tv[2];
+ if (fchown(dest_desc, sb.st_uid, sb.st_gid) != 0)
+ 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 */
+ use_clone = false;
+ }
+
+normal_copy:
+ pid = fork();
+ const char *preservation = (geteuid() == 0
+ && no_xattr == false) ? "--preserve=mode,xattr,timestamps,ownership" : "--preserve=mode,timestamps,ownership";
+ if (pid == 0) {
+ execlp("cp", "cp", "--reflink=auto", "--sparse=auto", preservation, "-fL", src, dst, NULL);
+ _exit(errno == ENOENT ? 127 : 126);
+ }
+
+ while (waitpid(pid, &ret, 0) == -1) {
+ if (errno != EINTR) {
+ log_error("ERROR: waitpid() failed: %m");
+ return 1;
+ }
+ }
+ ret = WIFSIGNALED(ret) ? 128 + WTERMSIG(ret) : WEXITSTATUS(ret);
+ if (ret != 0)
+ log_error("ERROR: 'cp --reflink=auto --sparse=auto %s -fL %s %s' failed with %d", preservation, src, dst, ret);
+ 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, *pppdir = NULL, *clib = NULL;
+ char *q, *clibdir;
+ int r, ret = 0;
+
+ r = dracut_install(lib, lib, false, false, true);
+ if (r != 0)
+ log_error("ERROR: failed to install '%s' for '%s'", lib, src);
+ else
+ log_debug("Lib install: '%s'", lib);
+ ret += r;
+
+ /* also install lib.so for lib.so.* files */
+ q = strstr(lib, ".so.");
+ if (q) {
+ p = strndup(lib, q - lib + 3);
+
+ /* ignore errors for base lib symlink */
+ if (dracut_install(p, p, false, false, true) == 0)
+ log_debug("Lib install: '%s'", p);
+
+ free(p);
+ }
+
+ /* Also try to install the same library from one directory above
+ * or from one directory above glibc-hwcaps.
+ 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
+ */
+
+ p = strdup(lib);
+
+ pdir = dirname_malloc(p);
+ if (!pdir)
+ return ret;
+
+ ppdir = dirname_malloc(pdir);
+ /* only one parent directory, not HWCAP library */
+ if (!ppdir || streq(ppdir, "/"))
+ return ret;
+
+ pppdir = dirname_malloc(ppdir);
+ if (!pppdir)
+ return ret;
+
+ clibdir = streq(basename(ppdir), "glibc-hwcaps") ? pppdir : ppdir;
+ clib = strjoin(clibdir, "/", 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;
+}
+
+static char *get_real_file(const char *src, bool fullyresolve)
+{
+ struct stat sb;
+ ssize_t linksz;
+ char linktarget[PATH_MAX + 1];
+ _cleanup_free_ char *fullsrcpath_a = NULL;
+ const char *fullsrcpath;
+ _cleanup_free_ char *abspath = NULL;
+
+ if (sysrootdirlen) {
+ if (strncmp(src, sysrootdir, sysrootdirlen) == 0) {
+ fullsrcpath = src;
+ } else {
+ _asprintf(&fullsrcpath_a, "%s/%s",
+ (sysrootdirlen ? sysrootdir : ""),
+ (src[0] == '/' ? src + 1 : src));
+ fullsrcpath = fullsrcpath_a;
+ }
+ } else {
+ fullsrcpath = src;
+ }
+
+ log_debug("get_real_file('%s')", fullsrcpath);
+
+ if (lstat(fullsrcpath, &sb) < 0)
+ return NULL;
+
+ switch (sb.st_mode & S_IFMT) {
+ case S_IFDIR:
+ case S_IFREG:
+ return strdup(fullsrcpath);
+ case S_IFLNK:
+ break;
+ default:
+ return NULL;
+ }
+
+ linksz = readlink(fullsrcpath, linktarget, sizeof(linktarget));
+ if (linksz < 0)
+ return NULL;
+ linktarget[linksz] = '\0';
+
+ log_debug("get_real_file: readlink('%s') returns '%s'", fullsrcpath, linktarget);
+
+ if (streq(fullsrcpath, linktarget)) {
+ log_error("ERROR: '%s' is pointing to itself", fullsrcpath);
+ return NULL;
+ }
+
+ if (linktarget[0] == '/') {
+ _asprintf(&abspath, "%s%s", (sysrootdirlen ? sysrootdir : ""), linktarget);
+ } else {
+ _asprintf(&abspath, "%.*s/%s", (int)dir_len(fullsrcpath), fullsrcpath, linktarget);
+ }
+
+ if (fullyresolve) {
+ struct stat st;
+ if (lstat(abspath, &st) < 0) {
+ if (errno != ENOENT) {
+ return NULL;
+ }
+ }
+ if (S_ISLNK(st.st_mode)) {
+ return get_real_file(abspath, fullyresolve);
+ }
+ }
+
+ log_debug("get_real_file('%s') => '%s'", src, abspath);
+ return TAKE_PTR(abspath);
+}
+
+static int resolve_deps(const char *src)
+{
+ int ret = 0, err;
+
+ _cleanup_free_ char *buf = NULL;
+ size_t linesize = LINE_MAX + 1;
+ _cleanup_free_ char *fullsrcpath = NULL;
+
+ fullsrcpath = get_real_file(src, true);
+ log_debug("resolve_deps('%s') -> get_real_file('%s', true) = '%s'", src, src, fullsrcpath);
+ if (!fullsrcpath)
+ return 0;
+
+ buf = malloc(linesize);
+ if (buf == NULL)
+ return -errno;
+
+ if (strstr(src, ".so") == NULL) {
+ _cleanup_close_ int fd = -1;
+ fd = open(fullsrcpath, O_RDONLY | O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ ret = read(fd, buf, linesize - 1);
+ if (ret == -1)
+ return -errno;
+
+ buf[ret] = '\0';
+ if (buf[0] == '#' && buf[1] == '!') {
+ /* we have a shebang */
+ char *p, *q;
+ for (p = &buf[2]; *p && isspace(*p); p++) ;
+ for (q = p; *q && (!isspace(*q)); q++) ;
+ *q = '\0';
+ log_debug("Script install: '%s'", p);
+ ret = dracut_install(p, p, false, true, false);
+ if (ret != 0)
+ log_error("ERROR: failed to install '%s'", p);
+ return ret;
+ }
+ }
+
+ int fds[2];
+ FILE *fptr;
+ if (pipe2(fds, O_CLOEXEC) == -1 || (fptr = fdopen(fds[0], "r")) == NULL) {
+ log_error("ERROR: pipe stream initialization for '%s' failed: %m", ldd);
+ exit(EXIT_FAILURE);
+ }
+
+ log_debug("%s %s", ldd, fullsrcpath);
+ pid_t ldd_pid;
+ if ((ldd_pid = fork()) == 0) {
+ dup2(fds[1], 1);
+ dup2(fds[1], 2);
+ putenv("LC_ALL=C");
+ execlp(ldd, ldd, fullsrcpath, (char *)NULL);
+ _exit(errno == ENOENT ? 127 : 126);
+ }
+ close(fds[1]);
+
+ ret = 0;
+
+ while (getline(&buf, &linesize, fptr) >= 0) {
+ char *p;
+
+ log_debug("ldd: '%s'", buf);
+
+ if (strstr(buf, "you do not have execution permission")) {
+ log_error("%s", buf);
+ ret += 1;
+ break;
+ }
+
+ /* errors from cross-compiler-ldd */
+ if (strstr(buf, "unable to find sysroot")) {
+ 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"))
+ continue;
+
+ if (strstr(buf, "not a dynamic executable"))
+ break;
+
+ 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, "cannot be preloaded"))
+ break;
+
+ if (strstr(buf, destrootdir))
+ break;
+
+ p = buf;
+ if (strchr(p, '$')) {
+ /* take ldd variable expansion into account */
+ p = strstr(p, "=>");
+ if (!p)
+ p = buf;
+ }
+ p = strchr(p, '/');
+
+ if (p) {
+ char *q;
+
+ for (q = p; *q && *q != ' ' && *q != '\n'; q++) ;
+ *q = '\0';
+
+ ret += library_install(src, p);
+
+ }
+ }
+
+ fclose(fptr);
+ while (waitpid(ldd_pid, &err, 0) == -1) {
+ if (errno != EINTR) {
+ log_error("ERROR: waitpid() failed: %m");
+ return 1;
+ }
+ }
+ err = WIFSIGNALED(err) ? 128 + WTERMSIG(err) : WEXITSTATUS(err);
+ /* ldd has error conditions we largely don't care about ("not a dynamic executable", &c.):
+ only error out on hard errors (ENOENT, ENOEXEC, signals) */
+ if (err >= 126) {
+ log_error("ERROR: '%s %s' failed with %d", ldd, fullsrcpath, err);
+ return err;
+ } else
+ return ret;
+}
+
+/* Install ".<filename>.hmac" file for FIPS self-checks */
+static int hmac_install(const char *src, const char *dst, const char *hmacpath)
+{
+ _cleanup_free_ char *srchmacname = NULL;
+ _cleanup_free_ char *dsthmacname = NULL;
+
+ 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");
+ }
+
+ if (hmacpath) {
+ _asprintf(&srchmacname, "%s/%s.hmac", hmacpath, &src[dlen + 1]);
+ _asprintf(&dsthmacname, "%s/%s.hmac", hmacpath, &src[dlen + 1]);
+ } else {
+ _asprintf(&srchmacname, "%.*s/.%s.hmac", (int)dlen, src, &src[dlen + 1]);
+ _asprintf(&dsthmacname, "%.*s/.%s.hmac", (int)dir_len(dst), dst, &src[dlen + 1]);
+ }
+ log_debug("hmac cp '%s' '%s'", srchmacname, dsthmacname);
+ dracut_install(srchmacname, dsthmacname, false, false, true);
+ return 0;
+}
+
+void mark_hostonly(const char *path)
+{
+ _cleanup_free_ char *fulldstpath = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+
+ _asprintf(&fulldstpath, "%s/lib/dracut/hostonly-files", destrootdir);
+
+ 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_mkdir(const char *src)
+{
+ _cleanup_free_ char *parent = NULL;
+ char *path;
+ struct stat sb;
+
+ parent = strdup(src);
+ if (!parent)
+ return 1;
+
+ path = parent[0] == '/' ? parent + 1 : parent;
+ while (path) {
+ path = strstr(path, "/");
+ if (path)
+ *path = '\0';
+
+ if (stat(parent, &sb) == 0) {
+ if (!S_ISDIR(sb.st_mode)) {
+ log_error("%s exists but is not a directory!", parent);
+ return 1;
+ }
+ } else if (errno != ENOENT) {
+ log_error("ERROR: stat '%s': %m", parent);
+ return 1;
+ } else {
+ if (mkdir(parent, 0755) < 0) {
+ log_error("ERROR: mkdir '%s': %m", parent);
+ return 1;
+ }
+ }
+
+ if (path) {
+ *path = '/';
+ path++;
+ }
+ }
+
+ return 0;
+}
+
+static int dracut_install(const char *orig_src, const char *orig_dst, bool isdir, bool resolvedeps, bool hashdst)
+{
+ struct stat sb;
+ _cleanup_free_ char *fullsrcpath = NULL;
+ _cleanup_free_ char *fulldstpath = NULL;
+ _cleanup_free_ char *fulldstdir = NULL;
+ int ret;
+ bool src_islink = false;
+ bool src_isdir = false;
+ mode_t src_mode = 0;
+ bool dst_exists = true;
+ char *i = NULL;
+ const char *src, *dst;
+
+ if (sysrootdirlen) {
+ if (strncmp(orig_src, sysrootdir, sysrootdirlen) == 0) {
+ src = orig_src + sysrootdirlen;
+ fullsrcpath = strdup(orig_src);
+ } else {
+ src = orig_src;
+ _asprintf(&fullsrcpath, "%s%s", sysrootdir, src);
+ }
+ if (strncmp(orig_dst, sysrootdir, sysrootdirlen) == 0)
+ dst = orig_dst + sysrootdirlen;
+ else
+ dst = orig_dst;
+ } else {
+ src = orig_src;
+ fullsrcpath = strdup(src);
+ dst = orig_dst;
+ }
+
+ log_debug("dracut_install('%s', '%s', %d, %d, %d)", src, dst, isdir, resolvedeps, hashdst);
+
+ if (check_hashmap(items_failed, src)) {
+ log_debug("hash hit items_failed for '%s'", src);
+ return 1;
+ }
+
+ if (hashdst && check_hashmap(items, dst)) {
+ log_debug("hash hit items for '%s'", dst);
+ return 0;
+ }
+
+ if (lstat(fullsrcpath, &sb) < 0) {
+ if (!isdir) {
+ i = strdup(src);
+ hashmap_put(items_failed, i, i);
+ /* src does not exist */
+ return 1;
+ }
+ } else {
+ src_islink = S_ISLNK(sb.st_mode);
+ src_isdir = S_ISDIR(sb.st_mode);
+ src_mode = sb.st_mode;
+ }
+
+ _asprintf(&fulldstpath, "%s/%s", destrootdir, (dst[0] == '/' ? (dst + 1) : dst));
+
+ ret = stat(fulldstpath, &sb);
+ if (ret != 0) {
+ dst_exists = false;
+ if (errno != ENOENT) {
+ log_error("ERROR: stat '%s': %m", fulldstpath);
+ return 1;
+ }
+ }
+
+ if (ret == 0) {
+ 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(fulldstpath + sysrootdirlen);
+ } else
+ log_debug("'%s' already exists", fulldstpath);
+
+ /* dst does already exist */
+ } else {
+
+ /* check destination directory */
+ fulldstdir = strndup(fulldstpath, dir_len(fulldstpath));
+ if (!fulldstdir) {
+ log_error("Out of memory!");
+ return 1;
+ }
+
+ ret = access(fulldstdir, F_OK);
+
+ if (ret < 0) {
+ _cleanup_free_ char *dname = NULL;
+
+ if (errno != ENOENT) {
+ log_error("ERROR: stat '%s': %m", fulldstdir);
+ return 1;
+ }
+ /* create destination directory */
+ log_debug("dest dir '%s' does not exist", fulldstdir);
+
+ dname = strndup(dst, dir_len(dst));
+ if (!dname)
+ return 1;
+ ret = dracut_install(dname, dname, true, false, true);
+
+ if (ret != 0) {
+ log_error("ERROR: failed to create directory '%s'", fulldstdir);
+ return 1;
+ }
+ }
+
+ if (src_isdir) {
+ if (dst_exists) {
+ if (S_ISDIR(sb.st_mode)) {
+ log_debug("dest dir '%s' already exists", fulldstpath);
+ return 0;
+ }
+ log_error("dest dir '%s' already exists but is not a directory", fulldstpath);
+ return 1;
+ }
+
+ log_info("mkdir '%s'", fulldstpath);
+ ret = dracut_mkdir(fulldstpath);
+ if (ret == 0) {
+ i = strdup(dst);
+ if (!i)
+ return -ENOMEM;
+
+ hashmap_put(items, i, i);
+ }
+ return ret;
+ }
+
+ /* ready to install src */
+
+ if (src_islink) {
+ _cleanup_free_ char *abspath = NULL;
+
+ abspath = get_real_file(src, false);
+
+ if (abspath == NULL)
+ return 1;
+
+ if (dracut_install(abspath, abspath, false, resolvedeps, hashdst)) {
+ log_debug("'%s' install error", abspath);
+ return 1;
+ }
+
+ if (faccessat(AT_FDCWD, abspath, F_OK, AT_SYMLINK_NOFOLLOW) != 0) {
+ log_debug("lstat '%s': %m", abspath);
+ return 1;
+ }
+
+ if (faccessat(AT_FDCWD, fulldstpath, F_OK, AT_SYMLINK_NOFOLLOW) != 0) {
+ _cleanup_free_ char *absdestpath = NULL;
+
+ _asprintf(&absdestpath, "%s/%s", destrootdir,
+ (abspath[0] == '/' ? (abspath + 1) : abspath) + sysrootdirlen);
+
+ ln_r(absdestpath, fulldstpath);
+ }
+
+ if (arg_hmac) {
+ /* copy .hmac files also */
+ hmac_install(src, dst, NULL);
+ }
+
+ return 0;
+ }
+
+ if (src_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+ if (resolvedeps)
+ ret += resolve_deps(fullsrcpath + sysrootdirlen);
+ if (arg_hmac) {
+ /* copy .hmac files also */
+ hmac_install(src, dst, NULL);
+ }
+ }
+
+ log_debug("dracut_install ret = %d", ret);
+
+ if (arg_hostonly && !arg_module)
+ mark_hostonly(dst);
+
+ if (isdir) {
+ log_info("mkdir '%s'", fulldstpath);
+ ret += dracut_mkdir(fulldstpath);
+ } else {
+ log_info("cp '%s' '%s'", fullsrcpath, fulldstpath);
+ ret += cp(fullsrcpath, fulldstpath);
+ }
+ }
+
+ if (ret == 0) {
+ i = strdup(dst);
+ if (!i)
+ return -ENOMEM;
+
+ hashmap_put(items, i, i);
+
+ if (logfile_f)
+ dracut_log_cp(src);
+ }
+
+ log_debug("dracut_install ret = %d", ret);
+
+ return ret;
+}
+
+static void usage(int status)
+{
+ /* */
+ printf("Usage: %s -D DESTROOTDIR [-r SYSROOTDIR] [OPTION]... -a SOURCE...\n"
+ "or: %s -D DESTROOTDIR [-r SYSROOTDIR] [OPTION]... SOURCE DEST\n"
+ "or: %s -D DESTROOTDIR [-r SYSROOTDIR] [OPTION]... -m KERNELMODULE [KERNELMODULE …]\n"
+ "\n"
+ "Install SOURCE (from rootfs or SYSROOTDIR) 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"
+ " -r --sysrootdir Install all files from SYSROOTDIR\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"
+ " (default: /lib/modules/$(uname -r))\n"
+ " --firmwaredirs Specify the firmware directory search path with : separation\n"
+ " (default: $DRACUT_FIRMWARE_PATH, otherwise kernel-compatible\n"
+ " $(</sys/module/firmware_class/parameters/path),\n"
+ " /lib/firmware/updates/$(uname -r), /lib/firmware/updates\n"
+ " /lib/firmware/$(uname -r), /lib/firmware)\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);
+}
+
+static int parse_argv(int argc, char *argv[])
+{
+ int c;
+
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_SILENT,
+ ARG_MODALIAS,
+ ARG_KERNELDIR,
+ ARG_FIRMWAREDIRS,
+ ARG_DEBUG
+ };
+
+ static struct option const options[] = {
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, ARG_VERSION},
+ {"dir", no_argument, NULL, 'd'},
+ {"debug", no_argument, NULL, ARG_DEBUG},
+ {"verbose", no_argument, NULL, 'v'},
+ {"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'},
+ {"module", no_argument, NULL, 'm'},
+ {"fips", no_argument, NULL, 'f'},
+ {"destrootdir", required_argument, NULL, 'D'},
+ {"sysrootdir", required_argument, NULL, 'r'},
+ {"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, "madfhlL:oD:Hr:Rp:P:s:S:N:v", options, NULL)) != -1) {
+ switch (c) {
+ case ARG_VERSION:
+ puts(PROGRAM_VERSION_STRING);
+ return 0;
+ case 'd':
+ arg_createdir = true;
+ break;
+ 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;
+ case 'o':
+ arg_optional = true;
+ break;
+ case 'l':
+ arg_resolvedeps = true;
+ break;
+ case 'R':
+ arg_resolvelazy = true;
+ break;
+ case 'a':
+ arg_all = true;
+ break;
+ case 'm':
+ arg_module = true;
+ break;
+ case 'D':
+ destrootdir = optarg;
+ break;
+ case 'r':
+ sysrootdir = optarg;
+ sysrootdirlen = strlen(sysrootdir);
+ 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 = optarg;
+ break;
+ case ARG_KERNELDIR:
+ kerneldir = 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;
+ default:
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ if (arg_loglevel >= 0) {
+ log_set_max_level(arg_loglevel);
+ }
+
+ struct utsname buf = {0};
+ if (!kerneldir) {
+ uname(&buf);
+ _asprintf(&kerneldir, "/lib/modules/%s", buf.release);
+ }
+
+ if (arg_modalias) {
+ return 1;
+ }
+
+ if (arg_module) {
+ if (!firmwaredirs) {
+ char *path = getenv("DRACUT_FIRMWARE_PATH");
+
+ if (path) {
+ log_debug("DRACUT_FIRMWARE_PATH=%s", path);
+ firmwaredirs = strv_split(path, ":");
+ } else {
+ if (!*buf.release)
+ uname(&buf);
+
+ char fw_path_para[PATH_MAX + 1] = "";
+ int path = open("/sys/module/firmware_class/parameters/path", O_RDONLY | O_CLOEXEC);
+ if (path != -1) {
+ ssize_t rd = read(path, fw_path_para, PATH_MAX);
+ if (rd != -1)
+ fw_path_para[rd - 1] = '\0';
+ close(path);
+ }
+ char uk[22 + sizeof(buf.release)], fk[14 + sizeof(buf.release)];
+ sprintf(uk, "/lib/firmware/updates/%s", buf.release);
+ sprintf(fk, "/lib/firmware/%s", buf.release);
+ firmwaredirs = strv_new(STRV_IFNOTNULL(*fw_path_para ? fw_path_para : NULL),
+ uk,
+ "/lib/firmware/updates",
+ fk,
+ "/lib/firmware",
+ NULL);
+ }
+ }
+ }
+
+ if (!optind || optind == argc) {
+ if (!arg_optional) {
+ log_error("No SOURCE argument given");
+ usage(EXIT_FAILURE);
+ } else {
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ return 1;
+}
+
+static int resolve_lazy(int argc, char **argv)
+{
+ int i;
+ size_t destrootdirlen = strlen(destrootdir);
+ int ret = 0;
+ char *item;
+ for (i = 0; i < argc; i++) {
+ const char *src = argv[i];
+ char *p = argv[i];
+
+ log_debug("resolve_deps('%s')", src);
+
+ if (strstr(src, destrootdir)) {
+ p = &argv[i][destrootdirlen];
+ }
+
+ if (check_hashmap(items, p)) {
+ continue;
+ }
+
+ item = strdup(p);
+ hashmap_put(items, item, item);
+
+ ret += resolve_deps(src);
+ }
+ return ret;
+}
+
+static char **find_binary(const char *src)
+{
+ char **ret = NULL;
+ char **q;
+ char *newsrc = NULL;
+
+ STRV_FOREACH(q, pathdirs) {
+ char *fullsrcpath;
+
+ _asprintf(&newsrc, "%s/%s", *q, src);
+
+ fullsrcpath = get_real_file(newsrc, false);
+ if (!fullsrcpath) {
+ log_debug("get_real_file(%s) not found", newsrc);
+ free(newsrc);
+ newsrc = NULL;
+ continue;
+ }
+
+ if (faccessat(AT_FDCWD, fullsrcpath, F_OK, AT_SYMLINK_NOFOLLOW) != 0) {
+ log_debug("lstat(%s) != 0", fullsrcpath);
+ free(newsrc);
+ newsrc = NULL;
+ free(fullsrcpath);
+ fullsrcpath = NULL;
+ continue;
+ }
+
+ strv_push(&ret, newsrc);
+
+ free(fullsrcpath);
+ fullsrcpath = NULL;
+ };
+
+ 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 = EXIT_SUCCESS;
+ int i;
+ for (i = 0; i < argc; i++) {
+ int ret = 0;
+ log_debug("Handle '%s'", argv[i]);
+
+ if (strchr(argv[i], '/') == NULL) {
+ 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 {
+ if (strchr(argv[i], '*') == NULL) {
+ ret = dracut_install(argv[i], argv[i], arg_createdir, arg_resolvedeps, true);
+ } else {
+ _cleanup_free_ char *realsrc = NULL;
+ _cleanup_globfree_ glob_t globbuf;
+
+ _asprintf(&realsrc, "%s%s", sysrootdir ? sysrootdir : "", argv[i]);
+
+ ret = glob(realsrc, 0, NULL, &globbuf);
+ if (ret == 0) {
+ size_t j;
+
+ for (j = 0; j < globbuf.gl_pathc; j++) {
+ ret |= dracut_install(globbuf.gl_pathv[j] + sysrootdirlen,
+ globbuf.gl_pathv[j] + sysrootdirlen,
+ 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_fullpath(const char *fwpath)
+{
+ const char *fw = fwpath;
+ _cleanup_free_ char *fwpath_compressed = NULL;
+ int ret;
+ if (access(fwpath, F_OK) != 0) {
+ _asprintf(&fwpath_compressed, "%s.zst", fwpath);
+ if (access(fwpath_compressed, F_OK) != 0) {
+ strcpy(fwpath_compressed + strlen(fwpath) + 1, "xz");
+ if (access(fwpath_compressed, F_OK) != 0) {
+ log_debug("stat(%s) != 0", fwpath);
+ return 1;
+ }
+ }
+ fw = fwpath_compressed;
+ }
+ ret = dracut_install(fw, fw, false, false, true);
+ if (ret == 0) {
+ log_debug("dracut_install '%s' OK", fwpath);
+ }
+ return ret;
+}
+
+static int install_firmware(struct kmod_module *mod)
+{
+ struct kmod_list *l = NULL;
+ _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;
+ bool found_this = false;
+
+ 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;
+
+ _asprintf(&fwpath, "%s/%s", *q, value);
+
+ if (strpbrk(value, "*?[") != NULL
+ && access(fwpath, F_OK) != 0) {
+ size_t i;
+ _cleanup_globfree_ glob_t globbuf;
+
+ glob(fwpath, 0, NULL, &globbuf);
+ for (i = 0; i < globbuf.gl_pathc; i++) {
+ ret = install_firmware_fullpath(globbuf.gl_pathv[i]);
+ if (ret == 0)
+ found_this = true;
+ }
+ } else {
+ ret = install_firmware_fullpath(fwpath);
+ if (ret == 0)
+ found_this = true;
+ }
+ }
+ if (!found_this) {
+ /* firmware path was not found in any firmwaredirs */
+ log_info("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 = NULL;
+ _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 find_kmod_module_from_sysfs_node(struct kmod_ctx *ctx, const char *sysfs_node, int sysfs_node_len,
+ struct kmod_list **modules)
+{
+ char modalias_path[PATH_MAX];
+ if (snprintf(modalias_path, sizeof(modalias_path), "%.*s/modalias", sysfs_node_len,
+ sysfs_node) >= sizeof(modalias_path))
+ return -1;
+
+ _cleanup_close_ int modalias_file = -1;
+ if ((modalias_file = open(modalias_path, O_RDONLY | O_CLOEXEC)) == -1)
+ return 0;
+
+ char alias[page_size()];
+ ssize_t len = read(modalias_file, alias, sizeof(alias));
+ alias[len - 1] = '\0';
+
+ return kmod_module_new_from_lookup(ctx, alias, modules);
+}
+
+static int find_modules_from_sysfs_node(struct kmod_ctx *ctx, const char *sysfs_node, Hashmap *modules)
+{
+ _cleanup_kmod_module_unref_list_ struct kmod_list *list = NULL;
+ struct kmod_list *l = NULL;
+
+ if (find_kmod_module_from_sysfs_node(ctx, sysfs_node, strlen(sysfs_node), &list) >= 0) {
+ kmod_list_foreach(l, list) {
+ struct kmod_module *mod = kmod_module_get_module(l);
+ char *module = strdup(kmod_module_get_name(mod));
+ kmod_module_unref(mod);
+
+ if (hashmap_put(modules, module, module) < 0)
+ free(module);
+ }
+ }
+
+ return 0;
+}
+
+static void find_suppliers_for_sys_node(struct kmod_ctx *ctx, Hashmap *suppliers, const char *node_path_raw,
+ size_t node_path_len)
+{
+ char node_path[PATH_MAX];
+ char real_path[PATH_MAX];
+
+ memcpy(node_path, node_path_raw, node_path_len);
+ node_path[node_path_len] = '\0';
+
+ DIR *d;
+ struct dirent *dir;
+ while (realpath(node_path, real_path) != NULL && strcmp(real_path, "/sys/devices")) {
+ d = opendir(node_path);
+ if (d) {
+ size_t real_path_len = strlen(real_path);
+ while ((dir = readdir(d)) != NULL) {
+ if (strstr(dir->d_name, "supplier:platform") != NULL) {
+ if (snprintf(real_path + real_path_len, sizeof(real_path) - real_path_len, "/%s/supplier",
+ dir->d_name) < sizeof(real_path) - real_path_len) {
+ char *real_supplier_path = realpath(real_path, NULL);
+ if (real_supplier_path != NULL)
+ if (hashmap_put(suppliers, real_supplier_path, real_supplier_path) < 0)
+ free(real_supplier_path);
+ }
+ }
+ }
+ closedir(d);
+ }
+ strncat(node_path, "/..", 3); // Also find suppliers of parents
+ }
+}
+
+static void find_suppliers(struct kmod_ctx *ctx)
+{
+ _cleanup_fts_close_ FTS *fts;
+ char *paths[] = { "/sys/devices/platform", NULL };
+ fts = fts_open(paths, FTS_NOSTAT | FTS_PHYSICAL, NULL);
+
+ for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
+ if (strcmp(ftsent->fts_name, "modalias") == 0) {
+ _cleanup_kmod_module_unref_list_ struct kmod_list *list = NULL;
+ struct kmod_list *l;
+
+ if (find_kmod_module_from_sysfs_node(ctx, ftsent->fts_parent->fts_path, ftsent->fts_parent->fts_pathlen, &list) < 0)
+ continue;
+
+ kmod_list_foreach(l, list) {
+ _cleanup_kmod_module_unref_ struct kmod_module *mod = kmod_module_get_module(l);
+ const char *name = kmod_module_get_name(mod);
+ Hashmap *suppliers = hashmap_get(modules_suppliers, name);
+ if (suppliers == NULL) {
+ suppliers = hashmap_new(string_hash_func, string_compare_func);
+ hashmap_put(modules_suppliers, strdup(name), suppliers);
+ }
+
+ find_suppliers_for_sys_node(ctx, suppliers, ftsent->fts_parent->fts_path, ftsent->fts_parent->fts_pathlen);
+ }
+ }
+ }
+}
+
+static Hashmap *find_suppliers_paths_for_module(const char *module)
+{
+ return hashmap_get(modules_suppliers, module);
+}
+
+static int install_dependent_module(struct kmod_ctx *ctx, struct kmod_module *mod, Hashmap *suppliers_paths, int *err)
+{
+ const char *path = NULL;
+ const char *name = NULL;
+
+ path = kmod_module_get_path(mod);
+
+ if (path == NULL)
+ return 0;
+
+ if (check_hashmap(items_failed, path))
+ return -1;
+
+ if (check_hashmap(items, &path[kerneldirlen])) {
+ return 0;
+ }
+
+ name = kmod_module_get_name(mod);
+
+ if (arg_mod_filter_noname && (regexec(&mod_filter_noname, name, 0, NULL, 0) == 0)) {
+ return 0;
+ }
+
+ *err = dracut_install(path, &path[kerneldirlen], false, false, true);
+ if (*err == 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);
+ *err = install_dependent_modules(ctx, modlist, suppliers_paths);
+ if (*err == 0) {
+ *err = kmod_module_get_softdeps(mod, &modpre, &modpost);
+ if (*err == 0) {
+ int r;
+ *err = install_dependent_modules(ctx, modpre, NULL);
+ r = install_dependent_modules(ctx, modpost, NULL);
+ *err = *err ? : r;
+ }
+ }
+ } else {
+ log_error("dracut_install '%s' '%s' ERROR", path, &path[kerneldirlen]);
+ }
+
+ return 0;
+}
+
+static int install_dependent_modules(struct kmod_ctx *ctx, struct kmod_list *modlist, Hashmap *suppliers_paths)
+{
+ struct kmod_list *itr = NULL;
+ int ret = 0;
+
+ kmod_list_foreach(itr, modlist) {
+ _cleanup_kmod_module_unref_ struct kmod_module *mod = NULL;
+ mod = kmod_module_get_module(itr);
+ if (install_dependent_module(ctx, mod, find_suppliers_paths_for_module(kmod_module_get_name(mod)), &ret))
+ return -1;
+ }
+
+ const char *supplier_path;
+ Iterator i;
+ HASHMAP_FOREACH(supplier_path, suppliers_paths, i) {
+ if (check_hashmap(processed_suppliers, supplier_path))
+ continue;
+
+ char *path = strdup(supplier_path);
+ hashmap_put(processed_suppliers, path, path);
+
+ _cleanup_destroy_hashmap_ Hashmap *modules = hashmap_new(string_hash_func, string_compare_func);
+ find_modules_from_sysfs_node(ctx, supplier_path, modules);
+
+ _cleanup_destroy_hashmap_ Hashmap *suppliers = hashmap_new(string_hash_func, string_compare_func);
+ find_suppliers_for_sys_node(ctx, suppliers, supplier_path, strlen(supplier_path));
+
+ if (!hashmap_isempty(modules)) { // Supplier is a module
+ const char *module;
+ Iterator j;
+ HASHMAP_FOREACH(module, modules, j) {
+ _cleanup_kmod_module_unref_ struct kmod_module *mod = NULL;
+ if (!kmod_module_new_from_name(ctx, module, &mod)) {
+ if (install_dependent_module(ctx, mod, suppliers, &ret))
+ return -1;
+ }
+ }
+ } else { // Supplier is builtin
+ install_dependent_modules(ctx, NULL, suppliers);
+ }
+ }
+
+ return ret;
+}
+
+static int install_module(struct kmod_ctx *ctx, 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);
+
+ path = kmod_module_get_path(mod);
+ if (!path) {
+ log_debug("dracut_install '%s' is a builtin kernel module", name);
+ return 0;
+ }
+
+ 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;
+ }
+
+ 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);
+
+ Hashmap *suppliers = find_suppliers_paths_for_module(name);
+ modlist = kmod_module_get_dependencies(mod);
+ ret = install_dependent_modules(ctx, modlist, suppliers);
+
+ if (ret == 0) {
+ ret = kmod_module_get_softdeps(mod, &modpre, &modpost);
+ if (ret == 0) {
+ int r;
+ ret = install_dependent_modules(ctx, modpre, NULL);
+ r = install_dependent_modules(ctx, modpost, NULL);
+ ret = ret ? : r;
+ }
+ }
+
+ return ret;
+}
+
+static int modalias_list(struct kmod_ctx *ctx)
+{
+ int err;
+ struct kmod_list *loaded_list = NULL;
+ struct kmod_list *l = NULL;
+ struct kmod_list *itr = NULL;
+ _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;
+
+ int err;
+
+ char alias[2048] = {0};
+ 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 = NULL;
+
+ 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;
+
+ modules_suppliers = hashmap_new(string_hash_func, string_compare_func);
+ find_suppliers(ctx);
+
+ 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(ctx, 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 */
+ _asprintf(&path2, "%s/kernel/%s", kerneldir, &argv[i][1]);
+ _asprintf(&path1, "%s/extra/%s", kerneldir, &argv[i][1]);
+ _asprintf(&path3, "%s/updates/%s", kerneldir, &argv[i][1]);
+
+ {
+ 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)) {
+ _cleanup_kmod_module_unref_list_ struct kmod_list *modlist = NULL;
+ _cleanup_free_ const char *modname = NULL;
+
+ 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;
+ }
+
+ /* Check, if we have to load another module with that name instead */
+ modname = strdup(kmod_module_get_name(mod_o));
+
+ 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(ctx, 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;
+ }
+ }
+ if (errno) {
+ log_error("FTS ERROR: %m");
+ }
+ } else {
+ _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;
+ }
+ if (endswith(modname, ".ko.zst")) {
+ int len = strlen(modname);
+ modname[len - 7] = 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(ctx, 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 ((modinst != 0) && (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;
+ char *env_no_xattr = NULL;
+
+ log_set_target(LOG_TARGET_CONSOLE);
+ log_parse_environment();
+ log_open();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+ 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);
+ }
+
+ log_debug("Program arguments:");
+ for (r = 0; r < argc; r++)
+ log_debug("%s", argv[r]);
+
+ path = getenv("DRACUT_INSTALL_PATH");
+ if (path == NULL)
+ path = getenv("PATH");
+
+ if (path == NULL) {
+ log_error("PATH is not set");
+ exit(EXIT_FAILURE);
+ }
+
+ log_debug("PATH=%s", path);
+
+ ldd = getenv("DRACUT_LDD");
+ if (ldd == NULL)
+ ldd = "ldd";
+ log_debug("LDD=%s", ldd);
+
+ env_no_xattr = getenv("DRACUT_NO_XATTR");
+ if (env_no_xattr != NULL)
+ no_xattr = true;
+
+ pathdirs = strv_split(path, ":");
+
+ umask(0022);
+
+ if (destrootdir == NULL || strlen(destrootdir) == 0) {
+ destrootdir = getenv("DESTROOTDIR");
+ if (destrootdir == NULL || strlen(destrootdir) == 0) {
+ log_error("Environment DESTROOTDIR or argument -D is not set!");
+ usage(EXIT_FAILURE);
+ }
+ }
+
+ if (strcmp(destrootdir, "/") == 0) {
+ log_error("Environment DESTROOTDIR or argument -D is set to '/'!");
+ usage(EXIT_FAILURE);
+ }
+
+ i = destrootdir;
+ if (!(destrootdir = realpath(i, NULL))) {
+ log_error("Environment DESTROOTDIR or argument -D is set to '%s': %m", i);
+ r = EXIT_FAILURE;
+ goto finish2;
+ }
+
+ items = hashmap_new(string_hash_func, string_compare_func);
+ items_failed = hashmap_new(string_hash_func, string_compare_func);
+ processed_suppliers = hashmap_new(string_hash_func, string_compare_func);
+
+ if (!items || !items_failed || !processed_suppliers || !modules_loaded) {
+ log_error("Out of memory");
+ r = EXIT_FAILURE;
+ goto finish1;
+ }
+
+ if (logdir) {
+ _asprintf(&logfile, "%s/%d.log", logdir, getpid());
+
+ logfile_f = fopen(logfile, "a");
+ if (logfile_f == NULL) {
+ log_error("Could not open %s for logging: %m", logfile);
+ r = EXIT_FAILURE;
+ goto finish1;
+ }
+ }
+
+ r = EXIT_SUCCESS;
+
+ if (((optind + 1) < argc) && (strcmp(argv[optind + 1], destrootdir) == 0)) {
+ /* ugly hack for compat mode "inst src $destrootdir" */
+ if ((optind + 2) == argc) {
+ argc--;
+ } else {
+ /* ugly hack for compat mode "inst src $destrootdir dst" */
+ if ((optind + 3) == argc) {
+ argc--;
+ argv[optind + 1] = argv[optind + 2];
+ }
+ }
+ }
+
+ 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 = install_one(argv[optind], argv[optind + 1]);
+ }
+
+ if (arg_optional)
+ r = EXIT_SUCCESS;
+
+finish1:
+ free(destrootdir);
+finish2:
+ 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);
+
+ while ((i = hashmap_steal_first(items_failed)))
+ item_free(i);
+
+ Hashmap *h;
+ while ((h = hashmap_steal_first(modules_suppliers))) {
+ while ((i = hashmap_steal_first(h))) {
+ item_free(i);
+ }
+ hashmap_free(h);
+ }
+
+ while ((i = hashmap_steal_first(processed_suppliers)))
+ item_free(i);
+
+ hashmap_free(items);
+ hashmap_free(items_failed);
+ hashmap_free(modules_loaded);
+ hashmap_free(modules_suppliers);
+ hashmap_free(processed_suppliers);
+
+ strv_free(firmwaredirs);
+ strv_free(pathdirs);
+ return r;
+}
diff --git a/src/install/hashmap.c b/src/install/hashmap.c
new file mode 100644
index 0000000..ff0f681
--- /dev/null
+++ b/src/install/hashmap.c
@@ -0,0 +1,658 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "hashmap.h"
+#include "macro.h"
+
+#define NBUCKETS 127
+
+struct hashmap_entry {
+ const void *key;
+ void *value;
+ struct hashmap_entry *bucket_next, *bucket_previous;
+ struct hashmap_entry *iterate_next, *iterate_previous;
+};
+
+struct Hashmap {
+ hash_func_t hash_func;
+ compare_func_t compare_func;
+
+ struct hashmap_entry *iterate_list_head, *iterate_list_tail;
+ unsigned int n_entries;
+};
+
+#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
+
+unsigned int string_hash_func(const void *p)
+{
+ unsigned int hash = 5381;
+ const signed char *c;
+
+ /* DJB's hash function */
+
+ for (c = p; *c; c++)
+ hash = (hash << 5) + hash + (unsigned int)*c;
+
+ return hash;
+}
+
+int string_compare_func(const void *a, const void *b)
+{
+ return strcmp(a, b);
+}
+
+unsigned int trivial_hash_func(const void *p)
+{
+ return PTR_TO_UINT(p);
+}
+
+int trivial_compare_func(const void *a, const void *b)
+{
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func)
+{
+ Hashmap *h;
+ size_t size;
+
+ size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry *);
+
+ h = malloc0(size);
+
+ if (!h)
+ return NULL;
+
+ h->hash_func = hash_func ? hash_func : trivial_hash_func;
+ h->compare_func = compare_func ? compare_func : trivial_compare_func;
+
+ h->n_entries = 0;
+ h->iterate_list_head = h->iterate_list_tail = NULL;
+
+ return h;
+}
+
+int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func)
+{
+ assert(h);
+
+ if (*h)
+ return 0;
+
+ if (!(*h = hashmap_new(hash_func, compare_func)))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned int hash)
+{
+ assert(h);
+ assert(e);
+
+ /* Insert into hash table */
+ e->bucket_next = BY_HASH(h)[hash];
+ e->bucket_previous = NULL;
+ if (BY_HASH(h)[hash])
+ BY_HASH(h)[hash]->bucket_previous = e;
+ BY_HASH(h)[hash] = e;
+
+ /* Insert into iteration list */
+ e->iterate_previous = h->iterate_list_tail;
+ e->iterate_next = NULL;
+ if (h->iterate_list_tail) {
+ assert(h->iterate_list_head);
+ h->iterate_list_tail->iterate_next = e;
+ } else {
+ assert(!h->iterate_list_head);
+ h->iterate_list_head = e;
+ }
+ h->iterate_list_tail = e;
+
+ h->n_entries++;
+ assert(h->n_entries >= 1);
+}
+
+static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned int hash)
+{
+ assert(h);
+ assert(e);
+
+ /* Remove from iteration list */
+ if (e->iterate_next)
+ e->iterate_next->iterate_previous = e->iterate_previous;
+ else
+ h->iterate_list_tail = e->iterate_previous;
+
+ if (e->iterate_previous)
+ e->iterate_previous->iterate_next = e->iterate_next;
+ else
+ h->iterate_list_head = e->iterate_next;
+
+ /* Remove from hash table bucket list */
+ if (e->bucket_next)
+ e->bucket_next->bucket_previous = e->bucket_previous;
+
+ if (e->bucket_previous)
+ e->bucket_previous->bucket_next = e->bucket_next;
+ else
+ BY_HASH(h)[hash] = e->bucket_next;
+
+ assert(h->n_entries >= 1);
+ h->n_entries--;
+}
+
+static void remove_entry(Hashmap *h, struct hashmap_entry **ep)
+{
+ struct hashmap_entry *e = *ep;
+ unsigned int hash;
+
+ assert(h);
+ assert(e);
+
+ hash = h->hash_func(e->key) % NBUCKETS;
+
+ unlink_entry(h, e, hash);
+
+ free(e);
+ *ep = NULL;
+}
+
+void hashmap_free(Hashmap *h)
+{
+
+ if (!h)
+ return;
+
+ hashmap_clear(h);
+
+ free(h);
+}
+
+void hashmap_free_free(Hashmap *h)
+{
+ void *p;
+
+ while ((p = hashmap_steal_first(h)))
+ free(p);
+
+ hashmap_free(h);
+}
+
+void hashmap_clear(Hashmap *h)
+{
+ if (!h)
+ return;
+
+ while (h->iterate_list_head) {
+ struct hashmap_entry *e = h->iterate_list_head;
+ remove_entry(h, &e);
+ }
+}
+
+static struct hashmap_entry *hash_scan(Hashmap *h, unsigned int hash, const void *key)
+{
+ struct hashmap_entry *e;
+ assert(h);
+ assert(hash < NBUCKETS);
+
+ for (e = BY_HASH(h)[hash]; e; e = e->bucket_next)
+ if (h->compare_func(e->key, key) == 0)
+ return e;
+
+ return NULL;
+}
+
+int hashmap_put(Hashmap *h, const void *key, void *value)
+{
+ struct hashmap_entry *e;
+ unsigned int hash;
+
+ assert(h);
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if ((e = hash_scan(h, hash, key))) {
+
+ if (e->value == value)
+ return 0;
+
+ return -EEXIST;
+ }
+
+ e = new(struct hashmap_entry, 1);
+
+ if (!e)
+ return -ENOMEM;
+
+ e->key = key;
+ e->value = value;
+
+ link_entry(h, e, hash);
+
+ return 1;
+}
+
+int hashmap_replace(Hashmap *h, const void *key, void *value)
+{
+ struct hashmap_entry *e;
+ unsigned int hash;
+
+ assert(h);
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if ((e = hash_scan(h, hash, key))) {
+ e->key = key;
+ e->value = value;
+ return 0;
+ }
+
+ return hashmap_put(h, key, value);
+}
+
+void *hashmap_get(Hashmap *h, const void *key)
+{
+ unsigned int hash;
+ struct hashmap_entry *e;
+
+ if (!h)
+ return NULL;
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if (!(e = hash_scan(h, hash, key)))
+ return NULL;
+
+ return e->value;
+}
+
+void *hashmap_remove(Hashmap *h, const void *key)
+{
+ struct hashmap_entry *e;
+ unsigned int hash;
+ void *data;
+
+ if (!h)
+ return NULL;
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if (!(e = hash_scan(h, hash, key)))
+ return NULL;
+
+ data = e->value;
+ remove_entry(h, &e);
+
+ return data;
+}
+
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value)
+{
+ struct hashmap_entry *e;
+ unsigned int old_hash, new_hash;
+
+ if (!h)
+ return -ENOENT;
+
+ old_hash = h->hash_func(old_key) % NBUCKETS;
+ if (!(e = hash_scan(h, old_hash, old_key)))
+ return -ENOENT;
+
+ new_hash = h->hash_func(new_key) % NBUCKETS;
+ if (hash_scan(h, new_hash, new_key))
+ return -EEXIST;
+
+ unlink_entry(h, e, old_hash);
+
+ e->key = new_key;
+ e->value = value;
+
+ link_entry(h, e, new_hash);
+
+ return 0;
+}
+
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value)
+{
+ struct hashmap_entry *e, *k;
+ unsigned int old_hash, new_hash;
+
+ if (!h)
+ return -ENOENT;
+
+ old_hash = h->hash_func(old_key) % NBUCKETS;
+ if (!(e = hash_scan(h, old_hash, old_key)))
+ return -ENOENT;
+
+ new_hash = h->hash_func(new_key) % NBUCKETS;
+
+ if ((k = hash_scan(h, new_hash, new_key)))
+ if (e != k)
+ remove_entry(h, &k);
+
+ unlink_entry(h, e, old_hash);
+
+ e->key = new_key;
+ e->value = value;
+
+ link_entry(h, e, new_hash);
+
+ return 0;
+}
+
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value)
+{
+ struct hashmap_entry *e;
+ unsigned int hash;
+
+ if (!h)
+ return NULL;
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if (!(e = hash_scan(h, hash, key)))
+ return NULL;
+
+ if (e->value != value)
+ return NULL;
+
+ remove_entry(h, &e);
+
+ return value;
+}
+
+void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key)
+{
+ struct hashmap_entry *e;
+
+ assert(i);
+
+ if (!h)
+ goto at_end;
+
+ if (*i == ITERATOR_LAST)
+ goto at_end;
+
+ if (*i == ITERATOR_FIRST && !h->iterate_list_head)
+ goto at_end;
+
+ e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry *)*i;
+
+ if (e->iterate_next)
+ *i = (Iterator) e->iterate_next;
+ else
+ *i = ITERATOR_LAST;
+
+ if (key)
+ *key = e->key;
+
+ return e->value;
+
+at_end:
+ *i = ITERATOR_LAST;
+
+ if (key)
+ *key = NULL;
+
+ return NULL;
+}
+
+void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key)
+{
+ struct hashmap_entry *e;
+
+ assert(i);
+
+ if (!h)
+ goto at_beginning;
+
+ if (*i == ITERATOR_FIRST)
+ goto at_beginning;
+
+ if (*i == ITERATOR_LAST && !h->iterate_list_tail)
+ goto at_beginning;
+
+ e = *i == ITERATOR_LAST ? h->iterate_list_tail : (struct hashmap_entry *)*i;
+
+ if (e->iterate_previous)
+ *i = (Iterator) e->iterate_previous;
+ else
+ *i = ITERATOR_FIRST;
+
+ if (key)
+ *key = e->key;
+
+ return e->value;
+
+at_beginning:
+ *i = ITERATOR_FIRST;
+
+ if (key)
+ *key = NULL;
+
+ return NULL;
+}
+
+void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i)
+{
+ unsigned int hash;
+ struct hashmap_entry *e;
+
+ if (!h)
+ return NULL;
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if (!(e = hash_scan(h, hash, key)))
+ return NULL;
+
+ *i = (Iterator) e;
+
+ return e->value;
+}
+
+void *hashmap_first(Hashmap *h)
+{
+
+ if (!h)
+ return NULL;
+
+ if (!h->iterate_list_head)
+ return NULL;
+
+ return h->iterate_list_head->value;
+}
+
+void *hashmap_first_key(Hashmap *h)
+{
+
+ if (!h)
+ return NULL;
+
+ if (!h->iterate_list_head)
+ return NULL;
+
+ return (void *)h->iterate_list_head->key;
+}
+
+void *hashmap_last(Hashmap *h)
+{
+
+ if (!h)
+ return NULL;
+
+ if (!h->iterate_list_tail)
+ return NULL;
+
+ return h->iterate_list_tail->value;
+}
+
+void *hashmap_steal_first(Hashmap *h)
+{
+ struct hashmap_entry *e;
+ void *data;
+
+ if (!h)
+ return NULL;
+
+ if (!h->iterate_list_head)
+ return NULL;
+
+ e = h->iterate_list_head;
+ data = e->value;
+ remove_entry(h, &e);
+
+ return data;
+}
+
+void *hashmap_steal_first_key(Hashmap *h)
+{
+ struct hashmap_entry *e;
+ void *key;
+
+ if (!h)
+ return NULL;
+
+ if (!h->iterate_list_head)
+ return NULL;
+
+ e = h->iterate_list_head;
+ key = (void *)e->key;
+ remove_entry(h, &e);
+
+ return key;
+}
+
+unsigned int hashmap_size(Hashmap *h)
+{
+
+ if (!h)
+ return 0;
+
+ return h->n_entries;
+}
+
+bool hashmap_isempty(Hashmap *h)
+{
+
+ if (!h)
+ return true;
+
+ return h->n_entries == 0;
+}
+
+int hashmap_merge(Hashmap *h, Hashmap *other)
+{
+ struct hashmap_entry *e;
+
+ assert(h);
+
+ if (!other)
+ return 0;
+
+ for (e = other->iterate_list_head; e; e = e->iterate_next) {
+ int r;
+
+ if ((r = hashmap_put(h, e->key, e->value)) < 0)
+ if (r != -EEXIST)
+ return r;
+ }
+
+ return 0;
+}
+
+void hashmap_move(Hashmap *h, Hashmap *other)
+{
+ struct hashmap_entry *e, *n;
+
+ assert(h);
+
+ /* The same as hashmap_merge(), but every new item from other
+ * is moved to h. This function is guaranteed to succeed. */
+
+ if (!other)
+ return;
+
+ for (e = other->iterate_list_head; e; e = n) {
+ unsigned int h_hash, other_hash;
+
+ n = e->iterate_next;
+
+ h_hash = h->hash_func(e->key) % NBUCKETS;
+
+ if (hash_scan(h, h_hash, e->key))
+ continue;
+
+ other_hash = other->hash_func(e->key) % NBUCKETS;
+
+ unlink_entry(other, e, other_hash);
+ link_entry(h, e, h_hash);
+ }
+}
+
+int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key)
+{
+ unsigned int h_hash, other_hash;
+ struct hashmap_entry *e;
+
+ if (!other)
+ return 0;
+
+ assert(h);
+
+ h_hash = h->hash_func(key) % NBUCKETS;
+ if (hash_scan(h, h_hash, key))
+ return -EEXIST;
+
+ other_hash = other->hash_func(key) % NBUCKETS;
+ if (!(e = hash_scan(other, other_hash, key)))
+ return -ENOENT;
+
+ unlink_entry(other, e, other_hash);
+ link_entry(h, e, h_hash);
+
+ return 0;
+}
+
+char **hashmap_get_strv(Hashmap *h)
+{
+ char **sv;
+ Iterator it;
+ char *item;
+ int n;
+
+ sv = new(char *, h->n_entries + 1);
+ if (!sv)
+ return NULL;
+
+ n = 0;
+ HASHMAP_FOREACH(item, h, it) {
+ sv[n++] = item;
+ }
+ sv[n] = NULL;
+
+ return sv;
+}
diff --git a/src/install/hashmap.h b/src/install/hashmap.h
new file mode 100644
index 0000000..2537c49
--- /dev/null
+++ b/src/install/hashmap.h
@@ -0,0 +1,88 @@
+#ifndef foohashmaphfoo
+#define foohashmaphfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+/* Pretty straightforward hash table implementation. As a minor
+ * optimization a NULL hashmap object will be treated as empty hashmap
+ * for all read operations. That way it is not necessary to
+ * instantiate an object for each Hashmap use. */
+
+typedef struct Hashmap Hashmap;
+typedef struct _IteratorStruct _IteratorStruct;
+typedef _IteratorStruct *Iterator;
+
+#define ITERATOR_FIRST ((Iterator) 0)
+#define ITERATOR_LAST ((Iterator) -1)
+
+typedef unsigned int (*hash_func_t)(const void *p);
+typedef int (*compare_func_t)(const void *a, const void *b);
+
+unsigned int string_hash_func(const void *p);
+int string_compare_func(const void *a, const void *b);
+
+unsigned int trivial_hash_func(const void *p);
+int trivial_compare_func(const void *a, const void *b);
+
+Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func);
+void hashmap_free(Hashmap *h);
+void hashmap_free_free(Hashmap *h);
+int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func);
+
+int hashmap_put(Hashmap *h, const void *key, void *value);
+int hashmap_replace(Hashmap *h, const void *key, void *value);
+void *hashmap_get(Hashmap *h, const void *key);
+void *hashmap_remove(Hashmap *h, const void *key);
+void *hashmap_remove_value(Hashmap *h, const void *key, void *value);
+int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
+int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value);
+
+int hashmap_merge(Hashmap *h, Hashmap *other);
+void hashmap_move(Hashmap *h, Hashmap *other);
+int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key);
+
+unsigned int hashmap_size(Hashmap *h);
+bool hashmap_isempty(Hashmap *h);
+
+void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key);
+void *hashmap_iterate_backwards(Hashmap *h, Iterator *i, const void **key);
+void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i);
+
+void hashmap_clear(Hashmap *h);
+void *hashmap_steal_first(Hashmap *h);
+void *hashmap_steal_first_key(Hashmap *h);
+void *hashmap_first(Hashmap *h);
+void *hashmap_first_key(Hashmap *h);
+void *hashmap_last(Hashmap *h);
+
+char **hashmap_get_strv(Hashmap *h);
+
+#define HASHMAP_FOREACH(e, h, i) \
+ for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL))
+
+#define HASHMAP_FOREACH_KEY(e, k, h, i) \
+ for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); (e); (e) = hashmap_iterate((h), &(i), (const void**) &(k)))
+
+#define HASHMAP_FOREACH_BACKWARDS(e, h, i) \
+ for ((i) = ITERATOR_LAST, (e) = hashmap_iterate_backwards((h), &(i), NULL); (e); (e) = hashmap_iterate_backwards((h), &(i), NULL))
+
+#endif
diff --git a/src/install/log.c b/src/install/log.c
new file mode 100644
index 0000000..f5ba54e
--- /dev/null
+++ b/src/install/log.c
@@ -0,0 +1,303 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stddef.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+
+#define SNDBUF_SIZE (8*1024*1024)
+
+static LogTarget log_target = LOG_TARGET_CONSOLE;
+static int log_max_level = LOG_WARNING;
+static int log_facility = LOG_DAEMON;
+
+static int console_fd = STDERR_FILENO;
+
+static bool show_location = false;
+
+/* Akin to glibc's __abort_msg; which is private and we hence cannot
+ * use here. */
+static char *log_abort_msg = NULL;
+
+void log_close_console(void)
+{
+
+ if (console_fd < 0)
+ return;
+
+ if (getpid() == 1) {
+ if (console_fd >= 3)
+ close_nointr_nofail(console_fd);
+
+ console_fd = -1;
+ }
+}
+
+static int log_open_console(void)
+{
+
+ if (console_fd >= 0)
+ return 0;
+
+ if (getpid() == 1) {
+
+ console_fd = open_terminal("/dev/console", O_WRONLY | O_NOCTTY | O_CLOEXEC);
+ if (console_fd < 0) {
+ log_error("Failed to open /dev/console for logging: %s", strerror(-console_fd));
+ return console_fd;
+ }
+
+ log_debug("Successfully opened /dev/console for logging.");
+ } else
+ console_fd = STDERR_FILENO;
+
+ return 0;
+}
+
+int log_open(void)
+{
+ return log_open_console();
+}
+
+void log_close(void)
+{
+ log_close_console();
+}
+
+void log_set_max_level(int level)
+{
+ assert((level & LOG_PRIMASK) == level);
+
+ log_max_level = level;
+}
+
+void log_set_facility(int facility)
+{
+ log_facility = facility;
+}
+
+static int write_to_console(int level, const char *file, unsigned int line, const char *func, const char *buffer)
+{
+ struct iovec iovec[5];
+ unsigned int n = 0;
+
+ // might be useful going ahead
+ UNUSED(level);
+
+ if (console_fd < 0)
+ return 0;
+
+ zero(iovec);
+
+ IOVEC_SET_STRING(iovec[n++], "dracut-install: ");
+
+ if (show_location) {
+ char location[LINE_MAX] = {0};
+ if (snprintf(location, sizeof(location), "(%s:%s:%u) ", file, func, line) <= 0)
+ return -errno;
+ IOVEC_SET_STRING(iovec[n++], location);
+ }
+
+ IOVEC_SET_STRING(iovec[n++], buffer);
+ IOVEC_SET_STRING(iovec[n++], "\n");
+
+ if (writev(console_fd, iovec, n) < 0)
+ return -errno;
+
+ return 1;
+}
+
+static int log_dispatch(int level, const char *file, unsigned int line, const char *func, char *buffer)
+{
+
+ int r = 0;
+
+ if (log_target == LOG_TARGET_NULL)
+ return 0;
+
+ /* Patch in LOG_DAEMON facility if necessary */
+ if ((level & LOG_FACMASK) == 0)
+ level = log_facility | LOG_PRI(level);
+
+ do {
+ char *e;
+ int k = 0;
+
+ buffer += strspn(buffer, NEWLINE);
+
+ if (buffer[0] == 0)
+ break;
+
+ if ((e = strpbrk(buffer, NEWLINE)))
+ *(e++) = 0;
+
+ k = write_to_console(level, file, line, func, buffer);
+ if (k < 0)
+ return k;
+ buffer = e;
+ } while (buffer);
+
+ return r;
+}
+
+int log_metav(int level, const char *file, unsigned int line, const char *func, const char *format, va_list ap)
+{
+ char buffer[LINE_MAX] = {0};
+ int saved_errno, r;
+
+ if (_likely_(LOG_PRI(level) > log_max_level))
+ return 0;
+
+ saved_errno = errno;
+
+ r = vsnprintf(buffer, sizeof(buffer), format, ap);
+ if (r <= 0) {
+ goto end;
+ }
+
+ char_array_0(buffer);
+
+ r = log_dispatch(level, file, line, func, buffer);
+
+end:
+ errno = saved_errno;
+ return r;
+}
+
+int log_meta(int level, const char *file, unsigned int line, const char *func, const char *format, ...)
+{
+
+ int r;
+ va_list ap;
+
+ va_start(ap, format);
+ r = log_metav(level, file, line, func, format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+_noreturn_ static void log_assert(const char *text, const char *file, unsigned int line, const char *func,
+ const char *format)
+{
+ static char buffer[LINE_MAX];
+
+ if (snprintf(buffer, sizeof(buffer), format, text, file, line, func) > 0) {
+ char_array_0(buffer);
+ log_abort_msg = buffer;
+ log_dispatch(LOG_CRIT, file, line, func, buffer);
+ }
+
+ abort();
+}
+
+#pragma GCC diagnostic pop
+
+_noreturn_ void log_assert_failed(const char *text, const char *file, unsigned int line, const char *func)
+{
+ log_assert(text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting.");
+}
+
+_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, unsigned int line, const char *func)
+{
+ log_assert(text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting.");
+}
+
+void log_set_target(LogTarget target)
+{
+ assert(target >= 0);
+ assert(target < _LOG_TARGET_MAX);
+
+ log_target = target;
+}
+
+int log_set_target_from_string(const char *e)
+{
+ LogTarget t;
+
+ t = log_target_from_string(e);
+ if (t < 0)
+ return -EINVAL;
+
+ log_set_target(t);
+ return 0;
+}
+
+int log_set_max_level_from_string(const char *e)
+{
+ int t;
+
+ t = log_level_from_string(e);
+ if (t < 0)
+ return t;
+
+ log_set_max_level(t);
+ return 0;
+}
+
+void log_parse_environment(void)
+{
+ const char *e;
+
+ if ((e = getenv("DRACUT_INSTALL_LOG_TARGET"))) {
+ if (log_set_target_from_string(e) < 0)
+ log_warning("Failed to parse log target %s. Ignoring.", e);
+ } else if ((e = getenv("DRACUT_LOG_TARGET"))) {
+ if (log_set_target_from_string(e) < 0)
+ log_warning("Failed to parse log target %s. Ignoring.", e);
+ }
+
+ if ((e = getenv("DRACUT_INSTALL_LOG_LEVEL"))) {
+ if (log_set_max_level_from_string(e) < 0)
+ log_warning("Failed to parse log level %s. Ignoring.", e);
+ } else if ((e = getenv("DRACUT_LOG_LEVEL"))) {
+ if (log_set_max_level_from_string(e) < 0)
+ log_warning("Failed to parse log level %s. Ignoring.", e);
+ }
+}
+
+LogTarget log_get_target(void)
+{
+ return log_target;
+}
+
+int log_get_max_level(void)
+{
+ return log_max_level;
+}
+
+static const char *const log_target_table[] = {
+ [LOG_TARGET_CONSOLE] = "console",
+ [LOG_TARGET_AUTO] = "auto",
+ [LOG_TARGET_SAFE] = "safe",
+ [LOG_TARGET_NULL] = "null"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
diff --git a/src/install/log.h b/src/install/log.h
new file mode 100644
index 0000000..08536f3
--- /dev/null
+++ b/src/install/log.h
@@ -0,0 +1,98 @@
+#ifndef foologhfoo
+#define foologhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <syslog.h>
+#include <stdbool.h>
+#include <stdarg.h>
+
+#include "macro.h"
+
+typedef enum LogTarget {
+ LOG_TARGET_CONSOLE,
+ LOG_TARGET_KMSG,
+ LOG_TARGET_JOURNAL,
+ LOG_TARGET_JOURNAL_OR_KMSG,
+ LOG_TARGET_SYSLOG,
+ LOG_TARGET_SYSLOG_OR_KMSG,
+ LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
+ LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */
+ LOG_TARGET_NULL,
+ _LOG_TARGET_MAX,
+ _LOG_TARGET_INVALID = -1
+} LogTarget;
+
+void log_set_target(LogTarget target);
+void log_set_max_level(int level);
+void log_set_facility(int facility);
+
+int log_set_target_from_string(const char *e);
+int log_set_max_level_from_string(const char *e);
+
+void log_show_color(bool b);
+void log_show_location(bool b);
+
+int log_show_color_from_string(const char *e);
+int log_show_location_from_string(const char *e);
+
+LogTarget log_get_target(void);
+int log_get_max_level(void);
+
+int log_open(void);
+void log_close(void);
+void log_forget_fds(void);
+
+void log_close_syslog(void);
+void log_close_journal(void);
+void log_close_kmsg(void);
+void log_close_console(void);
+
+void log_parse_environment(void);
+
+int log_meta(int level, const char *file, unsigned int line, const char *func,
+ const char *format, ...) _printf_attr_(5, 6);
+
+int log_metav(int level, const char *file, unsigned int line, const char *func, const char *format, va_list ap);
+
+_noreturn_ void log_assert_failed(const char *text, const char *file, unsigned int line, const char *func);
+_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, unsigned int line, const char *func);
+
+/* This modifies the buffer passed! */
+int log_dump_internal(int level, const char *file, int line, const char *func, char *buffer);
+
+#define log_full(level, ...) log_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+#define log_debug(...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_info(...) log_meta(LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_notice(...) log_meta(LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_warning(...) log_meta(LOG_WARNING, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define log_error(...) log_meta(LOG_ERR, __FILE__, __LINE__, __func__, __VA_ARGS__)
+
+/* This modifies the buffer passed! */
+#define log_dump(level, buffer) log_dump_internal(level, __FILE__, __LINE__, __func__, buffer)
+
+const char *log_target_to_string(LogTarget target);
+LogTarget log_target_from_string(const char *s);
+
+const char *log_level_to_string(int i);
+int log_level_from_string(const char *s);
+
+#endif
diff --git a/src/install/macro.h b/src/install/macro.h
new file mode 100644
index 0000000..cc426c9
--- /dev/null
+++ b/src/install/macro.h
@@ -0,0 +1,299 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <inttypes.h>
+
+#define _printf_attr_(a,b) __attribute__ ((format (printf, a, b)))
+#define _sentinel_ __attribute__ ((sentinel))
+#define _noreturn_ __attribute__((noreturn))
+#define _unused_ __attribute__ ((unused))
+#define _destructor_ __attribute__ ((destructor))
+#define _pure_ __attribute__ ((pure))
+#define _const_ __attribute__ ((const))
+#define _deprecated_ __attribute__ ((deprecated))
+#define _packed_ __attribute__ ((packed))
+#define _malloc_ __attribute__ ((malloc))
+#define _weak_ __attribute__ ((weak))
+#define _likely_(x) (__builtin_expect(!!(x),1))
+#define _unlikely_(x) (__builtin_expect(!!(x),0))
+#define _public_ __attribute__ ((visibility("default")))
+#define _hidden_ __attribute__ ((visibility("hidden")))
+#define _weakref_(x) __attribute__((weakref(#x)))
+#define _introspect_(x) __attribute__((section("introspect." x)))
+#define _alignas_(x) __attribute__((aligned(__alignof(x))))
+#define _cleanup_(x) __attribute__((cleanup(x)))
+
+/* automake test harness */
+#define EXIT_TEST_SKIP 77
+
+#define XSTRINGIFY(x) #x
+#define STRINGIFY(x) XSTRINGIFY(x)
+
+/* Rounds up */
+
+#define ALIGN4(l) (((l) + 3) & ~3)
+#define ALIGN8(l) (((l) + 7) & ~7)
+
+#if __SIZEOF_POINTER__ == 8
+#define ALIGN(l) ALIGN8(l)
+#elif __SIZEOF_POINTER__ == 4
+#define ALIGN(l) ALIGN4(l)
+#else
+#error "Wut? Pointers are neither 4 nor 8 bytes long?"
+#endif
+
+#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) p))
+#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) p))
+#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) p))
+
+static inline size_t ALIGN_TO(size_t l, size_t ali)
+{
+ return ((l + ali - 1) & ~(ali - 1));
+}
+
+#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) p))
+
+#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
+
+/*
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) \
+ __extension__ ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) ); \
+ })
+
+#undef MAX
+#define MAX(a,b) \
+ __extension__ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a > _b ? _a : _b; \
+ })
+
+#define MAX3(x,y,z) \
+ __extension__ ({ \
+ typeof(x) _c = MAX(x,y); \
+ MAX(_c, z); \
+ })
+
+#undef MIN
+#define MIN(a,b) \
+ __extension__ ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a < _b ? _a : _b; \
+ })
+
+#ifndef CLAMP
+#define CLAMP(x, low, high) \
+ __extension__ ({ \
+ typeof(x) _x = (x); \
+ typeof(low) _low = (low); \
+ typeof(high) _high = (high); \
+ ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \
+ })
+#endif
+
+#define assert_se(expr) \
+ do { \
+ if (_unlikely_(!(expr))) \
+ log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ } while (false) \
+
+/* We override the glibc assert() here. */
+#undef assert
+#ifdef NDEBUG
+#define assert(expr) do {} while(false)
+#else
+#define assert(expr) assert_se(expr)
+#endif
+
+#define assert_not_reached(t) \
+ do { \
+ log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ } while (false)
+
+#if defined(static_assert)
+#define assert_cc(expr) \
+ do { \
+ static_assert(expr, #expr); \
+ } while (false)
+#else
+#define assert_cc(expr) \
+ do { \
+ switch (0) { \
+ case 0: \
+ case !!(expr): \
+ ; \
+ } \
+ } while (false)
+#endif
+
+#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define UINT_TO_PTR(u) ((void*) ((uintptr_t) (u)))
+
+#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
+#define UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u)))
+
+#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p)))
+#define ULONG_TO_PTR(u) ((void*) ((uintptr_t) (u)))
+
+#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
+#define INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
+
+#define TO_INT32(p) ((int32_t) ((intptr_t) (p)))
+#define INT32_TO_PTR(u) ((void*) ((intptr_t) (u)))
+
+#define PTR_TO_LONG(p) ((long) ((intptr_t) (p)))
+#define LONG_TO_PTR(u) ((void*) ((intptr_t) (u)))
+
+#define memzero(x,l) (memset((x), 0, (l)))
+#define zero(x) (memzero(&(x), sizeof(x)))
+
+#define CHAR_TO_STR(x) ((char[2]) { x, 0 })
+
+#define char_array_0(x) x[sizeof(x)-1] = 0;
+
+#define IOVEC_SET_STRING(i, s) \
+ do { \
+ struct iovec *_i = &(i); \
+ char *_s = (char *)(s); \
+ _i->iov_base = _s; \
+ _i->iov_len = strlen(_s); \
+ } while(false)
+
+static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned int n)
+{
+ unsigned int j;
+ size_t r = 0;
+
+ for (j = 0; j < n; j++)
+ r += i[j].iov_len;
+
+ return r;
+}
+
+static inline size_t IOVEC_INCREMENT(struct iovec *i, unsigned int n, size_t k)
+{
+ unsigned int j;
+
+ for (j = 0; j < n; j++) {
+ size_t sub;
+
+ if (_unlikely_(k == 0))
+ break;
+
+ sub = MIN(i[j].iov_len, k);
+ i[j].iov_len -= sub;
+ i[j].iov_base = (uint8_t *) i[j].iov_base + sub;
+ k -= sub;
+ }
+
+ return k;
+}
+
+#define VA_FORMAT_ADVANCE(format, ap) \
+do { \
+ int _argtypes[128]; \
+ size_t _i, _k; \
+ _k = parse_printf_format((format), ELEMENTSOF(_argtypes), _argtypes); \
+ assert(_k < ELEMENTSOF(_argtypes)); \
+ for (_i = 0; _i < _k; _i++) { \
+ if (_argtypes[_i] & PA_FLAG_PTR) { \
+ (void) va_arg(ap, void*); \
+ continue; \
+ } \
+ \
+ switch (_argtypes[_i]) { \
+ case PA_INT: \
+ case PA_INT|PA_FLAG_SHORT: \
+ case PA_CHAR: \
+ (void) va_arg(ap, int); \
+ break; \
+ case PA_INT|PA_FLAG_LONG: \
+ (void) va_arg(ap, long int); \
+ break; \
+ case PA_INT|PA_FLAG_LONG_LONG: \
+ (void) va_arg(ap, long long int); \
+ break; \
+ case PA_WCHAR: \
+ (void) va_arg(ap, wchar_t); \
+ break; \
+ case PA_WSTRING: \
+ case PA_STRING: \
+ case PA_POINTER: \
+ (void) va_arg(ap, void*); \
+ break; \
+ case PA_FLOAT: \
+ case PA_DOUBLE: \
+ (void) va_arg(ap, double); \
+ break; \
+ case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: \
+ (void) va_arg(ap, long double); \
+ break; \
+ default: \
+ assert_not_reached("Unknown format string argument."); \
+ } \
+ } \
+} while(false)
+
+/* Because statfs.t_type can be int on some architectures, we have to cast
+ * the const magic to the type, otherwise the compiler warns about
+ * signed/unsigned comparison, because the magic can be 32 bit unsigned.
+ */
+#define F_TYPE_CMP(a, b) (a == (typeof(a)) b)
+
+/* Returns the number of chars needed to format variables of the
+ * specified type as a decimal string. Adds in extra space for a
+ * negative '-' prefix. */
+
+#define DECIMAL_STR_MAX(type) \
+ (1+(sizeof(type) <= 1 ? 3 : \
+ sizeof(type) <= 2 ? 5 : \
+ sizeof(type) <= 4 ? 10 : \
+ sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)])))
+
+/*
+ * Takes inspiration from Rust's Option::take() method: reads and returns a pointer.
+ * But at the same time resets it to NULL.
+ * See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take
+ */
+#define TAKE_PTR(ptr) \
+ ({ \
+ typeof(ptr) _ptr_ = (ptr); \
+ (ptr) = NULL; \
+ _ptr_; \
+ })
+
+/* Use to suppress unused variable/function arg warning */
+#define UNUSED(var) ((void)var)
+
+#include "log.h"
diff --git a/src/install/strv.c b/src/install/strv.c
new file mode 100644
index 0000000..02b0507
--- /dev/null
+++ b/src/install/strv.c
@@ -0,0 +1,616 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "strv.h"
+
+char *strv_find(char **l, const char *name)
+{
+ char **i;
+
+ assert(name);
+
+ STRV_FOREACH(i, l) {
+ if (streq(*i, name))
+ return *i;
+ }
+
+ return NULL;
+}
+
+char *strv_find_prefix(char **l, const char *name)
+{
+ char **i;
+
+ assert(name);
+
+ STRV_FOREACH(i, l) {
+ if (startswith(*i, name))
+ return *i;
+ }
+
+ return NULL;
+}
+
+void strv_free(char **l)
+{
+ char **k;
+
+ if (!l)
+ return;
+
+ for (k = l; *k; k++)
+ free(*k);
+
+ free(l);
+}
+
+char **strv_copy(char *const *l)
+{
+ char **r, **k;
+
+ k = r = new(char *, strv_length(l) + 1);
+ if (!r)
+ return NULL;
+
+ if (l)
+ for (; *l; k++, l++) {
+ *k = strdup(*l);
+ if (!*k) {
+ strv_free(r);
+ return NULL;
+ }
+ }
+
+ *k = NULL;
+ return r;
+}
+
+unsigned int strv_length(char *const *l)
+{
+ unsigned int n = 0;
+
+ if (!l)
+ return 0;
+
+ for (; *l; l++)
+ n++;
+
+ return n;
+}
+
+char **strv_new_ap(const char *x, va_list ap)
+{
+ const char *s;
+ char **a;
+ unsigned int n = 0, i = 0;
+ va_list aq;
+
+ /* As a special trick we ignore all listed strings that equal
+ * (const char*) -1. This is supposed to be used with the
+ * STRV_IFNOTNULL() macro to include possibly NULL strings in
+ * the string list. */
+
+ if (x) {
+ n = x == (const char *)-1 ? 0 : 1;
+
+ va_copy(aq, ap);
+ while ((s = va_arg(aq, const char *))) {
+ if (s == (const char *)-1)
+ continue;
+
+ n++;
+ }
+
+ va_end(aq);
+ }
+
+ a = new(char *, n + 1);
+ if (!a)
+ return NULL;
+
+ if (x) {
+ if (x != (const char *)-1) {
+ a[i] = strdup(x);
+ if (!a[i])
+ goto fail;
+ i++;
+ }
+
+ while ((s = va_arg(ap, const char *))) {
+
+ if (s == (const char *)-1)
+ continue;
+
+ a[i] = strdup(s);
+ if (!a[i])
+ goto fail;
+
+ i++;
+ }
+ }
+
+ a[i] = NULL;
+
+ return a;
+
+fail:
+ strv_free(a);
+ return NULL;
+}
+
+char **strv_new(const char *x, ...)
+{
+ char **r;
+ va_list ap;
+
+ va_start(ap, x);
+ r = strv_new_ap(x, ap);
+ va_end(ap);
+
+ return r;
+}
+
+char **strv_merge(char **a, char **b)
+{
+ char **r, **k;
+
+ if (!a)
+ return strv_copy(b);
+
+ if (!b)
+ return strv_copy(a);
+
+ r = new(char *, strv_length(a) + strv_length(b) + 1);
+ if (!r)
+ return NULL;
+
+ for (k = r; *a; k++, a++) {
+ *k = strdup(*a);
+ if (!*k)
+ goto fail;
+ }
+
+ for (; *b; k++, b++) {
+ *k = strdup(*b);
+ if (!*k)
+ goto fail;
+ }
+
+ *k = NULL;
+ return r;
+
+fail:
+ strv_free(r);
+ return NULL;
+}
+
+char **strv_merge_concat(char **a, char **b, const char *suffix)
+{
+ char **r, **k;
+
+ /* Like strv_merge(), but appends suffix to all strings in b, before adding */
+
+ if (!b)
+ return strv_copy(a);
+
+ r = new(char *, strv_length(a) + strv_length(b) + 1);
+ if (!r)
+ return NULL;
+
+ k = r;
+ if (a)
+ for (; *a; k++, a++) {
+ *k = strdup(*a);
+ if (!*k)
+ goto fail;
+ }
+
+ for (; *b; k++, b++) {
+ *k = strappend(*b, suffix);
+ if (!*k)
+ goto fail;
+ }
+
+ *k = NULL;
+ return r;
+
+fail:
+ strv_free(r);
+ return NULL;
+
+}
+
+char **strv_split(const char *s, const char *separator)
+{
+ char *state;
+ char *w;
+ size_t l;
+ unsigned int n, i;
+ char **r;
+
+ assert(s);
+
+ n = 0;
+ FOREACH_WORD_SEPARATOR(w, l, s, separator, state) {
+ n++;
+ }
+
+ r = new(char *, n + 1);
+ if (!r)
+ return NULL;
+
+ i = 0;
+ FOREACH_WORD_SEPARATOR(w, l, s, separator, state) {
+ r[i] = strndup(w, l);
+ if (!r[i]) {
+ strv_free(r);
+ return NULL;
+ }
+
+ i++;
+ }
+
+ r[i] = NULL;
+ return r;
+}
+
+char **strv_split_quoted(const char *s)
+{
+ char *state;
+ char *w;
+ size_t l;
+ unsigned int n, i;
+ char **r;
+
+ assert(s);
+
+ n = 0;
+ FOREACH_WORD_QUOTED(w, l, s, state) {
+ n++;
+ }
+
+ r = new(char *, n + 1);
+ if (!r)
+ return NULL;
+
+ i = 0;
+ FOREACH_WORD_QUOTED(w, l, s, state) {
+ r[i] = cunescape_length(w, l);
+ if (!r[i]) {
+ strv_free(r);
+ return NULL;
+ }
+ i++;
+ }
+
+ r[i] = NULL;
+ return r;
+}
+
+char **strv_split_newlines(const char *s)
+{
+ char **l;
+ unsigned int n;
+
+ assert(s);
+
+ /* Special version of strv_split() that splits on newlines and
+ * suppresses an empty string at the end */
+
+ l = strv_split(s, NEWLINE);
+ if (!l)
+ return NULL;
+
+ n = strv_length(l);
+ if (n == 0)
+ return l;
+
+ if (isempty(l[n - 1])) {
+ free(l[n - 1]);
+ l[n - 1] = NULL;
+ }
+
+ return l;
+}
+
+char *strv_join(char **l, const char *separator)
+{
+ char *r, *e;
+ char **s;
+ size_t n, k;
+
+ if (!separator)
+ separator = " ";
+
+ k = strlen(separator);
+
+ n = 0;
+ STRV_FOREACH(s, l) {
+ if (n != 0)
+ n += k;
+ n += strlen(*s);
+ }
+
+ r = new(char, n + 1);
+ if (!r)
+ return NULL;
+
+ e = r;
+ STRV_FOREACH(s, l) {
+ if (e != r)
+ e = stpcpy(e, separator);
+
+ e = stpcpy(e, *s);
+ }
+
+ *e = 0;
+
+ return r;
+}
+
+char **strv_append(char **l, const char *s)
+{
+ char **r, **k;
+
+ if (!l)
+ return strv_new(s, NULL);
+
+ if (!s)
+ return strv_copy(l);
+
+ r = new(char *, strv_length(l) + 2);
+ if (!r)
+ return NULL;
+
+ for (k = r; *l; k++, l++) {
+ *k = strdup(*l);
+ if (!*k)
+ goto fail;
+ }
+
+ k[0] = strdup(s);
+ if (!k[0])
+ goto fail;
+
+ k[1] = NULL;
+ return r;
+
+fail:
+ strv_free(r);
+ return NULL;
+}
+
+int strv_push(char ***l, char *value)
+{
+ char **c;
+ unsigned int n;
+
+ if (!value)
+ return 0;
+
+ n = strv_length(*l);
+ c = realloc(*l, sizeof(char *) * (n + 2));
+ if (!c)
+ return -ENOMEM;
+
+ c[n] = value;
+ c[n + 1] = NULL;
+
+ *l = c;
+ return 0;
+}
+
+int strv_extend(char ***l, const char *value)
+{
+ char *v;
+ int r;
+
+ if (!value)
+ return 0;
+
+ v = strdup(value);
+ if (!v)
+ return -ENOMEM;
+
+ r = strv_push(l, v);
+ if (r < 0)
+ free(v);
+
+ return r;
+}
+
+char **strv_uniq(char **l)
+{
+ char **i;
+
+ /* Drops duplicate entries. The first identical string will be
+ * kept, the others dropped */
+
+ STRV_FOREACH(i, l) {
+ strv_remove(i + 1, *i);
+ }
+ return l;
+}
+
+char **strv_remove(char **l, const char *s)
+{
+ char **f, **t;
+
+ if (!l)
+ return NULL;
+
+ assert(s);
+
+ /* Drops every occurrence of s in the string list, edits
+ * in-place. */
+
+ for (f = t = l; *f; f++) {
+
+ if (streq(*f, s)) {
+ free(*f);
+ continue;
+ }
+
+ *(t++) = *f;
+ }
+
+ *t = NULL;
+ return l;
+}
+
+char **strv_remove_prefix(char **l, const char *s)
+{
+ char **f, **t;
+
+ if (!l)
+ return NULL;
+
+ assert(s);
+
+ /* Drops every occurrence of a string prefixed with s in the
+ * string list, edits in-place. */
+
+ for (f = t = l; *f; f++) {
+
+ if (startswith(*f, s)) {
+ free(*f);
+ continue;
+ }
+
+ *(t++) = *f;
+ }
+
+ *t = NULL;
+ return l;
+}
+
+char **strv_parse_nulstr(const char *s, size_t l)
+{
+ const char *p;
+ unsigned int c = 0, i = 0;
+ char **v;
+
+ assert(s || l == 0);
+
+ if (l == 0)
+ return new0(char *, 1);
+
+ for (p = s; p < s + l; p++)
+ if (*p == 0)
+ c++;
+
+ if (s[l - 1] != 0)
+ c++;
+
+ v = new0(char *, c + 1);
+ if (!v)
+ return NULL;
+
+ p = s;
+ while (p < s + l) {
+ const char *e;
+
+ e = memchr(p, 0, s + l - p);
+
+ v[i] = strndup(p, e ? e - p : s + l - p);
+ if (!v[i]) {
+ strv_free(v);
+ return NULL;
+ }
+
+ i++;
+
+ if (!e)
+ break;
+
+ p = e + 1;
+ }
+
+ assert(i == c);
+
+ return v;
+}
+
+char **strv_split_nulstr(const char *s)
+{
+ const char *i;
+ char **r = NULL;
+
+ NULSTR_FOREACH(i, s) {
+ if (strv_extend(&r, i) < 0) {
+ strv_free(r);
+ return NULL;
+ }
+ }
+
+ if (!r)
+ return strv_new(NULL, NULL);
+
+ return r;
+}
+
+bool strv_overlap(char **a, char **b)
+{
+ char **i, **j;
+
+ STRV_FOREACH(i, a) {
+ STRV_FOREACH(j, b) {
+ if (streq(*i, *j))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int str_compare(const void *_a, const void *_b)
+{
+ const char **a = (const char **)_a, **b = (const char **)_b;
+
+ return strcmp(*a, *b);
+}
+
+char **strv_sort(char **l)
+{
+
+ if (strv_isempty(l))
+ return l;
+
+ qsort(l, strv_length(l), sizeof(char *), str_compare);
+ return l;
+}
+
+void strv_print(char **l)
+{
+ char **s;
+
+ if (!l)
+ return;
+
+ STRV_FOREACH(s, l) {
+ puts(*s);
+ }
+}
diff --git a/src/install/strv.h b/src/install/strv.h
new file mode 100644
index 0000000..cf99f5d
--- /dev/null
+++ b/src/install/strv.h
@@ -0,0 +1,118 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "util.h"
+
+char *strv_find(char **l, const char *name) _pure_;
+char *strv_find_prefix(char **l, const char *name) _pure_;
+
+void strv_free(char **l);
+DEFINE_TRIVIAL_CLEANUP_FUNC(char **, strv_free);
+#define _cleanup_strv_free_ _cleanup_(strv_freep)
+
+char **strv_copy(char *const *l);
+unsigned int strv_length(char *const *l) _pure_;
+
+char **strv_merge(char **a, char **b);
+char **strv_merge_concat(char **a, char **b, const char *suffix);
+char **strv_append(char **l, const char *s);
+int strv_extend(char ***l, const char *value);
+int strv_push(char ***l, char *value);
+
+char **strv_remove(char **l, const char *s);
+char **strv_remove_prefix(char **l, const char *s);
+char **strv_uniq(char **l);
+
+#define strv_contains(l, s) (!!strv_find((l), (s)))
+
+char **strv_new(const char *x, ...) _sentinel_;
+char **strv_new_ap(const char *x, va_list ap);
+
+static inline const char *STRV_IFNOTNULL(const char *x)
+{
+ return x ? x : (const char *)-1;
+}
+
+static inline bool strv_isempty(char *const *l)
+{
+ return !l || !*l;
+}
+
+char **strv_split(const char *s, const char *separator);
+char **strv_split_quoted(const char *s);
+char **strv_split_newlines(const char *s);
+
+char *strv_join(char **l, const char *separator);
+
+char **strv_parse_nulstr(const char *s, size_t l);
+char **strv_split_nulstr(const char *s);
+
+bool strv_overlap(char **a, char **b) _pure_;
+
+#define STRV_FOREACH(s, l) \
+ for ((s) = (l); (s) && *(s); (s)++)
+
+#define STRV_FOREACH_BACKWARDS(s, l) \
+ STRV_FOREACH(s, l) \
+ ; \
+ for ((s)--; (l) && ((s) >= (l)); (s)--)
+
+#define STRV_FOREACH_PAIR(x, y, l) \
+ for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
+
+char **strv_sort(char **l);
+void strv_print(char **l);
+
+#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL }))
+
+#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
+
+#define strv_from_stdarg_alloca(first) \
+ ({ \
+ char **_l; \
+ \
+ if (!first) \
+ _l = (char**) &first; \
+ else { \
+ unsigned int _n; \
+ va_list _ap; \
+ \
+ _n = 1; \
+ va_start(_ap, first); \
+ while (va_arg(_ap, char*)) \
+ _n++; \
+ va_end(_ap); \
+ \
+ _l = newa(char*, _n+1); \
+ _l[_n = 0] = (char*) first; \
+ va_start(_ap, first); \
+ for (;;) { \
+ _l[++_n] = va_arg(_ap, char*); \
+ if (!_l[_n]) \
+ break; \
+ } \
+ va_end(_ap); \
+ } \
+ _l; \
+ })
diff --git a/src/install/util.c b/src/install/util.c
new file mode 100644
index 0000000..5f32f64
--- /dev/null
+++ b/src/install/util.c
@@ -0,0 +1,574 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#define _GNU_SOURCE
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <libgen.h>
+
+#include "util.h"
+
+#define gettid() ((pid_t) syscall(SYS_gettid))
+
+size_t page_size(void)
+{
+ static __thread size_t pgsz = 0;
+ long r;
+
+ if (_likely_(pgsz > 0))
+ return pgsz;
+
+ assert_se((r = sysconf(_SC_PAGESIZE)) > 0);
+
+ pgsz = (size_t)r;
+
+ return pgsz;
+}
+
+bool endswith(const char *s, const char *postfix)
+{
+ size_t sl, pl;
+
+ assert(s);
+ assert(postfix);
+
+ sl = strlen(s);
+ pl = strlen(postfix);
+
+ if (pl == 0)
+ return true;
+
+ if (sl < pl)
+ return false;
+
+ return memcmp(s + sl - pl, postfix, pl) == 0;
+}
+
+int close_nointr(int fd)
+{
+ assert(fd >= 0);
+
+ for (;;) {
+ int r;
+
+ r = close(fd);
+ if (r >= 0)
+ return r;
+
+ if (errno != EINTR)
+ return -errno;
+ }
+}
+
+void close_nointr_nofail(int fd)
+{
+ int saved_errno = errno;
+
+ /* like close_nointr() but cannot fail, and guarantees errno
+ * is unchanged */
+
+ assert_se(close_nointr(fd) == 0);
+
+ errno = saved_errno;
+}
+
+int open_terminal(const char *name, int mode)
+{
+ int fd, r;
+ unsigned int c = 0;
+
+ /*
+ * If a TTY is in the process of being closed opening it might
+ * cause EIO. This is horribly awful, but unlikely to be
+ * changed in the kernel. Hence we work around this problem by
+ * retrying a couple of times.
+ *
+ * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
+ */
+
+ for (;;) {
+ if ((fd = open(name, mode)) >= 0)
+ break;
+
+ if (errno != EIO)
+ return -errno;
+
+ if (c >= 20)
+ return -errno;
+
+ usleep(50 * USEC_PER_MSEC);
+ c++;
+ }
+
+ if (fd < 0)
+ return -errno;
+
+ if ((r = isatty(fd)) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ if (!r) {
+ close_nointr_nofail(fd);
+ return -ENOTTY;
+ }
+
+ return fd;
+}
+
+bool streq_ptr(const char *a, const char *b)
+{
+
+ /* Like streq(), but tries to make sense of NULL pointers */
+
+ if (a && b)
+ return streq(a, b);
+
+ if (!a && !b)
+ return true;
+
+ return false;
+}
+
+bool is_main_thread(void)
+{
+ static __thread int cached = 0;
+
+ if (_unlikely_(cached == 0))
+ cached = getpid() == gettid()? 1 : -1;
+
+ return cached > 0;
+}
+
+int safe_atou(const char *s, unsigned int *ret_u)
+{
+ char *x = NULL;
+ unsigned long l;
+
+ assert(s);
+ assert(ret_u);
+
+ errno = 0;
+ l = strtoul(s, &x, 0);
+
+ if (!x || *x || errno)
+ return errno ? -errno : -EINVAL;
+
+ if ((unsigned long)(unsigned int)l != l)
+ return -ERANGE;
+
+ *ret_u = (unsigned int)l;
+ return 0;
+}
+
+static const char *const log_level_table[] = {
+ [LOG_EMERG] = "emerg",
+ [LOG_ALERT] = "alert",
+ [LOG_CRIT] = "crit",
+ [LOG_ERR] = "err",
+ [LOG_WARNING] = "warning",
+ [LOG_NOTICE] = "notice",
+ [LOG_INFO] = "info",
+ [LOG_DEBUG] = "debug"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(log_level, int);
+
+char *strnappend(const char *s, const char *suffix, size_t b)
+{
+ size_t a;
+ char *r;
+
+ if (!s && !suffix)
+ return strdup("");
+
+ if (!s)
+ return strndup(suffix, b);
+
+ if (!suffix)
+ return strdup(s);
+
+ assert(s);
+ assert(suffix);
+
+ a = strlen(s);
+ if (b > ((size_t)-1) - a)
+ return NULL;
+
+ r = new(char, a + b + 1);
+ if (!r)
+ return NULL;
+
+ memcpy(r, s, a);
+ memcpy(r + a, suffix, b);
+ r[a + b] = 0;
+
+ return r;
+}
+
+char *strappend(const char *s, const char *suffix)
+{
+ return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
+}
+
+char *strjoin(const char *x, ...)
+{
+ va_list ap;
+ size_t l;
+ char *r;
+
+ va_start(ap, x);
+
+ if (x) {
+ l = strlen(x);
+
+ for (;;) {
+ const char *t;
+ size_t n;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ n = strlen(t);
+ if (n > ((size_t)-1) - l) {
+ va_end(ap);
+ return NULL;
+ }
+
+ l += n;
+ }
+ } else
+ l = 0;
+
+ va_end(ap);
+
+ r = new(char, l + 1);
+ if (!r)
+ return NULL;
+
+ if (x) {
+ char *p;
+
+ p = stpcpy(r, x);
+
+ va_start(ap, x);
+
+ for (;;) {
+ const char *t;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ p = stpcpy(p, t);
+ }
+
+ va_end(ap);
+ } else
+ r[0] = 0;
+
+ return r;
+}
+
+char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix)
+{
+ char *r, *t;
+ const char *f;
+ size_t pl;
+
+ assert(s);
+
+ /* Undoes C style string escaping, and optionally prefixes it. */
+
+ pl = prefix ? strlen(prefix) : 0;
+
+ r = new(char, pl + length + 1);
+ if (!r)
+ return r;
+
+ if (prefix)
+ memcpy(r, prefix, pl);
+
+ for (f = s, t = r + pl; f < s + length; f++) {
+
+ if (*f != '\\') {
+ *(t++) = *f;
+ continue;
+ }
+
+ f++;
+
+ switch (*f) {
+
+ case 'a':
+ *(t++) = '\a';
+ break;
+ case 'b':
+ *(t++) = '\b';
+ break;
+ case 'f':
+ *(t++) = '\f';
+ break;
+ case 'n':
+ *(t++) = '\n';
+ break;
+ case 'r':
+ *(t++) = '\r';
+ break;
+ case 't':
+ *(t++) = '\t';
+ break;
+ case 'v':
+ *(t++) = '\v';
+ break;
+ case '\\':
+ *(t++) = '\\';
+ break;
+ case '"':
+ *(t++) = '"';
+ break;
+ case '\'':
+ *(t++) = '\'';
+ break;
+
+ case 's':
+ /* This is an extension of the XDG syntax files */
+ *(t++) = ' ';
+ break;
+
+ case 'x': {
+ /* hexadecimal encoding */
+ int a, b;
+
+ a = unhexchar(f[1]);
+ b = unhexchar(f[2]);
+
+ if (a < 0 || b < 0) {
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '\\';
+ *(t++) = 'x';
+ } else {
+ *(t++) = (char)((a << 4) | b);
+ f += 2;
+ }
+
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': {
+ /* octal encoding */
+ int a, b, c;
+
+ a = unoctchar(f[0]);
+ b = unoctchar(f[1]);
+ c = unoctchar(f[2]);
+
+ if (a < 0 || b < 0 || c < 0) {
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '\\';
+ *(t++) = f[0];
+ } else {
+ *(t++) = (char)((a << 6) | (b << 3) | c);
+ f += 2;
+ }
+
+ break;
+ }
+
+ case 0:
+ /* premature end of string. */
+ *(t++) = '\\';
+ goto finish;
+
+ default:
+ /* Invalid escape code, let's take it literal then */
+ *(t++) = '\\';
+ *(t++) = *f;
+ break;
+ }
+ }
+
+finish:
+ *t = 0;
+ return r;
+}
+
+char *cunescape_length(const char *s, size_t length)
+{
+ return cunescape_length_with_prefix(s, length, NULL);
+}
+
+/* Split a string into words, but consider strings enclosed in '' and
+ * "" as words even if they include spaces. */
+char *split_quoted(const char *c, size_t *l, char **state)
+{
+ const char *current, *e;
+ bool escaped = false;
+
+ assert(c);
+ assert(l);
+ assert(state);
+
+ current = *state ? *state : c;
+
+ current += strspn(current, WHITESPACE);
+
+ if (*current == 0)
+ return NULL;
+
+ else if (*current == '\'') {
+ current++;
+
+ for (e = current; *e; e++) {
+ if (escaped)
+ escaped = false;
+ else if (*e == '\\')
+ escaped = true;
+ else if (*e == '\'')
+ break;
+ }
+
+ *l = e - current;
+ *state = (char *)(*e == 0 ? e : e + 1);
+
+ } else if (*current == '\"') {
+ current++;
+
+ for (e = current; *e; e++) {
+ if (escaped)
+ escaped = false;
+ else if (*e == '\\')
+ escaped = true;
+ else if (*e == '\"')
+ break;
+ }
+
+ *l = e - current;
+ *state = (char *)(*e == 0 ? e : e + 1);
+
+ } else {
+ for (e = current; *e; e++) {
+ if (escaped)
+ escaped = false;
+ else if (*e == '\\')
+ escaped = true;
+ else if (strchr(WHITESPACE, *e))
+ break;
+ }
+ *l = e - current;
+ *state = (char *)e;
+ }
+
+ return (char *)current;
+}
+
+/* Split a string into words. */
+char *split(const char *c, size_t *l, const char *separator, char **state)
+{
+ char *current;
+
+ current = *state ? *state : (char *)c;
+
+ if (!*current || *c == 0)
+ return NULL;
+
+ current += strspn(current, separator);
+ *l = strcspn(current, separator);
+ *state = current + *l;
+
+ return (char *)current;
+}
+
+int unhexchar(char c)
+{
+
+ if (c >= '0' && c <= '9')
+ return c - '0';
+
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+
+ return -1;
+}
+
+int unoctchar(char c)
+{
+
+ if (c >= '0' && c <= '7')
+ return c - '0';
+
+ return -1;
+}
+
+int dracut_asprintf(char **restrict strp, const char *restrict fmt, ...)
+{
+ int ret = -1;
+ va_list args;
+
+ if (!strp || !fmt) {
+ return ret;
+ }
+
+ va_start(args, fmt);
+ ret = vasprintf(strp, fmt, args);
+ if (ret < 0) {
+ *strp = NULL;
+ }
+ va_end(args);
+
+ return ret;
+}
+
+char *dirname_malloc(const char *path)
+{
+ char *d, *dir, *dir2;
+
+ assert(path);
+
+ d = strdup(path);
+ if (!d)
+ return NULL;
+
+ dir = dirname(d);
+ assert(dir);
+
+ if (dir == d)
+ return d;
+
+ dir2 = strdup(dir);
+ free(d);
+
+ return dir2;
+}
diff --git a/src/install/util.h b/src/install/util.h
new file mode 100644
index 0000000..f022f15
--- /dev/null
+++ b/src/install/util.h
@@ -0,0 +1,609 @@
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#define _GNU_SOURCE
+
+#include <inttypes.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sched.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/resource.h>
+
+#include "macro.h"
+
+typedef uint64_t usec_t;
+typedef uint64_t nsec_t;
+
+typedef struct dual_timestamp {
+ usec_t realtime;
+ usec_t monotonic;
+} dual_timestamp;
+
+#define MSEC_PER_SEC 1000ULL
+#define USEC_PER_SEC 1000000ULL
+#define USEC_PER_MSEC 1000ULL
+#define NSEC_PER_SEC 1000000000ULL
+#define NSEC_PER_MSEC 1000000ULL
+#define NSEC_PER_USEC 1000ULL
+
+#define USEC_PER_MINUTE (60ULL*USEC_PER_SEC)
+#define NSEC_PER_MINUTE (60ULL*NSEC_PER_SEC)
+#define USEC_PER_HOUR (60ULL*USEC_PER_MINUTE)
+#define NSEC_PER_HOUR (60ULL*NSEC_PER_MINUTE)
+#define USEC_PER_DAY (24ULL*USEC_PER_HOUR)
+#define NSEC_PER_DAY (24ULL*NSEC_PER_HOUR)
+#define USEC_PER_WEEK (7ULL*USEC_PER_DAY)
+#define NSEC_PER_WEEK (7ULL*NSEC_PER_DAY)
+#define USEC_PER_MONTH (2629800ULL*USEC_PER_SEC)
+#define NSEC_PER_MONTH (2629800ULL*NSEC_PER_SEC)
+#define USEC_PER_YEAR (31557600ULL*USEC_PER_SEC)
+#define NSEC_PER_YEAR (31557600ULL*NSEC_PER_SEC)
+
+/* What is interpreted as whitespace? */
+#define WHITESPACE " \t\n\r"
+#define NEWLINE "\n\r"
+#define QUOTES "\"\'"
+#define COMMENTS "#;\n"
+
+#define FORMAT_TIMESTAMP_MAX 64
+#define FORMAT_TIMESTAMP_PRETTY_MAX 256
+#define FORMAT_TIMESPAN_MAX 64
+#define FORMAT_BYTES_MAX 8
+
+#define ANSI_HIGHLIGHT_ON "\x1B[1;39m"
+#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m"
+#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m"
+#define ANSI_HIGHLIGHT_YELLOW_ON "\x1B[1;33m"
+#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
+
+usec_t now(clockid_t clock);
+
+dual_timestamp *dual_timestamp_get(dual_timestamp *ts);
+dual_timestamp *dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
+
+#define dual_timestamp_is_set(ts) ((ts)->realtime > 0)
+
+usec_t timespec_load(const struct timespec *ts);
+struct timespec *timespec_store(struct timespec *ts, usec_t u);
+
+usec_t timeval_load(const struct timeval *tv);
+struct timeval *timeval_store(struct timeval *tv, usec_t u);
+
+size_t page_size(void);
+#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0)
+
+bool streq_ptr(const char *a, const char *b);
+
+#define new(t, n) ((t*) malloc(sizeof(t)*(n)))
+
+#define new0(t, n) ((t*) calloc((n), sizeof(t)))
+
+#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
+
+#define newdup(t, p, n) ((t*) memdup(p, sizeof(t)*(n)))
+
+#define malloc0(n) (calloc((n), 1))
+
+static inline const char *yes_no(bool b)
+{
+ return b ? "yes" : "no";
+}
+
+static inline const char *strempty(const char *s)
+{
+ return s ? s : "";
+}
+
+static inline const char *strnull(const char *s)
+{
+ return s ? s : "(null)";
+}
+
+static inline const char *strna(const char *s)
+{
+ return s ? s : "n/a";
+}
+
+static inline bool isempty(const char *p)
+{
+ return !p || !p[0];
+}
+
+static inline const char *startswith(const char *s, const char *prefix)
+{
+ if (strncmp(s, prefix, strlen(prefix)) == 0)
+ return s + strlen(prefix);
+ return NULL;
+}
+
+bool endswith(const char *s, const char *postfix);
+
+bool startswith_no_case(const char *s, const char *prefix);
+
+bool first_word(const char *s, const char *word);
+
+int close_nointr(int fd);
+void close_nointr_nofail(int fd);
+void close_many(const int fds[], unsigned int n_fd);
+
+int parse_boolean(const char *v);
+int parse_usec(const char *t, usec_t *usec);
+int parse_nsec(const char *t, nsec_t *nsec);
+int parse_bytes(const char *t, off_t *bytes);
+int parse_pid(const char *s, pid_t *ret_pid);
+int parse_uid(const char *s, uid_t *ret_uid);
+#define parse_gid(s, ret_uid) parse_uid(s, ret_uid)
+
+int safe_atou(const char *s, unsigned int *ret_u);
+int safe_atoi(const char *s, int *ret_i);
+
+int safe_atollu(const char *s, unsigned long long *ret_u);
+int safe_atolli(const char *s, long long int *ret_i);
+
+#if LONG_MAX == INT_MAX
+static inline int safe_atolu(const char *s, unsigned long *ret_u)
+{
+ assert_cc(sizeof(unsigned long) == sizeof(unsigned int));
+ return safe_atou(s, (unsigned int *)ret_u);
+}
+
+static inline int safe_atoli(const char *s, long int *ret_u)
+{
+ assert_cc(sizeof(long int) == sizeof(int));
+ return safe_atoi(s, (int *)ret_u);
+}
+#else
+static inline int safe_atolu(const char *s, unsigned long *ret_u)
+{
+ assert_cc(sizeof(unsigned long) == sizeof(unsigned long long));
+ return safe_atollu(s, (unsigned long long *)ret_u);
+}
+
+static inline int safe_atoli(const char *s, long int *ret_u)
+{
+ assert_cc(sizeof(long int) == sizeof(long long int));
+ return safe_atolli(s, (long long int *)ret_u);
+}
+#endif
+
+static inline int safe_atou32(const char *s, uint32_t *ret_u)
+{
+ assert_cc(sizeof(uint32_t) == sizeof(unsigned int));
+ return safe_atou(s, (unsigned int *)ret_u);
+}
+
+static inline int safe_atoi32(const char *s, int32_t *ret_i)
+{
+ assert_cc(sizeof(int32_t) == sizeof(int));
+ return safe_atoi(s, (int *)ret_i);
+}
+
+static inline int safe_atou64(const char *s, uint64_t *ret_u)
+{
+ assert_cc(sizeof(uint64_t) == sizeof(unsigned long long));
+ return safe_atollu(s, (unsigned long long *)ret_u);
+}
+
+static inline int safe_atoi64(const char *s, int64_t *ret_i)
+{
+ assert_cc(sizeof(int64_t) == sizeof(long long int));
+ return safe_atolli(s, (long long int *)ret_i);
+}
+
+char *split(const char *c, size_t *l, const char *separator, char **state);
+char *split_quoted(const char *c, size_t *l, char **state);
+
+#define FOREACH_WORD(word, length, s, state) \
+ for ((state) = NULL, (word) = split((s), &(length), WHITESPACE, &(state)); (word); (word) = split((s), &(length), WHITESPACE, &(state)))
+
+#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
+ for ((state) = NULL, (word) = split((s), &(length), (separator), &(state)); (word); (word) = split((s), &(length), (separator), &(state)))
+
+#define FOREACH_WORD_QUOTED(word, length, s, state) \
+ for ((state) = NULL, (word) = split_quoted((s), &(length), &(state)); (word); (word) = split_quoted((s), &(length), &(state)))
+
+pid_t get_parent_of_pid(pid_t pid, pid_t *ppid);
+int get_starttime_of_pid(pid_t pid, unsigned long long *st);
+
+int write_one_line_file(const char *fn, const char *line);
+int write_one_line_file_atomic(const char *fn, const char *line);
+int read_one_line_file(const char *fn, char **line);
+int read_full_file(const char *fn, char **contents, size_t *size);
+
+int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
+int load_env_file(const char *fname, char ***l);
+int write_env_file(const char *fname, char **l);
+
+char *strappend(const char *s, const char *suffix);
+char *strnappend(const char *s, const char *suffix, size_t length);
+
+char *replace_env(const char *format, char **env);
+char **replace_env_argv(char **argv, char **env);
+
+int readlink_malloc(const char *p, char **r);
+int readlink_and_make_absolute(const char *p, char **r);
+int readlink_and_canonicalize(const char *p, char **r);
+
+int reset_all_signal_handlers(void);
+
+char *strstrip(char *s);
+char *delete_chars(char *s, const char *bad);
+char *truncate_nl(char *s);
+
+char *file_in_same_dir(const char *path, const char *filename);
+
+int rmdir_parents(const char *path, const char *stop);
+
+int get_process_comm(pid_t pid, char **name);
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line);
+int get_process_exe(pid_t pid, char **name);
+int get_process_uid(pid_t pid, uid_t *uid);
+
+char hexchar(int x);
+int unhexchar(char c);
+char octchar(int x);
+int unoctchar(char c);
+char decchar(int x);
+int undecchar(char c);
+
+char *cescape(const char *s);
+char *cunescape(const char *s);
+char *cunescape_length(const char *s, size_t length);
+
+char *xescape(const char *s, const char *bad);
+
+char *bus_path_escape(const char *s);
+char *bus_path_unescape(const char *s);
+
+char *ascii_strlower(char *path);
+
+bool dirent_is_file(const struct dirent *de);
+bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix);
+
+bool ignore_file(const char *filename);
+
+bool chars_intersect(const char *a, const char *b);
+
+char *format_timestamp(char *buf, size_t l, usec_t t);
+char *format_timestamp_pretty(char *buf, size_t l, usec_t t);
+char *format_timespan(char *buf, size_t l, usec_t t);
+
+int make_stdio(int fd);
+int make_null_stdio(void);
+
+unsigned long long random_ull(void);
+
+#define __DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \
+ scope const char *name##_to_string(type i) { \
+ if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \
+ return NULL; \
+ return name##_table[i]; \
+ } \
+ scope type name##_from_string(const char *s) { \
+ type i; \
+ unsigned int u = 0; \
+ assert(s); \
+ for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \
+ if (name##_table[i] && \
+ streq(name##_table[i], s)) \
+ return i; \
+ if (safe_atou(s, &u) >= 0 && \
+ u < ELEMENTSOF(name##_table)) \
+ return (type) u; \
+ return (type) -1; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+#define DEFINE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,)
+#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) __DEFINE_STRING_TABLE_LOOKUP(name,type,static)
+
+int fd_nonblock(int fd, bool nonblock);
+int fd_cloexec(int fd, bool cloexec);
+
+int close_all_fds(const int except[], unsigned int n_except);
+
+bool fstype_is_network(const char *fstype);
+
+int chvt(int vt);
+
+int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
+int ask(char *ret, const char *replies, const char *text, ...);
+
+int reset_terminal_fd(int fd, bool switch_to_text);
+int reset_terminal(const char *name);
+
+int open_terminal(const char *name, int mode);
+int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm);
+int release_terminal(void);
+
+int flush_fd(int fd);
+
+int ignore_signals(int sig, ...);
+int default_signals(int sig, ...);
+int sigaction_many(const struct sigaction *sa, ...);
+
+int close_pipe(int p[]);
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
+
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll);
+ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll);
+
+bool is_device_path(const char *path);
+
+int dir_is_empty(const char *path);
+
+void rename_process(const char name[8]);
+
+void sigset_add_many(sigset_t *ss, ...);
+
+char *gethostname_malloc(void);
+bool hostname_is_set(void);
+char *getlogname_malloc(void);
+
+int getttyname_malloc(int fd, char **r);
+int getttyname_harder(int fd, char **r);
+
+int get_ctty_devnr(pid_t pid, dev_t *d);
+int get_ctty(pid_t, dev_t *_devnr, char **r);
+
+int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid);
+
+int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev);
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky);
+
+int pipe_eof(int fd);
+
+cpu_set_t *cpu_set_malloc(unsigned int *ncpus);
+
+void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap);
+void status_printf(const char *status, bool ellipse, const char *format, ...);
+void status_welcome(void);
+
+int fd_columns(int fd);
+unsigned int columns(void);
+
+int fd_lines(int fd);
+unsigned int lines(void);
+
+int running_in_chroot(void);
+
+char *ellipsize(const char *s, size_t length, unsigned int percent);
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned int percent);
+
+int touch(const char *path);
+
+char *unquote(const char *s, const char *quotes);
+char *normalize_env_assignment(const char *s);
+
+int wait_for_terminate(pid_t pid, siginfo_t *status);
+int wait_for_terminate_and_warn(const char *name, pid_t pid);
+
+_noreturn_ void freeze(void);
+
+bool null_or_empty(struct stat *st);
+int null_or_empty_path(const char *fn);
+
+DIR *xopendirat(int dirfd, const char *name, int flags);
+
+void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t);
+void dual_timestamp_deserialize(const char *value, dual_timestamp *t);
+
+char *fstab_node_to_udev_node(const char *p);
+
+bool tty_is_vc(const char *tty);
+bool tty_is_vc_resolve(const char *tty);
+bool tty_is_console(const char *tty);
+int vtnr_from_tty(const char *tty);
+const char *default_term_for_tty(const char *tty);
+
+void execute_directory(const char *directory, DIR *_d, char *argv[]);
+
+int kill_and_sigcont(pid_t pid, int sig);
+
+bool nulstr_contains(const char *nulstr, const char *needle);
+
+bool plymouth_running(void);
+
+void parse_syslog_priority(char **p, int *priority);
+void skip_syslog_pid(char **buf);
+void skip_syslog_date(char **buf);
+
+bool hostname_is_valid(const char *s);
+char *hostname_cleanup(char *s);
+
+char *strshorten(char *s, size_t l);
+
+int terminal_vhangup_fd(int fd);
+int terminal_vhangup(const char *name);
+
+int vt_disallocate(const char *name);
+
+int copy_file(const char *from, const char *to);
+int symlink_or_copy(const char *from, const char *to);
+int symlink_or_copy_atomic(const char *from, const char *to);
+
+int fchmod_umask(int fd, mode_t mode);
+
+bool display_is_local(const char *display);
+int socket_from_display(const char *display, char **path);
+
+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home);
+int get_group_creds(const char **groupname, gid_t *gid);
+
+int in_group(const char *name);
+
+int glob_exists(const char *path);
+
+int dirent_ensure_type(DIR *d, struct dirent *de);
+
+int in_search_path(const char *path, char **search);
+int get_files_in_directory(const char *path, char ***list);
+
+char *join(const char *x, ...) _sentinel_;
+
+bool is_main_thread(void);
+
+bool in_charset(const char *s, const char *charset);
+
+int block_get_whole_disk(dev_t d, dev_t *ret);
+
+int file_is_priv_sticky(const char *p);
+
+int strdup_or_null(const char *a, char **b);
+
+#define NULSTR_FOREACH(i, l) \
+ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
+
+#define NULSTR_FOREACH_PAIR(i, j, l) \
+ for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i))
+
+const char *ioprio_class_to_string(int i);
+int ioprio_class_from_string(const char *s);
+
+const char *sigchld_code_to_string(int i);
+int sigchld_code_from_string(const char *s);
+
+const char *log_facility_unshifted_to_string(int i);
+int log_facility_unshifted_from_string(const char *s);
+
+const char *log_level_to_string(int i);
+int log_level_from_string(const char *s);
+
+const char *sched_policy_to_string(int i);
+int sched_policy_from_string(const char *s);
+
+const char *rlimit_to_string(int i);
+int rlimit_from_string(const char *s);
+
+const char *ip_tos_to_string(int i);
+int ip_tos_from_string(const char *s);
+
+const char *signal_to_string(int i);
+int signal_from_string(const char *s);
+
+int signal_from_string_try_harder(const char *s);
+
+extern int saved_argc;
+extern char **saved_argv;
+
+bool kexec_loaded(void);
+
+int prot_from_flags(int flags);
+
+char *format_bytes(char *buf, size_t l, off_t t);
+
+int fd_wait_for_event(int fd, int event, usec_t timeout);
+
+void *memdup(const void *p, size_t l);
+
+int is_kernel_thread(pid_t pid);
+
+static inline void freep(void *p)
+{
+ free(*(void **)p);
+}
+
+static inline void fclosep(FILE **f)
+{
+ if (*f)
+ fclose(*f);
+}
+
+static inline void pclosep(FILE **f)
+{
+ if (*f)
+ pclose(*f);
+}
+
+static inline void closep(int *fd)
+{
+ if (*fd >= 0)
+ close_nointr_nofail(*fd);
+}
+
+static inline void closedirp(DIR **d)
+{
+ if (*d)
+ closedir(*d);
+}
+
+static inline void umaskp(mode_t *u)
+{
+ umask(*u);
+}
+
+#define _cleanup_free_ _cleanup_(freep)
+#define _cleanup_fclose_ _cleanup_(fclosep)
+#define _cleanup_pclose_ _cleanup_(pclosep)
+#define _cleanup_close_ _cleanup_(closep)
+#define _cleanup_closedir_ _cleanup_(closedirp)
+#define _cleanup_umask_ _cleanup_(umaskp)
+#define _cleanup_globfree_ _cleanup_(globfree)
+
+int fd_inc_sndbuf(int fd, size_t n);
+int fd_inc_rcvbuf(int fd, size_t n);
+
+int fork_agent(pid_t *pid, const int except[], unsigned int n_except, const char *path, ...);
+
+int setrlimit_closest(int resource, const struct rlimit *rlim);
+
+int getenv_for_pid(pid_t pid, const char *field, char **_value);
+
+int can_sleep(const char *type);
+
+bool is_valid_documentation_url(const char *url);
+
+bool in_initrd(void);
+
+void warn_melody(void);
+
+char *strjoin(const char *x, ...) _sentinel_;
+
+#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \
+ static inline void func##p(type *p) { \
+ if (*p) \
+ func(*p); \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+char *split_quoted(const char *c, size_t *l, char **state);
+char *cunescape_length(const char *s, size_t length);
+int unhexchar(char c) _const_;
+int unoctchar(char c) _const_;
+
+int dracut_asprintf(char **restrict strp, const char *restrict fmt, ...);
+char *dirname_malloc(const char *path);
+
+#endif