[systemd-commits] 2 commits - Makefile.am configure.ac src/login src/readahead src/readahead-collect.c src/readahead-common.c src/readahead-common.h src/readahead-replay.c src/sd-readahead.c src/sd-readahead.h src/user-sessions.c src/vconsole src/vconsole-setup.c

Lennart Poettering lennart at kemper.freedesktop.org
Sat Dec 31 10:45:48 PST 2011


 Makefile.am                       |  185 +++++-----
 configure.ac                      |   16 
 src/login/user-sessions.c         |  100 +++++
 src/readahead-collect.c           |  692 --------------------------------------
 src/readahead-common.c            |  269 --------------
 src/readahead-common.h            |   50 --
 src/readahead-replay.c            |  371 --------------------
 src/readahead/readahead-collect.c |  692 ++++++++++++++++++++++++++++++++++++++
 src/readahead/readahead-common.c  |  269 ++++++++++++++
 src/readahead/readahead-common.h  |   50 ++
 src/readahead/readahead-replay.c  |  371 ++++++++++++++++++++
 src/readahead/sd-readahead.c      |   76 ++++
 src/readahead/sd-readahead.h      |   81 ++++
 src/sd-readahead.c                |   76 ----
 src/sd-readahead.h                |   81 ----
 src/user-sessions.c               |  100 -----
 src/vconsole-setup.c              |  459 -------------------------
 src/vconsole/vconsole-setup.c     |  459 +++++++++++++++++++++++++
 18 files changed, 2219 insertions(+), 2178 deletions(-)

New commits:
commit 927f62bd54ee1c70b1c855b7def84169b950b5d0
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Dec 31 19:44:52 2011 +0100

    login: move systemd-user-sessions.service into login/, too

diff --git a/Makefile.am b/Makefile.am
index 496f730..d0b3f62 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -189,7 +189,6 @@ rootlibexec_PROGRAMS = \
 	systemd-remount-api-vfs \
 	systemd-kmsg-syslogd \
 	systemd-reply-password \
-	systemd-user-sessions \
 	systemd-fsck \
 	systemd-quotacheck \
 	systemd-timestamp \
@@ -339,7 +338,6 @@ nodist_systemunit_DATA = \
 	units/systemd-random-seed-load.service \
 	units/systemd-tmpfiles-setup.service \
 	units/systemd-tmpfiles-clean.service \
-	units/systemd-user-sessions.service \
 	units/systemd-ask-password-wall.service \
 	units/systemd-ask-password-console.service \
 	units/systemd-sysctl.service \
@@ -377,7 +375,6 @@ EXTRA_DIST += \
 	units/systemd-random-seed-load.service.in \
 	units/systemd-tmpfiles-setup.service.in \
 	units/systemd-tmpfiles-clean.service.in \
-	units/systemd-user-sessions.service.in \
 	units/systemd-ask-password-wall.service.in \
 	units/systemd-ask-password-console.service.in \
 	units/systemd-sysctl.service.in \
@@ -943,13 +940,6 @@ systemd_rc_local_generator_SOURCES = \
 systemd_rc_local_generator_LDADD = \
 	libsystemd-basic.la
 
-systemd_user_sessions_SOURCES = \
-	src/user-sessions.c \
-	src/cgroup-util.c
-
-systemd_user_sessions_LDADD = \
-	libsystemd-basic.la
-
 systemd_remount_api_vfs_SOURCES = \
 	src/remount-api-vfs.c \
 	src/mount-setup.c \
@@ -1702,8 +1692,16 @@ systemd_logind_LDADD = \
 	$(UDEV_LIBS) \
 	$(ACL_LIBS)
 
+systemd_user_sessions_SOURCES = \
+	src/login/user-sessions.c \
+	src/cgroup-util.c
+
+systemd_user_sessions_LDADD = \
+	libsystemd-basic.la
+
 rootlibexec_PROGRAMS += \
-	systemd-logind
+	systemd-logind \
+	systemd-user-sessions
 
 systemd_loginctl_SOURCES = \
 	src/login/loginctl.c \
@@ -1800,7 +1798,8 @@ UNINSTALL_EXEC_HOOKS += \
 	libsystemd-login-uninstall-hook
 
 nodist_systemunit_DATA += \
-	units/systemd-logind.service
+	units/systemd-logind.service \
+	units/systemd-user-sessions.service
 
 dist_dbussystemservice_DATA += \
 	src/login/org.freedesktop.login1.service
@@ -1828,8 +1827,9 @@ logind-install-data-hook:
 		rm -f dbus-org.freedesktop.login1.service && \
 		$(LN_S) systemd-logind.service dbus-org.freedesktop.login1.service)
 	( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \
-		rm -f systemd-logind.service && \
-		$(LN_S) ../systemd-logind.service systemd-logind.service )
+		rm -f systemd-logind.service systemd-user-sessions.service && \
+		$(LN_S) ../systemd-logind.service systemd-logind.service && \
+		$(LN_S) ../systemd-user-sessions.service systemd-user-sessions.service )
 
 INSTALL_DATA_HOOKS += \
 	logind-install-data-hook
@@ -1904,6 +1904,7 @@ man/sd_get_uids.3: man/sd_get_seats.3
 EXTRA_DIST += \
 	src/login/logind-gperf.gperf \
 	units/systemd-logind.service.in \
+	units/systemd-user-sessions.service.in \
 	src/login/libsystemd-login.pc.in \
 	src/login/libsystemd-login.sym \
 	src/login/logind.h \
@@ -2171,9 +2172,8 @@ systemd-install-data-hook:
 		$(LN_S) reboot.target ctrl-alt-del.target && \
 		$(LN_S) getty at .service autovt at .service )
 	( cd $(DESTDIR)$(systemunitdir)/multi-user.target.wants && \
-		rm -f getty.target systemd-user-sessions.service systemd-ask-password-wall.path && \
+		rm -f getty.target systemd-ask-password-wall.path && \
 		$(LN_S) ../getty.target getty.target && \
-		$(LN_S) ../systemd-user-sessions.service systemd-user-sessions.service && \
 		$(LN_S) ../systemd-ask-password-wall.path systemd-ask-password-wall.path)
 	( cd $(DESTDIR)$(pkgsysconfdir)/system/getty.target.wants && \
 		rm -f getty at tty1.service && \
diff --git a/src/login/user-sessions.c b/src/login/user-sessions.c
new file mode 100644
index 0000000..df46b76
--- /dev/null
+++ b/src/login/user-sessions.c
@@ -0,0 +1,100 @@
+/*-*- 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 <unistd.h>
+#include <errno.h>
+
+#include "log.h"
+#include "util.h"
+#include "cgroup-util.h"
+
+int main(int argc, char*argv[]) {
+        int ret = EXIT_FAILURE;
+
+        if (argc != 2) {
+                log_error("This program requires one argument.");
+                return EXIT_FAILURE;
+        }
+
+        log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        if (streq(argv[1], "start")) {
+                int q = 0, r = 0;
+
+                if (unlink("/run/nologin") < 0 && errno != ENOENT) {
+                        log_error("Failed to remove /run/nologin file: %m");
+                        r = -errno;
+                }
+
+                if (unlink("/etc/nologin") < 0 && errno != ENOENT) {
+
+                        /* If the file doesn't exist and /etc simply
+                         * was read-only (in which case unlink()
+                         * returns EROFS even if the file doesn't
+                         * exist), don't complain */
+
+                        if (errno != EROFS || access("/etc/nologin", F_OK) >= 0) {
+                                log_error("Failed to remove /etc/nologin file: %m");
+                                q = -errno;
+                        }
+                }
+
+                if (r < 0 || q < 0)
+                        goto finish;
+
+        } else if (streq(argv[1], "stop")) {
+                int r, q;
+                char *cgroup_user_tree = NULL;
+
+                if ((r = write_one_line_file_atomic("/run/nologin", "System is going down.")) < 0)
+                        log_error("Failed to create /run/nologin: %s", strerror(-r));
+
+                if ((q = cg_get_user_path(&cgroup_user_tree)) < 0) {
+                        log_error("Failed to determine use path: %s", strerror(-q));
+                        goto finish;
+                }
+
+                q = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, cgroup_user_tree, true);
+                free(cgroup_user_tree);
+
+                if (q < 0) {
+                        log_error("Failed to kill sessions: %s", strerror(-q));
+                        goto finish;
+                }
+
+                if (r < 0)
+                        goto finish;
+
+        } else {
+                log_error("Unknown verb %s.", argv[1]);
+                goto finish;
+        }
+
+        ret = EXIT_SUCCESS;
+
+finish:
+        return ret;
+}
diff --git a/src/user-sessions.c b/src/user-sessions.c
deleted file mode 100644
index df46b76..0000000
--- a/src/user-sessions.c
+++ /dev/null
@@ -1,100 +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 <unistd.h>
-#include <errno.h>
-
-#include "log.h"
-#include "util.h"
-#include "cgroup-util.h"
-
-int main(int argc, char*argv[]) {
-        int ret = EXIT_FAILURE;
-
-        if (argc != 2) {
-                log_error("This program requires one argument.");
-                return EXIT_FAILURE;
-        }
-
-        log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        if (streq(argv[1], "start")) {
-                int q = 0, r = 0;
-
-                if (unlink("/run/nologin") < 0 && errno != ENOENT) {
-                        log_error("Failed to remove /run/nologin file: %m");
-                        r = -errno;
-                }
-
-                if (unlink("/etc/nologin") < 0 && errno != ENOENT) {
-
-                        /* If the file doesn't exist and /etc simply
-                         * was read-only (in which case unlink()
-                         * returns EROFS even if the file doesn't
-                         * exist), don't complain */
-
-                        if (errno != EROFS || access("/etc/nologin", F_OK) >= 0) {
-                                log_error("Failed to remove /etc/nologin file: %m");
-                                q = -errno;
-                        }
-                }
-
-                if (r < 0 || q < 0)
-                        goto finish;
-
-        } else if (streq(argv[1], "stop")) {
-                int r, q;
-                char *cgroup_user_tree = NULL;
-
-                if ((r = write_one_line_file_atomic("/run/nologin", "System is going down.")) < 0)
-                        log_error("Failed to create /run/nologin: %s", strerror(-r));
-
-                if ((q = cg_get_user_path(&cgroup_user_tree)) < 0) {
-                        log_error("Failed to determine use path: %s", strerror(-q));
-                        goto finish;
-                }
-
-                q = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, cgroup_user_tree, true);
-                free(cgroup_user_tree);
-
-                if (q < 0) {
-                        log_error("Failed to kill sessions: %s", strerror(-q));
-                        goto finish;
-                }
-
-                if (r < 0)
-                        goto finish;
-
-        } else {
-                log_error("Unknown verb %s.", argv[1]);
-                goto finish;
-        }
-
-        ret = EXIT_SUCCESS;
-
-finish:
-        return ret;
-}

commit e5e83e8362e946890ac991fc86a2c5869f9befdf
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Dec 31 19:35:52 2011 +0100

    build-sys: make readahead and vconsole optional

diff --git a/Makefile.am b/Makefile.am
index 0d34fb3..496f730 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -96,7 +96,9 @@ AM_CPPFLAGS = \
 	-DUSER_GENERATOR_PATH=\"$(usergeneratordir)\" \
 	-DSYSTEM_SHUTDOWN_PATH=\"$(systemshutdowndir)\" \
 	-DSYSTEMD_KBD_MODEL_MAP=\"$(pkgdatadir)/kbd-model-map\" \
-	-I $(top_srcdir)/src
+	-I $(top_srcdir)/src \
+	-I $(top_srcdir)/src/readahead \
+	-I $(top_srcdir)/src/login
 
 if TARGET_GENTOO
 AM_CPPFLAGS += \
@@ -186,7 +188,6 @@ rootlibexec_PROGRAMS = \
 	systemd-modules-load \
 	systemd-remount-api-vfs \
 	systemd-kmsg-syslogd \
-	systemd-vconsole-setup \
 	systemd-reply-password \
 	systemd-user-sessions \
 	systemd-fsck \
@@ -331,7 +332,6 @@ nodist_systemunit_DATA = \
 	units/systemd-shutdownd.service \
 	units/systemd-kmsg-syslogd.service \
 	units/systemd-modules-load.service \
-	units/systemd-vconsole-setup.service \
 	units/systemd-remount-api-vfs.service \
 	units/systemd-update-utmp-runlevel.service \
 	units/systemd-update-utmp-shutdown.service \
@@ -370,7 +370,6 @@ EXTRA_DIST += \
 	units/systemd-shutdownd.service.in \
 	units/systemd-kmsg-syslogd.service.in \
 	units/systemd-modules-load.service.in \
-	units/systemd-vconsole-setup.service.in \
 	units/systemd-remount-api-vfs.service.in \
 	units/systemd-update-utmp-runlevel.service.in \
 	units/systemd-update-utmp-shutdown.service.in \
@@ -644,7 +643,6 @@ MANPAGES = \
 	man/hostname.5 \
 	man/timezone.5 \
 	man/machine-id.5 \
-	man/vconsole.conf.5 \
 	man/locale.conf.5 \
 	man/os-release.5 \
 	man/machine-info.5 \
@@ -952,12 +950,6 @@ systemd_user_sessions_SOURCES = \
 systemd_user_sessions_LDADD = \
 	libsystemd-basic.la
 
-systemd_vconsole_setup_SOURCES = \
-	src/vconsole-setup.c
-
-systemd_vconsole_setup_LDADD = \
-	libsystemd-basic.la
-
 systemd_remount_api_vfs_SOURCES = \
 	src/remount-api-vfs.c \
 	src/mount-setup.c \
@@ -1010,7 +1002,7 @@ systemctl_LDADD = \
 
 systemd_notify_SOURCES = \
 	src/notify.c \
-	src/sd-readahead.c
+	src/readahead/sd-readahead.c
 
 systemd_notify_LDADD = \
 	libsystemd-basic.la \
@@ -1108,56 +1100,6 @@ systemd_tty_ask_password_agent_LDADD = \
 	libsystemd-basic.la
 
 # ------------------------------------------------------------------------------
