[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