install/*: add dracut-install tool
authorHarald Hoyer <harald@redhat.com>
Fri, 29 Jun 2012 09:59:09 +0000 (11:59 +0200)
committerHarald Hoyer <harald@redhat.com>
Fri, 29 Jun 2012 10:54:38 +0000 (12:54 +0200)
13 files changed:
Makefile
dracut.spec
install/Makefile [new file with mode: 0644]
install/dracut-install.c [new file with mode: 0644]
install/hashmap.c [new file with mode: 0644]
install/hashmap.h [new file with mode: 0644]
install/hashmap.lo [new file with mode: 0644]
install/hashmap.o [new file with mode: 0644]
install/log.c [new file with mode: 0644]
install/log.h [new file with mode: 0644]
install/macro.h [new file with mode: 0644]
install/util.c [new file with mode: 0644]
install/util.h [new file with mode: 0644]

index 4ed8d1f..a85147d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,10 @@ manpages = dracut.8 dracut.cmdline.7 dracut.conf.5 dracut-catimages.8
 
 .PHONY: install clean archive rpm testimage test all check AUTHORS doc
 
-all: syncheck dracut-version.sh
+all: syncheck dracut-version.sh install/dracut-install
+
+install/dracut-install:
+       $(MAKE) -C install dracut-install
 
 doc: $(manpages) dracut.html
 
@@ -71,6 +74,7 @@ install: doc dracut-version.sh
                ln -s ../dracut-shutdown.service \
                $(DESTDIR)$(systemdsystemunitdir)/shutdown.target.wants/dracut-shutdown.service; \
        fi
+       $(MAKE) -C install install
 
 dracut-version.sh:
        @echo "DRACUT_VERSION=$(VERSION)-$(GITVERSION)" > dracut-version.sh
@@ -83,6 +87,7 @@ clean:
        $(RM) dracut-*.rpm dracut-*.tar.bz2
        $(RM) $(manpages) dracut.html
        $(MAKE) -C test clean
+       $(MAKE) -C install clean
 
 archive: dracut-$(VERSION)-$(GITVERSION).tar.bz2
 
@@ -104,7 +109,7 @@ rpm: dracut-$(VERSION).tar.bz2
        (cd "$$rpmbuild"; rpmbuild --define "_topdir $$PWD" --define "_sourcedir $$PWD" \
                --define "_specdir $$PWD" --define "_srcrpmdir $$PWD" \
                --define "_rpmdir $$PWD" -ba dracut.spec; ) && \
-       ( mv "$$rpmbuild"/noarch/*.rpm .; mv "$$rpmbuild"/*.src.rpm .;rm -fr "$$rpmbuild"; ls *.rpm )
+       ( mv "$$rpmbuild"/$$(arch)/*.rpm .; mv "$$rpmbuild"/*.src.rpm .;rm -fr "$$rpmbuild"; ls *.rpm )
 
 syncheck:
        @ret=0;for i in dracut-initramfs-restore.sh dracut-logger.sh \
@@ -123,17 +128,17 @@ check: all syncheck
 
 testimage: all
        ./dracut.sh -l -a debug -f test-$(shell uname -r).img $(shell uname -r)
-       @echo wrote  test-$(shell uname -r).img 
+       @echo wrote  test-$(shell uname -r).img
 
 testimages: all
        ./dracut.sh -l -a debug --kernel-only -f test-kernel-$(shell uname -r).img $(shell uname -r)
-       @echo wrote  test-$(shell uname -r).img 
+       @echo wrote  test-$(shell uname -r).img
        ./dracut.sh -l -a debug --no-kernel -f test-dracut.img $(shell uname -r)
-       @echo wrote  test-dracut.img 
+       @echo wrote  test-dracut.img
 
 hostimage: all
        ./dracut.sh -H -l -a debug -f test-$(shell uname -r).img $(shell uname -r)
-       @echo wrote  test-$(shell uname -r).img 
+       @echo wrote  test-$(shell uname -r).img
 
 AUTHORS:
        git shortlog  --numbered --summary -e |while read a rest; do echo $$rest;done > AUTHORS
index b19d15b..96dbc9c 100644 (file)
@@ -25,7 +25,6 @@ URL: https://dracut.wiki.kernel.org/
 # http://git.kernel.org/?p=boot/dracut/dracut.git;a=snapshot;h=%{version};sf=tgz
 Source0: http://www.kernel.org/pub/linux/utils/boot/dracut/dracut-%{version}.tar.bz2
 
-BuildArch: noarch
 BuildRequires: dash bash git
 
 %if 0%{?fedora} || 0%{?rhel}
@@ -236,6 +235,7 @@ rm -rf $RPM_BUILD_ROOT
 %if 0%{?fedora} > 12 || 0%{?rhel} >= 6 || 0%{?suse_version} > 9999
 %{_bindir}/mkinitrd
 %{_bindir}/lsinitrd
+%{_bindir}/dracut-install
 %endif
 %dir %{dracutlibdir}
 %dir %{dracutlibdir}/modules.d
diff --git a/install/Makefile b/install/Makefile
new file mode 100644 (file)
index 0000000..59532a8
--- /dev/null
@@ -0,0 +1,17 @@
+prefix ?= /usr
+bindir ?= ${prefix}/bin
+strip  ?= -s
+
+all: dracut-install
+
+dracut-install: dracut-install.c hashmap.c log.c util.c
+       gcc -std=gnu99 -O2 -g -Wall -o dracut-install dracut-install.c hashmap.c log.c util.c
+
+install: dracut-install
+       install $(strip) -m 0755 dracut-install $(DESTDIR)$(bindir)/dracut-install
+
+clean:
+       rm -f dracut-install *~
+
+indent:
+       indent -i8 -nut -br -linux -l120 dracut-install.c
diff --git a/install/dracut-install.c b/install/dracut-install.c
new file mode 100644 (file)
index 0000000..ccd4ba4
--- /dev/null
@@ -0,0 +1,736 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/* 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 "1"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.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 "log.h"
+#include "hashmap.h"
+#include "util.h"
+
+static bool arg_hmac = false;
+static bool arg_createdir = false;
+static int arg_loglevel = -1;
+static bool arg_optional = false;
+static bool arg_all = false;
+static bool arg_resolvelazy = false;
+static bool arg_resolvedeps = false;
+static char *destrootdir = NULL;
+
+static Hashmap *items = NULL;
+static Hashmap *items_failed = NULL;
+
+static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps);
+
+static size_t dir_len(char const *file)
+{
+        size_t length;
+        /* Strip the basename and any redundant slashes before it.  */
+        for (length = strlen(file); 0 < length; length--)
+                if (file[length] == '/')
+                        break;
+        return length;
+}
+
+static char *convert_abs_rel(const char *from, const char *target)
+{
+        /* we use the 4*MAXPATHLEN, which should not overrun */
+        char relative_from[MAXPATHLEN * 4];
+        char *realtarget = NULL;
+        char *p, *q;
+        const char *realfrom = from;
+        int level = 0, fromlevel = 0, targetlevel = 0;
+        int l, i, rl;
+        int dirlen;
+
+        p = strdup(target);
+        dirlen = dir_len(p);
+        p[dirlen] = '\0';
+        q = realpath(p, NULL);
+
+        if (q == NULL) {
+                free(p);
+                log_warning("convert_abs_rel(): target '%s' directory has no realpath.", target);
+                return strdup(from);
+        }
+
+        asprintf(&realtarget, "%s/%s", q, &p[dirlen + 1]);
+        free(p);
+        free(q);
+
+        /* now calculate the relative path from <from> to <target> and
+           store it in <relative_from>
+         */
+        relative_from[0] = 0;
+        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++;
+
+        free(realtarget);
+
+        /* add "../" to the relative_from path, until the common pathname is
+           reached */
+        for (i = level; i < targetlevel; i++) {
+                if (i != level)
+                        relative_from[rl++] = '/';
+                relative_from[rl++] = '.';
+                relative_from[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 relative_from path */
+        for (i = level; i <= fromlevel; i++) {
+                if (rl)
+                        relative_from[rl++] = '/';
+                while (realfrom[l] && realfrom[l] != '/')
+                        relative_from[rl++] = realfrom[l++];
+                l++;
+        }
+
+        relative_from[rl] = 0;
+        return strdup(relative_from);
+}
+
+static int ln_r(const char *src, const char *dst)
+{
+        int ret;
+        const char *points_to = convert_abs_rel(src, dst);
+        log_info("ln -s '%s' '%s'", points_to, dst);
+        ret = symlink(points_to, dst);
+
+        if (ret != 0) {
+                log_error("ERROR: ln -s '%s' '%s': %m", points_to, dst);
+                free((char *)points_to);
+                return 1;
+        }
+
+        free((char *)points_to);
+
+        return 0;
+}
+
+static int cp(const char *src, const char *dst)
+{
+        int pid;
+        int status;
+
+        pid = fork();
+        if (pid == 0) {
+                execlp("cp", "cp", "--reflink=auto", "--sparse=auto", "--preserve=mode", "-fL", src, dst, NULL);
+                _exit(EXIT_FAILURE);
+        }
+
+        while (waitpid(pid, &status, 0) < 0) {
+                if (errno != EINTR) {
+                        status = -1;
+                        break;
+                }
+        }
+
+        return status;
+}
+
+static int resolve_deps(const char *src)
+{
+        int ret = 0;
+
+        char *buf = malloc(LINE_MAX);
+        size_t linesize = LINE_MAX;
+        FILE *fptr;
+        char *cmd;
+
+        if (strstr(src, ".so") == 0) {
+                int fd;
+                fd = open(src, O_RDONLY | O_CLOEXEC);
+                read(fd, buf, LINE_MAX);
+                buf[LINE_MAX - 1] = '\0';
+                close(fd);
+                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);
+                        if (ret != 0)
+                                log_error("ERROR: failed to install '%s'", p);
+                        return ret;
+                }
+        }
+
+        /* run ldd */
+        asprintf(&cmd, "ldd %s", src);
+        fptr = popen(cmd, "r");
+
+        while (!feof(fptr)) {
+                char *p, *q;
+
+                if (getline(&buf, &linesize, fptr) <= 0)
+                        continue;
+
+                log_debug("ldd: '%s'", buf);
+
+                if (strstr(buf, "not a dynamic executable"))
+                        break;
+
+                p = strstr(buf, "/");
+                if (p) {
+                        int r;
+                        for (q = p; *q && *q != ' ' && *q != '\n'; q++) ;
+                        *q = '\0';
+                        r = dracut_install(p, p, false, false);
+                        if (r != 0)
+                                log_error("ERROR: failed to install '%s' for '%s'", p, src);
+                        else
+                                log_debug("Lib install: '%s'", p);
+                        ret += r;
+
+                        /* also install lib.so for lib.so.* files */
+                        q = strstr(p, ".so.");
+                        if (q) {
+                                q += 3;
+                                *q = '\0';
+
+                                /* ignore errors for base lib symlink */
+                                if (dracut_install(p, p, false, false) == 0)
+                                        log_debug("Lib install: '%s'", p);
+                        }
+                }
+        }
+        pclose(fptr);
+
+        return ret;
+}
+
+/* Install ".<filename>.hmac" file for FIPS self-checks */
+static int hmac_install(const char *src, const char *dst)
+{
+        char *srcpath = strdup(src);
+        char *dstpath = strdup(dst);
+        char *srchmacname = NULL;
+        char *dsthmacname = NULL;
+        size_t dlen = dir_len(src);
+
+        if (endswith(src, ".hmac"))
+                return 0;
+
+        srcpath[dlen] = '\0';
+        dstpath[dir_len(dst)] = '\0';
+        asprintf(&srchmacname, "%s/.%s.hmac", srcpath, &src[dlen + 1]);
+        asprintf(&dsthmacname, "%s/.%s.hmac", dstpath, &src[dlen + 1]);
+        log_debug("hmac cp '%s' '%s')", srchmacname, dsthmacname);
+        dracut_install(srchmacname, dsthmacname, false, false);
+        free(dsthmacname);
+        free(srchmacname);
+        free(srcpath);
+        free(dstpath);
+        return 0;
+}
+
+static int dracut_install(const char *src, const char *dst, bool isdir, bool resolvedeps)
+{
+        struct stat sb, db;
+        char *dname = NULL;
+        char *fulldstpath = NULL;
+        char *fulldstdir = NULL;
+        int ret;
+        bool src_exists = true;
+        char *i, *existing;
+
+        log_debug("dracut_install('%s', '%s')", src, dst);
+
+        existing = hashmap_get(items_failed, src);
+        if (existing) {
+                if (strcmp(existing, src) == 0) {
+                        log_debug("hash hit items_failed for '%s'", src);
+                        return 1;
+                }
+        }
+
+        existing = hashmap_get(items, dst);
+        if (existing) {
+                if (strcmp(existing, dst) == 0) {
+                        log_debug("hash hit items for '%s'", dst);
+                        return 0;
+                }
+        }
+
+        if (lstat(src, &sb) < 0) {
+                src_exists = false;
+                if (!isdir) {
+                        i = strdup(src);
+                        hashmap_put(items_failed, i, i);
+                        /* src does not exist */
+                        return 1;
+                }
+        }
+
+        i = strdup(dst);
+        hashmap_put(items, i, i);
+
+        asprintf(&fulldstpath, "%s%s", destrootdir, dst);
+
+        ret = stat(fulldstpath, &sb);
+
+        if (ret != 0 && (errno != ENOENT)) {
+                log_error("ERROR: stat '%s': %m", fulldstpath);
+                return 1;
+        }
+
+        if (ret == 0) {
+                log_debug("'%s' already exists", fulldstpath);
+                free(fulldstpath);
+                /* dst does already exist */
+                return 0;
+        }
+
+        /* check destination directory */
+        fulldstdir = strdup(fulldstpath);
+        fulldstdir[dir_len(fulldstdir)] = '\0';
+
+        ret = stat(fulldstdir, &db);
+
+        if (ret < 0) {
+                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 = strdup(dst);
+                dname[dir_len(dname)] = '\0';
+                ret = dracut_install(dname, dname, true, false);
+
+                free(dname);
+
+                if (ret != 0) {
+                        log_error("ERROR: failed to create directory '%s'", fulldstdir);
+                        free(fulldstdir);
+                        return 1;
+                }
+        }
+
+        free(fulldstdir);
+
+        if (isdir && !src_exists) {
+                log_info("mkdir '%s'", fulldstpath);
+                return mkdir(fulldstpath, 0755);
+        }
+
+        /* ready to install src */
+
+        if (S_ISDIR(sb.st_mode)) {
+                log_info("mkdir '%s'", fulldstpath);
+                return mkdir(fulldstpath, sb.st_mode | S_IWUSR);
+        }
+
+        if (S_ISLNK(sb.st_mode)) {
+                char *abspath;
+                char *absdestpath = NULL;
+
+                abspath = realpath(src, NULL);
+
+                if (abspath == NULL)
+                        return 1;
+
+                if (dracut_install(abspath, abspath, false, resolvedeps)) {
+                        log_debug("'%s' install error", abspath);
+                        return 1;
+                }
+
+                if (lstat(abspath, &sb) != 0) {
+                        log_debug("lstat '%s': %m", abspath);
+                        return 1;
+                }
+
+                if (lstat(fulldstpath, &sb) != 0) {
+
+                        asprintf(&absdestpath, "%s%s", destrootdir, abspath);
+
+                        ln_r(absdestpath, fulldstpath);
+
+                        free(absdestpath);
+                }
+
+                free(abspath);
+                if (arg_hmac) {
+                        /* copy .hmac files also */
+                        hmac_install(src, dst);
+                }
+
+                return 0;
+        }
+
+        if (sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+                if (resolvedeps)
+                        ret += resolve_deps(src);
+                if (arg_hmac) {
+                        /* copy .hmac files also */
+                        hmac_install(src, dst);
+                }
+        }
+
+        log_info("cp '%s' '%s'", src, fulldstpath);
+        ret += cp(src, fulldstpath);
+        return ret;
+}
+
+static void item_free(char *i)
+{
+        assert(i);
+        free(i);
+}
+
+static void usage(int status)
+{
+        /*                                                                     */
+        printf("\
+Usage: %s -D DESTROOTDIR [OPTION]... -a SOURCE...\n\
+   or: %s -D DESTROOTDIR [OPTION]... SOURCE DEST\n\
+\n\
+Install SOURCE to DEST in DESTROOTDIR with all needed dependencies.\n\
+\n\
+  -D --destrootdir    Install all files to DESTROOTDIR as the root\n\
+  -a --all            Install all SOURCE arguments to DESTROOTDIR\n\
+  -o --optional       If SOURCE does not exist, do not fail\n\
+  -d --dir            SOURCE is a directory\n\
+  -l --ldd            Also install shebang executables and libraries\n\
+  -R --resolvelazy    Only install shebang executables and libraries for all SOURCE files\n\
+  -f --fips           Also install all '.SOURCE.hmac' files\n\
+  -v --verbose        Show more output\n\
+     --debug          Show debug output\n\
+     --version        Show package version\n\
+  -h --help           Show this help\n\
+\n\
+Example:\n\
+# %s -D /var/tmp/test-root --ldd -a sh tr\n\
+# tree /var/tmp/test-root\n\
+/var/tmp/test-root\n\
+|-- lib64 -> usr/lib64\n\
+`-- usr\n\
+    |-- bin\n\
+    |   |-- bash\n\
+    |   |-- sh -> bash\n\
+    |   `-- tr\n\
+    `-- lib64\n\
+        |-- ld-2.15.90.so\n\
+        |-- ld-linux-x86-64.so.2 -> ld-2.15.90.so\n\
+        |-- libc-2.15.90.so\n\
+        |-- libc.so\n\
+        |-- libc.so.6 -> libc-2.15.90.so\n\
+        |-- libdl-2.15.90.so\n\
+        |-- libdl.so -> libdl-2.15.90.so\n\
+        |-- libdl.so.2 -> libdl-2.15.90.so\n\
+        |-- libtinfo.so.5 -> libtinfo.so.5.9\n\
+        `-- libtinfo.so.5.9\n\
+", program_invocation_short_name, program_invocation_short_name, program_invocation_short_name);
+        exit(status);
+}
+
+static int parse_argv(int argc, char *argv[])
+{
+        int c;
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_DEBUG
+        };
+
+        static const 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'},
+                {"all", no_argument, NULL, 'a'},
+                {"fips", no_argument, NULL, 'H'},
+                {"destrootdir", required_argument, NULL, 'D'},
+                {NULL, 0, NULL, 0}
+        };
+
+        while ((c = getopt_long(argc, argv, "adhloD:DHILR", 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 '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 'D':
+                        destrootdir = strdup(optarg);
+                        break;
+                case 'H':
+                        arg_hmac = true;
+                        break;
+                case 'h':
+                        usage(EXIT_SUCCESS);
+                        break;
+                default:
+                        usage(EXIT_FAILURE);
+                }
+        }
+
+        if (!optind || optind == argc) {
+                usage(EXIT_FAILURE);
+        }
+
+        return 1;
+}
+
+static int resolve_lazy(int argc, char **argv)
+{
+        int i;
+        int destrootdirlen = strlen(destrootdir);
+        int ret = 0;
+        char *item;
+        for (i = 0; i < argc; i++) {
+                const char *src = argv[i];
+                char *p = argv[i];
+                char *existing;
+
+                log_debug("resolve_deps('%s')", src);
+
+                if (strstr(src, destrootdir)) {
+                        p = &argv[i][destrootdirlen];
+                }
+
+                existing = hashmap_get(items, p);
+                if (existing) {
+                        if (strcmp(existing, p) == 0)
+                                continue;
+                }
+
+                item = strdup(p);
+                hashmap_put(items, item, item);
+
+                ret += resolve_deps(src);
+        }
+        return ret;
+}
+
+static int install_all(int argc, char **argv)
+{
+        int r = 0;
+        int i;
+        for (i = 0; i < argc; i++) {
+                int ret;
+                log_debug("Handle '%s'", argv[i]);
+
+                if (strchr(argv[i], '/') == NULL) {
+                        char *path;
+                        char *p, *q;
+                        bool end = false;
+                        path = getenv("PATH");
+                        if (path == NULL) {
+                                log_error("PATH is not set");
+                                exit(EXIT_FAILURE);
+                        }
+                        path = strdup(path);
+                        p = path;
+                        log_debug("PATH=%s", path);
+                        do {
+                                char *newsrc = NULL;
+                                char *dest;
+                                struct stat sb;
+
+                                for (q = p; *q && *q != ':'; q++) ;
+
+                                if (*q == '\0')
+                                        end = true;
+                                else
+                                        *q = '\0';
+
+                                asprintf(&newsrc, "%s/%s", p, argv[i]);
+                                p = q + 1;
+
+                                if (stat(newsrc, &sb) != 0) {
+                                        free(newsrc);
+                                        ret = -1;
+                                        continue;
+                                }
+
+                                dest = strdup(newsrc);
+
+                                log_debug("dracut_install '%s'", newsrc);
+                                ret = dracut_install(newsrc, dest, arg_createdir, arg_resolvedeps);
+                                if (ret == 0) {
+                                        end = true;
+                                        log_debug("dracut_install '%s' OK", newsrc);
+                                }
+                                free(newsrc);
+                                free(dest);
+                        } while (!end);
+                        free(path);
+                } else {
+                        char *dest = strdup(argv[i]);
+                        ret = dracut_install(argv[i], dest, arg_createdir, arg_resolvedeps);
+                        free(dest);
+                }
+
+                if ((ret != 0) &&  (!arg_optional)) {
+                        log_error("ERROR: installing '%s'", argv[i]);
+                        r = EXIT_FAILURE;
+                }
+        }
+        return r;
+}
+
+int main(int argc, char **argv)
+{
+        int r;
+        char *i;
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+        log_set_target(LOG_TARGET_CONSOLE);
+        log_parse_environment();
+
+        if (arg_loglevel >= 0)
+                log_set_max_level(arg_loglevel);
+
+        log_open();
+
+        umask(0022);
+
+        if (destrootdir == NULL) {
+                destrootdir = getenv("DESTROOTDIR");
+                if (destrootdir == NULL) {
+                        log_error("Environment DESTROOTDIR or argument -D is not set!");
+                        usage(EXIT_FAILURE);
+                }
+                destrootdir = strdup(destrootdir);
+        }
+
+        items = hashmap_new(string_hash_func, string_compare_func);
+        items_failed = hashmap_new(string_hash_func, string_compare_func);
+
+        if (!items || !items_failed) {
+                log_error("Out of memory");
+                r = EXIT_FAILURE;
+                goto finish;
+        }
+
+        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_resolvelazy) {
+                r = resolve_lazy(argc - optind, &argv[optind]);
+        } else if (arg_all || (argc - optind > 2) || ((argc - optind) == 1)) {
+                r = install_all(argc - optind, &argv[optind]);
+        } else {
+                /* simple "inst src dst" */
+                r = dracut_install(argv[optind], argv[optind + 1], arg_createdir, arg_resolvedeps);
+                if ((r != 0) && (!arg_optional)) {
+                        log_error("ERROR: installing '%s' to '%s'", argv[optind], argv[optind + 1]);
+                        r = EXIT_FAILURE;
+                }
+        }
+
+        if (arg_optional)
+                r = EXIT_SUCCESS;
+
+ finish:
+
+        while ((i = hashmap_steal_first(items)))
+                item_free(i);
+
+        while ((i = hashmap_steal_first(items_failed)))
+                item_free(i);
+
+        hashmap_free(items);
+        hashmap_free(items_failed);
+
+        free(destrootdir);
+
+        return r;
+}
diff --git a/install/hashmap.c b/install/hashmap.c
new file mode 100644 (file)
index 0000000..5d1e632
--- /dev/null
@@ -0,0 +1,731 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  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 n_entries;
+
+        bool from_pool;
+};
+
+#define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
+
+struct pool {
+        struct pool *next;
+        unsigned n_tiles;
+        unsigned n_used;
+};
+
+static struct pool *first_hashmap_pool = NULL;
+static void *first_hashmap_tile = NULL;
+
+static struct pool *first_entry_pool = NULL;
+static void *first_entry_tile = NULL;
+
+static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size) {
+        unsigned i;
+
+        if (*first_tile) {
+                void *r;
+
+                r = *first_tile;
+                *first_tile = * (void**) (*first_tile);
+                return r;
+        }
+
+        if (_unlikely_(!*first_pool) || _unlikely_((*first_pool)->n_used >= (*first_pool)->n_tiles)) {
+                unsigned n;
+                size_t size;
+                struct pool *p;
+
+                n = *first_pool ? (*first_pool)->n_tiles : 0;
+                n = MAX(512U, n * 2);
+                size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size);
+                n = (size - ALIGN(sizeof(struct pool))) / tile_size;
+
+                p = malloc(size);
+                if (!p)
+                        return NULL;
+
+                p->next = *first_pool;
+                p->n_tiles = n;
+                p->n_used = 0;
+
+                *first_pool = p;
+        }
+
+        i = (*first_pool)->n_used++;
+
+        return ((uint8_t*) (*first_pool)) + ALIGN(sizeof(struct pool)) + i*tile_size;
+}
+
+static void deallocate_tile(void **first_tile, void *p) {
+        * (void**) p = *first_tile;
+        *first_tile = p;
+}
+
+#ifndef __OPTIMIZE__
+
+static void drop_pool(struct pool *p) {
+        while (p) {
+                struct pool *n;
+                n = p->next;
+                free(p);
+                p = n;
+        }
+}
+
+__attribute__((destructor)) static void cleanup_pool(void) {
+        /* Be nice to valgrind */
+
+        drop_pool(first_hashmap_pool);
+        drop_pool(first_entry_pool);
+}
+
+#endif
+
+unsigned string_hash_func(const void *p) {
+        unsigned hash = 5381;
+        const signed char *c;
+
+        /* DJB's hash function */
+
+        for (c = p; *c; c++)
+                hash = (hash << 5) + hash + (unsigned) *c;
+
+        return hash;
+}
+
+int string_compare_func(const void *a, const void *b) {
+        return strcmp(a, b);
+}
+
+unsigned 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) {
+        bool b;
+        Hashmap *h;
+        size_t size;
+
+        b = is_main_thread();
+
+        size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*);
+
+        if (b) {
+                h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size);
+                if (!h)
+                        return NULL;
+
+                memset(h, 0, size);
+        } else {
+                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;
+
+        h->from_pool = b;
+
+        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 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 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 *e) {
+        unsigned hash;
+
+        assert(h);
+        assert(e);
+
+        hash = h->hash_func(e->key) % NBUCKETS;
+
+        unlink_entry(h, e, hash);
+
+        if (h->from_pool)
+                deallocate_tile(&first_entry_tile, e);
+        else
+                free(e);
+}
+
+void hashmap_free(Hashmap*h) {
+
+        if (!h)
+                return;
+
+        hashmap_clear(h);
+
+        if (h->from_pool)
+                deallocate_tile(&first_hashmap_tile, h);
+        else
+                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)
+                remove_entry(h, h->iterate_list_head);
+}
+
+static struct hashmap_entry *hash_scan(Hashmap *h, unsigned 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 hash;
+
+        assert(h);
+
+        hash = h->hash_func(key) % NBUCKETS;
+
+        if ((e = hash_scan(h, hash, key))) {
+
+                if (e->value == value)
+                        return 0;
+
+                return -EEXIST;
+        }
+
+        if (h->from_pool)
+                e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry));
+        else
+                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 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 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 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 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 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 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 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) {
+        void *data;
+
+        if (!h)
+                return NULL;
+
+        if (!h->iterate_list_head)
+                return NULL;
+
+        data = h->iterate_list_head->value;
+        remove_entry(h, h->iterate_list_head);
+
+        return data;
+}
+
+void* hashmap_steal_first_key(Hashmap *h) {
+        void *key;
+
+        if (!h)
+                return NULL;
+
+        if (!h->iterate_list_head)
+                return NULL;
+
+        key = (void*) h->iterate_list_head->key;
+        remove_entry(h, h->iterate_list_head);
+
+        return key;
+}
+
+unsigned 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 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 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;
+}
+
+Hashmap *hashmap_copy(Hashmap *h) {
+        Hashmap *copy;
+
+        assert(h);
+
+        if (!(copy = hashmap_new(h->hash_func, h->compare_func)))
+                return NULL;
+
+        if (hashmap_merge(copy, h) < 0) {
+                hashmap_free(copy);
+                return NULL;
+        }
+
+        return copy;
+}
+
+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/install/hashmap.h b/install/hashmap.h
new file mode 100644 (file)
index 0000000..fcf2cb1
--- /dev/null
@@ -0,0 +1,91 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#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 (*hash_func_t)(const void *p);
+typedef int (*compare_func_t)(const void *a, const void *b);
+
+unsigned string_hash_func(const void *p);
+int string_compare_func(const void *a, const void *b);
+
+unsigned 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);
+Hashmap *hashmap_copy(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 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/install/hashmap.lo b/install/hashmap.lo
new file mode 100644 (file)
index 0000000..c7260f2
--- /dev/null
@@ -0,0 +1,12 @@
+# src/shared/hashmap.lo - a libtool object file
+# Generated by libtool (GNU libtool) 2.4.2
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object='.libs/hashmap.o'
+
+# Name of the non-PIC object
+non_pic_object='hashmap.o'
+
diff --git a/install/hashmap.o b/install/hashmap.o
new file mode 100644 (file)
index 0000000..70262be
Binary files /dev/null and b/install/hashmap.o differ
diff --git a/install/log.c b/install/log.c
new file mode 100644 (file)
index 0000000..127774f
--- /dev/null
@@ -0,0 +1,294 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  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,
+                int line,
+                const char *func,
+                const char *buffer) {
+
+        char location[64];
+        struct iovec iovec[5];
+        unsigned n = 0;
+
+        if (console_fd < 0)
+                return 0;
+
+        zero(iovec);
+
+        IOVEC_SET_STRING(iovec[n++], "dracut-install: ");
+
+        if (show_location) {
+                snprintf(location, sizeof(location), "(%s:%u) ", file, line);
+                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,
+        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,
+        int line,
+        const char *func,
+        const char *format,
+        va_list ap) {
+
+        char buffer[LINE_MAX];
+        int saved_errno, r;
+
+        if (_likely_(LOG_PRI(level) > log_max_level))
+                return 0;
+
+        saved_errno = errno;
+        vsnprintf(buffer, sizeof(buffer), format, ap);
+        char_array_0(buffer);
+
+        r = log_dispatch(level, file, line, func, buffer);
+        errno = saved_errno;
+
+        return r;
+}
+
+int log_meta(
+        int level,
+        const char*file,
+        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, int line, const char *func, const char *format) {
+        static char buffer[LINE_MAX];
+
+        snprintf(buffer, sizeof(buffer), format, text, file, line, func);
+
+        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, 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, 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_LOG_TARGET")))
+                if (log_set_target_from_string(e) < 0)
+                        log_warning("Failed to parse log target %s. Ignoring.", e);
+
+        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/install/log.h b/install/log.h
new file mode 100644 (file)
index 0000000..ad1ca33
--- /dev/null
@@ -0,0 +1,115 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#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,
+        int line,
+        const char *func,
+        const char *format, ...) _printf_attr_(5,6);
+
+int log_metav(
+        int level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *format,
+        va_list ap);
+
+_noreturn_ void log_assert_failed(const char *text, const char *file, int line, const char *func);
+_noreturn_ void log_assert_failed_unreachable(const char *text, const char *file, 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/install/macro.h b/install/macro.h
new file mode 100644 (file)
index 0000000..1c0aa91
--- /dev/null
@@ -0,0 +1,192 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foomacrohfoo
+#define foomacrohfoo
+
+/***
+  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 XSTRINGIFY(x) #x
+#define STRINGIFY(x) XSTRINGIFY(x)
+
+/* Rounds up */
+#define ALIGN(l) ALIGN_TO((l), sizeof(void*))
+static inline size_t ALIGN_TO(size_t l, size_t ali) {
+        return ((l + ali - 1) & ~(ali - 1));
+}
+
+#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) ({ \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#ifndef MAX
+#define MAX(a,b)                                \
+        __extension__ ({                        \
+                        typeof(a) _a = (a);     \
+                        typeof(b) _b = (b);     \
+                        _a > _b ? _a : _b;      \
+                })
+#endif
+
+#define MAX3(a,b,c)                             \
+        MAX(MAX(a,b),c)
+
+#ifndef MIN
+#define MIN(a,b)                                \
+        __extension__ ({                        \
+                        typeof(a) _a = (a);     \
+                        typeof(b) _b = (b);     \
+                        _a < _b ? _a : _b;      \
+                })
+#endif
+
+#define MIN3(a,b,c)                             \
+        MIN(MIN(a,b),c)
+
+#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)); \
+                })
+
+#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)
+
+#define assert_cc(expr)                            \
+        do {                                       \
+                switch (0) {                       \
+                        case 0:                    \
+                        case !!(expr):             \
+                                ;                  \
+                }                                  \
+        } while (false)
+
+#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_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 n) {
+        unsigned 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 n, size_t k) {
+        unsigned 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;
+}
+
+#include "log.h"
+
+#endif
diff --git a/install/util.c b/install/util.c
new file mode 100644 (file)
index 0000000..0247184
--- /dev/null
@@ -0,0 +1,187 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  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 <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include "util.h"
+
+static inline pid_t gettid(void) {
+        return (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 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 *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) l != l)
+                return -ERANGE;
+
+        *ret_u = (unsigned) 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);
diff --git a/install/util.h b/install/util.h
new file mode 100644 (file)
index 0000000..9085935
--- /dev/null
@@ -0,0 +1,527 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#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/>.
+***/
+
+#include <inttypes.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.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];
+}
+
+bool endswith(const char *s, const char *postfix);
+bool startswith(const char *s, const char *prefix);
+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 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 *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 __WORDSIZE == 32
+static inline int safe_atolu(const char *s, unsigned long *ret_u) {
+        assert_cc(sizeof(unsigned long) == sizeof(unsigned));
+        return safe_atou(s, (unsigned*) 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));
+        return safe_atou(s, (unsigned*) 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 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 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 *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 columns(void);
+
+int fd_lines(int fd);
+unsigned lines(void);
+
+int running_in_chroot(void);
+
+char *ellipsize(const char *s, size_t length, unsigned percent);
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned 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);
+
+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 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);
+
+#endif