1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
31 #include <dbus/dbus.h>
34 #include "dbus-common.h"
36 #include "bus-errors.h"
40 static bool arg_skip = false;
41 static bool arg_force = false;
42 static bool arg_show_progress = false;
44 static void start_target(const char *target, bool isolate) {
45 DBusMessage *m = NULL, *reply = NULL;
47 const char *mode, *basic_target = "basic.target";
48 DBusConnection *bus = NULL;
52 dbus_error_init(&error);
54 if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) {
55 log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
64 log_info("Running request %s/start/%s", target, mode);
66 if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnitReplace"))) {
67 log_error("Could not allocate message.");
71 /* Start these units only if we can replace base.target with it */
73 if (!dbus_message_append_args(m,
74 DBUS_TYPE_STRING, &basic_target,
75 DBUS_TYPE_STRING, &target,
76 DBUS_TYPE_STRING, &mode,
78 log_error("Could not attach target and flag information to message.");
82 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
84 /* Don't print a warning if we aren't called during
86 if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
87 log_error("Failed to start unit: %s", bus_error_message(&error));
94 dbus_message_unref(m);
97 dbus_message_unref(reply);
100 dbus_connection_flush(bus);
101 dbus_connection_close(bus);
102 dbus_connection_unref(bus);
105 dbus_error_free(&error);
108 static int parse_proc_cmdline(void) {
109 char *line, *w, *state;
113 if (detect_container(NULL) > 0)
116 r = read_one_line_file("/proc/cmdline", &line);
118 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
122 FOREACH_WORD_QUOTED(w, l, line, state) {
124 if (strneq(w, "fsck.mode=auto", l))
125 arg_force = arg_skip = false;
126 else if (strneq(w, "fsck.mode=force", l))
128 else if (strneq(w, "fsck.mode=skip", l))
130 else if (startswith(w, "fsck"))
131 log_warning("Invalid fsck parameter. Ignoring.");
132 #ifdef HAVE_SYSV_COMPAT
133 else if (strneq(w, "fastboot", l)) {
134 log_error("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
136 } else if (strneq(w, "forcefsck", l)) {
137 log_error("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
147 static void test_files(void) {
148 #ifdef HAVE_SYSV_COMPAT
149 if (access("/fastboot", F_OK) >= 0) {
150 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
154 if (access("/forcefsck", F_OK) >= 0) {
155 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
160 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
161 arg_show_progress = true;
164 static double percent(int pass, unsigned long cur, unsigned long max) {
165 /* Values stolen from e2fsck */
167 static const int pass_table[] = {
168 0, 70, 90, 92, 95, 100
174 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
177 return (double) pass_table[pass-1] +
178 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
179 (double) cur / (double) max;
182 static int process_progress(int fd) {
190 close_nointr_nofail(fd);
194 console = fopen("/dev/console", "w");
202 unsigned long cur, max;
207 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
210 /* Only show one progress counter at max */
212 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
220 /* Only update once every 50ms */
221 t = now(CLOCK_MONOTONIC);
222 if (last + 50 * USEC_PER_MSEC > t) {
229 p = percent(pass, cur, max);
230 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
242 fputc('\r', console);
243 for (j = 0; j < (unsigned) clear; j++)
245 fputc('\r', console);
254 int main(int argc, char *argv[]) {
255 const char *cmdline[9];
256 int i = 0, r = EXIT_FAILURE, q;
259 struct udev *udev = NULL;
260 struct udev_device *udev_device = NULL;
263 int progress_pipe[2] = { -1, -1 };
267 log_error("This program expects one or no arguments.");
271 log_set_target(LOG_TARGET_AUTO);
272 log_parse_environment();
277 parse_proc_cmdline();
280 if (!arg_force && arg_skip)
285 root_directory = false;
288 struct timespec times[2];
290 /* Find root device */
292 if (stat("/", &st) < 0) {
293 log_error("Failed to stat() the root directory: %m");
297 /* Virtual root devices don't need an fsck */
298 if (major(st.st_dev) == 0)
301 /* check if we are already writable */
302 times[0] = st.st_atim;
303 times[1] = st.st_mtim;
304 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
305 log_info("Root directory is writable, skipping check.");
309 if (!(udev = udev_new())) {
314 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
315 log_error("Failed to detect root device.");
319 if (!(device = udev_device_get_devnode(udev_device))) {
320 log_error("Failed to detect device node of root directory.");
324 root_directory = true;
327 if (arg_show_progress)
328 if (pipe(progress_pipe) < 0) {
329 log_error("pipe(): %m");
333 cmdline[i++] = "/sbin/fsck";
344 if (progress_pipe[1] >= 0) {
345 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
346 char_array_0(dash_c);
347 cmdline[i++] = dash_c;
350 cmdline[i++] = device;
355 log_error("fork(): %m");
357 } else if (pid == 0) {
359 if (progress_pipe[0] >= 0)
360 close_nointr_nofail(progress_pipe[0]);
361 execv(cmdline[0], (char**) cmdline);
362 _exit(8); /* Operational error */
365 if (progress_pipe[1] >= 0) {
366 close_nointr_nofail(progress_pipe[1]);
367 progress_pipe[1] = -1;
370 if (progress_pipe[0] >= 0) {
371 process_progress(progress_pipe[0]);
372 progress_pipe[0] = -1;
375 q = wait_for_terminate(pid, &status);
377 log_error("waitid(): %s", strerror(-q));
381 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
383 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
384 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
385 else if (status.si_code == CLD_EXITED)
386 log_error("fsck failed with error code %i.", status.si_status);
388 log_error("fsck failed due to unknown reason.");
390 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
391 /* System should be rebooted. */
392 start_target(SPECIAL_REBOOT_TARGET, false);
393 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
394 /* Some other problem */
395 start_target(SPECIAL_EMERGENCY_TARGET, true);
398 log_warning("Ignoring error.");
404 if (status.si_code == CLD_EXITED && (status.si_status & 1))
405 touch("/run/systemd/quotacheck");
409 udev_device_unref(udev_device);
414 close_pipe(progress_pipe);