-systemd_readahead_collect_SOURCES = \
-	src/readahead-collect.c \
-	src/readahead-common.c
-
-systemd_readahead_collect_LDADD = \
-	libsystemd-basic.la \
-	libsystemd-daemon.la \
-	$(UDEV_LIBS)
-
-systemd_readahead_replay_SOURCES = \
-	src/readahead-replay.c \
-	src/readahead-common.c
-
-systemd_readahead_replay_CFLAGS = \
-	$(AM_CFLAGS) \
-	$(UDEV_CFLAGS)
-
-systemd_readahead_replay_LDADD = \
-	libsystemd-basic.la \
-	libsystemd-daemon.la \
-	$(UDEV_LIBS)
-
-rootlibexec_PROGRAMS += \
-	systemd-readahead-collect \
-	systemd-readahead-replay
-
-dist_systemunit_DATA += \
-	units/systemd-readahead-done.timer
-
-nodist_systemunit_DATA += \
-	units/systemd-readahead-collect.service \
-	units/systemd-readahead-replay.service \
-	units/systemd-readahead-done.service
-
-EXTRA_DIST += \
-	src/sd-readahead.h \
-	src/readahead-common.h \
-	units/systemd-readahead-collect.service.in \
-	units/systemd-readahead-replay.service.in \
-	units/systemd-readahead-done.service.in
-
-dist_doc_DATA += \
-	src/sd-readahead.h \
-	src/sd-readahead.c
-
-MANPAGES += \
-	man/sd_readahead.3 \
-	man/sd-readahead.7
-
-# ------------------------------------------------------------------------------
 libsystemd_daemon_la_SOURCES = \
 	src/sd-daemon.c
 
@@ -1480,6 +1422,91 @@ EXTRA_DIST += \
 endif
 
 # ------------------------------------------------------------------------------
+if ENABLE_VCONSOLE
+systemd_vconsole_setup_SOURCES = \
+	src/vconsole/vconsole-setup.c
+
+systemd_vconsole_setup_LDADD = \
+	libsystemd-basic.la
+
+rootlibexec_PROGRAMS += \
+	systemd-vconsole-setup
+
+nodist_systemunit_DATA += \
+	units/systemd-vconsole-setup.service
+
+vconsole-install-data-hook:
+	( cd $(DESTDIR)$(systemunitdir)/sysinit.target.wants && \
+		rm -f systemd-vconsole-setup.service && \
+		$(LN_S) ../systemd-vconsole-setup.service systemd-vconsole-setup.service )
+
+INSTALL_DATA_HOOKS += \
+	vconsole-install-data-hook
+
+MANPAGES += \
+	man/vconsole.conf.5
+
+EXTRA_DIST += \
+	units/systemd-vconsole-setup.service.in
+endif
+
+# ------------------------------------------------------------------------------
+if ENABLE_READAHEAD
+systemd_readahead_collect_SOURCES = \
+	src/readahead/readahead-collect.c \
+	src/readahead/readahead-common.c
+
+systemd_readahead_collect_LDADD = \
+	libsystemd-basic.la \
+	libsystemd-daemon.la \
+	$(UDEV_LIBS)
+
+systemd_readahead_collect_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(UDEV_CFLAGS)
+
+systemd_readahead_replay_SOURCES = \
+	src/readahead/readahead-replay.c \
+	src/readahead/readahead-common.c
+
+systemd_readahead_replay_CFLAGS = \
+	$(AM_CFLAGS) \
+	$(UDEV_CFLAGS)
+
+systemd_readahead_replay_LDADD = \
+	libsystemd-basic.la \
+	libsystemd-daemon.la \
+	$(UDEV_LIBS)
+
+rootlibexec_PROGRAMS += \
+	systemd-readahead-collect \
+	systemd-readahead-replay
+
+dist_systemunit_DATA += \
+	units/systemd-readahead-done.timer
+
+nodist_systemunit_DATA += \
+	units/systemd-readahead-collect.service \
+	units/systemd-readahead-replay.service \
+	units/systemd-readahead-done.service
+
+EXTRA_DIST += \
+	src/readahead/sd-readahead.h \
+	src/readahead/readahead-common.h \
+	units/systemd-readahead-collect.service.in \
+	units/systemd-readahead-replay.service.in \
+	units/systemd-readahead-done.service.in
+
+dist_doc_DATA += \
+	src/readahead/sd-readahead.h \
+	src/readahead/sd-readahead.c
+
+MANPAGES += \
+	man/sd_readahead.3 \
+	man/sd-readahead.7
+endif
+
+# ------------------------------------------------------------------------------
 if ENABLE_HOSTNAMED
 systemd_hostnamed_SOURCES = \
 	src/hostname/hostnamed.c \
@@ -2161,7 +2188,6 @@ systemd-install-data-hook:
 			sys-kernel-debug.mount \
 			sys-kernel-security.mount \
 			sys-fs-fuse-connections.mount \
-			systemd-vconsole-setup.service \
 			systemd-modules-load.service \
 			systemd-random-seed-load.service \
 			systemd-tmpfiles-setup.service \
@@ -2175,7 +2201,6 @@ systemd-install-data-hook:
 		$(LN_S) ../sys-kernel-debug.mount sys-kernel-debug.mount && \
 		$(LN_S) ../sys-kernel-security.mount sys-kernel-security.mount && \
 		$(LN_S) ../sys-fs-fuse-connections.mount sys-fs-fuse-connections.mount && \
-		$(LN_S) ../systemd-vconsole-setup.service systemd-vconsole-setup.service && \
 		$(LN_S) ../systemd-modules-load.service systemd-modules-load.service && \
 		$(LN_S) ../systemd-random-seed-load.service systemd-random-seed-load.service && \
 		$(LN_S) ../systemd-tmpfiles-setup.service systemd-tmpfiles-setup.service && \
diff --git a/configure.ac b/configure.ac
index 1f3bdc5..51278bd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -316,6 +316,20 @@ if test "x$enable_binfmt" != "xno"; then
 fi
 AM_CONDITIONAL(ENABLE_BINFMT, [test "$have_binfmt" = "yes"])
 
+have_vconsole=no
+AC_ARG_ENABLE(vconsole, AS_HELP_STRING([--disable-vconsole], [disable vconsole tool]))
+if test "x$enable_vconsole" != "xno"; then
+	have_vconsole=yes
+fi
+AM_CONDITIONAL(ENABLE_VCONSOLE, [test "$have_vconsole" = "yes"])
+
+have_readahead=no
+AC_ARG_ENABLE(readahead, AS_HELP_STRING([--disable-readahead], [disable readahead tools]))
+if test "x$enable_readahead" != "xno"; then
+	have_readahead=yes
+fi
+AM_CONDITIONAL(ENABLE_READAHEAD, [test "$have_readahead" = "yes"])
+
 have_logind=no
 AC_ARG_ENABLE(logind, AS_HELP_STRING([--disable-logind], [disable login daemon]))
 if test "x$enable_logind" != "xno"; then
