[systemd-commits] 2 commits - Makefile.am src/conf-parser.c src/conf-parser.h src/execute.c src/exit-status.c src/exit-status.h src/hashmap.c src/hashmap.h src/journal src/label.c src/label.h src/log.c src/log.h src/login src/nspawn.c src/ratelimit.c src/ratelimit.h src/set.c src/set.h src/shared src/socket-util.c src/socket-util.h src/strv.c src/strv.h src/udev src/utf8.c src/utf8.h src/util.c src/util.h src/virt.c src/virt.h

Kay Sievers kay at kemper.freedesktop.org
Tue Apr 10 05:15:15 PDT 2012


 Makefile.am                |  115 
 src/conf-parser.c          |  852 ------
 src/conf-parser.h          |  135 
 src/execute.c              |    1 
 src/exit-status.c          |  180 -
 src/exit-status.h          |   85 
 src/hashmap.c              |  731 -----
 src/hashmap.h              |   91 
 src/journal/journald.h     |    1 
 src/label.c                |  413 --
 src/label.h                |   51 
 src/log.c                  |  747 -----
 src/log.h                  |  111 
 src/login/logind.h         |    1 
 src/login/pam-module.c     |    1 
 src/nspawn.c               |    1 
 src/ratelimit.c            |   57 
 src/ratelimit.h            |   53 
 src/set.c                  |  118 
 src/set.h                  |   69 
 src/shared/audit.c         |  118 
 src/shared/audit.h         |   29 
 src/shared/capability.c    |   86 
 src/shared/capability.h    |   27 
 src/shared/conf-parser.c   |  852 ++++++
 src/shared/conf-parser.h   |  135 
 src/shared/exit-status.c   |  180 +
 src/shared/exit-status.h   |   85 
 src/shared/hashmap.c       |  731 +++++
 src/shared/hashmap.h       |   91 
 src/shared/label.c         |  413 ++
 src/shared/label.h         |   51 
 src/shared/log.c           |  747 +++++
 src/shared/log.h           |  111 
 src/shared/ratelimit.c     |   57 
 src/shared/ratelimit.h     |   53 
 src/shared/set.c           |  118 
 src/shared/set.h           |   69 
 src/shared/socket-util.c   |  652 ++++
 src/shared/socket-util.h   |  102 
 src/shared/strv.c          |  690 ++++
 src/shared/strv.h          |   79 
 src/shared/utf8.c          |  214 +
 src/shared/utf8.h          |   33 
 src/shared/util.c          | 6123 ++++++++++++++++++++++++++++++++++++++++++++
 src/shared/util.h          |  536 +++
 src/shared/virt.c          |  292 ++
 src/shared/virt.h          |   38 
 src/socket-util.c          |  652 ----
 src/socket-util.h          |  102 
 src/strv.c                 |  690 ----
 src/strv.h                 |   79 
 src/udev/libudev-monitor.c |    1 
 src/utf8.c                 |  214 -
 src/utf8.h                 |   33 
 src/util.c                 | 6256 ---------------------------------------------
 src/util.h                 |  544 ---
 src/virt.c                 |  292 --
 src/virt.h                 |   38 
 59 files changed, 12803 insertions(+), 12623 deletions(-)

New commits:
commit d7832d2c6e0ef5f2839a2296c1cc2fc85c7d9632
Author: Kay Sievers <kay at vrfy.org>
Date:   Tue Apr 10 13:39:02 2012 +0200

    util: move all to shared/ and split external dependencies in separate internal libraries
    
    Before:
      $ ldd /lib/systemd/systemd-timestamp
      linux-vdso.so.1 =>  (0x00007fffb05ff000)
      libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f90aac57000)
      libcap.so.2 => /lib64/libcap.so.2 (0x00007f90aaa53000)
      librt.so.1 => /lib64/librt.so.1 (0x00007f90aa84a000)
      libc.so.6 => /lib64/libc.so.6 (0x00007f90aa494000)
      /lib64/ld-linux-x86-64.so.2 (0x00007f90aae90000)
      libdl.so.2 => /lib64/libdl.so.2 (0x00007f90aa290000)
      libattr.so.1 => /lib64/libattr.so.1 (0x00007f90aa08a000)
      libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f90a9e6e000)
    
    After:
      $ ldd systemd-timestamp
      linux-vdso.so.1 =>  (0x00007fff3cbff000)
      libselinux.so.1 => /lib64/libselinux.so.1 (0x00007f5eaa1c3000)
      librt.so.1 => /lib64/librt.so.1 (0x00007f5ea9fbb000)
      libc.so.6 => /lib64/libc.so.6 (0x00007f5ea9c04000)
      /lib64/ld-linux-x86-64.so.2 (0x00007f5eaa3fc000)
      libdl.so.2 => /lib64/libdl.so.2 (0x00007f5ea9a00000)
      libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f5ea97e4000)

diff --git a/Makefile.am b/Makefile.am
index 8f56d25..357002b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -120,6 +120,7 @@ AM_CPPFLAGS = \
 	-DX_SERVER=\"$(bindir)/X\" \
 	-DUDEVLIBEXECDIR=\""$(libexecdir)/udev"\" \
 	-I $(top_srcdir)/src \
+	-I $(top_srcdir)/src/shared \
 	-I $(top_srcdir)/src/readahead \
 	-I $(top_srcdir)/src/login \
 	-I $(top_srcdir)/src/journal \
@@ -129,6 +130,7 @@ AM_CPPFLAGS = \
 AM_CFLAGS = $(WARNINGFLAGS)
 AM_LDFLAGS = $(GCLDFLAGS)
 
+# ------------------------------------------------------------------------------
 if TARGET_GENTOO
 AM_CPPFLAGS += \
 	-DKBD_LOADKEYS=\"/usr/bin/loadkeys\" \
@@ -183,6 +185,7 @@ endif
 endif
 endif
 
+# ------------------------------------------------------------------------------
 rootbin_PROGRAMS = \
 	systemctl \
 	systemd-notify \
@@ -472,42 +475,66 @@ EXTRA_DIST += \
 
 noinst_LTLIBRARIES = \
 	libsystemd-basic.la \
+	libsystemd-capability.la \
+	libsystemd-audit.la \
 	libsystemd-core.la
 
+# ------------------------------------------------------------------------------
 libsystemd_basic_la_SOURCES = \
-	src/util.c \
-	src/util.h \
-	src/virt.c \
-	src/virt.h \
-	src/label.c \
-	src/label.h \
-	src/hashmap.c \
-	src/hashmap.h \
-	src/set.c \
-	src/set.h \
-	src/strv.c \
-	src/strv.h \
-	src/conf-parser.c \
-	src/conf-parser.h \
-	src/socket-util.c \
-	src/socket-util.h \
-	src/log.c \
-	src/log.h \
-	src/ratelimit.h \
-	src/ratelimit.c \
-	src/exit-status.c \
-	src/exit-status.h \
-	src/utf8.c \
-	src/utf8.h
+	src/shared/util.c \
+	src/shared/util.h \
+	src/shared/virt.c \
+	src/shared/virt.h \
+	src/shared/label.c \
+	src/shared/label.h \
+	src/shared/hashmap.c \
+	src/shared/hashmap.h \
+	src/shared/set.c \
+	src/shared/set.h \
+	src/shared/strv.c \
+	src/shared/strv.h \
+	src/shared/conf-parser.c \
+	src/shared/conf-parser.h \
+	src/shared/socket-util.c \
+	src/shared/socket-util.h \
+	src/shared/log.c \
+	src/shared/log.h \
+	src/shared/ratelimit.h \
+	src/shared/ratelimit.c \
+	src/shared/exit-status.c \
+	src/shared/exit-status.h \
+	src/shared/utf8.c \
+	src/shared/utf8.h
 
 libsystemd_basic_la_CFLAGS = \
 	$(AM_CFLAGS) \
 	$(SELINUX_CFLAGS)
 
 libsystemd_basic_la_LIBADD = \
-	$(SELINUX_LIBS) \
+	libsystemd-capability.la \
+	$(SELINUX_LIBS)
+
+# ------------------------------------------------------------------------------
+libsystemd_capability_la_SOURCES = \
+	src/shared/capability.c \
+	src/shared/capability.h
+
+libsystemd_capability_la_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(CAP_CFLAGS)
+
+libsystemd_capability_la_LIBADD = \
 	$(CAP_LIBS)
 
+# ------------------------------------------------------------------------------
+libsystemd_audit_la_SOURCES = \
+	src/shared/audit.c \
+	src/shared/audit.h
+
+libsystemd_audit_la_LIBADD = \
+	libsystemd-capability.la
+
+# ------------------------------------------------------------------------------
 libsystemd_core_la_SOURCES = \
 	src/unit.c \
 	src/unit.h \
@@ -613,8 +640,8 @@ libsystemd_core_la_SOURCES = \
 	src/tcpwrap.h \
 	src/cgroup-attr.c \
 	src/cgroup-attr.h \
-        src/watchdog.c \
-        src/watchdog.h \
+	src/watchdog.c \
+	src/watchdog.h \
 	src/sd-daemon.c \
 	src/sd-id128.c \
 	src/macro.h \
@@ -667,6 +694,7 @@ libsystemd_core_la_LIBADD = \
 	$(CAP_LIBS) \
 	$(KMOD_LIBS)
 
+# ------------------------------------------------------------------------------
 MANPAGES = \
 	man/systemd.1 \
 	man/systemctl.1 \
@@ -741,6 +769,7 @@ EXTRA_DIST += \
 	$(MANPAGES) \
 	$(MANPAGES_ALIAS)
 
+# ------------------------------------------------------------------------------
 systemd_SOURCES = \
 	src/main.c
 
@@ -751,6 +780,7 @@ systemd_CFLAGS = \
 systemd_LDADD = \
 	libsystemd-core.la
 
+# ------------------------------------------------------------------------------
 test_engine_SOURCES = \
 	src/test-engine.c
 
@@ -831,6 +861,7 @@ test_watchdog_SOURCES = \
 test_watchdog_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_initctl_SOURCES = \
 	src/initctl.c \
 	src/dbus-common.c
@@ -844,6 +875,7 @@ systemd_initctl_LDADD = \
 	libsystemd-daemon.la \
 	$(DBUS_LIBS)
 
+# ------------------------------------------------------------------------------
 systemd_update_utmp_SOURCES = \
 	src/update-utmp.c \
 	src/dbus-common.c \
@@ -859,6 +891,7 @@ systemd_update_utmp_LDADD = \
 	$(DBUS_LIBS) \
 	$(AUDIT_LIBS)
 
+# ------------------------------------------------------------------------------
 systemd_shutdownd_SOURCES = \
 	src/utmp-wtmp.c \
 	src/shutdownd.c
@@ -881,6 +914,7 @@ systemd_shutdown_LDADD = \
 	libsystemd-basic.la \
 	libudev.la
 
+# ------------------------------------------------------------------------------
 systemd_modules_load_SOURCES = \
 	src/modules-load.c
 
@@ -891,12 +925,14 @@ systemd_modules_load_LDADD = \
 	libsystemd-basic.la \
 	$(KMOD_LIBS)
 
+# ------------------------------------------------------------------------------
 systemd_tmpfiles_SOURCES = \
 	src/tmpfiles.c
 
 systemd_tmpfiles_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_machine_id_setup_SOURCES = \
 	src/machine-id-setup.c \
 	src/machine-id-main.c \
@@ -905,12 +941,14 @@ systemd_machine_id_setup_SOURCES = \
 systemd_machine_id_setup_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_sysctl_SOURCES = \
 	src/sysctl.c
 
 systemd_sysctl_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_fsck_SOURCES = \
 	src/fsck.c \
 	src/dbus-common.c
@@ -924,12 +962,14 @@ systemd_fsck_LDADD = \
 	libudev.la \
 	$(DBUS_LIBS)
 
+# ------------------------------------------------------------------------------
 systemd_timestamp_SOURCES = \
 	src/timestamp.c
 
 systemd_timestamp_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_ac_power_SOURCES = \
 	src/ac-power.c
 
@@ -937,12 +977,14 @@ systemd_ac_power_LDADD = \
 	libsystemd-basic.la \
 	libudev.la
 
+# ------------------------------------------------------------------------------
 systemd_detect_virt_SOURCES = \
 	src/detect-virt.c
 
 systemd_detect_virt_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_getty_generator_SOURCES = \
 	src/getty-generator.c \
 	src/unit-name.c
@@ -950,20 +992,22 @@ systemd_getty_generator_SOURCES = \
 systemd_getty_generator_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_rc_local_generator_SOURCES = \
 	src/rc-local-generator.c
 
 systemd_rc_local_generator_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_remount_api_vfs_SOURCES = \
 	src/remount-api-vfs.c \
-	src/mount-setup.c \
-	src/exit-status.c
+	src/mount-setup.c
 
 systemd_remount_api_vfs_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_cgroups_agent_SOURCES = \
 	src/cgroups-agent.c \
 	src/dbus-common.c
@@ -976,6 +1020,7 @@ systemd_cgroups_agent_LDADD = \
 	libsystemd-basic.la \
 	$(DBUS_LIBS)
 
+# ------------------------------------------------------------------------------
 systemctl_SOURCES = \
 	src/systemctl.c \
 	src/utmp-wtmp.c \
@@ -983,7 +1028,6 @@ systemctl_SOURCES = \
 	src/path-lookup.c \
 	src/cgroup-show.c \
 	src/cgroup-util.c \
-	src/exit-status.c \
 	src/unit-name.c \
 	src/pager.c \
 	src/install.c \
@@ -1001,6 +1045,7 @@ systemctl_LDADD = \
 	libsystemd-id128.la \
 	$(DBUS_LIBS)
 
+# ------------------------------------------------------------------------------
 systemd_notify_SOURCES = \
 	src/notify.c \
 	src/readahead/sd-readahead.c
@@ -1009,6 +1054,7 @@ systemd_notify_LDADD = \
 	libsystemd-basic.la \
 	libsystemd-daemon.la
 
+# ------------------------------------------------------------------------------
 systemd_ask_password_SOURCES = \
 	src/ask-password.c \
 	src/ask-password-api.c
@@ -1016,12 +1062,14 @@ systemd_ask_password_SOURCES = \
 systemd_ask_password_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_reply_password_SOURCES = \
 	src/reply-password.c
 
 systemd_reply_password_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_cgls_SOURCES = \
 	src/cgls.c \
 	src/cgroup-show.c \
@@ -1031,6 +1079,7 @@ systemd_cgls_SOURCES = \
 systemd_cgls_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_cgtop_SOURCES = \
 	src/cgtop.c \
         src/cgroup-util.c
@@ -1038,6 +1087,7 @@ systemd_cgtop_SOURCES = \
 systemd_cgtop_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_nspawn_SOURCES = \
 	src/nspawn.c \
 	src/cgroup-util.c \
@@ -1045,14 +1095,17 @@ systemd_nspawn_SOURCES = \
 
 systemd_nspawn_LDADD = \
 	libsystemd-basic.la \
+	libsystemd-capability.la \
 	libsystemd-daemon.la
 
+# ------------------------------------------------------------------------------
 systemd_stdio_bridge_SOURCES = \
 	src/bridge.c
 
 systemd_stdio_bridge_LDADD = \
 	libsystemd-basic.la
 
+# ------------------------------------------------------------------------------
 systemd_tty_ask_password_agent_SOURCES = \
 	src/tty-ask-password-agent.c \
 	src/ask-password-api.c \
@@ -1844,6 +1897,7 @@ systemd_journald_CFLAGS = \
 
 systemd_journald_LDADD = \
 	libsystemd-basic.la \
+	libsystemd-audit.la \
 	libsystemd-daemon.la \
 	libsystemd-login.la \
 	$(ACL_LIBS)
@@ -2452,6 +2506,7 @@ systemd_logind_CFLAGS = \
 
 systemd_logind_LDADD = \
 	libsystemd-basic.la \
+	libsystemd-audit.la \
 	libsystemd-daemon.la \
 	libudev.la \
 	$(DBUS_LIBS) \
diff --git a/src/conf-parser.c b/src/conf-parser.c
deleted file mode 100644
index a9b0113..0000000
--- a/src/conf-parser.c
+++ /dev/null
@@ -1,852 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <assert.h>
-#include <stdlib.h>
-
-#include "conf-parser.h"
-#include "util.h"
-#include "macro.h"
-#include "strv.h"
-#include "log.h"
-#include "utf8.h"
-
-int config_item_table_lookup(
-                void *table,
-                const char *section,
-                const char *lvalue,
-                ConfigParserCallback *func,
-                int *ltype,
-                void **data,
-                void *userdata) {
-
-        ConfigTableItem *t;
-
-        assert(table);
-        assert(lvalue);
-        assert(func);
-        assert(ltype);
-        assert(data);
-
-        for (t = table; t->lvalue; t++) {
-
-                if (!streq(lvalue, t->lvalue))
-                        continue;
-
-                if (!streq_ptr(section, t->section))
-                        continue;
-
-                *func = t->parse;
-                *ltype = t->ltype;
-                *data = t->data;
-                return 1;
-        }
-
-        return 0;
-}
-
-int config_item_perf_lookup(
-                void *table,
-                const char *section,
-                const char *lvalue,
-                ConfigParserCallback *func,
-                int *ltype,
-                void **data,
-                void *userdata) {
-
-        ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
-        const ConfigPerfItem *p;
-
-        assert(table);
-        assert(lvalue);
-        assert(func);
-        assert(ltype);
-        assert(data);
-
-        if (!section)
-                p = lookup(lvalue, strlen(lvalue));
-        else {
-                char *key;
-
-                key = join(section, ".", lvalue, NULL);
-                if (!key)
-                        return -ENOMEM;
-
-                p = lookup(key, strlen(key));
-                free(key);
-        }
-
-        if (!p)
-                return 0;
-
-        *func = p->parse;
-        *ltype = p->ltype;
-        *data = (uint8_t*) userdata + p->offset;
-        return 1;
-}
-
-/* Run the user supplied parser for an assignment */
-static int next_assignment(
-                const char *filename,
-                unsigned line,
-                ConfigItemLookup lookup,
-                void *table,
-                const char *section,
-                const char *lvalue,
-                const char *rvalue,
-                bool relaxed,
-                void *userdata) {
-
-        ConfigParserCallback func = NULL;
-        int ltype = 0;
-        void *data = NULL;
-        int r;
-
-        assert(filename);
-        assert(line > 0);
-        assert(lookup);
-        assert(lvalue);
-        assert(rvalue);
-
-        r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
-        if (r < 0)
-                return r;
-
-        if (r > 0) {
-                if (func)
-                        return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
-
-                return 0;
-        }
-
-        /* Warn about unknown non-extension fields. */
-        if (!relaxed && !startswith(lvalue, "X-"))
-                log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, section);
-
-        return 0;
-}
-
-/* Parse a variable assignment line */
-static int parse_line(
-                const char *filename,
-                unsigned line,
-                const char *sections,
-                ConfigItemLookup lookup,
-                void *table,
-                bool relaxed,
-                char **section,
-                char *l,
-                void *userdata) {
-
-        char *e;
-
-        assert(filename);
-        assert(line > 0);
-        assert(lookup);
-        assert(l);
-
-        l = strstrip(l);
-
-        if (!*l)
-                return 0;
-
-        if (strchr(COMMENTS, *l))
-                return 0;
-
-        if (startswith(l, ".include ")) {
-                char *fn;
-                int r;
-
-                fn = file_in_same_dir(filename, strstrip(l+9));
-                if (!fn)
-                        return -ENOMEM;
-
-                r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata);
-                free(fn);
-
-                return r;
-        }
-
-        if (*l == '[') {
-                size_t k;
-                char *n;
-
-                k = strlen(l);
-                assert(k > 0);
-
-                if (l[k-1] != ']') {
-                        log_error("[%s:%u] Invalid section header.", filename, line);
-                        return -EBADMSG;
-                }
-
-                n = strndup(l+1, k-2);
-                if (!n)
-                        return -ENOMEM;
-
-                if (sections && !nulstr_contains(sections, n)) {
-
-                        if (!relaxed)
-                                log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
-
-                        free(n);
-                        *section = NULL;
-                } else {
-                        free(*section);
-                        *section = n;
-                }
-
-                return 0;
-        }
-
-        if (sections && !*section) {
-
-                if (!relaxed)
-                        log_info("[%s:%u] Assignment outside of section. Ignoring.", filename, line);
-
-                return 0;
-        }
-
-        e = strchr(l, '=');
-        if (!e) {
-                log_error("[%s:%u] Missing '='.", filename, line);
-                return -EBADMSG;
-        }
-
-        *e = 0;
-        e++;
-
-        return next_assignment(
-                        filename,
-                        line,
-                        lookup,
-                        table,
-                        *section,
-                        strstrip(l),
-                        strstrip(e),
-                        relaxed,
-                        userdata);
-}
-
-/* Go through the file and parse each line */
-int config_parse(
-                const char *filename,
-                FILE *f,
-                const char *sections,
-                ConfigItemLookup lookup,
-                void *table,
-                bool relaxed,
-                void *userdata) {
-
-        unsigned line = 0;
-        char *section = NULL;
-        int r;
-        bool ours = false;
-        char *continuation = NULL;
-
-        assert(filename);
-        assert(lookup);
-
-        if (!f) {
-                f = fopen(filename, "re");
-                if (!f) {
-                        r = -errno;
-                        log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
-                        goto finish;
-                }
-
-                ours = true;
-        }
-
-        while (!feof(f)) {
-                char l[LINE_MAX], *p, *c = NULL, *e;
-                bool escaped = false;
-
-                if (!fgets(l, sizeof(l), f)) {
-                        if (feof(f))
-                                break;
-
-                        r = -errno;
-                        log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
-                        goto finish;
-                }
-
-                truncate_nl(l);
-
-                if (continuation) {
-                        c = strappend(continuation, l);
-                        if (!c) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
-
-                        free(continuation);
-                        continuation = NULL;
-                        p = c;
-                } else
-                        p = l;
-
-                for (e = p; *e; e++) {
-                        if (escaped)
-                                escaped = false;
-                        else if (*e == '\\')
-                                escaped = true;
-                }
-
-                if (escaped) {
-                        *(e-1) = ' ';
-
-                        if (c)
-                                continuation = c;
-                        else {
-                                continuation = strdup(l);
-                                if (!continuation) {
-                                        r = -ENOMEM;
-                                        goto finish;
-                                }
-                        }
-
-                        continue;
-                }
-
-                r = parse_line(filename,
-                                ++line,
-                                sections,
-                                lookup,
-                                table,
-                                relaxed,
-                                &section,
-                                p,
-                                userdata);
-                free(c);
-
-                if (r < 0)
-                        goto finish;
-        }
-
-        r = 0;
-
-finish:
-        free(section);
-        free(continuation);
-
-        if (f && ours)
-                fclose(f);
-
-        return r;
-}
-
-int config_parse_int(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        int *i = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        if ((r = safe_atoi(rvalue, i)) < 0) {
-                log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        return 0;
-}
-
-int config_parse_long(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        long *i = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        if ((r = safe_atoli(rvalue, i)) < 0) {
-                log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        return 0;
-}
-
-int config_parse_uint64(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        uint64_t *u = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        if ((r = safe_atou64(rvalue, u)) < 0) {
-                log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        return 0;
-}
-
-int config_parse_unsigned(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        unsigned *u = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        if ((r = safe_atou(rvalue, u)) < 0) {
-                log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
-                return r;
-        }
-
-        return 0;
-}
-
-int config_parse_bytes_size(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        size_t *sz = data;
-        off_t o;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
-                log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        *sz = (size_t) o;
-        return 0;
-}
-
-
-int config_parse_bytes_off(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        off_t *bytes = data;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        assert_cc(sizeof(off_t) == sizeof(uint64_t));
-
-        if (parse_bytes(rvalue, bytes) < 0) {
-                log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        return 0;
-}
-
-int config_parse_bool(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        int k;
-        bool *b = data;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        if ((k = parse_boolean(rvalue)) < 0) {
-                log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        *b = !!k;
-        return 0;
-}
-
-int config_parse_tristate(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        int k;
-        int *b = data;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
-
-        k = parse_boolean(rvalue);
-        if (k < 0) {
-                log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        *b = !!k;
-        return 0;
-}
-
-int config_parse_string(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        char **s = data;
-        char *n;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        n = cunescape(rvalue);
-        if (!n)
-                return -ENOMEM;
-
-        if (!utf8_is_valid(n)) {
-                log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
-                free(n);
-                return 0;
-        }
-
-        free(*s);
-        if (*n)
-                *s = n;
-        else {
-                free(n);
-                *s = NULL;
-        }
-
-        return 0;
-}
-
-int config_parse_path(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        char **s = data;
-        char *n;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        if (!utf8_is_valid(rvalue)) {
-                log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        if (!path_is_absolute(rvalue)) {
-                log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        n = strdup(rvalue);
-        if (!n)
-                return -ENOMEM;
-
-        path_kill_slashes(n);
-
-        free(*s);
-        *s = n;
-
-        return 0;
-}
-
-int config_parse_strv(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        char*** sv = data;
-        char **n;
-        char *w;
-        unsigned k;
-        size_t l;
-        char *state;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        k = strv_length(*sv);
-        FOREACH_WORD_QUOTED(w, l, rvalue, state)
-                k++;
-
-        n = new(char*, k+1);
-        if (!n)
-                return -ENOMEM;
-
-        if (*sv)
-                for (k = 0; (*sv)[k]; k++)
-                        n[k] = (*sv)[k];
-        else
-                k = 0;
-
-        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
-                n[k] = cunescape_length(w, l);
-                if (!n[k]) {
-                        r = -ENOMEM;
-                        goto fail;
-                }
-
-                if (!utf8_is_valid(n[k])) {
-                        log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
-                        free(n[k]);
-                        continue;
-                }
-
-                k++;
-        }
-
-        n[k] = NULL;
-        free(*sv);
-        *sv = n;
-
-        return 0;
-
-fail:
-        for (; k > 0; k--)
-                free(n[k-1]);
-        free(n);
-
-        return r;
-}
-
-int config_parse_path_strv(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        char*** sv = data;
-        char **n;
-        char *w;
-        unsigned k;
-        size_t l;
-        char *state;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        k = strv_length(*sv);
-        FOREACH_WORD_QUOTED(w, l, rvalue, state)
-                k++;
-
-        n = new(char*, k+1);
-        if (!n)
-                return -ENOMEM;
-
-        k = 0;
-        if (*sv)
-                for (; (*sv)[k]; k++)
-                        n[k] = (*sv)[k];
-
-        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
-                n[k] = strndup(w, l);
-                if (!n[k]) {
-                        r = -ENOMEM;
-                        goto fail;
-                }
-
-                if (!utf8_is_valid(n[k])) {
-                        log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
-                        free(n[k]);
-                        continue;
-                }
-
-                if (!path_is_absolute(n[k])) {
-                        log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
-                        free(n[k]);
-                        continue;
-                }
-
-                path_kill_slashes(n[k]);
-                k++;
-        }
-
-        n[k] = NULL;
-        free(*sv);
-        *sv = n;
-
-        return 0;
-
-fail:
-        for (; k > 0; k--)
-                free(n[k-1]);
-        free(n);
-
-        return r;
-}
-
-int config_parse_usec(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        usec_t *usec = data;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        if (parse_usec(rvalue, usec) < 0) {
-                log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        return 0;
-}
-
-int config_parse_mode(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        mode_t *m = data;
-        long l;
-        char *x = NULL;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        errno = 0;
-        l = strtol(rvalue, &x, 8);
-        if (!x || *x || errno) {
-                log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        if (l < 0000 || l > 07777) {
-                log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
-                return 0;
-        }
-
-        *m = (mode_t) l;
-        return 0;
-}
diff --git a/src/conf-parser.h b/src/conf-parser.h
deleted file mode 100644
index be7d708..0000000
--- a/src/conf-parser.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooconfparserhfoo
-#define fooconfparserhfoo
-
-/***
-  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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdio.h>
-#include <stdbool.h>
-
-/* An abstract parser for simple, line based, shallow configuration
- * files consisting of variable assignments only. */
-
-/* Prototype for a parser for a specific configuration setting */
-typedef int (*ConfigParserCallback)(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                int ltype,
-                const char *rvalue,
-                void *data,
-                void *userdata);
-
-/* Wraps information for parsing a specific configuration variable, to
- * be stored in a simple array */
-typedef struct ConfigTableItem {
-        const char *section;            /* Section */
-        const char *lvalue;             /* Name of the variable */
-        ConfigParserCallback parse;     /* Function that is called to parse the variable's value */
-        int ltype;                      /* Distinguish different variables passed to the same callback */
-        void *data;                     /* Where to store the variable's data */
-} ConfigTableItem;
-
-/* Wraps information for parsing a specific configuration variable, to
- * ve srored in a gperf perfect hashtable */
-typedef struct ConfigPerfItem {
-        const char *section_and_lvalue; /* Section + "." + name of the variable */
-        ConfigParserCallback parse;     /* Function that is called to parse the variable's value */
-        int ltype;                      /* Distinguish different variables passed to the same callback */
-        size_t offset;                  /* Offset where to store data, from the beginning of userdata */
-} ConfigPerfItem;
-
-/* Prototype for a low-level gperf lookup function */
-typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length);
-
-/* Prototype for a generic high-level lookup function */
-typedef int (*ConfigItemLookup)(
-                void *table,
-                const char *section,
-                const char *lvalue,
-                ConfigParserCallback *func,
-                int *ltype,
-                void **data,
-                void *userdata);
-
-/* Linear table search implementation of ConfigItemLookup, based on
- * ConfigTableItem arrays */
-int config_item_table_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
-
-/* gperf implementation of ConfigItemLookup, based on gperf
- * ConfigPerfItem tables */
-int config_item_perf_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
-
-int config_parse(
-                const char *filename,
-                FILE *f,
-                const char *sections,  /* nulstr */
-                ConfigItemLookup lookup,
-                void *table,
-                bool relaxed,
-                void *userdata);
-
-/* Generic parsers */
-int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_long(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_uint64(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bytes_size(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bytes_off(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_tristate(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_path_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_usec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-
-#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg)                \
-        int function(                                                   \
-                        const char *filename,                           \
-                        unsigned line,                                  \
-                        const char *section,                            \
-                        const char *lvalue,                             \
-                        int ltype,                                      \
-                        const char *rvalue,                             \
-                        void *data,                                     \
-                        void *userdata) {                               \
-                                                                        \
-                type *i = data, x;                                      \
-                                                                        \
-                assert(filename);                                       \
-                assert(lvalue);                                         \
-                assert(rvalue);                                         \
-                assert(data);                                           \
-                                                                        \
-                if ((x = name##_from_string(rvalue)) < 0) {             \
-                        log_error("[%s:%u] " msg ", ignoring: %s", filename, line, rvalue); \
-                        return 0;                                       \
-                }                                                       \
-                                                                        \
-                *i = x;                                                 \
-                                                                        \
-                return 0;                                               \
-        }
-
-#endif
diff --git a/src/execute.c b/src/execute.c
index dab4856..179ca7e 100644
--- a/src/execute.c
+++ b/src/execute.c
@@ -45,6 +45,7 @@
 #include "execute.h"
 #include "strv.h"
 #include "macro.h"
+#include "capability.h"
 #include "util.h"
 #include "log.h"
 #include "ioprio.h"
diff --git a/src/exit-status.c b/src/exit-status.c
deleted file mode 100644
index ab8907d..0000000
--- a/src/exit-status.c
+++ /dev/null
@@ -1,180 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdlib.h>
-#include <sys/wait.h>
-
-#include "exit-status.h"
-
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
-
-        /* We cast to int here, so that -Wenum doesn't complain that
-         * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
-
-        switch ((int) status) {
-
-        case EXIT_SUCCESS:
-                return "SUCCESS";
-
-        case EXIT_FAILURE:
-                return "FAILURE";
-        }
-
-
-        if (level == EXIT_STATUS_SYSTEMD || level == EXIT_STATUS_LSB) {
-                switch ((int) status) {
-
-                case EXIT_CHDIR:
-                        return "CHDIR";
-
-                case EXIT_NICE:
-                        return "NICE";
-
-                case EXIT_FDS:
-                        return "FDS";
-
-                case EXIT_EXEC:
-                        return "EXEC";
-
-                case EXIT_MEMORY:
-                        return "MEMORY";
-
-                case EXIT_LIMITS:
-                        return "LIMITS";
-
-                case EXIT_OOM_ADJUST:
-                        return "OOM_ADJUST";
-
-                case EXIT_SIGNAL_MASK:
-                        return "SIGNAL_MASK";
-
-                case EXIT_STDIN:
-                        return "STDIN";
-
-                case EXIT_STDOUT:
-                        return "STDOUT";
-
-                case EXIT_CHROOT:
-                        return "CHROOT";
-
-                case EXIT_IOPRIO:
-                        return "IOPRIO";
-
-                case EXIT_TIMERSLACK:
-                        return "TIMERSLACK";
-
-                case EXIT_SECUREBITS:
-                        return "SECUREBITS";
-
-                case EXIT_SETSCHEDULER:
-                        return "SETSCHEDULER";
-
-                case EXIT_CPUAFFINITY:
-                        return "CPUAFFINITY";
-
-                case EXIT_GROUP:
-                        return "GROUP";
-
-                case EXIT_USER:
-                        return "USER";
-
-                case EXIT_CAPABILITIES:
-                        return "CAPABILITIES";
-
-                case EXIT_CGROUP:
-                        return "CGROUP";
-
-                case EXIT_SETSID:
-                        return "SETSID";
-
-                case EXIT_CONFIRM:
-                        return "CONFIRM";
-
-                case EXIT_STDERR:
-                        return "STDERR";
-
-                case EXIT_TCPWRAP:
-                        return "TCPWRAP";
-
-                case EXIT_PAM:
-                        return "PAM";
-
-                case EXIT_NETWORK:
-                        return "NETWORK";
-
-                case EXIT_NAMESPACE:
-                        return "NAMESPACE";
-                }
-        }
-
-        if (level == EXIT_STATUS_LSB) {
-                switch ((int) status) {
-
-                case EXIT_INVALIDARGUMENT:
-                        return "INVALIDARGUMENT";
-
-                case EXIT_NOTIMPLEMENTED:
-                        return "NOTIMPLEMENTED";
-
-                case EXIT_NOPERMISSION:
-                        return "NOPERMISSION";
-
-                case EXIT_NOTINSTALLED:
-                        return "NOTINSSTALLED";
-
-                case EXIT_NOTCONFIGURED:
-                        return "NOTCONFIGURED";
-
-                case EXIT_NOTRUNNING:
-                        return "NOTRUNNING";
-                }
-        }
-
-        return NULL;
-}
-
-
-bool is_clean_exit(int code, int status) {
-
-        if (code == CLD_EXITED)
-                return status == 0;
-
-        /* If a daemon does not implement handlers for some of the
-         * signals that's not considered an unclean shutdown */
-        if (code == CLD_KILLED)
-                return
-                        status == SIGHUP ||
-                        status == SIGINT ||
-                        status == SIGTERM ||
-                        status == SIGPIPE;
-
-        return false;
-}
-
-bool is_clean_exit_lsb(int code, int status) {
-
-        if (is_clean_exit(code, status))
-                return true;
-
-        return
-                code == CLD_EXITED &&
-                (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
-}
diff --git a/src/exit-status.h b/src/exit-status.h
deleted file mode 100644
index 44ef879..0000000
--- a/src/exit-status.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooexitstatushfoo
-#define fooexitstatushfoo
-
-/***
-  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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdbool.h>
-
-typedef enum ExitStatus {
-        /* EXIT_SUCCESS defined by libc */
-        /* EXIT_FAILURE defined by libc */
-        EXIT_INVALIDARGUMENT = 2,
-        EXIT_NOTIMPLEMENTED = 3,
-        EXIT_NOPERMISSION = 4,
-        EXIT_NOTINSTALLED = 5,
-        EXIT_NOTCONFIGURED = 6,
-        EXIT_NOTRUNNING = 7,
-
-        /* The LSB suggests that error codes >= 200 are "reserved". We
-         * use them here under the assumption that they hence are
-         * unused by init scripts.
-         *
-         * http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
-
-        EXIT_CHDIR = 200,
-        EXIT_NICE,
-        EXIT_FDS,
-        EXIT_EXEC,
-        EXIT_MEMORY,
-        EXIT_LIMITS,
-        EXIT_OOM_ADJUST,
-        EXIT_SIGNAL_MASK,
-        EXIT_STDIN,
-        EXIT_STDOUT,
-        EXIT_CHROOT,   /* 210 */
-        EXIT_IOPRIO,
-        EXIT_TIMERSLACK,
-        EXIT_SECUREBITS,
-        EXIT_SETSCHEDULER,
-        EXIT_CPUAFFINITY,
-        EXIT_GROUP,
-        EXIT_USER,
-        EXIT_CAPABILITIES,
-        EXIT_CGROUP,
-        EXIT_SETSID,   /* 220 */
-        EXIT_CONFIRM,
-        EXIT_STDERR,
-        EXIT_TCPWRAP,
-        EXIT_PAM,
-        EXIT_NETWORK,
-        EXIT_NAMESPACE
-
-} ExitStatus;
-
-typedef enum ExitStatusLevel {
-        EXIT_STATUS_MINIMAL,
-        EXIT_STATUS_SYSTEMD,
-        EXIT_STATUS_LSB,
-        EXIT_STATUS_FULL = EXIT_STATUS_LSB
-} ExitStatusLevel;
-
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level);
-
-bool is_clean_exit(int code, int status);
-bool is_clean_exit_lsb(int code, int status);
-
-#endif
diff --git a/src/hashmap.c b/src/hashmap.c
deleted file mode 100644
index 6928118..0000000
--- a/src/hashmap.c
+++ /dev/null
@@ -1,731 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU 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/src/hashmap.h b/src/hashmap.h
deleted file mode 100644
index ab4363a..0000000
--- a/src/hashmap.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU 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/src/journal/journald.h b/src/journal/journald.h
index 6160991..7840fd5 100644
--- a/src/journal/journald.h
+++ b/src/journal/journald.h
@@ -29,6 +29,7 @@
 #include "journal-file.h"
 #include "hashmap.h"
 #include "util.h"
+#include "audit.h"
 #include "journal-rate-limit.h"
 #include "list.h"
 
diff --git a/src/label.c b/src/label.c
deleted file mode 100644
index 2c887a0..0000000
--- a/src/label.c
+++ /dev/null
@@ -1,413 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <errno.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <malloc.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include "label.h"
-#include "util.h"
-
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#include <selinux/label.h>
-
-static struct selabel_handle *label_hnd = NULL;
-
-static int use_selinux_cached = -1;
-
-static inline bool use_selinux(void) {
-
-        if (use_selinux_cached < 0)
-                use_selinux_cached = is_selinux_enabled() > 0;
-
-        return use_selinux_cached;
-}
-
-void label_retest_selinux(void) {
-        use_selinux_cached = -1;
-}
-
-#endif
-
-int label_init(void) {
-        int r = 0;
-
-#ifdef HAVE_SELINUX
-        usec_t before_timestamp, after_timestamp;
-        struct mallinfo before_mallinfo, after_mallinfo;
-
-        if (!use_selinux())
-                return 0;
-
-        if (label_hnd)
-                return 0;
-
-        before_mallinfo = mallinfo();
-        before_timestamp = now(CLOCK_MONOTONIC);
-
-        label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
-        if (!label_hnd) {
-                log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
-                         "Failed to initialize SELinux context: %m");
-                r = security_getenforce() == 1 ? -errno : 0;
-        } else  {
-                char timespan[FORMAT_TIMESPAN_MAX];
-                int l;
-
-                after_timestamp = now(CLOCK_MONOTONIC);
-                after_mallinfo = mallinfo();
-
-                l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
-
-                log_info("Successfully loaded SELinux database in %s, size on heap is %iK.",
-                         format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp),
-                         (l+1023)/1024);
-        }
-#endif
-
-        return r;
-}
-
-int label_fix(const char *path, bool ignore_enoent) {
-        int r = 0;
-
-#ifdef HAVE_SELINUX
-        struct stat st;
-        security_context_t fcon;
-
-        if (!use_selinux() || !label_hnd)
-                return 0;
-
-        r = lstat(path, &st);
-        if (r == 0) {
-                r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
-
-                /* If there's no label to set, then exit without warning */
-                if (r < 0 && errno == ENOENT)
-                        return 0;
-
-                if (r == 0) {
-                        r = lsetfilecon(path, fcon);
-                        freecon(fcon);
-
-                        /* If the FS doesn't support labels, then exit without warning */
-                        if (r < 0 && errno == ENOTSUP)
-                                return 0;
-                }
-        }
-
-        if (r < 0) {
-                /* Ignore ENOENT in some cases */
-                if (ignore_enoent && errno == ENOENT)
-                        return 0;
-
-                log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
-                         "Unable to fix label of %s: %m", path);
-                r = security_getenforce() == 1 ? -errno : 0;
-        }
-#endif
-
-        return r;
-}
-
-void label_finish(void) {
-
-#ifdef HAVE_SELINUX
-        if (use_selinux() && label_hnd)
-                selabel_close(label_hnd);
-#endif
-}
-
-int label_get_create_label_from_exe(const char *exe, char **label) {
-
-        int r = 0;
-
-#ifdef HAVE_SELINUX
-        security_context_t mycon = NULL, fcon = NULL;
-        security_class_t sclass;
-
-        if (!use_selinux()) {
-                *label = NULL;
-                return 0;
-        }
-
-        r = getcon(&mycon);
-        if (r < 0)
-                goto fail;
-
-        r = getfilecon(exe, &fcon);
-        if (r < 0)
-                goto fail;
-
-        sclass = string_to_security_class("process");
-        r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
-        if (r == 0)
-                log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
-
-fail:
-        if (r < 0 && security_getenforce() == 1)
-                r = -errno;
-
-        freecon(mycon);
-        freecon(fcon);
-#endif
-
-        return r;
-}
-
-int label_fifofile_set(const char *path) {
-        int r = 0;
-
-#ifdef HAVE_SELINUX
-        security_context_t filecon = NULL;
-
-        if (!use_selinux() || !label_hnd)
-                return 0;
-
-        r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFIFO);
-        if (r < 0)
-                r = -errno;
-        else if (r == 0) {
-                r = setfscreatecon(filecon);
-                if (r < 0) {
-                        log_error("Failed to set SELinux file context on %s: %m", path);
-                        r = -errno;
-                }
-
-                freecon(filecon);
-        }
-
-        if (r < 0 && security_getenforce() == 0)
-                r = 0;
-#endif
-
-        return r;
-}
-
-int label_symlinkfile_set(const char *path) {
-        int r = 0;
-
-#ifdef HAVE_SELINUX
-        security_context_t filecon = NULL;
-
-        if (!use_selinux() || !label_hnd)
-                return 0;
-
-        r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFLNK);
-        if (r < 0)
-                r = -errno;
-        else if (r == 0) {
-                r = setfscreatecon(filecon);
-                if (r < 0) {
-                        log_error("Failed to set SELinux file context on %s: %m", path);
-                        r = -errno;
-                }
-
-                freecon(filecon);
-        }
-
-        if (r < 0 && security_getenforce() == 0)
-                r = 0;
-#endif
-
-        return r;
-}
-
-int label_socket_set(const char *label) {
-
-#ifdef HAVE_SELINUX
-        if (!use_selinux())
-                return 0;
-
-        if (setsockcreatecon((security_context_t) label) < 0) {
-                log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
-                         "Failed to set SELinux context (%s) on socket: %m", label);
-
-                if (security_getenforce() == 1)
-                        return -errno;
-        }
-#endif
-
-        return 0;
-}
-
-void label_file_clear(void) {
-
-#ifdef HAVE_SELINUX
-        if (!use_selinux())
-                return;
-
-        setfscreatecon(NULL);
-#endif
-}
-
-void label_socket_clear(void) {
-
-#ifdef HAVE_SELINUX
-        if (!use_selinux())
-                return;
-
-        setsockcreatecon(NULL);
-#endif
-}
-
-void label_free(const char *label) {
-
-#ifdef HAVE_SELINUX
-        if (!use_selinux())
-                return;
-
-        freecon((security_context_t) label);
-#endif
-}
-
-int label_mkdir(const char *path, mode_t mode) {
-
-        /* Creates a directory and labels it according to the SELinux policy */
-
-#ifdef HAVE_SELINUX
-        int r;
-        security_context_t fcon = NULL;
-
-        if (!use_selinux() || !label_hnd)
-                goto skipped;
-
-        if (path_is_absolute(path))
-                r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
-        else {
-                char *newpath;
-
-                newpath = path_make_absolute_cwd(path);
-                if (!newpath)
-                        return -ENOMEM;
-
-                r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
-                free(newpath);
-        }
-
-        if (r == 0)
-                r = setfscreatecon(fcon);
-
-        if (r < 0 && errno != ENOENT) {
-                log_error("Failed to set security context %s for %s: %m", fcon, path);
-
-                if (security_getenforce() == 1) {
-                        r = -errno;
-                        goto finish;
-                }
-        }
-
-        r = mkdir(path, mode);
-        if (r < 0)
-                r = -errno;
-
-finish:
-        setfscreatecon(NULL);
-        freecon(fcon);
-
-        return r;
-
-skipped:
-#endif
-        return mkdir(path, mode) < 0 ? -errno : 0;
-}
-
-int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
-
-        /* Binds a socket and label its file system object according to the SELinux policy */
-
-#ifdef HAVE_SELINUX
-        int r;
-        security_context_t fcon = NULL;
-        const struct sockaddr_un *un;
-        char *path = NULL;
-
-        assert(fd >= 0);
-        assert(addr);
-        assert(addrlen >= sizeof(sa_family_t));
-
-        if (!use_selinux() || !label_hnd)
-                goto skipped;
-
-        /* Filter out non-local sockets */
-        if (addr->sa_family != AF_UNIX)
-                goto skipped;
-
-        /* Filter out anonymous sockets */
-        if (addrlen < sizeof(sa_family_t) + 1)
-                goto skipped;
-
-        /* Filter out abstract namespace sockets */
-        un = (const struct sockaddr_un*) addr;
-        if (un->sun_path[0] == 0)
-                goto skipped;
-
-        path = strndup(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
-        if (!path)
-                return -ENOMEM;
-
-        if (path_is_absolute(path))
-                r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
-        else {
-                char *newpath;
-
-                newpath = path_make_absolute_cwd(path);
-
-                if (!newpath) {
-                        free(path);
-                        return -ENOMEM;
-                }
-
-                r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
-                free(newpath);
-        }
-
-        if (r == 0)
-                r = setfscreatecon(fcon);
-
-        if (r < 0 && errno != ENOENT) {
-                log_error("Failed to set security context %s for %s: %m", fcon, path);
-
-                if (security_getenforce() == 1) {
-                        r = -errno;
-                        goto finish;
-                }
-        }
-
-        r = bind(fd, addr, addrlen);
-        if (r < 0)
-                r = -errno;
-
-finish:
-        setfscreatecon(NULL);
-        freecon(fcon);
-        free(path);
-
-        return r;
-
-skipped:
-#endif
-        return bind(fd, addr, addrlen) < 0 ? -errno : 0;
-}
diff --git a/src/label.h b/src/label.h
deleted file mode 100644
index ead4483..0000000
--- a/src/label.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foolabelhfoo
-#define foolabelhfoo
-
-/***
-  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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/types.h>
-#include <stdbool.h>
-#include <sys/socket.h>
-
-int label_init(void);
-void label_finish(void);
-
-int label_fix(const char *path, bool ignore_enoent);
-
-int label_socket_set(const char *label);
-void label_socket_clear(void);
-
-int label_fifofile_set(const char *path);
-int label_symlinkfile_set(const char *path);
-void label_file_clear(void);
-
-void label_free(const char *label);
-
-int label_get_create_label_from_exe(const char *exe, char **label);
-
-int label_mkdir(const char *path, mode_t mode);
-
-void label_retest_selinux(void);
-
-int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen);
-
-#endif
diff --git a/src/log.c b/src/log.c
deleted file mode 100644
index 9fffc1d..0000000
--- a/src/log.c
+++ /dev/null
@@ -1,747 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU 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"
-#include "socket-util.h"
-
-#define SNDBUF_SIZE (8*1024*1024)
-
-static LogTarget log_target = LOG_TARGET_CONSOLE;
-static int log_max_level = LOG_INFO;
-static int log_facility = LOG_DAEMON;
-
-static int console_fd = STDERR_FILENO;
-static int syslog_fd = -1;
-static int kmsg_fd = -1;
-static int journal_fd = -1;
-
-static bool syslog_is_stream = false;
-
-static bool show_color = false;
-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;
-}
-
-void log_close_kmsg(void) {
-
-        if (kmsg_fd < 0)
-                return;
-
-        close_nointr_nofail(kmsg_fd);
-        kmsg_fd = -1;
-}
-
-static int log_open_kmsg(void) {
-
-        if (kmsg_fd >= 0)
-                return 0;
-
-        kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
-        if (kmsg_fd < 0) {
-                log_error("Failed to open /dev/kmsg for logging: %s", strerror(errno));
-                return -errno;
-        }
-
-        log_debug("Successfully opened /dev/kmsg for logging.");
-
-        return 0;
-}
-
-void log_close_syslog(void) {
-
-        if (syslog_fd < 0)
-                return;
-
-        close_nointr_nofail(syslog_fd);
-        syslog_fd = -1;
-}
-
-static int create_log_socket(int type) {
-        int fd;
-
-        /* All output to the syslog/journal fds we do asynchronously,
-         * and if the buffers are full we just drop the messages */
-
-        fd = socket(AF_UNIX, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-        if (fd < 0)
-                return -errno;
-
-        fd_inc_sndbuf(fd, SNDBUF_SIZE);
-
-        return fd;
-}
-
-static int log_open_syslog(void) {
-        union sockaddr_union sa;
-        int r;
-
-        if (syslog_fd >= 0)
-                return 0;
-
-        zero(sa);
-        sa.un.sun_family = AF_UNIX;
-        strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
-
-        syslog_fd = create_log_socket(SOCK_DGRAM);
-        if (syslog_fd < 0) {
-                r = syslog_fd;
-                goto fail;
-        }
-
-        if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
-                close_nointr_nofail(syslog_fd);
-
-                /* Some legacy syslog systems still use stream
-                 * sockets. They really shouldn't. But what can we
-                 * do... */
-                syslog_fd = create_log_socket(SOCK_STREAM);
-                if (syslog_fd < 0) {
-                        r = syslog_fd;
-                        goto fail;
-                }
-
-                if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
-                        r = -errno;
-                        goto fail;
-                }
-
-                syslog_is_stream = true;
-        } else
-                syslog_is_stream = false;
-
-        log_debug("Successfully opened syslog for logging.");
-
-        return 0;
-
-fail:
-        log_close_syslog();
-        log_debug("Failed to open syslog for logging: %s", strerror(-r));
-        return r;
-}
-
-void log_close_journal(void) {
-
-        if (journal_fd < 0)
-                return;
-
-        close_nointr_nofail(journal_fd);
-        journal_fd = -1;
-}
-
-static int log_open_journal(void) {
-        union sockaddr_union sa;
-        int r;
-
-        if (journal_fd >= 0)
-                return 0;
-
-        journal_fd = create_log_socket(SOCK_DGRAM);
-        if (journal_fd < 0) {
-                r = journal_fd;
-                goto fail;
-        }
-
-        zero(sa);
-        sa.un.sun_family = AF_UNIX;
-        strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
-
-        if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
-                r = -errno;
-                goto fail;
-        }
-
-        log_debug("Successfully opened journal for logging.");
-
-        return 0;
-
-fail:
-        log_close_journal();
-        log_debug("Failed to open journal for logging: %s", strerror(-r));
-        return r;
-}
-
-int log_open(void) {
-        int r;
-
-        /* If we don't use the console we close it here, to not get
-         * killed by SAK. If we don't use syslog we close it here so
-         * that we are not confused by somebody deleting the socket in
-         * the fs. If we don't use /dev/kmsg we still keep it open,
-         * because there is no reason to close it. */
-
-        if (log_target == LOG_TARGET_NULL) {
-                log_close_journal();
-                log_close_syslog();
-                log_close_console();
-                return 0;
-        }
-
-        if (log_target != LOG_TARGET_AUTO ||
-            getpid() == 1 ||
-            isatty(STDERR_FILENO) <= 0) {
-
-                if (log_target == LOG_TARGET_AUTO ||
-                    log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
-                    log_target == LOG_TARGET_JOURNAL) {
-                        r = log_open_journal();
-                        if (r >= 0) {
-                                log_close_syslog();
-                                log_close_console();
-                                return r;
-                        }
-                }
-
-                if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
-                    log_target == LOG_TARGET_SYSLOG) {
-                        r = log_open_syslog();
-                        if (r >= 0) {
-                                log_close_journal();
-                                log_close_console();
-                                return r;
-                        }
-                }
-
-                if (log_target == LOG_TARGET_AUTO ||
-                    log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
-                    log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
-                    log_target == LOG_TARGET_KMSG) {
-                        r = log_open_kmsg();
-                        if (r >= 0) {
-                                log_close_journal();
-                                log_close_syslog();
-                                log_close_console();
-                                return r;
-                        }
-                }
-        }
-
-        log_close_journal();
-        log_close_syslog();
-
-        /* Get the real /dev/console if we are PID=1, hence reopen */
-        log_close_console();
-        return log_open_console();
-}
-
-void log_set_target(LogTarget target) {
-        assert(target >= 0);
-        assert(target < _LOG_TARGET_MAX);
-
-        log_target = target;
-}
-
-void log_close(void) {
-        log_close_journal();
-        log_close_syslog();
-        log_close_kmsg();
-        log_close_console();
-}
-
-void log_forget_fds(void) {
-        console_fd = kmsg_fd = syslog_fd = journal_fd = -1;
-}
-
-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;
-        bool highlight;
-
-        if (console_fd < 0)
-                return 0;
-
-        highlight = LOG_PRI(level) <= LOG_ERR && show_color;
-
-        zero(iovec);
-
-        if (show_location) {
-                snprintf(location, sizeof(location), "(%s:%u) ", file, line);
-                char_array_0(location);
-                IOVEC_SET_STRING(iovec[n++], location);
-        }
-
-        if (highlight)
-                IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED_ON);
-        IOVEC_SET_STRING(iovec[n++], buffer);
-        if (highlight)
-                IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF);
-        IOVEC_SET_STRING(iovec[n++], "\n");
-
-        if (writev(console_fd, iovec, n) < 0)
-                return -errno;
-
-        return 1;
-}
-
-static int write_to_syslog(
-        int level,
-        const char*file,
-        int line,
-        const char *func,
-        const char *buffer) {
-
-        char header_priority[16], header_time[64], header_pid[16];
-        struct iovec iovec[5];
-        struct msghdr msghdr;
-        time_t t;
-        struct tm *tm;
-
-        if (syslog_fd < 0)
-                return 0;
-
-        snprintf(header_priority, sizeof(header_priority), "<%i>", level);
-        char_array_0(header_priority);
-
-        t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
-        if (!(tm = localtime(&t)))
-                return -EINVAL;
-
-        if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
-                return -EINVAL;
-
-        snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
-        char_array_0(header_pid);
-
-        zero(iovec);
-        IOVEC_SET_STRING(iovec[0], header_priority);
-        IOVEC_SET_STRING(iovec[1], header_time);
-        IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
-        IOVEC_SET_STRING(iovec[3], header_pid);
-        IOVEC_SET_STRING(iovec[4], buffer);
-
-        /* When using syslog via SOCK_STREAM separate the messages by NUL chars */
-        if (syslog_is_stream)
-                iovec[4].iov_len++;
-
-        zero(msghdr);
-        msghdr.msg_iov = iovec;
-        msghdr.msg_iovlen = ELEMENTSOF(iovec);
-
-        for (;;) {
-                ssize_t n;
-
-                n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
-                if (n < 0)
-                        return -errno;
-
-                if (!syslog_is_stream ||
-                    (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec)))
-                        break;
-
-                IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n);
-        }
-
-        return 1;
-}
-
-static int write_to_kmsg(
-        int level,
-        const char*file,
-        int line,
-        const char *func,
-        const char *buffer) {
-
-        char header_priority[16], header_pid[16];
-        struct iovec iovec[5];
-
-        if (kmsg_fd < 0)
-                return 0;
-
-        snprintf(header_priority, sizeof(header_priority), "<%i>", level);
-        char_array_0(header_priority);
-
-        snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
-        char_array_0(header_pid);
-
-        zero(iovec);
-        IOVEC_SET_STRING(iovec[0], header_priority);
-        IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
-        IOVEC_SET_STRING(iovec[2], header_pid);
-        IOVEC_SET_STRING(iovec[3], buffer);
-        IOVEC_SET_STRING(iovec[4], "\n");
-
-        if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
-                return -errno;
-
-        return 1;
-}
-
-static int write_to_journal(
-        int level,
-        const char*file,
-        int line,
-        const char *func,
-        const char *buffer) {
-
-        char header[LINE_MAX];
-        struct iovec iovec[3];
-        struct msghdr mh;
-
-        if (journal_fd < 0)
-                return 0;
-
-        snprintf(header, sizeof(header),
-                 "PRIORITY=%i\n"
-                 "SYSLOG_FACILITY=%i\n"
-                 "CODE_FILE=%s\n"
-                 "CODE_LINE=%i\n"
-                 "CODE_FUNCTION=%s\n"
-                 "MESSAGE=",
-                 LOG_PRI(level),
-                 LOG_FAC(level),
-                 file,
-                 line,
-                 func);
-
-        char_array_0(header);
-
-        zero(iovec);
-        IOVEC_SET_STRING(iovec[0], header);
-        IOVEC_SET_STRING(iovec[1], buffer);
-        IOVEC_SET_STRING(iovec[2], "\n");
-
-        zero(mh);
-        mh.msg_iov = iovec;
-        mh.msg_iovlen = ELEMENTSOF(iovec);
-
-        if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 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;
-
-                if (log_target == LOG_TARGET_AUTO ||
-                    log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
-                    log_target == LOG_TARGET_JOURNAL) {
-
-                        k = write_to_journal(level, file, line, func, buffer);
-                        if (k < 0) {
-                                if (k != -EAGAIN)
-                                        log_close_journal();
-                                log_open_kmsg();
-                        } else if (k > 0)
-                                r++;
-                }
-
-                if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
-                    log_target == LOG_TARGET_SYSLOG) {
-
-                        k = write_to_syslog(level, file, line, func, buffer);
-                        if (k < 0) {
-                                if (k != -EAGAIN)
-                                        log_close_syslog();
-                                log_open_kmsg();
-                        } else if (k > 0)
-                                r++;
-                }
-
-                if (k <= 0 &&
-                    (log_target == LOG_TARGET_AUTO ||
-                     log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
-                     log_target == LOG_TARGET_KMSG)) {
-
-                        k = write_to_kmsg(level, file, line, func, buffer);
-                        if (k < 0) {
-                                log_close_kmsg();
-                                log_open_console();
-                        } else if (k > 0)
-                                r++;
-                }
-
-                if (k <= 0) {
-                        k = write_to_console(level, file, line, func, buffer);
-                        if (k < 0)
-                                return k;
-                }
-
-                buffer = e;
-        } while (buffer);
-
-        return r;
-}
-
-int log_dump_internal(
-        int level,
-        const char*file,
-        int line,
-        const char *func,
-        char *buffer) {
-
-        int saved_errno, r;
-
-        /* This modifies the buffer... */
-
-        if (_likely_(LOG_PRI(level) > log_max_level))
-                return 0;
-
-        saved_errno = errno;
-        r = log_dispatch(level, file, line, func, buffer);
-        errno = saved_errno;
-
-        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.");
-}
-
-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("SYSTEMD_LOG_TARGET")))
-                if (log_set_target_from_string(e) < 0)
-                        log_warning("Failed to parse log target %s. Ignoring.", e);
-
-        if ((e = getenv("SYSTEMD_LOG_LEVEL")))
-                if (log_set_max_level_from_string(e) < 0)
-                        log_warning("Failed to parse log level %s. Ignoring.", e);
-
-        if ((e = getenv("SYSTEMD_LOG_COLOR")))
-                if (log_show_color_from_string(e) < 0)
-                        log_warning("Failed to parse bool %s. Ignoring.", e);
-
-        if ((e = getenv("SYSTEMD_LOG_LOCATION")))
-                if (log_show_location_from_string(e) < 0)
-                        log_warning("Failed to parse bool %s. Ignoring.", e);
-}
-
-LogTarget log_get_target(void) {
-        return log_target;
-}
-
-int log_get_max_level(void) {
-        return log_max_level;
-}
-
-void log_show_color(bool b) {
-        show_color = b;
-}
-
-void log_show_location(bool b) {
-        show_location = b;
-}
-
-int log_show_color_from_string(const char *e) {
-        int t;
-
-        t = parse_boolean(e);
-        if (t < 0)
-                return t;
-
-        log_show_color(t);
-        return 0;
-}
-
-int log_show_location_from_string(const char *e) {
-        int t;
-
-        t = parse_boolean(e);
-        if (t < 0)
-                return t;
-
-        log_show_location(t);
-        return 0;
-}
-
-static const char *const log_target_table[] = {
-        [LOG_TARGET_CONSOLE] = "console",
-        [LOG_TARGET_KMSG] = "kmsg",
-        [LOG_TARGET_JOURNAL] = "journal",
-        [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg",
-        [LOG_TARGET_SYSLOG] = "syslog",
-        [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
-        [LOG_TARGET_AUTO] = "auto",
-        [LOG_TARGET_NULL] = "null"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
diff --git a/src/log.h b/src/log.h
deleted file mode 100644
index 3283808..0000000
--- a/src/log.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU 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_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);
-
-#endif
diff --git a/src/login/logind.h b/src/login/logind.h
index a4b4602..9586793 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -28,6 +28,7 @@
 #include <libudev.h>
 
 #include "util.h"
+#include "audit.h"
 #include "list.h"
 #include "hashmap.h"
 #include "cgroup-util.h"
diff --git a/src/login/pam-module.c b/src/login/pam-module.c
index 4106d2b..1edb91c 100644
--- a/src/login/pam-module.c
+++ b/src/login/pam-module.c
@@ -35,6 +35,7 @@
 #include <systemd/sd-daemon.h>
 
 #include "util.h"
+#include "audit.h"
 #include "macro.h"
 #include "strv.h"
 #include "dbus-common.h"
diff --git a/src/nspawn.c b/src/nspawn.c
index 6f5a9d9..66910bc 100644
--- a/src/nspawn.c
+++ b/src/nspawn.c
@@ -43,6 +43,7 @@
 
 #include "log.h"
 #include "util.h"
+#include "audit.h"
 #include "missing.h"
 #include "cgroup-util.h"
 #include "strv.h"
diff --git a/src/ratelimit.c b/src/ratelimit.c
deleted file mode 100644
index 93157c7..0000000
--- a/src/ratelimit.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-
-#include "ratelimit.h"
-#include "log.h"
-
-/* Modelled after Linux' lib/ratelimit.c by Dave Young
- * <hidave.darkstar at gmail.com>, which is licensed GPLv2. */
-
-bool ratelimit_test(RateLimit *r) {
-        usec_t ts;
-
-        assert(r);
-
-        if (r->interval <= 0 || r->burst <= 0)
-                return true;
-
-        ts = now(CLOCK_MONOTONIC);
-
-        if (r->begin <= 0 ||
-            r->begin + r->interval < ts) {
-                r->begin = ts;
-
-                /* Reset counter */
-                r->num = 0;
-                goto good;
-        }
-
-        if (r->num <= r->burst)
-                goto good;
-
-        return false;
-
-good:
-        r->num++;
-        return true;
-}
diff --git a/src/ratelimit.h b/src/ratelimit.h
deleted file mode 100644
index a6443e7..0000000
--- a/src/ratelimit.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooratelimithfoo
-#define fooratelimithfoo
-
-/***
-  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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "util.h"
-
-typedef struct RateLimit {
-        usec_t interval;
-        usec_t begin;
-        unsigned burst;
-        unsigned num;
-} RateLimit;
-
-#define RATELIMIT_DEFINE(_name, _interval, _burst)       \
-        RateLimit _name = {                              \
-                .interval = (_interval),                 \
-                .burst = (_burst),                       \
-                .num = 0,                                \
-                .begin = 0                               \
-        }
-
-#define RATELIMIT_INIT(v, _interval, _burst)             \
-        do {                                             \
-                RateLimit *_r = &(v);                    \
-                _r->interval = (_interval);              \
-                _r->burst = (_burst);                    \
-                _r->num = 0;                             \
-                _r->begin = 0;                           \
-        } while (false)
-
-bool ratelimit_test(RateLimit *r);
-
-#endif
diff --git a/src/set.c b/src/set.c
deleted file mode 100644
index 097b9d3..0000000
--- a/src/set.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdlib.h>
-
-#include "set.h"
-#include "hashmap.h"
-
-#define MAKE_SET(h) ((Set*) (h))
-#define MAKE_HASHMAP(s) ((Hashmap*) (s))
-
-/* For now this is not much more than a wrapper around a hashmap */
-
-Set *set_new(hash_func_t hash_func, compare_func_t compare_func) {
-        return MAKE_SET(hashmap_new(hash_func, compare_func));
-}
-
-void set_free(Set* s) {
-        hashmap_free(MAKE_HASHMAP(s));
-}
-
-void set_free_free(Set *s) {
-        hashmap_free_free(MAKE_HASHMAP(s));
-}
-
-int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func) {
-        return hashmap_ensure_allocated((Hashmap**) s, hash_func, compare_func);
-}
-
-int set_put(Set *s, void *value) {
-        return hashmap_put(MAKE_HASHMAP(s), value, value);
-}
-
-int set_replace(Set *s, void *value) {
-        return hashmap_replace(MAKE_HASHMAP(s), value, value);
-}
-
-void *set_get(Set *s, void *value) {
-        return hashmap_get(MAKE_HASHMAP(s), value);
-}
-
-void *set_remove(Set *s, void *value) {
-        return hashmap_remove(MAKE_HASHMAP(s), value);
-}
-
-int set_remove_and_put(Set *s, void *old_value, void *new_value) {
-        return hashmap_remove_and_put(MAKE_HASHMAP(s), old_value, new_value, new_value);
-}
-
-unsigned set_size(Set *s) {
-        return hashmap_size(MAKE_HASHMAP(s));
-}
-
-bool set_isempty(Set *s) {
-        return hashmap_isempty(MAKE_HASHMAP(s));
-}
-
-void *set_iterate(Set *s, Iterator *i) {
-        return hashmap_iterate(MAKE_HASHMAP(s), i, NULL);
-}
-
-void *set_iterate_backwards(Set *s, Iterator *i) {
-        return hashmap_iterate_backwards(MAKE_HASHMAP(s), i, NULL);
-}
-
-void *set_iterate_skip(Set *s, void *value, Iterator *i) {
-        return hashmap_iterate_skip(MAKE_HASHMAP(s), value, i);
-}
-
-void *set_steal_first(Set *s) {
-        return hashmap_steal_first(MAKE_HASHMAP(s));
-}
-
-void* set_first(Set *s) {
-        return hashmap_first(MAKE_HASHMAP(s));
-}
-
-void* set_last(Set *s) {
-        return hashmap_last(MAKE_HASHMAP(s));
-}
-
-int set_merge(Set *s, Set *other) {
-        return hashmap_merge(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
-}
-
-void set_move(Set *s, Set *other) {
-        return hashmap_move(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
-}
-
-int set_move_one(Set *s, Set *other, void *value) {
-        return hashmap_move_one(MAKE_HASHMAP(s), MAKE_HASHMAP(other), value);
-}
-
-Set* set_copy(Set *s) {
-        return MAKE_SET(hashmap_copy(MAKE_HASHMAP(s)));
-}
-
-void set_clear(Set *s) {
-        hashmap_clear(MAKE_HASHMAP(s));
-}
diff --git a/src/set.h b/src/set.h
deleted file mode 100644
index 885780c..0000000
--- a/src/set.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosethfoo
-#define foosethfoo
-
-/***
-  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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/* Pretty straightforward set implementation. Internally based on the
- * hashmap. That means that as a minor optimization a NULL set
- * object will be treated as empty set for all read
- * operations. That way it is not necessary to instantiate an object
- * for each set use. */
-
-#include "hashmap.h"
-
-typedef struct Set Set;
-
-Set *set_new(hash_func_t hash_func, compare_func_t compare_func);
-void set_free(Set* s);
-void set_free_free(Set *s);
-Set* set_copy(Set *s);
-int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func);
-
-int set_put(Set *s, void *value);
-int set_replace(Set *s, void *value);
-void *set_get(Set *s, void *value);
-void *set_remove(Set *s, void *value);
-int set_remove_and_put(Set *s, void *old_value, void *new_value);
-
-int set_merge(Set *s, Set *other);
-void set_move(Set *s, Set *other);
-int set_move_one(Set *s, Set *other, void *value);
-
-unsigned set_size(Set *s);
-bool set_isempty(Set *s);
-
-void *set_iterate(Set *s, Iterator *i);
-void *set_iterate_backwards(Set *s, Iterator *i);
-void *set_iterate_skip(Set *s, void *value, Iterator *i);
-
-void set_clear(Set *s);
-void *set_steal_first(Set *s);
-void* set_first(Set *s);
-void* set_last(Set *s);
-
-#define SET_FOREACH(e, s, i) \
-        for ((i) = ITERATOR_FIRST, (e) = set_iterate((s), &(i)); (e); (e) = set_iterate((s), &(i)))
-
-#define SET_FOREACH_BACKWARDS(e, s, i) \
-        for ((i) = ITERATOR_LAST, (e) = set_iterate_backwards((s), &(i)); (e); (e) = set_iterate_backwards((s), &(i)))
-
-#endif
diff --git a/src/shared/audit.c b/src/shared/audit.c
new file mode 100644
index 0000000..3850059
--- /dev/null
+++ b/src/shared/audit.c
@@ -0,0 +1,118 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/prctl.h>
+#include <sys/capability.h>
+
+#include "macro.h"
+#include "audit.h"
+#include "util.h"
+#include "log.h"
+
+int audit_session_from_pid(pid_t pid, uint32_t *id) {
+        char *s;
+        uint32_t u;
+        int r;
+
+        assert(id);
+
+        if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
+                return -ENOENT;
+
+        if (pid == 0)
+                r = read_one_line_file("/proc/self/sessionid", &s);
+        else {
+                char *p;
+
+                if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0)
+                        return -ENOMEM;
+
+                r = read_one_line_file(p, &s);
+                free(p);
+        }
+
+        if (r < 0)
+                return r;
+
+        r = safe_atou32(s, &u);
+        free(s);
+
+        if (r < 0)
+                return r;
+
+        if (u == (uint32_t) -1 || u <= 0)
+                return -ENOENT;
+
+        *id = u;
+        return 0;
+}
+
+int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
+        char *s;
+        uid_t u;
+        int r;
+
+        assert(uid);
+
+        /* Only use audit login uid if we are executed with sufficient
+         * capabilities so that pam_loginuid could do its job. If we
+         * are lacking the CAP_AUDIT_CONTROL capabality we most likely
+         * are being run in a container and /proc/self/loginuid is
+         * useless since it probably contains a uid of the host
+         * system. */
+
+        if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
+                return -ENOENT;
+
+        if (pid == 0)
+                r = read_one_line_file("/proc/self/loginuid", &s);
+        else {
+                char *p;
+
+                if (asprintf(&p, "/proc/%lu/loginuid", (unsigned long) pid) < 0)
+                        return -ENOMEM;
+
+                r = read_one_line_file(p, &s);
+                free(p);
+        }
+
+        if (r < 0)
+                return r;
+
+        r = parse_uid(s, &u);
+        free(s);
+
+        if (r < 0)
+                return r;
+
+        if (u == (uid_t) -1)
+                return -ENOENT;
+
+        *uid = (uid_t) u;
+        return 0;
+}
diff --git a/src/shared/audit.h b/src/shared/audit.h
new file mode 100644
index 0000000..3529046
--- /dev/null
+++ b/src/shared/audit.h
@@ -0,0 +1,29 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooaudithfoo
+#define fooaudithfoo
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "capability.h"
+
+int audit_session_from_pid(pid_t pid, uint32_t *id);
+int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
+#endif
diff --git a/src/shared/capability.c b/src/shared/capability.c
new file mode 100644
index 0000000..b800215
--- /dev/null
+++ b/src/shared/capability.c
@@ -0,0 +1,86 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+
+#include "macro.h"
+#include "capability.h"
+#include "util.h"
+#include "log.h"
+
+int have_effective_cap(int value) {
+        cap_t cap;
+        cap_flag_value_t fv;
+        int r;
+
+        if (!(cap = cap_get_proc()))
+                return -errno;
+
+        if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
+                r = -errno;
+        else
+                r = fv == CAP_SET;
+
+        cap_free(cap);
+        return r;
+}
+
+unsigned long cap_last_cap(void) {
+        static __thread unsigned long saved;
+        static __thread bool valid = false;
+        unsigned long p;
+
+        if (valid)
+                return saved;
+
+        p = (unsigned long) CAP_LAST_CAP;
+
+        if (prctl(PR_CAPBSET_READ, p) < 0) {
+
+                /* Hmm, look downwards, until we find one that
+                 * works */
+                for (p--; p > 0; p --)
+                        if (prctl(PR_CAPBSET_READ, p) >= 0)
+                                break;
+
+        } else {
+
+                /* Hmm, look upwards, until we find one that doesn't
+                 * work */
+                for (;; p++)
+                        if (prctl(PR_CAPBSET_READ, p+1) < 0)
+                                break;
+        }
+
+        saved = p;
+        valid = true;
+
+        return p;
+}
diff --git a/src/shared/capability.h b/src/shared/capability.h
new file mode 100644
index 0000000..ab7e40b
--- /dev/null
+++ b/src/shared/capability.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foocapabilityhfoo
+#define foocapabilityhfoo
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+unsigned long cap_last_cap(void);
+int have_effective_cap(int value);
+#endif
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
new file mode 100644
index 0000000..a9b0113
--- /dev/null
+++ b/src/shared/conf-parser.c
@@ -0,0 +1,852 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "conf-parser.h"
+#include "util.h"
+#include "macro.h"
+#include "strv.h"
+#include "log.h"
+#include "utf8.h"
+
+int config_item_table_lookup(
+                void *table,
+                const char *section,
+                const char *lvalue,
+                ConfigParserCallback *func,
+                int *ltype,
+                void **data,
+                void *userdata) {
+
+        ConfigTableItem *t;
+
+        assert(table);
+        assert(lvalue);
+        assert(func);
+        assert(ltype);
+        assert(data);
+
+        for (t = table; t->lvalue; t++) {
+
+                if (!streq(lvalue, t->lvalue))
+                        continue;
+
+                if (!streq_ptr(section, t->section))
+                        continue;
+
+                *func = t->parse;
+                *ltype = t->ltype;
+                *data = t->data;
+                return 1;
+        }
+
+        return 0;
+}
+
+int config_item_perf_lookup(
+                void *table,
+                const char *section,
+                const char *lvalue,
+                ConfigParserCallback *func,
+                int *ltype,
+                void **data,
+                void *userdata) {
+
+        ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
+        const ConfigPerfItem *p;
+
+        assert(table);
+        assert(lvalue);
+        assert(func);
+        assert(ltype);
+        assert(data);
+
+        if (!section)
+                p = lookup(lvalue, strlen(lvalue));
+        else {
+                char *key;
+
+                key = join(section, ".", lvalue, NULL);
+                if (!key)
+                        return -ENOMEM;
+
+                p = lookup(key, strlen(key));
+                free(key);
+        }
+
+        if (!p)
+                return 0;
+
+        *func = p->parse;
+        *ltype = p->ltype;
+        *data = (uint8_t*) userdata + p->offset;
+        return 1;
+}
+
+/* Run the user supplied parser for an assignment */
+static int next_assignment(
+                const char *filename,
+                unsigned line,
+                ConfigItemLookup lookup,
+                void *table,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                bool relaxed,
+                void *userdata) {
+
+        ConfigParserCallback func = NULL;
+        int ltype = 0;
+        void *data = NULL;
+        int r;
+
+        assert(filename);
+        assert(line > 0);
+        assert(lookup);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
+        if (r < 0)
+                return r;
+
+        if (r > 0) {
+                if (func)
+                        return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
+
+                return 0;
+        }
+
+        /* Warn about unknown non-extension fields. */
+        if (!relaxed && !startswith(lvalue, "X-"))
+                log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, section);
+
+        return 0;
+}
+
+/* Parse a variable assignment line */
+static int parse_line(
+                const char *filename,
+                unsigned line,
+                const char *sections,
+                ConfigItemLookup lookup,
+                void *table,
+                bool relaxed,
+                char **section,
+                char *l,
+                void *userdata) {
+
+        char *e;
+
+        assert(filename);
+        assert(line > 0);
+        assert(lookup);
+        assert(l);
+
+        l = strstrip(l);
+
+        if (!*l)
+                return 0;
+
+        if (strchr(COMMENTS, *l))
+                return 0;
+
+        if (startswith(l, ".include ")) {
+                char *fn;
+                int r;
+
+                fn = file_in_same_dir(filename, strstrip(l+9));
+                if (!fn)
+                        return -ENOMEM;
+
+                r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata);
+                free(fn);
+
+                return r;
+        }
+
+        if (*l == '[') {
+                size_t k;
+                char *n;
+
+                k = strlen(l);
+                assert(k > 0);
+
+                if (l[k-1] != ']') {
+                        log_error("[%s:%u] Invalid section header.", filename, line);
+                        return -EBADMSG;
+                }
+
+                n = strndup(l+1, k-2);
+                if (!n)
+                        return -ENOMEM;
+
+                if (sections && !nulstr_contains(sections, n)) {
+
+                        if (!relaxed)
+                                log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
+
+                        free(n);
+                        *section = NULL;
+                } else {
+                        free(*section);
+                        *section = n;
+                }
+
+                return 0;
+        }
+
+        if (sections && !*section) {
+
+                if (!relaxed)
+                        log_info("[%s:%u] Assignment outside of section. Ignoring.", filename, line);
+
+                return 0;
+        }
+
+        e = strchr(l, '=');
+        if (!e) {
+                log_error("[%s:%u] Missing '='.", filename, line);
+                return -EBADMSG;
+        }
+
+        *e = 0;
+        e++;
+
+        return next_assignment(
+                        filename,
+                        line,
+                        lookup,
+                        table,
+                        *section,
+                        strstrip(l),
+                        strstrip(e),
+                        relaxed,
+                        userdata);
+}
+
+/* Go through the file and parse each line */
+int config_parse(
+                const char *filename,
+                FILE *f,
+                const char *sections,
+                ConfigItemLookup lookup,
+                void *table,
+                bool relaxed,
+                void *userdata) {
+
+        unsigned line = 0;
+        char *section = NULL;
+        int r;
+        bool ours = false;
+        char *continuation = NULL;
+
+        assert(filename);
+        assert(lookup);
+
+        if (!f) {
+                f = fopen(filename, "re");
+                if (!f) {
+                        r = -errno;
+                        log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
+                        goto finish;
+                }
+
+                ours = true;
+        }
+
+        while (!feof(f)) {
+                char l[LINE_MAX], *p, *c = NULL, *e;
+                bool escaped = false;
+
+                if (!fgets(l, sizeof(l), f)) {
+                        if (feof(f))
+                                break;
+
+                        r = -errno;
+                        log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
+                        goto finish;
+                }
+
+                truncate_nl(l);
+
+                if (continuation) {
+                        c = strappend(continuation, l);
+                        if (!c) {
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        free(continuation);
+                        continuation = NULL;
+                        p = c;
+                } else
+                        p = l;
+
+                for (e = p; *e; e++) {
+                        if (escaped)
+                                escaped = false;
+                        else if (*e == '\\')
+                                escaped = true;
+                }
+
+                if (escaped) {
+                        *(e-1) = ' ';
+
+                        if (c)
+                                continuation = c;
+                        else {
+                                continuation = strdup(l);
+                                if (!continuation) {
+                                        r = -ENOMEM;
+                                        goto finish;
+                                }
+                        }
+
+                        continue;
+                }
+
+                r = parse_line(filename,
+                                ++line,
+                                sections,
+                                lookup,
+                                table,
+                                relaxed,
+                                &section,
+                                p,
+                                userdata);
+                free(c);
+
+                if (r < 0)
+                        goto finish;
+        }
+
+        r = 0;
+
+finish:
+        free(section);
+        free(continuation);
+
+        if (f && ours)
+                fclose(f);
+
+        return r;
+}
+
+int config_parse_int(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        int *i = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((r = safe_atoi(rvalue, i)) < 0) {
+                log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
+int config_parse_long(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        long *i = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((r = safe_atoli(rvalue, i)) < 0) {
+                log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
+int config_parse_uint64(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        uint64_t *u = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((r = safe_atou64(rvalue, u)) < 0) {
+                log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
+int config_parse_unsigned(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        unsigned *u = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((r = safe_atou(rvalue, u)) < 0) {
+                log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
+                return r;
+        }
+
+        return 0;
+}
+
+int config_parse_bytes_size(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        size_t *sz = data;
+        off_t o;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
+                log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        *sz = (size_t) o;
+        return 0;
+}
+
+
+int config_parse_bytes_off(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        off_t *bytes = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        assert_cc(sizeof(off_t) == sizeof(uint64_t));
+
+        if (parse_bytes(rvalue, bytes) < 0) {
+                log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
+int config_parse_bool(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        int k;
+        bool *b = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if ((k = parse_boolean(rvalue)) < 0) {
+                log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        *b = !!k;
+        return 0;
+}
+
+int config_parse_tristate(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        int k;
+        int *b = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
+
+        k = parse_boolean(rvalue);
+        if (k < 0) {
+                log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        *b = !!k;
+        return 0;
+}
+
+int config_parse_string(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        char **s = data;
+        char *n;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        n = cunescape(rvalue);
+        if (!n)
+                return -ENOMEM;
+
+        if (!utf8_is_valid(n)) {
+                log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+                free(n);
+                return 0;
+        }
+
+        free(*s);
+        if (*n)
+                *s = n;
+        else {
+                free(n);
+                *s = NULL;
+        }
+
+        return 0;
+}
+
+int config_parse_path(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        char **s = data;
+        char *n;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (!utf8_is_valid(rvalue)) {
+                log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        if (!path_is_absolute(rvalue)) {
+                log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        n = strdup(rvalue);
+        if (!n)
+                return -ENOMEM;
+
+        path_kill_slashes(n);
+
+        free(*s);
+        *s = n;
+
+        return 0;
+}
+
+int config_parse_strv(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        char*** sv = data;
+        char **n;
+        char *w;
+        unsigned k;
+        size_t l;
+        char *state;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        k = strv_length(*sv);
+        FOREACH_WORD_QUOTED(w, l, rvalue, state)
+                k++;
+
+        n = new(char*, k+1);
+        if (!n)
+                return -ENOMEM;
+
+        if (*sv)
+                for (k = 0; (*sv)[k]; k++)
+                        n[k] = (*sv)[k];
+        else
+                k = 0;
+
+        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+                n[k] = cunescape_length(w, l);
+                if (!n[k]) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+
+                if (!utf8_is_valid(n[k])) {
+                        log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+                        free(n[k]);
+                        continue;
+                }
+
+                k++;
+        }
+
+        n[k] = NULL;
+        free(*sv);
+        *sv = n;
+
+        return 0;
+
+fail:
+        for (; k > 0; k--)
+                free(n[k-1]);
+        free(n);
+
+        return r;
+}
+
+int config_parse_path_strv(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        char*** sv = data;
+        char **n;
+        char *w;
+        unsigned k;
+        size_t l;
+        char *state;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        k = strv_length(*sv);
+        FOREACH_WORD_QUOTED(w, l, rvalue, state)
+                k++;
+
+        n = new(char*, k+1);
+        if (!n)
+                return -ENOMEM;
+
+        k = 0;
+        if (*sv)
+                for (; (*sv)[k]; k++)
+                        n[k] = (*sv)[k];
+
+        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+                n[k] = strndup(w, l);
+                if (!n[k]) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+
+                if (!utf8_is_valid(n[k])) {
+                        log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
+                        free(n[k]);
+                        continue;
+                }
+
+                if (!path_is_absolute(n[k])) {
+                        log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
+                        free(n[k]);
+                        continue;
+                }
+
+                path_kill_slashes(n[k]);
+                k++;
+        }
+
+        n[k] = NULL;
+        free(*sv);
+        *sv = n;
+
+        return 0;
+
+fail:
+        for (; k > 0; k--)
+                free(n[k-1]);
+        free(n);
+
+        return r;
+}
+
+int config_parse_usec(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        usec_t *usec = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (parse_usec(rvalue, usec) < 0) {
+                log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
+int config_parse_mode(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        mode_t *m = data;
+        long l;
+        char *x = NULL;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        errno = 0;
+        l = strtol(rvalue, &x, 8);
+        if (!x || *x || errno) {
+                log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        if (l < 0000 || l > 07777) {
+                log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        *m = (mode_t) l;
+        return 0;
+}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
new file mode 100644
index 0000000..be7d708
--- /dev/null
+++ b/src/shared/conf-parser.h
@@ -0,0 +1,135 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooconfparserhfoo
+#define fooconfparserhfoo
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <stdbool.h>
+
+/* An abstract parser for simple, line based, shallow configuration
+ * files consisting of variable assignments only. */
+
+/* Prototype for a parser for a specific configuration setting */
+typedef int (*ConfigParserCallback)(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata);
+
+/* Wraps information for parsing a specific configuration variable, to
+ * be stored in a simple array */
+typedef struct ConfigTableItem {
+        const char *section;            /* Section */
+        const char *lvalue;             /* Name of the variable */
+        ConfigParserCallback parse;     /* Function that is called to parse the variable's value */
+        int ltype;                      /* Distinguish different variables passed to the same callback */
+        void *data;                     /* Where to store the variable's data */
+} ConfigTableItem;
+
+/* Wraps information for parsing a specific configuration variable, to
+ * ve srored in a gperf perfect hashtable */
+typedef struct ConfigPerfItem {
+        const char *section_and_lvalue; /* Section + "." + name of the variable */
+        ConfigParserCallback parse;     /* Function that is called to parse the variable's value */
+        int ltype;                      /* Distinguish different variables passed to the same callback */
+        size_t offset;                  /* Offset where to store data, from the beginning of userdata */
+} ConfigPerfItem;
+
+/* Prototype for a low-level gperf lookup function */
+typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length);
+
+/* Prototype for a generic high-level lookup function */
+typedef int (*ConfigItemLookup)(
+                void *table,
+                const char *section,
+                const char *lvalue,
+                ConfigParserCallback *func,
+                int *ltype,
+                void **data,
+                void *userdata);
+
+/* Linear table search implementation of ConfigItemLookup, based on
+ * ConfigTableItem arrays */
+int config_item_table_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
+
+/* gperf implementation of ConfigItemLookup, based on gperf
+ * ConfigPerfItem tables */
+int config_item_perf_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
+
+int config_parse(
+                const char *filename,
+                FILE *f,
+                const char *sections,  /* nulstr */
+                ConfigItemLookup lookup,
+                void *table,
+                bool relaxed,
+                void *userdata);
+
+/* Generic parsers */
+int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_long(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_uint64(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bytes_size(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bytes_off(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_tristate(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_usec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
+#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg)                \
+        int function(                                                   \
+                        const char *filename,                           \
+                        unsigned line,                                  \
+                        const char *section,                            \
+                        const char *lvalue,                             \
+                        int ltype,                                      \
+                        const char *rvalue,                             \
+                        void *data,                                     \
+                        void *userdata) {                               \
+                                                                        \
+                type *i = data, x;                                      \
+                                                                        \
+                assert(filename);                                       \
+                assert(lvalue);                                         \
+                assert(rvalue);                                         \
+                assert(data);                                           \
+                                                                        \
+                if ((x = name##_from_string(rvalue)) < 0) {             \
+                        log_error("[%s:%u] " msg ", ignoring: %s", filename, line, rvalue); \
+                        return 0;                                       \
+                }                                                       \
+                                                                        \
+                *i = x;                                                 \
+                                                                        \
+                return 0;                                               \
+        }
+
+#endif
diff --git a/src/shared/exit-status.c b/src/shared/exit-status.c
new file mode 100644
index 0000000..ab8907d
--- /dev/null
+++ b/src/shared/exit-status.c
@@ -0,0 +1,180 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+#include <sys/wait.h>
+
+#include "exit-status.h"
+
+const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
+
+        /* We cast to int here, so that -Wenum doesn't complain that
+         * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
+
+        switch ((int) status) {
+
+        case EXIT_SUCCESS:
+                return "SUCCESS";
+
+        case EXIT_FAILURE:
+                return "FAILURE";
+        }
+
+
+        if (level == EXIT_STATUS_SYSTEMD || level == EXIT_STATUS_LSB) {
+                switch ((int) status) {
+
+                case EXIT_CHDIR:
+                        return "CHDIR";
+
+                case EXIT_NICE:
+                        return "NICE";
+
+                case EXIT_FDS:
+                        return "FDS";
+
+                case EXIT_EXEC:
+                        return "EXEC";
+
+                case EXIT_MEMORY:
+                        return "MEMORY";
+
+                case EXIT_LIMITS:
+                        return "LIMITS";
+
+                case EXIT_OOM_ADJUST:
+                        return "OOM_ADJUST";
+
+                case EXIT_SIGNAL_MASK:
+                        return "SIGNAL_MASK";
+
+                case EXIT_STDIN:
+                        return "STDIN";
+
+                case EXIT_STDOUT:
+                        return "STDOUT";
+
+                case EXIT_CHROOT:
+                        return "CHROOT";
+
+                case EXIT_IOPRIO:
+                        return "IOPRIO";
+
+                case EXIT_TIMERSLACK:
+                        return "TIMERSLACK";
+
+                case EXIT_SECUREBITS:
+                        return "SECUREBITS";
+
+                case EXIT_SETSCHEDULER:
+                        return "SETSCHEDULER";
+
+                case EXIT_CPUAFFINITY:
+                        return "CPUAFFINITY";
+
+                case EXIT_GROUP:
+                        return "GROUP";
+
+                case EXIT_USER:
+                        return "USER";
+
+                case EXIT_CAPABILITIES:
+                        return "CAPABILITIES";
+
+                case EXIT_CGROUP:
+                        return "CGROUP";
+
+                case EXIT_SETSID:
+                        return "SETSID";
+
+                case EXIT_CONFIRM:
+                        return "CONFIRM";
+
+                case EXIT_STDERR:
+                        return "STDERR";
+
+                case EXIT_TCPWRAP:
+                        return "TCPWRAP";
+
+                case EXIT_PAM:
+                        return "PAM";
+
+                case EXIT_NETWORK:
+                        return "NETWORK";
+
+                case EXIT_NAMESPACE:
+                        return "NAMESPACE";
+                }
+        }
+
+        if (level == EXIT_STATUS_LSB) {
+                switch ((int) status) {
+
+                case EXIT_INVALIDARGUMENT:
+                        return "INVALIDARGUMENT";
+
+                case EXIT_NOTIMPLEMENTED:
+                        return "NOTIMPLEMENTED";
+
+                case EXIT_NOPERMISSION:
+                        return "NOPERMISSION";
+
+                case EXIT_NOTINSTALLED:
+                        return "NOTINSSTALLED";
+
+                case EXIT_NOTCONFIGURED:
+                        return "NOTCONFIGURED";
+
+                case EXIT_NOTRUNNING:
+                        return "NOTRUNNING";
+                }
+        }
+
+        return NULL;
+}
+
+
+bool is_clean_exit(int code, int status) {
+
+        if (code == CLD_EXITED)
+                return status == 0;
+
+        /* If a daemon does not implement handlers for some of the
+         * signals that's not considered an unclean shutdown */
+        if (code == CLD_KILLED)
+                return
+                        status == SIGHUP ||
+                        status == SIGINT ||
+                        status == SIGTERM ||
+                        status == SIGPIPE;
+
+        return false;
+}
+
+bool is_clean_exit_lsb(int code, int status) {
+
+        if (is_clean_exit(code, status))
+                return true;
+
+        return
+                code == CLD_EXITED &&
+                (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
+}
diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h
new file mode 100644
index 0000000..44ef879
--- /dev/null
+++ b/src/shared/exit-status.h
@@ -0,0 +1,85 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooexitstatushfoo
+#define fooexitstatushfoo
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+typedef enum ExitStatus {
+        /* EXIT_SUCCESS defined by libc */
+        /* EXIT_FAILURE defined by libc */
+        EXIT_INVALIDARGUMENT = 2,
+        EXIT_NOTIMPLEMENTED = 3,
+        EXIT_NOPERMISSION = 4,
+        EXIT_NOTINSTALLED = 5,
+        EXIT_NOTCONFIGURED = 6,
+        EXIT_NOTRUNNING = 7,
+
+        /* The LSB suggests that error codes >= 200 are "reserved". We
+         * use them here under the assumption that they hence are
+         * unused by init scripts.
+         *
+         * http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html */
+
+        EXIT_CHDIR = 200,
+        EXIT_NICE,
+        EXIT_FDS,
+        EXIT_EXEC,
+        EXIT_MEMORY,
+        EXIT_LIMITS,
+        EXIT_OOM_ADJUST,
+        EXIT_SIGNAL_MASK,
+        EXIT_STDIN,
+        EXIT_STDOUT,
+        EXIT_CHROOT,   /* 210 */
+        EXIT_IOPRIO,
+        EXIT_TIMERSLACK,
+        EXIT_SECUREBITS,
+        EXIT_SETSCHEDULER,
+        EXIT_CPUAFFINITY,
+        EXIT_GROUP,
+        EXIT_USER,
+        EXIT_CAPABILITIES,
+        EXIT_CGROUP,
+        EXIT_SETSID,   /* 220 */
+        EXIT_CONFIRM,
+        EXIT_STDERR,
+        EXIT_TCPWRAP,
+        EXIT_PAM,
+        EXIT_NETWORK,
+        EXIT_NAMESPACE
+
+} ExitStatus;
+
+typedef enum ExitStatusLevel {
+        EXIT_STATUS_MINIMAL,
+        EXIT_STATUS_SYSTEMD,
+        EXIT_STATUS_LSB,
+        EXIT_STATUS_FULL = EXIT_STATUS_LSB
+} ExitStatusLevel;
+
+const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level);
+
+bool is_clean_exit(int code, int status);
+bool is_clean_exit_lsb(int code, int status);
+
+#endif
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
new file mode 100644
index 0000000..6928118
--- /dev/null
+++ b/src/shared/hashmap.c
@@ -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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU 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/src/shared/hashmap.h b/src/shared/hashmap.h
new file mode 100644
index 0000000..ab4363a
--- /dev/null
+++ b/src/shared/hashmap.h
@@ -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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU 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/src/shared/label.c b/src/shared/label.c
new file mode 100644
index 0000000..2c887a0
--- /dev/null
+++ b/src/shared/label.c
@@ -0,0 +1,413 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "label.h"
+#include "util.h"
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+
+static struct selabel_handle *label_hnd = NULL;
+
+static int use_selinux_cached = -1;
+
+static inline bool use_selinux(void) {
+
+        if (use_selinux_cached < 0)
+                use_selinux_cached = is_selinux_enabled() > 0;
+
+        return use_selinux_cached;
+}
+
+void label_retest_selinux(void) {
+        use_selinux_cached = -1;
+}
+
+#endif
+
+int label_init(void) {
+        int r = 0;
+
+#ifdef HAVE_SELINUX
+        usec_t before_timestamp, after_timestamp;
+        struct mallinfo before_mallinfo, after_mallinfo;
+
+        if (!use_selinux())
+                return 0;
+
+        if (label_hnd)
+                return 0;
+
+        before_mallinfo = mallinfo();
+        before_timestamp = now(CLOCK_MONOTONIC);
+
+        label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
+        if (!label_hnd) {
+                log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+                         "Failed to initialize SELinux context: %m");
+                r = security_getenforce() == 1 ? -errno : 0;
+        } else  {
+                char timespan[FORMAT_TIMESPAN_MAX];
+                int l;
+
+                after_timestamp = now(CLOCK_MONOTONIC);
+                after_mallinfo = mallinfo();
+
+                l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
+
+                log_info("Successfully loaded SELinux database in %s, size on heap is %iK.",
+                         format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp),
+                         (l+1023)/1024);
+        }
+#endif
+
+        return r;
+}
+
+int label_fix(const char *path, bool ignore_enoent) {
+        int r = 0;
+
+#ifdef HAVE_SELINUX
+        struct stat st;
+        security_context_t fcon;
+
+        if (!use_selinux() || !label_hnd)
+                return 0;
+
+        r = lstat(path, &st);
+        if (r == 0) {
+                r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode);
+
+                /* If there's no label to set, then exit without warning */
+                if (r < 0 && errno == ENOENT)
+                        return 0;
+
+                if (r == 0) {
+                        r = lsetfilecon(path, fcon);
+                        freecon(fcon);
+
+                        /* If the FS doesn't support labels, then exit without warning */
+                        if (r < 0 && errno == ENOTSUP)
+                                return 0;
+                }
+        }
+
+        if (r < 0) {
+                /* Ignore ENOENT in some cases */
+                if (ignore_enoent && errno == ENOENT)
+                        return 0;
+
+                log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+                         "Unable to fix label of %s: %m", path);
+                r = security_getenforce() == 1 ? -errno : 0;
+        }
+#endif
+
+        return r;
+}
+
+void label_finish(void) {
+
+#ifdef HAVE_SELINUX
+        if (use_selinux() && label_hnd)
+                selabel_close(label_hnd);
+#endif
+}
+
+int label_get_create_label_from_exe(const char *exe, char **label) {
+
+        int r = 0;
+
+#ifdef HAVE_SELINUX
+        security_context_t mycon = NULL, fcon = NULL;
+        security_class_t sclass;
+
+        if (!use_selinux()) {
+                *label = NULL;
+                return 0;
+        }
+
+        r = getcon(&mycon);
+        if (r < 0)
+                goto fail;
+
+        r = getfilecon(exe, &fcon);
+        if (r < 0)
+                goto fail;
+
+        sclass = string_to_security_class("process");
+        r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label);
+        if (r == 0)
+                log_debug("SELinux Socket context for %s will be set to %s", exe, *label);
+
+fail:
+        if (r < 0 && security_getenforce() == 1)
+                r = -errno;
+
+        freecon(mycon);
+        freecon(fcon);
+#endif
+
+        return r;
+}
+
+int label_fifofile_set(const char *path) {
+        int r = 0;
+
+#ifdef HAVE_SELINUX
+        security_context_t filecon = NULL;
+
+        if (!use_selinux() || !label_hnd)
+                return 0;
+
+        r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFIFO);
+        if (r < 0)
+                r = -errno;
+        else if (r == 0) {
+                r = setfscreatecon(filecon);
+                if (r < 0) {
+                        log_error("Failed to set SELinux file context on %s: %m", path);
+                        r = -errno;
+                }
+
+                freecon(filecon);
+        }
+
+        if (r < 0 && security_getenforce() == 0)
+                r = 0;
+#endif
+
+        return r;
+}
+
+int label_symlinkfile_set(const char *path) {
+        int r = 0;
+
+#ifdef HAVE_SELINUX
+        security_context_t filecon = NULL;
+
+        if (!use_selinux() || !label_hnd)
+                return 0;
+
+        r = selabel_lookup_raw(label_hnd, &filecon, path, S_IFLNK);
+        if (r < 0)
+                r = -errno;
+        else if (r == 0) {
+                r = setfscreatecon(filecon);
+                if (r < 0) {
+                        log_error("Failed to set SELinux file context on %s: %m", path);
+                        r = -errno;
+                }
+
+                freecon(filecon);
+        }
+
+        if (r < 0 && security_getenforce() == 0)
+                r = 0;
+#endif
+
+        return r;
+}
+
+int label_socket_set(const char *label) {
+
+#ifdef HAVE_SELINUX
+        if (!use_selinux())
+                return 0;
+
+        if (setsockcreatecon((security_context_t) label) < 0) {
+                log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG,
+                         "Failed to set SELinux context (%s) on socket: %m", label);
+
+                if (security_getenforce() == 1)
+                        return -errno;
+        }
+#endif
+
+        return 0;
+}
+
+void label_file_clear(void) {
+
+#ifdef HAVE_SELINUX
+        if (!use_selinux())
+                return;
+
+        setfscreatecon(NULL);
+#endif
+}
+
+void label_socket_clear(void) {
+
+#ifdef HAVE_SELINUX
+        if (!use_selinux())
+                return;
+
+        setsockcreatecon(NULL);
+#endif
+}
+
+void label_free(const char *label) {
+
+#ifdef HAVE_SELINUX
+        if (!use_selinux())
+                return;
+
+        freecon((security_context_t) label);
+#endif
+}
+
+int label_mkdir(const char *path, mode_t mode) {
+
+        /* Creates a directory and labels it according to the SELinux policy */
+
+#ifdef HAVE_SELINUX
+        int r;
+        security_context_t fcon = NULL;
+
+        if (!use_selinux() || !label_hnd)
+                goto skipped;
+
+        if (path_is_absolute(path))
+                r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFDIR);
+        else {
+                char *newpath;
+
+                newpath = path_make_absolute_cwd(path);
+                if (!newpath)
+                        return -ENOMEM;
+
+                r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFDIR);
+                free(newpath);
+        }
+
+        if (r == 0)
+                r = setfscreatecon(fcon);
+
+        if (r < 0 && errno != ENOENT) {
+                log_error("Failed to set security context %s for %s: %m", fcon, path);
+
+                if (security_getenforce() == 1) {
+                        r = -errno;
+                        goto finish;
+                }
+        }
+
+        r = mkdir(path, mode);
+        if (r < 0)
+                r = -errno;
+
+finish:
+        setfscreatecon(NULL);
+        freecon(fcon);
+
+        return r;
+
+skipped:
+#endif
+        return mkdir(path, mode) < 0 ? -errno : 0;
+}
+
+int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
+
+        /* Binds a socket and label its file system object according to the SELinux policy */
+
+#ifdef HAVE_SELINUX
+        int r;
+        security_context_t fcon = NULL;
+        const struct sockaddr_un *un;
+        char *path = NULL;
+
+        assert(fd >= 0);
+        assert(addr);
+        assert(addrlen >= sizeof(sa_family_t));
+
+        if (!use_selinux() || !label_hnd)
+                goto skipped;
+
+        /* Filter out non-local sockets */
+        if (addr->sa_family != AF_UNIX)
+                goto skipped;
+
+        /* Filter out anonymous sockets */
+        if (addrlen < sizeof(sa_family_t) + 1)
+                goto skipped;
+
+        /* Filter out abstract namespace sockets */
+        un = (const struct sockaddr_un*) addr;
+        if (un->sun_path[0] == 0)
+                goto skipped;
+
+        path = strndup(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
+        if (!path)
+                return -ENOMEM;
+
+        if (path_is_absolute(path))
+                r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
+        else {
+                char *newpath;
+
+                newpath = path_make_absolute_cwd(path);
+
+                if (!newpath) {
+                        free(path);
+                        return -ENOMEM;
+                }
+
+                r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK);
+                free(newpath);
+        }
+
+        if (r == 0)
+                r = setfscreatecon(fcon);
+
+        if (r < 0 && errno != ENOENT) {
+                log_error("Failed to set security context %s for %s: %m", fcon, path);
+
+                if (security_getenforce() == 1) {
+                        r = -errno;
+                        goto finish;
+                }
+        }
+
+        r = bind(fd, addr, addrlen);
+        if (r < 0)
+                r = -errno;
+
+finish:
+        setfscreatecon(NULL);
+        freecon(fcon);
+        free(path);
+
+        return r;
+
+skipped:
+#endif
+        return bind(fd, addr, addrlen) < 0 ? -errno : 0;
+}
diff --git a/src/shared/label.h b/src/shared/label.h
new file mode 100644
index 0000000..ead4483
--- /dev/null
+++ b/src/shared/label.h
@@ -0,0 +1,51 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foolabelhfoo
+#define foolabelhfoo
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+int label_init(void);
+void label_finish(void);
+
+int label_fix(const char *path, bool ignore_enoent);
+
+int label_socket_set(const char *label);
+void label_socket_clear(void);
+
+int label_fifofile_set(const char *path);
+int label_symlinkfile_set(const char *path);
+void label_file_clear(void);
+
+void label_free(const char *label);
+
+int label_get_create_label_from_exe(const char *exe, char **label);
+
+int label_mkdir(const char *path, mode_t mode);
+
+void label_retest_selinux(void);
+
+int label_bind(int fd, const struct sockaddr *addr, socklen_t addrlen);
+
+#endif
diff --git a/src/shared/log.c b/src/shared/log.c
new file mode 100644
index 0000000..9fffc1d
--- /dev/null
+++ b/src/shared/log.c
@@ -0,0 +1,747 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU 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"
+#include "socket-util.h"
+
+#define SNDBUF_SIZE (8*1024*1024)
+
+static LogTarget log_target = LOG_TARGET_CONSOLE;
+static int log_max_level = LOG_INFO;
+static int log_facility = LOG_DAEMON;
+
+static int console_fd = STDERR_FILENO;
+static int syslog_fd = -1;
+static int kmsg_fd = -1;
+static int journal_fd = -1;
+
+static bool syslog_is_stream = false;
+
+static bool show_color = false;
+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;
+}
+
+void log_close_kmsg(void) {
+
+        if (kmsg_fd < 0)
+                return;
+
+        close_nointr_nofail(kmsg_fd);
+        kmsg_fd = -1;
+}
+
+static int log_open_kmsg(void) {
+
+        if (kmsg_fd >= 0)
+                return 0;
+
+        kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+        if (kmsg_fd < 0) {
+                log_error("Failed to open /dev/kmsg for logging: %s", strerror(errno));
+                return -errno;
+        }
+
+        log_debug("Successfully opened /dev/kmsg for logging.");
+
+        return 0;
+}
+
+void log_close_syslog(void) {
+
+        if (syslog_fd < 0)
+                return;
+
+        close_nointr_nofail(syslog_fd);
+        syslog_fd = -1;
+}
+
+static int create_log_socket(int type) {
+        int fd;
+
+        /* All output to the syslog/journal fds we do asynchronously,
+         * and if the buffers are full we just drop the messages */
+
+        fd = socket(AF_UNIX, type|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
+        if (fd < 0)
+                return -errno;
+
+        fd_inc_sndbuf(fd, SNDBUF_SIZE);
+
+        return fd;
+}
+
+static int log_open_syslog(void) {
+        union sockaddr_union sa;
+        int r;
+
+        if (syslog_fd >= 0)
+                return 0;
+
+        zero(sa);
+        sa.un.sun_family = AF_UNIX;
+        strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
+
+        syslog_fd = create_log_socket(SOCK_DGRAM);
+        if (syslog_fd < 0) {
+                r = syslog_fd;
+                goto fail;
+        }
+
+        if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+                close_nointr_nofail(syslog_fd);
+
+                /* Some legacy syslog systems still use stream
+                 * sockets. They really shouldn't. But what can we
+                 * do... */
+                syslog_fd = create_log_socket(SOCK_STREAM);
+                if (syslog_fd < 0) {
+                        r = syslog_fd;
+                        goto fail;
+                }
+
+                if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+
+                syslog_is_stream = true;
+        } else
+                syslog_is_stream = false;
+
+        log_debug("Successfully opened syslog for logging.");
+
+        return 0;
+
+fail:
+        log_close_syslog();
+        log_debug("Failed to open syslog for logging: %s", strerror(-r));
+        return r;
+}
+
+void log_close_journal(void) {
+
+        if (journal_fd < 0)
+                return;
+
+        close_nointr_nofail(journal_fd);
+        journal_fd = -1;
+}
+
+static int log_open_journal(void) {
+        union sockaddr_union sa;
+        int r;
+
+        if (journal_fd >= 0)
+                return 0;
+
+        journal_fd = create_log_socket(SOCK_DGRAM);
+        if (journal_fd < 0) {
+                r = journal_fd;
+                goto fail;
+        }
+
+        zero(sa);
+        sa.un.sun_family = AF_UNIX;
+        strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
+
+        if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+                r = -errno;
+                goto fail;
+        }
+
+        log_debug("Successfully opened journal for logging.");
+
+        return 0;
+
+fail:
+        log_close_journal();
+        log_debug("Failed to open journal for logging: %s", strerror(-r));
+        return r;
+}
+
+int log_open(void) {
+        int r;
+
+        /* If we don't use the console we close it here, to not get
+         * killed by SAK. If we don't use syslog we close it here so
+         * that we are not confused by somebody deleting the socket in
+         * the fs. If we don't use /dev/kmsg we still keep it open,
+         * because there is no reason to close it. */
+
+        if (log_target == LOG_TARGET_NULL) {
+                log_close_journal();
+                log_close_syslog();
+                log_close_console();
+                return 0;
+        }
+
+        if (log_target != LOG_TARGET_AUTO ||
+            getpid() == 1 ||
+            isatty(STDERR_FILENO) <= 0) {
+
+                if (log_target == LOG_TARGET_AUTO ||
+                    log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+                    log_target == LOG_TARGET_JOURNAL) {
+                        r = log_open_journal();
+                        if (r >= 0) {
+                                log_close_syslog();
+                                log_close_console();
+                                return r;
+                        }
+                }
+
+                if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+                    log_target == LOG_TARGET_SYSLOG) {
+                        r = log_open_syslog();
+                        if (r >= 0) {
+                                log_close_journal();
+                                log_close_console();
+                                return r;
+                        }
+                }
+
+                if (log_target == LOG_TARGET_AUTO ||
+                    log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+                    log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+                    log_target == LOG_TARGET_KMSG) {
+                        r = log_open_kmsg();
+                        if (r >= 0) {
+                                log_close_journal();
+                                log_close_syslog();
+                                log_close_console();
+                                return r;
+                        }
+                }
+        }
+
+        log_close_journal();
+        log_close_syslog();
+
+        /* Get the real /dev/console if we are PID=1, hence reopen */
+        log_close_console();
+        return log_open_console();
+}
+
+void log_set_target(LogTarget target) {
+        assert(target >= 0);
+        assert(target < _LOG_TARGET_MAX);
+
+        log_target = target;
+}
+
+void log_close(void) {
+        log_close_journal();
+        log_close_syslog();
+        log_close_kmsg();
+        log_close_console();
+}
+
+void log_forget_fds(void) {
+        console_fd = kmsg_fd = syslog_fd = journal_fd = -1;
+}
+
+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;
+        bool highlight;
+
+        if (console_fd < 0)
+                return 0;
+
+        highlight = LOG_PRI(level) <= LOG_ERR && show_color;
+
+        zero(iovec);
+
+        if (show_location) {
+                snprintf(location, sizeof(location), "(%s:%u) ", file, line);
+                char_array_0(location);
+                IOVEC_SET_STRING(iovec[n++], location);
+        }
+
+        if (highlight)
+                IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED_ON);
+        IOVEC_SET_STRING(iovec[n++], buffer);
+        if (highlight)
+                IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF);
+        IOVEC_SET_STRING(iovec[n++], "\n");
+
+        if (writev(console_fd, iovec, n) < 0)
+                return -errno;
+
+        return 1;
+}
+
+static int write_to_syslog(
+        int level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *buffer) {
+
+        char header_priority[16], header_time[64], header_pid[16];
+        struct iovec iovec[5];
+        struct msghdr msghdr;
+        time_t t;
+        struct tm *tm;
+
+        if (syslog_fd < 0)
+                return 0;
+
+        snprintf(header_priority, sizeof(header_priority), "<%i>", level);
+        char_array_0(header_priority);
+
+        t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC);
+        if (!(tm = localtime(&t)))
+                return -EINVAL;
+
+        if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0)
+                return -EINVAL;
+
+        snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
+        char_array_0(header_pid);
+
+        zero(iovec);
+        IOVEC_SET_STRING(iovec[0], header_priority);
+        IOVEC_SET_STRING(iovec[1], header_time);
+        IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
+        IOVEC_SET_STRING(iovec[3], header_pid);
+        IOVEC_SET_STRING(iovec[4], buffer);
+
+        /* When using syslog via SOCK_STREAM separate the messages by NUL chars */
+        if (syslog_is_stream)
+                iovec[4].iov_len++;
+
+        zero(msghdr);
+        msghdr.msg_iov = iovec;
+        msghdr.msg_iovlen = ELEMENTSOF(iovec);
+
+        for (;;) {
+                ssize_t n;
+
+                n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL);
+                if (n < 0)
+                        return -errno;
+
+                if (!syslog_is_stream ||
+                    (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec)))
+                        break;
+
+                IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n);
+        }
+
+        return 1;
+}
+
+static int write_to_kmsg(
+        int level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *buffer) {
+
+        char header_priority[16], header_pid[16];
+        struct iovec iovec[5];
+
+        if (kmsg_fd < 0)
+                return 0;
+
+        snprintf(header_priority, sizeof(header_priority), "<%i>", level);
+        char_array_0(header_priority);
+
+        snprintf(header_pid, sizeof(header_pid), "[%lu]: ", (unsigned long) getpid());
+        char_array_0(header_pid);
+
+        zero(iovec);
+        IOVEC_SET_STRING(iovec[0], header_priority);
+        IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
+        IOVEC_SET_STRING(iovec[2], header_pid);
+        IOVEC_SET_STRING(iovec[3], buffer);
+        IOVEC_SET_STRING(iovec[4], "\n");
+
+        if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0)
+                return -errno;
+
+        return 1;
+}
+
+static int write_to_journal(
+        int level,
+        const char*file,
+        int line,
+        const char *func,
+        const char *buffer) {
+
+        char header[LINE_MAX];
+        struct iovec iovec[3];
+        struct msghdr mh;
+
+        if (journal_fd < 0)
+                return 0;
+
+        snprintf(header, sizeof(header),
+                 "PRIORITY=%i\n"
+                 "SYSLOG_FACILITY=%i\n"
+                 "CODE_FILE=%s\n"
+                 "CODE_LINE=%i\n"
+                 "CODE_FUNCTION=%s\n"
+                 "MESSAGE=",
+                 LOG_PRI(level),
+                 LOG_FAC(level),
+                 file,
+                 line,
+                 func);
+
+        char_array_0(header);
+
+        zero(iovec);
+        IOVEC_SET_STRING(iovec[0], header);
+        IOVEC_SET_STRING(iovec[1], buffer);
+        IOVEC_SET_STRING(iovec[2], "\n");
+
+        zero(mh);
+        mh.msg_iov = iovec;
+        mh.msg_iovlen = ELEMENTSOF(iovec);
+
+        if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 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;
+
+                if (log_target == LOG_TARGET_AUTO ||
+                    log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
+                    log_target == LOG_TARGET_JOURNAL) {
+
+                        k = write_to_journal(level, file, line, func, buffer);
+                        if (k < 0) {
+                                if (k != -EAGAIN)
+                                        log_close_journal();
+                                log_open_kmsg();
+                        } else if (k > 0)
+                                r++;
+                }
+
+                if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+                    log_target == LOG_TARGET_SYSLOG) {
+
+                        k = write_to_syslog(level, file, line, func, buffer);
+                        if (k < 0) {
+                                if (k != -EAGAIN)
+                                        log_close_syslog();
+                                log_open_kmsg();
+                        } else if (k > 0)
+                                r++;
+                }
+
+                if (k <= 0 &&
+                    (log_target == LOG_TARGET_AUTO ||
+                     log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
+                     log_target == LOG_TARGET_KMSG)) {
+
+                        k = write_to_kmsg(level, file, line, func, buffer);
+                        if (k < 0) {
+                                log_close_kmsg();
+                                log_open_console();
+                        } else if (k > 0)
+                                r++;
+                }
+
+                if (k <= 0) {
+                        k = write_to_console(level, file, line, func, buffer);
+                        if (k < 0)
+                                return k;
+                }
+
+                buffer = e;
+        } while (buffer);
+
+        return r;
+}
+
+int log_dump_internal(
+        int level,
+        const char*file,
+        int line,
+        const char *func,
+        char *buffer) {
+
+        int saved_errno, r;
+
+        /* This modifies the buffer... */
+
+        if (_likely_(LOG_PRI(level) > log_max_level))
+                return 0;
+
+        saved_errno = errno;
+        r = log_dispatch(level, file, line, func, buffer);
+        errno = saved_errno;
+
+        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.");
+}
+
+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("SYSTEMD_LOG_TARGET")))
+                if (log_set_target_from_string(e) < 0)
+                        log_warning("Failed to parse log target %s. Ignoring.", e);
+
+        if ((e = getenv("SYSTEMD_LOG_LEVEL")))
+                if (log_set_max_level_from_string(e) < 0)
+                        log_warning("Failed to parse log level %s. Ignoring.", e);
+
+        if ((e = getenv("SYSTEMD_LOG_COLOR")))
+                if (log_show_color_from_string(e) < 0)
+                        log_warning("Failed to parse bool %s. Ignoring.", e);
+
+        if ((e = getenv("SYSTEMD_LOG_LOCATION")))
+                if (log_show_location_from_string(e) < 0)
+                        log_warning("Failed to parse bool %s. Ignoring.", e);
+}
+
+LogTarget log_get_target(void) {
+        return log_target;
+}
+
+int log_get_max_level(void) {
+        return log_max_level;
+}
+
+void log_show_color(bool b) {
+        show_color = b;
+}
+
+void log_show_location(bool b) {
+        show_location = b;
+}
+
+int log_show_color_from_string(const char *e) {
+        int t;
+
+        t = parse_boolean(e);
+        if (t < 0)
+                return t;
+
+        log_show_color(t);
+        return 0;
+}
+
+int log_show_location_from_string(const char *e) {
+        int t;
+
+        t = parse_boolean(e);
+        if (t < 0)
+                return t;
+
+        log_show_location(t);
+        return 0;
+}
+
+static const char *const log_target_table[] = {
+        [LOG_TARGET_CONSOLE] = "console",
+        [LOG_TARGET_KMSG] = "kmsg",
+        [LOG_TARGET_JOURNAL] = "journal",
+        [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg",
+        [LOG_TARGET_SYSLOG] = "syslog",
+        [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
+        [LOG_TARGET_AUTO] = "auto",
+        [LOG_TARGET_NULL] = "null"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
diff --git a/src/shared/log.h b/src/shared/log.h
new file mode 100644
index 0000000..3283808
--- /dev/null
+++ b/src/shared/log.h
@@ -0,0 +1,111 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU 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_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);
+
+#endif
diff --git a/src/shared/ratelimit.c b/src/shared/ratelimit.c
new file mode 100644
index 0000000..93157c7
--- /dev/null
+++ b/src/shared/ratelimit.c
@@ -0,0 +1,57 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+
+#include "ratelimit.h"
+#include "log.h"
+
+/* Modelled after Linux' lib/ratelimit.c by Dave Young
+ * <hidave.darkstar at gmail.com>, which is licensed GPLv2. */
+
+bool ratelimit_test(RateLimit *r) {
+        usec_t ts;
+
+        assert(r);
+
+        if (r->interval <= 0 || r->burst <= 0)
+                return true;
+
+        ts = now(CLOCK_MONOTONIC);
+
+        if (r->begin <= 0 ||
+            r->begin + r->interval < ts) {
+                r->begin = ts;
+
+                /* Reset counter */
+                r->num = 0;
+                goto good;
+        }
+
+        if (r->num <= r->burst)
+                goto good;
+
+        return false;
+
+good:
+        r->num++;
+        return true;
+}
diff --git a/src/shared/ratelimit.h b/src/shared/ratelimit.h
new file mode 100644
index 0000000..a6443e7
--- /dev/null
+++ b/src/shared/ratelimit.h
@@ -0,0 +1,53 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooratelimithfoo
+#define fooratelimithfoo
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+
+typedef struct RateLimit {
+        usec_t interval;
+        usec_t begin;
+        unsigned burst;
+        unsigned num;
+} RateLimit;
+
+#define RATELIMIT_DEFINE(_name, _interval, _burst)       \
+        RateLimit _name = {                              \
+                .interval = (_interval),                 \
+                .burst = (_burst),                       \
+                .num = 0,                                \
+                .begin = 0                               \
+        }
+
+#define RATELIMIT_INIT(v, _interval, _burst)             \
+        do {                                             \
+                RateLimit *_r = &(v);                    \
+                _r->interval = (_interval);              \
+                _r->burst = (_burst);                    \
+                _r->num = 0;                             \
+                _r->begin = 0;                           \
+        } while (false)
+
+bool ratelimit_test(RateLimit *r);
+
+#endif
diff --git a/src/shared/set.c b/src/shared/set.c
new file mode 100644
index 0000000..097b9d3
--- /dev/null
+++ b/src/shared/set.c
@@ -0,0 +1,118 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdlib.h>
+
+#include "set.h"
+#include "hashmap.h"
+
+#define MAKE_SET(h) ((Set*) (h))
+#define MAKE_HASHMAP(s) ((Hashmap*) (s))
+
+/* For now this is not much more than a wrapper around a hashmap */
+
+Set *set_new(hash_func_t hash_func, compare_func_t compare_func) {
+        return MAKE_SET(hashmap_new(hash_func, compare_func));
+}
+
+void set_free(Set* s) {
+        hashmap_free(MAKE_HASHMAP(s));
+}
+
+void set_free_free(Set *s) {
+        hashmap_free_free(MAKE_HASHMAP(s));
+}
+
+int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func) {
+        return hashmap_ensure_allocated((Hashmap**) s, hash_func, compare_func);
+}
+
+int set_put(Set *s, void *value) {
+        return hashmap_put(MAKE_HASHMAP(s), value, value);
+}
+
+int set_replace(Set *s, void *value) {
+        return hashmap_replace(MAKE_HASHMAP(s), value, value);
+}
+
+void *set_get(Set *s, void *value) {
+        return hashmap_get(MAKE_HASHMAP(s), value);
+}
+
+void *set_remove(Set *s, void *value) {
+        return hashmap_remove(MAKE_HASHMAP(s), value);
+}
+
+int set_remove_and_put(Set *s, void *old_value, void *new_value) {
+        return hashmap_remove_and_put(MAKE_HASHMAP(s), old_value, new_value, new_value);
+}
+
+unsigned set_size(Set *s) {
+        return hashmap_size(MAKE_HASHMAP(s));
+}
+
+bool set_isempty(Set *s) {
+        return hashmap_isempty(MAKE_HASHMAP(s));
+}
+
+void *set_iterate(Set *s, Iterator *i) {
+        return hashmap_iterate(MAKE_HASHMAP(s), i, NULL);
+}
+
+void *set_iterate_backwards(Set *s, Iterator *i) {
+        return hashmap_iterate_backwards(MAKE_HASHMAP(s), i, NULL);
+}
+
+void *set_iterate_skip(Set *s, void *value, Iterator *i) {
+        return hashmap_iterate_skip(MAKE_HASHMAP(s), value, i);
+}
+
+void *set_steal_first(Set *s) {
+        return hashmap_steal_first(MAKE_HASHMAP(s));
+}
+
+void* set_first(Set *s) {
+        return hashmap_first(MAKE_HASHMAP(s));
+}
+
+void* set_last(Set *s) {
+        return hashmap_last(MAKE_HASHMAP(s));
+}
+
+int set_merge(Set *s, Set *other) {
+        return hashmap_merge(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
+}
+
+void set_move(Set *s, Set *other) {
+        return hashmap_move(MAKE_HASHMAP(s), MAKE_HASHMAP(other));
+}
+
+int set_move_one(Set *s, Set *other, void *value) {
+        return hashmap_move_one(MAKE_HASHMAP(s), MAKE_HASHMAP(other), value);
+}
+
+Set* set_copy(Set *s) {
+        return MAKE_SET(hashmap_copy(MAKE_HASHMAP(s)));
+}
+
+void set_clear(Set *s) {
+        hashmap_clear(MAKE_HASHMAP(s));
+}
diff --git a/src/shared/set.h b/src/shared/set.h
new file mode 100644
index 0000000..885780c
--- /dev/null
+++ b/src/shared/set.h
@@ -0,0 +1,69 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosethfoo
+#define foosethfoo
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* Pretty straightforward set implementation. Internally based on the
+ * hashmap. That means that as a minor optimization a NULL set
+ * object will be treated as empty set for all read
+ * operations. That way it is not necessary to instantiate an object
+ * for each set use. */
+
+#include "hashmap.h"
+
+typedef struct Set Set;
+
+Set *set_new(hash_func_t hash_func, compare_func_t compare_func);
+void set_free(Set* s);
+void set_free_free(Set *s);
+Set* set_copy(Set *s);
+int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_func);
+
+int set_put(Set *s, void *value);
+int set_replace(Set *s, void *value);
+void *set_get(Set *s, void *value);
+void *set_remove(Set *s, void *value);
+int set_remove_and_put(Set *s, void *old_value, void *new_value);
+
+int set_merge(Set *s, Set *other);
+void set_move(Set *s, Set *other);
+int set_move_one(Set *s, Set *other, void *value);
+
+unsigned set_size(Set *s);
+bool set_isempty(Set *s);
+
+void *set_iterate(Set *s, Iterator *i);
+void *set_iterate_backwards(Set *s, Iterator *i);
+void *set_iterate_skip(Set *s, void *value, Iterator *i);
+
+void set_clear(Set *s);
+void *set_steal_first(Set *s);
+void* set_first(Set *s);
+void* set_last(Set *s);
+
+#define SET_FOREACH(e, s, i) \
+        for ((i) = ITERATOR_FIRST, (e) = set_iterate((s), &(i)); (e); (e) = set_iterate((s), &(i)))
+
+#define SET_FOREACH_BACKWARDS(e, s, i) \
+        for ((i) = ITERATOR_LAST, (e) = set_iterate_backwards((s), &(i)); (e); (e) = set_iterate_backwards((s), &(i)))
+
+#endif
diff --git a/src/shared/socket-util.c b/src/shared/socket-util.c
new file mode 100644
index 0000000..acc4d33
--- /dev/null
+++ b/src/shared/socket-util.c
@@ -0,0 +1,652 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <net/if.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stddef.h>
+#include <sys/ioctl.h>
+
+#include "macro.h"
+#include "util.h"
+#include "socket-util.h"
+#include "missing.h"
+#include "label.h"
+
+int socket_address_parse(SocketAddress *a, const char *s) {
+        int r;
+        char *e, *n;
+        unsigned u;
+
+        assert(a);
+        assert(s);
+
+        zero(*a);
+        a->type = SOCK_STREAM;
+
+        if (*s == '[') {
+                /* IPv6 in [x:.....:z]:p notation */
+
+                if (!socket_ipv6_is_supported()) {
+                        log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
+                        return -EAFNOSUPPORT;
+                }
+
+                if (!(e = strchr(s+1, ']')))
+                        return -EINVAL;
+
+                if (!(n = strndup(s+1, e-s-1)))
+                        return -ENOMEM;
+
+                errno = 0;
+                if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) {
+                        free(n);
+                        return errno != 0 ? -errno : -EINVAL;
+                }
+
+                free(n);
+
+                e++;
+                if (*e != ':')
+                        return -EINVAL;
+
+                e++;
+                if ((r = safe_atou(e, &u)) < 0)
+                        return r;
+
+                if (u <= 0 || u > 0xFFFF)
+                        return -EINVAL;
+
+                a->sockaddr.in6.sin6_family = AF_INET6;
+                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                a->size = sizeof(struct sockaddr_in6);
+
+        } else if (*s == '/') {
+                /* AF_UNIX socket */
+
+                size_t l;
+
+                l = strlen(s);
+                if (l >= sizeof(a->sockaddr.un.sun_path))
+                        return -EINVAL;
+
+                a->sockaddr.un.sun_family = AF_UNIX;
+                memcpy(a->sockaddr.un.sun_path, s, l);
+                a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
+
+        } else if (*s == '@') {
+                /* Abstract AF_UNIX socket */
+                size_t l;
+
+                l = strlen(s+1);
+                if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
+                        return -EINVAL;
+
+                a->sockaddr.un.sun_family = AF_UNIX;
+                memcpy(a->sockaddr.un.sun_path+1, s+1, l);
+                a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+
+        } else {
+
+                if ((e = strchr(s, ':'))) {
+
+                        if ((r = safe_atou(e+1, &u)) < 0)
+                                return r;
+
+                        if (u <= 0 || u > 0xFFFF)
+                                return -EINVAL;
+
+                        if (!(n = strndup(s, e-s)))
+                                return -ENOMEM;
+
+                        /* IPv4 in w.x.y.z:p notation? */
+                        if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) {
+                                free(n);
+                                return -errno;
+                        }
+
+                        if (r > 0) {
+                                /* Gotcha, it's a traditional IPv4 address */
+                                free(n);
+
+                                a->sockaddr.in4.sin_family = AF_INET;
+                                a->sockaddr.in4.sin_port = htons((uint16_t) u);
+                                a->size = sizeof(struct sockaddr_in);
+                        } else {
+                                unsigned idx;
+
+                                if (strlen(n) > IF_NAMESIZE-1) {
+                                        free(n);
+                                        return -EINVAL;
+                                }
+
+                                /* Uh, our last resort, an interface name */
+                                idx = if_nametoindex(n);
+                                free(n);
+
+                                if (idx == 0)
+                                        return -EINVAL;
+
+                                if (!socket_ipv6_is_supported()) {
+                                        log_warning("Binding to interface is not available since kernel does not support IPv6.");
+                                        return -EAFNOSUPPORT;
+                                }
+
+                                a->sockaddr.in6.sin6_family = AF_INET6;
+                                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                                a->sockaddr.in6.sin6_scope_id = idx;
+                                a->sockaddr.in6.sin6_addr = in6addr_any;
+                                a->size = sizeof(struct sockaddr_in6);
+                        }
+                } else {
+
+                        /* Just a port */
+                        if ((r = safe_atou(s, &u)) < 0)
+                                return r;
+
+                        if (u <= 0 || u > 0xFFFF)
+                                return -EINVAL;
+
+                        if (socket_ipv6_is_supported()) {
+                                a->sockaddr.in6.sin6_family = AF_INET6;
+                                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
+                                a->sockaddr.in6.sin6_addr = in6addr_any;
+                                a->size = sizeof(struct sockaddr_in6);
+                        } else {
+                                a->sockaddr.in4.sin_family = AF_INET;
+                                a->sockaddr.in4.sin_port = htons((uint16_t) u);
+                                a->sockaddr.in4.sin_addr.s_addr = INADDR_ANY;
+                                a->size = sizeof(struct sockaddr_in);
+                        }
+                }
+        }
+
+        return 0;
+}
+
+int socket_address_parse_netlink(SocketAddress *a, const char *s) {
+        int family;
+        unsigned group = 0;
+        char* sfamily = NULL;
+        assert(a);
+        assert(s);
+
+        zero(*a);
+        a->type = SOCK_RAW;
+
+        errno = 0;
+        if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
+                return errno ? -errno : -EINVAL;
+
+        if ((family = netlink_family_from_string(sfamily)) < 0)
+                if (safe_atoi(sfamily, &family) < 0) {
+                        free(sfamily);
+                        return -EINVAL;
+                }
+
+        free(sfamily);
+
+        a->sockaddr.nl.nl_family = AF_NETLINK;
+        a->sockaddr.nl.nl_groups = group;
+
+        a->type = SOCK_RAW;
+        a->size = sizeof(struct sockaddr_nl);
+        a->protocol = family;
+
+        return 0;
+}
+
+int socket_address_verify(const SocketAddress *a) {
+        assert(a);
+
+        switch (socket_address_family(a)) {
+
+        case AF_INET:
+                if (a->size != sizeof(struct sockaddr_in))
+                        return -EINVAL;
+
+                if (a->sockaddr.in4.sin_port == 0)
+                        return -EINVAL;
+
+                if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+                        return -EINVAL;
+
+                return 0;
+
+        case AF_INET6:
+                if (a->size != sizeof(struct sockaddr_in6))
+                        return -EINVAL;
+
+                if (a->sockaddr.in6.sin6_port == 0)
+                        return -EINVAL;
+
+                if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+                        return -EINVAL;
+
+                return 0;
+
+        case AF_UNIX:
+                if (a->size < offsetof(struct sockaddr_un, sun_path))
+                        return -EINVAL;
+
+                if (a->size > offsetof(struct sockaddr_un, sun_path)) {
+
+                        if (a->sockaddr.un.sun_path[0] != 0) {
+                                char *e;
+
+                                /* path */
+                                if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path))))
+                                        return -EINVAL;
+
+                                if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
+                                        return -EINVAL;
+                        }
+                }
+
+                if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
+                        return -EINVAL;
+
+                return 0;
+
+        case AF_NETLINK:
+
+                if (a->size != sizeof(struct sockaddr_nl))
+                        return -EINVAL;
+
+                if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
+                        return -EINVAL;
+
+                return 0;
+
+        default:
+                return -EAFNOSUPPORT;
+        }
+}
+
+int socket_address_print(const SocketAddress *a, char **p) {
+        int r;
+        assert(a);
+        assert(p);
+
+        if ((r = socket_address_verify(a)) < 0)
+                return r;
+
+        switch (socket_address_family(a)) {
+
+        case AF_INET: {
+                char *ret;
+
+                if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1)))
+                        return -ENOMEM;
+
+                if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) {
+                        free(ret);
+                        return -errno;
+                }
+
+                sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port));
+                *p = ret;
+                return 0;
+        }
+
+        case AF_INET6: {
+                char *ret;
+
+                if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1)))
+                        return -ENOMEM;
+
+                ret[0] = '[';
+                if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) {
+                        free(ret);
+                        return -errno;
+                }
+
+                sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port));
+                *p = ret;
+                return 0;
+        }
+
+        case AF_UNIX: {
+                char *ret;
+
+                if (a->size <= offsetof(struct sockaddr_un, sun_path)) {
+
+                        if (!(ret = strdup("<unnamed>")))
+                                return -ENOMEM;
+
+                } else if (a->sockaddr.un.sun_path[0] == 0) {
+                        /* abstract */
+
+                        /* FIXME: We assume we can print the
+                         * socket path here and that it hasn't
+                         * more than one NUL byte. That is
+                         * actually an invalid assumption */
+
+                        if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1)))
+                                return -ENOMEM;
+
+                        ret[0] = '@';
+                        memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1);
+                        ret[sizeof(a->sockaddr.un.sun_path)] = 0;
+
+                } else {
+
+                        if (!(ret = strdup(a->sockaddr.un.sun_path)))
+                                return -ENOMEM;
+                }
+
+                *p = ret;
+                return 0;
+        }
+
+        case AF_NETLINK: {
+                const char *sfamily;
+
+                if ((sfamily = netlink_family_to_string(a->protocol)))
+                        r = asprintf(p, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
+                else
+                        r = asprintf(p, "%i %u", a->protocol, a->sockaddr.nl.nl_groups);
+
+                if (r < 0)
+                        return -ENOMEM;
+
+                return 0;
+        }
+
+        default:
+                return -EINVAL;
+        }
+}
+
+int socket_address_listen(
+                const SocketAddress *a,
+                int backlog,
+                SocketAddressBindIPv6Only only,
+                const char *bind_to_device,
+                bool free_bind,
+                bool transparent,
+                mode_t directory_mode,
+                mode_t socket_mode,
+                const char *label,
+                int *ret) {
+
+        int r, fd, one;
+        assert(a);
+        assert(ret);
+
+        if ((r = socket_address_verify(a)) < 0)
+                return r;
+
+        if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
+                return -EAFNOSUPPORT;
+
+        r = label_socket_set(label);
+        if (r < 0)
+                return r;
+
+        fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, a->protocol);
+        r = fd < 0 ? -errno : 0;
+
+        label_socket_clear();
+
+        if (r < 0)
+                return r;
+
+        if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
+                int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
+
+                if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
+                        goto fail;
+        }
+
+        if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) {
+                if (bind_to_device)
+                        if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
+                                goto fail;
+
+                if (free_bind) {
+                        one = 1;
+                        if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
+                                log_warning("IP_FREEBIND failed: %m");
+                }
+
+                if (transparent) {
+                        one = 1;
+                        if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
+                                log_warning("IP_TRANSPARENT failed: %m");
+                }
+        }
+
+        one = 1;
+        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
+                goto fail;
+
+        if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
+                mode_t old_mask;
+
+                /* Create parents */
+                mkdir_parents(a->sockaddr.un.sun_path, directory_mode);
+
+                /* Enforce the right access mode for the socket*/
+                old_mask = umask(~ socket_mode);
+
+                /* Include the original umask in our mask */
+                umask(~socket_mode | old_mask);
+
+                r = label_bind(fd, &a->sockaddr.sa, a->size);
+
+                if (r < 0 && errno == EADDRINUSE) {
+                        /* Unlink and try again */
+                        unlink(a->sockaddr.un.sun_path);
+                        r = bind(fd, &a->sockaddr.sa, a->size);
+                }
+
+                umask(old_mask);
+        } else
+                r = bind(fd, &a->sockaddr.sa, a->size);
+
+        if (r < 0)
+                goto fail;
+
+        if (socket_address_can_accept(a))
+                if (listen(fd, backlog) < 0)
+                        goto fail;
+
+        *ret = fd;
+        return 0;
+
+fail:
+        r = -errno;
+        close_nointr_nofail(fd);
+        return r;
+}
+
+bool socket_address_can_accept(const SocketAddress *a) {
+        assert(a);
+
+        return
+                a->type == SOCK_STREAM ||
+                a->type == SOCK_SEQPACKET;
+}
+
+bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
+        assert(a);
+        assert(b);
+
+        /* Invalid addresses are unequal to all */
+        if (socket_address_verify(a) < 0 ||
+            socket_address_verify(b) < 0)
+                return false;
+
+        if (a->type != b->type)
+                return false;
+
+        if (a->size != b->size)
+                return false;
+
+        if (socket_address_family(a) != socket_address_family(b))
+                return false;
+
+        switch (socket_address_family(a)) {
+
+        case AF_INET:
+                if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr)
+                        return false;
+
+                if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port)
+                        return false;
+
+                break;
+
+        case AF_INET6:
+                if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
+                        return false;
+
+                if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
+                        return false;
+
+                break;
+
+        case AF_UNIX:
+
+                if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
+                        return false;
+
+                if (a->sockaddr.un.sun_path[0]) {
+                        if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
+                                return false;
+                } else {
+                        if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
+                                return false;
+                }
+
+                break;
+
+        case AF_NETLINK:
+
+                if (a->protocol != b->protocol)
+                        return false;
+
+                if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
+                        return false;
+
+                break;
+
+        default:
+                /* Cannot compare, so we assume the addresses are different */
+                return false;
+        }
+
+        return true;
+}
+
+bool socket_address_is(const SocketAddress *a, const char *s, int type) {
+        struct SocketAddress b;
+
+        assert(a);
+        assert(s);
+
+        if (socket_address_parse(&b, s) < 0)
+                return false;
+
+        b.type = type;
+
+        return socket_address_equal(a, &b);
+}
+
+bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
+        struct SocketAddress b;
+
+        assert(a);
+        assert(s);
+
+        if (socket_address_parse_netlink(&b, s) < 0)
+                return false;
+
+        return socket_address_equal(a, &b);
+}
+
+bool socket_address_needs_mount(const SocketAddress *a, const char *prefix) {
+        assert(a);
+
+        if (socket_address_family(a) != AF_UNIX)
+                return false;
+
+        if (a->sockaddr.un.sun_path[0] == 0)
+                return false;
+
+        return path_startswith(a->sockaddr.un.sun_path, prefix);
+}
+
+bool socket_ipv6_is_supported(void) {
+        char *l = 0;
+        bool enabled;
+
+        if (access("/sys/module/ipv6", F_OK) != 0)
+                return 0;
+
+        /* If we can't check "disable" parameter, assume enabled */
+        if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
+                return 1;
+
+        /* If module was loaded with disable=1 no IPv6 available */
+        enabled = l[0] == '0';
+        free(l);
+
+        return enabled;
+}
+
+static const char* const netlink_family_table[] = {
+        [NETLINK_ROUTE] = "route",
+        [NETLINK_FIREWALL] = "firewall",
+        [NETLINK_INET_DIAG] = "inet-diag",
+        [NETLINK_NFLOG] = "nflog",
+        [NETLINK_XFRM] = "xfrm",
+        [NETLINK_SELINUX] = "selinux",
+        [NETLINK_ISCSI] = "iscsi",
+        [NETLINK_AUDIT] = "audit",
+        [NETLINK_FIB_LOOKUP] = "fib-lookup",
+        [NETLINK_CONNECTOR] = "connector",
+        [NETLINK_NETFILTER] = "netfilter",
+        [NETLINK_IP6_FW] = "ip6-fw",
+        [NETLINK_DNRTMSG] = "dnrtmsg",
+        [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
+        [NETLINK_GENERIC] = "generic",
+        [NETLINK_SCSITRANSPORT] = "scsitransport",
+        [NETLINK_ECRYPTFS] = "ecryptfs"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(netlink_family, int);
+
+static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
+        [SOCKET_ADDRESS_DEFAULT] = "default",
+        [SOCKET_ADDRESS_BOTH] = "both",
+        [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
diff --git a/src/shared/socket-util.h b/src/shared/socket-util.h
new file mode 100644
index 0000000..8ccbd37
--- /dev/null
+++ b/src/shared/socket-util.h
@@ -0,0 +1,102 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosocketutilhfoo
+#define foosocketutilhfoo
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <net/if.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+
+#include "macro.h"
+#include "util.h"
+
+union sockaddr_union {
+        struct sockaddr sa;
+        struct sockaddr_in in4;
+        struct sockaddr_in6 in6;
+        struct sockaddr_un un;
+        struct sockaddr_nl nl;
+        struct sockaddr_storage storage;
+};
+
+typedef struct SocketAddress {
+        union sockaddr_union sockaddr;
+
+        /* We store the size here explicitly due to the weird
+         * sockaddr_un semantics for abstract sockets */
+        socklen_t size;
+
+        /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */
+        int type;
+
+        /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */
+        int protocol;
+} SocketAddress;
+
+typedef enum SocketAddressBindIPv6Only {
+        SOCKET_ADDRESS_DEFAULT,
+        SOCKET_ADDRESS_BOTH,
+        SOCKET_ADDRESS_IPV6_ONLY,
+        _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX,
+        _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1
+} SocketAddressBindIPv6Only;
+
+#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
+
+int socket_address_parse(SocketAddress *a, const char *s);
+int socket_address_parse_netlink(SocketAddress *a, const char *s);
+int socket_address_print(const SocketAddress *a, char **p);
+int socket_address_verify(const SocketAddress *a);
+
+bool socket_address_can_accept(const SocketAddress *a);
+
+int socket_address_listen(
+                const SocketAddress *a,
+                int backlog,
+                SocketAddressBindIPv6Only only,
+                const char *bind_to_device,
+                bool free_bind,
+                bool transparent,
+                mode_t directory_mode,
+                mode_t socket_mode,
+                const char *label,
+                int *ret);
+
+bool socket_address_is(const SocketAddress *a, const char *s, int type);
+bool socket_address_is_netlink(const SocketAddress *a, const char *s);
+
+bool socket_address_equal(const SocketAddress *a, const SocketAddress *b);
+
+bool socket_address_needs_mount(const SocketAddress *a, const char *prefix);
+
+const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b);
+SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s);
+
+const char* netlink_family_to_string(int b);
+int netlink_family_from_string(const char *s);
+
+bool socket_ipv6_is_supported(void);
+
+#endif
diff --git a/src/shared/strv.c b/src/shared/strv.c
new file mode 100644
index 0000000..bb309d9
--- /dev/null
+++ b/src/shared/strv.c
@@ -0,0 +1,690 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU 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 **l) {
+        char **r, **k;
+
+        k = r = new(char*, strv_length(l)+1);
+        if (!k)
+                return NULL;
+
+        if (l)
+                for (; *l; k++, l++)
+                        if (!(*k = strdup(*l)))
+                                goto fail;
+
+        *k = NULL;
+        return r;
+
+fail:
+        for (k--; k >= r; k--)
+                free(*k);
+
+        free(r);
+
+        return NULL;
+}
+
+unsigned strv_length(char **l) {
+        unsigned 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 n = 0, i = 0;
+        va_list aq;
+
+        if (x) {
+                n = 1;
+
+                va_copy(aq, ap);
+                while (va_arg(aq, const char*))
+                        n++;
+                va_end(aq);
+        }
+
+        if (!(a = new(char*, n+1)))
+                return NULL;
+
+        if (x) {
+                if (!(a[i] = strdup(x))) {
+                        free(a);
+                        return NULL;
+                }
+
+                i++;
+
+                while ((s = va_arg(ap, const char*))) {
+                        if (!(a[i] = strdup(s)))
+                                goto fail;
+
+                        i++;
+                }
+        }
+
+        a[i] = NULL;
+
+        return a;
+
+fail:
+
+        for (; i > 0; i--)
+                if (a[i-1])
+                        free(a[i-1]);
+
+        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);
+
+        if (!(r = new(char*, strv_length(a)+strv_length(b)+1)))
+                return NULL;
+
+        for (k = r; *a; k++, a++)
+                if (!(*k = strdup(*a)))
+                        goto fail;
+        for (; *b; k++, b++)
+                if (!(*k = strdup(*b)))
+                        goto fail;
+
+        *k = NULL;
+        return r;
+
+fail:
+        for (k--; k >= r; k--)
+                free(*k);
+
+        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:
+        for (k--; k >= r; k--)
+                free(*k);
+
+        free(r);
+
+        return NULL;
+
+}
+
+char **strv_split(const char *s, const char *separator) {
+        char *state;
+        char *w;
+        size_t l;
+        unsigned n, i;
+        char **r;
+
+        assert(s);
+
+        n = 0;
+        FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
+                n++;
+
+        if (!(r = new(char*, n+1)))
+                return NULL;
+
+        i = 0;
+        FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
+                if (!(r[i++] = strndup(w, l))) {
+                        strv_free(r);
+                        return NULL;
+                }
+
+        r[i] = NULL;
+        return r;
+}
+
+char **strv_split_quoted(const char *s) {
+        char *state;
+        char *w;
+        size_t l;
+        unsigned n, i;
+        char **r;
+
+        assert(s);
+
+        n = 0;
+        FOREACH_WORD_QUOTED(w, l, s, state)
+                n++;
+
+        if (!(r = new(char*, n+1)))
+                return NULL;
+
+        i = 0;
+        FOREACH_WORD_QUOTED(w, l, s, state)
+                if (!(r[i++] = cunescape_length(w, l))) {
+                        strv_free(r);
+                        return NULL;
+                }
+
+        r[i] = NULL;
+        return r;
+}
+
+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);
+        }
+
+        if (!(r = new(char, n+1)))
+                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++)
+                if (!(*k = strdup(*l)))
+                        goto fail;
+
+        if (!(*(k++) = strdup(s)))
+                goto fail;
+
+        *k = NULL;
+        return r;
+
+fail:
+        for (k--; k >= r; k--)
+                free(*k);
+
+        free(r);
+
+        return NULL;
+}
+
+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;
+}
+
+static int env_append(char **r, char ***k, char **a) {
+        assert(r);
+        assert(k);
+
+        if (!a)
+                return 0;
+
+        /* Add the entries of a to *k unless they already exist in *r
+         * in which case they are overridden instead. This assumes
+         * there is enough space in the r array. */
+
+        for (; *a; a++) {
+                char **j;
+                size_t n;
+
+                n = strcspn(*a, "=");
+
+                if ((*a)[n] == '=')
+                        n++;
+
+                for (j = r; j < *k; j++)
+                        if (strncmp(*j, *a, n) == 0)
+                                break;
+
+                if (j >= *k)
+                        (*k)++;
+                else
+                        free(*j);
+
+                if (!(*j = strdup(*a)))
+                        return -ENOMEM;
+        }
+
+        return 0;
+}
+
+char **strv_env_merge(unsigned n_lists, ...) {
+        size_t n = 0;
+        char **l, **k, **r;
+        va_list ap;
+        unsigned i;
+
+        /* Merges an arbitrary number of environment sets */
+
+        va_start(ap, n_lists);
+        for (i = 0; i < n_lists; i++) {
+                l = va_arg(ap, char**);
+                n += strv_length(l);
+        }
+        va_end(ap);
+
+        if (!(r = new(char*, n+1)))
+                return NULL;
+
+        k = r;
+
+        va_start(ap, n_lists);
+        for (i = 0; i < n_lists; i++) {
+                l = va_arg(ap, char**);
+                if (env_append(r, &k, l) < 0)
+                        goto fail;
+        }
+        va_end(ap);
+
+        *k = NULL;
+
+        return r;
+
+fail:
+        va_end(ap);
+
+        for (k--; k >= r; k--)
+                free(*k);
+
+        free(r);
+
+        return NULL;
+}
+
+static bool env_match(const char *t, const char *pattern) {
+        assert(t);
+        assert(pattern);
+
+        /* pattern a matches string a
+         *         a matches a=
+         *         a matches a=b
+         *         a= matches a=
+         *         a=b matches a=b
+         *         a= does not match a
+         *         a=b does not match a=
+         *         a=b does not match a
+         *         a=b does not match a=c */
+
+        if (streq(t, pattern))
+                return true;
+
+        if (!strchr(pattern, '=')) {
+                size_t l = strlen(pattern);
+
+                return strncmp(t, pattern, l) == 0 && t[l] == '=';
+        }
+
+        return false;
+}
+
+char **strv_env_delete(char **x, unsigned n_lists, ...) {
+        size_t n, i = 0;
+        char **k, **r;
+        va_list ap;
+
+        /* Deletes every entry from x that is mentioned in the other
+         * string lists */
+
+        n = strv_length(x);
+
+        r = new(char*, n+1);
+        if (!r)
+                return NULL;
+
+        STRV_FOREACH(k, x) {
+                unsigned v;
+
+                va_start(ap, n_lists);
+                for (v = 0; v < n_lists; v++) {
+                        char **l, **j;
+
+                        l = va_arg(ap, char**);
+                        STRV_FOREACH(j, l)
+                                if (env_match(*k, *j))
+                                        goto skip;
+                }
+                va_end(ap);
+
+                r[i] = strdup(*k);
+                if (!r[i]) {
+                        strv_free(r);
+                        return NULL;
+                }
+
+                i++;
+                continue;
+
+        skip:
+                va_end(ap);
+        }
+
+        r[i] = NULL;
+
+        assert(i <= n);
+
+        return r;
+}
+
+char **strv_env_unset(char **l, const char *p) {
+
+        char **f, **t;
+
+        if (!l)
+                return NULL;
+
+        assert(p);
+
+        /* Drops every occurrence of the env var setting p in the
+         * string list. edits in-place. */
+
+        for (f = t = l; *f; f++) {
+
+                if (env_match(*f, p)) {
+                        free(*f);
+                        continue;
+                }
+
+                *(t++) = *f;
+        }
+
+        *t = NULL;
+        return l;
+}
+
+char **strv_env_set(char **x, const char *p) {
+
+        char **k, **r;
+        char* m[2] = { (char*) p, NULL };
+
+        /* Overrides the env var setting of p, returns a new copy */
+
+        if (!(r = new(char*, strv_length(x)+2)))
+                return NULL;
+
+        k = r;
+        if (env_append(r, &k, x) < 0)
+                goto fail;
+
+        if (env_append(r, &k, m) < 0)
+                goto fail;
+
+        *k = NULL;
+
+        return r;
+
+fail:
+        for (k--; k >= r; k--)
+                free(*k);
+
+        free(r);
+
+        return NULL;
+
+}
+
+char *strv_env_get_with_length(char **l, const char *name, size_t k) {
+        char **i;
+
+        assert(name);
+
+        STRV_FOREACH(i, l)
+                if (strncmp(*i, name, k) == 0 &&
+                    (*i)[k] == '=')
+                        return *i + k + 1;
+
+        return NULL;
+}
+
+char *strv_env_get(char **l, const char *name) {
+        return strv_env_get_with_length(l, name, strlen(name));
+}
+
+char **strv_env_clean(char **l) {
+        char **r, **ret;
+
+        for (r = ret = l; *l; l++) {
+                const char *equal;
+
+                equal = strchr(*l, '=');
+
+                if (equal && equal[1] == 0) {
+                        free(*l);
+                        continue;
+                }
+
+                *(r++) = *l;
+        }
+
+        *r = NULL;
+
+        return ret;
+}
+
+char **strv_parse_nulstr(const char *s, size_t l) {
+        const char *p;
+        unsigned c = 0, i = 0;
+        char **v;
+
+        assert(s || l <= 0);
+
+        if (l <= 0)
+                return strv_new(NULL, NULL);
+
+        for (p = s; p < s + l; p++)
+                if (*p == 0)
+                        c++;
+
+        if (s[l-1] != 0)
+                c++;
+
+        if (!(v = new0(char*, c+1)))
+                return NULL;
+
+        p = s;
+        while (p < s + l) {
+                const char *e;
+
+                e = memchr(p, 0, s + l - p);
+
+                if (!(v[i++] = strndup(p, e ? e - p : s + l - p))) {
+                        strv_free(v);
+                        return NULL;
+                }
+
+                if (!e)
+                        break;
+
+                p = e + 1;
+        }
+
+        assert(i == c);
+
+        return v;
+}
+
+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;
+}
diff --git a/src/shared/strv.h b/src/shared/strv.h
new file mode 100644
index 0000000..d038c9f
--- /dev/null
+++ b/src/shared/strv.h
@@ -0,0 +1,79 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foostrvhfoo
+#define foostrvhfoo
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "macro.h"
+
+char *strv_find(char **l, const char *name);
+char *strv_find_prefix(char **l, const char *name);
+
+void strv_free(char **l);
+char **strv_copy(char **l) _malloc_;
+unsigned strv_length(char **l);
+
+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);
+
+char **strv_remove(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_ _malloc_;
+char **strv_new_ap(const char *x, va_list ap) _malloc_;
+
+static inline bool strv_isempty(char **l) {
+        return !l || !*l;
+}
+
+char **strv_split(const char *s, const char *separator) _malloc_;
+char **strv_split_quoted(const char *s) _malloc_;
+
+char *strv_join(char **l, const char *separator) _malloc_;
+
+char **strv_env_merge(unsigned n_lists, ...);
+char **strv_env_delete(char **x, unsigned n_lists, ...);
+
+char **strv_env_set(char **x, const char *p);
+char **strv_env_unset(char **l, const char *p);
+
+char *strv_env_get_with_length(char **l, const char *name, size_t k);
+char *strv_env_get(char **x, const char *n);
+
+char **strv_env_clean(char **l);
+
+char **strv_parse_nulstr(const char *s, size_t l);
+
+bool strv_overlap(char **a, char **b);
+
+#define STRV_FOREACH(s, l)                      \
+        for ((s) = (l); (s) && *(s); (s)++)
+
+#define STRV_FOREACH_BACKWARDS(s, l)            \
+        for (; (l) && ((s) >= (l)); (s)--)
+
+#endif
diff --git a/src/shared/utf8.c b/src/shared/utf8.c
new file mode 100644
index 0000000..11619dc
--- /dev/null
+++ b/src/shared/utf8.c
@@ -0,0 +1,214 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2012 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+/* This file is based on the GLIB utf8 validation functions. The
+ * original license text follows. */
+
+/* gutf8.c - Operations on UTF-8 strings.
+ *
+ * Copyright (C) 1999 Tom Tromey
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library 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 of the License, or (at your option) any later version.
+ *
+ * This library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "utf8.h"
+
+#define FILTER_CHAR '_'
+
+static inline bool is_unicode_valid(uint32_t ch) {
+
+        if (ch >= 0x110000) /* End of unicode space */
+                return false;
+        if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
+                return false;
+        if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
+                return false;
+        if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
+                return false;
+
+        return true;
+}
+
+static inline bool is_continuation_char(uint8_t ch) {
+        if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
+                return false;
+        return true;
+}
+
+static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
+        *u_ch <<= 6;
+        *u_ch |= ch & 0x3f;
+}
+
+static char* utf8_validate(const char *str, char *output) {
+        uint32_t val = 0;
+        uint32_t min = 0;
+        const uint8_t *p, *last;
+        int size;
+        uint8_t *o;
+
+        assert(str);
+
+        o = (uint8_t*) output;
+        for (p = (const uint8_t*) str; *p; p++) {
+                if (*p < 128) {
+                        if (o)
+                                *o = *p;
+                } else {
+                        last = p;
+
+                        if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
+                                size = 2;
+                                min = 128;
+                                val = (uint32_t) (*p & 0x1e);
+                                goto ONE_REMAINING;
+                        } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
+                                size = 3;
+                                min = (1 << 11);
+                                val = (uint32_t) (*p & 0x0f);
+                                goto TWO_REMAINING;
+                        } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
+                                size = 4;
+                                min = (1 << 16);
+                                val = (uint32_t) (*p & 0x07);
+                        } else
+                                goto error;
+
+                        p++;
+                        if (!is_continuation_char(*p))
+                                goto error;
+                        merge_continuation_char(&val, *p);
+
+                TWO_REMAINING:
+                        p++;
+                        if (!is_continuation_char(*p))
+                                goto error;
+                        merge_continuation_char(&val, *p);
+
+                ONE_REMAINING:
+                        p++;
+                        if (!is_continuation_char(*p))
+                                goto error;
+                        merge_continuation_char(&val, *p);
+
+                        if (val < min)
+                                goto error;
+
+                        if (!is_unicode_valid(val))
+                                goto error;
+
+                        if (o) {
+                                memcpy(o, last, (size_t) size);
+                                o += size;
+                        }
+
+                        continue;
+
+                error:
+                        if (o) {
+                                *o = FILTER_CHAR;
+                                p = last; /* We retry at the next character */
+                        } else
+                                goto failure;
+                }
+
+                if (o)
+                        o++;
+        }
+
+        if (o) {
+                *o = '\0';
+                return output;
+        }
+
+        return (char*) str;
+
+failure:
+        return NULL;
+}
+
+char* utf8_is_valid (const char *str) {
+        return utf8_validate(str, NULL);
+}
+
+char* utf8_filter (const char *str) {
+        char *new_str;
+
+        assert(str);
+
+        new_str = malloc(strlen(str) + 1);
+        if (!new_str)
+                return NULL;
+
+        return utf8_validate(str, new_str);
+}
+
+char *ascii_is_valid(const char *str) {
+        const char *p;
+
+        assert(str);
+
+        for (p = str; *p; p++)
+                if ((unsigned char) *p >= 128)
+                        return NULL;
+
+        return (char*) str;
+}
+
+char *ascii_filter(const char *str) {
+        char *r, *s, *d;
+        size_t l;
+
+        assert(str);
+
+        l = strlen(str);
+        r = malloc(l + 1);
+        if (!r)
+                return NULL;
+
+        for (s = r, d = r; *s; s++)
+                if ((unsigned char) *s < 128)
+                        *(d++) = *s;
+
+        *d = 0;
+
+        return r;
+}
diff --git a/src/shared/utf8.h b/src/shared/utf8.h
new file mode 100644
index 0000000..9a72bec
--- /dev/null
+++ b/src/shared/utf8.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooutf8hfoo
+#define fooutf8hfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2012 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "macro.h"
+
+char *utf8_is_valid(const char *s) _pure_;
+char *ascii_is_valid(const char *s) _pure_;
+
+char *utf8_filter(const char *s);
+char *ascii_filter(const char *s);
+
+#endif
diff --git a/src/shared/util.c b/src/shared/util.c
new file mode 100644
index 0000000..563853f
--- /dev/null
+++ b/src/shared/util.c
@@ -0,0 +1,6123 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <linux/sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <linux/vt.h>
+#include <linux/tiocl.h>
+#include <termios.h>
+#include <stdarg.h>
+#include <sys/inotify.h>
+#include <sys/poll.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <sys/prctl.h>
+#include <sys/utsname.h>
+#include <pwd.h>
+#include <netinet/ip.h>
+#include <linux/kd.h>
+#include <dlfcn.h>
+#include <sys/wait.h>
+#include <sys/capability.h>
+#include <sys/time.h>
+#include <linux/rtc.h>
+#include <glob.h>
+#include <grp.h>
+#include <sys/mman.h>
+
+#include "macro.h"
+#include "util.h"
+#include "ioprio.h"
+#include "missing.h"
+#include "log.h"
+#include "strv.h"
+#include "label.h"
+#include "exit-status.h"
+#include "hashmap.h"
+
+int saved_argc = 0;
+char **saved_argv = NULL;
+
+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 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;
+}
+
+usec_t now(clockid_t clock_id) {
+        struct timespec ts;
+
+        assert_se(clock_gettime(clock_id, &ts) == 0);
+
+        return timespec_load(&ts);
+}
+
+dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
+        assert(ts);
+
+        ts->realtime = now(CLOCK_REALTIME);
+        ts->monotonic = now(CLOCK_MONOTONIC);
+
+        return ts;
+}
+
+dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
+        int64_t delta;
+        assert(ts);
+
+        ts->realtime = u;
+
+        if (u == 0)
+                ts->monotonic = 0;
+        else {
+                delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
+
+                ts->monotonic = now(CLOCK_MONOTONIC);
+
+                if ((int64_t) ts->monotonic > delta)
+                        ts->monotonic -= delta;
+                else
+                        ts->monotonic = 0;
+        }
+
+        return ts;
+}
+
+usec_t timespec_load(const struct timespec *ts) {
+        assert(ts);
+
+        return
+                (usec_t) ts->tv_sec * USEC_PER_SEC +
+                (usec_t) ts->tv_nsec / NSEC_PER_USEC;
+}
+
+struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
+        assert(ts);
+
+        ts->tv_sec = (time_t) (u / USEC_PER_SEC);
+        ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
+
+        return ts;
+}
+
+usec_t timeval_load(const struct timeval *tv) {
+        assert(tv);
+
+        return
+                (usec_t) tv->tv_sec * USEC_PER_SEC +
+                (usec_t) tv->tv_usec;
+}
+
+struct timeval *timeval_store(struct timeval *tv, usec_t u) {
+        assert(tv);
+
+        tv->tv_sec = (time_t) (u / USEC_PER_SEC);
+        tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
+
+        return tv;
+}
+
+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;
+}
+
+bool startswith(const char *s, const char *prefix) {
+        size_t sl, pl;
+
+        assert(s);
+        assert(prefix);
+
+        sl = strlen(s);
+        pl = strlen(prefix);
+
+        if (pl == 0)
+                return true;
+
+        if (sl < pl)
+                return false;
+
+        return memcmp(s, prefix, pl) == 0;
+}
+
+bool startswith_no_case(const char *s, const char *prefix) {
+        size_t sl, pl;
+        unsigned i;
+
+        assert(s);
+        assert(prefix);
+
+        sl = strlen(s);
+        pl = strlen(prefix);
+
+        if (pl == 0)
+                return true;
+
+        if (sl < pl)
+                return false;
+
+        for(i = 0; i < pl; ++i) {
+                if (tolower(s[i]) != tolower(prefix[i]))
+                        return false;
+        }
+
+        return true;
+}
+
+bool first_word(const char *s, const char *word) {
+        size_t sl, wl;
+
+        assert(s);
+        assert(word);
+
+        sl = strlen(s);
+        wl = strlen(word);
+
+        if (sl < wl)
+                return false;
+
+        if (wl == 0)
+                return true;
+
+        if (memcmp(s, word, wl) != 0)
+                return false;
+
+        return s[wl] == 0 ||
+                strchr(WHITESPACE, s[wl]);
+}
+
+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;
+}
+
+void close_many(const int fds[], unsigned n_fd) {
+        unsigned i;
+
+        for (i = 0; i < n_fd; i++)
+                close_nointr_nofail(fds[i]);
+}
+
+int parse_boolean(const char *v) {
+        assert(v);
+
+        if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
+                return 1;
+        else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
+                return 0;
+
+        return -EINVAL;
+}
+
+int parse_pid(const char *s, pid_t* ret_pid) {
+        unsigned long ul = 0;
+        pid_t pid;
+        int r;
+
+        assert(s);
+        assert(ret_pid);
+
+        if ((r = safe_atolu(s, &ul)) < 0)
+                return r;
+
+        pid = (pid_t) ul;
+
+        if ((unsigned long) pid != ul)
+                return -ERANGE;
+
+        if (pid <= 0)
+                return -ERANGE;
+
+        *ret_pid = pid;
+        return 0;
+}
+
+int parse_uid(const char *s, uid_t* ret_uid) {
+        unsigned long ul = 0;
+        uid_t uid;
+        int r;
+
+        assert(s);
+        assert(ret_uid);
+
+        if ((r = safe_atolu(s, &ul)) < 0)
+                return r;
+
+        uid = (uid_t) ul;
+
+        if ((unsigned long) uid != ul)
+                return -ERANGE;
+
+        *ret_uid = uid;
+        return 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;
+}
+
+int safe_atoi(const char *s, int *ret_i) {
+        char *x = NULL;
+        long l;
+
+        assert(s);
+        assert(ret_i);
+
+        errno = 0;
+        l = strtol(s, &x, 0);
+
+        if (!x || *x || errno)
+                return errno ? -errno : -EINVAL;
+
+        if ((long) (int) l != l)
+                return -ERANGE;
+
+        *ret_i = (int) l;
+        return 0;
+}
+
+int safe_atollu(const char *s, long long unsigned *ret_llu) {
+        char *x = NULL;
+        unsigned long long l;
+
+        assert(s);
+        assert(ret_llu);
+
+        errno = 0;
+        l = strtoull(s, &x, 0);
+
+        if (!x || *x || errno)
+                return errno ? -errno : -EINVAL;
+
+        *ret_llu = l;
+        return 0;
+}
+
+int safe_atolli(const char *s, long long int *ret_lli) {
+        char *x = NULL;
+        long long l;
+
+        assert(s);
+        assert(ret_lli);
+
+        errno = 0;
+        l = strtoll(s, &x, 0);
+
+        if (!x || *x || errno)
+                return errno ? -errno : -EINVAL;
+
+        *ret_lli = l;
+        return 0;
+}
+
+/* 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;
+}
+
+/* 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) {
+        char *current, *e;
+        bool escaped = false;
+
+        current = *state ? *state : (char*) c;
+
+        if (!*current || *c == 0)
+                return NULL;
+
+        current += strspn(current, WHITESPACE);
+
+        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 = *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 = *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 = e;
+        }
+
+        return (char*) current;
+}
+
+char **split_path_and_make_absolute(const char *p) {
+        char **l;
+        assert(p);
+
+        if (!(l = strv_split(p, ":")))
+                return NULL;
+
+        if (!strv_path_make_absolute_cwd(l)) {
+                strv_free(l);
+                return NULL;
+        }
+
+        return l;
+}
+
+int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
+        int r;
+        FILE *f;
+        char fn[PATH_MAX], line[LINE_MAX], *p;
+        long unsigned ppid;
+
+        assert(pid > 0);
+        assert(_ppid);
+
+        assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
+        char_array_0(fn);
+
+        if (!(f = fopen(fn, "re")))
+                return -errno;
+
+        if (!(fgets(line, sizeof(line), f))) {
+                r = feof(f) ? -EIO : -errno;
+                fclose(f);
+                return r;
+        }
+
+        fclose(f);
+
+        /* Let's skip the pid and comm fields. The latter is enclosed
+         * in () but does not escape any () in its value, so let's
+         * skip over it manually */
+
+        if (!(p = strrchr(line, ')')))
+                return -EIO;
+
+        p++;
+
+        if (sscanf(p, " "
+                   "%*c "  /* state */
+                   "%lu ", /* ppid */
+                   &ppid) != 1)
+                return -EIO;
+
+        if ((long unsigned) (pid_t) ppid != ppid)
+                return -ERANGE;
+
+        *_ppid = (pid_t) ppid;
+
+        return 0;
+}
+
+int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
+        int r;
+        FILE *f;
+        char fn[PATH_MAX], line[LINE_MAX], *p;
+
+        assert(pid > 0);
+        assert(st);
+
+        assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
+        char_array_0(fn);
+
+        if (!(f = fopen(fn, "re")))
+                return -errno;
+
+        if (!(fgets(line, sizeof(line), f))) {
+                r = feof(f) ? -EIO : -errno;
+                fclose(f);
+                return r;
+        }
+
+        fclose(f);
+
+        /* Let's skip the pid and comm fields. The latter is enclosed
+         * in () but does not escape any () in its value, so let's
+         * skip over it manually */
+
+        if (!(p = strrchr(line, ')')))
+                return -EIO;
+
+        p++;
+
+        if (sscanf(p, " "
+                   "%*c "  /* state */
+                   "%*d "  /* ppid */
+                   "%*d "  /* pgrp */
+                   "%*d "  /* session */
+                   "%*d "  /* tty_nr */
+                   "%*d "  /* tpgid */
+                   "%*u "  /* flags */
+                   "%*u "  /* minflt */
+                   "%*u "  /* cminflt */
+                   "%*u "  /* majflt */
+                   "%*u "  /* cmajflt */
+                   "%*u "  /* utime */
+                   "%*u "  /* stime */
+                   "%*d "  /* cutime */
+                   "%*d "  /* cstime */
+                   "%*d "  /* priority */
+                   "%*d "  /* nice */
+                   "%*d "  /* num_threads */
+                   "%*d "  /* itrealvalue */
+                   "%llu "  /* starttime */,
+                   st) != 1)
+                return -EIO;
+
+        return 0;
+}
+
+int write_one_line_file(const char *fn, const char *line) {
+        FILE *f;
+        int r;
+
+        assert(fn);
+        assert(line);
+
+        if (!(f = fopen(fn, "we")))
+                return -errno;
+
+        errno = 0;
+        if (fputs(line, f) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        if (!endswith(line, "\n"))
+                fputc('\n', f);
+
+        fflush(f);
+
+        if (ferror(f)) {
+                if (errno != 0)
+                        r = -errno;
+                else
+                        r = -EIO;
+        } else
+                r = 0;
+
+finish:
+        fclose(f);
+        return r;
+}
+
+int fchmod_umask(int fd, mode_t m) {
+        mode_t u;
+        int r;
+
+        u = umask(0777);
+        r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
+        umask(u);
+
+        return r;
+}
+
+int write_one_line_file_atomic(const char *fn, const char *line) {
+        FILE *f;
+        int r;
+        char *p;
+
+        assert(fn);
+        assert(line);
+
+        r = fopen_temporary(fn, &f, &p);
+        if (r < 0)
+                return r;
+
+        fchmod_umask(fileno(f), 0644);
+
+        errno = 0;
+        if (fputs(line, f) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        if (!endswith(line, "\n"))
+                fputc('\n', f);
+
+        fflush(f);
+
+        if (ferror(f)) {
+                if (errno != 0)
+                        r = -errno;
+                else
+                        r = -EIO;
+        } else {
+                if (rename(p, fn) < 0)
+                        r = -errno;
+                else
+                        r = 0;
+        }
+
+finish:
+        if (r < 0)
+                unlink(p);
+
+        fclose(f);
+        free(p);
+
+        return r;
+}
+
+int read_one_line_file(const char *fn, char **line) {
+        FILE *f;
+        int r;
+        char t[LINE_MAX], *c;
+
+        assert(fn);
+        assert(line);
+
+        f = fopen(fn, "re");
+        if (!f)
+                return -errno;
+
+        if (!fgets(t, sizeof(t), f)) {
+
+                if (ferror(f)) {
+                        r = -errno;
+                        goto finish;
+                }
+
+                t[0] = 0;
+        }
+
+        c = strdup(t);
+        if (!c) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        truncate_nl(c);
+
+        *line = c;
+        r = 0;
+
+finish:
+        fclose(f);
+        return r;
+}
+
+int read_full_file(const char *fn, char **contents, size_t *size) {
+        FILE *f;
+        int r;
+        size_t n, l;
+        char *buf = NULL;
+        struct stat st;
+
+        if (!(f = fopen(fn, "re")))
+                return -errno;
+
+        if (fstat(fileno(f), &st) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        /* Safety check */
+        if (st.st_size > 4*1024*1024) {
+                r = -E2BIG;
+                goto finish;
+        }
+
+        n = st.st_size > 0 ? st.st_size : LINE_MAX;
+        l = 0;
+
+        for (;;) {
+                char *t;
+                size_t k;
+
+                if (!(t = realloc(buf, n+1))) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                buf = t;
+                k = fread(buf + l, 1, n - l, f);
+
+                if (k <= 0) {
+                        if (ferror(f)) {
+                                r = -errno;
+                                goto finish;
+                        }
+
+                        break;
+                }
+
+                l += k;
+                n *= 2;
+
+                /* Safety check */
+                if (n > 4*1024*1024) {
+                        r = -E2BIG;
+                        goto finish;
+                }
+        }
+
+        buf[l] = 0;
+        *contents = buf;
+        buf = NULL;
+
+        if (size)
+                *size = l;
+
+        r = 0;
+
+finish:
+        fclose(f);
+        free(buf);
+
+        return r;
+}
+
+int parse_env_file(
+                const char *fname,
+                const char *separator, ...) {
+
+        int r = 0;
+        char *contents = NULL, *p;
+
+        assert(fname);
+        assert(separator);
+
+        if ((r = read_full_file(fname, &contents, NULL)) < 0)
+                return r;
+
+        p = contents;
+        for (;;) {
+                const char *key = NULL;
+
+                p += strspn(p, separator);
+                p += strspn(p, WHITESPACE);
+
+                if (!*p)
+                        break;
+
+                if (!strchr(COMMENTS, *p)) {
+                        va_list ap;
+                        char **value;
+
+                        va_start(ap, separator);
+                        while ((key = va_arg(ap, char *))) {
+                                size_t n;
+                                char *v;
+
+                                value = va_arg(ap, char **);
+
+                                n = strlen(key);
+                                if (strncmp(p, key, n) != 0 ||
+                                    p[n] != '=')
+                                        continue;
+
+                                p += n + 1;
+                                n = strcspn(p, separator);
+
+                                if (n >= 2 &&
+                                    strchr(QUOTES, p[0]) &&
+                                    p[n-1] == p[0])
+                                        v = strndup(p+1, n-2);
+                                else
+                                        v = strndup(p, n);
+
+                                if (!v) {
+                                        r = -ENOMEM;
+                                        va_end(ap);
+                                        goto fail;
+                                }
+
+                                if (v[0] == '\0') {
+                                        /* return empty value strings as NULL */
+                                        free(v);
+                                        v = NULL;
+                                }
+
+                                free(*value);
+                                *value = v;
+
+                                p += n;
+
+                                r ++;
+                                break;
+                        }
+                        va_end(ap);
+                }
+
+                if (!key)
+                        p += strcspn(p, separator);
+        }
+
+fail:
+        free(contents);
+        return r;
+}
+
+int load_env_file(
+                const char *fname,
+                char ***rl) {
+
+        FILE *f;
+        char **m = NULL;
+        int r;
+
+        assert(fname);
+        assert(rl);
+
+        if (!(f = fopen(fname, "re")))
+                return -errno;
+
+        while (!feof(f)) {
+                char l[LINE_MAX], *p, *u;
+                char **t;
+
+                if (!fgets(l, sizeof(l), f)) {
+                        if (feof(f))
+                                break;
+
+                        r = -errno;
+                        goto finish;
+                }
+
+                p = strstrip(l);
+
+                if (!*p)
+                        continue;
+
+                if (strchr(COMMENTS, *p))
+                        continue;
+
+                if (!(u = normalize_env_assignment(p))) {
+                        log_error("Out of memory");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                t = strv_append(m, u);
+                free(u);
+
+                if (!t) {
+                        log_error("Out of memory");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                strv_free(m);
+                m = t;
+        }
+
+        r = 0;
+
+        *rl = m;
+        m = NULL;
+
+finish:
+        if (f)
+                fclose(f);
+
+        strv_free(m);
+
+        return r;
+}
+
+int write_env_file(const char *fname, char **l) {
+        char **i, *p;
+        FILE *f;
+        int r;
+
+        r = fopen_temporary(fname, &f, &p);
+        if (r < 0)
+                return r;
+
+        fchmod_umask(fileno(f), 0644);
+
+        errno = 0;
+        STRV_FOREACH(i, l) {
+                fputs(*i, f);
+                fputc('\n', f);
+        }
+
+        fflush(f);
+
+        if (ferror(f)) {
+                if (errno != 0)
+                        r = -errno;
+                else
+                        r = -EIO;
+        } else {
+                if (rename(p, fname) < 0)
+                        r = -errno;
+                else
+                        r = 0;
+        }
+
+        if (r < 0)
+                unlink(p);
+
+        fclose(f);
+        free(p);
+
+        return r;
+}
+
+char *truncate_nl(char *s) {
+        assert(s);
+
+        s[strcspn(s, NEWLINE)] = 0;
+        return s;
+}
+
+int get_process_comm(pid_t pid, char **name) {
+        int r;
+
+        assert(name);
+
+        if (pid == 0)
+                r = read_one_line_file("/proc/self/comm", name);
+        else {
+                char *p;
+                if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0)
+                        return -ENOMEM;
+
+                r = read_one_line_file(p, name);
+                free(p);
+        }
+
+        return r;
+}
+
+int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
+        char *r, *k;
+        int c;
+        bool space = false;
+        size_t left;
+        FILE *f;
+
+        assert(max_length > 0);
+        assert(line);
+
+        if (pid == 0)
+                f = fopen("/proc/self/cmdline", "re");
+        else {
+                char *p;
+                if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
+                        return -ENOMEM;
+
+                f = fopen(p, "re");
+                free(p);
+        }
+
+        if (!f)
+                return -errno;
+
+        r = new(char, max_length);
+        if (!r) {
+                fclose(f);
+                return -ENOMEM;
+        }
+
+        k = r;
+        left = max_length;
+        while ((c = getc(f)) != EOF) {
+
+                if (isprint(c)) {
+                        if (space) {
+                                if (left <= 4)
+                                        break;
+
+                                *(k++) = ' ';
+                                left--;
+                                space = false;
+                        }
+
+                        if (left <= 4)
+                                break;
+
+                        *(k++) = (char) c;
+                        left--;
+                }  else
+                        space = true;
+        }
+
+        if (left <= 4) {
+                size_t n = MIN(left-1, 3U);
+                memcpy(k, "...", n);
+                k[n] = 0;
+        } else
+                *k = 0;
+
+        fclose(f);
+
+        /* Kernel threads have no argv[] */
+        if (r[0] == 0) {
+                char *t;
+                int h;
+
+                free(r);
+
+                if (!comm_fallback)
+                        return -ENOENT;
+
+                h = get_process_comm(pid, &t);
+                if (h < 0)
+                        return h;
+
+                r = join("[", t, "]", NULL);
+                free(t);
+
+                if (!r)
+                        return -ENOMEM;
+        }
+
+        *line = r;
+        return 0;
+}
+
+int is_kernel_thread(pid_t pid) {
+        char *p;
+        size_t count;
+        char c;
+        bool eof;
+        FILE *f;
+
+        if (pid == 0)
+                return 0;
+
+        if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
+                return -ENOMEM;
+
+        f = fopen(p, "re");
+        free(p);
+
+        if (!f)
+                return -errno;
+
+        count = fread(&c, 1, 1, f);
+        eof = feof(f);
+        fclose(f);
+
+        /* Kernel threads have an empty cmdline */
+
+        if (count <= 0)
+                return eof ? 1 : -errno;
+
+        return 0;
+}
+
+int get_process_exe(pid_t pid, char **name) {
+        int r;
+
+        assert(name);
+
+        if (pid == 0)
+                r = readlink_malloc("/proc/self/exe", name);
+        else {
+                char *p;
+                if (asprintf(&p, "/proc/%lu/exe", (unsigned long) pid) < 0)
+                        return -ENOMEM;
+
+                r = readlink_malloc(p, name);
+                free(p);
+        }
+
+        return r;
+}
+
+int get_process_uid(pid_t pid, uid_t *uid) {
+        char *p;
+        FILE *f;
+        int r;
+
+        assert(uid);
+
+        if (pid == 0)
+                return getuid();
+
+        if (asprintf(&p, "/proc/%lu/status", (unsigned long) pid) < 0)
+                return -ENOMEM;
+
+        f = fopen(p, "re");
+        free(p);
+
+        if (!f)
+                return -errno;
+
+        while (!feof(f)) {
+                char line[LINE_MAX], *l;
+
+                if (!fgets(line, sizeof(line), f)) {
+                        if (feof(f))
+                                break;
+
+                        r = -errno;
+                        goto finish;
+                }
+
+                l = strstrip(line);
+
+                if (startswith(l, "Uid:")) {
+                        l += 4;
+                        l += strspn(l, WHITESPACE);
+
+                        l[strcspn(l, WHITESPACE)] = 0;
+
+                        r = parse_uid(l, uid);
+                        goto finish;
+                }
+        }
+
+        r = -EIO;
+
+finish:
+        fclose(f);
+
+        return r;
+}
+
+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 (!(r = new(char, a+b+1)))
+                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);
+}
+
+int readlink_malloc(const char *p, char **r) {
+        size_t l = 100;
+
+        assert(p);
+        assert(r);
+
+        for (;;) {
+                char *c;
+                ssize_t n;
+
+                if (!(c = new(char, l)))
+                        return -ENOMEM;
+
+                if ((n = readlink(p, c, l-1)) < 0) {
+                        int ret = -errno;
+                        free(c);
+                        return ret;
+                }
+
+                if ((size_t) n < l-1) {
+                        c[n] = 0;
+                        *r = c;
+                        return 0;
+                }
+
+                free(c);
+                l *= 2;
+        }
+}
+
+int readlink_and_make_absolute(const char *p, char **r) {
+        char *target, *k;
+        int j;
+
+        assert(p);
+        assert(r);
+
+        if ((j = readlink_malloc(p, &target)) < 0)
+                return j;
+
+        k = file_in_same_dir(p, target);
+        free(target);
+
+        if (!k)
+                return -ENOMEM;
+
+        *r = k;
+        return 0;
+}
+
+int readlink_and_canonicalize(const char *p, char **r) {
+        char *t, *s;
+        int j;
+
+        assert(p);
+        assert(r);
+
+        j = readlink_and_make_absolute(p, &t);
+        if (j < 0)
+                return j;
+
+        s = canonicalize_file_name(t);
+        if (s) {
+                free(t);
+                *r = s;
+        } else
+                *r = t;
+
+        path_kill_slashes(*r);
+
+        return 0;
+}
+
+int parent_of_path(const char *path, char **_r) {
+        const char *e, *a = NULL, *b = NULL, *p;
+        char *r;
+        bool slash = false;
+
+        assert(path);
+        assert(_r);
+
+        if (!*path)
+                return -EINVAL;
+
+        for (e = path; *e; e++) {
+
+                if (!slash && *e == '/') {
+                        a = b;
+                        b = e;
+                        slash = true;
+                } else if (slash && *e != '/')
+                        slash = false;
+        }
+
+        if (*(e-1) == '/')
+                p = a;
+        else
+                p = b;
+
+        if (!p)
+                return -EINVAL;
+
+        if (p == path)
+                r = strdup("/");
+        else
+                r = strndup(path, p-path);
+
+        if (!r)
+                return -ENOMEM;
+
+        *_r = r;
+        return 0;
+}
+
+
+char *file_name_from_path(const char *p) {
+        char *r;
+
+        assert(p);
+
+        if ((r = strrchr(p, '/')))
+                return r + 1;
+
+        return (char*) p;
+}
+
+bool path_is_absolute(const char *p) {
+        assert(p);
+
+        return p[0] == '/';
+}
+
+bool is_path(const char *p) {
+
+        return !!strchr(p, '/');
+}
+
+char *path_make_absolute(const char *p, const char *prefix) {
+        assert(p);
+
+        /* Makes every item in the list an absolute path by prepending
+         * the prefix, if specified and necessary */
+
+        if (path_is_absolute(p) || !prefix)
+                return strdup(p);
+
+        return join(prefix, "/", p, NULL);
+}
+
+char *path_make_absolute_cwd(const char *p) {
+        char *cwd, *r;
+
+        assert(p);
+
+        /* Similar to path_make_absolute(), but prefixes with the
+         * current working directory. */
+
+        if (path_is_absolute(p))
+                return strdup(p);
+
+        if (!(cwd = get_current_dir_name()))
+                return NULL;
+
+        r = path_make_absolute(p, cwd);
+        free(cwd);
+
+        return r;
+}
+
+char **strv_path_make_absolute_cwd(char **l) {
+        char **s;
+
+        /* Goes through every item in the string list and makes it
+         * absolute. This works in place and won't rollback any
+         * changes on failure. */
+
+        STRV_FOREACH(s, l) {
+                char *t;
+
+                if (!(t = path_make_absolute_cwd(*s)))
+                        return NULL;
+
+                free(*s);
+                *s = t;
+        }
+
+        return l;
+}
+
+char **strv_path_canonicalize(char **l) {
+        char **s;
+        unsigned k = 0;
+        bool enomem = false;
+
+        if (strv_isempty(l))
+                return l;
+
+        /* Goes through every item in the string list and canonicalize
+         * the path. This works in place and won't rollback any
+         * changes on failure. */
+
+        STRV_FOREACH(s, l) {
+                char *t, *u;
+
+                t = path_make_absolute_cwd(*s);
+                free(*s);
+
+                if (!t) {
+                        enomem = true;
+                        continue;
+                }
+
+                errno = 0;
+                u = canonicalize_file_name(t);
+                free(t);
+
+                if (!u) {
+                        if (errno == ENOMEM || !errno)
+                                enomem = true;
+
+                        continue;
+                }
+
+                l[k++] = u;
+        }
+
+        l[k] = NULL;
+
+        if (enomem)
+                return NULL;
+
+        return l;
+}
+
+char **strv_path_remove_empty(char **l) {
+        char **f, **t;
+
+        if (!l)
+                return NULL;
+
+        for (f = t = l; *f; f++) {
+
+                if (dir_is_empty(*f) > 0) {
+                        free(*f);
+                        continue;
+                }
+
+                *(t++) = *f;
+        }
+
+        *t = NULL;
+        return l;
+}
+
+int reset_all_signal_handlers(void) {
+        int sig;
+
+        for (sig = 1; sig < _NSIG; sig++) {
+                struct sigaction sa;
+
+                if (sig == SIGKILL || sig == SIGSTOP)
+                        continue;
+
+                zero(sa);
+                sa.sa_handler = SIG_DFL;
+                sa.sa_flags = SA_RESTART;
+
+                /* On Linux the first two RT signals are reserved by
+                 * glibc, and sigaction() will return EINVAL for them. */
+                if ((sigaction(sig, &sa, NULL) < 0))
+                        if (errno != EINVAL)
+                                return -errno;
+        }
+
+        return 0;
+}
+
+char *strstrip(char *s) {
+        char *e;
+
+        /* Drops trailing whitespace. Modifies the string in
+         * place. Returns pointer to first non-space character */
+
+        s += strspn(s, WHITESPACE);
+
+        for (e = strchr(s, 0); e > s; e --)
+                if (!strchr(WHITESPACE, e[-1]))
+                        break;
+
+        *e = 0;
+
+        return s;
+}
+
+char *delete_chars(char *s, const char *bad) {
+        char *f, *t;
+
+        /* Drops all whitespace, regardless where in the string */
+
+        for (f = s, t = s; *f; f++) {
+                if (strchr(bad, *f))
+                        continue;
+
+                *(t++) = *f;
+        }
+
+        *t = 0;
+
+        return s;
+}
+
+bool in_charset(const char *s, const char* charset) {
+        const char *i;
+
+        assert(s);
+        assert(charset);
+
+        for (i = s; *i; i++)
+                if (!strchr(charset, *i))
+                        return false;
+
+        return true;
+}
+
+char *file_in_same_dir(const char *path, const char *filename) {
+        char *e, *r;
+        size_t k;
+
+        assert(path);
+        assert(filename);
+
+        /* This removes the last component of path and appends
+         * filename, unless the latter is absolute anyway or the
+         * former isn't */
+
+        if (path_is_absolute(filename))
+                return strdup(filename);
+
+        if (!(e = strrchr(path, '/')))
+                return strdup(filename);
+
+        k = strlen(filename);
+        if (!(r = new(char, e-path+1+k+1)))
+                return NULL;
+
+        memcpy(r, path, e-path+1);
+        memcpy(r+(e-path)+1, filename, k+1);
+
+        return r;
+}
+
+int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+        struct stat st;
+
+        if (label_mkdir(path, mode) >= 0)
+                if (chmod_and_chown(path, mode, uid, gid) < 0)
+                        return -errno;
+
+        if (lstat(path, &st) < 0)
+                return -errno;
+
+        if ((st.st_mode & 0777) != mode ||
+            st.st_uid != uid ||
+            st.st_gid != gid ||
+            !S_ISDIR(st.st_mode)) {
+                errno = EEXIST;
+                return -errno;
+        }
+
+        return 0;
+}
+
+
+int mkdir_parents(const char *path, mode_t mode) {
+        const char *p, *e;
+
+        assert(path);
+
+        /* Creates every parent directory in the path except the last
+         * component. */
+
+        p = path + strspn(path, "/");
+        for (;;) {
+                int r;
+                char *t;
+
+                e = p + strcspn(p, "/");
+                p = e + strspn(e, "/");
+
+                /* Is this the last component? If so, then we're
+                 * done */
+                if (*p == 0)
+                        return 0;
+
+                if (!(t = strndup(path, e - path)))
+                        return -ENOMEM;
+
+                r = label_mkdir(t, mode);
+                free(t);
+
+                if (r < 0 && errno != EEXIST)
+                        return -errno;
+        }
+}
+
+int mkdir_p(const char *path, mode_t mode) {
+        int r;
+
+        /* Like mkdir -p */
+
+        if ((r = mkdir_parents(path, mode)) < 0)
+                return r;
+
+        if (label_mkdir(path, mode) < 0 && errno != EEXIST)
+                return -errno;
+
+        return 0;
+}
+
+int rmdir_parents(const char *path, const char *stop) {
+        size_t l;
+        int r = 0;
+
+        assert(path);
+        assert(stop);
+
+        l = strlen(path);
+
+        /* Skip trailing slashes */
+        while (l > 0 && path[l-1] == '/')
+                l--;
+
+        while (l > 0) {
+                char *t;
+
+                /* Skip last component */
+                while (l > 0 && path[l-1] != '/')
+                        l--;
+
+                /* Skip trailing slashes */
+                while (l > 0 && path[l-1] == '/')
+                        l--;
+
+                if (l <= 0)
+                        break;
+
+                if (!(t = strndup(path, l)))
+                        return -ENOMEM;
+
+                if (path_startswith(stop, t)) {
+                        free(t);
+                        return 0;
+                }
+
+                r = rmdir(t);
+                free(t);
+
+                if (r < 0)
+                        if (errno != ENOENT)
+                                return -errno;
+        }
+
+        return 0;
+}
+
+
+char hexchar(int x) {
+        static const char table[16] = "0123456789abcdef";
+
+        return table[x & 15];
+}
+
+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;
+}
+
+char octchar(int x) {
+        return '0' + (x & 7);
+}
+
+int unoctchar(char c) {
+
+        if (c >= '0' && c <= '7')
+                return c - '0';
+
+        return -1;
+}
+
+char decchar(int x) {
+        return '0' + (x % 10);
+}
+
+int undecchar(char c) {
+
+        if (c >= '0' && c <= '9')
+                return c - '0';
+
+        return -1;
+}
+
+char *cescape(const char *s) {
+        char *r, *t;
+        const char *f;
+
+        assert(s);
+
+        /* Does C style string escaping. */
+
+        if (!(r = new(char, strlen(s)*4 + 1)))
+                return NULL;
+
+        for (f = s, t = r; *f; f++)
+
+                switch (*f) {
+
+                case '\a':
+                        *(t++) = '\\';
+                        *(t++) = 'a';
+                        break;
+                case '\b':
+                        *(t++) = '\\';
+                        *(t++) = 'b';
+                        break;
+                case '\f':
+                        *(t++) = '\\';
+                        *(t++) = 'f';
+                        break;
+                case '\n':
+                        *(t++) = '\\';
+                        *(t++) = 'n';
+                        break;
+                case '\r':
+                        *(t++) = '\\';
+                        *(t++) = 'r';
+                        break;
+                case '\t':
+                        *(t++) = '\\';
+                        *(t++) = 't';
+                        break;
+                case '\v':
+                        *(t++) = '\\';
+                        *(t++) = 'v';
+                        break;
+                case '\\':
+                        *(t++) = '\\';
+                        *(t++) = '\\';
+                        break;
+                case '"':
+                        *(t++) = '\\';
+                        *(t++) = '"';
+                        break;
+                case '\'':
+                        *(t++) = '\\';
+                        *(t++) = '\'';
+                        break;
+
+                default:
+                        /* For special chars we prefer octal over
+                         * hexadecimal encoding, simply because glib's
+                         * g_strescape() does the same */
+                        if ((*f < ' ') || (*f >= 127)) {
+                                *(t++) = '\\';
+                                *(t++) = octchar((unsigned char) *f >> 6);
+                                *(t++) = octchar((unsigned char) *f >> 3);
+                                *(t++) = octchar((unsigned char) *f);
+                        } else
+                                *(t++) = *f;
+                        break;
+                }
+
+        *t = 0;
+
+        return r;
+}
+
+char *cunescape_length(const char *s, size_t length) {
+        char *r, *t;
+        const char *f;
+
+        assert(s);
+
+        /* Undoes C style string escaping */
+
+        r = new(char, length+1);
+        if (!r)
+                return r;
+
+        for (f = s, t = r; 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(const char *s) {
+        return cunescape_length(s, strlen(s));
+}
+
+char *xescape(const char *s, const char *bad) {
+        char *r, *t;
+        const char *f;
+
+        /* Escapes all chars in bad, in addition to \ and all special
+         * chars, in \xFF style escaping. May be reversed with
+         * cunescape. */
+
+        if (!(r = new(char, strlen(s)*4+1)))
+                return NULL;
+
+        for (f = s, t = r; *f; f++) {
+
+                if ((*f < ' ') || (*f >= 127) ||
+                    (*f == '\\') || strchr(bad, *f)) {
+                        *(t++) = '\\';
+                        *(t++) = 'x';
+                        *(t++) = hexchar(*f >> 4);
+                        *(t++) = hexchar(*f);
+                } else
+                        *(t++) = *f;
+        }
+
+        *t = 0;
+
+        return r;
+}
+
+char *bus_path_escape(const char *s) {
+        char *r, *t;
+        const char *f;
+
+        assert(s);
+
+        /* Escapes all chars that D-Bus' object path cannot deal
+         * with. Can be reverse with bus_path_unescape() */
+
+        if (!(r = new(char, strlen(s)*3+1)))
+                return NULL;
+
+        for (f = s, t = r; *f; f++) {
+
+                if (!(*f >= 'A' && *f <= 'Z') &&
+                    !(*f >= 'a' && *f <= 'z') &&
+                    !(*f >= '0' && *f <= '9')) {
+                        *(t++) = '_';
+                        *(t++) = hexchar(*f >> 4);
+                        *(t++) = hexchar(*f);
+                } else
+                        *(t++) = *f;
+        }
+
+        *t = 0;
+
+        return r;
+}
+
+char *bus_path_unescape(const char *f) {
+        char *r, *t;
+
+        assert(f);
+
+        if (!(r = strdup(f)))
+                return NULL;
+
+        for (t = r; *f; f++) {
+
+                if (*f == '_') {
+                        int a, b;
+
+                        if ((a = unhexchar(f[1])) < 0 ||
+                            (b = unhexchar(f[2])) < 0) {
+                                /* Invalid escape code, let's take it literal then */
+                                *(t++) = '_';
+                        } else {
+                                *(t++) = (char) ((a << 4) | b);
+                                f += 2;
+                        }
+                } else
+                        *(t++) = *f;
+        }
+
+        *t = 0;
+
+        return r;
+}
+
+char *path_kill_slashes(char *path) {
+        char *f, *t;
+        bool slash = false;
+
+        /* Removes redundant inner and trailing slashes. Modifies the
+         * passed string in-place.
+         *
+         * ///foo///bar/ becomes /foo/bar
+         */
+
+        for (f = path, t = path; *f; f++) {
+
+                if (*f == '/') {
+                        slash = true;
+                        continue;
+                }
+
+                if (slash) {
+                        slash = false;
+                        *(t++) = '/';
+                }
+
+                *(t++) = *f;
+        }
+
+        /* Special rule, if we are talking of the root directory, a
+        trailing slash is good */
+
+        if (t == path && slash)
+                *(t++) = '/';
+
+        *t = 0;
+        return path;
+}
+
+bool path_startswith(const char *path, const char *prefix) {
+        assert(path);
+        assert(prefix);
+
+        if ((path[0] == '/') != (prefix[0] == '/'))
+                return false;
+
+        for (;;) {
+                size_t a, b;
+
+                path += strspn(path, "/");
+                prefix += strspn(prefix, "/");
+
+                if (*prefix == 0)
+                        return true;
+
+                if (*path == 0)
+                        return false;
+
+                a = strcspn(path, "/");
+                b = strcspn(prefix, "/");
+
+                if (a != b)
+                        return false;
+
+                if (memcmp(path, prefix, a) != 0)
+                        return false;
+
+                path += a;
+                prefix += b;
+        }
+}
+
+bool path_equal(const char *a, const char *b) {
+        assert(a);
+        assert(b);
+
+        if ((a[0] == '/') != (b[0] == '/'))
+                return false;
+
+        for (;;) {
+                size_t j, k;
+
+                a += strspn(a, "/");
+                b += strspn(b, "/");
+
+                if (*a == 0 && *b == 0)
+                        return true;
+
+                if (*a == 0 || *b == 0)
+                        return false;
+
+                j = strcspn(a, "/");
+                k = strcspn(b, "/");
+
+                if (j != k)
+                        return false;
+
+                if (memcmp(a, b, j) != 0)
+                        return false;
+
+                a += j;
+                b += k;
+        }
+}
+
+char *ascii_strlower(char *t) {
+        char *p;
+
+        assert(t);
+
+        for (p = t; *p; p++)
+                if (*p >= 'A' && *p <= 'Z')
+                        *p = *p - 'A' + 'a';
+
+        return t;
+}
+
+bool ignore_file(const char *filename) {
+        assert(filename);
+
+        return
+                filename[0] == '.' ||
+                streq(filename, "lost+found") ||
+                streq(filename, "aquota.user") ||
+                streq(filename, "aquota.group") ||
+                endswith(filename, "~") ||
+                endswith(filename, ".rpmnew") ||
+                endswith(filename, ".rpmsave") ||
+                endswith(filename, ".rpmorig") ||
+                endswith(filename, ".dpkg-old") ||
+                endswith(filename, ".dpkg-new") ||
+                endswith(filename, ".swp");
+}
+
+int fd_nonblock(int fd, bool nonblock) {
+        int flags;
+
+        assert(fd >= 0);
+
+        if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
+                return -errno;
+
+        if (nonblock)
+                flags |= O_NONBLOCK;
+        else
+                flags &= ~O_NONBLOCK;
+
+        if (fcntl(fd, F_SETFL, flags) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int fd_cloexec(int fd, bool cloexec) {
+        int flags;
+
+        assert(fd >= 0);
+
+        if ((flags = fcntl(fd, F_GETFD, 0)) < 0)
+                return -errno;
+
+        if (cloexec)
+                flags |= FD_CLOEXEC;
+        else
+                flags &= ~FD_CLOEXEC;
+
+        if (fcntl(fd, F_SETFD, flags) < 0)
+                return -errno;
+
+        return 0;
+}
+
+static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
+        unsigned i;
+
+        assert(n_fdset == 0 || fdset);
+
+        for (i = 0; i < n_fdset; i++)
+                if (fdset[i] == fd)
+                        return true;
+
+        return false;
+}
+
+int close_all_fds(const int except[], unsigned n_except) {
+        DIR *d;
+        struct dirent *de;
+        int r = 0;
+
+        assert(n_except == 0 || except);
+
+        d = opendir("/proc/self/fd");
+        if (!d) {
+                int fd;
+                struct rlimit rl;
+
+                /* When /proc isn't available (for example in chroots)
+                 * the fallback is brute forcing through the fd
+                 * table */
+
+                assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
+                for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
+
+                        if (fd_in_set(fd, except, n_except))
+                                continue;
+
+                        if (close_nointr(fd) < 0)
+                                if (errno != EBADF && r == 0)
+                                        r = -errno;
+                }
+
+                return r;
+        }
+
+        while ((de = readdir(d))) {
+                int fd = -1;
+
+                if (ignore_file(de->d_name))
+                        continue;
+
+                if (safe_atoi(de->d_name, &fd) < 0)
+                        /* Let's better ignore this, just in case */
+                        continue;
+
+                if (fd < 3)
+                        continue;
+
+                if (fd == dirfd(d))
+                        continue;
+
+                if (fd_in_set(fd, except, n_except))
+                        continue;
+
+                if (close_nointr(fd) < 0) {
+                        /* Valgrind has its own FD and doesn't want to have it closed */
+                        if (errno != EBADF && r == 0)
+                                r = -errno;
+                }
+        }
+
+        closedir(d);
+        return r;
+}
+
+bool chars_intersect(const char *a, const char *b) {
+        const char *p;
+
+        /* Returns true if any of the chars in a are in b. */
+        for (p = a; *p; p++)
+                if (strchr(b, *p))
+                        return true;
+
+        return false;
+}
+
+char *format_timestamp(char *buf, size_t l, usec_t t) {
+        struct tm tm;
+        time_t sec;
+
+        assert(buf);
+        assert(l > 0);
+
+        if (t <= 0)
+                return NULL;
+
+        sec = (time_t) (t / USEC_PER_SEC);
+
+        if (strftime(buf, l, "%a, %d %b %Y %H:%M:%S %z", localtime_r(&sec, &tm)) <= 0)
+                return NULL;
+
+        return buf;
+}
+
+char *format_timestamp_pretty(char *buf, size_t l, usec_t t) {
+        usec_t n, d;
+
+        n = now(CLOCK_REALTIME);
+
+        if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
+                return NULL;
+
+        d = n - t;
+
+        if (d >= USEC_PER_YEAR)
+                snprintf(buf, l, "%llu years and %llu months ago",
+                         (unsigned long long) (d / USEC_PER_YEAR),
+                         (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
+        else if (d >= USEC_PER_MONTH)
+                snprintf(buf, l, "%llu months and %llu days ago",
+                         (unsigned long long) (d / USEC_PER_MONTH),
+                         (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
+        else if (d >= USEC_PER_WEEK)
+                snprintf(buf, l, "%llu weeks and %llu days ago",
+                         (unsigned long long) (d / USEC_PER_WEEK),
+                         (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
+        else if (d >= 2*USEC_PER_DAY)
+                snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
+        else if (d >= 25*USEC_PER_HOUR)
+                snprintf(buf, l, "1 day and %lluh ago",
+                         (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
+        else if (d >= 6*USEC_PER_HOUR)
+                snprintf(buf, l, "%lluh ago",
+                         (unsigned long long) (d / USEC_PER_HOUR));
+        else if (d >= USEC_PER_HOUR)
+                snprintf(buf, l, "%lluh %llumin ago",
+                         (unsigned long long) (d / USEC_PER_HOUR),
+                         (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
+        else if (d >= 5*USEC_PER_MINUTE)
+                snprintf(buf, l, "%llumin ago",
+                         (unsigned long long) (d / USEC_PER_MINUTE));
+        else if (d >= USEC_PER_MINUTE)
+                snprintf(buf, l, "%llumin %llus ago",
+                         (unsigned long long) (d / USEC_PER_MINUTE),
+                         (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
+        else if (d >= USEC_PER_SEC)
+                snprintf(buf, l, "%llus ago",
+                         (unsigned long long) (d / USEC_PER_SEC));
+        else if (d >= USEC_PER_MSEC)
+                snprintf(buf, l, "%llums ago",
+                         (unsigned long long) (d / USEC_PER_MSEC));
+        else if (d > 0)
+                snprintf(buf, l, "%lluus ago",
+                         (unsigned long long) d);
+        else
+                snprintf(buf, l, "now");
+
+        buf[l-1] = 0;
+        return buf;
+}
+
+char *format_timespan(char *buf, size_t l, usec_t t) {
+        static const struct {
+                const char *suffix;
+                usec_t usec;
+        } table[] = {
+                { "w", USEC_PER_WEEK },
+                { "d", USEC_PER_DAY },
+                { "h", USEC_PER_HOUR },
+                { "min", USEC_PER_MINUTE },
+                { "s", USEC_PER_SEC },
+                { "ms", USEC_PER_MSEC },
+                { "us", 1 },
+        };
+
+        unsigned i;
+        char *p = buf;
+
+        assert(buf);
+        assert(l > 0);
+
+        if (t == (usec_t) -1)
+                return NULL;
+
+        if (t == 0) {
+                snprintf(p, l, "0");
+                p[l-1] = 0;
+                return p;
+        }
+
+        /* The result of this function can be parsed with parse_usec */
+
+        for (i = 0; i < ELEMENTSOF(table); i++) {
+                int k;
+                size_t n;
+
+                if (t < table[i].usec)
+                        continue;
+
+                if (l <= 1)
+                        break;
+
+                k = snprintf(p, l, "%s%llu%s", p > buf ? " " : "", (unsigned long long) (t / table[i].usec), table[i].suffix);
+                n = MIN((size_t) k, l);
+
+                l -= n;
+                p += n;
+
+                t %= table[i].usec;
+        }
+
+        *p = 0;
+
+        return buf;
+}
+
+bool fstype_is_network(const char *fstype) {
+        static const char * const table[] = {
+                "cifs",
+                "smbfs",
+                "ncpfs",
+                "nfs",
+                "nfs4",
+                "gfs",
+                "gfs2"
+        };
+
+        unsigned i;
+
+        for (i = 0; i < ELEMENTSOF(table); i++)
+                if (streq(table[i], fstype))
+                        return true;
+
+        return false;
+}
+
+int chvt(int vt) {
+        int fd, r = 0;
+
+        if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+                return -errno;
+
+        if (vt < 0) {
+                int tiocl[2] = {
+                        TIOCL_GETKMSGREDIRECT,
+                        0
+                };
+
+                if (ioctl(fd, TIOCLINUX, tiocl) < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+
+                vt = tiocl[0] <= 0 ? 1 : tiocl[0];
+        }
+
+        if (ioctl(fd, VT_ACTIVATE, vt) < 0)
+                r = -errno;
+
+fail:
+        close_nointr_nofail(fd);
+        return r;
+}
+
+int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
+        struct termios old_termios, new_termios;
+        char c;
+        char line[LINE_MAX];
+
+        assert(f);
+        assert(ret);
+
+        if (tcgetattr(fileno(f), &old_termios) >= 0) {
+                new_termios = old_termios;
+
+                new_termios.c_lflag &= ~ICANON;
+                new_termios.c_cc[VMIN] = 1;
+                new_termios.c_cc[VTIME] = 0;
+
+                if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
+                        size_t k;
+
+                        if (t != (usec_t) -1) {
+                                if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
+                                        tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+                                        return -ETIMEDOUT;
+                                }
+                        }
+
+                        k = fread(&c, 1, 1, f);
+
+                        tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+
+                        if (k <= 0)
+                                return -EIO;
+
+                        if (need_nl)
+                                *need_nl = c != '\n';
+
+                        *ret = c;
+                        return 0;
+                }
+        }
+
+        if (t != (usec_t) -1)
+                if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
+                        return -ETIMEDOUT;
+
+        if (!fgets(line, sizeof(line), f))
+                return -EIO;
+
+        truncate_nl(line);
+
+        if (strlen(line) != 1)
+                return -EBADMSG;
+
+        if (need_nl)
+                *need_nl = false;
+
+        *ret = line[0];
+        return 0;
+}
+
+int ask(char *ret, const char *replies, const char *text, ...) {
+        bool on_tty;
+
+        assert(ret);
+        assert(replies);
+        assert(text);
+
+        on_tty = isatty(STDOUT_FILENO);
+
+        for (;;) {
+                va_list ap;
+                char c;
+                int r;
+                bool need_nl = true;
+
+                if (on_tty)
+                        fputs(ANSI_HIGHLIGHT_ON, stdout);
+
+                va_start(ap, text);
+                vprintf(text, ap);
+                va_end(ap);
+
+                if (on_tty)
+                        fputs(ANSI_HIGHLIGHT_OFF, stdout);
+
+                fflush(stdout);
+
+                r = read_one_char(stdin, &c, (usec_t) -1, &need_nl);
+                if (r < 0) {
+
+                        if (r == -EBADMSG) {
+                                puts("Bad input, please try again.");
+                                continue;
+                        }
+
+                        putchar('\n');
+                        return r;
+                }
+
+                if (need_nl)
+                        putchar('\n');
+
+                if (strchr(replies, c)) {
+                        *ret = c;
+                        return 0;
+                }
+
+                puts("Read unexpected character, please try again.");
+        }
+}
+
+int reset_terminal_fd(int fd, bool switch_to_text) {
+        struct termios termios;
+        int r = 0;
+
+        /* Set terminal to some sane defaults */
+
+        assert(fd >= 0);
+
+        /* We leave locked terminal attributes untouched, so that
+         * Plymouth may set whatever it wants to set, and we don't
+         * interfere with that. */
+
+        /* Disable exclusive mode, just in case */
+        ioctl(fd, TIOCNXCL);
+
+        /* Switch to text mode */
+        if (switch_to_text)
+                ioctl(fd, KDSETMODE, KD_TEXT);
+
+        /* Enable console unicode mode */
+        ioctl(fd, KDSKBMODE, K_UNICODE);
+
+        if (tcgetattr(fd, &termios) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        /* We only reset the stuff that matters to the software. How
+         * hardware is set up we don't touch assuming that somebody
+         * else will do that for us */
+
+        termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
+        termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
+        termios.c_oflag |= ONLCR;
+        termios.c_cflag |= CREAD;
+        termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
+
+        termios.c_cc[VINTR]    =   03;  /* ^C */
+        termios.c_cc[VQUIT]    =  034;  /* ^\ */
+        termios.c_cc[VERASE]   = 0177;
+        termios.c_cc[VKILL]    =  025;  /* ^X */
+        termios.c_cc[VEOF]     =   04;  /* ^D */
+        termios.c_cc[VSTART]   =  021;  /* ^Q */
+        termios.c_cc[VSTOP]    =  023;  /* ^S */
+        termios.c_cc[VSUSP]    =  032;  /* ^Z */
+        termios.c_cc[VLNEXT]   =  026;  /* ^V */
+        termios.c_cc[VWERASE]  =  027;  /* ^W */
+        termios.c_cc[VREPRINT] =  022;  /* ^R */
+        termios.c_cc[VEOL]     =    0;
+        termios.c_cc[VEOL2]    =    0;
+
+        termios.c_cc[VTIME]  = 0;
+        termios.c_cc[VMIN]   = 1;
+
+        if (tcsetattr(fd, TCSANOW, &termios) < 0)
+                r = -errno;
+
+finish:
+        /* Just in case, flush all crap out */
+        tcflush(fd, TCIOFLUSH);
+
+        return r;
+}
+
+int reset_terminal(const char *name) {
+        int fd, r;
+
+        fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return fd;
+
+        r = reset_terminal_fd(fd, true);
+        close_nointr_nofail(fd);
+
+        return r;
+}
+
+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;
+}
+
+int flush_fd(int fd) {
+        struct pollfd pollfd;
+
+        zero(pollfd);
+        pollfd.fd = fd;
+        pollfd.events = POLLIN;
+
+        for (;;) {
+                char buf[LINE_MAX];
+                ssize_t l;
+                int r;
+
+                if ((r = poll(&pollfd, 1, 0)) < 0) {
+
+                        if (errno == EINTR)
+                                continue;
+
+                        return -errno;
+                }
+
+                if (r == 0)
+                        return 0;
+
+                if ((l = read(fd, buf, sizeof(buf))) < 0) {
+
+                        if (errno == EINTR)
+                                continue;
+
+                        if (errno == EAGAIN)
+                                return 0;
+
+                        return -errno;
+                }
+
+                if (l <= 0)
+                        return 0;
+        }
+}
+
+int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm) {
+        int fd = -1, notify = -1, r, wd = -1;
+
+        assert(name);
+
+        /* We use inotify to be notified when the tty is closed. We
+         * create the watch before checking if we can actually acquire
+         * it, so that we don't lose any event.
+         *
+         * Note: strictly speaking this actually watches for the
+         * device being closed, it does *not* really watch whether a
+         * tty loses its controlling process. However, unless some
+         * rogue process uses TIOCNOTTY on /dev/tty *after* closing
+         * its tty otherwise this will not become a problem. As long
+         * as the administrator makes sure not configure any service
+         * on the same tty as an untrusted user this should not be a
+         * problem. (Which he probably should not do anyway.) */
+
+        if (!fail && !force) {
+                if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+
+                if ((wd = inotify_add_watch(notify, name, IN_CLOSE)) < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+        }
+
+        for (;;) {
+                if (notify >= 0)
+                        if ((r = flush_fd(notify)) < 0)
+                                goto fail;
+
+                /* We pass here O_NOCTTY only so that we can check the return
+                 * value TIOCSCTTY and have a reliable way to figure out if we
+                 * successfully became the controlling process of the tty */
+                if ((fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+                        return fd;
+
+                /* First, try to get the tty */
+                r = ioctl(fd, TIOCSCTTY, force);
+
+                /* Sometimes it makes sense to ignore TIOCSCTTY
+                 * returning EPERM, i.e. when very likely we already
+                 * are have this controlling terminal. */
+                if (r < 0 && errno == EPERM && ignore_tiocstty_eperm)
+                        r = 0;
+
+                if (r < 0 && (force || fail || errno != EPERM)) {
+                        r = -errno;
+                        goto fail;
+                }
+
+                if (r >= 0)
+                        break;
+
+                assert(!fail);
+                assert(!force);
+                assert(notify >= 0);
+
+                for (;;) {
+                        uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
+                        ssize_t l;
+                        struct inotify_event *e;
+
+                        if ((l = read(notify, inotify_buffer, sizeof(inotify_buffer))) < 0) {
+
+                                if (errno == EINTR)
+                                        continue;
+
+                                r = -errno;
+                                goto fail;
+                        }
+
+                        e = (struct inotify_event*) inotify_buffer;
+
+                        while (l > 0) {
+                                size_t step;
+
+                                if (e->wd != wd || !(e->mask & IN_CLOSE)) {
+                                        r = -EIO;
+                                        goto fail;
+                                }
+
+                                step = sizeof(struct inotify_event) + e->len;
+                                assert(step <= (size_t) l);
+
+                                e = (struct inotify_event*) ((uint8_t*) e + step);
+                                l -= step;
+                        }
+
+                        break;
+                }
+
+                /* We close the tty fd here since if the old session
+                 * ended our handle will be dead. It's important that
+                 * we do this after sleeping, so that we don't enter
+                 * an endless loop. */
+                close_nointr_nofail(fd);
+        }
+
+        if (notify >= 0)
+                close_nointr_nofail(notify);
+
+        r = reset_terminal_fd(fd, true);
+        if (r < 0)
+                log_warning("Failed to reset terminal: %s", strerror(-r));
+
+        return fd;
+
+fail:
+        if (fd >= 0)
+                close_nointr_nofail(fd);
+
+        if (notify >= 0)
+                close_nointr_nofail(notify);
+
+        return r;
+}
+
+int release_terminal(void) {
+        int r = 0, fd;
+        struct sigaction sa_old, sa_new;
+
+        if ((fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC)) < 0)
+                return -errno;
+
+        /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
+         * by our own TIOCNOTTY */
+
+        zero(sa_new);
+        sa_new.sa_handler = SIG_IGN;
+        sa_new.sa_flags = SA_RESTART;
+        assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
+
+        if (ioctl(fd, TIOCNOTTY) < 0)
+                r = -errno;
+
+        assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
+
+        close_nointr_nofail(fd);
+        return r;
+}
+
+int sigaction_many(const struct sigaction *sa, ...) {
+        va_list ap;
+        int r = 0, sig;
+
+        va_start(ap, sa);
+        while ((sig = va_arg(ap, int)) > 0)
+                if (sigaction(sig, sa, NULL) < 0)
+                        r = -errno;
+        va_end(ap);
+
+        return r;
+}
+
+int ignore_signals(int sig, ...) {
+        struct sigaction sa;
+        va_list ap;
+        int r = 0;
+
+        zero(sa);
+        sa.sa_handler = SIG_IGN;
+        sa.sa_flags = SA_RESTART;
+
+        if (sigaction(sig, &sa, NULL) < 0)
+                r = -errno;
+
+        va_start(ap, sig);
+        while ((sig = va_arg(ap, int)) > 0)
+                if (sigaction(sig, &sa, NULL) < 0)
+                        r = -errno;
+        va_end(ap);
+
+        return r;
+}
+
+int default_signals(int sig, ...) {
+        struct sigaction sa;
+        va_list ap;
+        int r = 0;
+
+        zero(sa);
+        sa.sa_handler = SIG_DFL;
+        sa.sa_flags = SA_RESTART;
+
+        if (sigaction(sig, &sa, NULL) < 0)
+                r = -errno;
+
+        va_start(ap, sig);
+        while ((sig = va_arg(ap, int)) > 0)
+                if (sigaction(sig, &sa, NULL) < 0)
+                        r = -errno;
+        va_end(ap);
+
+        return r;
+}
+
+int close_pipe(int p[]) {
+        int a = 0, b = 0;
+
+        assert(p);
+
+        if (p[0] >= 0) {
+                a = close_nointr(p[0]);
+                p[0] = -1;
+        }
+
+        if (p[1] >= 0) {
+                b = close_nointr(p[1]);
+                p[1] = -1;
+        }
+
+        return a < 0 ? a : b;
+}
+
+ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
+        uint8_t *p;
+        ssize_t n = 0;
+
+        assert(fd >= 0);
+        assert(buf);
+
+        p = buf;
+
+        while (nbytes > 0) {
+                ssize_t k;
+
+                if ((k = read(fd, p, nbytes)) <= 0) {
+
+                        if (k < 0 && errno == EINTR)
+                                continue;
+
+                        if (k < 0 && errno == EAGAIN && do_poll) {
+                                struct pollfd pollfd;
+
+                                zero(pollfd);
+                                pollfd.fd = fd;
+                                pollfd.events = POLLIN;
+
+                                if (poll(&pollfd, 1, -1) < 0) {
+                                        if (errno == EINTR)
+                                                continue;
+
+                                        return n > 0 ? n : -errno;
+                                }
+
+                                if (pollfd.revents != POLLIN)
+                                        return n > 0 ? n : -EIO;
+
+                                continue;
+                        }
+
+                        return n > 0 ? n : (k < 0 ? -errno : 0);
+                }
+
+                p += k;
+                nbytes -= k;
+                n += k;
+        }
+
+        return n;
+}
+
+ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
+        const uint8_t *p;
+        ssize_t n = 0;
+
+        assert(fd >= 0);
+        assert(buf);
+
+        p = buf;
+
+        while (nbytes > 0) {
+                ssize_t k;
+
+                k = write(fd, p, nbytes);
+                if (k <= 0) {
+
+                        if (k < 0 && errno == EINTR)
+                                continue;
+
+                        if (k < 0 && errno == EAGAIN && do_poll) {
+                                struct pollfd pollfd;
+
+                                zero(pollfd);
+                                pollfd.fd = fd;
+                                pollfd.events = POLLOUT;
+
+                                if (poll(&pollfd, 1, -1) < 0) {
+                                        if (errno == EINTR)
+                                                continue;
+
+                                        return n > 0 ? n : -errno;
+                                }
+
+                                if (pollfd.revents != POLLOUT)
+                                        return n > 0 ? n : -EIO;
+
+                                continue;
+                        }
+
+                        return n > 0 ? n : (k < 0 ? -errno : 0);
+                }
+
+                p += k;
+                nbytes -= k;
+                n += k;
+        }
+
+        return n;
+}
+
+int path_is_mount_point(const char *t, bool allow_symlink) {
+        struct stat a, b;
+        char *parent;
+        int r;
+
+        if (allow_symlink)
+                r = stat(t, &a);
+        else
+                r = lstat(t, &a);
+
+        if (r < 0) {
+                if (errno == ENOENT)
+                        return 0;
+
+                return -errno;
+        }
+
+        r = parent_of_path(t, &parent);
+        if (r < 0)
+                return r;
+
+        r = lstat(parent, &b);
+        free(parent);
+
+        if (r < 0)
+                return -errno;
+
+        return a.st_dev != b.st_dev;
+}
+
+int parse_usec(const char *t, usec_t *usec) {
+        static const struct {
+                const char *suffix;
+                usec_t usec;
+        } table[] = {
+                { "sec", USEC_PER_SEC },
+                { "s", USEC_PER_SEC },
+                { "min", USEC_PER_MINUTE },
+                { "hr", USEC_PER_HOUR },
+                { "h", USEC_PER_HOUR },
+                { "d", USEC_PER_DAY },
+                { "w", USEC_PER_WEEK },
+                { "msec", USEC_PER_MSEC },
+                { "ms", USEC_PER_MSEC },
+                { "m", USEC_PER_MINUTE },
+                { "usec", 1ULL },
+                { "us", 1ULL },
+                { "", USEC_PER_SEC },
+        };
+
+        const char *p;
+        usec_t r = 0;
+
+        assert(t);
+        assert(usec);
+
+        p = t;
+        do {
+                long long l;
+                char *e;
+                unsigned i;
+
+                errno = 0;
+                l = strtoll(p, &e, 10);
+
+                if (errno != 0)
+                        return -errno;
+
+                if (l < 0)
+                        return -ERANGE;
+
+                if (e == p)
+                        return -EINVAL;
+
+                e += strspn(e, WHITESPACE);
+
+                for (i = 0; i < ELEMENTSOF(table); i++)
+                        if (startswith(e, table[i].suffix)) {
+                                r += (usec_t) l * table[i].usec;
+                                p = e + strlen(table[i].suffix);
+                                break;
+                        }
+
+                if (i >= ELEMENTSOF(table))
+                        return -EINVAL;
+
+        } while (*p != 0);
+
+        *usec = r;
+
+        return 0;
+}
+
+int parse_bytes(const char *t, off_t *bytes) {
+        static const struct {
+                const char *suffix;
+                off_t factor;
+        } table[] = {
+                { "B", 1 },
+                { "K", 1024ULL },
+                { "M", 1024ULL*1024ULL },
+                { "G", 1024ULL*1024ULL*1024ULL },
+                { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+                { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+                { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+                { "", 1 },
+        };
+
+        const char *p;
+        off_t r = 0;
+
+        assert(t);
+        assert(bytes);
+
+        p = t;
+        do {
+                long long l;
+                char *e;
+                unsigned i;
+
+                errno = 0;
+                l = strtoll(p, &e, 10);
+
+                if (errno != 0)
+                        return -errno;
+
+                if (l < 0)
+                        return -ERANGE;
+
+                if (e == p)
+                        return -EINVAL;
+
+                e += strspn(e, WHITESPACE);
+
+                for (i = 0; i < ELEMENTSOF(table); i++)
+                        if (startswith(e, table[i].suffix)) {
+                                r += (off_t) l * table[i].factor;
+                                p = e + strlen(table[i].suffix);
+                                break;
+                        }
+
+                if (i >= ELEMENTSOF(table))
+                        return -EINVAL;
+
+        } while (*p != 0);
+
+        *bytes = r;
+
+        return 0;
+}
+
+int make_stdio(int fd) {
+        int r, s, t;
+
+        assert(fd >= 0);
+
+        r = dup2(fd, STDIN_FILENO);
+        s = dup2(fd, STDOUT_FILENO);
+        t = dup2(fd, STDERR_FILENO);
+
+        if (fd >= 3)
+                close_nointr_nofail(fd);
+
+        if (r < 0 || s < 0 || t < 0)
+                return -errno;
+
+        fd_cloexec(STDIN_FILENO, false);
+        fd_cloexec(STDOUT_FILENO, false);
+        fd_cloexec(STDERR_FILENO, false);
+
+        return 0;
+}
+
+int make_null_stdio(void) {
+        int null_fd;
+
+        if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0)
+                return -errno;
+
+        return make_stdio(null_fd);
+}
+
+bool is_device_path(const char *path) {
+
+        /* Returns true on paths that refer to a device, either in
+         * sysfs or in /dev */
+
+        return
+                path_startswith(path, "/dev/") ||
+                path_startswith(path, "/sys/");
+}
+
+int dir_is_empty(const char *path) {
+        DIR *d;
+        int r;
+        struct dirent buf, *de;
+
+        if (!(d = opendir(path)))
+                return -errno;
+
+        for (;;) {
+                if ((r = readdir_r(d, &buf, &de)) > 0) {
+                        r = -r;
+                        break;
+                }
+
+                if (!de) {
+                        r = 1;
+                        break;
+                }
+
+                if (!ignore_file(de->d_name)) {
+                        r = 0;
+                        break;
+                }
+        }
+
+        closedir(d);
+        return r;
+}
+
+unsigned long long random_ull(void) {
+        int fd;
+        uint64_t ull;
+        ssize_t r;
+
+        if ((fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0)
+                goto fallback;
+
+        r = loop_read(fd, &ull, sizeof(ull), true);
+        close_nointr_nofail(fd);
+
+        if (r != sizeof(ull))
+                goto fallback;
+
+        return ull;
+
+fallback:
+        return random() * RAND_MAX + random();
+}
+
+void rename_process(const char name[8]) {
+        assert(name);
+
+        /* This is a like a poor man's setproctitle(). It changes the
+         * comm field, argv[0], and also the glibc's internally used
+         * name of the process. For the first one a limit of 16 chars
+         * applies, to the second one usually one of 10 (i.e. length
+         * of "/sbin/init"), to the third one one of 7 (i.e. length of
+         * "systemd"). If you pass a longer string it will be
+         * truncated */
+
+        prctl(PR_SET_NAME, name);
+
+        if (program_invocation_name)
+                strncpy(program_invocation_name, name, strlen(program_invocation_name));
+
+        if (saved_argc > 0) {
+                int i;
+
+                if (saved_argv[0])
+                        strncpy(saved_argv[0], name, strlen(saved_argv[0]));
+
+                for (i = 1; i < saved_argc; i++) {
+                        if (!saved_argv[i])
+                                break;
+
+                        memset(saved_argv[i], 0, strlen(saved_argv[i]));
+                }
+        }
+}
+
+void sigset_add_many(sigset_t *ss, ...) {
+        va_list ap;
+        int sig;
+
+        assert(ss);
+
+        va_start(ap, ss);
+        while ((sig = va_arg(ap, int)) > 0)
+                assert_se(sigaddset(ss, sig) == 0);
+        va_end(ap);
+}
+
+char* gethostname_malloc(void) {
+        struct utsname u;
+
+        assert_se(uname(&u) >= 0);
+
+        if (u.nodename[0])
+                return strdup(u.nodename);
+
+        return strdup(u.sysname);
+}
+
+char* getlogname_malloc(void) {
+        uid_t uid;
+        long bufsize;
+        char *buf, *name;
+        struct passwd pwbuf, *pw = NULL;
+        struct stat st;
+
+        if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
+                uid = st.st_uid;
+        else
+                uid = getuid();
+
+        /* Shortcut things to avoid NSS lookups */
+        if (uid == 0)
+                return strdup("root");
+
+        if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) <= 0)
+                bufsize = 4096;
+
+        if (!(buf = malloc(bufsize)))
+                return NULL;
+
+        if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw) {
+                name = strdup(pw->pw_name);
+                free(buf);
+                return name;
+        }
+
+        free(buf);
+
+        if (asprintf(&name, "%lu", (unsigned long) uid) < 0)
+                return NULL;
+
+        return name;
+}
+
+int getttyname_malloc(int fd, char **r) {
+        char path[PATH_MAX], *c;
+        int k;
+
+        assert(r);
+
+        if ((k = ttyname_r(fd, path, sizeof(path))) != 0)
+                return -k;
+
+        char_array_0(path);
+
+        if (!(c = strdup(startswith(path, "/dev/") ? path + 5 : path)))
+                return -ENOMEM;
+
+        *r = c;
+        return 0;
+}
+
+int getttyname_harder(int fd, char **r) {
+        int k;
+        char *s;
+
+        if ((k = getttyname_malloc(fd, &s)) < 0)
+                return k;
+
+        if (streq(s, "tty")) {
+                free(s);
+                return get_ctty(0, NULL, r);
+        }
+
+        *r = s;
+        return 0;
+}
+
+int get_ctty_devnr(pid_t pid, dev_t *d) {
+        int k;
+        char line[LINE_MAX], *p, *fn;
+        unsigned long ttynr;
+        FILE *f;
+
+        if (asprintf(&fn, "/proc/%lu/stat", (unsigned long) (pid <= 0 ? getpid() : pid)) < 0)
+                return -ENOMEM;
+
+        f = fopen(fn, "re");
+        free(fn);
+        if (!f)
+                return -errno;
+
+        if (!fgets(line, sizeof(line), f)) {
+                k = feof(f) ? -EIO : -errno;
+                fclose(f);
+                return k;
+        }
+
+        fclose(f);
+
+        p = strrchr(line, ')');
+        if (!p)
+                return -EIO;
+
+        p++;
+
+        if (sscanf(p, " "
+                   "%*c "  /* state */
+                   "%*d "  /* ppid */
+                   "%*d "  /* pgrp */
+                   "%*d "  /* session */
+                   "%lu ", /* ttynr */
+                   &ttynr) != 1)
+                return -EIO;
+
+        *d = (dev_t) ttynr;
+        return 0;
+}
+
+int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
+        int k;
+        char fn[PATH_MAX], *s, *b, *p;
+        dev_t devnr;
+
+        assert(r);
+
+        k = get_ctty_devnr(pid, &devnr);
+        if (k < 0)
+                return k;
+
+        snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr));
+        char_array_0(fn);
+
+        if ((k = readlink_malloc(fn, &s)) < 0) {
+
+                if (k != -ENOENT)
+                        return k;
+
+                /* This is an ugly hack */
+                if (major(devnr) == 136) {
+                        if (asprintf(&b, "pts/%lu", (unsigned long) minor(devnr)) < 0)
+                                return -ENOMEM;
+
+                        *r = b;
+                        if (_devnr)
+                                *_devnr = devnr;
+
+                        return 0;
+                }
+
+                /* Probably something like the ptys which have no
+                 * symlink in /dev/char. Let's return something
+                 * vaguely useful. */
+
+                if (!(b = strdup(fn + 5)))
+                        return -ENOMEM;
+
+                *r = b;
+                if (_devnr)
+                        *_devnr = devnr;
+
+                return 0;
+        }
+
+        if (startswith(s, "/dev/"))
+                p = s + 5;
+        else if (startswith(s, "../"))
+                p = s + 3;
+        else
+                p = s;
+
+        b = strdup(p);
+        free(s);
+
+        if (!b)
+                return -ENOMEM;
+
+        *r = b;
+        if (_devnr)
+                *_devnr = devnr;
+
+        return 0;
+}
+
+static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
+        DIR *d;
+        int ret = 0;
+
+        assert(fd >= 0);
+
+        /* This returns the first error we run into, but nevertheless
+         * tries to go on */
+
+        if (!(d = fdopendir(fd))) {
+                close_nointr_nofail(fd);
+
+                return errno == ENOENT ? 0 : -errno;
+        }
+
+        for (;;) {
+                struct dirent buf, *de;
+                bool is_dir, keep_around = false;
+                int r;
+
+                if ((r = readdir_r(d, &buf, &de)) != 0) {
+                        if (ret == 0)
+                                ret = -r;
+                        break;
+                }
+
+                if (!de)
+                        break;
+
+                if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+                        continue;
+
+                if (de->d_type == DT_UNKNOWN) {
+                        struct stat st;
+
+                        if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+                                if (ret == 0 && errno != ENOENT)
+                                        ret = -errno;
+                                continue;
+                        }
+
+                        if (honour_sticky)
+                                keep_around =
+                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
+                                        (st.st_mode & S_ISVTX);
+
+                        is_dir = S_ISDIR(st.st_mode);
+
+                } else {
+                        if (honour_sticky) {
+                                struct stat st;
+
+                                if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+                                        if (ret == 0 && errno != ENOENT)
+                                                ret = -errno;
+                                        continue;
+                                }
+
+                                keep_around =
+                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
+                                        (st.st_mode & S_ISVTX);
+                        }
+
+                        is_dir = de->d_type == DT_DIR;
+                }
+
+                if (is_dir) {
+                        int subdir_fd;
+
+                        subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+                        if (subdir_fd < 0) {
+                                if (ret == 0 && errno != ENOENT)
+                                        ret = -errno;
+                                continue;
+                        }
+
+                        if ((r = rm_rf_children(subdir_fd, only_dirs, honour_sticky)) < 0) {
+                                if (ret == 0)
+                                        ret = r;
+                        }
+
+                        if (!keep_around)
+                                if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
+                                        if (ret == 0 && errno != ENOENT)
+                                                ret = -errno;
+                                }
+
+                } else if (!only_dirs && !keep_around) {
+
+                        if (unlinkat(fd, de->d_name, 0) < 0) {
+                                if (ret == 0 && errno != ENOENT)
+                                        ret = -errno;
+                        }
+                }
+        }
+
+        closedir(d);
+
+        return ret;
+}
+
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+        int fd;
+        int r;
+
+        assert(path);
+
+        if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
+
+                if (errno != ENOTDIR)
+                        return -errno;
+
+                if (delete_root && !only_dirs)
+                        if (unlink(path) < 0)
+                                return -errno;
+
+                return 0;
+        }
+
+        r = rm_rf_children(fd, only_dirs, honour_sticky);
+
+        if (delete_root) {
+
+                if (honour_sticky && file_is_priv_sticky(path) > 0)
+                        return r;
+
+                if (rmdir(path) < 0 && errno != ENOENT) {
+                        if (r == 0)
+                                r = -errno;
+                }
+        }
+
+        return r;
+}
+
+int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+        assert(path);
+
+        /* Under the assumption that we are running privileged we
+         * first change the access mode and only then hand out
+         * ownership to avoid a window where access is too open. */
+
+        if (mode != (mode_t) -1)
+                if (chmod(path, mode) < 0)
+                        return -errno;
+
+        if (uid != (uid_t) -1 || gid != (gid_t) -1)
+                if (chown(path, uid, gid) < 0)
+                        return -errno;
+
+        return 0;
+}
+
+int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
+        assert(fd >= 0);
+
+        /* Under the assumption that we are running privileged we
+         * first change the access mode and only then hand out
+         * ownership to avoid a window where access is too open. */
+
+        if (fchmod(fd, mode) < 0)
+                return -errno;
+
+        if (fchown(fd, uid, gid) < 0)
+                return -errno;
+
+        return 0;
+}
+
+cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
+        cpu_set_t *r;
+        unsigned n = 1024;
+
+        /* Allocates the cpuset in the right size */
+
+        for (;;) {
+                if (!(r = CPU_ALLOC(n)))
+                        return NULL;
+
+                if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) {
+                        CPU_ZERO_S(CPU_ALLOC_SIZE(n), r);
+
+                        if (ncpus)
+                                *ncpus = n;
+
+                        return r;
+                }
+
+                CPU_FREE(r);
+
+                if (errno != EINVAL)
+                        return NULL;
+
+                n *= 2;
+        }
+}
+
+void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) {
+        char *s = NULL, *spaces = NULL, *e;
+        int fd = -1, c;
+        size_t emax, sl, left;
+        struct iovec iovec[5];
+        int n = 0;
+
+        assert(format);
+
+        /* This independent of logging, as status messages are
+         * optional and go exclusively to the console. */
+
+        if (vasprintf(&s, format, ap) < 0)
+                goto finish;
+
+        fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                goto finish;
+
+        if (ellipse) {
+                c = fd_columns(fd);
+                if (c <= 0)
+                        c = 80;
+
+                if (status) {
+                        sl = 2 + 6 + 1; /* " [" status "]" */
+                        emax = (size_t) c > sl ? c - sl - 1 : 0;
+                } else
+                        emax = c - 1;
+
+                e = ellipsize(s, emax, 75);
+                if (e) {
+                        free(s);
+                        s = e;
+                }
+        }
+
+        zero(iovec);
+        IOVEC_SET_STRING(iovec[n++], s);
+
+        if (ellipse) {
+                sl = strlen(s);
+                left = emax > sl ? emax - sl : 0;
+                if (left > 0) {
+                        spaces = malloc(left);
+                        if (spaces) {
+                                memset(spaces, ' ', left);
+                                iovec[n].iov_base = spaces;
+                                iovec[n].iov_len = left;
+                                n++;
+                        }
+                }
+        }
+
+        if (status) {
+                IOVEC_SET_STRING(iovec[n++], " [");
+                IOVEC_SET_STRING(iovec[n++], status);
+                IOVEC_SET_STRING(iovec[n++], "]\n");
+        } else
+                IOVEC_SET_STRING(iovec[n++], "\n");
+
+        writev(fd, iovec, n);
+
+finish:
+        free(s);
+        free(spaces);
+
+        if (fd >= 0)
+                close_nointr_nofail(fd);
+}
+
+void status_printf(const char *status, bool ellipse, const char *format, ...) {
+        va_list ap;
+
+        assert(format);
+
+        va_start(ap, format);
+        status_vprintf(status, ellipse, format, ap);
+        va_end(ap);
+}
+
+void status_welcome(void) {
+        char *pretty_name = NULL, *ansi_color = NULL;
+        const char *const_pretty = NULL, *const_color = NULL;
+        int r;
+
+        if ((r = parse_env_file("/etc/os-release", NEWLINE,
+                                "PRETTY_NAME", &pretty_name,
+                                "ANSI_COLOR", &ansi_color,
+                                NULL)) < 0) {
+
+                if (r != -ENOENT)
+                        log_warning("Failed to read /etc/os-release: %s", strerror(-r));
+        }
+
+        if (!pretty_name && !const_pretty)
+                const_pretty = "Linux";
+
+        if (!ansi_color && !const_color)
+                const_color = "1";
+
+        status_printf(NULL,
+                      false,
+                      "\nWelcome to \x1B[%sm%s\x1B[0m!\n",
+                      const_color ? const_color : ansi_color,
+                      const_pretty ? const_pretty : pretty_name);
+
+        free(ansi_color);
+        free(pretty_name);
+}
+
+char *replace_env(const char *format, char **env) {
+        enum {
+                WORD,
+                CURLY,
+                VARIABLE
+        } state = WORD;
+
+        const char *e, *word = format;
+        char *r = NULL, *k;
+
+        assert(format);
+
+        for (e = format; *e; e ++) {
+
+                switch (state) {
+
+                case WORD:
+                        if (*e == '$')
+                                state = CURLY;
+                        break;
+
+                case CURLY:
+                        if (*e == '{') {
+                                if (!(k = strnappend(r, word, e-word-1)))
+                                        goto fail;
+
+                                free(r);
+                                r = k;
+
+                                word = e-1;
+                                state = VARIABLE;
+
+                        } else if (*e == '$') {
+                                if (!(k = strnappend(r, word, e-word)))
+                                        goto fail;
+
+                                free(r);
+                                r = k;
+
+                                word = e+1;
+                                state = WORD;
+                        } else
+                                state = WORD;
+                        break;
+
+                case VARIABLE:
+                        if (*e == '}') {
+                                const char *t;
+
+                                if (!(t = strv_env_get_with_length(env, word+2, e-word-2)))
+                                        t = "";
+
+                                if (!(k = strappend(r, t)))
+                                        goto fail;
+
+                                free(r);
+                                r = k;
+
+                                word = e+1;
+                                state = WORD;
+                        }
+                        break;
+                }
+        }
+
+        if (!(k = strnappend(r, word, e-word)))
+                goto fail;
+
+        free(r);
+        return k;
+
+fail:
+        free(r);
+        return NULL;
+}
+
+char **replace_env_argv(char **argv, char **env) {
+        char **r, **i;
+        unsigned k = 0, l = 0;
+
+        l = strv_length(argv);
+
+        if (!(r = new(char*, l+1)))
+                return NULL;
+
+        STRV_FOREACH(i, argv) {
+
+                /* If $FOO appears as single word, replace it by the split up variable */
+                if ((*i)[0] == '$' && (*i)[1] != '{') {
+                        char *e;
+                        char **w, **m;
+                        unsigned q;
+
+                        if ((e = strv_env_get(env, *i+1))) {
+
+                                if (!(m = strv_split_quoted(e))) {
+                                        r[k] = NULL;
+                                        strv_free(r);
+                                        return NULL;
+                                }
+                        } else
+                                m = NULL;
+
+                        q = strv_length(m);
+                        l = l + q - 1;
+
+                        if (!(w = realloc(r, sizeof(char*) * (l+1)))) {
+                                r[k] = NULL;
+                                strv_free(r);
+                                strv_free(m);
+                                return NULL;
+                        }
+
+                        r = w;
+                        if (m) {
+                                memcpy(r + k, m, q * sizeof(char*));
+                                free(m);
+                        }
+
+                        k += q;
+                        continue;
+                }
+
+                /* If ${FOO} appears as part of a word, replace it by the variable as-is */
+                if (!(r[k++] = replace_env(*i, env))) {
+                        strv_free(r);
+                        return NULL;
+                }
+        }
+
+        r[k] = NULL;
+        return r;
+}
+
+int fd_columns(int fd) {
+        struct winsize ws;
+        zero(ws);
+
+        if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+                return -errno;
+
+        if (ws.ws_col <= 0)
+                return -EIO;
+
+        return ws.ws_col;
+}
+
+unsigned columns(void) {
+        static __thread int parsed_columns = 0;
+        const char *e;
+
+        if (_likely_(parsed_columns > 0))
+                return parsed_columns;
+
+        e = getenv("COLUMNS");
+        if (e)
+                parsed_columns = atoi(e);
+
+        if (parsed_columns <= 0)
+                parsed_columns = fd_columns(STDOUT_FILENO);
+
+        if (parsed_columns <= 0)
+                parsed_columns = 80;
+
+        return parsed_columns;
+}
+
+int fd_lines(int fd) {
+        struct winsize ws;
+        zero(ws);
+
+        if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+                return -errno;
+
+        if (ws.ws_row <= 0)
+                return -EIO;
+
+        return ws.ws_row;
+}
+
+unsigned lines(void) {
+        static __thread int parsed_lines = 0;
+        const char *e;
+
+        if (_likely_(parsed_lines > 0))
+                return parsed_lines;
+
+        e = getenv("LINES");
+        if (e)
+                parsed_lines = atoi(e);
+
+        if (parsed_lines <= 0)
+                parsed_lines = fd_lines(STDOUT_FILENO);
+
+        if (parsed_lines <= 0)
+                parsed_lines = 25;
+
+        return parsed_lines;
+}
+
+int running_in_chroot(void) {
+        struct stat a, b;
+
+        zero(a);
+        zero(b);
+
+        /* Only works as root */
+
+        if (stat("/proc/1/root", &a) < 0)
+                return -errno;
+
+        if (stat("/", &b) < 0)
+                return -errno;
+
+        return
+                a.st_dev != b.st_dev ||
+                a.st_ino != b.st_ino;
+}
+
+char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
+        size_t x;
+        char *r;
+
+        assert(s);
+        assert(percent <= 100);
+        assert(new_length >= 3);
+
+        if (old_length <= 3 || old_length <= new_length)
+                return strndup(s, old_length);
+
+        r = new0(char, new_length+1);
+        if (!r)
+                return r;
+
+        x = (new_length * percent) / 100;
+
+        if (x > new_length - 3)
+                x = new_length - 3;
+
+        memcpy(r, s, x);
+        r[x] = '.';
+        r[x+1] = '.';
+        r[x+2] = '.';
+        memcpy(r + x + 3,
+               s + old_length - (new_length - x - 3),
+               new_length - x - 3);
+
+        return r;
+}
+
+char *ellipsize(const char *s, size_t length, unsigned percent) {
+        return ellipsize_mem(s, strlen(s), length, percent);
+}
+
+int touch(const char *path) {
+        int fd;
+
+        assert(path);
+
+        if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644)) < 0)
+                return -errno;
+
+        close_nointr_nofail(fd);
+        return 0;
+}
+
+char *unquote(const char *s, const char* quotes) {
+        size_t l;
+        assert(s);
+
+        l = strlen(s);
+        if (l < 2)
+                return strdup(s);
+
+        if (strchr(quotes, s[0]) && s[l-1] == s[0])
+                return strndup(s+1, l-2);
+
+        return strdup(s);
+}
+
+char *normalize_env_assignment(const char *s) {
+        char *name, *value, *p, *r;
+
+        p = strchr(s, '=');
+
+        if (!p) {
+                if (!(r = strdup(s)))
+                        return NULL;
+
+                return strstrip(r);
+        }
+
+        if (!(name = strndup(s, p - s)))
+                return NULL;
+
+        if (!(p = strdup(p+1))) {
+                free(name);
+                return NULL;
+        }
+
+        value = unquote(strstrip(p), QUOTES);
+        free(p);
+
+        if (!value) {
+                free(name);
+                return NULL;
+        }
+
+        if (asprintf(&r, "%s=%s", name, value) < 0)
+                r = NULL;
+
+        free(value);
+        free(name);
+
+        return r;
+}
+
+int wait_for_terminate(pid_t pid, siginfo_t *status) {
+        siginfo_t dummy;
+
+        assert(pid >= 1);
+
+        if (!status)
+                status = &dummy;
+
+        for (;;) {
+                zero(*status);
+
+                if (waitid(P_PID, pid, status, WEXITED) < 0) {
+
+                        if (errno == EINTR)
+                                continue;
+
+                        return -errno;
+                }
+
+                return 0;
+        }
+}
+
+int wait_for_terminate_and_warn(const char *name, pid_t pid) {
+        int r;
+        siginfo_t status;
+
+        assert(name);
+        assert(pid > 1);
+
+        if ((r = wait_for_terminate(pid, &status)) < 0) {
+                log_warning("Failed to wait for %s: %s", name, strerror(-r));
+                return r;
+        }
+
+        if (status.si_code == CLD_EXITED) {
+                if (status.si_status != 0) {
+                        log_warning("%s failed with error code %i.", name, status.si_status);
+                        return status.si_status;
+                }
+
+                log_debug("%s succeeded.", name);
+                return 0;
+
+        } else if (status.si_code == CLD_KILLED ||
+                   status.si_code == CLD_DUMPED) {
+
+                log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
+                return -EPROTO;
+        }
+
+        log_warning("%s failed due to unknown reason.", name);
+        return -EPROTO;
+
+}
+
+_noreturn_ void freeze(void) {
+
+        /* Make sure nobody waits for us on a socket anymore */
+        close_all_fds(NULL, 0);
+
+        sync();
+
+        for (;;)
+                pause();
+}
+
+bool null_or_empty(struct stat *st) {
+        assert(st);
+
+        if (S_ISREG(st->st_mode) && st->st_size <= 0)
+                return true;
+
+        if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
+                return true;
+
+        return false;
+}
+
+int null_or_empty_path(const char *fn) {
+        struct stat st;
+
+        assert(fn);
+
+        if (stat(fn, &st) < 0)
+                return -errno;
+
+        return null_or_empty(&st);
+}
+
+DIR *xopendirat(int fd, const char *name, int flags) {
+        int nfd;
+        DIR *d;
+
+        if ((nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags)) < 0)
+                return NULL;
+
+        if (!(d = fdopendir(nfd))) {
+                close_nointr_nofail(nfd);
+                return NULL;
+        }
+
+        return d;
+}
+
+int signal_from_string_try_harder(const char *s) {
+        int signo;
+        assert(s);
+
+        if ((signo = signal_from_string(s)) <= 0)
+                if (startswith(s, "SIG"))
+                        return signal_from_string(s+3);
+
+        return signo;
+}
+
+void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
+
+        assert(f);
+        assert(name);
+        assert(t);
+
+        if (!dual_timestamp_is_set(t))
+                return;
+
+        fprintf(f, "%s=%llu %llu\n",
+                name,
+                (unsigned long long) t->realtime,
+                (unsigned long long) t->monotonic);
+}
+
+void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
+        unsigned long long a, b;
+
+        assert(value);
+        assert(t);
+
+        if (sscanf(value, "%lli %llu", &a, &b) != 2)
+                log_debug("Failed to parse finish timestamp value %s", value);
+        else {
+                t->realtime = a;
+                t->monotonic = b;
+        }
+}
+
+char *fstab_node_to_udev_node(const char *p) {
+        char *dn, *t, *u;
+        int r;
+
+        /* FIXME: to follow udev's logic 100% we need to leave valid
+         * UTF8 chars unescaped */
+
+        if (startswith(p, "LABEL=")) {
+
+                if (!(u = unquote(p+6, "\"\'")))
+                        return NULL;
+
+                t = xescape(u, "/ ");
+                free(u);
+
+                if (!t)
+                        return NULL;
+
+                r = asprintf(&dn, "/dev/disk/by-label/%s", t);
+                free(t);
+
+                if (r < 0)
+                        return NULL;
+
+                return dn;
+        }
+
+        if (startswith(p, "UUID=")) {
+
+                if (!(u = unquote(p+5, "\"\'")))
+                        return NULL;
+
+                t = xescape(u, "/ ");
+                free(u);
+
+                if (!t)
+                        return NULL;
+
+                r = asprintf(&dn, "/dev/disk/by-uuid/%s", t);
+                free(t);
+
+                if (r < 0)
+                        return NULL;
+
+                return dn;
+        }
+
+        return strdup(p);
+}
+
+void filter_environ(const char *prefix) {
+        int i, j;
+        assert(prefix);
+
+        if (!environ)
+                return;
+
+        for (i = 0, j = 0; environ[i]; i++) {
+
+                if (startswith(environ[i], prefix))
+                        continue;
+
+                environ[j++] = environ[i];
+        }
+
+        environ[j] = NULL;
+}
+
+bool tty_is_vc(const char *tty) {
+        assert(tty);
+
+        if (startswith(tty, "/dev/"))
+                tty += 5;
+
+        return vtnr_from_tty(tty) >= 0;
+}
+
+int vtnr_from_tty(const char *tty) {
+        int i, r;
+
+        assert(tty);
+
+        if (startswith(tty, "/dev/"))
+                tty += 5;
+
+        if (!startswith(tty, "tty") )
+                return -EINVAL;
+
+        if (tty[3] < '0' || tty[3] > '9')
+                return -EINVAL;
+
+        r = safe_atoi(tty+3, &i);
+        if (r < 0)
+                return r;
+
+        if (i < 0 || i > 63)
+                return -EINVAL;
+
+        return i;
+}
+
+bool tty_is_vc_resolve(const char *tty) {
+        char *active = NULL;
+        bool b;
+
+        assert(tty);
+
+        if (startswith(tty, "/dev/"))
+                tty += 5;
+
+        /* Resolve where /dev/console is pointing to */
+        if (streq(tty, "console"))
+                if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
+                        /* If multiple log outputs are configured the
+                         * last one is what /dev/console points to */
+                        tty = strrchr(active, ' ');
+                        if (tty)
+                                tty++;
+                        else
+                                tty = active;
+                }
+
+        b = tty_is_vc(tty);
+        free(active);
+
+        return b;
+}
+
+const char *default_term_for_tty(const char *tty) {
+        assert(tty);
+
+        return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt100";
+}
+
+bool dirent_is_file(const struct dirent *de) {
+        assert(de);
+
+        if (ignore_file(de->d_name))
+                return false;
+
+        if (de->d_type != DT_REG &&
+            de->d_type != DT_LNK &&
+            de->d_type != DT_UNKNOWN)
+                return false;
+
+        return true;
+}
+
+bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
+        assert(de);
+
+        if (!dirent_is_file(de))
+                return false;
+
+        return endswith(de->d_name, suffix);
+}
+
+void execute_directory(const char *directory, DIR *d, char *argv[]) {
+        DIR *_d = NULL;
+        struct dirent *de;
+        Hashmap *pids = NULL;
+
+        assert(directory);
+
+        /* Executes all binaries in a directory in parallel and waits
+         * until all they all finished. */
+
+        if (!d) {
+                if (!(_d = opendir(directory))) {
+
+                        if (errno == ENOENT)
+                                return;
+
+                        log_error("Failed to enumerate directory %s: %m", directory);
+                        return;
+                }
+
+                d = _d;
+        }
+
+        if (!(pids = hashmap_new(trivial_hash_func, trivial_compare_func))) {
+                log_error("Failed to allocate set.");
+                goto finish;
+        }
+
+        while ((de = readdir(d))) {
+                char *path;
+                pid_t pid;
+                int k;
+
+                if (!dirent_is_file(de))
+                        continue;
+
+                if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) {
+                        log_error("Out of memory");
+                        continue;
+                }
+
+                if ((pid = fork()) < 0) {
+                        log_error("Failed to fork: %m");
+                        free(path);
+                        continue;
+                }
+
+                if (pid == 0) {
+                        char *_argv[2];
+                        /* Child */
+
+                        if (!argv) {
+                                _argv[0] = path;
+                                _argv[1] = NULL;
+                                argv = _argv;
+                        } else
+                                if (!argv[0])
+                                        argv[0] = path;
+
+                        execv(path, argv);
+
+                        log_error("Failed to execute %s: %m", path);
+                        _exit(EXIT_FAILURE);
+                }
+
+                log_debug("Spawned %s as %lu", path, (unsigned long) pid);
+
+                if ((k = hashmap_put(pids, UINT_TO_PTR(pid), path)) < 0) {
+                        log_error("Failed to add PID to set: %s", strerror(-k));
+                        free(path);
+                }
+        }
+
+        while (!hashmap_isempty(pids)) {
+                pid_t pid = PTR_TO_UINT(hashmap_first_key(pids));
+                siginfo_t si;
+                char *path;
+
+                zero(si);
+                if (waitid(P_PID, pid, &si, WEXITED) < 0) {
+
+                        if (errno == EINTR)
+                                continue;
+
+                        log_error("waitid() failed: %m");
+                        goto finish;
+                }
+
+                if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) {
+                        if (!is_clean_exit(si.si_code, si.si_status)) {
+                                if (si.si_code == CLD_EXITED)
+                                        log_error("%s exited with exit status %i.", path, si.si_status);
+                                else
+                                        log_error("%s terminated by signal %s.", path, signal_to_string(si.si_status));
+                        } else
+                                log_debug("%s exited successfully.", path);
+
+                        free(path);
+                }
+        }
+
+finish:
+        if (_d)
+                closedir(_d);
+
+        if (pids)
+                hashmap_free_free(pids);
+}
+
+int kill_and_sigcont(pid_t pid, int sig) {
+        int r;
+
+        r = kill(pid, sig) < 0 ? -errno : 0;
+
+        if (r >= 0)
+                kill(pid, SIGCONT);
+
+        return r;
+}
+
+bool nulstr_contains(const char*nulstr, const char *needle) {
+        const char *i;
+
+        if (!nulstr)
+                return false;
+
+        NULSTR_FOREACH(i, nulstr)
+                if (streq(i, needle))
+                        return true;
+
+        return false;
+}
+
+bool plymouth_running(void) {
+        return access("/run/plymouth/pid", F_OK) >= 0;
+}
+
+void parse_syslog_priority(char **p, int *priority) {
+        int a = 0, b = 0, c = 0;
+        int k;
+
+        assert(p);
+        assert(*p);
+        assert(priority);
+
+        if ((*p)[0] != '<')
+                return;
+
+        if (!strchr(*p, '>'))
+                return;
+
+        if ((*p)[2] == '>') {
+                c = undecchar((*p)[1]);
+                k = 3;
+        } else if ((*p)[3] == '>') {
+                b = undecchar((*p)[1]);
+                c = undecchar((*p)[2]);
+                k = 4;
+        } else if ((*p)[4] == '>') {
+                a = undecchar((*p)[1]);
+                b = undecchar((*p)[2]);
+                c = undecchar((*p)[3]);
+                k = 5;
+        } else
+                return;
+
+        if (a < 0 || b < 0 || c < 0)
+                return;
+
+        *priority = a*100+b*10+c;
+        *p += k;
+}
+
+void skip_syslog_pid(char **buf) {
+        char *p;
+
+        assert(buf);
+        assert(*buf);
+
+        p = *buf;
+
+        if (*p != '[')
+                return;
+
+        p++;
+        p += strspn(p, "0123456789");
+
+        if (*p != ']')
+                return;
+
+        p++;
+
+        *buf = p;
+}
+
+void skip_syslog_date(char **buf) {
+        enum {
+                LETTER,
+                SPACE,
+                NUMBER,
+                SPACE_OR_NUMBER,
+                COLON
+        } sequence[] = {
+                LETTER, LETTER, LETTER,
+                SPACE,
+                SPACE_OR_NUMBER, NUMBER,
+                SPACE,
+                SPACE_OR_NUMBER, NUMBER,
+                COLON,
+                SPACE_OR_NUMBER, NUMBER,
+                COLON,
+                SPACE_OR_NUMBER, NUMBER,
+                SPACE
+        };
+
+        char *p;
+        unsigned i;
+
+        assert(buf);
+        assert(*buf);
+
+        p = *buf;
+
+        for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
+
+                if (!*p)
+                        return;
+
+                switch (sequence[i]) {
+
+                case SPACE:
+                        if (*p != ' ')
+                                return;
+                        break;
+
+                case SPACE_OR_NUMBER:
+                        if (*p == ' ')
+                                break;
+
+                        /* fall through */
+
+                case NUMBER:
+                        if (*p < '0' || *p > '9')
+                                return;
+
+                        break;
+
+                case LETTER:
+                        if (!(*p >= 'A' && *p <= 'Z') &&
+                            !(*p >= 'a' && *p <= 'z'))
+                                return;
+
+                        break;
+
+                case COLON:
+                        if (*p != ':')
+                                return;
+                        break;
+
+                }
+        }
+
+        *buf = p;
+}
+
+char* strshorten(char *s, size_t l) {
+        assert(s);
+
+        if (l < strlen(s))
+                s[l] = 0;
+
+        return s;
+}
+
+static bool hostname_valid_char(char c) {
+        return
+                (c >= 'a' && c <= 'z') ||
+                (c >= 'A' && c <= 'Z') ||
+                (c >= '0' && c <= '9') ||
+                c == '-' ||
+                c == '_' ||
+                c == '.';
+}
+
+bool hostname_is_valid(const char *s) {
+        const char *p;
+
+        if (isempty(s))
+                return false;
+
+        for (p = s; *p; p++)
+                if (!hostname_valid_char(*p))
+                        return false;
+
+        if (p-s > HOST_NAME_MAX)
+                return false;
+
+        return true;
+}
+
+char* hostname_cleanup(char *s) {
+        char *p, *d;
+
+        for (p = s, d = s; *p; p++)
+                if ((*p >= 'a' && *p <= 'z') ||
+                    (*p >= 'A' && *p <= 'Z') ||
+                    (*p >= '0' && *p <= '9') ||
+                    *p == '-' ||
+                    *p == '_' ||
+                    *p == '.')
+                        *(d++) = *p;
+
+        *d = 0;
+
+        strshorten(s, HOST_NAME_MAX);
+        return s;
+}
+
+int pipe_eof(int fd) {
+        struct pollfd pollfd;
+        int r;
+
+        zero(pollfd);
+        pollfd.fd = fd;
+        pollfd.events = POLLIN|POLLHUP;
+
+        r = poll(&pollfd, 1, 0);
+        if (r < 0)
+                return -errno;
+
+        if (r == 0)
+                return 0;
+
+        return pollfd.revents & POLLHUP;
+}
+
+int fd_wait_for_event(int fd, int event, usec_t t) {
+        struct pollfd pollfd;
+        int r;
+
+        zero(pollfd);
+        pollfd.fd = fd;
+        pollfd.events = event;
+
+        r = poll(&pollfd, 1, t == (usec_t) -1 ? -1 : (int) (t / USEC_PER_MSEC));
+        if (r < 0)
+                return -errno;
+
+        if (r == 0)
+                return 0;
+
+        return pollfd.revents;
+}
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
+        FILE *f;
+        char *t;
+        const char *fn;
+        size_t k;
+        int fd;
+
+        assert(path);
+        assert(_f);
+        assert(_temp_path);
+
+        t = new(char, strlen(path) + 1 + 6 + 1);
+        if (!t)
+                return -ENOMEM;
+
+        fn = file_name_from_path(path);
+        k = fn-path;
+        memcpy(t, path, k);
+        t[k] = '.';
+        stpcpy(stpcpy(t+k+1, fn), "XXXXXX");
+
+        fd = mkostemp(t, O_WRONLY|O_CLOEXEC);
+        if (fd < 0) {
+                free(t);
+                return -errno;
+        }
+
+        f = fdopen(fd, "we");
+        if (!f) {
+                unlink(t);
+                free(t);
+                return -errno;
+        }
+
+        *_f = f;
+        *_temp_path = t;
+
+        return 0;
+}
+
+int terminal_vhangup_fd(int fd) {
+        assert(fd >= 0);
+
+        if (ioctl(fd, TIOCVHANGUP) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int terminal_vhangup(const char *name) {
+        int fd, r;
+
+        fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return fd;
+
+        r = terminal_vhangup_fd(fd);
+        close_nointr_nofail(fd);
+
+        return r;
+}
+
+int vt_disallocate(const char *name) {
+        int fd, r;
+        unsigned u;
+
+        /* Deallocate the VT if possible. If not possible
+         * (i.e. because it is the active one), at least clear it
+         * entirely (including the scrollback buffer) */
+
+        if (!startswith(name, "/dev/"))
+                return -EINVAL;
+
+        if (!tty_is_vc(name)) {
+                /* So this is not a VT. I guess we cannot deallocate
+                 * it then. But let's at least clear the screen */
+
+                fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+                if (fd < 0)
+                        return fd;
+
+                loop_write(fd,
+                           "\033[r"    /* clear scrolling region */
+                           "\033[H"    /* move home */
+                           "\033[2J",  /* clear screen */
+                           10, false);
+                close_nointr_nofail(fd);
+
+                return 0;
+        }
+
+        if (!startswith(name, "/dev/tty"))
+                return -EINVAL;
+
+        r = safe_atou(name+8, &u);
+        if (r < 0)
+                return r;
+
+        if (u <= 0)
+                return -EINVAL;
+
+        /* Try to deallocate */
+        fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return fd;
+
+        r = ioctl(fd, VT_DISALLOCATE, u);
+        close_nointr_nofail(fd);
+
+        if (r >= 0)
+                return 0;
+
+        if (errno != EBUSY)
+                return -errno;
+
+        /* Couldn't deallocate, so let's clear it fully with
+         * scrollback */
+        fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+        if (fd < 0)
+                return fd;
+
+        loop_write(fd,
+                   "\033[r"   /* clear scrolling region */
+                   "\033[H"   /* move home */
+                   "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
+                   10, false);
+        close_nointr_nofail(fd);
+
+        return 0;
+}
+
+static int files_add(Hashmap *h, const char *path, const char *suffix) {
+        DIR *dir;
+        struct dirent buffer, *de;
+        int r = 0;
+
+        dir = opendir(path);
+        if (!dir) {
+                if (errno == ENOENT)
+                        return 0;
+                return -errno;
+        }
+
+        for (;;) {
+                int k;
+                char *p, *f;
+
+                k = readdir_r(dir, &buffer, &de);
+                if (k != 0) {
+                        r = -k;
+                        goto finish;
+                }
+
+                if (!de)
+                        break;
+
+                if (!dirent_is_file_with_suffix(de, suffix))
+                        continue;
+
+                if (asprintf(&p, "%s/%s", path, de->d_name) < 0) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                f = canonicalize_file_name(p);
+                if (!f) {
+                        log_error("Failed to canonicalize file name '%s': %m", p);
+                        free(p);
+                        continue;
+                }
+                free(p);
+
+                log_debug("found: %s\n", f);
+                if (hashmap_put(h, file_name_from_path(f), f) <= 0)
+                        free(f);
+        }
+
+finish:
+        closedir(dir);
+        return r;
+}
+
+static int base_cmp(const void *a, const void *b) {
+        const char *s1, *s2;
+
+        s1 = *(char * const *)a;
+        s2 = *(char * const *)b;
+        return strcmp(file_name_from_path(s1), file_name_from_path(s2));
+}
+
+int conf_files_list(char ***strv, const char *suffix, const char *dir, ...) {
+        Hashmap *fh = NULL;
+        char **dirs = NULL;
+        char **files = NULL;
+        char **p;
+        va_list ap;
+        int r = 0;
+
+        va_start(ap, dir);
+        dirs = strv_new_ap(dir, ap);
+        va_end(ap);
+        if (!dirs) {
+                r = -ENOMEM;
+                goto finish;
+        }
+        if (!strv_path_canonicalize(dirs)) {
+                r = -ENOMEM;
+                goto finish;
+        }
+        if (!strv_uniq(dirs)) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        fh = hashmap_new(string_hash_func, string_compare_func);
+        if (!fh) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        STRV_FOREACH(p, dirs) {
+                if (files_add(fh, *p, suffix) < 0) {
+                        log_error("Failed to search for files.");
+                        r = -EINVAL;
+                        goto finish;
+                }
+        }
+
+        files = hashmap_get_strv(fh);
+        if (files == NULL) {
+                log_error("Failed to compose list of files.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        qsort(files, hashmap_size(fh), sizeof(char *), base_cmp);
+
+finish:
+        strv_free(dirs);
+        hashmap_free(fh);
+        *strv = files;
+        return r;
+}
+
+int hwclock_is_localtime(void) {
+        FILE *f;
+        bool local = false;
+
+        /*
+         * The third line of adjtime is "UTC" or "LOCAL" or nothing.
+         *   # /etc/adjtime
+         *   0.0 0 0
+         *   0
+         *   UTC
+         */
+        f = fopen("/etc/adjtime", "re");
+        if (f) {
+                char line[LINE_MAX];
+                bool b;
+
+                b = fgets(line, sizeof(line), f) &&
+                        fgets(line, sizeof(line), f) &&
+                        fgets(line, sizeof(line), f);
+
+                fclose(f);
+
+                if (!b)
+                        return -EIO;
+
+
+                truncate_nl(line);
+                local = streq(line, "LOCAL");
+
+        } else if (errno != -ENOENT)
+                return -errno;
+
+        return local;
+}
+
+int hwclock_apply_localtime_delta(int *min) {
+        const struct timeval *tv_null = NULL;
+        struct timespec ts;
+        struct tm *tm;
+        int minuteswest;
+        struct timezone tz;
+
+        assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+        assert_se(tm = localtime(&ts.tv_sec));
+        minuteswest = tm->tm_gmtoff / 60;
+
+        tz.tz_minuteswest = -minuteswest;
+        tz.tz_dsttime = 0; /* DST_NONE*/
+
+        /*
+         * If the hardware clock does not run in UTC, but in local time:
+         * The very first time we set the kernel's timezone, it will warp
+         * the clock so that it runs in UTC instead of local time.
+         */
+        if (settimeofday(tv_null, &tz) < 0)
+                return -errno;
+        if (min)
+                *min = minuteswest;
+        return 0;
+}
+
+int hwclock_reset_localtime_delta(void) {
+        const struct timeval *tv_null = NULL;
+        struct timezone tz;
+
+        tz.tz_minuteswest = 0;
+        tz.tz_dsttime = 0; /* DST_NONE*/
+
+        if (settimeofday(tv_null, &tz) < 0)
+                return -errno;
+
+        return 0;
+}
+
+int rtc_open(int flags) {
+        int fd;
+        DIR *d;
+
+        /* First, we try to make use of the /dev/rtc symlink. If that
+         * doesn't exist, we open the first RTC which has hctosys=1
+         * set. If we don't find any we just take the first RTC that
+         * exists at all. */
+
+        fd = open("/dev/rtc", flags);
+        if (fd >= 0)
+                return fd;
+
+        d = opendir("/sys/class/rtc");
+        if (!d)
+                goto fallback;
+
+        for (;;) {
+                char *p, *v;
+                struct dirent buf, *de;
+                int r;
+
+                r = readdir_r(d, &buf, &de);
+                if (r != 0)
+                        goto fallback;
+
+                if (!de)
+                        goto fallback;
+
+                if (ignore_file(de->d_name))
+                        continue;
+
+                p = join("/sys/class/rtc/", de->d_name, "/hctosys", NULL);
+                if (!p) {
+                        closedir(d);
+                        return -ENOMEM;
+                }
+
+                r = read_one_line_file(p, &v);
+                free(p);
+
+                if (r < 0)
+                        continue;
+
+                r = parse_boolean(v);
+                free(v);
+
+                if (r <= 0)
+                        continue;
+
+                p = strappend("/dev/", de->d_name);
+                fd = open(p, flags);
+                free(p);
+
+                if (fd >= 0) {
+                        closedir(d);
+                        return fd;
+                }
+        }
+
+fallback:
+        if (d)
+                closedir(d);
+
+        fd = open("/dev/rtc0", flags);
+        if (fd < 0)
+                return -errno;
+
+        return fd;
+}
+
+int hwclock_get_time(struct tm *tm) {
+        int fd;
+        int err = 0;
+
+        assert(tm);
+
+        fd = rtc_open(O_RDONLY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        /* This leaves the timezone fields of struct tm
+         * uninitialized! */
+        if (ioctl(fd, RTC_RD_TIME, tm) < 0)
+                err = -errno;
+
+        /* We don't now daylight saving, so we reset this in order not
+         * to confused mktime(). */
+        tm->tm_isdst = -1;
+
+        close_nointr_nofail(fd);
+
+        return err;
+}
+
+int hwclock_set_time(const struct tm *tm) {
+        int fd;
+        int err = 0;
+
+        assert(tm);
+
+        fd = rtc_open(O_RDONLY|O_CLOEXEC);
+        if (fd < 0)
+                return -errno;
+
+        if (ioctl(fd, RTC_SET_TIME, tm) < 0)
+                err = -errno;
+
+        close_nointr_nofail(fd);
+
+        return err;
+}
+
+int copy_file(const char *from, const char *to) {
+        int r, fdf, fdt;
+
+        assert(from);
+        assert(to);
+
+        fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+        if (fdf < 0)
+                return -errno;
+
+        fdt = open(to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY, 0644);
+        if (fdt < 0) {
+                close_nointr_nofail(fdf);
+                return -errno;
+        }
+
+        for (;;) {
+                char buf[PIPE_BUF];
+                ssize_t n, k;
+
+                n = read(fdf, buf, sizeof(buf));
+                if (n < 0) {
+                        r = -errno;
+
+                        close_nointr_nofail(fdf);
+                        close_nointr(fdt);
+                        unlink(to);
+
+                        return r;
+                }
+
+                if (n == 0)
+                        break;
+
+                errno = 0;
+                k = loop_write(fdt, buf, n, false);
+                if (n != k) {
+                        r = k < 0 ? k : (errno ? -errno : -EIO);
+
+                        close_nointr_nofail(fdf);
+                        close_nointr(fdt);
+
+                        unlink(to);
+                        return r;
+                }
+        }
+
+        close_nointr_nofail(fdf);
+        r = close_nointr(fdt);
+
+        if (r < 0) {
+                unlink(to);
+                return r;
+        }
+
+        return 0;
+}
+
+int symlink_or_copy(const char *from, const char *to) {
+        char *pf = NULL, *pt = NULL;
+        struct stat a, b;
+        int r;
+
+        assert(from);
+        assert(to);
+
+        if (parent_of_path(from, &pf) < 0 ||
+            parent_of_path(to, &pt) < 0) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (stat(pf, &a) < 0 ||
+            stat(pt, &b) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        if (a.st_dev != b.st_dev) {
+                free(pf);
+                free(pt);
+
+                return copy_file(from, to);
+        }
+
+        if (symlink(from, to) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        free(pf);
+        free(pt);
+
+        return r;
+}
+
+int symlink_or_copy_atomic(const char *from, const char *to) {
+        char *t, *x;
+        const char *fn;
+        size_t k;
+        unsigned long long ull;
+        unsigned i;
+        int r;
+
+        assert(from);
+        assert(to);
+
+        t = new(char, strlen(to) + 1 + 16 + 1);
+        if (!t)
+                return -ENOMEM;
+
+        fn = file_name_from_path(to);
+        k = fn-to;
+        memcpy(t, to, k);
+        t[k] = '.';
+        x = stpcpy(t+k+1, fn);
+
+        ull = random_ull();
+        for (i = 0; i < 16; i++) {
+                *(x++) = hexchar(ull & 0xF);
+                ull >>= 4;
+        }
+
+        *x = 0;
+
+        r = symlink_or_copy(from, t);
+        if (r < 0) {
+                unlink(t);
+                free(t);
+                return r;
+        }
+
+        if (rename(t, to) < 0) {
+                r = -errno;
+                unlink(t);
+                free(t);
+                return r;
+        }
+
+        free(t);
+        return r;
+}
+
+bool display_is_local(const char *display) {
+        assert(display);
+
+        return
+                display[0] == ':' &&
+                display[1] >= '0' &&
+                display[1] <= '9';
+}
+
+int socket_from_display(const char *display, char **path) {
+        size_t k;
+        char *f, *c;
+
+        assert(display);
+        assert(path);
+
+        if (!display_is_local(display))
+                return -EINVAL;
+
+        k = strspn(display+1, "0123456789");
+
+        f = new(char, sizeof("/tmp/.X11-unix/X") + k);
+        if (!f)
+                return -ENOMEM;
+
+        c = stpcpy(f, "/tmp/.X11-unix/X");
+        memcpy(c, display+1, k);
+        c[k] = 0;
+
+        *path = f;
+
+        return 0;
+}
+
+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
+        struct passwd *p;
+        uid_t u;
+
+        assert(username);
+        assert(*username);
+
+        /* We enforce some special rules for uid=0: in order to avoid
+         * NSS lookups for root we hardcode its data. */
+
+        if (streq(*username, "root") || streq(*username, "0")) {
+                *username = "root";
+
+                if (uid)
+                        *uid = 0;
+
+                if (gid)
+                        *gid = 0;
+
+                if (home)
+                        *home = "/root";
+                return 0;
+        }
+
+        if (parse_uid(*username, &u) >= 0) {
+                errno = 0;
+                p = getpwuid(u);
+
+                /* If there are multiple users with the same id, make
+                 * sure to leave $USER to the configured value instead
+                 * of the first occurrence in the database. However if
+                 * the uid was configured by a numeric uid, then let's
+                 * pick the real username from /etc/passwd. */
+                if (p)
+                        *username = p->pw_name;
+        } else {
+                errno = 0;
+                p = getpwnam(*username);
+        }
+
+        if (!p)
+                return errno != 0 ? -errno : -ESRCH;
+
+        if (uid)
+                *uid = p->pw_uid;
+
+        if (gid)
+                *gid = p->pw_gid;
+
+        if (home)
+                *home = p->pw_dir;
+
+        return 0;
+}
+
+int get_group_creds(const char **groupname, gid_t *gid) {
+        struct group *g;
+        gid_t id;
+
+        assert(groupname);
+
+        /* We enforce some special rules for gid=0: in order to avoid
+         * NSS lookups for root we hardcode its data. */
+
+        if (streq(*groupname, "root") || streq(*groupname, "0")) {
+                *groupname = "root";
+
+                if (gid)
+                        *gid = 0;
+
+                return 0;
+        }
+
+        if (parse_gid(*groupname, &id) >= 0) {
+                errno = 0;
+                g = getgrgid(id);
+
+                if (g)
+                        *groupname = g->gr_name;
+        } else {
+                errno = 0;
+                g = getgrnam(*groupname);
+        }
+
+        if (!g)
+                return errno != 0 ? -errno : -ESRCH;
+
+        if (gid)
+                *gid = g->gr_gid;
+
+        return 0;
+}
+
+int in_group(const char *name) {
+        gid_t gid, *gids;
+        int ngroups_max, r, i;
+
+        r = get_group_creds(&name, &gid);
+        if (r < 0)
+                return r;
+
+        if (getgid() == gid)
+                return 1;
+
+        if (getegid() == gid)
+                return 1;
+
+        ngroups_max = sysconf(_SC_NGROUPS_MAX);
+        assert(ngroups_max > 0);
+
+        gids = alloca(sizeof(gid_t) * ngroups_max);
+
+        r = getgroups(ngroups_max, gids);
+        if (r < 0)
+                return -errno;
+
+        for (i = 0; i < r; i++)
+                if (gids[i] == gid)
+                        return 1;
+
+        return 0;
+}
+
+int glob_exists(const char *path) {
+        glob_t g;
+        int r, k;
+
+        assert(path);
+
+        zero(g);
+        errno = 0;
+        k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+        if (k == GLOB_NOMATCH)
+                r = 0;
+        else if (k == GLOB_NOSPACE)
+                r = -ENOMEM;
+        else if (k == 0)
+                r = !strv_isempty(g.gl_pathv);
+        else
+                r = errno ? -errno : -EIO;
+
+        globfree(&g);
+
+        return r;
+}
+
+int dirent_ensure_type(DIR *d, struct dirent *de) {
+        struct stat st;
+
+        assert(d);
+        assert(de);
+
+        if (de->d_type != DT_UNKNOWN)
+                return 0;
+
+        if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
+                return -errno;
+
+        de->d_type =
+                S_ISREG(st.st_mode)  ? DT_REG  :
+                S_ISDIR(st.st_mode)  ? DT_DIR  :
+                S_ISLNK(st.st_mode)  ? DT_LNK  :
+                S_ISFIFO(st.st_mode) ? DT_FIFO :
+                S_ISSOCK(st.st_mode) ? DT_SOCK :
+                S_ISCHR(st.st_mode)  ? DT_CHR  :
+                S_ISBLK(st.st_mode)  ? DT_BLK  :
+                                       DT_UNKNOWN;
+
+        return 0;
+}
+
+int in_search_path(const char *path, char **search) {
+        char **i, *parent;
+        int r;
+
+        r = parent_of_path(path, &parent);
+        if (r < 0)
+                return r;
+
+        r = 0;
+
+        STRV_FOREACH(i, search) {
+                if (path_equal(parent, *i)) {
+                        r = 1;
+                        break;
+                }
+        }
+
+        free(parent);
+
+        return r;
+}
+
+int get_files_in_directory(const char *path, char ***list) {
+        DIR *d;
+        int r = 0;
+        unsigned n = 0;
+        char **l = NULL;
+
+        assert(path);
+
+        /* Returns all files in a directory in *list, and the number
+         * of files as return value. If list is NULL returns only the
+         * number */
+
+        d = opendir(path);
+        if (!d)
+                return -errno;
+
+        for (;;) {
+                struct dirent buffer, *de;
+                int k;
+
+                k = readdir_r(d, &buffer, &de);
+                if (k != 0) {
+                        r = -k;
+                        goto finish;
+                }
+
+                if (!de)
+                        break;
+
+                dirent_ensure_type(d, de);
+
+                if (!dirent_is_file(de))
+                        continue;
+
+                if (list) {
+                        if ((unsigned) r >= n) {
+                                char **t;
+
+                                n = MAX(16, 2*r);
+                                t = realloc(l, sizeof(char*) * n);
+                                if (!t) {
+                                        r = -ENOMEM;
+                                        goto finish;
+                                }
+
+                                l = t;
+                        }
+
+                        assert((unsigned) r < n);
+
+                        l[r] = strdup(de->d_name);
+                        if (!l[r]) {
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        l[++r] = NULL;
+                } else
+                        r++;
+        }
+
+finish:
+        if (d)
+                closedir(d);
+
+        if (r >= 0) {
+                if (list)
+                        *list = l;
+        } else
+                strv_free(l);
+
+        return r;
+}
+
+char *join(const char *x, ...) {
+        va_list ap;
+        size_t l;
+        char *r, *p;
+
+        va_start(ap, x);
+
+        if (x) {
+                l = strlen(x);
+
+                for (;;) {
+                        const char *t;
+
+                        t = va_arg(ap, const char *);
+                        if (!t)
+                                break;
+
+                        l += strlen(t);
+                }
+        } else
+                l = 0;
+
+        va_end(ap);
+
+        r = new(char, l+1);
+        if (!r)
+                return NULL;
+
+        if (x) {
+                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;
+}
+
+bool is_main_thread(void) {
+        static __thread int cached = 0;
+
+        if (_unlikely_(cached == 0))
+                cached = getpid() == gettid() ? 1 : -1;
+
+        return cached > 0;
+}
+
+int block_get_whole_disk(dev_t d, dev_t *ret) {
+        char *p, *s;
+        int r;
+        unsigned n, m;
+
+        assert(ret);
+
+        /* If it has a queue this is good enough for us */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
+                return -ENOMEM;
+
+        r = access(p, F_OK);
+        free(p);
+
+        if (r >= 0) {
+                *ret = d;
+                return 0;
+        }
+
+        /* If it is a partition find the originating device */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
+                return -ENOMEM;
+
+        r = access(p, F_OK);
+        free(p);
+
+        if (r < 0)
+                return -ENOENT;
+
+        /* Get parent dev_t */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
+                return -ENOMEM;
+
+        r = read_one_line_file(p, &s);
+        free(p);
+
+        if (r < 0)
+                return r;
+
+        r = sscanf(s, "%u:%u", &m, &n);
+        free(s);
+
+        if (r != 2)
+                return -EINVAL;
+
+        /* Only return this if it is really good enough for us. */
+        if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
+                return -ENOMEM;
+
+        r = access(p, F_OK);
+        free(p);
+
+        if (r >= 0) {
+                *ret = makedev(m, n);
+                return 0;
+        }
+
+        return -ENOENT;
+}
+
+int file_is_priv_sticky(const char *p) {
+        struct stat st;
+
+        assert(p);
+
+        if (lstat(p, &st) < 0)
+                return -errno;
+
+        return
+                (st.st_uid == 0 || st.st_uid == getuid()) &&
+                (st.st_mode & S_ISVTX);
+}
+
+static const char *const ioprio_class_table[] = {
+        [IOPRIO_CLASS_NONE] = "none",
+        [IOPRIO_CLASS_RT] = "realtime",
+        [IOPRIO_CLASS_BE] = "best-effort",
+        [IOPRIO_CLASS_IDLE] = "idle"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ioprio_class, int);
+
+static const char *const sigchld_code_table[] = {
+        [CLD_EXITED] = "exited",
+        [CLD_KILLED] = "killed",
+        [CLD_DUMPED] = "dumped",
+        [CLD_TRAPPED] = "trapped",
+        [CLD_STOPPED] = "stopped",
+        [CLD_CONTINUED] = "continued",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
+
+static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
+        [LOG_FAC(LOG_KERN)] = "kern",
+        [LOG_FAC(LOG_USER)] = "user",
+        [LOG_FAC(LOG_MAIL)] = "mail",
+        [LOG_FAC(LOG_DAEMON)] = "daemon",
+        [LOG_FAC(LOG_AUTH)] = "auth",
+        [LOG_FAC(LOG_SYSLOG)] = "syslog",
+        [LOG_FAC(LOG_LPR)] = "lpr",
+        [LOG_FAC(LOG_NEWS)] = "news",
+        [LOG_FAC(LOG_UUCP)] = "uucp",
+        [LOG_FAC(LOG_CRON)] = "cron",
+        [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
+        [LOG_FAC(LOG_FTP)] = "ftp",
+        [LOG_FAC(LOG_LOCAL0)] = "local0",
+        [LOG_FAC(LOG_LOCAL1)] = "local1",
+        [LOG_FAC(LOG_LOCAL2)] = "local2",
+        [LOG_FAC(LOG_LOCAL3)] = "local3",
+        [LOG_FAC(LOG_LOCAL4)] = "local4",
+        [LOG_FAC(LOG_LOCAL5)] = "local5",
+        [LOG_FAC(LOG_LOCAL6)] = "local6",
+        [LOG_FAC(LOG_LOCAL7)] = "local7"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(log_facility_unshifted, int);
+
+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);
+
+static const char* const sched_policy_table[] = {
+        [SCHED_OTHER] = "other",
+        [SCHED_BATCH] = "batch",
+        [SCHED_IDLE] = "idle",
+        [SCHED_FIFO] = "fifo",
+        [SCHED_RR] = "rr"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(sched_policy, int);
+
+static const char* const rlimit_table[] = {
+        [RLIMIT_CPU] = "LimitCPU",
+        [RLIMIT_FSIZE] = "LimitFSIZE",
+        [RLIMIT_DATA] = "LimitDATA",
+        [RLIMIT_STACK] = "LimitSTACK",
+        [RLIMIT_CORE] = "LimitCORE",
+        [RLIMIT_RSS] = "LimitRSS",
+        [RLIMIT_NOFILE] = "LimitNOFILE",
+        [RLIMIT_AS] = "LimitAS",
+        [RLIMIT_NPROC] = "LimitNPROC",
+        [RLIMIT_MEMLOCK] = "LimitMEMLOCK",
+        [RLIMIT_LOCKS] = "LimitLOCKS",
+        [RLIMIT_SIGPENDING] = "LimitSIGPENDING",
+        [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
+        [RLIMIT_NICE] = "LimitNICE",
+        [RLIMIT_RTPRIO] = "LimitRTPRIO",
+        [RLIMIT_RTTIME] = "LimitRTTIME"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
+
+static const char* const ip_tos_table[] = {
+        [IPTOS_LOWDELAY] = "low-delay",
+        [IPTOS_THROUGHPUT] = "throughput",
+        [IPTOS_RELIABILITY] = "reliability",
+        [IPTOS_LOWCOST] = "low-cost",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ip_tos, int);
+
+static const char *const __signal_table[] = {
+        [SIGHUP] = "HUP",
+        [SIGINT] = "INT",
+        [SIGQUIT] = "QUIT",
+        [SIGILL] = "ILL",
+        [SIGTRAP] = "TRAP",
+        [SIGABRT] = "ABRT",
+        [SIGBUS] = "BUS",
+        [SIGFPE] = "FPE",
+        [SIGKILL] = "KILL",
+        [SIGUSR1] = "USR1",
+        [SIGSEGV] = "SEGV",
+        [SIGUSR2] = "USR2",
+        [SIGPIPE] = "PIPE",
+        [SIGALRM] = "ALRM",
+        [SIGTERM] = "TERM",
+#ifdef SIGSTKFLT
+        [SIGSTKFLT] = "STKFLT",  /* Linux on SPARC doesn't know SIGSTKFLT */
+#endif
+        [SIGCHLD] = "CHLD",
+        [SIGCONT] = "CONT",
+        [SIGSTOP] = "STOP",
+        [SIGTSTP] = "TSTP",
+        [SIGTTIN] = "TTIN",
+        [SIGTTOU] = "TTOU",
+        [SIGURG] = "URG",
+        [SIGXCPU] = "XCPU",
+        [SIGXFSZ] = "XFSZ",
+        [SIGVTALRM] = "VTALRM",
+        [SIGPROF] = "PROF",
+        [SIGWINCH] = "WINCH",
+        [SIGIO] = "IO",
+        [SIGPWR] = "PWR",
+        [SIGSYS] = "SYS"
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
+
+const char *signal_to_string(int signo) {
+        static __thread char buf[12];
+        const char *name;
+
+        name = __signal_to_string(signo);
+        if (name)
+                return name;
+
+        if (signo >= SIGRTMIN && signo <= SIGRTMAX)
+                snprintf(buf, sizeof(buf) - 1, "RTMIN+%d", signo - SIGRTMIN);
+        else
+                snprintf(buf, sizeof(buf) - 1, "%d", signo);
+        char_array_0(buf);
+        return buf;
+}
+
+int signal_from_string(const char *s) {
+        int signo;
+        int offset = 0;
+        unsigned u;
+
+        signo =__signal_from_string(s);
+        if (signo > 0)
+                return signo;
+
+        if (startswith(s, "RTMIN+")) {
+                s += 6;
+                offset = SIGRTMIN;
+        }
+        if (safe_atou(s, &u) >= 0) {
+                signo = (int) u + offset;
+                if (signo > 0 && signo < _NSIG)
+                        return signo;
+        }
+        return -1;
+}
+
+bool kexec_loaded(void) {
+       bool loaded = false;
+       char *s;
+
+       if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
+               if (s[0] == '1')
+                       loaded = true;
+               free(s);
+       }
+       return loaded;
+}
+
+int strdup_or_null(const char *a, char **b) {
+        char *c;
+
+        assert(b);
+
+        if (!a) {
+                *b = NULL;
+                return 0;
+        }
+
+        c = strdup(a);
+        if (!c)
+                return -ENOMEM;
+
+        *b = c;
+        return 0;
+}
+
+int prot_from_flags(int flags) {
+
+        switch (flags & O_ACCMODE) {
+
+        case O_RDONLY:
+                return PROT_READ;
+
+        case O_WRONLY:
+                return PROT_WRITE;
+
+        case O_RDWR:
+                return PROT_READ|PROT_WRITE;
+
+        default:
+                return -EINVAL;
+        }
+}
+
+char *format_bytes(char *buf, size_t l, off_t t) {
+        unsigned i;
+
+        static const struct {
+                const char *suffix;
+                off_t factor;
+        } table[] = {
+                { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+                { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+                { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+                { "G", 1024ULL*1024ULL*1024ULL },
+                { "M", 1024ULL*1024ULL },
+                { "K", 1024ULL },
+        };
+
+        for (i = 0; i < ELEMENTSOF(table); i++) {
+
+                if (t >= table[i].factor) {
+                        snprintf(buf, l,
+                                 "%llu.%llu%s",
+                                 (unsigned long long) (t / table[i].factor),
+                                 (unsigned long long) (((t*10ULL) / table[i].factor) % 10ULL),
+                                 table[i].suffix);
+
+                        goto finish;
+                }
+        }
+
+        snprintf(buf, l, "%lluB", (unsigned long long) t);
+
+finish:
+        buf[l-1] = 0;
+        return buf;
+
+}
+
+void* memdup(const void *p, size_t l) {
+        void *r;
+
+        assert(p);
+
+        r = malloc(l);
+        if (!r)
+                return NULL;
+
+        memcpy(r, p, l);
+        return r;
+}
+
+int fd_inc_sndbuf(int fd, size_t n) {
+        int r, value;
+        socklen_t l = sizeof(value);
+
+        r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
+        if (r >= 0 &&
+            l == sizeof(value) &&
+            (size_t) value >= n*2)
+                return 0;
+
+        value = (int) n;
+        r = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value));
+        if (r < 0)
+                return -errno;
+
+        return 1;
+}
+
+int fd_inc_rcvbuf(int fd, size_t n) {
+        int r, value;
+        socklen_t l = sizeof(value);
+
+        r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
+        if (r >= 0 &&
+            l == sizeof(value) &&
+            (size_t) value >= n*2)
+                return 0;
+
+        value = (int) n;
+        r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value));
+        if (r < 0)
+                return -errno;
+
+        return 1;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
new file mode 100644
index 0000000..e96d56d
--- /dev/null
+++ b/src/shared/util.h
@@ -0,0 +1,536 @@
+/*-*- 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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU 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 "macro.h"
+
+typedef uint64_t usec_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 USEC_PER_HOUR (60ULL*USEC_PER_MINUTE)
+#define USEC_PER_DAY (24ULL*USEC_PER_HOUR)
+#define USEC_PER_WEEK (7ULL*USEC_PER_DAY)
+#define USEC_PER_MONTH (2629800ULL*USEC_PER_SEC)
+#define USEC_PER_YEAR (31557600ULL*USEC_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_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 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 is_path_absolute(const char *p) {
+        return *p == '/';
+}
+
+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_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)))
+
+char **split_path_and_make_absolute(const char *p);
+
+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);
+
+char *file_name_from_path(const char *p);
+bool is_path(const char *p);
+
+bool path_is_absolute(const char *p);
+char *path_make_absolute(const char *p, const char *prefix);
+char *path_make_absolute_cwd(const char *p);
+
+char **strv_path_make_absolute_cwd(char **l);
+char **strv_path_canonicalize(char **l);
+char **strv_path_remove_empty(char **l);
+
+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 safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid);
+int mkdir_parents(const char *path, mode_t mode);
+int mkdir_p(const char *path, mode_t mode);
+
+int parent_of_path(const char *path, char **parent);
+
+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 *path_kill_slashes(char *path);
+
+bool path_startswith(const char *path, const char *prefix);
+bool path_equal(const char *a, const char *b);
+
+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);
+
+int path_is_mount_point(const char *path, bool allow_symlink);
+
+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);
+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(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);
+
+void filter_environ(const char *prefix);
+
+bool tty_is_vc(const char *tty);
+bool tty_is_vc_resolve(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);
+
+int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
+
+int hwclock_is_localtime(void);
+int hwclock_apply_localtime_delta(int *min);
+int hwclock_reset_localtime_delta(void);
+int hwclock_get_time(struct tm *tm);
+int hwclock_set_time(const struct tm *tm);
+
+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 rtc_open(int flags);
+
+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);
+#endif
diff --git a/src/shared/virt.c b/src/shared/virt.c
new file mode 100644
index 0000000..4c526ff
--- /dev/null
+++ b/src/shared/virt.c
@@ -0,0 +1,292 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "virt.h"
+
+/* Returns a short identifier for the various VM implementations */
+int detect_vm(const char **id) {
+
+#if defined(__i386__) || defined(__x86_64__)
+
+        /* Both CPUID and DMI are x86 specific interfaces... */
+
+        static const char *const dmi_vendors[] = {
+                "/sys/class/dmi/id/sys_vendor",
+                "/sys/class/dmi/id/board_vendor",
+                "/sys/class/dmi/id/bios_vendor"
+        };
+
+        static const char dmi_vendor_table[] =
+                "QEMU\0"                  "qemu\0"
+                /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+                "VMware\0"                "vmware\0"
+                "VMW\0"                   "vmware\0"
+                "Microsoft Corporation\0" "microsoft\0"
+                "innotek GmbH\0"          "oracle\0"
+                "Xen\0"                   "xen\0"
+                "Bochs\0"                 "bochs\0";
+
+        static const char cpuid_vendor_table[] =
+                "XenVMMXenVMM\0"          "xen\0"
+                "KVMKVMKVM\0"             "kvm\0"
+                /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+                "VMwareVMware\0"          "vmware\0"
+                /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
+                "Microsoft Hv\0"          "microsoft\0";
+
+        uint32_t eax, ecx;
+        union {
+                uint32_t sig32[3];
+                char text[13];
+        } sig;
+        unsigned i;
+        const char *j, *k;
+        bool hypervisor;
+
+        /* http://lwn.net/Articles/301888/ */
+        zero(sig);
+
+#if defined (__i386__)
+#define REG_a "eax"
+#define REG_b "ebx"
+#elif defined (__amd64__)
+#define REG_a "rax"
+#define REG_b "rbx"
+#endif
+
+        /* First detect whether there is a hypervisor */
+        eax = 1;
+        __asm__ __volatile__ (
+                /* ebx/rbx is being used for PIC! */
+                "  push %%"REG_b"         \n\t"
+                "  cpuid                  \n\t"
+                "  pop %%"REG_b"          \n\t"
+
+                : "=a" (eax), "=c" (ecx)
+                : "0" (eax)
+        );
+
+        hypervisor = !!(ecx & 0x80000000U);
+
+        if (hypervisor) {
+
+                /* There is a hypervisor, see what it is */
+                eax = 0x40000000U;
+                __asm__ __volatile__ (
+                        /* ebx/rbx is being used for PIC! */
+                        "  push %%"REG_b"         \n\t"
+                        "  cpuid                  \n\t"
+                        "  mov %%ebx, %1          \n\t"
+                        "  pop %%"REG_b"          \n\t"
+
+                        : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
+                        : "0" (eax)
+                );
+
+                NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
+                        if (streq(sig.text, j)) {
+
+                                if (id)
+                                        *id = k;
+
+                                return 1;
+                        }
+        }
+
+        for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
+                char *s;
+                int r;
+                const char *found = NULL;
+
+                if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) {
+                        if (r != -ENOENT)
+                                return r;
+
+                        continue;
+                }
+
+                NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
+                        if (startswith(s, j))
+                                found = k;
+                free(s);
+
+                if (found) {
+                        if (id)
+                                *id = found;
+
+                        return 1;
+                }
+        }
+
+        if (hypervisor) {
+                if (id)
+                        *id = "other";
+
+                return 1;
+        }
+
+#endif
+        return 0;
+}
+
+int detect_container(const char **id) {
+        FILE *f;
+
+        /* Unfortunately many of these operations require root access
+         * in one way or another */
+
+        if (geteuid() != 0)
+                return -EPERM;
+
+        if (running_in_chroot() > 0) {
+
+                if (id)
+                        *id = "chroot";
+
+                return 1;
+        }
+
+        /* /proc/vz exists in container and outside of the container,
+         * /proc/bc only outside of the container. */
+        if (access("/proc/vz", F_OK) >= 0 &&
+            access("/proc/bc", F_OK) < 0) {
+
+                if (id)
+                        *id = "openvz";
+
+                return 1;
+        }
+
+        f = fopen("/proc/1/environ", "re");
+        if (f) {
+                bool done = false;
+
+                do {
+                        char line[LINE_MAX];
+                        unsigned i;
+
+                        for (i = 0; i < sizeof(line)-1; i++) {
+                                int c;
+
+                                c = getc(f);
+                                if (_unlikely_(c == EOF)) {
+                                        done = true;
+                                        break;
+                                } else if (c == 0)
+                                        break;
+
+                                line[i] = c;
+                        }
+                        line[i] = 0;
+
+                        if (streq(line, "container=lxc")) {
+                                fclose(f);
+
+                                if (id)
+                                        *id = "lxc";
+                                return 1;
+
+                        } else if (streq(line, "container=lxc-libvirt")) {
+                                fclose(f);
+
+                                if (id)
+                                        *id = "lxc-libvirt";
+                                return 1;
+
+                        } else if (streq(line, "container=systemd-nspawn")) {
+                                fclose(f);
+
+                                if (id)
+                                        *id = "systemd-nspawn";
+                                return 1;
+
+                        } else if (startswith(line, "container=")) {
+                                fclose(f);
+
+                                if (id)
+                                        *id = "other";
+                                return 1;
+                        }
+
+                } while (!done);
+
+                fclose(f);
+        }
+
+        return 0;
+}
+
+/* Returns a short identifier for the various VM/container implementations */
+Virtualization detect_virtualization(const char **id) {
+
+        static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID;
+        static __thread const char *cached_id = NULL;
+
+        const char *_id;
+        int r;
+        Virtualization v;
+
+        if (_likely_(cached_virt >= 0)) {
+
+                if (id && cached_virt > 0)
+                        *id = cached_id;
+
+                return cached_virt;
+        }
+
+        r = detect_container(&_id);
+        if (r < 0) {
+                v = r;
+                goto finish;
+        } else if (r > 0) {
+                v = VIRTUALIZATION_CONTAINER;
+                goto finish;
+        }
+
+        r = detect_vm(&_id);
+        if (r < 0) {
+                v = r;
+                goto finish;
+        } else if (r > 0) {
+                v = VIRTUALIZATION_VM;
+                goto finish;
+        }
+
+        v = VIRTUALIZATION_NONE;
+
+finish:
+        if (v > 0) {
+                cached_id = _id;
+
+                if (id)
+                        *id = _id;
+        }
+
+        if (v >= 0)
+                cached_virt = v;
+
+        return v;
+}
diff --git a/src/shared/virt.h b/src/shared/virt.h
new file mode 100644
index 0000000..f55c9a6
--- /dev/null
+++ b/src/shared/virt.h
@@ -0,0 +1,38 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foovirthfoo
+#define foovirthfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2011 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int detect_vm(const char **id);
+int detect_container(const char **id);
+
+typedef enum Virtualization {
+        VIRTUALIZATION_NONE = 0,
+        VIRTUALIZATION_VM,
+        VIRTUALIZATION_CONTAINER,
+        _VIRTUALIZATION_MAX,
+        _VIRTUALIZATION_INVALID = -1
+} Virtualization;
+
+Virtualization detect_virtualization(const char **id);
+
+#endif
diff --git a/src/socket-util.c b/src/socket-util.c
deleted file mode 100644
index acc4d33..0000000
--- a/src/socket-util.c
+++ /dev/null
@@ -1,652 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <net/if.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stddef.h>
-#include <sys/ioctl.h>
-
-#include "macro.h"
-#include "util.h"
-#include "socket-util.h"
-#include "missing.h"
-#include "label.h"
-
-int socket_address_parse(SocketAddress *a, const char *s) {
-        int r;
-        char *e, *n;
-        unsigned u;
-
-        assert(a);
-        assert(s);
-
-        zero(*a);
-        a->type = SOCK_STREAM;
-
-        if (*s == '[') {
-                /* IPv6 in [x:.....:z]:p notation */
-
-                if (!socket_ipv6_is_supported()) {
-                        log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
-                        return -EAFNOSUPPORT;
-                }
-
-                if (!(e = strchr(s+1, ']')))
-                        return -EINVAL;
-
-                if (!(n = strndup(s+1, e-s-1)))
-                        return -ENOMEM;
-
-                errno = 0;
-                if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) {
-                        free(n);
-                        return errno != 0 ? -errno : -EINVAL;
-                }
-
-                free(n);
-
-                e++;
-                if (*e != ':')
-                        return -EINVAL;
-
-                e++;
-                if ((r = safe_atou(e, &u)) < 0)
-                        return r;
-
-                if (u <= 0 || u > 0xFFFF)
-                        return -EINVAL;
-
-                a->sockaddr.in6.sin6_family = AF_INET6;
-                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
-                a->size = sizeof(struct sockaddr_in6);
-
-        } else if (*s == '/') {
-                /* AF_UNIX socket */
-
-                size_t l;
-
-                l = strlen(s);
-                if (l >= sizeof(a->sockaddr.un.sun_path))
-                        return -EINVAL;
-
-                a->sockaddr.un.sun_family = AF_UNIX;
-                memcpy(a->sockaddr.un.sun_path, s, l);
-                a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
-
-        } else if (*s == '@') {
-                /* Abstract AF_UNIX socket */
-                size_t l;
-
-                l = strlen(s+1);
-                if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
-                        return -EINVAL;
-
-                a->sockaddr.un.sun_family = AF_UNIX;
-                memcpy(a->sockaddr.un.sun_path+1, s+1, l);
-                a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
-
-        } else {
-
-                if ((e = strchr(s, ':'))) {
-
-                        if ((r = safe_atou(e+1, &u)) < 0)
-                                return r;
-
-                        if (u <= 0 || u > 0xFFFF)
-                                return -EINVAL;
-
-                        if (!(n = strndup(s, e-s)))
-                                return -ENOMEM;
-
-                        /* IPv4 in w.x.y.z:p notation? */
-                        if ((r = inet_pton(AF_INET, n, &a->sockaddr.in4.sin_addr)) < 0) {
-                                free(n);
-                                return -errno;
-                        }
-
-                        if (r > 0) {
-                                /* Gotcha, it's a traditional IPv4 address */
-                                free(n);
-
-                                a->sockaddr.in4.sin_family = AF_INET;
-                                a->sockaddr.in4.sin_port = htons((uint16_t) u);
-                                a->size = sizeof(struct sockaddr_in);
-                        } else {
-                                unsigned idx;
-
-                                if (strlen(n) > IF_NAMESIZE-1) {
-                                        free(n);
-                                        return -EINVAL;
-                                }
-
-                                /* Uh, our last resort, an interface name */
-                                idx = if_nametoindex(n);
-                                free(n);
-
-                                if (idx == 0)
-                                        return -EINVAL;
-
-                                if (!socket_ipv6_is_supported()) {
-                                        log_warning("Binding to interface is not available since kernel does not support IPv6.");
-                                        return -EAFNOSUPPORT;
-                                }
-
-                                a->sockaddr.in6.sin6_family = AF_INET6;
-                                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
-                                a->sockaddr.in6.sin6_scope_id = idx;
-                                a->sockaddr.in6.sin6_addr = in6addr_any;
-                                a->size = sizeof(struct sockaddr_in6);
-                        }
-                } else {
-
-                        /* Just a port */
-                        if ((r = safe_atou(s, &u)) < 0)
-                                return r;
-
-                        if (u <= 0 || u > 0xFFFF)
-                                return -EINVAL;
-
-                        if (socket_ipv6_is_supported()) {
-                                a->sockaddr.in6.sin6_family = AF_INET6;
-                                a->sockaddr.in6.sin6_port = htons((uint16_t) u);
-                                a->sockaddr.in6.sin6_addr = in6addr_any;
-                                a->size = sizeof(struct sockaddr_in6);
-                        } else {
-                                a->sockaddr.in4.sin_family = AF_INET;
-                                a->sockaddr.in4.sin_port = htons((uint16_t) u);
-                                a->sockaddr.in4.sin_addr.s_addr = INADDR_ANY;
-                                a->size = sizeof(struct sockaddr_in);
-                        }
-                }
-        }
-
-        return 0;
-}
-
-int socket_address_parse_netlink(SocketAddress *a, const char *s) {
-        int family;
-        unsigned group = 0;
-        char* sfamily = NULL;
-        assert(a);
-        assert(s);
-
-        zero(*a);
-        a->type = SOCK_RAW;
-
-        errno = 0;
-        if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
-                return errno ? -errno : -EINVAL;
-
-        if ((family = netlink_family_from_string(sfamily)) < 0)
-                if (safe_atoi(sfamily, &family) < 0) {
-                        free(sfamily);
-                        return -EINVAL;
-                }
-
-        free(sfamily);
-
-        a->sockaddr.nl.nl_family = AF_NETLINK;
-        a->sockaddr.nl.nl_groups = group;
-
-        a->type = SOCK_RAW;
-        a->size = sizeof(struct sockaddr_nl);
-        a->protocol = family;
-
-        return 0;
-}
-
-int socket_address_verify(const SocketAddress *a) {
-        assert(a);
-
-        switch (socket_address_family(a)) {
-
-        case AF_INET:
-                if (a->size != sizeof(struct sockaddr_in))
-                        return -EINVAL;
-
-                if (a->sockaddr.in4.sin_port == 0)
-                        return -EINVAL;
-
-                if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
-                        return -EINVAL;
-
-                return 0;
-
-        case AF_INET6:
-                if (a->size != sizeof(struct sockaddr_in6))
-                        return -EINVAL;
-
-                if (a->sockaddr.in6.sin6_port == 0)
-                        return -EINVAL;
-
-                if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
-                        return -EINVAL;
-
-                return 0;
-
-        case AF_UNIX:
-                if (a->size < offsetof(struct sockaddr_un, sun_path))
-                        return -EINVAL;
-
-                if (a->size > offsetof(struct sockaddr_un, sun_path)) {
-
-                        if (a->sockaddr.un.sun_path[0] != 0) {
-                                char *e;
-
-                                /* path */
-                                if (!(e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path))))
-                                        return -EINVAL;
-
-                                if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
-                                        return -EINVAL;
-                        }
-                }
-
-                if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
-                        return -EINVAL;
-
-                return 0;
-
-        case AF_NETLINK:
-
-                if (a->size != sizeof(struct sockaddr_nl))
-                        return -EINVAL;
-
-                if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
-                        return -EINVAL;
-
-                return 0;
-
-        default:
-                return -EAFNOSUPPORT;
-        }
-}
-
-int socket_address_print(const SocketAddress *a, char **p) {
-        int r;
-        assert(a);
-        assert(p);
-
-        if ((r = socket_address_verify(a)) < 0)
-                return r;
-
-        switch (socket_address_family(a)) {
-
-        case AF_INET: {
-                char *ret;
-
-                if (!(ret = new(char, INET_ADDRSTRLEN+1+5+1)))
-                        return -ENOMEM;
-
-                if (!inet_ntop(AF_INET, &a->sockaddr.in4.sin_addr, ret, INET_ADDRSTRLEN)) {
-                        free(ret);
-                        return -errno;
-                }
-
-                sprintf(strchr(ret, 0), ":%u", ntohs(a->sockaddr.in4.sin_port));
-                *p = ret;
-                return 0;
-        }
-
-        case AF_INET6: {
-                char *ret;
-
-                if (!(ret = new(char, 1+INET6_ADDRSTRLEN+2+5+1)))
-                        return -ENOMEM;
-
-                ret[0] = '[';
-                if (!inet_ntop(AF_INET6, &a->sockaddr.in6.sin6_addr, ret+1, INET6_ADDRSTRLEN)) {
-                        free(ret);
-                        return -errno;
-                }
-
-                sprintf(strchr(ret, 0), "]:%u", ntohs(a->sockaddr.in6.sin6_port));
-                *p = ret;
-                return 0;
-        }
-
-        case AF_UNIX: {
-                char *ret;
-
-                if (a->size <= offsetof(struct sockaddr_un, sun_path)) {
-
-                        if (!(ret = strdup("<unnamed>")))
-                                return -ENOMEM;
-
-                } else if (a->sockaddr.un.sun_path[0] == 0) {
-                        /* abstract */
-
-                        /* FIXME: We assume we can print the
-                         * socket path here and that it hasn't
-                         * more than one NUL byte. That is
-                         * actually an invalid assumption */
-
-                        if (!(ret = new(char, sizeof(a->sockaddr.un.sun_path)+1)))
-                                return -ENOMEM;
-
-                        ret[0] = '@';
-                        memcpy(ret+1, a->sockaddr.un.sun_path+1, sizeof(a->sockaddr.un.sun_path)-1);
-                        ret[sizeof(a->sockaddr.un.sun_path)] = 0;
-
-                } else {
-
-                        if (!(ret = strdup(a->sockaddr.un.sun_path)))
-                                return -ENOMEM;
-                }
-
-                *p = ret;
-                return 0;
-        }
-
-        case AF_NETLINK: {
-                const char *sfamily;
-
-                if ((sfamily = netlink_family_to_string(a->protocol)))
-                        r = asprintf(p, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
-                else
-                        r = asprintf(p, "%i %u", a->protocol, a->sockaddr.nl.nl_groups);
-
-                if (r < 0)
-                        return -ENOMEM;
-
-                return 0;
-        }
-
-        default:
-                return -EINVAL;
-        }
-}
-
-int socket_address_listen(
-                const SocketAddress *a,
-                int backlog,
-                SocketAddressBindIPv6Only only,
-                const char *bind_to_device,
-                bool free_bind,
-                bool transparent,
-                mode_t directory_mode,
-                mode_t socket_mode,
-                const char *label,
-                int *ret) {
-
-        int r, fd, one;
-        assert(a);
-        assert(ret);
-
-        if ((r = socket_address_verify(a)) < 0)
-                return r;
-
-        if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported())
-                return -EAFNOSUPPORT;
-
-        r = label_socket_set(label);
-        if (r < 0)
-                return r;
-
-        fd = socket(socket_address_family(a), a->type | SOCK_NONBLOCK | SOCK_CLOEXEC, a->protocol);
-        r = fd < 0 ? -errno : 0;
-
-        label_socket_clear();
-
-        if (r < 0)
-                return r;
-
-        if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) {
-                int flag = only == SOCKET_ADDRESS_IPV6_ONLY;
-
-                if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0)
-                        goto fail;
-        }
-
-        if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) {
-                if (bind_to_device)
-                        if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0)
-                                goto fail;
-
-                if (free_bind) {
-                        one = 1;
-                        if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0)
-                                log_warning("IP_FREEBIND failed: %m");
-                }
-
-                if (transparent) {
-                        one = 1;
-                        if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0)
-                                log_warning("IP_TRANSPARENT failed: %m");
-                }
-        }
-
-        one = 1;
-        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
-                goto fail;
-
-        if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
-                mode_t old_mask;
-
-                /* Create parents */
-                mkdir_parents(a->sockaddr.un.sun_path, directory_mode);
-
-                /* Enforce the right access mode for the socket*/
-                old_mask = umask(~ socket_mode);
-
-                /* Include the original umask in our mask */
-                umask(~socket_mode | old_mask);
-
-                r = label_bind(fd, &a->sockaddr.sa, a->size);
-
-                if (r < 0 && errno == EADDRINUSE) {
-                        /* Unlink and try again */
-                        unlink(a->sockaddr.un.sun_path);
-                        r = bind(fd, &a->sockaddr.sa, a->size);
-                }
-
-                umask(old_mask);
-        } else
-                r = bind(fd, &a->sockaddr.sa, a->size);
-
-        if (r < 0)
-                goto fail;
-
-        if (socket_address_can_accept(a))
-                if (listen(fd, backlog) < 0)
-                        goto fail;
-
-        *ret = fd;
-        return 0;
-
-fail:
-        r = -errno;
-        close_nointr_nofail(fd);
-        return r;
-}
-
-bool socket_address_can_accept(const SocketAddress *a) {
-        assert(a);
-
-        return
-                a->type == SOCK_STREAM ||
-                a->type == SOCK_SEQPACKET;
-}
-
-bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
-        assert(a);
-        assert(b);
-
-        /* Invalid addresses are unequal to all */
-        if (socket_address_verify(a) < 0 ||
-            socket_address_verify(b) < 0)
-                return false;
-
-        if (a->type != b->type)
-                return false;
-
-        if (a->size != b->size)
-                return false;
-
-        if (socket_address_family(a) != socket_address_family(b))
-                return false;
-
-        switch (socket_address_family(a)) {
-
-        case AF_INET:
-                if (a->sockaddr.in4.sin_addr.s_addr != b->sockaddr.in4.sin_addr.s_addr)
-                        return false;
-
-                if (a->sockaddr.in4.sin_port != b->sockaddr.in4.sin_port)
-                        return false;
-
-                break;
-
-        case AF_INET6:
-                if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
-                        return false;
-
-                if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
-                        return false;
-
-                break;
-
-        case AF_UNIX:
-
-                if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
-                        return false;
-
-                if (a->sockaddr.un.sun_path[0]) {
-                        if (strncmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)) != 0)
-                                return false;
-                } else {
-                        if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
-                                return false;
-                }
-
-                break;
-
-        case AF_NETLINK:
-
-                if (a->protocol != b->protocol)
-                        return false;
-
-                if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
-                        return false;
-
-                break;
-
-        default:
-                /* Cannot compare, so we assume the addresses are different */
-                return false;
-        }
-
-        return true;
-}
-
-bool socket_address_is(const SocketAddress *a, const char *s, int type) {
-        struct SocketAddress b;
-
-        assert(a);
-        assert(s);
-
-        if (socket_address_parse(&b, s) < 0)
-                return false;
-
-        b.type = type;
-
-        return socket_address_equal(a, &b);
-}
-
-bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
-        struct SocketAddress b;
-
-        assert(a);
-        assert(s);
-
-        if (socket_address_parse_netlink(&b, s) < 0)
-                return false;
-
-        return socket_address_equal(a, &b);
-}
-
-bool socket_address_needs_mount(const SocketAddress *a, const char *prefix) {
-        assert(a);
-
-        if (socket_address_family(a) != AF_UNIX)
-                return false;
-
-        if (a->sockaddr.un.sun_path[0] == 0)
-                return false;
-
-        return path_startswith(a->sockaddr.un.sun_path, prefix);
-}
-
-bool socket_ipv6_is_supported(void) {
-        char *l = 0;
-        bool enabled;
-
-        if (access("/sys/module/ipv6", F_OK) != 0)
-                return 0;
-
-        /* If we can't check "disable" parameter, assume enabled */
-        if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
-                return 1;
-
-        /* If module was loaded with disable=1 no IPv6 available */
-        enabled = l[0] == '0';
-        free(l);
-
-        return enabled;
-}
-
-static const char* const netlink_family_table[] = {
-        [NETLINK_ROUTE] = "route",
-        [NETLINK_FIREWALL] = "firewall",
-        [NETLINK_INET_DIAG] = "inet-diag",
-        [NETLINK_NFLOG] = "nflog",
-        [NETLINK_XFRM] = "xfrm",
-        [NETLINK_SELINUX] = "selinux",
-        [NETLINK_ISCSI] = "iscsi",
-        [NETLINK_AUDIT] = "audit",
-        [NETLINK_FIB_LOOKUP] = "fib-lookup",
-        [NETLINK_CONNECTOR] = "connector",
-        [NETLINK_NETFILTER] = "netfilter",
-        [NETLINK_IP6_FW] = "ip6-fw",
-        [NETLINK_DNRTMSG] = "dnrtmsg",
-        [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
-        [NETLINK_GENERIC] = "generic",
-        [NETLINK_SCSITRANSPORT] = "scsitransport",
-        [NETLINK_ECRYPTFS] = "ecryptfs"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(netlink_family, int);
-
-static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
-        [SOCKET_ADDRESS_DEFAULT] = "default",
-        [SOCKET_ADDRESS_BOTH] = "both",
-        [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
diff --git a/src/socket-util.h b/src/socket-util.h
deleted file mode 100644
index 8ccbd37..0000000
--- a/src/socket-util.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosocketutilhfoo
-#define foosocketutilhfoo
-
-/***
-  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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <sys/un.h>
-#include <net/if.h>
-#include <asm/types.h>
-#include <linux/netlink.h>
-
-#include "macro.h"
-#include "util.h"
-
-union sockaddr_union {
-        struct sockaddr sa;
-        struct sockaddr_in in4;
-        struct sockaddr_in6 in6;
-        struct sockaddr_un un;
-        struct sockaddr_nl nl;
-        struct sockaddr_storage storage;
-};
-
-typedef struct SocketAddress {
-        union sockaddr_union sockaddr;
-
-        /* We store the size here explicitly due to the weird
-         * sockaddr_un semantics for abstract sockets */
-        socklen_t size;
-
-        /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */
-        int type;
-
-        /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */
-        int protocol;
-} SocketAddress;
-
-typedef enum SocketAddressBindIPv6Only {
-        SOCKET_ADDRESS_DEFAULT,
-        SOCKET_ADDRESS_BOTH,
-        SOCKET_ADDRESS_IPV6_ONLY,
-        _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX,
-        _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1
-} SocketAddressBindIPv6Only;
-
-#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
-
-int socket_address_parse(SocketAddress *a, const char *s);
-int socket_address_parse_netlink(SocketAddress *a, const char *s);
-int socket_address_print(const SocketAddress *a, char **p);
-int socket_address_verify(const SocketAddress *a);
-
-bool socket_address_can_accept(const SocketAddress *a);
-
-int socket_address_listen(
-                const SocketAddress *a,
-                int backlog,
-                SocketAddressBindIPv6Only only,
-                const char *bind_to_device,
-                bool free_bind,
-                bool transparent,
-                mode_t directory_mode,
-                mode_t socket_mode,
-                const char *label,
-                int *ret);
-
-bool socket_address_is(const SocketAddress *a, const char *s, int type);
-bool socket_address_is_netlink(const SocketAddress *a, const char *s);
-
-bool socket_address_equal(const SocketAddress *a, const SocketAddress *b);
-
-bool socket_address_needs_mount(const SocketAddress *a, const char *prefix);
-
-const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b);
-SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s);
-
-const char* netlink_family_to_string(int b);
-int netlink_family_from_string(const char *s);
-
-bool socket_ipv6_is_supported(void);
-
-#endif
diff --git a/src/strv.c b/src/strv.c
deleted file mode 100644
index bb309d9..0000000
--- a/src/strv.c
+++ /dev/null
@@ -1,690 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU 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 **l) {
-        char **r, **k;
-
-        k = r = new(char*, strv_length(l)+1);
-        if (!k)
-                return NULL;
-
-        if (l)
-                for (; *l; k++, l++)
-                        if (!(*k = strdup(*l)))
-                                goto fail;
-
-        *k = NULL;
-        return r;
-
-fail:
-        for (k--; k >= r; k--)
-                free(*k);
-
-        free(r);
-
-        return NULL;
-}
-
-unsigned strv_length(char **l) {
-        unsigned 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 n = 0, i = 0;
-        va_list aq;
-
-        if (x) {
-                n = 1;
-
-                va_copy(aq, ap);
-                while (va_arg(aq, const char*))
-                        n++;
-                va_end(aq);
-        }
-
-        if (!(a = new(char*, n+1)))
-                return NULL;
-
-        if (x) {
-                if (!(a[i] = strdup(x))) {
-                        free(a);
-                        return NULL;
-                }
-
-                i++;
-
-                while ((s = va_arg(ap, const char*))) {
-                        if (!(a[i] = strdup(s)))
-                                goto fail;
-
-                        i++;
-                }
-        }
-
-        a[i] = NULL;
-
-        return a;
-
-fail:
-
-        for (; i > 0; i--)
-                if (a[i-1])
-                        free(a[i-1]);
-
-        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);
-
-        if (!(r = new(char*, strv_length(a)+strv_length(b)+1)))
-                return NULL;
-
-        for (k = r; *a; k++, a++)
-                if (!(*k = strdup(*a)))
-                        goto fail;
-        for (; *b; k++, b++)
-                if (!(*k = strdup(*b)))
-                        goto fail;
-
-        *k = NULL;
-        return r;
-
-fail:
-        for (k--; k >= r; k--)
-                free(*k);
-
-        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:
-        for (k--; k >= r; k--)
-                free(*k);
-
-        free(r);
-
-        return NULL;
-
-}
-
-char **strv_split(const char *s, const char *separator) {
-        char *state;
-        char *w;
-        size_t l;
-        unsigned n, i;
-        char **r;
-
-        assert(s);
-
-        n = 0;
-        FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
-                n++;
-
-        if (!(r = new(char*, n+1)))
-                return NULL;
-
-        i = 0;
-        FOREACH_WORD_SEPARATOR(w, l, s, separator, state)
-                if (!(r[i++] = strndup(w, l))) {
-                        strv_free(r);
-                        return NULL;
-                }
-
-        r[i] = NULL;
-        return r;
-}
-
-char **strv_split_quoted(const char *s) {
-        char *state;
-        char *w;
-        size_t l;
-        unsigned n, i;
-        char **r;
-
-        assert(s);
-
-        n = 0;
-        FOREACH_WORD_QUOTED(w, l, s, state)
-                n++;
-
-        if (!(r = new(char*, n+1)))
-                return NULL;
-
-        i = 0;
-        FOREACH_WORD_QUOTED(w, l, s, state)
-                if (!(r[i++] = cunescape_length(w, l))) {
-                        strv_free(r);
-                        return NULL;
-                }
-
-        r[i] = NULL;
-        return r;
-}
-
-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);
-        }
-
-        if (!(r = new(char, n+1)))
-                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++)
-                if (!(*k = strdup(*l)))
-                        goto fail;
-
-        if (!(*(k++) = strdup(s)))
-                goto fail;
-
-        *k = NULL;
-        return r;
-
-fail:
-        for (k--; k >= r; k--)
-                free(*k);
-
-        free(r);
-
-        return NULL;
-}
-
-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;
-}
-
-static int env_append(char **r, char ***k, char **a) {
-        assert(r);
-        assert(k);
-
-        if (!a)
-                return 0;
-
-        /* Add the entries of a to *k unless they already exist in *r
-         * in which case they are overridden instead. This assumes
-         * there is enough space in the r array. */
-
-        for (; *a; a++) {
-                char **j;
-                size_t n;
-
-                n = strcspn(*a, "=");
-
-                if ((*a)[n] == '=')
-                        n++;
-
-                for (j = r; j < *k; j++)
-                        if (strncmp(*j, *a, n) == 0)
-                                break;
-
-                if (j >= *k)
-                        (*k)++;
-                else
-                        free(*j);
-
-                if (!(*j = strdup(*a)))
-                        return -ENOMEM;
-        }
-
-        return 0;
-}
-
-char **strv_env_merge(unsigned n_lists, ...) {
-        size_t n = 0;
-        char **l, **k, **r;
-        va_list ap;
-        unsigned i;
-
-        /* Merges an arbitrary number of environment sets */
-
-        va_start(ap, n_lists);
-        for (i = 0; i < n_lists; i++) {
-                l = va_arg(ap, char**);
-                n += strv_length(l);
-        }
-        va_end(ap);
-
-        if (!(r = new(char*, n+1)))
-                return NULL;
-
-        k = r;
-
-        va_start(ap, n_lists);
-        for (i = 0; i < n_lists; i++) {
-                l = va_arg(ap, char**);
-                if (env_append(r, &k, l) < 0)
-                        goto fail;
-        }
-        va_end(ap);
-
-        *k = NULL;
-
-        return r;
-
-fail:
-        va_end(ap);
-
-        for (k--; k >= r; k--)
-                free(*k);
-
-        free(r);
-
-        return NULL;
-}
-
-static bool env_match(const char *t, const char *pattern) {
-        assert(t);
-        assert(pattern);
-
-        /* pattern a matches string a
-         *         a matches a=
-         *         a matches a=b
-         *         a= matches a=
-         *         a=b matches a=b
-         *         a= does not match a
-         *         a=b does not match a=
-         *         a=b does not match a
-         *         a=b does not match a=c */
-
-        if (streq(t, pattern))
-                return true;
-
-        if (!strchr(pattern, '=')) {
-                size_t l = strlen(pattern);
-
-                return strncmp(t, pattern, l) == 0 && t[l] == '=';
-        }
-
-        return false;
-}
-
-char **strv_env_delete(char **x, unsigned n_lists, ...) {
-        size_t n, i = 0;
-        char **k, **r;
-        va_list ap;
-
-        /* Deletes every entry from x that is mentioned in the other
-         * string lists */
-
-        n = strv_length(x);
-
-        r = new(char*, n+1);
-        if (!r)
-                return NULL;
-
-        STRV_FOREACH(k, x) {
-                unsigned v;
-
-                va_start(ap, n_lists);
-                for (v = 0; v < n_lists; v++) {
-                        char **l, **j;
-
-                        l = va_arg(ap, char**);
-                        STRV_FOREACH(j, l)
-                                if (env_match(*k, *j))
-                                        goto skip;
-                }
-                va_end(ap);
-
-                r[i] = strdup(*k);
-                if (!r[i]) {
-                        strv_free(r);
-                        return NULL;
-                }
-
-                i++;
-                continue;
-
-        skip:
-                va_end(ap);
-        }
-
-        r[i] = NULL;
-
-        assert(i <= n);
-
-        return r;
-}
-
-char **strv_env_unset(char **l, const char *p) {
-
-        char **f, **t;
-
-        if (!l)
-                return NULL;
-
-        assert(p);
-
-        /* Drops every occurrence of the env var setting p in the
-         * string list. edits in-place. */
-
-        for (f = t = l; *f; f++) {
-
-                if (env_match(*f, p)) {
-                        free(*f);
-                        continue;
-                }
-
-                *(t++) = *f;
-        }
-
-        *t = NULL;
-        return l;
-}
-
-char **strv_env_set(char **x, const char *p) {
-
-        char **k, **r;
-        char* m[2] = { (char*) p, NULL };
-
-        /* Overrides the env var setting of p, returns a new copy */
-
-        if (!(r = new(char*, strv_length(x)+2)))
-                return NULL;
-
-        k = r;
-        if (env_append(r, &k, x) < 0)
-                goto fail;
-
-        if (env_append(r, &k, m) < 0)
-                goto fail;
-
-        *k = NULL;
-
-        return r;
-
-fail:
-        for (k--; k >= r; k--)
-                free(*k);
-
-        free(r);
-
-        return NULL;
-
-}
-
-char *strv_env_get_with_length(char **l, const char *name, size_t k) {
-        char **i;
-
-        assert(name);
-
-        STRV_FOREACH(i, l)
-                if (strncmp(*i, name, k) == 0 &&
-                    (*i)[k] == '=')
-                        return *i + k + 1;
-
-        return NULL;
-}
-
-char *strv_env_get(char **l, const char *name) {
-        return strv_env_get_with_length(l, name, strlen(name));
-}
-
-char **strv_env_clean(char **l) {
-        char **r, **ret;
-
-        for (r = ret = l; *l; l++) {
-                const char *equal;
-
-                equal = strchr(*l, '=');
-
-                if (equal && equal[1] == 0) {
-                        free(*l);
-                        continue;
-                }
-
-                *(r++) = *l;
-        }
-
-        *r = NULL;
-
-        return ret;
-}
-
-char **strv_parse_nulstr(const char *s, size_t l) {
-        const char *p;
-        unsigned c = 0, i = 0;
-        char **v;
-
-        assert(s || l <= 0);
-
-        if (l <= 0)
-                return strv_new(NULL, NULL);
-
-        for (p = s; p < s + l; p++)
-                if (*p == 0)
-                        c++;
-
-        if (s[l-1] != 0)
-                c++;
-
-        if (!(v = new0(char*, c+1)))
-                return NULL;
-
-        p = s;
-        while (p < s + l) {
-                const char *e;
-
-                e = memchr(p, 0, s + l - p);
-
-                if (!(v[i++] = strndup(p, e ? e - p : s + l - p))) {
-                        strv_free(v);
-                        return NULL;
-                }
-
-                if (!e)
-                        break;
-
-                p = e + 1;
-        }
-
-        assert(i == c);
-
-        return v;
-}
-
-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;
-}
diff --git a/src/strv.h b/src/strv.h
deleted file mode 100644
index d038c9f..0000000
--- a/src/strv.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foostrvhfoo
-#define foostrvhfoo
-
-/***
-  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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stdarg.h>
-#include <stdbool.h>
-
-#include "macro.h"
-
-char *strv_find(char **l, const char *name);
-char *strv_find_prefix(char **l, const char *name);
-
-void strv_free(char **l);
-char **strv_copy(char **l) _malloc_;
-unsigned strv_length(char **l);
-
-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);
-
-char **strv_remove(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_ _malloc_;
-char **strv_new_ap(const char *x, va_list ap) _malloc_;
-
-static inline bool strv_isempty(char **l) {
-        return !l || !*l;
-}
-
-char **strv_split(const char *s, const char *separator) _malloc_;
-char **strv_split_quoted(const char *s) _malloc_;
-
-char *strv_join(char **l, const char *separator) _malloc_;
-
-char **strv_env_merge(unsigned n_lists, ...);
-char **strv_env_delete(char **x, unsigned n_lists, ...);
-
-char **strv_env_set(char **x, const char *p);
-char **strv_env_unset(char **l, const char *p);
-
-char *strv_env_get_with_length(char **l, const char *name, size_t k);
-char *strv_env_get(char **x, const char *n);
-
-char **strv_env_clean(char **l);
-
-char **strv_parse_nulstr(const char *s, size_t l);
-
-bool strv_overlap(char **a, char **b);
-
-#define STRV_FOREACH(s, l)                      \
-        for ((s) = (l); (s) && *(s); (s)++)
-
-#define STRV_FOREACH_BACKWARDS(s, l)            \
-        for (; (l) && ((s) >= (l)); (s)--)
-
-#endif
diff --git a/src/utf8.c b/src/utf8.c
deleted file mode 100644
index 11619dc..0000000
--- a/src/utf8.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2012 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-/* This file is based on the GLIB utf8 validation functions. The
- * original license text follows. */
-
-/* gutf8.c - Operations on UTF-8 strings.
- *
- * Copyright (C) 1999 Tom Tromey
- * Copyright (C) 2000 Red Hat, Inc.
- *
- * This library 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 of the License, or (at your option) any later version.
- *
- * This library 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 library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <errno.h>
-#include <stdlib.h>
-#include <inttypes.h>
-#include <string.h>
-#include <stdbool.h>
-
-#include "utf8.h"
-
-#define FILTER_CHAR '_'
-
-static inline bool is_unicode_valid(uint32_t ch) {
-
-        if (ch >= 0x110000) /* End of unicode space */
-                return false;
-        if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
-                return false;
-        if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
-                return false;
-        if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
-                return false;
-
-        return true;
-}
-
-static inline bool is_continuation_char(uint8_t ch) {
-        if ((ch & 0xc0) != 0x80) /* 10xxxxxx */
-                return false;
-        return true;
-}
-
-static inline void merge_continuation_char(uint32_t *u_ch, uint8_t ch) {
-        *u_ch <<= 6;
-        *u_ch |= ch & 0x3f;
-}
-
-static char* utf8_validate(const char *str, char *output) {
-        uint32_t val = 0;
-        uint32_t min = 0;
-        const uint8_t *p, *last;
-        int size;
-        uint8_t *o;
-
-        assert(str);
-
-        o = (uint8_t*) output;
-        for (p = (const uint8_t*) str; *p; p++) {
-                if (*p < 128) {
-                        if (o)
-                                *o = *p;
-                } else {
-                        last = p;
-
-                        if ((*p & 0xe0) == 0xc0) { /* 110xxxxx two-char seq. */
-                                size = 2;
-                                min = 128;
-                                val = (uint32_t) (*p & 0x1e);
-                                goto ONE_REMAINING;
-                        } else if ((*p & 0xf0) == 0xe0) { /* 1110xxxx three-char seq.*/
-                                size = 3;
-                                min = (1 << 11);
-                                val = (uint32_t) (*p & 0x0f);
-                                goto TWO_REMAINING;
-                        } else if ((*p & 0xf8) == 0xf0) { /* 11110xxx four-char seq */
-                                size = 4;
-                                min = (1 << 16);
-                                val = (uint32_t) (*p & 0x07);
-                        } else
-                                goto error;
-
-                        p++;
-                        if (!is_continuation_char(*p))
-                                goto error;
-                        merge_continuation_char(&val, *p);
-
-                TWO_REMAINING:
-                        p++;
-                        if (!is_continuation_char(*p))
-                                goto error;
-                        merge_continuation_char(&val, *p);
-
-                ONE_REMAINING:
-                        p++;
-                        if (!is_continuation_char(*p))
-                                goto error;
-                        merge_continuation_char(&val, *p);
-
-                        if (val < min)
-                                goto error;
-
-                        if (!is_unicode_valid(val))
-                                goto error;
-
-                        if (o) {
-                                memcpy(o, last, (size_t) size);
-                                o += size;
-                        }
-
-                        continue;
-
-                error:
-                        if (o) {
-                                *o = FILTER_CHAR;
-                                p = last; /* We retry at the next character */
-                        } else
-                                goto failure;
-                }
-
-                if (o)
-                        o++;
-        }
-
-        if (o) {
-                *o = '\0';
-                return output;
-        }
-
-        return (char*) str;
-
-failure:
-        return NULL;
-}
-
-char* utf8_is_valid (const char *str) {
-        return utf8_validate(str, NULL);
-}
-
-char* utf8_filter (const char *str) {
-        char *new_str;
-
-        assert(str);
-
-        new_str = malloc(strlen(str) + 1);
-        if (!new_str)
-                return NULL;
-
-        return utf8_validate(str, new_str);
-}
-
-char *ascii_is_valid(const char *str) {
-        const char *p;
-
-        assert(str);
-
-        for (p = str; *p; p++)
-                if ((unsigned char) *p >= 128)
-                        return NULL;
-
-        return (char*) str;
-}
-
-char *ascii_filter(const char *str) {
-        char *r, *s, *d;
-        size_t l;
-
-        assert(str);
-
-        l = strlen(str);
-        r = malloc(l + 1);
-        if (!r)
-                return NULL;
-
-        for (s = r, d = r; *s; s++)
-                if ((unsigned char) *s < 128)
-                        *(d++) = *s;
-
-        *d = 0;
-
-        return r;
-}
diff --git a/src/utf8.h b/src/utf8.h
deleted file mode 100644
index 9a72bec..0000000
--- a/src/utf8.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooutf8hfoo
-#define fooutf8hfoo
-
-/***
-  This file is part of systemd.
-
-  Copyright 2012 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "macro.h"
-
-char *utf8_is_valid(const char *s) _pure_;
-char *ascii_is_valid(const char *s) _pure_;
-
-char *utf8_filter(const char *s);
-char *ascii_filter(const char *s);
-
-#endif
diff --git a/src/util.c b/src/util.c
deleted file mode 100644
index dfc1dc6..0000000
--- a/src/util.c
+++ /dev/null
@@ -1,6256 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <assert.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <stdio.h>
-#include <syslog.h>
-#include <sched.h>
-#include <sys/resource.h>
-#include <linux/sched.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <sys/ioctl.h>
-#include <linux/vt.h>
-#include <linux/tiocl.h>
-#include <termios.h>
-#include <stdarg.h>
-#include <sys/inotify.h>
-#include <sys/poll.h>
-#include <libgen.h>
-#include <ctype.h>
-#include <sys/prctl.h>
-#include <sys/utsname.h>
-#include <pwd.h>
-#include <netinet/ip.h>
-#include <linux/kd.h>
-#include <dlfcn.h>
-#include <sys/wait.h>
-#include <sys/capability.h>
-#include <sys/time.h>
-#include <linux/rtc.h>
-#include <glob.h>
-#include <grp.h>
-#include <sys/mman.h>
-
-#include "macro.h"
-#include "util.h"
-#include "ioprio.h"
-#include "missing.h"
-#include "log.h"
-#include "strv.h"
-#include "label.h"
-#include "exit-status.h"
-#include "hashmap.h"
-
-int saved_argc = 0;
-char **saved_argv = NULL;
-
-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 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;
-}
-
-usec_t now(clockid_t clock_id) {
-        struct timespec ts;
-
-        assert_se(clock_gettime(clock_id, &ts) == 0);
-
-        return timespec_load(&ts);
-}
-
-dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
-        assert(ts);
-
-        ts->realtime = now(CLOCK_REALTIME);
-        ts->monotonic = now(CLOCK_MONOTONIC);
-
-        return ts;
-}
-
-dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
-        int64_t delta;
-        assert(ts);
-
-        ts->realtime = u;
-
-        if (u == 0)
-                ts->monotonic = 0;
-        else {
-                delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
-
-                ts->monotonic = now(CLOCK_MONOTONIC);
-
-                if ((int64_t) ts->monotonic > delta)
-                        ts->monotonic -= delta;
-                else
-                        ts->monotonic = 0;
-        }
-
-        return ts;
-}
-
-usec_t timespec_load(const struct timespec *ts) {
-        assert(ts);
-
-        return
-                (usec_t) ts->tv_sec * USEC_PER_SEC +
-                (usec_t) ts->tv_nsec / NSEC_PER_USEC;
-}
-
-struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
-        assert(ts);
-
-        ts->tv_sec = (time_t) (u / USEC_PER_SEC);
-        ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
-
-        return ts;
-}
-
-usec_t timeval_load(const struct timeval *tv) {
-        assert(tv);
-
-        return
-                (usec_t) tv->tv_sec * USEC_PER_SEC +
-                (usec_t) tv->tv_usec;
-}
-
-struct timeval *timeval_store(struct timeval *tv, usec_t u) {
-        assert(tv);
-
-        tv->tv_sec = (time_t) (u / USEC_PER_SEC);
-        tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
-
-        return tv;
-}
-
-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;
-}
-
-bool startswith(const char *s, const char *prefix) {
-        size_t sl, pl;
-
-        assert(s);
-        assert(prefix);
-
-        sl = strlen(s);
-        pl = strlen(prefix);
-
-        if (pl == 0)
-                return true;
-
-        if (sl < pl)
-                return false;
-
-        return memcmp(s, prefix, pl) == 0;
-}
-
-bool startswith_no_case(const char *s, const char *prefix) {
-        size_t sl, pl;
-        unsigned i;
-
-        assert(s);
-        assert(prefix);
-
-        sl = strlen(s);
-        pl = strlen(prefix);
-
-        if (pl == 0)
-                return true;
-
-        if (sl < pl)
-                return false;
-
-        for(i = 0; i < pl; ++i) {
-                if (tolower(s[i]) != tolower(prefix[i]))
-                        return false;
-        }
-
-        return true;
-}
-
-bool first_word(const char *s, const char *word) {
-        size_t sl, wl;
-
-        assert(s);
-        assert(word);
-
-        sl = strlen(s);
-        wl = strlen(word);
-
-        if (sl < wl)
-                return false;
-
-        if (wl == 0)
-                return true;
-
-        if (memcmp(s, word, wl) != 0)
-                return false;
-
-        return s[wl] == 0 ||
-                strchr(WHITESPACE, s[wl]);
-}
-
-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;
-}
-
-void close_many(const int fds[], unsigned n_fd) {
-        unsigned i;
-
-        for (i = 0; i < n_fd; i++)
-                close_nointr_nofail(fds[i]);
-}
-
-int parse_boolean(const char *v) {
-        assert(v);
-
-        if (streq(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on"))
-                return 1;
-        else if (streq(v, "0") || v[0] == 'n' || v[0] == 'N' || v[0] == 'f' || v[0] == 'F' || !strcasecmp(v, "off"))
-                return 0;
-
-        return -EINVAL;
-}
-
-int parse_pid(const char *s, pid_t* ret_pid) {
-        unsigned long ul = 0;
-        pid_t pid;
-        int r;
-
-        assert(s);
-        assert(ret_pid);
-
-        if ((r = safe_atolu(s, &ul)) < 0)
-                return r;
-
-        pid = (pid_t) ul;
-
-        if ((unsigned long) pid != ul)
-                return -ERANGE;
-
-        if (pid <= 0)
-                return -ERANGE;
-
-        *ret_pid = pid;
-        return 0;
-}
-
-int parse_uid(const char *s, uid_t* ret_uid) {
-        unsigned long ul = 0;
-        uid_t uid;
-        int r;
-
-        assert(s);
-        assert(ret_uid);
-
-        if ((r = safe_atolu(s, &ul)) < 0)
-                return r;
-
-        uid = (uid_t) ul;
-
-        if ((unsigned long) uid != ul)
-                return -ERANGE;
-
-        *ret_uid = uid;
-        return 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;
-}
-
-int safe_atoi(const char *s, int *ret_i) {
-        char *x = NULL;
-        long l;
-
-        assert(s);
-        assert(ret_i);
-
-        errno = 0;
-        l = strtol(s, &x, 0);
-
-        if (!x || *x || errno)
-                return errno ? -errno : -EINVAL;
-
-        if ((long) (int) l != l)
-                return -ERANGE;
-
-        *ret_i = (int) l;
-        return 0;
-}
-
-int safe_atollu(const char *s, long long unsigned *ret_llu) {
-        char *x = NULL;
-        unsigned long long l;
-
-        assert(s);
-        assert(ret_llu);
-
-        errno = 0;
-        l = strtoull(s, &x, 0);
-
-        if (!x || *x || errno)
-                return errno ? -errno : -EINVAL;
-
-        *ret_llu = l;
-        return 0;
-}
-
-int safe_atolli(const char *s, long long int *ret_lli) {
-        char *x = NULL;
-        long long l;
-
-        assert(s);
-        assert(ret_lli);
-
-        errno = 0;
-        l = strtoll(s, &x, 0);
-
-        if (!x || *x || errno)
-                return errno ? -errno : -EINVAL;
-
-        *ret_lli = l;
-        return 0;
-}
-
-/* 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;
-}
-
-/* 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) {
-        char *current, *e;
-        bool escaped = false;
-
-        current = *state ? *state : (char*) c;
-
-        if (!*current || *c == 0)
-                return NULL;
-
-        current += strspn(current, WHITESPACE);
-
-        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 = *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 = *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 = e;
-        }
-
-        return (char*) current;
-}
-
-char **split_path_and_make_absolute(const char *p) {
-        char **l;
-        assert(p);
-
-        if (!(l = strv_split(p, ":")))
-                return NULL;
-
-        if (!strv_path_make_absolute_cwd(l)) {
-                strv_free(l);
-                return NULL;
-        }
-
-        return l;
-}
-
-int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
-        int r;
-        FILE *f;
-        char fn[PATH_MAX], line[LINE_MAX], *p;
-        long unsigned ppid;
-
-        assert(pid > 0);
-        assert(_ppid);
-
-        assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
-        char_array_0(fn);
-
-        if (!(f = fopen(fn, "re")))
-                return -errno;
-
-        if (!(fgets(line, sizeof(line), f))) {
-                r = feof(f) ? -EIO : -errno;
-                fclose(f);
-                return r;
-        }
-
-        fclose(f);
-
-        /* Let's skip the pid and comm fields. The latter is enclosed
-         * in () but does not escape any () in its value, so let's
-         * skip over it manually */
-
-        if (!(p = strrchr(line, ')')))
-                return -EIO;
-
-        p++;
-
-        if (sscanf(p, " "
-                   "%*c "  /* state */
-                   "%lu ", /* ppid */
-                   &ppid) != 1)
-                return -EIO;
-
-        if ((long unsigned) (pid_t) ppid != ppid)
-                return -ERANGE;
-
-        *_ppid = (pid_t) ppid;
-
-        return 0;
-}
-
-int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
-        int r;
-        FILE *f;
-        char fn[PATH_MAX], line[LINE_MAX], *p;
-
-        assert(pid > 0);
-        assert(st);
-
-        assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
-        char_array_0(fn);
-
-        if (!(f = fopen(fn, "re")))
-                return -errno;
-
-        if (!(fgets(line, sizeof(line), f))) {
-                r = feof(f) ? -EIO : -errno;
-                fclose(f);
-                return r;
-        }
-
-        fclose(f);
-
-        /* Let's skip the pid and comm fields. The latter is enclosed
-         * in () but does not escape any () in its value, so let's
-         * skip over it manually */
-
-        if (!(p = strrchr(line, ')')))
-                return -EIO;
-
-        p++;
-
-        if (sscanf(p, " "
-                   "%*c "  /* state */
-                   "%*d "  /* ppid */
-                   "%*d "  /* pgrp */
-                   "%*d "  /* session */
-                   "%*d "  /* tty_nr */
-                   "%*d "  /* tpgid */
-                   "%*u "  /* flags */
-                   "%*u "  /* minflt */
-                   "%*u "  /* cminflt */
-                   "%*u "  /* majflt */
-                   "%*u "  /* cmajflt */
-                   "%*u "  /* utime */
-                   "%*u "  /* stime */
-                   "%*d "  /* cutime */
-                   "%*d "  /* cstime */
-                   "%*d "  /* priority */
-                   "%*d "  /* nice */
-                   "%*d "  /* num_threads */
-                   "%*d "  /* itrealvalue */
-                   "%llu "  /* starttime */,
-                   st) != 1)
-                return -EIO;
-
-        return 0;
-}
-
-int write_one_line_file(const char *fn, const char *line) {
-        FILE *f;
-        int r;
-
-        assert(fn);
-        assert(line);
-
-        if (!(f = fopen(fn, "we")))
-                return -errno;
-
-        errno = 0;
-        if (fputs(line, f) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        if (!endswith(line, "\n"))
-                fputc('\n', f);
-
-        fflush(f);
-
-        if (ferror(f)) {
-                if (errno != 0)
-                        r = -errno;
-                else
-                        r = -EIO;
-        } else
-                r = 0;
-
-finish:
-        fclose(f);
-        return r;
-}
-
-int fchmod_umask(int fd, mode_t m) {
-        mode_t u;
-        int r;
-
-        u = umask(0777);
-        r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
-        umask(u);
-
-        return r;
-}
-
-int write_one_line_file_atomic(const char *fn, const char *line) {
-        FILE *f;
-        int r;
-        char *p;
-
-        assert(fn);
-        assert(line);
-
-        r = fopen_temporary(fn, &f, &p);
-        if (r < 0)
-                return r;
-
-        fchmod_umask(fileno(f), 0644);
-
-        errno = 0;
-        if (fputs(line, f) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        if (!endswith(line, "\n"))
-                fputc('\n', f);
-
-        fflush(f);
-
-        if (ferror(f)) {
-                if (errno != 0)
-                        r = -errno;
-                else
-                        r = -EIO;
-        } else {
-                if (rename(p, fn) < 0)
-                        r = -errno;
-                else
-                        r = 0;
-        }
-
-finish:
-        if (r < 0)
-                unlink(p);
-
-        fclose(f);
-        free(p);
-
-        return r;
-}
-
-int read_one_line_file(const char *fn, char **line) {
-        FILE *f;
-        int r;
-        char t[LINE_MAX], *c;
-
-        assert(fn);
-        assert(line);
-
-        f = fopen(fn, "re");
-        if (!f)
-                return -errno;
-
-        if (!fgets(t, sizeof(t), f)) {
-
-                if (ferror(f)) {
-                        r = -errno;
-                        goto finish;
-                }
-
-                t[0] = 0;
-        }
-
-        c = strdup(t);
-        if (!c) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        truncate_nl(c);
-
-        *line = c;
-        r = 0;
-
-finish:
-        fclose(f);
-        return r;
-}
-
-int read_full_file(const char *fn, char **contents, size_t *size) {
-        FILE *f;
-        int r;
-        size_t n, l;
-        char *buf = NULL;
-        struct stat st;
-
-        if (!(f = fopen(fn, "re")))
-                return -errno;
-
-        if (fstat(fileno(f), &st) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        /* Safety check */
-        if (st.st_size > 4*1024*1024) {
-                r = -E2BIG;
-                goto finish;
-        }
-
-        n = st.st_size > 0 ? st.st_size : LINE_MAX;
-        l = 0;
-
-        for (;;) {
-                char *t;
-                size_t k;
-
-                if (!(t = realloc(buf, n+1))) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                buf = t;
-                k = fread(buf + l, 1, n - l, f);
-
-                if (k <= 0) {
-                        if (ferror(f)) {
-                                r = -errno;
-                                goto finish;
-                        }
-
-                        break;
-                }
-
-                l += k;
-                n *= 2;
-
-                /* Safety check */
-                if (n > 4*1024*1024) {
-                        r = -E2BIG;
-                        goto finish;
-                }
-        }
-
-        buf[l] = 0;
-        *contents = buf;
-        buf = NULL;
-
-        if (size)
-                *size = l;
-
-        r = 0;
-
-finish:
-        fclose(f);
-        free(buf);
-
-        return r;
-}
-
-int parse_env_file(
-                const char *fname,
-                const char *separator, ...) {
-
-        int r = 0;
-        char *contents = NULL, *p;
-
-        assert(fname);
-        assert(separator);
-
-        if ((r = read_full_file(fname, &contents, NULL)) < 0)
-                return r;
-
-        p = contents;
-        for (;;) {
-                const char *key = NULL;
-
-                p += strspn(p, separator);
-                p += strspn(p, WHITESPACE);
-
-                if (!*p)
-                        break;
-
-                if (!strchr(COMMENTS, *p)) {
-                        va_list ap;
-                        char **value;
-
-                        va_start(ap, separator);
-                        while ((key = va_arg(ap, char *))) {
-                                size_t n;
-                                char *v;
-
-                                value = va_arg(ap, char **);
-
-                                n = strlen(key);
-                                if (strncmp(p, key, n) != 0 ||
-                                    p[n] != '=')
-                                        continue;
-
-                                p += n + 1;
-                                n = strcspn(p, separator);
-
-                                if (n >= 2 &&
-                                    strchr(QUOTES, p[0]) &&
-                                    p[n-1] == p[0])
-                                        v = strndup(p+1, n-2);
-                                else
-                                        v = strndup(p, n);
-
-                                if (!v) {
-                                        r = -ENOMEM;
-                                        va_end(ap);
-                                        goto fail;
-                                }
-
-                                if (v[0] == '\0') {
-                                        /* return empty value strings as NULL */
-                                        free(v);
-                                        v = NULL;
-                                }
-
-                                free(*value);
-                                *value = v;
-
-                                p += n;
-
-                                r ++;
-                                break;
-                        }
-                        va_end(ap);
-                }
-
-                if (!key)
-                        p += strcspn(p, separator);
-        }
-
-fail:
-        free(contents);
-        return r;
-}
-
-int load_env_file(
-                const char *fname,
-                char ***rl) {
-
-        FILE *f;
-        char **m = NULL;
-        int r;
-
-        assert(fname);
-        assert(rl);
-
-        if (!(f = fopen(fname, "re")))
-                return -errno;
-
-        while (!feof(f)) {
-                char l[LINE_MAX], *p, *u;
-                char **t;
-
-                if (!fgets(l, sizeof(l), f)) {
-                        if (feof(f))
-                                break;
-
-                        r = -errno;
-                        goto finish;
-                }
-
-                p = strstrip(l);
-
-                if (!*p)
-                        continue;
-
-                if (strchr(COMMENTS, *p))
-                        continue;
-
-                if (!(u = normalize_env_assignment(p))) {
-                        log_error("Out of memory");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                t = strv_append(m, u);
-                free(u);
-
-                if (!t) {
-                        log_error("Out of memory");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                strv_free(m);
-                m = t;
-        }
-
-        r = 0;
-
-        *rl = m;
-        m = NULL;
-
-finish:
-        if (f)
-                fclose(f);
-
-        strv_free(m);
-
-        return r;
-}
-
-int write_env_file(const char *fname, char **l) {
-        char **i, *p;
-        FILE *f;
-        int r;
-
-        r = fopen_temporary(fname, &f, &p);
-        if (r < 0)
-                return r;
-
-        fchmod_umask(fileno(f), 0644);
-
-        errno = 0;
-        STRV_FOREACH(i, l) {
-                fputs(*i, f);
-                fputc('\n', f);
-        }
-
-        fflush(f);
-
-        if (ferror(f)) {
-                if (errno != 0)
-                        r = -errno;
-                else
-                        r = -EIO;
-        } else {
-                if (rename(p, fname) < 0)
-                        r = -errno;
-                else
-                        r = 0;
-        }
-
-        if (r < 0)
-                unlink(p);
-
-        fclose(f);
-        free(p);
-
-        return r;
-}
-
-char *truncate_nl(char *s) {
-        assert(s);
-
-        s[strcspn(s, NEWLINE)] = 0;
-        return s;
-}
-
-int get_process_comm(pid_t pid, char **name) {
-        int r;
-
-        assert(name);
-
-        if (pid == 0)
-                r = read_one_line_file("/proc/self/comm", name);
-        else {
-                char *p;
-                if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0)
-                        return -ENOMEM;
-
-                r = read_one_line_file(p, name);
-                free(p);
-        }
-
-        return r;
-}
-
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
-        char *r, *k;
-        int c;
-        bool space = false;
-        size_t left;
-        FILE *f;
-
-        assert(max_length > 0);
-        assert(line);
-
-        if (pid == 0)
-                f = fopen("/proc/self/cmdline", "re");
-        else {
-                char *p;
-                if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
-                        return -ENOMEM;
-
-                f = fopen(p, "re");
-                free(p);
-        }
-
-        if (!f)
-                return -errno;
-
-        r = new(char, max_length);
-        if (!r) {
-                fclose(f);
-                return -ENOMEM;
-        }
-
-        k = r;
-        left = max_length;
-        while ((c = getc(f)) != EOF) {
-
-                if (isprint(c)) {
-                        if (space) {
-                                if (left <= 4)
-                                        break;
-
-                                *(k++) = ' ';
-                                left--;
-                                space = false;
-                        }
-
-                        if (left <= 4)
-                                break;
-
-                        *(k++) = (char) c;
-                        left--;
-                }  else
-                        space = true;
-        }
-
-        if (left <= 4) {
-                size_t n = MIN(left-1, 3U);
-                memcpy(k, "...", n);
-                k[n] = 0;
-        } else
-                *k = 0;
-
-        fclose(f);
-
-        /* Kernel threads have no argv[] */
-        if (r[0] == 0) {
-                char *t;
-                int h;
-
-                free(r);
-
-                if (!comm_fallback)
-                        return -ENOENT;
-
-                h = get_process_comm(pid, &t);
-                if (h < 0)
-                        return h;
-
-                r = join("[", t, "]", NULL);
-                free(t);
-
-                if (!r)
-                        return -ENOMEM;
-        }
-
-        *line = r;
-        return 0;
-}
-
-int is_kernel_thread(pid_t pid) {
-        char *p;
-        size_t count;
-        char c;
-        bool eof;
-        FILE *f;
-
-        if (pid == 0)
-                return 0;
-
-        if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
-                return -ENOMEM;
-
-        f = fopen(p, "re");
-        free(p);
-
-        if (!f)
-                return -errno;
-
-        count = fread(&c, 1, 1, f);
-        eof = feof(f);
-        fclose(f);
-
-        /* Kernel threads have an empty cmdline */
-
-        if (count <= 0)
-                return eof ? 1 : -errno;
-
-        return 0;
-}
-
-int get_process_exe(pid_t pid, char **name) {
-        int r;
-
-        assert(name);
-
-        if (pid == 0)
-                r = readlink_malloc("/proc/self/exe", name);
-        else {
-                char *p;
-                if (asprintf(&p, "/proc/%lu/exe", (unsigned long) pid) < 0)
-                        return -ENOMEM;
-
-                r = readlink_malloc(p, name);
-                free(p);
-        }
-
-        return r;
-}
-
-int get_process_uid(pid_t pid, uid_t *uid) {
-        char *p;
-        FILE *f;
-        int r;
-
-        assert(uid);
-
-        if (pid == 0)
-                return getuid();
-
-        if (asprintf(&p, "/proc/%lu/status", (unsigned long) pid) < 0)
-                return -ENOMEM;
-
-        f = fopen(p, "re");
-        free(p);
-
-        if (!f)
-                return -errno;
-
-        while (!feof(f)) {
-                char line[LINE_MAX], *l;
-
-                if (!fgets(line, sizeof(line), f)) {
-                        if (feof(f))
-                                break;
-
-                        r = -errno;
-                        goto finish;
-                }
-
-                l = strstrip(line);
-
-                if (startswith(l, "Uid:")) {
-                        l += 4;
-                        l += strspn(l, WHITESPACE);
-
-                        l[strcspn(l, WHITESPACE)] = 0;
-
-                        r = parse_uid(l, uid);
-                        goto finish;
-                }
-        }
-
-        r = -EIO;
-
-finish:
-        fclose(f);
-
-        return r;
-}
-
-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 (!(r = new(char, a+b+1)))
-                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);
-}
-
-int readlink_malloc(const char *p, char **r) {
-        size_t l = 100;
-
-        assert(p);
-        assert(r);
-
-        for (;;) {
-                char *c;
-                ssize_t n;
-
-                if (!(c = new(char, l)))
-                        return -ENOMEM;
-
-                if ((n = readlink(p, c, l-1)) < 0) {
-                        int ret = -errno;
-                        free(c);
-                        return ret;
-                }
-
-                if ((size_t) n < l-1) {
-                        c[n] = 0;
-                        *r = c;
-                        return 0;
-                }
-
-                free(c);
-                l *= 2;
-        }
-}
-
-int readlink_and_make_absolute(const char *p, char **r) {
-        char *target, *k;
-        int j;
-
-        assert(p);
-        assert(r);
-
-        if ((j = readlink_malloc(p, &target)) < 0)
-                return j;
-
-        k = file_in_same_dir(p, target);
-        free(target);
-
-        if (!k)
-                return -ENOMEM;
-
-        *r = k;
-        return 0;
-}
-
-int readlink_and_canonicalize(const char *p, char **r) {
-        char *t, *s;
-        int j;
-
-        assert(p);
-        assert(r);
-
-        j = readlink_and_make_absolute(p, &t);
-        if (j < 0)
-                return j;
-
-        s = canonicalize_file_name(t);
-        if (s) {
-                free(t);
-                *r = s;
-        } else
-                *r = t;
-
-        path_kill_slashes(*r);
-
-        return 0;
-}
-
-int parent_of_path(const char *path, char **_r) {
-        const char *e, *a = NULL, *b = NULL, *p;
-        char *r;
-        bool slash = false;
-
-        assert(path);
-        assert(_r);
-
-        if (!*path)
-                return -EINVAL;
-
-        for (e = path; *e; e++) {
-
-                if (!slash && *e == '/') {
-                        a = b;
-                        b = e;
-                        slash = true;
-                } else if (slash && *e != '/')
-                        slash = false;
-        }
-
-        if (*(e-1) == '/')
-                p = a;
-        else
-                p = b;
-
-        if (!p)
-                return -EINVAL;
-
-        if (p == path)
-                r = strdup("/");
-        else
-                r = strndup(path, p-path);
-
-        if (!r)
-                return -ENOMEM;
-
-        *_r = r;
-        return 0;
-}
-
-
-char *file_name_from_path(const char *p) {
-        char *r;
-
-        assert(p);
-
-        if ((r = strrchr(p, '/')))
-                return r + 1;
-
-        return (char*) p;
-}
-
-bool path_is_absolute(const char *p) {
-        assert(p);
-
-        return p[0] == '/';
-}
-
-bool is_path(const char *p) {
-
-        return !!strchr(p, '/');
-}
-
-char *path_make_absolute(const char *p, const char *prefix) {
-        assert(p);
-
-        /* Makes every item in the list an absolute path by prepending
-         * the prefix, if specified and necessary */
-
-        if (path_is_absolute(p) || !prefix)
-                return strdup(p);
-
-        return join(prefix, "/", p, NULL);
-}
-
-char *path_make_absolute_cwd(const char *p) {
-        char *cwd, *r;
-
-        assert(p);
-
-        /* Similar to path_make_absolute(), but prefixes with the
-         * current working directory. */
-
-        if (path_is_absolute(p))
-                return strdup(p);
-
-        if (!(cwd = get_current_dir_name()))
-                return NULL;
-
-        r = path_make_absolute(p, cwd);
-        free(cwd);
-
-        return r;
-}
-
-char **strv_path_make_absolute_cwd(char **l) {
-        char **s;
-
-        /* Goes through every item in the string list and makes it
-         * absolute. This works in place and won't rollback any
-         * changes on failure. */
-
-        STRV_FOREACH(s, l) {
-                char *t;
-
-                if (!(t = path_make_absolute_cwd(*s)))
-                        return NULL;
-
-                free(*s);
-                *s = t;
-        }
-
-        return l;
-}
-
-char **strv_path_canonicalize(char **l) {
-        char **s;
-        unsigned k = 0;
-        bool enomem = false;
-
-        if (strv_isempty(l))
-                return l;
-
-        /* Goes through every item in the string list and canonicalize
-         * the path. This works in place and won't rollback any
-         * changes on failure. */
-
-        STRV_FOREACH(s, l) {
-                char *t, *u;
-
-                t = path_make_absolute_cwd(*s);
-                free(*s);
-
-                if (!t) {
-                        enomem = true;
-                        continue;
-                }
-
-                errno = 0;
-                u = canonicalize_file_name(t);
-                free(t);
-
-                if (!u) {
-                        if (errno == ENOMEM || !errno)
-                                enomem = true;
-
-                        continue;
-                }
-
-                l[k++] = u;
-        }
-
-        l[k] = NULL;
-
-        if (enomem)
-                return NULL;
-
-        return l;
-}
-
-char **strv_path_remove_empty(char **l) {
-        char **f, **t;
-
-        if (!l)
-                return NULL;
-
-        for (f = t = l; *f; f++) {
-
-                if (dir_is_empty(*f) > 0) {
-                        free(*f);
-                        continue;
-                }
-
-                *(t++) = *f;
-        }
-
-        *t = NULL;
-        return l;
-}
-
-int reset_all_signal_handlers(void) {
-        int sig;
-
-        for (sig = 1; sig < _NSIG; sig++) {
-                struct sigaction sa;
-
-                if (sig == SIGKILL || sig == SIGSTOP)
-                        continue;
-
-                zero(sa);
-                sa.sa_handler = SIG_DFL;
-                sa.sa_flags = SA_RESTART;
-
-                /* On Linux the first two RT signals are reserved by
-                 * glibc, and sigaction() will return EINVAL for them. */
-                if ((sigaction(sig, &sa, NULL) < 0))
-                        if (errno != EINVAL)
-                                return -errno;
-        }
-
-        return 0;
-}
-
-char *strstrip(char *s) {
-        char *e;
-
-        /* Drops trailing whitespace. Modifies the string in
-         * place. Returns pointer to first non-space character */
-
-        s += strspn(s, WHITESPACE);
-
-        for (e = strchr(s, 0); e > s; e --)
-                if (!strchr(WHITESPACE, e[-1]))
-                        break;
-
-        *e = 0;
-
-        return s;
-}
-
-char *delete_chars(char *s, const char *bad) {
-        char *f, *t;
-
-        /* Drops all whitespace, regardless where in the string */
-
-        for (f = s, t = s; *f; f++) {
-                if (strchr(bad, *f))
-                        continue;
-
-                *(t++) = *f;
-        }
-
-        *t = 0;
-
-        return s;
-}
-
-bool in_charset(const char *s, const char* charset) {
-        const char *i;
-
-        assert(s);
-        assert(charset);
-
-        for (i = s; *i; i++)
-                if (!strchr(charset, *i))
-                        return false;
-
-        return true;
-}
-
-char *file_in_same_dir(const char *path, const char *filename) {
-        char *e, *r;
-        size_t k;
-
-        assert(path);
-        assert(filename);
-
-        /* This removes the last component of path and appends
-         * filename, unless the latter is absolute anyway or the
-         * former isn't */
-
-        if (path_is_absolute(filename))
-                return strdup(filename);
-
-        if (!(e = strrchr(path, '/')))
-                return strdup(filename);
-
-        k = strlen(filename);
-        if (!(r = new(char, e-path+1+k+1)))
-                return NULL;
-
-        memcpy(r, path, e-path+1);
-        memcpy(r+(e-path)+1, filename, k+1);
-
-        return r;
-}
-
-int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) {
-        struct stat st;
-
-        if (label_mkdir(path, mode) >= 0)
-                if (chmod_and_chown(path, mode, uid, gid) < 0)
-                        return -errno;
-
-        if (lstat(path, &st) < 0)
-                return -errno;
-
-        if ((st.st_mode & 0777) != mode ||
-            st.st_uid != uid ||
-            st.st_gid != gid ||
-            !S_ISDIR(st.st_mode)) {
-                errno = EEXIST;
-                return -errno;
-        }
-
-        return 0;
-}
-
-
-int mkdir_parents(const char *path, mode_t mode) {
-        const char *p, *e;
-
-        assert(path);
-
-        /* Creates every parent directory in the path except the last
-         * component. */
-
-        p = path + strspn(path, "/");
-        for (;;) {
-                int r;
-                char *t;
-
-                e = p + strcspn(p, "/");
-                p = e + strspn(e, "/");
-
-                /* Is this the last component? If so, then we're
-                 * done */
-                if (*p == 0)
-                        return 0;
-
-                if (!(t = strndup(path, e - path)))
-                        return -ENOMEM;
-
-                r = label_mkdir(t, mode);
-                free(t);
-
-                if (r < 0 && errno != EEXIST)
-                        return -errno;
-        }
-}
-
-int mkdir_p(const char *path, mode_t mode) {
-        int r;
-
-        /* Like mkdir -p */
-
-        if ((r = mkdir_parents(path, mode)) < 0)
-                return r;
-
-        if (label_mkdir(path, mode) < 0 && errno != EEXIST)
-                return -errno;
-
-        return 0;
-}
-
-int rmdir_parents(const char *path, const char *stop) {
-        size_t l;
-        int r = 0;
-
-        assert(path);
-        assert(stop);
-
-        l = strlen(path);
-
-        /* Skip trailing slashes */
-        while (l > 0 && path[l-1] == '/')
-                l--;
-
-        while (l > 0) {
-                char *t;
-
-                /* Skip last component */
-                while (l > 0 && path[l-1] != '/')
-                        l--;
-
-                /* Skip trailing slashes */
-                while (l > 0 && path[l-1] == '/')
-                        l--;
-
-                if (l <= 0)
-                        break;
-
-                if (!(t = strndup(path, l)))
-                        return -ENOMEM;
-
-                if (path_startswith(stop, t)) {
-                        free(t);
-                        return 0;
-                }
-
-                r = rmdir(t);
-                free(t);
-
-                if (r < 0)
-                        if (errno != ENOENT)
-                                return -errno;
-        }
-
-        return 0;
-}
-
-
-char hexchar(int x) {
-        static const char table[16] = "0123456789abcdef";
-
-        return table[x & 15];
-}
-
-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;
-}
-
-char octchar(int x) {
-        return '0' + (x & 7);
-}
-
-int unoctchar(char c) {
-
-        if (c >= '0' && c <= '7')
-                return c - '0';
-
-        return -1;
-}
-
-char decchar(int x) {
-        return '0' + (x % 10);
-}
-
-int undecchar(char c) {
-
-        if (c >= '0' && c <= '9')
-                return c - '0';
-
-        return -1;
-}
-
-char *cescape(const char *s) {
-        char *r, *t;
-        const char *f;
-
-        assert(s);
-
-        /* Does C style string escaping. */
-
-        if (!(r = new(char, strlen(s)*4 + 1)))
-                return NULL;
-
-        for (f = s, t = r; *f; f++)
-
-                switch (*f) {
-
-                case '\a':
-                        *(t++) = '\\';
-                        *(t++) = 'a';
-                        break;
-                case '\b':
-                        *(t++) = '\\';
-                        *(t++) = 'b';
-                        break;
-                case '\f':
-                        *(t++) = '\\';
-                        *(t++) = 'f';
-                        break;
-                case '\n':
-                        *(t++) = '\\';
-                        *(t++) = 'n';
-                        break;
-                case '\r':
-                        *(t++) = '\\';
-                        *(t++) = 'r';
-                        break;
-                case '\t':
-                        *(t++) = '\\';
-                        *(t++) = 't';
-                        break;
-                case '\v':
-                        *(t++) = '\\';
-                        *(t++) = 'v';
-                        break;
-                case '\\':
-                        *(t++) = '\\';
-                        *(t++) = '\\';
-                        break;
-                case '"':
-                        *(t++) = '\\';
-                        *(t++) = '"';
-                        break;
-                case '\'':
-                        *(t++) = '\\';
-                        *(t++) = '\'';
-                        break;
-
-                default:
-                        /* For special chars we prefer octal over
-                         * hexadecimal encoding, simply because glib's
-                         * g_strescape() does the same */
-                        if ((*f < ' ') || (*f >= 127)) {
-                                *(t++) = '\\';
-                                *(t++) = octchar((unsigned char) *f >> 6);
-                                *(t++) = octchar((unsigned char) *f >> 3);
-                                *(t++) = octchar((unsigned char) *f);
-                        } else
-                                *(t++) = *f;
-                        break;
-                }
-
-        *t = 0;
-
-        return r;
-}
-
-char *cunescape_length(const char *s, size_t length) {
-        char *r, *t;
-        const char *f;
-
-        assert(s);
-
-        /* Undoes C style string escaping */
-
-        r = new(char, length+1);
-        if (!r)
-                return r;
-
-        for (f = s, t = r; 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(const char *s) {
-        return cunescape_length(s, strlen(s));
-}
-
-char *xescape(const char *s, const char *bad) {
-        char *r, *t;
-        const char *f;
-
-        /* Escapes all chars in bad, in addition to \ and all special
-         * chars, in \xFF style escaping. May be reversed with
-         * cunescape. */
-
-        if (!(r = new(char, strlen(s)*4+1)))
-                return NULL;
-
-        for (f = s, t = r; *f; f++) {
-
-                if ((*f < ' ') || (*f >= 127) ||
-                    (*f == '\\') || strchr(bad, *f)) {
-                        *(t++) = '\\';
-                        *(t++) = 'x';
-                        *(t++) = hexchar(*f >> 4);
-                        *(t++) = hexchar(*f);
-                } else
-                        *(t++) = *f;
-        }
-
-        *t = 0;
-
-        return r;
-}
-
-char *bus_path_escape(const char *s) {
-        char *r, *t;
-        const char *f;
-
-        assert(s);
-
-        /* Escapes all chars that D-Bus' object path cannot deal
-         * with. Can be reverse with bus_path_unescape() */
-
-        if (!(r = new(char, strlen(s)*3+1)))
-                return NULL;
-
-        for (f = s, t = r; *f; f++) {
-
-                if (!(*f >= 'A' && *f <= 'Z') &&
-                    !(*f >= 'a' && *f <= 'z') &&
-                    !(*f >= '0' && *f <= '9')) {
-                        *(t++) = '_';
-                        *(t++) = hexchar(*f >> 4);
-                        *(t++) = hexchar(*f);
-                } else
-                        *(t++) = *f;
-        }
-
-        *t = 0;
-
-        return r;
-}
-
-char *bus_path_unescape(const char *f) {
-        char *r, *t;
-
-        assert(f);
-
-        if (!(r = strdup(f)))
-                return NULL;
-
-        for (t = r; *f; f++) {
-
-                if (*f == '_') {
-                        int a, b;
-
-                        if ((a = unhexchar(f[1])) < 0 ||
-                            (b = unhexchar(f[2])) < 0) {
-                                /* Invalid escape code, let's take it literal then */
-                                *(t++) = '_';
-                        } else {
-                                *(t++) = (char) ((a << 4) | b);
-                                f += 2;
-                        }
-                } else
-                        *(t++) = *f;
-        }
-
-        *t = 0;
-
-        return r;
-}
-
-char *path_kill_slashes(char *path) {
-        char *f, *t;
-        bool slash = false;
-
-        /* Removes redundant inner and trailing slashes. Modifies the
-         * passed string in-place.
-         *
-         * ///foo///bar/ becomes /foo/bar
-         */
-
-        for (f = path, t = path; *f; f++) {
-
-                if (*f == '/') {
-                        slash = true;
-                        continue;
-                }
-
-                if (slash) {
-                        slash = false;
-                        *(t++) = '/';
-                }
-
-                *(t++) = *f;
-        }
-
-        /* Special rule, if we are talking of the root directory, a
-        trailing slash is good */
-
-        if (t == path && slash)
-                *(t++) = '/';
-
-        *t = 0;
-        return path;
-}
-
-bool path_startswith(const char *path, const char *prefix) {
-        assert(path);
-        assert(prefix);
-
-        if ((path[0] == '/') != (prefix[0] == '/'))
-                return false;
-
-        for (;;) {
-                size_t a, b;
-
-                path += strspn(path, "/");
-                prefix += strspn(prefix, "/");
-
-                if (*prefix == 0)
-                        return true;
-
-                if (*path == 0)
-                        return false;
-
-                a = strcspn(path, "/");
-                b = strcspn(prefix, "/");
-
-                if (a != b)
-                        return false;
-
-                if (memcmp(path, prefix, a) != 0)
-                        return false;
-
-                path += a;
-                prefix += b;
-        }
-}
-
-bool path_equal(const char *a, const char *b) {
-        assert(a);
-        assert(b);
-
-        if ((a[0] == '/') != (b[0] == '/'))
-                return false;
-
-        for (;;) {
-                size_t j, k;
-
-                a += strspn(a, "/");
-                b += strspn(b, "/");
-
-                if (*a == 0 && *b == 0)
-                        return true;
-
-                if (*a == 0 || *b == 0)
-                        return false;
-
-                j = strcspn(a, "/");
-                k = strcspn(b, "/");
-
-                if (j != k)
-                        return false;
-
-                if (memcmp(a, b, j) != 0)
-                        return false;
-
-                a += j;
-                b += k;
-        }
-}
-
-char *ascii_strlower(char *t) {
-        char *p;
-
-        assert(t);
-
-        for (p = t; *p; p++)
-                if (*p >= 'A' && *p <= 'Z')
-                        *p = *p - 'A' + 'a';
-
-        return t;
-}
-
-bool ignore_file(const char *filename) {
-        assert(filename);
-
-        return
-                filename[0] == '.' ||
-                streq(filename, "lost+found") ||
-                streq(filename, "aquota.user") ||
-                streq(filename, "aquota.group") ||
-                endswith(filename, "~") ||
-                endswith(filename, ".rpmnew") ||
-                endswith(filename, ".rpmsave") ||
-                endswith(filename, ".rpmorig") ||
-                endswith(filename, ".dpkg-old") ||
-                endswith(filename, ".dpkg-new") ||
-                endswith(filename, ".swp");
-}
-
-int fd_nonblock(int fd, bool nonblock) {
-        int flags;
-
-        assert(fd >= 0);
-
-        if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
-                return -errno;
-
-        if (nonblock)
-                flags |= O_NONBLOCK;
-        else
-                flags &= ~O_NONBLOCK;
-
-        if (fcntl(fd, F_SETFL, flags) < 0)
-                return -errno;
-
-        return 0;
-}
-
-int fd_cloexec(int fd, bool cloexec) {
-        int flags;
-
-        assert(fd >= 0);
-
-        if ((flags = fcntl(fd, F_GETFD, 0)) < 0)
-                return -errno;
-
-        if (cloexec)
-                flags |= FD_CLOEXEC;
-        else
-                flags &= ~FD_CLOEXEC;
-
-        if (fcntl(fd, F_SETFD, flags) < 0)
-                return -errno;
-
-        return 0;
-}
-
-static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
-        unsigned i;
-
-        assert(n_fdset == 0 || fdset);
-
-        for (i = 0; i < n_fdset; i++)
-                if (fdset[i] == fd)
-                        return true;
-
-        return false;
-}
-
-int close_all_fds(const int except[], unsigned n_except) {
-        DIR *d;
-        struct dirent *de;
-        int r = 0;
-
-        assert(n_except == 0 || except);
-
-        d = opendir("/proc/self/fd");
-        if (!d) {
-                int fd;
-                struct rlimit rl;
-
-                /* When /proc isn't available (for example in chroots)
-                 * the fallback is brute forcing through the fd
-                 * table */
-
-                assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
-                for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
-
-                        if (fd_in_set(fd, except, n_except))
-                                continue;
-
-                        if (close_nointr(fd) < 0)
-                                if (errno != EBADF && r == 0)
-                                        r = -errno;
-                }
-
-                return r;
-        }
-
-        while ((de = readdir(d))) {
-                int fd = -1;
-
-                if (ignore_file(de->d_name))
-                        continue;
-
-                if (safe_atoi(de->d_name, &fd) < 0)
-                        /* Let's better ignore this, just in case */
-                        continue;
-
-                if (fd < 3)
-                        continue;
-
-                if (fd == dirfd(d))
-                        continue;
-
-                if (fd_in_set(fd, except, n_except))
-                        continue;
-
-                if (close_nointr(fd) < 0) {
-                        /* Valgrind has its own FD and doesn't want to have it closed */
-                        if (errno != EBADF && r == 0)
-                                r = -errno;
-                }
-        }
-
-        closedir(d);
-        return r;
-}
-
-bool chars_intersect(const char *a, const char *b) {
-        const char *p;
-
-        /* Returns true if any of the chars in a are in b. */
-        for (p = a; *p; p++)
-                if (strchr(b, *p))
-                        return true;
-
-        return false;
-}
-
-char *format_timestamp(char *buf, size_t l, usec_t t) {
-        struct tm tm;
-        time_t sec;
-
-        assert(buf);
-        assert(l > 0);
-
-        if (t <= 0)
-                return NULL;
-
-        sec = (time_t) (t / USEC_PER_SEC);
-
-        if (strftime(buf, l, "%a, %d %b %Y %H:%M:%S %z", localtime_r(&sec, &tm)) <= 0)
-                return NULL;
-
-        return buf;
-}
-
-char *format_timestamp_pretty(char *buf, size_t l, usec_t t) {
-        usec_t n, d;
-
-        n = now(CLOCK_REALTIME);
-
-        if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
-                return NULL;
-
-        d = n - t;
-
-        if (d >= USEC_PER_YEAR)
-                snprintf(buf, l, "%llu years and %llu months ago",
-                         (unsigned long long) (d / USEC_PER_YEAR),
-                         (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
-        else if (d >= USEC_PER_MONTH)
-                snprintf(buf, l, "%llu months and %llu days ago",
-                         (unsigned long long) (d / USEC_PER_MONTH),
-                         (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
-        else if (d >= USEC_PER_WEEK)
-                snprintf(buf, l, "%llu weeks and %llu days ago",
-                         (unsigned long long) (d / USEC_PER_WEEK),
-                         (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
-        else if (d >= 2*USEC_PER_DAY)
-                snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
-        else if (d >= 25*USEC_PER_HOUR)
-                snprintf(buf, l, "1 day and %lluh ago",
-                         (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
-        else if (d >= 6*USEC_PER_HOUR)
-                snprintf(buf, l, "%lluh ago",
-                         (unsigned long long) (d / USEC_PER_HOUR));
-        else if (d >= USEC_PER_HOUR)
-                snprintf(buf, l, "%lluh %llumin ago",
-                         (unsigned long long) (d / USEC_PER_HOUR),
-                         (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
-        else if (d >= 5*USEC_PER_MINUTE)
-                snprintf(buf, l, "%llumin ago",
-                         (unsigned long long) (d / USEC_PER_MINUTE));
-        else if (d >= USEC_PER_MINUTE)
-                snprintf(buf, l, "%llumin %llus ago",
-                         (unsigned long long) (d / USEC_PER_MINUTE),
-                         (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
-        else if (d >= USEC_PER_SEC)
-                snprintf(buf, l, "%llus ago",
-                         (unsigned long long) (d / USEC_PER_SEC));
-        else if (d >= USEC_PER_MSEC)
-                snprintf(buf, l, "%llums ago",
-                         (unsigned long long) (d / USEC_PER_MSEC));
-        else if (d > 0)
-                snprintf(buf, l, "%lluus ago",
-                         (unsigned long long) d);
-        else
-                snprintf(buf, l, "now");
-
-        buf[l-1] = 0;
-        return buf;
-}
-
-char *format_timespan(char *buf, size_t l, usec_t t) {
-        static const struct {
-                const char *suffix;
-                usec_t usec;
-        } table[] = {
-                { "w", USEC_PER_WEEK },
-                { "d", USEC_PER_DAY },
-                { "h", USEC_PER_HOUR },
-                { "min", USEC_PER_MINUTE },
-                { "s", USEC_PER_SEC },
-                { "ms", USEC_PER_MSEC },
-                { "us", 1 },
-        };
-
-        unsigned i;
-        char *p = buf;
-
-        assert(buf);
-        assert(l > 0);
-
-        if (t == (usec_t) -1)
-                return NULL;
-
-        if (t == 0) {
-                snprintf(p, l, "0");
-                p[l-1] = 0;
-                return p;
-        }
-
-        /* The result of this function can be parsed with parse_usec */
-
-        for (i = 0; i < ELEMENTSOF(table); i++) {
-                int k;
-                size_t n;
-
-                if (t < table[i].usec)
-                        continue;
-
-                if (l <= 1)
-                        break;
-
-                k = snprintf(p, l, "%s%llu%s", p > buf ? " " : "", (unsigned long long) (t / table[i].usec), table[i].suffix);
-                n = MIN((size_t) k, l);
-
-                l -= n;
-                p += n;
-
-                t %= table[i].usec;
-        }
-
-        *p = 0;
-
-        return buf;
-}
-
-bool fstype_is_network(const char *fstype) {
-        static const char * const table[] = {
-                "cifs",
-                "smbfs",
-                "ncpfs",
-                "nfs",
-                "nfs4",
-                "gfs",
-                "gfs2"
-        };
-
-        unsigned i;
-
-        for (i = 0; i < ELEMENTSOF(table); i++)
-                if (streq(table[i], fstype))
-                        return true;
-
-        return false;
-}
-
-int chvt(int vt) {
-        int fd, r = 0;
-
-        if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
-                return -errno;
-
-        if (vt < 0) {
-                int tiocl[2] = {
-                        TIOCL_GETKMSGREDIRECT,
-                        0
-                };
-
-                if (ioctl(fd, TIOCLINUX, tiocl) < 0) {
-                        r = -errno;
-                        goto fail;
-                }
-
-                vt = tiocl[0] <= 0 ? 1 : tiocl[0];
-        }
-
-        if (ioctl(fd, VT_ACTIVATE, vt) < 0)
-                r = -errno;
-
-fail:
-        close_nointr_nofail(fd);
-        return r;
-}
-
-int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
-        struct termios old_termios, new_termios;
-        char c;
-        char line[LINE_MAX];
-
-        assert(f);
-        assert(ret);
-
-        if (tcgetattr(fileno(f), &old_termios) >= 0) {
-                new_termios = old_termios;
-
-                new_termios.c_lflag &= ~ICANON;
-                new_termios.c_cc[VMIN] = 1;
-                new_termios.c_cc[VTIME] = 0;
-
-                if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
-                        size_t k;
-
-                        if (t != (usec_t) -1) {
-                                if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
-                                        tcsetattr(fileno(f), TCSADRAIN, &old_termios);
-                                        return -ETIMEDOUT;
-                                }
-                        }
-
-                        k = fread(&c, 1, 1, f);
-
-                        tcsetattr(fileno(f), TCSADRAIN, &old_termios);
-
-                        if (k <= 0)
-                                return -EIO;
-
-                        if (need_nl)
-                                *need_nl = c != '\n';
-
-                        *ret = c;
-                        return 0;
-                }
-        }
-
-        if (t != (usec_t) -1)
-                if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
-                        return -ETIMEDOUT;
-
-        if (!fgets(line, sizeof(line), f))
-                return -EIO;
-
-        truncate_nl(line);
-
-        if (strlen(line) != 1)
-                return -EBADMSG;
-
-        if (need_nl)
-                *need_nl = false;
-
-        *ret = line[0];
-        return 0;
-}
-
-int ask(char *ret, const char *replies, const char *text, ...) {
-        bool on_tty;
-
-        assert(ret);
-        assert(replies);
-        assert(text);
-
-        on_tty = isatty(STDOUT_FILENO);
-
-        for (;;) {
-                va_list ap;
-                char c;
-                int r;
-                bool need_nl = true;
-
-                if (on_tty)
-                        fputs(ANSI_HIGHLIGHT_ON, stdout);
-
-                va_start(ap, text);
-                vprintf(text, ap);
-                va_end(ap);
-
-                if (on_tty)
-                        fputs(ANSI_HIGHLIGHT_OFF, stdout);
-
-                fflush(stdout);
-
-                r = read_one_char(stdin, &c, (usec_t) -1, &need_nl);
-                if (r < 0) {
-
-                        if (r == -EBADMSG) {
-                                puts("Bad input, please try again.");
-                                continue;
-                        }
-
-                        putchar('\n');
-                        return r;
-                }
-
-                if (need_nl)
-                        putchar('\n');
-
-                if (strchr(replies, c)) {
-                        *ret = c;
-                        return 0;
-                }
-
-                puts("Read unexpected character, please try again.");
-        }
-}
-
-int reset_terminal_fd(int fd, bool switch_to_text) {
-        struct termios termios;
-        int r = 0;
-
-        /* Set terminal to some sane defaults */
-
-        assert(fd >= 0);
-
-        /* We leave locked terminal attributes untouched, so that
-         * Plymouth may set whatever it wants to set, and we don't
-         * interfere with that. */
-
-        /* Disable exclusive mode, just in case */
-        ioctl(fd, TIOCNXCL);
-
-        /* Switch to text mode */
-        if (switch_to_text)
-                ioctl(fd, KDSETMODE, KD_TEXT);
-
-        /* Enable console unicode mode */
-        ioctl(fd, KDSKBMODE, K_UNICODE);
-
-        if (tcgetattr(fd, &termios) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        /* We only reset the stuff that matters to the software. How
-         * hardware is set up we don't touch assuming that somebody
-         * else will do that for us */
-
-        termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
-        termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
-        termios.c_oflag |= ONLCR;
-        termios.c_cflag |= CREAD;
-        termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
-
-        termios.c_cc[VINTR]    =   03;  /* ^C */
-        termios.c_cc[VQUIT]    =  034;  /* ^\ */
-        termios.c_cc[VERASE]   = 0177;
-        termios.c_cc[VKILL]    =  025;  /* ^X */
-        termios.c_cc[VEOF]     =   04;  /* ^D */
-        termios.c_cc[VSTART]   =  021;  /* ^Q */
-        termios.c_cc[VSTOP]    =  023;  /* ^S */
-        termios.c_cc[VSUSP]    =  032;  /* ^Z */
-        termios.c_cc[VLNEXT]   =  026;  /* ^V */
-        termios.c_cc[VWERASE]  =  027;  /* ^W */
-        termios.c_cc[VREPRINT] =  022;  /* ^R */
-        termios.c_cc[VEOL]     =    0;
-        termios.c_cc[VEOL2]    =    0;
-
-        termios.c_cc[VTIME]  = 0;
-        termios.c_cc[VMIN]   = 1;
-
-        if (tcsetattr(fd, TCSANOW, &termios) < 0)
-                r = -errno;
-
-finish:
-        /* Just in case, flush all crap out */
-        tcflush(fd, TCIOFLUSH);
-
-        return r;
-}
-
-int reset_terminal(const char *name) {
-        int fd, r;
-
-        fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
-        if (fd < 0)
-                return fd;
-
-        r = reset_terminal_fd(fd, true);
-        close_nointr_nofail(fd);
-
-        return r;
-}
-
-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;
-}
-
-int flush_fd(int fd) {
-        struct pollfd pollfd;
-
-        zero(pollfd);
-        pollfd.fd = fd;
-        pollfd.events = POLLIN;
-
-        for (;;) {
-                char buf[LINE_MAX];
-                ssize_t l;
-                int r;
-
-                if ((r = poll(&pollfd, 1, 0)) < 0) {
-
-                        if (errno == EINTR)
-                                continue;
-
-                        return -errno;
-                }
-
-                if (r == 0)
-                        return 0;
-
-                if ((l = read(fd, buf, sizeof(buf))) < 0) {
-
-                        if (errno == EINTR)
-                                continue;
-
-                        if (errno == EAGAIN)
-                                return 0;
-
-                        return -errno;
-                }
-
-                if (l <= 0)
-                        return 0;
-        }
-}
-
-int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm) {
-        int fd = -1, notify = -1, r, wd = -1;
-
-        assert(name);
-
-        /* We use inotify to be notified when the tty is closed. We
-         * create the watch before checking if we can actually acquire
-         * it, so that we don't lose any event.
-         *
-         * Note: strictly speaking this actually watches for the
-         * device being closed, it does *not* really watch whether a
-         * tty loses its controlling process. However, unless some
-         * rogue process uses TIOCNOTTY on /dev/tty *after* closing
-         * its tty otherwise this will not become a problem. As long
-         * as the administrator makes sure not configure any service
-         * on the same tty as an untrusted user this should not be a
-         * problem. (Which he probably should not do anyway.) */
-
-        if (!fail && !force) {
-                if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
-                        r = -errno;
-                        goto fail;
-                }
-
-                if ((wd = inotify_add_watch(notify, name, IN_CLOSE)) < 0) {
-                        r = -errno;
-                        goto fail;
-                }
-        }
-
-        for (;;) {
-                if (notify >= 0)
-                        if ((r = flush_fd(notify)) < 0)
-                                goto fail;
-
-                /* We pass here O_NOCTTY only so that we can check the return
-                 * value TIOCSCTTY and have a reliable way to figure out if we
-                 * successfully became the controlling process of the tty */
-                if ((fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
-                        return fd;
-
-                /* First, try to get the tty */
-                r = ioctl(fd, TIOCSCTTY, force);
-
-                /* Sometimes it makes sense to ignore TIOCSCTTY
-                 * returning EPERM, i.e. when very likely we already
-                 * are have this controlling terminal. */
-                if (r < 0 && errno == EPERM && ignore_tiocstty_eperm)
-                        r = 0;
-
-                if (r < 0 && (force || fail || errno != EPERM)) {
-                        r = -errno;
-                        goto fail;
-                }
-
-                if (r >= 0)
-                        break;
-
-                assert(!fail);
-                assert(!force);
-                assert(notify >= 0);
-
-                for (;;) {
-                        uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
-                        ssize_t l;
-                        struct inotify_event *e;
-
-                        if ((l = read(notify, inotify_buffer, sizeof(inotify_buffer))) < 0) {
-
-                                if (errno == EINTR)
-                                        continue;
-
-                                r = -errno;
-                                goto fail;
-                        }
-
-                        e = (struct inotify_event*) inotify_buffer;
-
-                        while (l > 0) {
-                                size_t step;
-
-                                if (e->wd != wd || !(e->mask & IN_CLOSE)) {
-                                        r = -EIO;
-                                        goto fail;
-                                }
-
-                                step = sizeof(struct inotify_event) + e->len;
-                                assert(step <= (size_t) l);
-
-                                e = (struct inotify_event*) ((uint8_t*) e + step);
-                                l -= step;
-                        }
-
-                        break;
-                }
-
-                /* We close the tty fd here since if the old session
-                 * ended our handle will be dead. It's important that
-                 * we do this after sleeping, so that we don't enter
-                 * an endless loop. */
-                close_nointr_nofail(fd);
-        }
-
-        if (notify >= 0)
-                close_nointr_nofail(notify);
-
-        r = reset_terminal_fd(fd, true);
-        if (r < 0)
-                log_warning("Failed to reset terminal: %s", strerror(-r));
-
-        return fd;
-
-fail:
-        if (fd >= 0)
-                close_nointr_nofail(fd);
-
-        if (notify >= 0)
-                close_nointr_nofail(notify);
-
-        return r;
-}
-
-int release_terminal(void) {
-        int r = 0, fd;
-        struct sigaction sa_old, sa_new;
-
-        if ((fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC)) < 0)
-                return -errno;
-
-        /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
-         * by our own TIOCNOTTY */
-
-        zero(sa_new);
-        sa_new.sa_handler = SIG_IGN;
-        sa_new.sa_flags = SA_RESTART;
-        assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
-
-        if (ioctl(fd, TIOCNOTTY) < 0)
-                r = -errno;
-
-        assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
-
-        close_nointr_nofail(fd);
-        return r;
-}
-
-int sigaction_many(const struct sigaction *sa, ...) {
-        va_list ap;
-        int r = 0, sig;
-
-        va_start(ap, sa);
-        while ((sig = va_arg(ap, int)) > 0)
-                if (sigaction(sig, sa, NULL) < 0)
-                        r = -errno;
-        va_end(ap);
-
-        return r;
-}
-
-int ignore_signals(int sig, ...) {
-        struct sigaction sa;
-        va_list ap;
-        int r = 0;
-
-        zero(sa);
-        sa.sa_handler = SIG_IGN;
-        sa.sa_flags = SA_RESTART;
-
-        if (sigaction(sig, &sa, NULL) < 0)
-                r = -errno;
-
-        va_start(ap, sig);
-        while ((sig = va_arg(ap, int)) > 0)
-                if (sigaction(sig, &sa, NULL) < 0)
-                        r = -errno;
-        va_end(ap);
-
-        return r;
-}
-
-int default_signals(int sig, ...) {
-        struct sigaction sa;
-        va_list ap;
-        int r = 0;
-
-        zero(sa);
-        sa.sa_handler = SIG_DFL;
-        sa.sa_flags = SA_RESTART;
-
-        if (sigaction(sig, &sa, NULL) < 0)
-                r = -errno;
-
-        va_start(ap, sig);
-        while ((sig = va_arg(ap, int)) > 0)
-                if (sigaction(sig, &sa, NULL) < 0)
-                        r = -errno;
-        va_end(ap);
-
-        return r;
-}
-
-int close_pipe(int p[]) {
-        int a = 0, b = 0;
-
-        assert(p);
-
-        if (p[0] >= 0) {
-                a = close_nointr(p[0]);
-                p[0] = -1;
-        }
-
-        if (p[1] >= 0) {
-                b = close_nointr(p[1]);
-                p[1] = -1;
-        }
-
-        return a < 0 ? a : b;
-}
-
-ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) {
-        uint8_t *p;
-        ssize_t n = 0;
-
-        assert(fd >= 0);
-        assert(buf);
-
-        p = buf;
-
-        while (nbytes > 0) {
-                ssize_t k;
-
-                if ((k = read(fd, p, nbytes)) <= 0) {
-
-                        if (k < 0 && errno == EINTR)
-                                continue;
-
-                        if (k < 0 && errno == EAGAIN && do_poll) {
-                                struct pollfd pollfd;
-
-                                zero(pollfd);
-                                pollfd.fd = fd;
-                                pollfd.events = POLLIN;
-
-                                if (poll(&pollfd, 1, -1) < 0) {
-                                        if (errno == EINTR)
-                                                continue;
-
-                                        return n > 0 ? n : -errno;
-                                }
-
-                                if (pollfd.revents != POLLIN)
-                                        return n > 0 ? n : -EIO;
-
-                                continue;
-                        }
-
-                        return n > 0 ? n : (k < 0 ? -errno : 0);
-                }
-
-                p += k;
-                nbytes -= k;
-                n += k;
-        }
-
-        return n;
-}
-
-ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
-        const uint8_t *p;
-        ssize_t n = 0;
-
-        assert(fd >= 0);
-        assert(buf);
-
-        p = buf;
-
-        while (nbytes > 0) {
-                ssize_t k;
-
-                k = write(fd, p, nbytes);
-                if (k <= 0) {
-
-                        if (k < 0 && errno == EINTR)
-                                continue;
-
-                        if (k < 0 && errno == EAGAIN && do_poll) {
-                                struct pollfd pollfd;
-
-                                zero(pollfd);
-                                pollfd.fd = fd;
-                                pollfd.events = POLLOUT;
-
-                                if (poll(&pollfd, 1, -1) < 0) {
-                                        if (errno == EINTR)
-                                                continue;
-
-                                        return n > 0 ? n : -errno;
-                                }
-
-                                if (pollfd.revents != POLLOUT)
-                                        return n > 0 ? n : -EIO;
-
-                                continue;
-                        }
-
-                        return n > 0 ? n : (k < 0 ? -errno : 0);
-                }
-
-                p += k;
-                nbytes -= k;
-                n += k;
-        }
-
-        return n;
-}
-
-int path_is_mount_point(const char *t, bool allow_symlink) {
-        struct stat a, b;
-        char *parent;
-        int r;
-
-        if (allow_symlink)
-                r = stat(t, &a);
-        else
-                r = lstat(t, &a);
-
-        if (r < 0) {
-                if (errno == ENOENT)
-                        return 0;
-
-                return -errno;
-        }
-
-        r = parent_of_path(t, &parent);
-        if (r < 0)
-                return r;
-
-        r = lstat(parent, &b);
-        free(parent);
-
-        if (r < 0)
-                return -errno;
-
-        return a.st_dev != b.st_dev;
-}
-
-int parse_usec(const char *t, usec_t *usec) {
-        static const struct {
-                const char *suffix;
-                usec_t usec;
-        } table[] = {
-                { "sec", USEC_PER_SEC },
-                { "s", USEC_PER_SEC },
-                { "min", USEC_PER_MINUTE },
-                { "hr", USEC_PER_HOUR },
-                { "h", USEC_PER_HOUR },
-                { "d", USEC_PER_DAY },
-                { "w", USEC_PER_WEEK },
-                { "msec", USEC_PER_MSEC },
-                { "ms", USEC_PER_MSEC },
-                { "m", USEC_PER_MINUTE },
-                { "usec", 1ULL },
-                { "us", 1ULL },
-                { "", USEC_PER_SEC },
-        };
-
-        const char *p;
-        usec_t r = 0;
-
-        assert(t);
-        assert(usec);
-
-        p = t;
-        do {
-                long long l;
-                char *e;
-                unsigned i;
-
-                errno = 0;
-                l = strtoll(p, &e, 10);
-
-                if (errno != 0)
-                        return -errno;
-
-                if (l < 0)
-                        return -ERANGE;
-
-                if (e == p)
-                        return -EINVAL;
-
-                e += strspn(e, WHITESPACE);
-
-                for (i = 0; i < ELEMENTSOF(table); i++)
-                        if (startswith(e, table[i].suffix)) {
-                                r += (usec_t) l * table[i].usec;
-                                p = e + strlen(table[i].suffix);
-                                break;
-                        }
-
-                if (i >= ELEMENTSOF(table))
-                        return -EINVAL;
-
-        } while (*p != 0);
-
-        *usec = r;
-
-        return 0;
-}
-
-int parse_bytes(const char *t, off_t *bytes) {
-        static const struct {
-                const char *suffix;
-                off_t factor;
-        } table[] = {
-                { "B", 1 },
-                { "K", 1024ULL },
-                { "M", 1024ULL*1024ULL },
-                { "G", 1024ULL*1024ULL*1024ULL },
-                { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
-                { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
-                { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
-                { "", 1 },
-        };
-
-        const char *p;
-        off_t r = 0;
-
-        assert(t);
-        assert(bytes);
-
-        p = t;
-        do {
-                long long l;
-                char *e;
-                unsigned i;
-
-                errno = 0;
-                l = strtoll(p, &e, 10);
-
-                if (errno != 0)
-                        return -errno;
-
-                if (l < 0)
-                        return -ERANGE;
-
-                if (e == p)
-                        return -EINVAL;
-
-                e += strspn(e, WHITESPACE);
-
-                for (i = 0; i < ELEMENTSOF(table); i++)
-                        if (startswith(e, table[i].suffix)) {
-                                r += (off_t) l * table[i].factor;
-                                p = e + strlen(table[i].suffix);
-                                break;
-                        }
-
-                if (i >= ELEMENTSOF(table))
-                        return -EINVAL;
-
-        } while (*p != 0);
-
-        *bytes = r;
-
-        return 0;
-}
-
-int make_stdio(int fd) {
-        int r, s, t;
-
-        assert(fd >= 0);
-
-        r = dup2(fd, STDIN_FILENO);
-        s = dup2(fd, STDOUT_FILENO);
-        t = dup2(fd, STDERR_FILENO);
-
-        if (fd >= 3)
-                close_nointr_nofail(fd);
-
-        if (r < 0 || s < 0 || t < 0)
-                return -errno;
-
-        fd_cloexec(STDIN_FILENO, false);
-        fd_cloexec(STDOUT_FILENO, false);
-        fd_cloexec(STDERR_FILENO, false);
-
-        return 0;
-}
-
-int make_null_stdio(void) {
-        int null_fd;
-
-        if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0)
-                return -errno;
-
-        return make_stdio(null_fd);
-}
-
-bool is_device_path(const char *path) {
-
-        /* Returns true on paths that refer to a device, either in
-         * sysfs or in /dev */
-
-        return
-                path_startswith(path, "/dev/") ||
-                path_startswith(path, "/sys/");
-}
-
-int dir_is_empty(const char *path) {
-        DIR *d;
-        int r;
-        struct dirent buf, *de;
-
-        if (!(d = opendir(path)))
-                return -errno;
-
-        for (;;) {
-                if ((r = readdir_r(d, &buf, &de)) > 0) {
-                        r = -r;
-                        break;
-                }
-
-                if (!de) {
-                        r = 1;
-                        break;
-                }
-
-                if (!ignore_file(de->d_name)) {
-                        r = 0;
-                        break;
-                }
-        }
-
-        closedir(d);
-        return r;
-}
-
-unsigned long long random_ull(void) {
-        int fd;
-        uint64_t ull;
-        ssize_t r;
-
-        if ((fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0)
-                goto fallback;
-
-        r = loop_read(fd, &ull, sizeof(ull), true);
-        close_nointr_nofail(fd);
-
-        if (r != sizeof(ull))
-                goto fallback;
-
-        return ull;
-
-fallback:
-        return random() * RAND_MAX + random();
-}
-
-void rename_process(const char name[8]) {
-        assert(name);
-
-        /* This is a like a poor man's setproctitle(). It changes the
-         * comm field, argv[0], and also the glibc's internally used
-         * name of the process. For the first one a limit of 16 chars
-         * applies, to the second one usually one of 10 (i.e. length
-         * of "/sbin/init"), to the third one one of 7 (i.e. length of
-         * "systemd"). If you pass a longer string it will be
-         * truncated */
-
-        prctl(PR_SET_NAME, name);
-
-        if (program_invocation_name)
-                strncpy(program_invocation_name, name, strlen(program_invocation_name));
-
-        if (saved_argc > 0) {
-                int i;
-
-                if (saved_argv[0])
-                        strncpy(saved_argv[0], name, strlen(saved_argv[0]));
-
-                for (i = 1; i < saved_argc; i++) {
-                        if (!saved_argv[i])
-                                break;
-
-                        memset(saved_argv[i], 0, strlen(saved_argv[i]));
-                }
-        }
-}
-
-void sigset_add_many(sigset_t *ss, ...) {
-        va_list ap;
-        int sig;
-
-        assert(ss);
-
-        va_start(ap, ss);
-        while ((sig = va_arg(ap, int)) > 0)
-                assert_se(sigaddset(ss, sig) == 0);
-        va_end(ap);
-}
-
-char* gethostname_malloc(void) {
-        struct utsname u;
-
-        assert_se(uname(&u) >= 0);
-
-        if (u.nodename[0])
-                return strdup(u.nodename);
-
-        return strdup(u.sysname);
-}
-
-char* getlogname_malloc(void) {
-        uid_t uid;
-        long bufsize;
-        char *buf, *name;
-        struct passwd pwbuf, *pw = NULL;
-        struct stat st;
-
-        if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
-                uid = st.st_uid;
-        else
-                uid = getuid();
-
-        /* Shortcut things to avoid NSS lookups */
-        if (uid == 0)
-                return strdup("root");
-
-        if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) <= 0)
-                bufsize = 4096;
-
-        if (!(buf = malloc(bufsize)))
-                return NULL;
-
-        if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw) {
-                name = strdup(pw->pw_name);
-                free(buf);
-                return name;
-        }
-
-        free(buf);
-
-        if (asprintf(&name, "%lu", (unsigned long) uid) < 0)
-                return NULL;
-
-        return name;
-}
-
-int getttyname_malloc(int fd, char **r) {
-        char path[PATH_MAX], *c;
-        int k;
-
-        assert(r);
-
-        if ((k = ttyname_r(fd, path, sizeof(path))) != 0)
-                return -k;
-
-        char_array_0(path);
-
-        if (!(c = strdup(startswith(path, "/dev/") ? path + 5 : path)))
-                return -ENOMEM;
-
-        *r = c;
-        return 0;
-}
-
-int getttyname_harder(int fd, char **r) {
-        int k;
-        char *s;
-
-        if ((k = getttyname_malloc(fd, &s)) < 0)
-                return k;
-
-        if (streq(s, "tty")) {
-                free(s);
-                return get_ctty(0, NULL, r);
-        }
-
-        *r = s;
-        return 0;
-}
-
-int get_ctty_devnr(pid_t pid, dev_t *d) {
-        int k;
-        char line[LINE_MAX], *p, *fn;
-        unsigned long ttynr;
-        FILE *f;
-
-        if (asprintf(&fn, "/proc/%lu/stat", (unsigned long) (pid <= 0 ? getpid() : pid)) < 0)
-                return -ENOMEM;
-
-        f = fopen(fn, "re");
-        free(fn);
-        if (!f)
-                return -errno;
-
-        if (!fgets(line, sizeof(line), f)) {
-                k = feof(f) ? -EIO : -errno;
-                fclose(f);
-                return k;
-        }
-
-        fclose(f);
-
-        p = strrchr(line, ')');
-        if (!p)
-                return -EIO;
-
-        p++;
-
-        if (sscanf(p, " "
-                   "%*c "  /* state */
-                   "%*d "  /* ppid */
-                   "%*d "  /* pgrp */
-                   "%*d "  /* session */
-                   "%lu ", /* ttynr */
-                   &ttynr) != 1)
-                return -EIO;
-
-        *d = (dev_t) ttynr;
-        return 0;
-}
-
-int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
-        int k;
-        char fn[PATH_MAX], *s, *b, *p;
-        dev_t devnr;
-
-        assert(r);
-
-        k = get_ctty_devnr(pid, &devnr);
-        if (k < 0)
-                return k;
-
-        snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr));
-        char_array_0(fn);
-
-        if ((k = readlink_malloc(fn, &s)) < 0) {
-
-                if (k != -ENOENT)
-                        return k;
-
-                /* This is an ugly hack */
-                if (major(devnr) == 136) {
-                        if (asprintf(&b, "pts/%lu", (unsigned long) minor(devnr)) < 0)
-                                return -ENOMEM;
-
-                        *r = b;
-                        if (_devnr)
-                                *_devnr = devnr;
-
-                        return 0;
-                }
-
-                /* Probably something like the ptys which have no
-                 * symlink in /dev/char. Let's return something
-                 * vaguely useful. */
-
-                if (!(b = strdup(fn + 5)))
-                        return -ENOMEM;
-
-                *r = b;
-                if (_devnr)
-                        *_devnr = devnr;
-
-                return 0;
-        }
-
-        if (startswith(s, "/dev/"))
-                p = s + 5;
-        else if (startswith(s, "../"))
-                p = s + 3;
-        else
-                p = s;
-
-        b = strdup(p);
-        free(s);
-
-        if (!b)
-                return -ENOMEM;
-
-        *r = b;
-        if (_devnr)
-                *_devnr = devnr;
-
-        return 0;
-}
-
-static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
-        DIR *d;
-        int ret = 0;
-
-        assert(fd >= 0);
-
-        /* This returns the first error we run into, but nevertheless
-         * tries to go on */
-
-        if (!(d = fdopendir(fd))) {
-                close_nointr_nofail(fd);
-
-                return errno == ENOENT ? 0 : -errno;
-        }
-
-        for (;;) {
-                struct dirent buf, *de;
-                bool is_dir, keep_around = false;
-                int r;
-
-                if ((r = readdir_r(d, &buf, &de)) != 0) {
-                        if (ret == 0)
-                                ret = -r;
-                        break;
-                }
-
-                if (!de)
-                        break;
-
-                if (streq(de->d_name, ".") || streq(de->d_name, ".."))
-                        continue;
-
-                if (de->d_type == DT_UNKNOWN) {
-                        struct stat st;
-
-                        if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
-                                if (ret == 0 && errno != ENOENT)
-                                        ret = -errno;
-                                continue;
-                        }
-
-                        if (honour_sticky)
-                                keep_around =
-                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
-                                        (st.st_mode & S_ISVTX);
-
-                        is_dir = S_ISDIR(st.st_mode);
-
-                } else {
-                        if (honour_sticky) {
-                                struct stat st;
-
-                                if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
-                                        if (ret == 0 && errno != ENOENT)
-                                                ret = -errno;
-                                        continue;
-                                }
-
-                                keep_around =
-                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
-                                        (st.st_mode & S_ISVTX);
-                        }
-
-                        is_dir = de->d_type == DT_DIR;
-                }
-
-                if (is_dir) {
-                        int subdir_fd;
-
-                        subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
-                        if (subdir_fd < 0) {
-                                if (ret == 0 && errno != ENOENT)
-                                        ret = -errno;
-                                continue;
-                        }
-
-                        if ((r = rm_rf_children(subdir_fd, only_dirs, honour_sticky)) < 0) {
-                                if (ret == 0)
-                                        ret = r;
-                        }
-
-                        if (!keep_around)
-                                if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
-                                        if (ret == 0 && errno != ENOENT)
-                                                ret = -errno;
-                                }
-
-                } else if (!only_dirs && !keep_around) {
-
-                        if (unlinkat(fd, de->d_name, 0) < 0) {
-                                if (ret == 0 && errno != ENOENT)
-                                        ret = -errno;
-                        }
-                }
-        }
-
-        closedir(d);
-
-        return ret;
-}
-
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
-        int fd;
-        int r;
-
-        assert(path);
-
-        if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
-
-                if (errno != ENOTDIR)
-                        return -errno;
-
-                if (delete_root && !only_dirs)
-                        if (unlink(path) < 0)
-                                return -errno;
-
-                return 0;
-        }
-
-        r = rm_rf_children(fd, only_dirs, honour_sticky);
-
-        if (delete_root) {
-
-                if (honour_sticky && file_is_priv_sticky(path) > 0)
-                        return r;
-
-                if (rmdir(path) < 0 && errno != ENOENT) {
-                        if (r == 0)
-                                r = -errno;
-                }
-        }
-
-        return r;
-}
-
-int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
-        assert(path);
-
-        /* Under the assumption that we are running privileged we
-         * first change the access mode and only then hand out
-         * ownership to avoid a window where access is too open. */
-
-        if (mode != (mode_t) -1)
-                if (chmod(path, mode) < 0)
-                        return -errno;
-
-        if (uid != (uid_t) -1 || gid != (gid_t) -1)
-                if (chown(path, uid, gid) < 0)
-                        return -errno;
-
-        return 0;
-}
-
-int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) {
-        assert(fd >= 0);
-
-        /* Under the assumption that we are running privileged we
-         * first change the access mode and only then hand out
-         * ownership to avoid a window where access is too open. */
-
-        if (fchmod(fd, mode) < 0)
-                return -errno;
-
-        if (fchown(fd, uid, gid) < 0)
-                return -errno;
-
-        return 0;
-}
-
-cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
-        cpu_set_t *r;
-        unsigned n = 1024;
-
-        /* Allocates the cpuset in the right size */
-
-        for (;;) {
-                if (!(r = CPU_ALLOC(n)))
-                        return NULL;
-
-                if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) {
-                        CPU_ZERO_S(CPU_ALLOC_SIZE(n), r);
-
-                        if (ncpus)
-                                *ncpus = n;
-
-                        return r;
-                }
-
-                CPU_FREE(r);
-
-                if (errno != EINVAL)
-                        return NULL;
-
-                n *= 2;
-        }
-}
-
-void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) {
-        char *s = NULL, *spaces = NULL, *e;
-        int fd = -1, c;
-        size_t emax, sl, left;
-        struct iovec iovec[5];
-        int n = 0;
-
-        assert(format);
-
-        /* This independent of logging, as status messages are
-         * optional and go exclusively to the console. */
-
-        if (vasprintf(&s, format, ap) < 0)
-                goto finish;
-
-        fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
-        if (fd < 0)
-                goto finish;
-
-        if (ellipse) {
-                c = fd_columns(fd);
-                if (c <= 0)
-                        c = 80;
-
-                if (status) {
-                        sl = 2 + 6 + 1; /* " [" status "]" */
-                        emax = (size_t) c > sl ? c - sl - 1 : 0;
-                } else
-                        emax = c - 1;
-
-                e = ellipsize(s, emax, 75);
-                if (e) {
-                        free(s);
-                        s = e;
-                }
-        }
-
-        zero(iovec);
-        IOVEC_SET_STRING(iovec[n++], s);
-
-        if (ellipse) {
-                sl = strlen(s);
-                left = emax > sl ? emax - sl : 0;
-                if (left > 0) {
-                        spaces = malloc(left);
-                        if (spaces) {
-                                memset(spaces, ' ', left);
-                                iovec[n].iov_base = spaces;
-                                iovec[n].iov_len = left;
-                                n++;
-                        }
-                }
-        }
-
-        if (status) {
-                IOVEC_SET_STRING(iovec[n++], " [");
-                IOVEC_SET_STRING(iovec[n++], status);
-                IOVEC_SET_STRING(iovec[n++], "]\n");
-        } else
-                IOVEC_SET_STRING(iovec[n++], "\n");
-
-        writev(fd, iovec, n);
-
-finish:
-        free(s);
-        free(spaces);
-
-        if (fd >= 0)
-                close_nointr_nofail(fd);
-}
-
-void status_printf(const char *status, bool ellipse, const char *format, ...) {
-        va_list ap;
-
-        assert(format);
-
-        va_start(ap, format);
-        status_vprintf(status, ellipse, format, ap);
-        va_end(ap);
-}
-
-void status_welcome(void) {
-        char *pretty_name = NULL, *ansi_color = NULL;
-        const char *const_pretty = NULL, *const_color = NULL;
-        int r;
-
-        if ((r = parse_env_file("/etc/os-release", NEWLINE,
-                                "PRETTY_NAME", &pretty_name,
-                                "ANSI_COLOR", &ansi_color,
-                                NULL)) < 0) {
-
-                if (r != -ENOENT)
-                        log_warning("Failed to read /etc/os-release: %s", strerror(-r));
-        }
-
-        if (!pretty_name && !const_pretty)
-                const_pretty = "Linux";
-
-        if (!ansi_color && !const_color)
-                const_color = "1";
-
-        status_printf(NULL,
-                      false,
-                      "\nWelcome to \x1B[%sm%s\x1B[0m!\n",
-                      const_color ? const_color : ansi_color,
-                      const_pretty ? const_pretty : pretty_name);
-
-        free(ansi_color);
-        free(pretty_name);
-}
-
-char *replace_env(const char *format, char **env) {
-        enum {
-                WORD,
-                CURLY,
-                VARIABLE
-        } state = WORD;
-
-        const char *e, *word = format;
-        char *r = NULL, *k;
-
-        assert(format);
-
-        for (e = format; *e; e ++) {
-
-                switch (state) {
-
-                case WORD:
-                        if (*e == '$')
-                                state = CURLY;
-                        break;
-
-                case CURLY:
-                        if (*e == '{') {
-                                if (!(k = strnappend(r, word, e-word-1)))
-                                        goto fail;
-
-                                free(r);
-                                r = k;
-
-                                word = e-1;
-                                state = VARIABLE;
-
-                        } else if (*e == '$') {
-                                if (!(k = strnappend(r, word, e-word)))
-                                        goto fail;
-
-                                free(r);
-                                r = k;
-
-                                word = e+1;
-                                state = WORD;
-                        } else
-                                state = WORD;
-                        break;
-
-                case VARIABLE:
-                        if (*e == '}') {
-                                const char *t;
-
-                                if (!(t = strv_env_get_with_length(env, word+2, e-word-2)))
-                                        t = "";
-
-                                if (!(k = strappend(r, t)))
-                                        goto fail;
-
-                                free(r);
-                                r = k;
-
-                                word = e+1;
-                                state = WORD;
-                        }
-                        break;
-                }
-        }
-
-        if (!(k = strnappend(r, word, e-word)))
-                goto fail;
-
-        free(r);
-        return k;
-
-fail:
-        free(r);
-        return NULL;
-}
-
-char **replace_env_argv(char **argv, char **env) {
-        char **r, **i;
-        unsigned k = 0, l = 0;
-
-        l = strv_length(argv);
-
-        if (!(r = new(char*, l+1)))
-                return NULL;
-
-        STRV_FOREACH(i, argv) {
-
-                /* If $FOO appears as single word, replace it by the split up variable */
-                if ((*i)[0] == '$' && (*i)[1] != '{') {
-                        char *e;
-                        char **w, **m;
-                        unsigned q;
-
-                        if ((e = strv_env_get(env, *i+1))) {
-
-                                if (!(m = strv_split_quoted(e))) {
-                                        r[k] = NULL;
-                                        strv_free(r);
-                                        return NULL;
-                                }
-                        } else
-                                m = NULL;
-
-                        q = strv_length(m);
-                        l = l + q - 1;
-
-                        if (!(w = realloc(r, sizeof(char*) * (l+1)))) {
-                                r[k] = NULL;
-                                strv_free(r);
-                                strv_free(m);
-                                return NULL;
-                        }
-
-                        r = w;
-                        if (m) {
-                                memcpy(r + k, m, q * sizeof(char*));
-                                free(m);
-                        }
-
-                        k += q;
-                        continue;
-                }
-
-                /* If ${FOO} appears as part of a word, replace it by the variable as-is */
-                if (!(r[k++] = replace_env(*i, env))) {
-                        strv_free(r);
-                        return NULL;
-                }
-        }
-
-        r[k] = NULL;
-        return r;
-}
-
-int fd_columns(int fd) {
-        struct winsize ws;
-        zero(ws);
-
-        if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
-                return -errno;
-
-        if (ws.ws_col <= 0)
-                return -EIO;
-
-        return ws.ws_col;
-}
-
-unsigned columns(void) {
-        static __thread int parsed_columns = 0;
-        const char *e;
-
-        if (_likely_(parsed_columns > 0))
-                return parsed_columns;
-
-        e = getenv("COLUMNS");
-        if (e)
-                parsed_columns = atoi(e);
-
-        if (parsed_columns <= 0)
-                parsed_columns = fd_columns(STDOUT_FILENO);
-
-        if (parsed_columns <= 0)
-                parsed_columns = 80;
-
-        return parsed_columns;
-}
-
-int fd_lines(int fd) {
-        struct winsize ws;
-        zero(ws);
-
-        if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
-                return -errno;
-
-        if (ws.ws_row <= 0)
-                return -EIO;
-
-        return ws.ws_row;
-}
-
-unsigned lines(void) {
-        static __thread int parsed_lines = 0;
-        const char *e;
-
-        if (_likely_(parsed_lines > 0))
-                return parsed_lines;
-
-        e = getenv("LINES");
-        if (e)
-                parsed_lines = atoi(e);
-
-        if (parsed_lines <= 0)
-                parsed_lines = fd_lines(STDOUT_FILENO);
-
-        if (parsed_lines <= 0)
-                parsed_lines = 25;
-
-        return parsed_lines;
-}
-
-int running_in_chroot(void) {
-        struct stat a, b;
-
-        zero(a);
-        zero(b);
-
-        /* Only works as root */
-
-        if (stat("/proc/1/root", &a) < 0)
-                return -errno;
-
-        if (stat("/", &b) < 0)
-                return -errno;
-
-        return
-                a.st_dev != b.st_dev ||
-                a.st_ino != b.st_ino;
-}
-
-char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
-        size_t x;
-        char *r;
-
-        assert(s);
-        assert(percent <= 100);
-        assert(new_length >= 3);
-
-        if (old_length <= 3 || old_length <= new_length)
-                return strndup(s, old_length);
-
-        r = new0(char, new_length+1);
-        if (!r)
-                return r;
-
-        x = (new_length * percent) / 100;
-
-        if (x > new_length - 3)
-                x = new_length - 3;
-
-        memcpy(r, s, x);
-        r[x] = '.';
-        r[x+1] = '.';
-        r[x+2] = '.';
-        memcpy(r + x + 3,
-               s + old_length - (new_length - x - 3),
-               new_length - x - 3);
-
-        return r;
-}
-
-char *ellipsize(const char *s, size_t length, unsigned percent) {
-        return ellipsize_mem(s, strlen(s), length, percent);
-}
-
-int touch(const char *path) {
-        int fd;
-
-        assert(path);
-
-        if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644)) < 0)
-                return -errno;
-
-        close_nointr_nofail(fd);
-        return 0;
-}
-
-char *unquote(const char *s, const char* quotes) {
-        size_t l;
-        assert(s);
-
-        l = strlen(s);
-        if (l < 2)
-                return strdup(s);
-
-        if (strchr(quotes, s[0]) && s[l-1] == s[0])
-                return strndup(s+1, l-2);
-
-        return strdup(s);
-}
-
-char *normalize_env_assignment(const char *s) {
-        char *name, *value, *p, *r;
-
-        p = strchr(s, '=');
-
-        if (!p) {
-                if (!(r = strdup(s)))
-                        return NULL;
-
-                return strstrip(r);
-        }
-
-        if (!(name = strndup(s, p - s)))
-                return NULL;
-
-        if (!(p = strdup(p+1))) {
-                free(name);
-                return NULL;
-        }
-
-        value = unquote(strstrip(p), QUOTES);
-        free(p);
-
-        if (!value) {
-                free(name);
-                return NULL;
-        }
-
-        if (asprintf(&r, "%s=%s", name, value) < 0)
-                r = NULL;
-
-        free(value);
-        free(name);
-
-        return r;
-}
-
-int wait_for_terminate(pid_t pid, siginfo_t *status) {
-        siginfo_t dummy;
-
-        assert(pid >= 1);
-
-        if (!status)
-                status = &dummy;
-
-        for (;;) {
-                zero(*status);
-
-                if (waitid(P_PID, pid, status, WEXITED) < 0) {
-
-                        if (errno == EINTR)
-                                continue;
-
-                        return -errno;
-                }
-
-                return 0;
-        }
-}
-
-int wait_for_terminate_and_warn(const char *name, pid_t pid) {
-        int r;
-        siginfo_t status;
-
-        assert(name);
-        assert(pid > 1);
-
-        if ((r = wait_for_terminate(pid, &status)) < 0) {
-                log_warning("Failed to wait for %s: %s", name, strerror(-r));
-                return r;
-        }
-
-        if (status.si_code == CLD_EXITED) {
-                if (status.si_status != 0) {
-                        log_warning("%s failed with error code %i.", name, status.si_status);
-                        return status.si_status;
-                }
-
-                log_debug("%s succeeded.", name);
-                return 0;
-
-        } else if (status.si_code == CLD_KILLED ||
-                   status.si_code == CLD_DUMPED) {
-
-                log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
-                return -EPROTO;
-        }
-
-        log_warning("%s failed due to unknown reason.", name);
-        return -EPROTO;
-
-}
-
-_noreturn_ void freeze(void) {
-
-        /* Make sure nobody waits for us on a socket anymore */
-        close_all_fds(NULL, 0);
-
-        sync();
-
-        for (;;)
-                pause();
-}
-
-bool null_or_empty(struct stat *st) {
-        assert(st);
-
-        if (S_ISREG(st->st_mode) && st->st_size <= 0)
-                return true;
-
-        if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
-                return true;
-
-        return false;
-}
-
-int null_or_empty_path(const char *fn) {
-        struct stat st;
-
-        assert(fn);
-
-        if (stat(fn, &st) < 0)
-                return -errno;
-
-        return null_or_empty(&st);
-}
-
-DIR *xopendirat(int fd, const char *name, int flags) {
-        int nfd;
-        DIR *d;
-
-        if ((nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags)) < 0)
-                return NULL;
-
-        if (!(d = fdopendir(nfd))) {
-                close_nointr_nofail(nfd);
-                return NULL;
-        }
-
-        return d;
-}
-
-int signal_from_string_try_harder(const char *s) {
-        int signo;
-        assert(s);
-
-        if ((signo = signal_from_string(s)) <= 0)
-                if (startswith(s, "SIG"))
-                        return signal_from_string(s+3);
-
-        return signo;
-}
-
-void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
-
-        assert(f);
-        assert(name);
-        assert(t);
-
-        if (!dual_timestamp_is_set(t))
-                return;
-
-        fprintf(f, "%s=%llu %llu\n",
-                name,
-                (unsigned long long) t->realtime,
-                (unsigned long long) t->monotonic);
-}
-
-void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
-        unsigned long long a, b;
-
-        assert(value);
-        assert(t);
-
-        if (sscanf(value, "%lli %llu", &a, &b) != 2)
-                log_debug("Failed to parse finish timestamp value %s", value);
-        else {
-                t->realtime = a;
-                t->monotonic = b;
-        }
-}
-
-char *fstab_node_to_udev_node(const char *p) {
-        char *dn, *t, *u;
-        int r;
-
-        /* FIXME: to follow udev's logic 100% we need to leave valid
-         * UTF8 chars unescaped */
-
-        if (startswith(p, "LABEL=")) {
-
-                if (!(u = unquote(p+6, "\"\'")))
-                        return NULL;
-
-                t = xescape(u, "/ ");
-                free(u);
-
-                if (!t)
-                        return NULL;
-
-                r = asprintf(&dn, "/dev/disk/by-label/%s", t);
-                free(t);
-
-                if (r < 0)
-                        return NULL;
-
-                return dn;
-        }
-
-        if (startswith(p, "UUID=")) {
-
-                if (!(u = unquote(p+5, "\"\'")))
-                        return NULL;
-
-                t = xescape(u, "/ ");
-                free(u);
-
-                if (!t)
-                        return NULL;
-
-                r = asprintf(&dn, "/dev/disk/by-uuid/%s", t);
-                free(t);
-
-                if (r < 0)
-                        return NULL;
-
-                return dn;
-        }
-
-        return strdup(p);
-}
-
-void filter_environ(const char *prefix) {
-        int i, j;
-        assert(prefix);
-
-        if (!environ)
-                return;
-
-        for (i = 0, j = 0; environ[i]; i++) {
-
-                if (startswith(environ[i], prefix))
-                        continue;
-
-                environ[j++] = environ[i];
-        }
-
-        environ[j] = NULL;
-}
-
-bool tty_is_vc(const char *tty) {
-        assert(tty);
-
-        if (startswith(tty, "/dev/"))
-                tty += 5;
-
-        return vtnr_from_tty(tty) >= 0;
-}
-
-int vtnr_from_tty(const char *tty) {
-        int i, r;
-
-        assert(tty);
-
-        if (startswith(tty, "/dev/"))
-                tty += 5;
-
-        if (!startswith(tty, "tty") )
-                return -EINVAL;
-
-        if (tty[3] < '0' || tty[3] > '9')
-                return -EINVAL;
-
-        r = safe_atoi(tty+3, &i);
-        if (r < 0)
-                return r;
-
-        if (i < 0 || i > 63)
-                return -EINVAL;
-
-        return i;
-}
-
-bool tty_is_vc_resolve(const char *tty) {
-        char *active = NULL;
-        bool b;
-
-        assert(tty);
-
-        if (startswith(tty, "/dev/"))
-                tty += 5;
-
-        /* Resolve where /dev/console is pointing to */
-        if (streq(tty, "console"))
-                if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
-                        /* If multiple log outputs are configured the
-                         * last one is what /dev/console points to */
-                        tty = strrchr(active, ' ');
-                        if (tty)
-                                tty++;
-                        else
-                                tty = active;
-                }
-
-        b = tty_is_vc(tty);
-        free(active);
-
-        return b;
-}
-
-const char *default_term_for_tty(const char *tty) {
-        assert(tty);
-
-        return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt100";
-}
-
-bool dirent_is_file(const struct dirent *de) {
-        assert(de);
-
-        if (ignore_file(de->d_name))
-                return false;
-
-        if (de->d_type != DT_REG &&
-            de->d_type != DT_LNK &&
-            de->d_type != DT_UNKNOWN)
-                return false;
-
-        return true;
-}
-
-bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
-        assert(de);
-
-        if (!dirent_is_file(de))
-                return false;
-
-        return endswith(de->d_name, suffix);
-}
-
-void execute_directory(const char *directory, DIR *d, char *argv[]) {
-        DIR *_d = NULL;
-        struct dirent *de;
-        Hashmap *pids = NULL;
-
-        assert(directory);
-
-        /* Executes all binaries in a directory in parallel and waits
-         * until all they all finished. */
-
-        if (!d) {
-                if (!(_d = opendir(directory))) {
-
-                        if (errno == ENOENT)
-                                return;
-
-                        log_error("Failed to enumerate directory %s: %m", directory);
-                        return;
-                }
-
-                d = _d;
-        }
-
-        if (!(pids = hashmap_new(trivial_hash_func, trivial_compare_func))) {
-                log_error("Failed to allocate set.");
-                goto finish;
-        }
-
-        while ((de = readdir(d))) {
-                char *path;
-                pid_t pid;
-                int k;
-
-                if (!dirent_is_file(de))
-                        continue;
-
-                if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) {
-                        log_error("Out of memory");
-                        continue;
-                }
-
-                if ((pid = fork()) < 0) {
-                        log_error("Failed to fork: %m");
-                        free(path);
-                        continue;
-                }
-
-                if (pid == 0) {
-                        char *_argv[2];
-                        /* Child */
-
-                        if (!argv) {
-                                _argv[0] = path;
-                                _argv[1] = NULL;
-                                argv = _argv;
-                        } else
-                                if (!argv[0])
-                                        argv[0] = path;
-
-                        execv(path, argv);
-
-                        log_error("Failed to execute %s: %m", path);
-                        _exit(EXIT_FAILURE);
-                }
-
-                log_debug("Spawned %s as %lu", path, (unsigned long) pid);
-
-                if ((k = hashmap_put(pids, UINT_TO_PTR(pid), path)) < 0) {
-                        log_error("Failed to add PID to set: %s", strerror(-k));
-                        free(path);
-                }
-        }
-
-        while (!hashmap_isempty(pids)) {
-                pid_t pid = PTR_TO_UINT(hashmap_first_key(pids));
-                siginfo_t si;
-                char *path;
-
-                zero(si);
-                if (waitid(P_PID, pid, &si, WEXITED) < 0) {
-
-                        if (errno == EINTR)
-                                continue;
-
-                        log_error("waitid() failed: %m");
-                        goto finish;
-                }
-
-                if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) {
-                        if (!is_clean_exit(si.si_code, si.si_status)) {
-                                if (si.si_code == CLD_EXITED)
-                                        log_error("%s exited with exit status %i.", path, si.si_status);
-                                else
-                                        log_error("%s terminated by signal %s.", path, signal_to_string(si.si_status));
-                        } else
-                                log_debug("%s exited successfully.", path);
-
-                        free(path);
-                }
-        }
-
-finish:
-        if (_d)
-                closedir(_d);
-
-        if (pids)
-                hashmap_free_free(pids);
-}
-
-int kill_and_sigcont(pid_t pid, int sig) {
-        int r;
-
-        r = kill(pid, sig) < 0 ? -errno : 0;
-
-        if (r >= 0)
-                kill(pid, SIGCONT);
-
-        return r;
-}
-
-bool nulstr_contains(const char*nulstr, const char *needle) {
-        const char *i;
-
-        if (!nulstr)
-                return false;
-
-        NULSTR_FOREACH(i, nulstr)
-                if (streq(i, needle))
-                        return true;
-
-        return false;
-}
-
-bool plymouth_running(void) {
-        return access("/run/plymouth/pid", F_OK) >= 0;
-}
-
-void parse_syslog_priority(char **p, int *priority) {
-        int a = 0, b = 0, c = 0;
-        int k;
-
-        assert(p);
-        assert(*p);
-        assert(priority);
-
-        if ((*p)[0] != '<')
-                return;
-
-        if (!strchr(*p, '>'))
-                return;
-
-        if ((*p)[2] == '>') {
-                c = undecchar((*p)[1]);
-                k = 3;
-        } else if ((*p)[3] == '>') {
-                b = undecchar((*p)[1]);
-                c = undecchar((*p)[2]);
-                k = 4;
-        } else if ((*p)[4] == '>') {
-                a = undecchar((*p)[1]);
-                b = undecchar((*p)[2]);
-                c = undecchar((*p)[3]);
-                k = 5;
-        } else
-                return;
-
-        if (a < 0 || b < 0 || c < 0)
-                return;
-
-        *priority = a*100+b*10+c;
-        *p += k;
-}
-
-void skip_syslog_pid(char **buf) {
-        char *p;
-
-        assert(buf);
-        assert(*buf);
-
-        p = *buf;
-
-        if (*p != '[')
-                return;
-
-        p++;
-        p += strspn(p, "0123456789");
-
-        if (*p != ']')
-                return;
-
-        p++;
-
-        *buf = p;
-}
-
-void skip_syslog_date(char **buf) {
-        enum {
-                LETTER,
-                SPACE,
-                NUMBER,
-                SPACE_OR_NUMBER,
-                COLON
-        } sequence[] = {
-                LETTER, LETTER, LETTER,
-                SPACE,
-                SPACE_OR_NUMBER, NUMBER,
-                SPACE,
-                SPACE_OR_NUMBER, NUMBER,
-                COLON,
-                SPACE_OR_NUMBER, NUMBER,
-                COLON,
-                SPACE_OR_NUMBER, NUMBER,
-                SPACE
-        };
-
-        char *p;
-        unsigned i;
-
-        assert(buf);
-        assert(*buf);
-
-        p = *buf;
-
-        for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
-
-                if (!*p)
-                        return;
-
-                switch (sequence[i]) {
-
-                case SPACE:
-                        if (*p != ' ')
-                                return;
-                        break;
-
-                case SPACE_OR_NUMBER:
-                        if (*p == ' ')
-                                break;
-
-                        /* fall through */
-
-                case NUMBER:
-                        if (*p < '0' || *p > '9')
-                                return;
-
-                        break;
-
-                case LETTER:
-                        if (!(*p >= 'A' && *p <= 'Z') &&
-                            !(*p >= 'a' && *p <= 'z'))
-                                return;
-
-                        break;
-
-                case COLON:
-                        if (*p != ':')
-                                return;
-                        break;
-
-                }
-        }
-
-        *buf = p;
-}
-
-int have_effective_cap(int value) {
-        cap_t cap;
-        cap_flag_value_t fv;
-        int r;
-
-        if (!(cap = cap_get_proc()))
-                return -errno;
-
-        if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
-                r = -errno;
-        else
-                r = fv == CAP_SET;
-
-        cap_free(cap);
-        return r;
-}
-
-char* strshorten(char *s, size_t l) {
-        assert(s);
-
-        if (l < strlen(s))
-                s[l] = 0;
-
-        return s;
-}
-
-static bool hostname_valid_char(char c) {
-        return
-                (c >= 'a' && c <= 'z') ||
-                (c >= 'A' && c <= 'Z') ||
-                (c >= '0' && c <= '9') ||
-                c == '-' ||
-                c == '_' ||
-                c == '.';
-}
-
-bool hostname_is_valid(const char *s) {
-        const char *p;
-
-        if (isempty(s))
-                return false;
-
-        for (p = s; *p; p++)
-                if (!hostname_valid_char(*p))
-                        return false;
-
-        if (p-s > HOST_NAME_MAX)
-                return false;
-
-        return true;
-}
-
-char* hostname_cleanup(char *s) {
-        char *p, *d;
-
-        for (p = s, d = s; *p; p++)
-                if ((*p >= 'a' && *p <= 'z') ||
-                    (*p >= 'A' && *p <= 'Z') ||
-                    (*p >= '0' && *p <= '9') ||
-                    *p == '-' ||
-                    *p == '_' ||
-                    *p == '.')
-                        *(d++) = *p;
-
-        *d = 0;
-
-        strshorten(s, HOST_NAME_MAX);
-        return s;
-}
-
-int pipe_eof(int fd) {
-        struct pollfd pollfd;
-        int r;
-
-        zero(pollfd);
-        pollfd.fd = fd;
-        pollfd.events = POLLIN|POLLHUP;
-
-        r = poll(&pollfd, 1, 0);
-        if (r < 0)
-                return -errno;
-
-        if (r == 0)
-                return 0;
-
-        return pollfd.revents & POLLHUP;
-}
-
-int fd_wait_for_event(int fd, int event, usec_t t) {
-        struct pollfd pollfd;
-        int r;
-
-        zero(pollfd);
-        pollfd.fd = fd;
-        pollfd.events = event;
-
-        r = poll(&pollfd, 1, t == (usec_t) -1 ? -1 : (int) (t / USEC_PER_MSEC));
-        if (r < 0)
-                return -errno;
-
-        if (r == 0)
-                return 0;
-
-        return pollfd.revents;
-}
-
-int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
-        FILE *f;
-        char *t;
-        const char *fn;
-        size_t k;
-        int fd;
-
-        assert(path);
-        assert(_f);
-        assert(_temp_path);
-
-        t = new(char, strlen(path) + 1 + 6 + 1);
-        if (!t)
-                return -ENOMEM;
-
-        fn = file_name_from_path(path);
-        k = fn-path;
-        memcpy(t, path, k);
-        t[k] = '.';
-        stpcpy(stpcpy(t+k+1, fn), "XXXXXX");
-
-        fd = mkostemp(t, O_WRONLY|O_CLOEXEC);
-        if (fd < 0) {
-                free(t);
-                return -errno;
-        }
-
-        f = fdopen(fd, "we");
-        if (!f) {
-                unlink(t);
-                free(t);
-                return -errno;
-        }
-
-        *_f = f;
-        *_temp_path = t;
-
-        return 0;
-}
-
-int terminal_vhangup_fd(int fd) {
-        assert(fd >= 0);
-
-        if (ioctl(fd, TIOCVHANGUP) < 0)
-                return -errno;
-
-        return 0;
-}
-
-int terminal_vhangup(const char *name) {
-        int fd, r;
-
-        fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
-        if (fd < 0)
-                return fd;
-
-        r = terminal_vhangup_fd(fd);
-        close_nointr_nofail(fd);
-
-        return r;
-}
-
-int vt_disallocate(const char *name) {
-        int fd, r;
-        unsigned u;
-
-        /* Deallocate the VT if possible. If not possible
-         * (i.e. because it is the active one), at least clear it
-         * entirely (including the scrollback buffer) */
-
-        if (!startswith(name, "/dev/"))
-                return -EINVAL;
-
-        if (!tty_is_vc(name)) {
-                /* So this is not a VT. I guess we cannot deallocate
-                 * it then. But let's at least clear the screen */
-
-                fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
-                if (fd < 0)
-                        return fd;
-
-                loop_write(fd,
-                           "\033[r"    /* clear scrolling region */
-                           "\033[H"    /* move home */
-                           "\033[2J",  /* clear screen */
-                           10, false);
-                close_nointr_nofail(fd);
-
-                return 0;
-        }
-
-        if (!startswith(name, "/dev/tty"))
-                return -EINVAL;
-
-        r = safe_atou(name+8, &u);
-        if (r < 0)
-                return r;
-
-        if (u <= 0)
-                return -EINVAL;
-
-        /* Try to deallocate */
-        fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
-        if (fd < 0)
-                return fd;
-
-        r = ioctl(fd, VT_DISALLOCATE, u);
-        close_nointr_nofail(fd);
-
-        if (r >= 0)
-                return 0;
-
-        if (errno != EBUSY)
-                return -errno;
-
-        /* Couldn't deallocate, so let's clear it fully with
-         * scrollback */
-        fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
-        if (fd < 0)
-                return fd;
-
-        loop_write(fd,
-                   "\033[r"   /* clear scrolling region */
-                   "\033[H"   /* move home */
-                   "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
-                   10, false);
-        close_nointr_nofail(fd);
-
-        return 0;
-}
-
-static int files_add(Hashmap *h, const char *path, const char *suffix) {
-        DIR *dir;
-        struct dirent buffer, *de;
-        int r = 0;
-
-        dir = opendir(path);
-        if (!dir) {
-                if (errno == ENOENT)
-                        return 0;
-                return -errno;
-        }
-
-        for (;;) {
-                int k;
-                char *p, *f;
-
-                k = readdir_r(dir, &buffer, &de);
-                if (k != 0) {
-                        r = -k;
-                        goto finish;
-                }
-
-                if (!de)
-                        break;
-
-                if (!dirent_is_file_with_suffix(de, suffix))
-                        continue;
-
-                if (asprintf(&p, "%s/%s", path, de->d_name) < 0) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                f = canonicalize_file_name(p);
-                if (!f) {
-                        log_error("Failed to canonicalize file name '%s': %m", p);
-                        free(p);
-                        continue;
-                }
-                free(p);
-
-                log_debug("found: %s\n", f);
-                if (hashmap_put(h, file_name_from_path(f), f) <= 0)
-                        free(f);
-        }
-
-finish:
-        closedir(dir);
-        return r;
-}
-
-static int base_cmp(const void *a, const void *b) {
-        const char *s1, *s2;
-
-        s1 = *(char * const *)a;
-        s2 = *(char * const *)b;
-        return strcmp(file_name_from_path(s1), file_name_from_path(s2));
-}
-
-int conf_files_list(char ***strv, const char *suffix, const char *dir, ...) {
-        Hashmap *fh = NULL;
-        char **dirs = NULL;
-        char **files = NULL;
-        char **p;
-        va_list ap;
-        int r = 0;
-
-        va_start(ap, dir);
-        dirs = strv_new_ap(dir, ap);
-        va_end(ap);
-        if (!dirs) {
-                r = -ENOMEM;
-                goto finish;
-        }
-        if (!strv_path_canonicalize(dirs)) {
-                r = -ENOMEM;
-                goto finish;
-        }
-        if (!strv_uniq(dirs)) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        fh = hashmap_new(string_hash_func, string_compare_func);
-        if (!fh) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        STRV_FOREACH(p, dirs) {
-                if (files_add(fh, *p, suffix) < 0) {
-                        log_error("Failed to search for files.");
-                        r = -EINVAL;
-                        goto finish;
-                }
-        }
-
-        files = hashmap_get_strv(fh);
-        if (files == NULL) {
-                log_error("Failed to compose list of files.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        qsort(files, hashmap_size(fh), sizeof(char *), base_cmp);
-
-finish:
-        strv_free(dirs);
-        hashmap_free(fh);
-        *strv = files;
-        return r;
-}
-
-int hwclock_is_localtime(void) {
-        FILE *f;
-        bool local = false;
-
-        /*
-         * The third line of adjtime is "UTC" or "LOCAL" or nothing.
-         *   # /etc/adjtime
-         *   0.0 0 0
-         *   0
-         *   UTC
-         */
-        f = fopen("/etc/adjtime", "re");
-        if (f) {
-                char line[LINE_MAX];
-                bool b;
-
-                b = fgets(line, sizeof(line), f) &&
-                        fgets(line, sizeof(line), f) &&
-                        fgets(line, sizeof(line), f);
-
-                fclose(f);
-
-                if (!b)
-                        return -EIO;
-
-
-                truncate_nl(line);
-                local = streq(line, "LOCAL");
-
-        } else if (errno != -ENOENT)
-                return -errno;
-
-        return local;
-}
-
-int hwclock_apply_localtime_delta(int *min) {
-        const struct timeval *tv_null = NULL;
-        struct timespec ts;
-        struct tm *tm;
-        int minuteswest;
-        struct timezone tz;
-
-        assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
-        assert_se(tm = localtime(&ts.tv_sec));
-        minuteswest = tm->tm_gmtoff / 60;
-
-        tz.tz_minuteswest = -minuteswest;
-        tz.tz_dsttime = 0; /* DST_NONE*/
-
-        /*
-         * If the hardware clock does not run in UTC, but in local time:
-         * The very first time we set the kernel's timezone, it will warp
-         * the clock so that it runs in UTC instead of local time.
-         */
-        if (settimeofday(tv_null, &tz) < 0)
-                return -errno;
-        if (min)
-                *min = minuteswest;
-        return 0;
-}
-
-int hwclock_reset_localtime_delta(void) {
-        const struct timeval *tv_null = NULL;
-        struct timezone tz;
-
-        tz.tz_minuteswest = 0;
-        tz.tz_dsttime = 0; /* DST_NONE*/
-
-        if (settimeofday(tv_null, &tz) < 0)
-                return -errno;
-
-        return 0;
-}
-
-int rtc_open(int flags) {
-        int fd;
-        DIR *d;
-
-        /* First, we try to make use of the /dev/rtc symlink. If that
-         * doesn't exist, we open the first RTC which has hctosys=1
-         * set. If we don't find any we just take the first RTC that
-         * exists at all. */
-
-        fd = open("/dev/rtc", flags);
-        if (fd >= 0)
-                return fd;
-
-        d = opendir("/sys/class/rtc");
-        if (!d)
-                goto fallback;
-
-        for (;;) {
-                char *p, *v;
-                struct dirent buf, *de;
-                int r;
-
-                r = readdir_r(d, &buf, &de);
-                if (r != 0)
-                        goto fallback;
-
-                if (!de)
-                        goto fallback;
-
-                if (ignore_file(de->d_name))
-                        continue;
-
-                p = join("/sys/class/rtc/", de->d_name, "/hctosys", NULL);
-                if (!p) {
-                        closedir(d);
-                        return -ENOMEM;
-                }
-
-                r = read_one_line_file(p, &v);
-                free(p);
-
-                if (r < 0)
-                        continue;
-
-                r = parse_boolean(v);
-                free(v);
-
-                if (r <= 0)
-                        continue;
-
-                p = strappend("/dev/", de->d_name);
-                fd = open(p, flags);
-                free(p);
-
-                if (fd >= 0) {
-                        closedir(d);
-                        return fd;
-                }
-        }
-
-fallback:
-        if (d)
-                closedir(d);
-
-        fd = open("/dev/rtc0", flags);
-        if (fd < 0)
-                return -errno;
-
-        return fd;
-}
-
-int hwclock_get_time(struct tm *tm) {
-        int fd;
-        int err = 0;
-
-        assert(tm);
-
-        fd = rtc_open(O_RDONLY|O_CLOEXEC);
-        if (fd < 0)
-                return -errno;
-
-        /* This leaves the timezone fields of struct tm
-         * uninitialized! */
-        if (ioctl(fd, RTC_RD_TIME, tm) < 0)
-                err = -errno;
-
-        /* We don't now daylight saving, so we reset this in order not
-         * to confused mktime(). */
-        tm->tm_isdst = -1;
-
-        close_nointr_nofail(fd);
-
-        return err;
-}
-
-int hwclock_set_time(const struct tm *tm) {
-        int fd;
-        int err = 0;
-
-        assert(tm);
-
-        fd = rtc_open(O_RDONLY|O_CLOEXEC);
-        if (fd < 0)
-                return -errno;
-
-        if (ioctl(fd, RTC_SET_TIME, tm) < 0)
-                err = -errno;
-
-        close_nointr_nofail(fd);
-
-        return err;
-}
-
-int copy_file(const char *from, const char *to) {
-        int r, fdf, fdt;
-
-        assert(from);
-        assert(to);
-
-        fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        if (fdf < 0)
-                return -errno;
-
-        fdt = open(to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY, 0644);
-        if (fdt < 0) {
-                close_nointr_nofail(fdf);
-                return -errno;
-        }
-
-        for (;;) {
-                char buf[PIPE_BUF];
-                ssize_t n, k;
-
-                n = read(fdf, buf, sizeof(buf));
-                if (n < 0) {
-                        r = -errno;
-
-                        close_nointr_nofail(fdf);
-                        close_nointr(fdt);
-                        unlink(to);
-
-                        return r;
-                }
-
-                if (n == 0)
-                        break;
-
-                errno = 0;
-                k = loop_write(fdt, buf, n, false);
-                if (n != k) {
-                        r = k < 0 ? k : (errno ? -errno : -EIO);
-
-                        close_nointr_nofail(fdf);
-                        close_nointr(fdt);
-
-                        unlink(to);
-                        return r;
-                }
-        }
-
-        close_nointr_nofail(fdf);
-        r = close_nointr(fdt);
-
-        if (r < 0) {
-                unlink(to);
-                return r;
-        }
-
-        return 0;
-}
-
-int symlink_or_copy(const char *from, const char *to) {
-        char *pf = NULL, *pt = NULL;
-        struct stat a, b;
-        int r;
-
-        assert(from);
-        assert(to);
-
-        if (parent_of_path(from, &pf) < 0 ||
-            parent_of_path(to, &pt) < 0) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (stat(pf, &a) < 0 ||
-            stat(pt, &b) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        if (a.st_dev != b.st_dev) {
-                free(pf);
-                free(pt);
-
-                return copy_file(from, to);
-        }
-
-        if (symlink(from, to) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        r = 0;
-
-finish:
-        free(pf);
-        free(pt);
-
-        return r;
-}
-
-int symlink_or_copy_atomic(const char *from, const char *to) {
-        char *t, *x;
-        const char *fn;
-        size_t k;
-        unsigned long long ull;
-        unsigned i;
-        int r;
-
-        assert(from);
-        assert(to);
-
-        t = new(char, strlen(to) + 1 + 16 + 1);
-        if (!t)
-                return -ENOMEM;
-
-        fn = file_name_from_path(to);
-        k = fn-to;
-        memcpy(t, to, k);
-        t[k] = '.';
-        x = stpcpy(t+k+1, fn);
-
-        ull = random_ull();
-        for (i = 0; i < 16; i++) {
-                *(x++) = hexchar(ull & 0xF);
-                ull >>= 4;
-        }
-
-        *x = 0;
-
-        r = symlink_or_copy(from, t);
-        if (r < 0) {
-                unlink(t);
-                free(t);
-                return r;
-        }
-
-        if (rename(t, to) < 0) {
-                r = -errno;
-                unlink(t);
-                free(t);
-                return r;
-        }
-
-        free(t);
-        return r;
-}
-
-int audit_session_from_pid(pid_t pid, uint32_t *id) {
-        char *s;
-        uint32_t u;
-        int r;
-
-        assert(id);
-
-        if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
-                return -ENOENT;
-
-        if (pid == 0)
-                r = read_one_line_file("/proc/self/sessionid", &s);
-        else {
-                char *p;
-
-                if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0)
-                        return -ENOMEM;
-
-                r = read_one_line_file(p, &s);
-                free(p);
-        }
-
-        if (r < 0)
-                return r;
-
-        r = safe_atou32(s, &u);
-        free(s);
-
-        if (r < 0)
-                return r;
-
-        if (u == (uint32_t) -1 || u <= 0)
-                return -ENOENT;
-
-        *id = u;
-        return 0;
-}
-
-int audit_loginuid_from_pid(pid_t pid, uid_t *uid) {
-        char *s;
-        uid_t u;
-        int r;
-
-        assert(uid);
-
-        /* Only use audit login uid if we are executed with sufficient
-         * capabilities so that pam_loginuid could do its job. If we
-         * are lacking the CAP_AUDIT_CONTROL capabality we most likely
-         * are being run in a container and /proc/self/loginuid is
-         * useless since it probably contains a uid of the host
-         * system. */
-
-        if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
-                return -ENOENT;
-
-        if (pid == 0)
-                r = read_one_line_file("/proc/self/loginuid", &s);
-        else {
-                char *p;
-
-                if (asprintf(&p, "/proc/%lu/loginuid", (unsigned long) pid) < 0)
-                        return -ENOMEM;
-
-                r = read_one_line_file(p, &s);
-                free(p);
-        }
-
-        if (r < 0)
-                return r;
-
-        r = parse_uid(s, &u);
-        free(s);
-
-        if (r < 0)
-                return r;
-
-        if (u == (uid_t) -1)
-                return -ENOENT;
-
-        *uid = (uid_t) u;
-        return 0;
-}
-
-bool display_is_local(const char *display) {
-        assert(display);
-
-        return
-                display[0] == ':' &&
-                display[1] >= '0' &&
-                display[1] <= '9';
-}
-
-int socket_from_display(const char *display, char **path) {
-        size_t k;
-        char *f, *c;
-
-        assert(display);
-        assert(path);
-
-        if (!display_is_local(display))
-                return -EINVAL;
-
-        k = strspn(display+1, "0123456789");
-
-        f = new(char, sizeof("/tmp/.X11-unix/X") + k);
-        if (!f)
-                return -ENOMEM;
-
-        c = stpcpy(f, "/tmp/.X11-unix/X");
-        memcpy(c, display+1, k);
-        c[k] = 0;
-
-        *path = f;
-
-        return 0;
-}
-
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
-        struct passwd *p;
-        uid_t u;
-
-        assert(username);
-        assert(*username);
-
-        /* We enforce some special rules for uid=0: in order to avoid
-         * NSS lookups for root we hardcode its data. */
-
-        if (streq(*username, "root") || streq(*username, "0")) {
-                *username = "root";
-
-                if (uid)
-                        *uid = 0;
-
-                if (gid)
-                        *gid = 0;
-
-                if (home)
-                        *home = "/root";
-                return 0;
-        }
-
-        if (parse_uid(*username, &u) >= 0) {
-                errno = 0;
-                p = getpwuid(u);
-
-                /* If there are multiple users with the same id, make
-                 * sure to leave $USER to the configured value instead
-                 * of the first occurrence in the database. However if
-                 * the uid was configured by a numeric uid, then let's
-                 * pick the real username from /etc/passwd. */
-                if (p)
-                        *username = p->pw_name;
-        } else {
-                errno = 0;
-                p = getpwnam(*username);
-        }
-
-        if (!p)
-                return errno != 0 ? -errno : -ESRCH;
-
-        if (uid)
-                *uid = p->pw_uid;
-
-        if (gid)
-                *gid = p->pw_gid;
-
-        if (home)
-                *home = p->pw_dir;
-
-        return 0;
-}
-
-int get_group_creds(const char **groupname, gid_t *gid) {
-        struct group *g;
-        gid_t id;
-
-        assert(groupname);
-
-        /* We enforce some special rules for gid=0: in order to avoid
-         * NSS lookups for root we hardcode its data. */
-
-        if (streq(*groupname, "root") || streq(*groupname, "0")) {
-                *groupname = "root";
-
-                if (gid)
-                        *gid = 0;
-
-                return 0;
-        }
-
-        if (parse_gid(*groupname, &id) >= 0) {
-                errno = 0;
-                g = getgrgid(id);
-
-                if (g)
-                        *groupname = g->gr_name;
-        } else {
-                errno = 0;
-                g = getgrnam(*groupname);
-        }
-
-        if (!g)
-                return errno != 0 ? -errno : -ESRCH;
-
-        if (gid)
-                *gid = g->gr_gid;
-
-        return 0;
-}
-
-int in_group(const char *name) {
-        gid_t gid, *gids;
-        int ngroups_max, r, i;
-
-        r = get_group_creds(&name, &gid);
-        if (r < 0)
-                return r;
-
-        if (getgid() == gid)
-                return 1;
-
-        if (getegid() == gid)
-                return 1;
-
-        ngroups_max = sysconf(_SC_NGROUPS_MAX);
-        assert(ngroups_max > 0);
-
-        gids = alloca(sizeof(gid_t) * ngroups_max);
-
-        r = getgroups(ngroups_max, gids);
-        if (r < 0)
-                return -errno;
-
-        for (i = 0; i < r; i++)
-                if (gids[i] == gid)
-                        return 1;
-
-        return 0;
-}
-
-int glob_exists(const char *path) {
-        glob_t g;
-        int r, k;
-
-        assert(path);
-
-        zero(g);
-        errno = 0;
-        k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
-
-        if (k == GLOB_NOMATCH)
-                r = 0;
-        else if (k == GLOB_NOSPACE)
-                r = -ENOMEM;
-        else if (k == 0)
-                r = !strv_isempty(g.gl_pathv);
-        else
-                r = errno ? -errno : -EIO;
-
-        globfree(&g);
-
-        return r;
-}
-
-int dirent_ensure_type(DIR *d, struct dirent *de) {
-        struct stat st;
-
-        assert(d);
-        assert(de);
-
-        if (de->d_type != DT_UNKNOWN)
-                return 0;
-
-        if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
-                return -errno;
-
-        de->d_type =
-                S_ISREG(st.st_mode)  ? DT_REG  :
-                S_ISDIR(st.st_mode)  ? DT_DIR  :
-                S_ISLNK(st.st_mode)  ? DT_LNK  :
-                S_ISFIFO(st.st_mode) ? DT_FIFO :
-                S_ISSOCK(st.st_mode) ? DT_SOCK :
-                S_ISCHR(st.st_mode)  ? DT_CHR  :
-                S_ISBLK(st.st_mode)  ? DT_BLK  :
-                                       DT_UNKNOWN;
-
-        return 0;
-}
-
-int in_search_path(const char *path, char **search) {
-        char **i, *parent;
-        int r;
-
-        r = parent_of_path(path, &parent);
-        if (r < 0)
-                return r;
-
-        r = 0;
-
-        STRV_FOREACH(i, search) {
-                if (path_equal(parent, *i)) {
-                        r = 1;
-                        break;
-                }
-        }
-
-        free(parent);
-
-        return r;
-}
-
-int get_files_in_directory(const char *path, char ***list) {
-        DIR *d;
-        int r = 0;
-        unsigned n = 0;
-        char **l = NULL;
-
-        assert(path);
-
-        /* Returns all files in a directory in *list, and the number
-         * of files as return value. If list is NULL returns only the
-         * number */
-
-        d = opendir(path);
-        if (!d)
-                return -errno;
-
-        for (;;) {
-                struct dirent buffer, *de;
-                int k;
-
-                k = readdir_r(d, &buffer, &de);
-                if (k != 0) {
-                        r = -k;
-                        goto finish;
-                }
-
-                if (!de)
-                        break;
-
-                dirent_ensure_type(d, de);
-
-                if (!dirent_is_file(de))
-                        continue;
-
-                if (list) {
-                        if ((unsigned) r >= n) {
-                                char **t;
-
-                                n = MAX(16, 2*r);
-                                t = realloc(l, sizeof(char*) * n);
-                                if (!t) {
-                                        r = -ENOMEM;
-                                        goto finish;
-                                }
-
-                                l = t;
-                        }
-
-                        assert((unsigned) r < n);
-
-                        l[r] = strdup(de->d_name);
-                        if (!l[r]) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
-
-                        l[++r] = NULL;
-                } else
-                        r++;
-        }
-
-finish:
-        if (d)
-                closedir(d);
-
-        if (r >= 0) {
-                if (list)
-                        *list = l;
-        } else
-                strv_free(l);
-
-        return r;
-}
-
-char *join(const char *x, ...) {
-        va_list ap;
-        size_t l;
-        char *r, *p;
-
-        va_start(ap, x);
-
-        if (x) {
-                l = strlen(x);
-
-                for (;;) {
-                        const char *t;
-
-                        t = va_arg(ap, const char *);
-                        if (!t)
-                                break;
-
-                        l += strlen(t);
-                }
-        } else
-                l = 0;
-
-        va_end(ap);
-
-        r = new(char, l+1);
-        if (!r)
-                return NULL;
-
-        if (x) {
-                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;
-}
-
-bool is_main_thread(void) {
-        static __thread int cached = 0;
-
-        if (_unlikely_(cached == 0))
-                cached = getpid() == gettid() ? 1 : -1;
-
-        return cached > 0;
-}
-
-int block_get_whole_disk(dev_t d, dev_t *ret) {
-        char *p, *s;
-        int r;
-        unsigned n, m;
-
-        assert(ret);
-
-        /* If it has a queue this is good enough for us */
-        if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
-                return -ENOMEM;
-
-        r = access(p, F_OK);
-        free(p);
-
-        if (r >= 0) {
-                *ret = d;
-                return 0;
-        }
-
-        /* If it is a partition find the originating device */
-        if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
-                return -ENOMEM;
-
-        r = access(p, F_OK);
-        free(p);
-
-        if (r < 0)
-                return -ENOENT;
-
-        /* Get parent dev_t */
-        if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
-                return -ENOMEM;
-
-        r = read_one_line_file(p, &s);
-        free(p);
-
-        if (r < 0)
-                return r;
-
-        r = sscanf(s, "%u:%u", &m, &n);
-        free(s);
-
-        if (r != 2)
-                return -EINVAL;
-
-        /* Only return this if it is really good enough for us. */
-        if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
-                return -ENOMEM;
-
-        r = access(p, F_OK);
-        free(p);
-
-        if (r >= 0) {
-                *ret = makedev(m, n);
-                return 0;
-        }
-
-        return -ENOENT;
-}
-
-int file_is_priv_sticky(const char *p) {
-        struct stat st;
-
-        assert(p);
-
-        if (lstat(p, &st) < 0)
-                return -errno;
-
-        return
-                (st.st_uid == 0 || st.st_uid == getuid()) &&
-                (st.st_mode & S_ISVTX);
-}
-
-static const char *const ioprio_class_table[] = {
-        [IOPRIO_CLASS_NONE] = "none",
-        [IOPRIO_CLASS_RT] = "realtime",
-        [IOPRIO_CLASS_BE] = "best-effort",
-        [IOPRIO_CLASS_IDLE] = "idle"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(ioprio_class, int);
-
-static const char *const sigchld_code_table[] = {
-        [CLD_EXITED] = "exited",
-        [CLD_KILLED] = "killed",
-        [CLD_DUMPED] = "dumped",
-        [CLD_TRAPPED] = "trapped",
-        [CLD_STOPPED] = "stopped",
-        [CLD_CONTINUED] = "continued",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
-
-static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
-        [LOG_FAC(LOG_KERN)] = "kern",
-        [LOG_FAC(LOG_USER)] = "user",
-        [LOG_FAC(LOG_MAIL)] = "mail",
-        [LOG_FAC(LOG_DAEMON)] = "daemon",
-        [LOG_FAC(LOG_AUTH)] = "auth",
-        [LOG_FAC(LOG_SYSLOG)] = "syslog",
-        [LOG_FAC(LOG_LPR)] = "lpr",
-        [LOG_FAC(LOG_NEWS)] = "news",
-        [LOG_FAC(LOG_UUCP)] = "uucp",
-        [LOG_FAC(LOG_CRON)] = "cron",
-        [LOG_FAC(LOG_AUTHPRIV)] = "authpriv",
-        [LOG_FAC(LOG_FTP)] = "ftp",
-        [LOG_FAC(LOG_LOCAL0)] = "local0",
-        [LOG_FAC(LOG_LOCAL1)] = "local1",
-        [LOG_FAC(LOG_LOCAL2)] = "local2",
-        [LOG_FAC(LOG_LOCAL3)] = "local3",
-        [LOG_FAC(LOG_LOCAL4)] = "local4",
-        [LOG_FAC(LOG_LOCAL5)] = "local5",
-        [LOG_FAC(LOG_LOCAL6)] = "local6",
-        [LOG_FAC(LOG_LOCAL7)] = "local7"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(log_facility_unshifted, int);
-
-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);
-
-static const char* const sched_policy_table[] = {
-        [SCHED_OTHER] = "other",
-        [SCHED_BATCH] = "batch",
-        [SCHED_IDLE] = "idle",
-        [SCHED_FIFO] = "fifo",
-        [SCHED_RR] = "rr"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(sched_policy, int);
-
-static const char* const rlimit_table[] = {
-        [RLIMIT_CPU] = "LimitCPU",
-        [RLIMIT_FSIZE] = "LimitFSIZE",
-        [RLIMIT_DATA] = "LimitDATA",
-        [RLIMIT_STACK] = "LimitSTACK",
-        [RLIMIT_CORE] = "LimitCORE",
-        [RLIMIT_RSS] = "LimitRSS",
-        [RLIMIT_NOFILE] = "LimitNOFILE",
-        [RLIMIT_AS] = "LimitAS",
-        [RLIMIT_NPROC] = "LimitNPROC",
-        [RLIMIT_MEMLOCK] = "LimitMEMLOCK",
-        [RLIMIT_LOCKS] = "LimitLOCKS",
-        [RLIMIT_SIGPENDING] = "LimitSIGPENDING",
-        [RLIMIT_MSGQUEUE] = "LimitMSGQUEUE",
-        [RLIMIT_NICE] = "LimitNICE",
-        [RLIMIT_RTPRIO] = "LimitRTPRIO",
-        [RLIMIT_RTTIME] = "LimitRTTIME"
-};
-
-DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
-
-static const char* const ip_tos_table[] = {
-        [IPTOS_LOWDELAY] = "low-delay",
-        [IPTOS_THROUGHPUT] = "throughput",
-        [IPTOS_RELIABILITY] = "reliability",
-        [IPTOS_LOWCOST] = "low-cost",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(ip_tos, int);
-
-static const char *const __signal_table[] = {
-        [SIGHUP] = "HUP",
-        [SIGINT] = "INT",
-        [SIGQUIT] = "QUIT",
-        [SIGILL] = "ILL",
-        [SIGTRAP] = "TRAP",
-        [SIGABRT] = "ABRT",
-        [SIGBUS] = "BUS",
-        [SIGFPE] = "FPE",
-        [SIGKILL] = "KILL",
-        [SIGUSR1] = "USR1",
-        [SIGSEGV] = "SEGV",
-        [SIGUSR2] = "USR2",
-        [SIGPIPE] = "PIPE",
-        [SIGALRM] = "ALRM",
-        [SIGTERM] = "TERM",
-#ifdef SIGSTKFLT
-        [SIGSTKFLT] = "STKFLT",  /* Linux on SPARC doesn't know SIGSTKFLT */
-#endif
-        [SIGCHLD] = "CHLD",
-        [SIGCONT] = "CONT",
-        [SIGSTOP] = "STOP",
-        [SIGTSTP] = "TSTP",
-        [SIGTTIN] = "TTIN",
-        [SIGTTOU] = "TTOU",
-        [SIGURG] = "URG",
-        [SIGXCPU] = "XCPU",
-        [SIGXFSZ] = "XFSZ",
-        [SIGVTALRM] = "VTALRM",
-        [SIGPROF] = "PROF",
-        [SIGWINCH] = "WINCH",
-        [SIGIO] = "IO",
-        [SIGPWR] = "PWR",
-        [SIGSYS] = "SYS"
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
-
-const char *signal_to_string(int signo) {
-        static __thread char buf[12];
-        const char *name;
-
-        name = __signal_to_string(signo);
-        if (name)
-                return name;
-
-        if (signo >= SIGRTMIN && signo <= SIGRTMAX)
-                snprintf(buf, sizeof(buf) - 1, "RTMIN+%d", signo - SIGRTMIN);
-        else
-                snprintf(buf, sizeof(buf) - 1, "%d", signo);
-        char_array_0(buf);
-        return buf;
-}
-
-int signal_from_string(const char *s) {
-        int signo;
-        int offset = 0;
-        unsigned u;
-
-        signo =__signal_from_string(s);
-        if (signo > 0)
-                return signo;
-
-        if (startswith(s, "RTMIN+")) {
-                s += 6;
-                offset = SIGRTMIN;
-        }
-        if (safe_atou(s, &u) >= 0) {
-                signo = (int) u + offset;
-                if (signo > 0 && signo < _NSIG)
-                        return signo;
-        }
-        return -1;
-}
-
-bool kexec_loaded(void) {
-       bool loaded = false;
-       char *s;
-
-       if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
-               if (s[0] == '1')
-                       loaded = true;
-               free(s);
-       }
-       return loaded;
-}
-
-int strdup_or_null(const char *a, char **b) {
-        char *c;
-
-        assert(b);
-
-        if (!a) {
-                *b = NULL;
-                return 0;
-        }
-
-        c = strdup(a);
-        if (!c)
-                return -ENOMEM;
-
-        *b = c;
-        return 0;
-}
-
-int prot_from_flags(int flags) {
-
-        switch (flags & O_ACCMODE) {
-
-        case O_RDONLY:
-                return PROT_READ;
-
-        case O_WRONLY:
-                return PROT_WRITE;
-
-        case O_RDWR:
-                return PROT_READ|PROT_WRITE;
-
-        default:
-                return -EINVAL;
-        }
-}
-
-unsigned long cap_last_cap(void) {
-        static __thread unsigned long saved;
-        static __thread bool valid = false;
-        unsigned long p;
-
-        if (valid)
-                return saved;
-
-        p = (unsigned long) CAP_LAST_CAP;
-
-        if (prctl(PR_CAPBSET_READ, p) < 0) {
-
-                /* Hmm, look downwards, until we find one that
-                 * works */
-                for (p--; p > 0; p --)
-                        if (prctl(PR_CAPBSET_READ, p) >= 0)
-                                break;
-
-        } else {
-
-                /* Hmm, look upwards, until we find one that doesn't
-                 * work */
-                for (;; p++)
-                        if (prctl(PR_CAPBSET_READ, p+1) < 0)
-                                break;
-        }
-
-        saved = p;
-        valid = true;
-
-        return p;
-}
-
-char *format_bytes(char *buf, size_t l, off_t t) {
-        unsigned i;
-
-        static const struct {
-                const char *suffix;
-                off_t factor;
-        } table[] = {
-                { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
-                { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
-                { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
-                { "G", 1024ULL*1024ULL*1024ULL },
-                { "M", 1024ULL*1024ULL },
-                { "K", 1024ULL },
-        };
-
-        for (i = 0; i < ELEMENTSOF(table); i++) {
-
-                if (t >= table[i].factor) {
-                        snprintf(buf, l,
-                                 "%llu.%llu%s",
-                                 (unsigned long long) (t / table[i].factor),
-                                 (unsigned long long) (((t*10ULL) / table[i].factor) % 10ULL),
-                                 table[i].suffix);
-
-                        goto finish;
-                }
-        }
-
-        snprintf(buf, l, "%lluB", (unsigned long long) t);
-
-finish:
-        buf[l-1] = 0;
-        return buf;
-
-}
-
-void* memdup(const void *p, size_t l) {
-        void *r;
-
-        assert(p);
-
-        r = malloc(l);
-        if (!r)
-                return NULL;
-
-        memcpy(r, p, l);
-        return r;
-}
-
-int fd_inc_sndbuf(int fd, size_t n) {
-        int r, value;
-        socklen_t l = sizeof(value);
-
-        r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
-        if (r >= 0 &&
-            l == sizeof(value) &&
-            (size_t) value >= n*2)
-                return 0;
-
-        value = (int) n;
-        r = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value));
-        if (r < 0)
-                return -errno;
-
-        return 1;
-}
-
-int fd_inc_rcvbuf(int fd, size_t n) {
-        int r, value;
-        socklen_t l = sizeof(value);
-
-        r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
-        if (r >= 0 &&
-            l == sizeof(value) &&
-            (size_t) value >= n*2)
-                return 0;
-
-        value = (int) n;
-        r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value));
-        if (r < 0)
-                return -errno;
-
-        return 1;
-}
diff --git a/src/util.h b/src/util.h
deleted file mode 100644
index b1af6db..0000000
--- a/src/util.h
+++ /dev/null
@@ -1,544 +0,0 @@
-/*-*- 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 General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU 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 "macro.h"
-
-typedef uint64_t usec_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 USEC_PER_HOUR (60ULL*USEC_PER_MINUTE)
-#define USEC_PER_DAY (24ULL*USEC_PER_HOUR)
-#define USEC_PER_WEEK (7ULL*USEC_PER_DAY)
-#define USEC_PER_MONTH (2629800ULL*USEC_PER_SEC)
-#define USEC_PER_YEAR (31557600ULL*USEC_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_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 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 is_path_absolute(const char *p) {
-        return *p == '/';
-}
-
-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_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)))
-
-char **split_path_and_make_absolute(const char *p);
-
-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);
-
-char *file_name_from_path(const char *p);
-bool is_path(const char *p);
-
-bool path_is_absolute(const char *p);
-char *path_make_absolute(const char *p, const char *prefix);
-char *path_make_absolute_cwd(const char *p);
-
-char **strv_path_make_absolute_cwd(char **l);
-char **strv_path_canonicalize(char **l);
-char **strv_path_remove_empty(char **l);
-
-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 safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid);
-int mkdir_parents(const char *path, mode_t mode);
-int mkdir_p(const char *path, mode_t mode);
-
-int parent_of_path(const char *path, char **parent);
-
-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 *path_kill_slashes(char *path);
-
-bool path_startswith(const char *path, const char *prefix);
-bool path_equal(const char *a, const char *b);
-
-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);
-
-int path_is_mount_point(const char *path, bool allow_symlink);
-
-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);
-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(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);
-
-void filter_environ(const char *prefix);
-
-bool tty_is_vc(const char *tty);
-bool tty_is_vc_resolve(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);
-
-int have_effective_cap(int value);
-
-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);
-
-int conf_files_list(char ***strv, const char *suffix, const char *dir, ...);
-
-int hwclock_is_localtime(void);
-int hwclock_apply_localtime_delta(int *min);
-int hwclock_reset_localtime_delta(void);
-int hwclock_get_time(struct tm *tm);
-int hwclock_set_time(const struct tm *tm);
-
-int audit_session_from_pid(pid_t pid, uint32_t *id);
-int audit_loginuid_from_pid(pid_t pid, uid_t *uid);
-
-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);
-
-unsigned long cap_last_cap(void);
-
-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 rtc_open(int flags);
-
-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);
-
-#endif
diff --git a/src/virt.c b/src/virt.c
deleted file mode 100644
index 4c526ff..0000000
--- a/src/virt.c
+++ /dev/null
@@ -1,292 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2011 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include "util.h"
-#include "virt.h"
-
-/* Returns a short identifier for the various VM implementations */
-int detect_vm(const char **id) {
-
-#if defined(__i386__) || defined(__x86_64__)
-
-        /* Both CPUID and DMI are x86 specific interfaces... */
-
-        static const char *const dmi_vendors[] = {
-                "/sys/class/dmi/id/sys_vendor",
-                "/sys/class/dmi/id/board_vendor",
-                "/sys/class/dmi/id/bios_vendor"
-        };
-
-        static const char dmi_vendor_table[] =
-                "QEMU\0"                  "qemu\0"
-                /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
-                "VMware\0"                "vmware\0"
-                "VMW\0"                   "vmware\0"
-                "Microsoft Corporation\0" "microsoft\0"
-                "innotek GmbH\0"          "oracle\0"
-                "Xen\0"                   "xen\0"
-                "Bochs\0"                 "bochs\0";
-
-        static const char cpuid_vendor_table[] =
-                "XenVMMXenVMM\0"          "xen\0"
-                "KVMKVMKVM\0"             "kvm\0"
-                /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
-                "VMwareVMware\0"          "vmware\0"
-                /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
-                "Microsoft Hv\0"          "microsoft\0";
-
-        uint32_t eax, ecx;
-        union {
-                uint32_t sig32[3];
-                char text[13];
-        } sig;
-        unsigned i;
-        const char *j, *k;
-        bool hypervisor;
-
-        /* http://lwn.net/Articles/301888/ */
-        zero(sig);
-
-#if defined (__i386__)
-#define REG_a "eax"
-#define REG_b "ebx"
-#elif defined (__amd64__)
-#define REG_a "rax"
-#define REG_b "rbx"
-#endif
-
-        /* First detect whether there is a hypervisor */
-        eax = 1;
-        __asm__ __volatile__ (
-                /* ebx/rbx is being used for PIC! */
-                "  push %%"REG_b"         \n\t"
-                "  cpuid                  \n\t"
-                "  pop %%"REG_b"          \n\t"
-
-                : "=a" (eax), "=c" (ecx)
-                : "0" (eax)
-        );
-
-        hypervisor = !!(ecx & 0x80000000U);
-
-        if (hypervisor) {
-
-                /* There is a hypervisor, see what it is */
-                eax = 0x40000000U;
-                __asm__ __volatile__ (
-                        /* ebx/rbx is being used for PIC! */
-                        "  push %%"REG_b"         \n\t"
-                        "  cpuid                  \n\t"
-                        "  mov %%ebx, %1          \n\t"
-                        "  pop %%"REG_b"          \n\t"
-
-                        : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
-                        : "0" (eax)
-                );
-
-                NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
-                        if (streq(sig.text, j)) {
-
-                                if (id)
-                                        *id = k;
-
-                                return 1;
-                        }
-        }
-
-        for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
-                char *s;
-                int r;
-                const char *found = NULL;
-
-                if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) {
-                        if (r != -ENOENT)
-                                return r;
-
-                        continue;
-                }
-
-                NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
-                        if (startswith(s, j))
-                                found = k;
-                free(s);
-
-                if (found) {
-                        if (id)
-                                *id = found;
-
-                        return 1;
-                }
-        }
-
-        if (hypervisor) {
-                if (id)
-                        *id = "other";
-
-                return 1;
-        }
-
-#endif
-        return 0;
-}
-
-int detect_container(const char **id) {
-        FILE *f;
-
-        /* Unfortunately many of these operations require root access
-         * in one way or another */
-
-        if (geteuid() != 0)
-                return -EPERM;
-
-        if (running_in_chroot() > 0) {
-
-                if (id)
-                        *id = "chroot";
-
-                return 1;
-        }
-
-        /* /proc/vz exists in container and outside of the container,
-         * /proc/bc only outside of the container. */
-        if (access("/proc/vz", F_OK) >= 0 &&
-            access("/proc/bc", F_OK) < 0) {
-
-                if (id)
-                        *id = "openvz";
-
-                return 1;
-        }
-
-        f = fopen("/proc/1/environ", "re");
-        if (f) {
-                bool done = false;
-
-                do {
-                        char line[LINE_MAX];
-                        unsigned i;
-
-                        for (i = 0; i < sizeof(line)-1; i++) {
-                                int c;
-
-                                c = getc(f);
-                                if (_unlikely_(c == EOF)) {
-                                        done = true;
-                                        break;
-                                } else if (c == 0)
-                                        break;
-
-                                line[i] = c;
-                        }
-                        line[i] = 0;
-
-                        if (streq(line, "container=lxc")) {
-                                fclose(f);
-
-                                if (id)
-                                        *id = "lxc";
-                                return 1;
-
-                        } else if (streq(line, "container=lxc-libvirt")) {
-                                fclose(f);
-
-                                if (id)
-                                        *id = "lxc-libvirt";
-                                return 1;
-
-                        } else if (streq(line, "container=systemd-nspawn")) {
-                                fclose(f);
-
-                                if (id)
-                                        *id = "systemd-nspawn";
-                                return 1;
-
-                        } else if (startswith(line, "container=")) {
-                                fclose(f);
-
-                                if (id)
-                                        *id = "other";
-                                return 1;
-                        }
-
-                } while (!done);
-
-                fclose(f);
-        }
-
-        return 0;
-}
-
-/* Returns a short identifier for the various VM/container implementations */
-Virtualization detect_virtualization(const char **id) {
-
-        static __thread Virtualization cached_virt = _VIRTUALIZATION_INVALID;
-        static __thread const char *cached_id = NULL;
-
-        const char *_id;
-        int r;
-        Virtualization v;
-
-        if (_likely_(cached_virt >= 0)) {
-
-                if (id && cached_virt > 0)
-                        *id = cached_id;
-
-                return cached_virt;
-        }
-
-        r = detect_container(&_id);
-        if (r < 0) {
-                v = r;
-                goto finish;
-        } else if (r > 0) {
-                v = VIRTUALIZATION_CONTAINER;
-                goto finish;
-        }
-
-        r = detect_vm(&_id);
-        if (r < 0) {
-                v = r;
-                goto finish;
-        } else if (r > 0) {
-                v = VIRTUALIZATION_VM;
-                goto finish;
-        }
-
-        v = VIRTUALIZATION_NONE;
-
-finish:
-        if (v > 0) {
-                cached_id = _id;
-
-                if (id)
-                        *id = _id;
-        }
-
-        if (v >= 0)
-                cached_virt = v;
-
-        return v;
-}
diff --git a/src/virt.h b/src/virt.h
deleted file mode 100644
index f55c9a6..0000000
--- a/src/virt.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foovirthfoo
-#define foovirthfoo
-
-/***
-  This file is part of systemd.
-
-  Copyright 2011 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 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
-  General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-int detect_vm(const char **id);
-int detect_container(const char **id);
-
-typedef enum Virtualization {
-        VIRTUALIZATION_NONE = 0,
-        VIRTUALIZATION_VM,
-        VIRTUALIZATION_CONTAINER,
-        _VIRTUALIZATION_MAX,
-        _VIRTUALIZATION_INVALID = -1
-} Virtualization;
-
-Virtualization detect_virtualization(const char **id);
-
-#endif

commit 304b9f30781b8625c1f84fd27205f2707d4843b1
Author: Kay Sievers <kay at vrfy.org>
Date:   Tue Apr 10 13:33:43 2012 +0200

    udev: set errno = ENOSYS for removed interfaces

diff --git a/src/udev/libudev-monitor.c b/src/udev/libudev-monitor.c
index 5da41e7..5ff340a 100644
--- a/src/udev/libudev-monitor.c
+++ b/src/udev/libudev-monitor.c
@@ -107,6 +107,7 @@ static struct udev_monitor *udev_monitor_new(struct udev *udev)
  **/
 _public_ struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path)
 {
+        errno = ENOSYS;
         return NULL;
 }
 



More information about the systemd-commits mailing list