@@ -611,6 +625,8 @@ AC_MSG_RESULT([
         XZ:                      ${have_xz}
         ACL:                     ${have_acl}
         binfmt:                  ${have_binfmt}
+        vconsole:                ${have_vconsole}
+        readahead:               ${have_readahead}
         logind:                  ${have_logind}
         hostnamed:               ${have_hostnamed}
         timedated:               ${have_timedated}
diff --git a/src/readahead-collect.c b/src/readahead-collect.c
deleted file mode 100644
index eac11e7..0000000
--- a/src/readahead-collect.c
+++ /dev/null
@@ -1,692 +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 <inttypes.h>
-#include <fcntl.h>
-#include <linux/limits.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/select.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <linux/fanotify.h>
-#include <sys/signalfd.h>
-#include <sys/poll.h>
-#include <sys/mman.h>
-#include <linux/fs.h>
-#include <linux/fiemap.h>
-#include <sys/ioctl.h>
-#include <sys/vfs.h>
-#include <getopt.h>
-#include <sys/inotify.h>
-
-#include "missing.h"
-#include "util.h"
-#include "set.h"
-#include "sd-daemon.h"
-#include "ioprio.h"
-#include "readahead-common.h"
-#include "virt.h"
-
-/* fixme:
- *
- * - detect ssd on btrfs/lvm...
- * - read ahead directories
- * - gzip?
- * - remount rw?
- * - handle files where nothing is in mincore
- * - does ioprio_set work with fadvise()?
- */
-
-static unsigned arg_files_max = 16*1024;
-static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX;
-static usec_t arg_timeout = 2*USEC_PER_MINUTE;
-
-static ReadaheadShared *shared = NULL;
-
-/* Avoid collisions with the NULL pointer */
-#define SECTOR_TO_PTR(s) ULONG_TO_PTR((s)+1)
-#define PTR_TO_SECTOR(p) (PTR_TO_ULONG(p)-1)
-
-static int btrfs_defrag(int fd) {
-        struct btrfs_ioctl_vol_args data;
-
-        zero(data);
-        data.fd = fd;
-
-        return ioctl(fd, BTRFS_IOC_DEFRAG, &data);
-}
-
-static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
-        struct stat st;
-        void *start = MAP_FAILED;
-        uint8_t *vec;
-        uint32_t b, c;
-        size_t l, pages;
-        bool mapped;
-        int r = 0, fd = -1, k;
-
-        assert(pack);
-        assert(fn);
-
-        if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) {
-
-                if (errno == ENOENT)
-                        return 0;
-
-                if (errno == EPERM || errno == EACCES)
-                        return 0;
-
-                log_warning("open(%s) failed: %m", fn);
-                r = -errno;
-                goto finish;
-        }
-
-        if ((k = file_verify(fd, fn, arg_file_size_max, &st)) <= 0) {
-                r = k;
-                goto finish;
-        }
-
-        if (on_btrfs)
-                btrfs_defrag(fd);
-
-        l = PAGE_ALIGN(st.st_size);
-        if ((start = mmap(NULL, l, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
-                log_warning("mmap(%s) failed: %m", fn);
-                r = -errno;
-                goto finish;
-        }
-
-        pages = l / page_size();
-
-        vec = alloca(pages);
-        memset(vec, 0, pages);
-        if (mincore(start, l, vec) < 0) {
-                log_warning("mincore(%s) failed: %m", fn);
-                r = -errno;
-                goto finish;
-        }
-
-        fputs(fn, pack);
-        fputc('\n', pack);
-
-        mapped = false;
-        for (c = 0; c < pages; c++) {
-                bool new_mapped = !!(vec[c] & 1);
-
-                if (!mapped && new_mapped)
-                        b = c;
-                else if (mapped && !new_mapped) {
-                        fwrite(&b, sizeof(b), 1, pack);
-                        fwrite(&c, sizeof(c), 1, pack);
-
-                        log_debug("%s: page %u to %u", fn, b, c);
-                }
-
-                mapped = new_mapped;
-        }
-
-        /* We don't write any range data if we should read the entire file */
-        if (mapped && b > 0) {
-                fwrite(&b, sizeof(b), 1, pack);
-                fwrite(&c, sizeof(c), 1, pack);
-
-                log_debug("%s: page %u to %u", fn, b, c);
-        }
-
-        /* End marker */
-        b = 0;
-        fwrite(&b, sizeof(b), 1, pack);
-        fwrite(&b, sizeof(b), 1, pack);
-
-finish:
-        if (start != MAP_FAILED)
-                munmap(start, l);
-
-        if (fd >= 0)
-                close_nointr_nofail(fd);
-
-        return r;
-}
-
-static unsigned long fd_first_block(int fd) {
-        struct {
-                struct fiemap fiemap;
-                struct fiemap_extent extent;
-        } data;
-
-        zero(data);
-        data.fiemap.fm_length = ~0ULL;
-        data.fiemap.fm_extent_count = 1;
-
-        if (ioctl(fd, FS_IOC_FIEMAP, &data) < 0)
-                return 0;
-
-        if (data.fiemap.fm_mapped_extents <= 0)
-                return 0;
-
-        if (data.fiemap.fm_extents[0].fe_flags & FIEMAP_EXTENT_UNKNOWN)
-                return 0;
-
-        return (unsigned long) data.fiemap.fm_extents[0].fe_physical;
-}
-
-struct item {
-        const char *path;
-        unsigned long block;
-};
-
-static int qsort_compare(const void *a, const void *b) {
-        const struct item *i, *j;
-
-        i = a;
-        j = b;
-
-        if (i->block < j->block)
-                return -1;
-        if (i->block > j->block)
-                return 1;
-
-        return strcmp(i->path, j->path);
-}
-
-static int collect(const char *root) {
-        enum {
-                FD_FANOTIFY,  /* Get the actual fs events */
-                FD_SIGNAL,
-                FD_INOTIFY,   /* We get notifications to quit early via this fd */
-                _FD_MAX
-        };
-        struct pollfd pollfd[_FD_MAX];
-        int fanotify_fd = -1, signal_fd = -1, inotify_fd = -1, r = 0;
-        pid_t my_pid;
-        Hashmap *files = NULL;
-        Iterator i;
-        char *p, *q;
-        sigset_t mask;
-        FILE *pack = NULL;
-        char *pack_fn_new = NULL, *pack_fn = NULL;
-        bool on_ssd, on_btrfs;
-        struct statfs sfs;
-        usec_t not_after;
-
-        assert(root);
-
-        write_one_line_file("/proc/self/oom_score_adj", "1000");
-
-        if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)) < 0)
-                log_warning("Failed to set IDLE IO priority class: %m");
-
-        assert_se(sigemptyset(&mask) == 0);
-        sigset_add_many(&mask, SIGINT, SIGTERM, -1);
-        assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
-
-        if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
-                log_error("signalfd(): %m");
-                r = -errno;
-                goto finish;
-        }
-
-        if (!(files = hashmap_new(string_hash_func, string_compare_func))) {
-                log_error("Failed to allocate set.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if ((fanotify_fd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME)) < 0)  {
-                log_error("Failed to create fanotify object: %m");
-                r = -errno;
-                goto finish;
-        }
-
-        if (fanotify_mark(fanotify_fd, FAN_MARK_ADD|FAN_MARK_MOUNT, FAN_OPEN, AT_FDCWD, root) < 0) {
-                log_error("Failed to mark %s: %m", root);
-                r = -errno;
-                goto finish;
-        }
-
-        if ((inotify_fd = open_inotify()) < 0) {
-                r = inotify_fd;
-                goto finish;
-        }
-
-        not_after = now(CLOCK_MONOTONIC) + arg_timeout;
-
-        my_pid = getpid();
-
-        zero(pollfd);
-        pollfd[FD_FANOTIFY].fd = fanotify_fd;
-        pollfd[FD_FANOTIFY].events = POLLIN;
-        pollfd[FD_SIGNAL].fd = signal_fd;
-        pollfd[FD_SIGNAL].events = POLLIN;
-        pollfd[FD_INOTIFY].fd = inotify_fd;
-        pollfd[FD_INOTIFY].events = POLLIN;
-
-        sd_notify(0,
-                  "READY=1\n"
-                  "STATUS=Collecting readahead data");
-
-        log_debug("Collecting...");
-
-        if (access("/run/systemd/readahead/cancel", F_OK) >= 0) {
-                log_debug("Collection canceled");
-                r = -ECANCELED;
-                goto finish;
-        }
-
-        if (access("/run/systemd/readahead/done", F_OK) >= 0) {
-                log_debug("Got termination request");
-                goto done;
-        }
-
-        for (;;) {
-                union {
-                        struct fanotify_event_metadata metadata;
-                        char buffer[4096];
-                } data;
-                ssize_t n;
-                struct fanotify_event_metadata *m;
-                usec_t t;
-                int h;
-
-                if (hashmap_size(files) > arg_files_max) {
-                        log_debug("Reached maximum number of read ahead files, ending collection.");
-                        break;
-                }
-
-                t = now(CLOCK_MONOTONIC);
-                if (t >= not_after) {
-                        log_debug("Reached maximum collection time, ending collection.");
-                        break;
-                }
-
-                if ((h = poll(pollfd, _FD_MAX, (int) ((not_after - t) / USEC_PER_MSEC))) < 0) {
-
-                        if (errno == EINTR)
-                                continue;
-
-                        log_error("poll(): %m");
-                        r = -errno;
-                        goto finish;
-                }
-
-                if (h == 0) {
-                        log_debug("Reached maximum collection time, ending collection.");
-                        break;
-                }
-
-                if (pollfd[FD_SIGNAL].revents) {
-                        log_debug("Got signal.");
-                        break;
-                }
-
-                if (pollfd[FD_INOTIFY].revents) {
-                        uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
-                        struct inotify_event *e;
-
-                        if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
-                                if (errno == EINTR || errno == EAGAIN)
-                                        continue;
-
-                                log_error("Failed to read inotify event: %m");
-                                r = -errno;
-                                goto finish;
-                        }
-
-                        e = (struct inotify_event*) inotify_buffer;
-                        while (n > 0) {
-                                size_t step;
-
-                                if ((e->mask & IN_CREATE) && streq(e->name, "cancel")) {
-                                        log_debug("Collection canceled");
-                                        r = -ECANCELED;
-                                        goto finish;
-                                }
-
-                                if ((e->mask & IN_CREATE) && streq(e->name, "done")) {
-                                        log_debug("Got termination request");
-                                        goto done;
-                                }
-
-                                step = sizeof(struct inotify_event) + e->len;
-                                assert(step <= (size_t) n);
-
-                                e = (struct inotify_event*) ((uint8_t*) e + step);
-                                n -= step;
-                        }
-                }
-
-                if ((n = read(fanotify_fd, &data, sizeof(data))) < 0) {
-
-                        if (errno == EINTR || errno == EAGAIN)
-                                continue;
-
-                        /* fanotify sometimes returns EACCES on read()
-                         * where it shouldn't. For now let's just
-                         * ignore it here (which is safe), but
-                         * eventually this should be
-                         * dropped when the kernel is fixed.
-                         *
-                         * https://bugzilla.redhat.com/show_bug.cgi?id=707577 */
-                        if (errno == EACCES)
-                                continue;
-
-                        log_error("Failed to read event: %m");
-                        r = -errno;
-                        goto finish;
-                }
-
-                for (m = &data.metadata; FAN_EVENT_OK(m, n); m = FAN_EVENT_NEXT(m, n)) {
-                        char fn[PATH_MAX];
-                        int k;
-
-                        if (m->fd < 0)
-                                goto next_iteration;
-
-                        if (m->pid == my_pid)
-                                goto next_iteration;
-
-                        __sync_synchronize();
-                        if (m->pid == shared->replay)
-                                goto next_iteration;
-
-                        snprintf(fn, sizeof(fn), "/proc/self/fd/%i", m->fd);
-                        char_array_0(fn);
-
-                        if ((k = readlink_malloc(fn, &p)) >= 0) {
-                                if (startswith(p, "/tmp") ||
-                                    endswith(p, " (deleted)") ||
-                                    hashmap_get(files, p))
-                                        /* Not interesting, or
-                                         * already read */
-                                        free(p);
-                                else {
-                                        unsigned long ul;
-
-                                        ul = fd_first_block(m->fd);
-
-                                        if ((k = hashmap_put(files, p, SECTOR_TO_PTR(ul))) < 0) {
-                                                log_warning("set_put() failed: %s", strerror(-k));
-                                                free(p);
-                                        }
-                                }
-
-                        } else
-                                log_warning("readlink(%s) failed: %s", fn, strerror(-k));
-
-                next_iteration:
-                        if (m->fd)
-                                close_nointr_nofail(m->fd);
-                }
-        }
-
-done:
-        if (fanotify_fd >= 0) {
-                close_nointr_nofail(fanotify_fd);
-                fanotify_fd = -1;
-        }
-
-        log_debug("Writing Pack File...");
-
-        on_ssd = fs_on_ssd(root) > 0;
-        log_debug("On SSD: %s", yes_no(on_ssd));
-
-        on_btrfs = statfs(root, &sfs) >= 0 && (long) sfs.f_type == (long) BTRFS_SUPER_MAGIC;
-        log_debug("On btrfs: %s", yes_no(on_btrfs));
-
-        asprintf(&pack_fn, "%s/.readahead", root);
-        asprintf(&pack_fn_new, "%s/.readahead.new", root);
-
-        if (!pack_fn || !pack_fn_new) {
-                log_error("Out of memory");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!(pack = fopen(pack_fn_new, "we"))) {
-                log_error("Failed to open pack file: %m");
-                r = -errno;
-                goto finish;
-        }
-
-        fputs(CANONICAL_HOST "\n", pack);
-        putc(on_ssd ? 'S' : 'R', pack);
-
-        if (on_ssd || on_btrfs) {
-
-                /* On SSD or on btrfs, just write things out in the
-                 * order the files were accessed. */
-
-                HASHMAP_FOREACH_KEY(q, p, files, i)
-                        pack_file(pack, p, on_btrfs);
-        } else {
-                struct item *ordered, *j;
-                unsigned k, n;
-
-                /* On rotating media, order things by the block
-                 * numbers */
-
-                log_debug("Ordering...");
-
-                n = hashmap_size(files);
-                if (!(ordered = new(struct item, n))) {
-                        log_error("Out of memory");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                j = ordered;
-                HASHMAP_FOREACH_KEY(q, p, files, i) {
-                        j->path = p;
-                        j->block = PTR_TO_SECTOR(q);
-                        j++;
-                }
-
-                assert(ordered + n == j);
-
-                qsort(ordered, n, sizeof(struct item), qsort_compare);
-
-                for (k = 0; k < n; k++)
-                        pack_file(pack, ordered[k].path, on_btrfs);
-
-                free(ordered);
-        }
-
-        log_debug("Finalizing...");
-
-        fflush(pack);
-
-        if (ferror(pack)) {
-                log_error("Failed to write pack file.");
-                r = -EIO;
-                goto finish;
-        }
-
-        if (rename(pack_fn_new, pack_fn) < 0) {
-                log_error("Failed to rename readahead file: %m");
-                r = -errno;
-                goto finish;
-        }
-
-        fclose(pack);
-        pack = NULL;
-
-        log_debug("Done.");
-
-finish:
-        if (fanotify_fd >= 0)
-                close_nointr_nofail(fanotify_fd);
-
-        if (signal_fd >= 0)
-                close_nointr_nofail(signal_fd);
-
-        if (inotify_fd >= 0)
-                close_nointr_nofail(inotify_fd);
-
-        if (pack) {
-                fclose(pack);
-                unlink(pack_fn_new);
-        }
-
-        free(pack_fn_new);
-        free(pack_fn);
-
-        while ((p = hashmap_steal_first_key(files)))
-                free(p);
-
-        hashmap_free(files);
-
-        return r;
-}
-
-static int help(void) {
-
-        printf("%s [OPTIONS...] [DIRECTORY]\n\n"
-               "Collect read-ahead data on early boot.\n\n"
-               "  -h --help                 Show this help\n"
-               "     --max-files=INT        Maximum number of files to read ahead\n"
-               "     --max-file-size=BYTES  Maximum size of files to read ahead\n"
-               "     --timeout=USEC         Maximum time to spend collecting data\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_FILES_MAX = 0x100,
-                ARG_FILE_SIZE_MAX,
-                ARG_TIMEOUT
-        };
-
-        static const struct option options[] = {
-                { "help",          no_argument,       NULL, 'h'                },
-                { "files-max",     required_argument, NULL, ARG_FILES_MAX      },
-                { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX  },
-                { "timeout",       required_argument, NULL, ARG_TIMEOUT        },
-                { NULL,            0,                 NULL, 0                  }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case ARG_FILES_MAX:
-                        if (safe_atou(optarg, &arg_files_max) < 0 || arg_files_max <= 0) {
-                                log_error("Failed to parse maximum number of files %s.", optarg);
-                                return -EINVAL;
-                        }
-                        break;
-
-                case ARG_FILE_SIZE_MAX: {
-                        unsigned long long ull;
-
-                        if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
-                                log_error("Failed to parse maximum file size %s.", optarg);
-                                return -EINVAL;
-                        }
-
-                        arg_file_size_max = (off_t) ull;
-                        break;
-                }
-
-                case ARG_TIMEOUT:
-                        if (parse_usec(optarg, &arg_timeout) < 0 || arg_timeout <= 0) {
-                                log_error("Failed to parse timeout %s.", optarg);
-                                return -EINVAL;
-                        }
-
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (optind != argc &&
-            optind != argc-1) {
-                help();
-                return -EINVAL;
-        }
-
-        return 1;
-}
-
-int main(int argc, char *argv[]) {
-        int r;
-        const char *root;
-
-        log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        if ((r = parse_argv(argc, argv)) <= 0)
-                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-
-        root = optind < argc ? argv[optind] : "/";
-
-        if (fs_on_read_only(root) > 0) {
-                log_info("Disabling readahead collector due to read-only media.");
-                return 0;
-        }
-
-        if (!enough_ram()) {
-                log_info("Disabling readahead collector due to low memory.");
-                return 0;
-        }
-
-        if (detect_virtualization(NULL) > 0) {
-                log_info("Disabling readahead collector due to execution in virtualized environment.");
-                return 0;
-        }
-
-        if (!(shared = shared_get()))
-                return 1;
-
-        shared->collect = getpid();
-        __sync_synchronize();
-
-        if (collect(root) < 0)
-                return 1;
-
-        return 0;
-}
diff --git a/src/readahead-common.c b/src/readahead-common.c
deleted file mode 100644
index 67214ec..0000000
--- a/src/readahead-common.c
+++ /dev/null
@@ -1,269 +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 <stdlib.h>
-#include <string.h>
-#include <sys/sysinfo.h>
-#include <sys/inotify.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <unistd.h>
-#include <libudev.h>
-
-#include "log.h"
-#include "readahead-common.h"
-#include "util.h"
-
-int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st) {
-        assert(fd >= 0);
-        assert(fn);
-        assert(st);
-
-        if (fstat(fd, st) < 0) {
-                log_warning("fstat(%s) failed: %m", fn);
-                return -errno;
-        }
-
-        if (!S_ISREG(st->st_mode)) {
-                log_debug("Not preloading special file %s", fn);
-                return 0;
-        }
-
-        if (st->st_size <= 0 || st->st_size > file_size_max) {
-                log_debug("Not preloading file %s with size out of bounds %llu", fn, (unsigned long long) st->st_size);
-                return 0;
-        }
-
-        return 1;
-}
-
-int fs_on_ssd(const char *p) {
-        struct stat st;
-        struct udev *udev = NULL;
-        struct udev_device *udev_device = NULL, *look_at = NULL;
-        bool b = false;
-        const char *devtype, *rotational, *model, *id;
-
-        assert(p);
-
-        if (stat(p, &st) < 0)
-                return -errno;
-
-        if (major(st.st_dev) == 0)
-                return false;
-
-        if (!(udev = udev_new()))
-                return -ENOMEM;
-
-        if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
-                goto finish;
-
-        if ((devtype = udev_device_get_property_value(udev_device, "DEVTYPE")) &&
-            streq(devtype, "partition"))
-                look_at = udev_device_get_parent(udev_device);
-        else
-                look_at = udev_device;
-
-        if (!look_at)
-                goto finish;
-
-        /* First, try high-level property */
-        if ((id = udev_device_get_property_value(look_at, "ID_SSD"))) {
-                b = streq(id, "1");
-                goto finish;
-        }
-
-        /* Second, try kernel attribute */
-        if ((rotational = udev_device_get_sysattr_value(look_at, "queue/rotational")))
-                if ((b = streq(rotational, "0")))
-                        goto finish;
-
-        /* Finally, fallback to heuristics */
-        if (!(look_at = udev_device_get_parent(look_at)))
-                goto finish;
-
-        if ((model = udev_device_get_sysattr_value(look_at, "model")))
-                b = !!strstr(model, "SSD");
-
-finish:
-        if (udev_device)
-                udev_device_unref(udev_device);
-
-        if (udev)
-                udev_unref(udev);
-
-        return b;
-}
-
-int fs_on_read_only(const char *p) {
-        struct stat st;
-        struct udev *udev = NULL;
-        struct udev_device *udev_device = NULL;
-        bool b = false;
-        const char *read_only;
-
-        assert(p);
-
-        if (stat(p, &st) < 0)
-                return -errno;
-
-        if (major(st.st_dev) == 0)
-                return false;
-
-        if (!(udev = udev_new()))
-                return -ENOMEM;
-
-        if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
-                goto finish;
-
-        if ((read_only = udev_device_get_sysattr_value(udev_device, "ro")))
-                if ((b = streq(read_only, "1")))
-                        goto finish;
-
-finish:
-        if (udev_device)
-                udev_device_unref(udev_device);
-
-        if (udev)
-                udev_unref(udev);
-
-        return b;
-}
-
-bool enough_ram(void) {
-        struct sysinfo si;
-
-        assert_se(sysinfo(&si) >= 0);
-
-        /* Enable readahead only with at least 128MB memory */
-        return si.totalram > 127 * 1024*1024 / si.mem_unit;
-}
-
-int open_inotify(void) {
-        int fd;
-
-        if ((fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
-                log_error("Failed to create inotify handle: %m");
-                return -errno;
-        }
-
-        mkdir("/run/systemd", 0755);
-        mkdir("/run/systemd/readahead", 0755);
-
-        if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
-                log_error("Failed to watch /run/systemd/readahead: %m");
-                close_nointr_nofail(fd);
-                return -errno;
-        }
-
-        return fd;
-}
-
-ReadaheadShared *shared_get(void) {
-        int fd;
-        ReadaheadShared *m = NULL;
-
-        mkdir("/run/systemd", 0755);
-        mkdir("/run/systemd/readahead", 0755);
-
-        if ((fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644)) < 0) {
-                log_error("Failed to create shared memory segment: %m");
-                goto finish;
-        }
-
-        if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
-                log_error("Failed to truncate shared memory segment: %m");
-                goto finish;
-        }
-
-        if ((m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
-                log_error("Failed to mmap shared memory segment: %m");
-                m = NULL;
-                goto finish;
-        }
-
-finish:
-        if (fd >= 0)
-                close_nointr_nofail(fd);
-
-        return m;
-}
-
-#define BUMP_REQUEST_NR (16*1024)
-
-int bump_request_nr(const char *p) {
-        struct stat st;
-        uint64_t u;
-        char *ap = NULL, *line = NULL;
-        int r;
-        dev_t d;
-
-        assert(p);
-
-        if (stat(p, &st) < 0)
-                return -errno;
-
-        if (major(st.st_dev) == 0)
-                return 0;
-
-        d = st.st_dev;
-        block_get_whole_disk(d, &d);
-
-        if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
-                r= -ENOMEM;
-                goto finish;
-        }
-
-        r = read_one_line_file(ap, &line);
-        if (r < 0) {
-                if (r == -ENOENT)
-                        r = 0;
-                goto finish;
-        }
-
-        r = safe_atou64(line, &u);
-        if (r >= 0 && u >= BUMP_REQUEST_NR) {
-                r = 0;
-                goto finish;
-        }
-
-        free(line);
-        line = NULL;
-
-        if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        r = write_one_line_file(ap, line);
-        if (r < 0)
-                goto finish;
-
-        log_info("Bumped block_nr parameter of %u:%u to %lu. This is a temporary hack and should be removed one day.", major(d), minor(d), (unsigned long) BUMP_REQUEST_NR);
-        r = 1;
-
-finish:
-        free(ap);
-        free(line);
-
-        return r;
-}
diff --git a/src/readahead-common.h b/src/readahead-common.h
deleted file mode 100644
index 9547ad2..0000000
--- a/src/readahead-common.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooreadaheadcommonhfoo
-#define fooreadaheadcommonhfoo
-
-/***
-  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/stat.h>
-#include <sys/types.h>
-
-#include "macro.h"
-
-#define READAHEAD_FILE_SIZE_MAX (10*1024*1024)
-
-int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st);
-
-int fs_on_ssd(const char *p);
-int fs_on_read_only(const char *p);
-
-bool enough_ram(void);
-
-int open_inotify(void);
-
-typedef struct ReadaheadShared {
-        pid_t collect;
-        pid_t replay;
-} _packed_ ReadaheadShared;
-
-ReadaheadShared *shared_get(void);
-
-int bump_request_nr(const char *p);
-
-#endif
diff --git a/src/readahead-replay.c b/src/readahead-replay.c
deleted file mode 100644
index 65011ac..0000000
--- a/src/readahead-replay.c
+++ /dev/null
@@ -1,371 +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 <inttypes.h>
-#include <fcntl.h>
-#include <linux/limits.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/select.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <sys/inotify.h>
-
-#include "missing.h"
-#include "util.h"
-#include "set.h"
-#include "sd-daemon.h"
-#include "ioprio.h"
-#include "readahead-common.h"
-#include "virt.h"
-
-static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX;
-
-static ReadaheadShared *shared = NULL;
-
-static int unpack_file(FILE *pack) {
-        char fn[PATH_MAX];
-        int r = 0, fd = -1;
-        bool any = false;
-        struct stat st;
-
-        assert(pack);
-
-        if (!fgets(fn, sizeof(fn), pack))
-                return 0;
-
-        char_array_0(fn);
-        truncate_nl(fn);
-
-        if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) {
-
-                if (errno != ENOENT && errno != EPERM && errno != EACCES)
-                        log_warning("open(%s) failed: %m", fn);
-
-        } else if (file_verify(fd, fn, arg_file_size_max, &st) <= 0) {
-                close_nointr_nofail(fd);
-                fd = -1;
-        }
-
-        for (;;) {
-                uint32_t b, c;
-
-                if (fread(&b, sizeof(b), 1, pack) != 1 ||
-                    fread(&c, sizeof(c), 1, pack) != 1) {
-                        log_error("Premature end of pack file.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                if (b == 0 && c == 0)
-                        break;
-
-                if (c <= b) {
-                        log_error("Invalid pack file.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                log_debug("%s: page %u to %u", fn, b, c);
-
-                any = true;
-
-                if (fd >= 0)
-                        if (posix_fadvise(fd, b * page_size(), (c - b) * page_size(), POSIX_FADV_WILLNEED) < 0) {
-                                log_warning("posix_fadvise() failed: %m");
-                                goto finish;
-                        }
-        }
-
-        if (!any && fd >= 0) {
-                /* if no range is encoded in the pack file this is
-                 * intended to mean that the whole file shall be
-                 * read */
-
-                if (posix_fadvise(fd, 0, st.st_size, POSIX_FADV_WILLNEED) < 0) {
-                        log_warning("posix_fadvise() failed: %m");
-                        goto finish;
-                }
-        }
-
-finish:
-        if (fd >= 0)
-                close_nointr_nofail(fd);
-
-        return r;
-}
-
-static int replay(const char *root) {
-        FILE *pack = NULL;
-        char line[LINE_MAX];
-        int r = 0;
-        char *pack_fn = NULL;
-        int c;
-        bool on_ssd, ready = false;
-        int prio;
-        int inotify_fd = -1;
-
-        assert(root);
-
-        write_one_line_file("/proc/self/oom_score_adj", "1000");
-        bump_request_nr(root);
-
-        if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
-                log_error("Out of memory");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if ((!(pack = fopen(pack_fn, "re")))) {
-                if (errno == ENOENT)
-                        log_debug("No pack file found.");
-                else {
-                        log_error("Failed to open pack file: %m");
-                        r = -errno;
-                }
-
-                goto finish;
-        }
-
-        posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
-
-        if ((inotify_fd = open_inotify()) < 0) {
-                r = inotify_fd;
-                goto finish;
-        }
-
-        if (!(fgets(line, sizeof(line), pack))) {
-                log_error("Premature end of pack file.");
-                r = -EIO;
-                goto finish;
-        }
-
-        char_array_0(line);
-
-        if (!streq(line, CANONICAL_HOST "\n")) {
-                log_debug("Pack file host type mismatch.");
-                goto finish;
-        }
-
-        if ((c = getc(pack)) == EOF) {
-                log_debug("Premature end of pack file.");
-                r = -EIO;
-                goto finish;
-        }
-
-        /* We do not retest SSD here, so that we can start replaying
-         * before udev is up.*/
-        on_ssd = c == 'S';
-        log_debug("On SSD: %s", yes_no(on_ssd));
-
-        if (on_ssd)
-                prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
-        else
-                prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT, 7);
-
-        if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
-                log_warning("Failed to set IDLE IO priority class: %m");
-
-        sd_notify(0, "STATUS=Replaying readahead data");
-
-        log_debug("Replaying...");
-
-        if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
-                log_debug("Got termination request");
-                goto done;
-        }
-
-        while (!feof(pack) && !ferror(pack)) {
-                uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
-                int k;
-                ssize_t n;
-
-                if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
-                        if (errno != EINTR && errno != EAGAIN) {
-                                log_error("Failed to read inotify event: %m");
-                                r = -errno;
-                                goto finish;
-                        }
-                } else {
-                        struct inotify_event *e = (struct inotify_event*) inotify_buffer;
-
-                        while (n > 0) {
-                                size_t step;
-
-                                if ((e->mask & IN_CREATE) && streq(e->name, "noreplay")) {
-                                        log_debug("Got termination request");
-                                        goto done;
-                                }
-
-                                step = sizeof(struct inotify_event) + e->len;
-                                assert(step <= (size_t) n);
-
-                                e = (struct inotify_event*) ((uint8_t*) e + step);
-                                n -= step;
-                        }
-                }
-
-                if ((k = unpack_file(pack)) < 0) {
-                        r = k;
-                        goto finish;
-                }
-
-                if (!ready) {
-                        /* We delay the ready notification until we
-                         * queued at least one read */
-                        sd_notify(0, "READY=1");
-                        ready = true;
-                }
-        }
-
-done:
-        if (!ready)
-                sd_notify(0, "READY=1");
-
-        if (ferror(pack)) {
-                log_error("Failed to read pack file.");
-                r = -EIO;
-                goto finish;
-        }
-
-        log_debug("Done.");
-
-finish:
-        if (pack)
-                fclose(pack);
-
-        if (inotify_fd >= 0)
-                close_nointr_nofail(inotify_fd);
-
-        free(pack_fn);
-
-        return r;
-}
-
-
-static int help(void) {
-
-        printf("%s [OPTIONS...] [DIRECTORY]\n\n"
-               "Replay collected read-ahead data on early boot.\n\n"
-               "  -h --help                 Show this help\n"
-               "     --max-file-size=BYTES  Maximum size of files to read ahead\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_FILE_SIZE_MAX
-        };
-
-        static const struct option options[] = {
-                { "help",          no_argument,       NULL, 'h'                },
-                { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX  },
-                { NULL,            0,                 NULL, 0                  }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case ARG_FILE_SIZE_MAX: {
-                        unsigned long long ull;
-
-                        if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
-                                log_error("Failed to parse maximum file size %s.", optarg);
-                                return -EINVAL;
-                        }
-
-                        arg_file_size_max = (off_t) ull;
-                        break;
-                }
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (optind != argc &&
-            optind != argc-1) {
-                help();
-                return -EINVAL;
-        }
-
-        return 1;
-}
-
-int main(int argc, char*argv[]) {
-        int r;
-        const char *root;
-
-        log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        if ((r = parse_argv(argc, argv)) <= 0)
-                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-
-        root = optind < argc ? argv[optind] : "/";
-
-        if (!enough_ram()) {
-                log_info("Disabling readahead replay due to low memory.");
-                return 0;
-        }
-
-        if (detect_virtualization(NULL) > 0) {
-                log_info("Disabling readahead replay due to execution in virtualized environment.");
-                return 0;
-        }
-
-        if (!(shared = shared_get()))
-                return 1;
-
-        shared->replay = getpid();
-        __sync_synchronize();
-
-        if (replay(root) < 0)
-                return 1;
-
-        return 0;
-}
diff --git a/src/readahead/readahead-collect.c b/src/readahead/readahead-collect.c
new file mode 100644
index 0000000..eac11e7
--- /dev/null
+++ b/src/readahead/readahead-collect.c
@@ -0,0 +1,692 @@
+/*-*- 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 <inttypes.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <linux/fanotify.h>
+#include <sys/signalfd.h>
+#include <sys/poll.h>
+#include <sys/mman.h>
+#include <linux/fs.h>
+#include <linux/fiemap.h>
+#include <sys/ioctl.h>
+#include <sys/vfs.h>
+#include <getopt.h>
+#include <sys/inotify.h>
+
+#include "missing.h"
+#include "util.h"
+#include "set.h"
+#include "sd-daemon.h"
+#include "ioprio.h"
+#include "readahead-common.h"
+#include "virt.h"
+
+/* fixme:
+ *
+ * - detect ssd on btrfs/lvm...
+ * - read ahead directories
+ * - gzip?
+ * - remount rw?
+ * - handle files where nothing is in mincore
+ * - does ioprio_set work with fadvise()?
+ */
+
+static unsigned arg_files_max = 16*1024;
+static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX;
+static usec_t arg_timeout = 2*USEC_PER_MINUTE;
+
+static ReadaheadShared *shared = NULL;
+
+/* Avoid collisions with the NULL pointer */
+#define SECTOR_TO_PTR(s) ULONG_TO_PTR((s)+1)
+#define PTR_TO_SECTOR(p) (PTR_TO_ULONG(p)-1)
+
+static int btrfs_defrag(int fd) {
+        struct btrfs_ioctl_vol_args data;
+
+        zero(data);
+        data.fd = fd;
+
+        return ioctl(fd, BTRFS_IOC_DEFRAG, &data);
+}
+
+static int pack_file(FILE *pack, const char *fn, bool on_btrfs) {
+        struct stat st;
+        void *start = MAP_FAILED;
+        uint8_t *vec;
+        uint32_t b, c;
+        size_t l, pages;
+        bool mapped;
+        int r = 0, fd = -1, k;
+
+        assert(pack);
+        assert(fn);
+
+        if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) {
+
+                if (errno == ENOENT)
+                        return 0;
+
+                if (errno == EPERM || errno == EACCES)
+                        return 0;
+
+                log_warning("open(%s) failed: %m", fn);
+                r = -errno;
+                goto finish;
+        }
+
+        if ((k = file_verify(fd, fn, arg_file_size_max, &st)) <= 0) {
+                r = k;
+                goto finish;
+        }
+
+        if (on_btrfs)
+                btrfs_defrag(fd);
+
+        l = PAGE_ALIGN(st.st_size);
+        if ((start = mmap(NULL, l, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+                log_warning("mmap(%s) failed: %m", fn);
+                r = -errno;
+                goto finish;
+        }
+
+        pages = l / page_size();
+
+        vec = alloca(pages);
+        memset(vec, 0, pages);
+        if (mincore(start, l, vec) < 0) {
+                log_warning("mincore(%s) failed: %m", fn);
+                r = -errno;
+                goto finish;
+        }
+
+        fputs(fn, pack);
+        fputc('\n', pack);
+
+        mapped = false;
+        for (c = 0; c < pages; c++) {
+                bool new_mapped = !!(vec[c] & 1);
+
+                if (!mapped && new_mapped)
+                        b = c;
+                else if (mapped && !new_mapped) {
+                        fwrite(&b, sizeof(b), 1, pack);
+                        fwrite(&c, sizeof(c), 1, pack);
+
+                        log_debug("%s: page %u to %u", fn, b, c);
+                }
+
+                mapped = new_mapped;
+        }
+
+        /* We don't write any range data if we should read the entire file */
+        if (mapped && b > 0) {
+                fwrite(&b, sizeof(b), 1, pack);
+                fwrite(&c, sizeof(c), 1, pack);
+
+                log_debug("%s: page %u to %u", fn, b, c);
+        }
+
+        /* End marker */
+        b = 0;
+        fwrite(&b, sizeof(b), 1, pack);
+        fwrite(&b, sizeof(b), 1, pack);
+
+finish:
+        if (start != MAP_FAILED)
+                munmap(start, l);
+
+        if (fd >= 0)
+                close_nointr_nofail(fd);
+
+        return r;
+}
+
+static unsigned long fd_first_block(int fd) {
+        struct {
+                struct fiemap fiemap;
+                struct fiemap_extent extent;
+        } data;
+
+        zero(data);
+        data.fiemap.fm_length = ~0ULL;
+        data.fiemap.fm_extent_count = 1;
+
+        if (ioctl(fd, FS_IOC_FIEMAP, &data) < 0)
+                return 0;
+
+        if (data.fiemap.fm_mapped_extents <= 0)
+                return 0;
+
+        if (data.fiemap.fm_extents[0].fe_flags & FIEMAP_EXTENT_UNKNOWN)
+                return 0;
+
+        return (unsigned long) data.fiemap.fm_extents[0].fe_physical;
+}
+
+struct item {
+        const char *path;
+        unsigned long block;
+};
+
+static int qsort_compare(const void *a, const void *b) {
+        const struct item *i, *j;
+
+        i = a;
+        j = b;
+
+        if (i->block < j->block)
+                return -1;
+        if (i->block > j->block)
+                return 1;
+
+        return strcmp(i->path, j->path);
+}
+
+static int collect(const char *root) {
+        enum {
+                FD_FANOTIFY,  /* Get the actual fs events */
+                FD_SIGNAL,
+                FD_INOTIFY,   /* We get notifications to quit early via this fd */
+                _FD_MAX
+        };
+        struct pollfd pollfd[_FD_MAX];
+        int fanotify_fd = -1, signal_fd = -1, inotify_fd = -1, r = 0;
+        pid_t my_pid;
+        Hashmap *files = NULL;
+        Iterator i;
+        char *p, *q;
+        sigset_t mask;
+        FILE *pack = NULL;
+        char *pack_fn_new = NULL, *pack_fn = NULL;
+        bool on_ssd, on_btrfs;
+        struct statfs sfs;
+        usec_t not_after;
+
+        assert(root);
+
+        write_one_line_file("/proc/self/oom_score_adj", "1000");
+
+        if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0)) < 0)
+                log_warning("Failed to set IDLE IO priority class: %m");
+
+        assert_se(sigemptyset(&mask) == 0);
+        sigset_add_many(&mask, SIGINT, SIGTERM, -1);
+        assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
+
+        if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
+                log_error("signalfd(): %m");
+                r = -errno;
+                goto finish;
+        }
+
+        if (!(files = hashmap_new(string_hash_func, string_compare_func))) {
+                log_error("Failed to allocate set.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if ((fanotify_fd = fanotify_init(FAN_CLOEXEC|FAN_NONBLOCK, O_RDONLY|O_LARGEFILE|O_CLOEXEC|O_NOATIME)) < 0)  {
+                log_error("Failed to create fanotify object: %m");
+                r = -errno;
+                goto finish;
+        }
+
+        if (fanotify_mark(fanotify_fd, FAN_MARK_ADD|FAN_MARK_MOUNT, FAN_OPEN, AT_FDCWD, root) < 0) {
+                log_error("Failed to mark %s: %m", root);
+                r = -errno;
+                goto finish;
+        }
+
+        if ((inotify_fd = open_inotify()) < 0) {
+                r = inotify_fd;
+                goto finish;
+        }
+
+        not_after = now(CLOCK_MONOTONIC) + arg_timeout;
+
+        my_pid = getpid();
+
+        zero(pollfd);
+        pollfd[FD_FANOTIFY].fd = fanotify_fd;
+        pollfd[FD_FANOTIFY].events = POLLIN;
+        pollfd[FD_SIGNAL].fd = signal_fd;
+        pollfd[FD_SIGNAL].events = POLLIN;
+        pollfd[FD_INOTIFY].fd = inotify_fd;
+        pollfd[FD_INOTIFY].events = POLLIN;
+
+        sd_notify(0,
+                  "READY=1\n"
+                  "STATUS=Collecting readahead data");
+
+        log_debug("Collecting...");
+
+        if (access("/run/systemd/readahead/cancel", F_OK) >= 0) {
+                log_debug("Collection canceled");
+                r = -ECANCELED;
+                goto finish;
+        }
+
+        if (access("/run/systemd/readahead/done", F_OK) >= 0) {
+                log_debug("Got termination request");
+                goto done;
+        }
+
+        for (;;) {
+                union {
+                        struct fanotify_event_metadata metadata;
+                        char buffer[4096];
+                } data;
+                ssize_t n;
+                struct fanotify_event_metadata *m;
+                usec_t t;
+                int h;
+
+                if (hashmap_size(files) > arg_files_max) {
+                        log_debug("Reached maximum number of read ahead files, ending collection.");
+                        break;
+                }
+
+                t = now(CLOCK_MONOTONIC);
+                if (t >= not_after) {
+                        log_debug("Reached maximum collection time, ending collection.");
+                        break;
+                }
+
+                if ((h = poll(pollfd, _FD_MAX, (int) ((not_after - t) / USEC_PER_MSEC))) < 0) {
+
+                        if (errno == EINTR)
+                                continue;
+
+                        log_error("poll(): %m");
+                        r = -errno;
+                        goto finish;
+                }
+
+                if (h == 0) {
+                        log_debug("Reached maximum collection time, ending collection.");
+                        break;
+                }
+
+                if (pollfd[FD_SIGNAL].revents) {
+                        log_debug("Got signal.");
+                        break;
+                }
+
+                if (pollfd[FD_INOTIFY].revents) {
+                        uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
+                        struct inotify_event *e;
+
+                        if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
+                                if (errno == EINTR || errno == EAGAIN)
+                                        continue;
+
+                                log_error("Failed to read inotify event: %m");
+                                r = -errno;
+                                goto finish;
+                        }
+
+                        e = (struct inotify_event*) inotify_buffer;
+                        while (n > 0) {
+                                size_t step;
+
+                                if ((e->mask & IN_CREATE) && streq(e->name, "cancel")) {
+                                        log_debug("Collection canceled");
+                                        r = -ECANCELED;
+                                        goto finish;
+                                }
+
+                                if ((e->mask & IN_CREATE) && streq(e->name, "done")) {
+                                        log_debug("Got termination request");
+                                        goto done;
+                                }
+
+                                step = sizeof(struct inotify_event) + e->len;
+                                assert(step <= (size_t) n);
+
+                                e = (struct inotify_event*) ((uint8_t*) e + step);
+                                n -= step;
+                        }
+                }
+
+                if ((n = read(fanotify_fd, &data, sizeof(data))) < 0) {
+
+                        if (errno == EINTR || errno == EAGAIN)
+                                continue;
+
+                        /* fanotify sometimes returns EACCES on read()
+                         * where it shouldn't. For now let's just
+                         * ignore it here (which is safe), but
+                         * eventually this should be
+                         * dropped when the kernel is fixed.
+                         *
+                         * https://bugzilla.redhat.com/show_bug.cgi?id=707577 */
+                        if (errno == EACCES)
+                                continue;
+
+                        log_error("Failed to read event: %m");
+                        r = -errno;
+                        goto finish;
+                }
+
+                for (m = &data.metadata; FAN_EVENT_OK(m, n); m = FAN_EVENT_NEXT(m, n)) {
+                        char fn[PATH_MAX];
+                        int k;
+
+                        if (m->fd < 0)
+                                goto next_iteration;
+
+                        if (m->pid == my_pid)
+                                goto next_iteration;
+
+                        __sync_synchronize();
+                        if (m->pid == shared->replay)
+                                goto next_iteration;
+
+                        snprintf(fn, sizeof(fn), "/proc/self/fd/%i", m->fd);
+                        char_array_0(fn);
+
+                        if ((k = readlink_malloc(fn, &p)) >= 0) {
+                                if (startswith(p, "/tmp") ||
+                                    endswith(p, " (deleted)") ||
+                                    hashmap_get(files, p))
+                                        /* Not interesting, or
+                                         * already read */
+                                        free(p);
+                                else {
+                                        unsigned long ul;
+
+                                        ul = fd_first_block(m->fd);
+
+                                        if ((k = hashmap_put(files, p, SECTOR_TO_PTR(ul))) < 0) {
+                                                log_warning("set_put() failed: %s", strerror(-k));
+                                                free(p);
+                                        }
+                                }
+
+                        } else
+                                log_warning("readlink(%s) failed: %s", fn, strerror(-k));
+
+                next_iteration:
+                        if (m->fd)
+                                close_nointr_nofail(m->fd);
+                }
+        }
+
+done:
+        if (fanotify_fd >= 0) {
+                close_nointr_nofail(fanotify_fd);
+                fanotify_fd = -1;
+        }
+
+        log_debug("Writing Pack File...");
+
+        on_ssd = fs_on_ssd(root) > 0;
+        log_debug("On SSD: %s", yes_no(on_ssd));
+
+        on_btrfs = statfs(root, &sfs) >= 0 && (long) sfs.f_type == (long) BTRFS_SUPER_MAGIC;
+        log_debug("On btrfs: %s", yes_no(on_btrfs));
+
+        asprintf(&pack_fn, "%s/.readahead", root);
+        asprintf(&pack_fn_new, "%s/.readahead.new", root);
+
+        if (!pack_fn || !pack_fn_new) {
+                log_error("Out of memory");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(pack = fopen(pack_fn_new, "we"))) {
+                log_error("Failed to open pack file: %m");
+                r = -errno;
+                goto finish;
+        }
+
+        fputs(CANONICAL_HOST "\n", pack);
+        putc(on_ssd ? 'S' : 'R', pack);
+
+        if (on_ssd || on_btrfs) {
+
+                /* On SSD or on btrfs, just write things out in the
+                 * order the files were accessed. */
+
+                HASHMAP_FOREACH_KEY(q, p, files, i)
+                        pack_file(pack, p, on_btrfs);
+        } else {
+                struct item *ordered, *j;
+                unsigned k, n;
+
+                /* On rotating media, order things by the block
+                 * numbers */
+
+                log_debug("Ordering...");
+
+                n = hashmap_size(files);
+                if (!(ordered = new(struct item, n))) {
+                        log_error("Out of memory");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                j = ordered;
+                HASHMAP_FOREACH_KEY(q, p, files, i) {
+                        j->path = p;
+                        j->block = PTR_TO_SECTOR(q);
+                        j++;
+                }
+
+                assert(ordered + n == j);
+
+                qsort(ordered, n, sizeof(struct item), qsort_compare);
+
+                for (k = 0; k < n; k++)
+                        pack_file(pack, ordered[k].path, on_btrfs);
+
+                free(ordered);
+        }
+
+        log_debug("Finalizing...");
+
+        fflush(pack);
+
+        if (ferror(pack)) {
+                log_error("Failed to write pack file.");
+                r = -EIO;
+                goto finish;
+        }
+
+        if (rename(pack_fn_new, pack_fn) < 0) {
+                log_error("Failed to rename readahead file: %m");
+                r = -errno;
+                goto finish;
+        }
+
+        fclose(pack);
+        pack = NULL;
+
+        log_debug("Done.");
+
+finish:
+        if (fanotify_fd >= 0)
+                close_nointr_nofail(fanotify_fd);
+
+        if (signal_fd >= 0)
+                close_nointr_nofail(signal_fd);
+
+        if (inotify_fd >= 0)
+                close_nointr_nofail(inotify_fd);
+
+        if (pack) {
+                fclose(pack);
+                unlink(pack_fn_new);
+        }
+
+        free(pack_fn_new);
+        free(pack_fn);
+
+        while ((p = hashmap_steal_first_key(files)))
+                free(p);
+
+        hashmap_free(files);
+
+        return r;
+}
+
+static int help(void) {
+
+        printf("%s [OPTIONS...] [DIRECTORY]\n\n"
+               "Collect read-ahead data on early boot.\n\n"
+               "  -h --help                 Show this help\n"
+               "     --max-files=INT        Maximum number of files to read ahead\n"
+               "     --max-file-size=BYTES  Maximum size of files to read ahead\n"
+               "     --timeout=USEC         Maximum time to spend collecting data\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_FILES_MAX = 0x100,
+                ARG_FILE_SIZE_MAX,
+                ARG_TIMEOUT
+        };
+
+        static const struct option options[] = {
+                { "help",          no_argument,       NULL, 'h'                },
+                { "files-max",     required_argument, NULL, ARG_FILES_MAX      },
+                { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX  },
+                { "timeout",       required_argument, NULL, ARG_TIMEOUT        },
+                { NULL,            0,                 NULL, 0                  }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_FILES_MAX:
+                        if (safe_atou(optarg, &arg_files_max) < 0 || arg_files_max <= 0) {
+                                log_error("Failed to parse maximum number of files %s.", optarg);
+                                return -EINVAL;
+                        }
+                        break;
+
+                case ARG_FILE_SIZE_MAX: {
+                        unsigned long long ull;
+
+                        if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
+                                log_error("Failed to parse maximum file size %s.", optarg);
+                                return -EINVAL;
+                        }
+
+                        arg_file_size_max = (off_t) ull;
+                        break;
+                }
+
+                case ARG_TIMEOUT:
+                        if (parse_usec(optarg, &arg_timeout) < 0 || arg_timeout <= 0) {
+                                log_error("Failed to parse timeout %s.", optarg);
+                                return -EINVAL;
+                        }
+
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (optind != argc &&
+            optind != argc-1) {
+                help();
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+        const char *root;
+
+        log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        if ((r = parse_argv(argc, argv)) <= 0)
+                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+        root = optind < argc ? argv[optind] : "/";
+
+        if (fs_on_read_only(root) > 0) {
+                log_info("Disabling readahead collector due to read-only media.");
+                return 0;
+        }
+
+        if (!enough_ram()) {
+                log_info("Disabling readahead collector due to low memory.");
+                return 0;
+        }
+
+        if (detect_virtualization(NULL) > 0) {
+                log_info("Disabling readahead collector due to execution in virtualized environment.");
+                return 0;
+        }
+
+        if (!(shared = shared_get()))
+                return 1;
+
+        shared->collect = getpid();
+        __sync_synchronize();
+
+        if (collect(root) < 0)
+                return 1;
+
+        return 0;
+}
diff --git a/src/readahead/readahead-common.c b/src/readahead/readahead-common.c
new file mode 100644
index 0000000..67214ec
--- /dev/null
+++ b/src/readahead/readahead-common.c
@@ -0,0 +1,269 @@
+/*-*- 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 <stdlib.h>
+#include <string.h>
+#include <sys/sysinfo.h>
+#include <sys/inotify.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <libudev.h>
+
+#include "log.h"
+#include "readahead-common.h"
+#include "util.h"
+
+int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st) {
+        assert(fd >= 0);
+        assert(fn);
+        assert(st);
+
+        if (fstat(fd, st) < 0) {
+                log_warning("fstat(%s) failed: %m", fn);
+                return -errno;
+        }
+
+        if (!S_ISREG(st->st_mode)) {
+                log_debug("Not preloading special file %s", fn);
+                return 0;
+        }
+
+        if (st->st_size <= 0 || st->st_size > file_size_max) {
+                log_debug("Not preloading file %s with size out of bounds %llu", fn, (unsigned long long) st->st_size);
+                return 0;
+        }
+
+        return 1;
+}
+
+int fs_on_ssd(const char *p) {
+        struct stat st;
+        struct udev *udev = NULL;
+        struct udev_device *udev_device = NULL, *look_at = NULL;
+        bool b = false;
+        const char *devtype, *rotational, *model, *id;
+
+        assert(p);
+
+        if (stat(p, &st) < 0)
+                return -errno;
+
+        if (major(st.st_dev) == 0)
+                return false;
+
+        if (!(udev = udev_new()))
+                return -ENOMEM;
+
+        if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
+                goto finish;
+
+        if ((devtype = udev_device_get_property_value(udev_device, "DEVTYPE")) &&
+            streq(devtype, "partition"))
+                look_at = udev_device_get_parent(udev_device);
+        else
+                look_at = udev_device;
+
+        if (!look_at)
+                goto finish;
+
+        /* First, try high-level property */
+        if ((id = udev_device_get_property_value(look_at, "ID_SSD"))) {
+                b = streq(id, "1");
+                goto finish;
+        }
+
+        /* Second, try kernel attribute */
+        if ((rotational = udev_device_get_sysattr_value(look_at, "queue/rotational")))
+                if ((b = streq(rotational, "0")))
+                        goto finish;
+
+        /* Finally, fallback to heuristics */
+        if (!(look_at = udev_device_get_parent(look_at)))
+                goto finish;
+
+        if ((model = udev_device_get_sysattr_value(look_at, "model")))
+                b = !!strstr(model, "SSD");
+
+finish:
+        if (udev_device)
+                udev_device_unref(udev_device);
+
+        if (udev)
+                udev_unref(udev);
+
+        return b;
+}
+
+int fs_on_read_only(const char *p) {
+        struct stat st;
+        struct udev *udev = NULL;
+        struct udev_device *udev_device = NULL;
+        bool b = false;
+        const char *read_only;
+
+        assert(p);
+
+        if (stat(p, &st) < 0)
+                return -errno;
+
+        if (major(st.st_dev) == 0)
+                return false;
+
+        if (!(udev = udev_new()))
+                return -ENOMEM;
+
+        if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
+                goto finish;
+
+        if ((read_only = udev_device_get_sysattr_value(udev_device, "ro")))
+                if ((b = streq(read_only, "1")))
+                        goto finish;
+
+finish:
+        if (udev_device)
+                udev_device_unref(udev_device);
+
+        if (udev)
+                udev_unref(udev);
+
+        return b;
+}
+
+bool enough_ram(void) {
+        struct sysinfo si;
+
+        assert_se(sysinfo(&si) >= 0);
+
+        /* Enable readahead only with at least 128MB memory */
+        return si.totalram > 127 * 1024*1024 / si.mem_unit;
+}
+
+int open_inotify(void) {
+        int fd;
+
+        if ((fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
+                log_error("Failed to create inotify handle: %m");
+                return -errno;
+        }
+
+        mkdir("/run/systemd", 0755);
+        mkdir("/run/systemd/readahead", 0755);
+
+        if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
+                log_error("Failed to watch /run/systemd/readahead: %m");
+                close_nointr_nofail(fd);
+                return -errno;
+        }
+
+        return fd;
+}
+
+ReadaheadShared *shared_get(void) {
+        int fd;
+        ReadaheadShared *m = NULL;
+
+        mkdir("/run/systemd", 0755);
+        mkdir("/run/systemd/readahead", 0755);
+
+        if ((fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644)) < 0) {
+                log_error("Failed to create shared memory segment: %m");
+                goto finish;
+        }
+
+        if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
+                log_error("Failed to truncate shared memory segment: %m");
+                goto finish;
+        }
+
+        if ((m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+                log_error("Failed to mmap shared memory segment: %m");
+                m = NULL;
+                goto finish;
+        }
+
+finish:
+        if (fd >= 0)
+                close_nointr_nofail(fd);
+
+        return m;
+}
+
+#define BUMP_REQUEST_NR (16*1024)
+
+int bump_request_nr(const char *p) {
+        struct stat st;
+        uint64_t u;
+        char *ap = NULL, *line = NULL;
+        int r;
+        dev_t d;
+
+        assert(p);
+
+        if (stat(p, &st) < 0)
+                return -errno;
+
+        if (major(st.st_dev) == 0)
+                return 0;
+
+        d = st.st_dev;
+        block_get_whole_disk(d, &d);
+
+        if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
+                r= -ENOMEM;
+                goto finish;
+        }
+
+        r = read_one_line_file(ap, &line);
+        if (r < 0) {
+                if (r == -ENOENT)
+                        r = 0;
+                goto finish;
+        }
+
+        r = safe_atou64(line, &u);
+        if (r >= 0 && u >= BUMP_REQUEST_NR) {
+                r = 0;
+                goto finish;
+        }
+
+        free(line);
+        line = NULL;
+
+        if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        r = write_one_line_file(ap, line);
+        if (r < 0)
+                goto finish;
+
+        log_info("Bumped block_nr parameter of %u:%u to %lu. This is a temporary hack and should be removed one day.", major(d), minor(d), (unsigned long) BUMP_REQUEST_NR);
+        r = 1;
+
+finish:
+        free(ap);
+        free(line);
+
+        return r;
+}
diff --git a/src/readahead/readahead-common.h b/src/readahead/readahead-common.h
new file mode 100644
index 0000000..9547ad2
--- /dev/null
+++ b/src/readahead/readahead-common.h
@@ -0,0 +1,50 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooreadaheadcommonhfoo
+#define fooreadaheadcommonhfoo
+
+/***
+  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/stat.h>
+#include <sys/types.h>
+
+#include "macro.h"
+
+#define READAHEAD_FILE_SIZE_MAX (10*1024*1024)
+
+int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st);
+
+int fs_on_ssd(const char *p);
+int fs_on_read_only(const char *p);
+
+bool enough_ram(void);
+
+int open_inotify(void);
+
+typedef struct ReadaheadShared {
+        pid_t collect;
+        pid_t replay;
+} _packed_ ReadaheadShared;
+
+ReadaheadShared *shared_get(void);
+
+int bump_request_nr(const char *p);
+
+#endif
diff --git a/src/readahead/readahead-replay.c b/src/readahead/readahead-replay.c
new file mode 100644
index 0000000..65011ac
--- /dev/null
+++ b/src/readahead/readahead-replay.c
@@ -0,0 +1,371 @@
+/*-*- 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 <inttypes.h>
+#include <fcntl.h>
+#include <linux/limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/inotify.h>
+
+#include "missing.h"
+#include "util.h"
+#include "set.h"
+#include "sd-daemon.h"
+#include "ioprio.h"
+#include "readahead-common.h"
+#include "virt.h"
+
+static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX;
+
+static ReadaheadShared *shared = NULL;
+
+static int unpack_file(FILE *pack) {
+        char fn[PATH_MAX];
+        int r = 0, fd = -1;
+        bool any = false;
+        struct stat st;
+
+        assert(pack);
+
+        if (!fgets(fn, sizeof(fn), pack))
+                return 0;
+
+        char_array_0(fn);
+        truncate_nl(fn);
+
+        if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) {
+
+                if (errno != ENOENT && errno != EPERM && errno != EACCES)
+                        log_warning("open(%s) failed: %m", fn);
+
+        } else if (file_verify(fd, fn, arg_file_size_max, &st) <= 0) {
+                close_nointr_nofail(fd);
+                fd = -1;
+        }
+
+        for (;;) {
+                uint32_t b, c;
+
+                if (fread(&b, sizeof(b), 1, pack) != 1 ||
+                    fread(&c, sizeof(c), 1, pack) != 1) {
+                        log_error("Premature end of pack file.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (b == 0 && c == 0)
+                        break;
+
+                if (c <= b) {
+                        log_error("Invalid pack file.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                log_debug("%s: page %u to %u", fn, b, c);
+
+                any = true;
+
+                if (fd >= 0)
+                        if (posix_fadvise(fd, b * page_size(), (c - b) * page_size(), POSIX_FADV_WILLNEED) < 0) {
+                                log_warning("posix_fadvise() failed: %m");
+                                goto finish;
+                        }
+        }
+
+        if (!any && fd >= 0) {
+                /* if no range is encoded in the pack file this is
+                 * intended to mean that the whole file shall be
+                 * read */
+
+                if (posix_fadvise(fd, 0, st.st_size, POSIX_FADV_WILLNEED) < 0) {
+                        log_warning("posix_fadvise() failed: %m");
+                        goto finish;
+                }
+        }
+
+finish:
+        if (fd >= 0)
+                close_nointr_nofail(fd);
+
+        return r;
+}
+
+static int replay(const char *root) {
+        FILE *pack = NULL;
+        char line[LINE_MAX];
+        int r = 0;
+        char *pack_fn = NULL;
+        int c;
+        bool on_ssd, ready = false;
+        int prio;
+        int inotify_fd = -1;
+
+        assert(root);
+
+        write_one_line_file("/proc/self/oom_score_adj", "1000");
+        bump_request_nr(root);
+
+        if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
+                log_error("Out of memory");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if ((!(pack = fopen(pack_fn, "re")))) {
+                if (errno == ENOENT)
+                        log_debug("No pack file found.");
+                else {
+                        log_error("Failed to open pack file: %m");
+                        r = -errno;
+                }
+
+                goto finish;
+        }
+
+        posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
+
+        if ((inotify_fd = open_inotify()) < 0) {
+                r = inotify_fd;
+                goto finish;
+        }
+
+        if (!(fgets(line, sizeof(line), pack))) {
+                log_error("Premature end of pack file.");
+                r = -EIO;
+                goto finish;
+        }
+
+        char_array_0(line);
+
+        if (!streq(line, CANONICAL_HOST "\n")) {
+                log_debug("Pack file host type mismatch.");
+                goto finish;
+        }
+
+        if ((c = getc(pack)) == EOF) {
+                log_debug("Premature end of pack file.");
+                r = -EIO;
+                goto finish;
+        }
+
+        /* We do not retest SSD here, so that we can start replaying
+         * before udev is up.*/
+        on_ssd = c == 'S';
+        log_debug("On SSD: %s", yes_no(on_ssd));
+
+        if (on_ssd)
+                prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
+        else
+                prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT, 7);
+
+        if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
+                log_warning("Failed to set IDLE IO priority class: %m");
+
+        sd_notify(0, "STATUS=Replaying readahead data");
+
+        log_debug("Replaying...");
+
+        if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
+                log_debug("Got termination request");
+                goto done;
+        }
+
+        while (!feof(pack) && !ferror(pack)) {
+                uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
+                int k;
+                ssize_t n;
+
+                if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
+                        if (errno != EINTR && errno != EAGAIN) {
+                                log_error("Failed to read inotify event: %m");
+                                r = -errno;
+                                goto finish;
+                        }
+                } else {
+                        struct inotify_event *e = (struct inotify_event*) inotify_buffer;
+
+                        while (n > 0) {
+                                size_t step;
+
+                                if ((e->mask & IN_CREATE) && streq(e->name, "noreplay")) {
+                                        log_debug("Got termination request");
+                                        goto done;
+                                }
+
+                                step = sizeof(struct inotify_event) + e->len;
+                                assert(step <= (size_t) n);
+
+                                e = (struct inotify_event*) ((uint8_t*) e + step);
+                                n -= step;
+                        }
+                }
+
+                if ((k = unpack_file(pack)) < 0) {
+                        r = k;
+                        goto finish;
+                }
+
+                if (!ready) {
+                        /* We delay the ready notification until we
+                         * queued at least one read */
+                        sd_notify(0, "READY=1");
+                        ready = true;
+                }
+        }
+
+done:
+        if (!ready)
+                sd_notify(0, "READY=1");
+
+        if (ferror(pack)) {
+                log_error("Failed to read pack file.");
+                r = -EIO;
+                goto finish;
+        }
+
+        log_debug("Done.");
+
+finish:
+        if (pack)
+                fclose(pack);
+
+        if (inotify_fd >= 0)
+                close_nointr_nofail(inotify_fd);
+
+        free(pack_fn);
+
+        return r;
+}
+
+
+static int help(void) {
+
+        printf("%s [OPTIONS...] [DIRECTORY]\n\n"
+               "Replay collected read-ahead data on early boot.\n\n"
+               "  -h --help                 Show this help\n"
+               "     --max-file-size=BYTES  Maximum size of files to read ahead\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_FILE_SIZE_MAX
+        };
+
+        static const struct option options[] = {
+                { "help",          no_argument,       NULL, 'h'                },
+                { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX  },
+                { NULL,            0,                 NULL, 0                  }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_FILE_SIZE_MAX: {
+                        unsigned long long ull;
+
+                        if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
+                                log_error("Failed to parse maximum file size %s.", optarg);
+                                return -EINVAL;
+                        }
+
+                        arg_file_size_max = (off_t) ull;
+                        break;
+                }
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (optind != argc &&
+            optind != argc-1) {
+                help();
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+int main(int argc, char*argv[]) {
+        int r;
+        const char *root;
+
+        log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        if ((r = parse_argv(argc, argv)) <= 0)
+                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+        root = optind < argc ? argv[optind] : "/";
+
+        if (!enough_ram()) {
+                log_info("Disabling readahead replay due to low memory.");
+                return 0;
+        }
+
+        if (detect_virtualization(NULL) > 0) {
+                log_info("Disabling readahead replay due to execution in virtualized environment.");
+                return 0;
+        }
+
+        if (!(shared = shared_get()))
+                return 1;
+
+        shared->replay = getpid();
+        __sync_synchronize();
+
+        if (replay(root) < 0)
+                return 1;
+
+        return 0;
+}
diff --git a/src/readahead/sd-readahead.c b/src/readahead/sd-readahead.c
new file mode 100644
index 0000000..c5cfe67
--- /dev/null
+++ b/src/readahead/sd-readahead.c
@@ -0,0 +1,76 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  Copyright 2010 Lennart Poettering
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "sd-readahead.h"
+
+static int touch(const char *path) {
+
+#if !defined(DISABLE_SYSTEMD) && defined(__linux__)
+        int fd;
+
+        mkdir("/run/systemd", 0755);
+        mkdir("/run/systemd/readahead", 0755);
+
+        if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0666)) < 0)
+                return -errno;
+
+        for (;;) {
+                if (close(fd) >= 0)
+                        break;
+
+                if (errno != -EINTR)
+                        return -errno;
+        }
+
+#endif
+        return 0;
+}
+
+int sd_readahead(const char *action) {
+
+        if (!action)
+                return -EINVAL;
+
+        if (strcmp(action, "cancel") == 0)
+                return touch("/run/systemd/readahead/cancel");
+        else if (strcmp(action, "done") == 0)
+                return touch("/run/systemd/readahead/done");
+        else if (strcmp(action, "noreplay") == 0)
+                return touch("/run/systemd/readahead/noreplay");
+
+        return -EINVAL;
+}
diff --git a/src/readahead/sd-readahead.h b/src/readahead/sd-readahead.h
new file mode 100644
index 0000000..5bf975a
--- /dev/null
+++ b/src/readahead/sd-readahead.h
@@ -0,0 +1,81 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foosdreadaheadhfoo
+#define foosdreadaheadhfoo
+
+/***
+  Copyright 2010 Lennart Poettering
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+***/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+  Reference implementation of a few boot readahead related
+  interfaces. These interfaces are trivial to implement. To simplify
+  porting we provide this reference implementation.  Applications are
+  welcome to reimplement the algorithms described here if they do not
+  want to include these two source files.
+
+  You may compile this with -DDISABLE_SYSTEMD to disable systemd
+  support. This makes all calls NOPs.
+
+  Since this is drop-in code we don't want any of our symbols to be
+  exported in any case. Hence we declare hidden visibility for all of
+  them.
+
+  You may find an up-to-date version of these source files online:
+
+  http://cgit.freedesktop.org/systemd/plain/src/sd-readahead.h
+  http://cgit.freedesktop.org/systemd/plain/src/sd-readahead.c
+
+  This should compile on non-Linux systems, too, but all functions
+  will become NOPs.
+
+  See sd-readahead(7) for more information.
+*/
+
+#ifndef _sd_hidden_
+#if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS)
+#define _sd_hidden_ __attribute__ ((visibility("hidden")))
+#else
+#define _sd_hidden_
+#endif
+#endif
+
+/*
+  Controls ongoing disk read-ahead operations during boot-up. The argument
+  must be a string, and either "cancel", "done" or "noreplay".
+
+  cancel = terminate read-ahead data collection, drop collected information
+  done = terminate read-ahead data collection, keep collected information
+  noreplay = terminate read-ahead replay
+*/
+int sd_readahead(const char *action) _sd_hidden_;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/sd-readahead.c b/src/sd-readahead.c
deleted file mode 100644
index c5cfe67..0000000
--- a/src/sd-readahead.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  Copyright 2010 Lennart Poettering
-
-  Permission is hereby granted, free of charge, to any person
-  obtaining a copy of this software and associated documentation files
-  (the "Software"), to deal in the Software without restriction,
-  including without limitation the rights to use, copy, modify, merge,
-  publish, distribute, sublicense, and/or sell copies of the Software,
-  and to permit persons to whom the Software is furnished to do so,
-  subject to the following conditions:
-
-  The above copyright notice and this permission notice shall be
-  included in all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-  SOFTWARE.
-***/
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <unistd.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <string.h>
-
-#include "sd-readahead.h"
-
-static int touch(const char *path) {
-
-#if !defined(DISABLE_SYSTEMD) && defined(__linux__)
-        int fd;
-
-        mkdir("/run/systemd", 0755);
-        mkdir("/run/systemd/readahead", 0755);
-
-        if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0666)) < 0)
-                return -errno;
-
-        for (;;) {
-                if (close(fd) >= 0)
-                        break;
-
-                if (errno != -EINTR)
-                        return -errno;
-        }
-
-#endif
-        return 0;
-}
-
-int sd_readahead(const char *action) {
-
-        if (!action)
-                return -EINVAL;
-
-        if (strcmp(action, "cancel") == 0)
-                return touch("/run/systemd/readahead/cancel");
-        else if (strcmp(action, "done") == 0)
-                return touch("/run/systemd/readahead/done");
-        else if (strcmp(action, "noreplay") == 0)
-                return touch("/run/systemd/readahead/noreplay");
-
-        return -EINVAL;
-}
diff --git a/src/sd-readahead.h b/src/sd-readahead.h
deleted file mode 100644
index 5bf975a..0000000
--- a/src/sd-readahead.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef foosdreadaheadhfoo
-#define foosdreadaheadhfoo
-
-/***
-  Copyright 2010 Lennart Poettering
-
-  Permission is hereby granted, free of charge, to any person
-  obtaining a copy of this software and associated documentation files
-  (the "Software"), to deal in the Software without restriction,
-  including without limitation the rights to use, copy, modify, merge,
-  publish, distribute, sublicense, and/or sell copies of the Software,
-  and to permit persons to whom the Software is furnished to do so,
-  subject to the following conditions:
-
-  The above copyright notice and this permission notice shall be
-  included in all copies or substantial portions of the Software.
-
-  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-  SOFTWARE.
-***/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
-  Reference implementation of a few boot readahead related
-  interfaces. These interfaces are trivial to implement. To simplify
-  porting we provide this reference implementation.  Applications are
-  welcome to reimplement the algorithms described here if they do not
-  want to include these two source files.
-
-  You may compile this with -DDISABLE_SYSTEMD to disable systemd
-  support. This makes all calls NOPs.
-
-  Since this is drop-in code we don't want any of our symbols to be
-  exported in any case. Hence we declare hidden visibility for all of
-  them.
-
-  You may find an up-to-date version of these source files online:
-
-  http://cgit.freedesktop.org/systemd/plain/src/sd-readahead.h
-  http://cgit.freedesktop.org/systemd/plain/src/sd-readahead.c
-
-  This should compile on non-Linux systems, too, but all functions
-  will become NOPs.
-
-  See sd-readahead(7) for more information.
-*/
-
-#ifndef _sd_hidden_
-#if (__GNUC__ >= 4) && !defined(SD_EXPORT_SYMBOLS)
-#define _sd_hidden_ __attribute__ ((visibility("hidden")))
-#else
-#define _sd_hidden_
-#endif
-#endif
-
-/*
-  Controls ongoing disk read-ahead operations during boot-up. The argument
-  must be a string, and either "cancel", "done" or "noreplay".
-
-  cancel = terminate read-ahead data collection, drop collected information
-  done = terminate read-ahead data collection, keep collected information
-  noreplay = terminate read-ahead replay
-*/
-int sd_readahead(const char *action) _sd_hidden_;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/src/vconsole-setup.c b/src/vconsole-setup.c
deleted file mode 100644
index 9196789..0000000
--- a/src/vconsole-setup.c
+++ /dev/null
@@ -1,459 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Kay Sievers
-
-  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 <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <stdbool.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <locale.h>
-#include <langinfo.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#include <linux/tiocl.h>
-#include <linux/kd.h>
-
-#include "util.h"
-#include "log.h"
-#include "macro.h"
-#include "virt.h"
-
-static bool is_vconsole(int fd) {
-        unsigned char data[1];
-
-        data[0] = TIOCL_GETFGCONSOLE;
-        return ioctl(fd, TIOCLINUX, data) >= 0;
-}
-
-static bool is_locale_utf8(void) {
-        const char *set;
-
-        if (!setlocale(LC_ALL, ""))
-                return true;
-
-        set = nl_langinfo(CODESET);
-        if (!set)
-                return true;
-
-        return streq(set, "UTF-8");
-}
-
-static int disable_utf8(int fd) {
-        int r = 0, k;
-
-        if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
-                r = -errno;
-
-        if (loop_write(fd, "\033%@", 3, false) < 0)
-                r = -errno;
-
-        if ((k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0")) < 0)
-                r = k;
-
-        if (r < 0)
-                log_warning("Failed to disable UTF-8: %s", strerror(errno));
-
-        return r;
-}
-
-static int load_keymap(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
-        const char *args[8];
-        int i = 0;
-        pid_t pid;
-
-        if (isempty(map)) {
-                /* An empty map means kernel map */
-                *_pid = 0;
-                return 0;
-        }
-
-        args[i++] = KBD_LOADKEYS;
-        args[i++] = "-q";
-        args[i++] = "-C";
-        args[i++] = vc;
-        if (utf8)
-                args[i++] = "-u";
-        args[i++] = map;
-        if (map_toggle)
-                args[i++] = map_toggle;
-        args[i++] = NULL;
-
-        if ((pid = fork()) < 0) {
-                log_error("Failed to fork: %m");
-                return -errno;
-        } else if (pid == 0) {
-                execv(args[0], (char **) args);
-                _exit(EXIT_FAILURE);
-        }
-
-        *_pid = pid;
-        return 0;
-}
-
-static int load_font(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
-        const char *args[9];
-        int i = 0;
-        pid_t pid;
-
-        if (isempty(font)) {
-                /* An empty font means kernel font */
-                *_pid = 0;
-                return 0;
-        }
-
-        args[i++] = KBD_SETFONT;
-        args[i++] = "-C";
-        args[i++] = vc;
-        args[i++] = font;
-        if (map) {
-                args[i++] = "-m";
-                args[i++] = map;
-        }
-        if (unimap) {
-                args[i++] = "-u";
-                args[i++] = unimap;
-        }
-        args[i++] = NULL;
-
-        if ((pid = fork()) < 0) {
-                log_error("Failed to fork: %m");
-                return -errno;
-        } else if (pid == 0) {
-                execv(args[0], (char **) args);
-                _exit(EXIT_FAILURE);
-        }
-
-        *_pid = pid;
-        return 0;
-}
-
-int main(int argc, char **argv) {
-        const char *vc;
-        char *vc_keymap = NULL;
-        char *vc_keymap_toggle = NULL;
-        char *vc_font = NULL;
-        char *vc_font_map = NULL;
-        char *vc_font_unimap = NULL;
-#ifdef TARGET_GENTOO
-        char *vc_unicode = NULL;
-#endif
-#if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
-        char *vc_keytable = NULL;
-#endif
-        int fd = -1;
-        bool utf8;
-        int r = EXIT_FAILURE;
-        pid_t font_pid = 0, keymap_pid = 0;
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        if (argv[1])
-                vc = argv[1];
-        else
-                vc = "/dev/tty0";
-
-        if ((fd = open_terminal(vc, O_RDWR|O_CLOEXEC)) < 0) {
-                log_error("Failed to open %s: %m", vc);
-                goto finish;
-        }
-
-        if (!is_vconsole(fd)) {
-                log_error("Device %s is not a virtual console.", vc);
-                goto finish;
-        }
-
-        utf8 = is_locale_utf8();
-
-        vc_keymap = strdup("us");
-        vc_font = strdup(DEFAULT_FONT);
-
-        if (!vc_keymap || !vc_font) {
-                log_error("Failed to allocate strings.");
-                goto finish;
-        }
-
-        r = 0;
-
-        if (detect_container(NULL) <= 0)
-                if ((r = parse_env_file("/proc/cmdline", WHITESPACE,
-                                        "vconsole.keymap", &vc_keymap,
-                                        "vconsole.keymap.toggle", &vc_keymap_toggle,
-                                        "vconsole.font", &vc_font,
-                                        "vconsole.font.map", &vc_font_map,
-                                        "vconsole.font.unimap", &vc_font_unimap,
-                                        NULL)) < 0) {
-
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
-                }
-
-        /* Hmm, nothing set on the kernel cmd line? Then let's
-         * try /etc/vconsole.conf */
-        if (r <= 0 &&
-            (r = parse_env_file("/etc/vconsole.conf", NEWLINE,
-                                "KEYMAP", &vc_keymap,
-                                "KEYMAP_TOGGLE", &vc_keymap_toggle,
-                                "FONT", &vc_font,
-                                "FONT_MAP", &vc_font_map,
-                                "FONT_UNIMAP", &vc_font_unimap,
-                                NULL)) < 0) {
-
-                if (r != -ENOENT)
-                        log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
-        }
-
-        if (r <= 0) {
-#if defined(TARGET_FEDORA) || defined(TARGET_MEEGO)
-                if ((r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
-                                        "SYSFONT", &vc_font,
-                                        "SYSFONTACM", &vc_font_map,
-                                        "UNIMAP", &vc_font_unimap,
-                                        NULL)) < 0) {
-
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
-                }
-
-                if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
-                                        "KEYTABLE", &vc_keymap,
-                                        "KEYMAP", &vc_keymap,
-                                        NULL)) < 0) {
-
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
-                }
-
-                if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
-                        char *t;
-
-                        if (!(t = strdup("/etc/sysconfig/console/default.kmap"))) {
-                                log_error("Out of memory.");
-                                goto finish;
-                        }
-
-                        free(vc_keymap);
-                        vc_keymap = t;
-                }
-
-#elif defined(TARGET_SUSE)
-                if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
-                                        "KEYTABLE", &vc_keymap,
-                                        NULL)) < 0) {
-
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
-                }
-
-                if ((r = parse_env_file("/etc/sysconfig/console", NEWLINE,
-                                        "CONSOLE_FONT", &vc_font,
-                                        "CONSOLE_SCREENMAP", &vc_font_map,
-                                        "CONSOLE_UNICODEMAP", &vc_font_unimap,
-                                        NULL)) < 0) {
-
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/sysconfig/console: %s", strerror(-r));
-                }
-
-#elif defined(TARGET_ARCH)
-                if ((r = parse_env_file("/etc/rc.conf", NEWLINE,
-                                        "KEYMAP", &vc_keymap,
-                                        "CONSOLEFONT", &vc_font,
-                                        "CONSOLEMAP", &vc_font_map,
-                                        NULL)) < 0) {
-
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
-                }
-
-#elif defined(TARGET_FRUGALWARE)
-                if ((r = parse_env_file("/etc/sysconfig/keymap", NEWLINE,
-                                        "keymap", &vc_keymap,
-                                        NULL)) < 0) {
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/sysconfig/keymap: %s", strerror(-r));
-                }
-                if ((r = parse_env_file("/etc/sysconfig/font", NEWLINE,
-                                        "font", &vc_font,
-                                        NULL)) < 0) {
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/sysconfig/font: %s", strerror(-r));
-                }
-
-#elif defined(TARGET_ALTLINUX)
-                if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
-                                        "KEYTABLE", &vc_keymap,
-                                        NULL)) < 0) {
-
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
-                }
-
-                if ((r = parse_env_file("/etc/sysconfig/consolefont", NEWLINE,
-                                        "SYSFONT", &vc_font,
-                                        NULL)) < 0) {
-
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/sysconfig/console: %s", strerror(-r));
-                }
-
-#elif defined(TARGET_GENTOO)
-                if ((r = parse_env_file("/etc/rc.conf", NEWLINE,
-                                        "unicode", &vc_unicode,
-                                        NULL)) < 0) {
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
-                }
-
-                if (vc_unicode) {
-                        int rc_unicode;
-
-                        if ((rc_unicode = parse_boolean(vc_unicode)) < 0)
-                                log_error("Unknown value for /etc/rc.conf unicode=%s", vc_unicode);
-                        else {
-                                if (rc_unicode && !utf8)
-                                        log_warning("/etc/rc.conf wants unicode, but current locale is not UTF-8 capable!");
-                                else if (!rc_unicode && utf8) {
-                                        log_debug("/etc/rc.conf does not want unicode, leave it on in kernel but does not apply to vconsole.");
-                                        utf8 = false;
-                                }
-                        }
-                }
-
-                /* /etc/conf.d/consolefont comments and gentoo
-                 * documentation mention uppercase, but the actual
-                 * contents are lowercase.  the existing
-                 * /etc/init.d/consolefont tries both
-                 */
-                if ((r = parse_env_file("/etc/conf.d/consolefont", NEWLINE,
-                                        "CONSOLEFONT", &vc_font,
-                                        "consolefont", &vc_font,
-                                        "consoletranslation", &vc_font_map,
-                                        "CONSOLETRANSLATION", &vc_font_map,
-                                        "unicodemap", &vc_font_unimap,
-                                        "UNICODEMAP", &vc_font_unimap,
-                                        NULL)) < 0) {
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/conf.d/consolefont: %s", strerror(-r));
-                }
-
-                if ((r = parse_env_file("/etc/conf.d/keymaps", NEWLINE,
-                                        "keymap", &vc_keymap,
-                                        "KEYMAP", &vc_keymap,
-                                        NULL)) < 0) {
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/conf.d/keymaps: %s", strerror(-r));
-                }
-
-#elif defined(TARGET_MANDRIVA) || defined (TARGET_MAGEIA)
-
-                if ((r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
-                                        "SYSFONT", &vc_font,
-                                        "SYSFONTACM", &vc_font_map,
-                                        "UNIMAP", &vc_font_unimap,
-                                        NULL)) < 0) {
-
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
-                }
-
-                if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
-                                        "KEYTABLE", &vc_keytable,
-                                        "KEYMAP", &vc_keymap,
-                                        "UNIKEYTABLE", &vc_keymap,
-                                        "GRP_TOGGLE", &vc_keymap_toggle,
-                                        NULL)) < 0) {
-
-                        if (r != -ENOENT)
-                                log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
-                }
-
-                if (vc_keytable) {
-                        if (vc_keymap)
-                                free(vc_keymap);
-                        if (utf8) {
-                                if (endswith(vc_keytable, ".uni") || strstr(vc_keytable, ".uni."))
-                                        vc_keymap = strdup(vc_keytable);
-                                else {
-                                        char *s;
-                                        if ((s = strstr(vc_keytable, ".map")))
-                                                vc_keytable[s-vc_keytable+1] = '\0';
-                                        vc_keymap = strappend(vc_keytable, ".uni");
-                                }
-                        } else
-                                vc_keymap = strdup(vc_keytable);
-
-                        free(vc_keytable);
-
-                        if (!vc_keymap) {
-                                log_error("Out of memory.");
-                                goto finish;
-                        }
-                }
-
-                if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
-                        char *t;
-
-                        if (!(t = strdup("/etc/sysconfig/console/default.kmap"))) {
-                                log_error("Out of memory.");
-                                goto finish;
-                        }
-
-                        free(vc_keymap);
-                        vc_keymap = t;
-                }
-#endif
-        }
-
-        r = EXIT_FAILURE;
-
-        if (!utf8)
-                disable_utf8(fd);
-
-        if (load_keymap(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
-            load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
-                r = EXIT_SUCCESS;
-
-finish:
-        if (keymap_pid > 0)
-                wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
-
-        if (font_pid > 0)
-                wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
-
-        free(vc_keymap);
-        free(vc_font);
-        free(vc_font_map);
-        free(vc_font_unimap);
-
-        if (fd >= 0)
-                close_nointr_nofail(fd);
-
-        return r;
-}
diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c
new file mode 100644
index 0000000..9196789
--- /dev/null
+++ b/src/vconsole/vconsole-setup.c
@@ -0,0 +1,459 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Kay Sievers
+
+  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 <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <linux/tiocl.h>
+#include <linux/kd.h>
+
+#include "util.h"
+#include "log.h"
+#include "macro.h"
+#include "virt.h"
+
+static bool is_vconsole(int fd) {
+        unsigned char data[1];
+
+        data[0] = TIOCL_GETFGCONSOLE;
+        return ioctl(fd, TIOCLINUX, data) >= 0;
+}
+
+static bool is_locale_utf8(void) {
+        const char *set;
+
+        if (!setlocale(LC_ALL, ""))
+                return true;
+
+        set = nl_langinfo(CODESET);
+        if (!set)
+                return true;
+
+        return streq(set, "UTF-8");
+}
+
+static int disable_utf8(int fd) {
+        int r = 0, k;
+
+        if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
+                r = -errno;
+
+        if (loop_write(fd, "\033%@", 3, false) < 0)
+                r = -errno;
+
+        if ((k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0")) < 0)
+                r = k;
+
+        if (r < 0)
+                log_warning("Failed to disable UTF-8: %s", strerror(errno));
+
+        return r;
+}
+
+static int load_keymap(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
+        const char *args[8];
+        int i = 0;
+        pid_t pid;
+
+        if (isempty(map)) {
+                /* An empty map means kernel map */
+                *_pid = 0;
+                return 0;
+        }
+
+        args[i++] = KBD_LOADKEYS;
+        args[i++] = "-q";
+        args[i++] = "-C";
+        args[i++] = vc;
+        if (utf8)
+                args[i++] = "-u";
+        args[i++] = map;
+        if (map_toggle)
+                args[i++] = map_toggle;
+        args[i++] = NULL;
+
+        if ((pid = fork()) < 0) {
+                log_error("Failed to fork: %m");
+                return -errno;
+        } else if (pid == 0) {
+                execv(args[0], (char **) args);
+                _exit(EXIT_FAILURE);
+        }
+
+        *_pid = pid;
+        return 0;
+}
+
+static int load_font(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
+        const char *args[9];
+        int i = 0;
+        pid_t pid;
+
+        if (isempty(font)) {
+                /* An empty font means kernel font */
+                *_pid = 0;
+                return 0;
+        }
+
+        args[i++] = KBD_SETFONT;
+        args[i++] = "-C";
+        args[i++] = vc;
+        args[i++] = font;
+        if (map) {
+                args[i++] = "-m";
+                args[i++] = map;
+        }
+        if (unimap) {
+                args[i++] = "-u";
+                args[i++] = unimap;
+        }
+        args[i++] = NULL;
+
+        if ((pid = fork()) < 0) {
+                log_error("Failed to fork: %m");
+                return -errno;
+        } else if (pid == 0) {
+                execv(args[0], (char **) args);
+                _exit(EXIT_FAILURE);
+        }
+
+        *_pid = pid;
+        return 0;
+}
+
+int main(int argc, char **argv) {
+        const char *vc;
+        char *vc_keymap = NULL;
+        char *vc_keymap_toggle = NULL;
+        char *vc_font = NULL;
+        char *vc_font_map = NULL;
+        char *vc_font_unimap = NULL;
+#ifdef TARGET_GENTOO
+        char *vc_unicode = NULL;
+#endif
+#if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+        char *vc_keytable = NULL;
+#endif
+        int fd = -1;
+        bool utf8;
+        int r = EXIT_FAILURE;
+        pid_t font_pid = 0, keymap_pid = 0;
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        if (argv[1])
+                vc = argv[1];
+        else
+                vc = "/dev/tty0";
+
+        if ((fd = open_terminal(vc, O_RDWR|O_CLOEXEC)) < 0) {
+                log_error("Failed to open %s: %m", vc);
+                goto finish;
+        }
+
+        if (!is_vconsole(fd)) {
+                log_error("Device %s is not a virtual console.", vc);
+                goto finish;
+        }
+
+        utf8 = is_locale_utf8();
+
+        vc_keymap = strdup("us");
+        vc_font = strdup(DEFAULT_FONT);
+
+        if (!vc_keymap || !vc_font) {
+                log_error("Failed to allocate strings.");
+                goto finish;
+        }
+
+        r = 0;
+
+        if (detect_container(NULL) <= 0)
+                if ((r = parse_env_file("/proc/cmdline", WHITESPACE,
+                                        "vconsole.keymap", &vc_keymap,
+                                        "vconsole.keymap.toggle", &vc_keymap_toggle,
+                                        "vconsole.font", &vc_font,
+                                        "vconsole.font.map", &vc_font_map,
+                                        "vconsole.font.unimap", &vc_font_unimap,
+                                        NULL)) < 0) {
+
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
+                }
+
+        /* Hmm, nothing set on the kernel cmd line? Then let's
+         * try /etc/vconsole.conf */
+        if (r <= 0 &&
+            (r = parse_env_file("/etc/vconsole.conf", NEWLINE,
+                                "KEYMAP", &vc_keymap,
+                                "KEYMAP_TOGGLE", &vc_keymap_toggle,
+                                "FONT", &vc_font,
+                                "FONT_MAP", &vc_font_map,
+                                "FONT_UNIMAP", &vc_font_unimap,
+                                NULL)) < 0) {
+
+                if (r != -ENOENT)
+                        log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
+        }
+
+        if (r <= 0) {
+#if defined(TARGET_FEDORA) || defined(TARGET_MEEGO)
+                if ((r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
+                                        "SYSFONT", &vc_font,
+                                        "SYSFONTACM", &vc_font_map,
+                                        "UNIMAP", &vc_font_unimap,
+                                        NULL)) < 0) {
+
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
+                }
+
+                if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
+                                        "KEYTABLE", &vc_keymap,
+                                        "KEYMAP", &vc_keymap,
+                                        NULL)) < 0) {
+
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
+                }
+
+                if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
+                        char *t;
+
+                        if (!(t = strdup("/etc/sysconfig/console/default.kmap"))) {
+                                log_error("Out of memory.");
+                                goto finish;
+                        }
+
+                        free(vc_keymap);
+                        vc_keymap = t;
+                }
+
+#elif defined(TARGET_SUSE)
+                if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
+                                        "KEYTABLE", &vc_keymap,
+                                        NULL)) < 0) {
+
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
+                }
+
+                if ((r = parse_env_file("/etc/sysconfig/console", NEWLINE,
+                                        "CONSOLE_FONT", &vc_font,
+                                        "CONSOLE_SCREENMAP", &vc_font_map,
+                                        "CONSOLE_UNICODEMAP", &vc_font_unimap,
+                                        NULL)) < 0) {
+
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/sysconfig/console: %s", strerror(-r));
+                }
+
+#elif defined(TARGET_ARCH)
+                if ((r = parse_env_file("/etc/rc.conf", NEWLINE,
+                                        "KEYMAP", &vc_keymap,
+                                        "CONSOLEFONT", &vc_font,
+                                        "CONSOLEMAP", &vc_font_map,
+                                        NULL)) < 0) {
+
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
+                }
+
+#elif defined(TARGET_FRUGALWARE)
+                if ((r = parse_env_file("/etc/sysconfig/keymap", NEWLINE,
+                                        "keymap", &vc_keymap,
+                                        NULL)) < 0) {
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/sysconfig/keymap: %s", strerror(-r));
+                }
+                if ((r = parse_env_file("/etc/sysconfig/font", NEWLINE,
+                                        "font", &vc_font,
+                                        NULL)) < 0) {
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/sysconfig/font: %s", strerror(-r));
+                }
+
+#elif defined(TARGET_ALTLINUX)
+                if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
+                                        "KEYTABLE", &vc_keymap,
+                                        NULL)) < 0) {
+
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
+                }
+
+                if ((r = parse_env_file("/etc/sysconfig/consolefont", NEWLINE,
+                                        "SYSFONT", &vc_font,
+                                        NULL)) < 0) {
+
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/sysconfig/console: %s", strerror(-r));
+                }
+
+#elif defined(TARGET_GENTOO)
+                if ((r = parse_env_file("/etc/rc.conf", NEWLINE,
+                                        "unicode", &vc_unicode,
+                                        NULL)) < 0) {
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
+                }
+
+                if (vc_unicode) {
+                        int rc_unicode;
+
+                        if ((rc_unicode = parse_boolean(vc_unicode)) < 0)
+                                log_error("Unknown value for /etc/rc.conf unicode=%s", vc_unicode);
+                        else {
+                                if (rc_unicode && !utf8)
+                                        log_warning("/etc/rc.conf wants unicode, but current locale is not UTF-8 capable!");
+                                else if (!rc_unicode && utf8) {
+                                        log_debug("/etc/rc.conf does not want unicode, leave it on in kernel but does not apply to vconsole.");
+                                        utf8 = false;
+                                }
+                        }
+                }
+
+                /* /etc/conf.d/consolefont comments and gentoo
+                 * documentation mention uppercase, but the actual
+                 * contents are lowercase.  the existing
+                 * /etc/init.d/consolefont tries both
+                 */
+                if ((r = parse_env_file("/etc/conf.d/consolefont", NEWLINE,
+                                        "CONSOLEFONT", &vc_font,
+                                        "consolefont", &vc_font,
+                                        "consoletranslation", &vc_font_map,
+                                        "CONSOLETRANSLATION", &vc_font_map,
+                                        "unicodemap", &vc_font_unimap,
+                                        "UNICODEMAP", &vc_font_unimap,
+                                        NULL)) < 0) {
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/conf.d/consolefont: %s", strerror(-r));
+                }
+
+                if ((r = parse_env_file("/etc/conf.d/keymaps", NEWLINE,
+                                        "keymap", &vc_keymap,
+                                        "KEYMAP", &vc_keymap,
+                                        NULL)) < 0) {
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/conf.d/keymaps: %s", strerror(-r));
+                }
+
+#elif defined(TARGET_MANDRIVA) || defined (TARGET_MAGEIA)
+
+                if ((r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
+                                        "SYSFONT", &vc_font,
+                                        "SYSFONTACM", &vc_font_map,
+                                        "UNIMAP", &vc_font_unimap,
+                                        NULL)) < 0) {
+
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
+                }
+
+                if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
+                                        "KEYTABLE", &vc_keytable,
+                                        "KEYMAP", &vc_keymap,
+                                        "UNIKEYTABLE", &vc_keymap,
+                                        "GRP_TOGGLE", &vc_keymap_toggle,
+                                        NULL)) < 0) {
+
+                        if (r != -ENOENT)
+                                log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
+                }
+
+                if (vc_keytable) {
+                        if (vc_keymap)
+                                free(vc_keymap);
+                        if (utf8) {
+                                if (endswith(vc_keytable, ".uni") || strstr(vc_keytable, ".uni."))
+                                        vc_keymap = strdup(vc_keytable);
+                                else {
+                                        char *s;
+                                        if ((s = strstr(vc_keytable, ".map")))
+                                                vc_keytable[s-vc_keytable+1] = '\0';
+                                        vc_keymap = strappend(vc_keytable, ".uni");
+                                }
+                        } else
+                                vc_keymap = strdup(vc_keytable);
+
+                        free(vc_keytable);
+
+                        if (!vc_keymap) {
+                                log_error("Out of memory.");
+                                goto finish;
+                        }
+                }
+
+                if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
+                        char *t;
+
+                        if (!(t = strdup("/etc/sysconfig/console/default.kmap"))) {
+                                log_error("Out of memory.");
+                                goto finish;
+                        }
+
+                        free(vc_keymap);
+                        vc_keymap = t;
+                }
+#endif
+        }
+
+        r = EXIT_FAILURE;
+
+        if (!utf8)
+                disable_utf8(fd);
+
+        if (load_keymap(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
+            load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
+                r = EXIT_SUCCESS;
+
+finish:
+        if (keymap_pid > 0)
+                wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
+
+        if (font_pid > 0)
+                wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
+
+        free(vc_keymap);
+        free(vc_font);
+        free(vc_font_map);
+        free(vc_font_unimap);
+
+        if (fd >= 0)
+                close_nointr_nofail(fd);
+
+        return r;
+}



More information about the systemd-commits mailing list