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) {
45 DBusMessage *m = NULL, *reply = NULL;
47 const char *mode = "replace", *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));
59 log_info("Running request %s/start/%s", target, mode);
61 if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnitReplace"))) {
62 log_error("Could not allocate message.");
66 /* Start these units only if we can replace base.target with it */
68 if (!dbus_message_append_args(m,
69 DBUS_TYPE_STRING, &basic_target,
70 DBUS_TYPE_STRING, &target,
71 DBUS_TYPE_STRING, &mode,
73 log_error("Could not attach target and flag information to message.");
77 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
79 /* Don't print a warning if we aren't called during
81 if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
82 log_error("Failed to start unit: %s", bus_error_message(&error));
89 dbus_message_unref(m);
92 dbus_message_unref(reply);
95 dbus_connection_flush(bus);
96 dbus_connection_close(bus);
97 dbus_connection_unref(bus);
100 dbus_error_free(&error);
103 static int parse_proc_cmdline(void) {
104 char *line, *w, *state;
108 if (detect_container(NULL) > 0)
111 r = read_one_line_file("/proc/cmdline", &line);
113 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
117 FOREACH_WORD_QUOTED(w, l, line, state) {
119 if (strneq(w, "fsck.mode=auto", l))
120 arg_force = arg_skip = false;
121 else if (strneq(w, "fsck.mode=force", l))
123 else if (strneq(w, "fsck.mode=skip", l))
125 else if (startswith(w, "fsck"))
126 log_warning("Invalid fsck parameter. Ignoring.");
127 #ifdef HAVE_SYSV_COMPAT
128 else if (strneq(w, "fastboot", l)) {
129 log_error("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
131 } else if (strneq(w, "forcefsck", l)) {
132 log_error("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
142 static void test_files(void) {
143 #ifdef HAVE_SYSV_COMPAT
144 if (access("/fastboot", F_OK) >= 0) {
145 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
149 if (access("/forcefsck", F_OK) >= 0) {
150 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
155 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
156 arg_show_progress = true;
159 static double percent(int pass, unsigned long cur, unsigned long max) {
160 /* Values stolen from e2fsck */
162 static const int pass_table[] = {
163 0, 70, 90, 92, 95, 100
169 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
172 return (double) pass_table[pass-1] +
173 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
174 (double) cur / (double) max;
177 static int process_progress(int fd) {
185 close_nointr_nofail(fd);
189 console = fopen("/dev/console", "w");
197 unsigned long cur, max;
202 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
205 /* Only show one progress counter at max */
207 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
215 /* Only update once every 50ms */
216 t = now(CLOCK_MONOTONIC);
217 if (last + 50 * USEC_PER_MSEC > t) {
224 p = percent(pass, cur, max);
225 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
237 fputc('\r', console);
238 for (j = 0; j < (unsigned) clear; j++)
240 fputc('\r', console);
249 int main(int argc, char *argv[]) {
250 const char *cmdline[9];
251 int i = 0, r = EXIT_FAILURE, q;
254 struct udev *udev = NULL;
255 struct udev_device *udev_device = NULL;
258 int progress_pipe[2] = { -1, -1 };
262 log_error("This program expects one or no arguments.");
266 log_set_target(LOG_TARGET_AUTO);
267 log_parse_environment();
272 parse_proc_cmdline();
275 if (!arg_force && arg_skip)
280 root_directory = false;
283 struct timespec times[2];
285 /* Find root device */
287 if (stat("/", &st) < 0) {
288 log_error("Failed to stat() the root directory: %m");
292 /* Virtual root devices don't need an fsck */
293 if (major(st.st_dev) == 0)
296 /* check if we are already writable */
297 times[0] = st.st_atim;
298 times[1] = st.st_mtim;
299 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
300 log_info("Root directory is writable, skipping check.");
304 if (!(udev = udev_new())) {
309 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
310 log_error("Failed to detect root device.");
314 if (!(device = udev_device_get_devnode(udev_device))) {
315 log_error("Failed to detect device node of root directory.");
319 root_directory = true;
322 if (arg_show_progress)
323 if (pipe(progress_pipe) < 0) {
324 log_error("pipe(): %m");
328 cmdline[i++] = "/sbin/fsck";
339 if (progress_pipe[1] >= 0) {
340 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
341 char_array_0(dash_c);
342 cmdline[i++] = dash_c;
345 cmdline[i++] = device;
350 log_error("fork(): %m");
352 } else if (pid == 0) {
354 if (progress_pipe[0] >= 0)
355 close_nointr_nofail(progress_pipe[0]);
356 execv(cmdline[0], (char**) cmdline);
357 _exit(8); /* Operational error */
360 if (progress_pipe[1] >= 0) {
361 close_nointr_nofail(progress_pipe[1]);
362 progress_pipe[1] = -1;
365 if (progress_pipe[0] >= 0) {
366 process_progress(progress_pipe[0]);
367 progress_pipe[0] = -1;
370 q = wait_for_terminate(pid, &status);
372 log_error("waitid(): %s", strerror(-q));
376 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
378 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
379 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
380 else if (status.si_code == CLD_EXITED)
381 log_error("fsck failed with error code %i.", status.si_status);
383 log_error("fsck failed due to unknown reason.");
385 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
386 /* System should be rebooted. */
387 start_target(SPECIAL_REBOOT_TARGET);
388 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
389 /* Some other problem */
390 start_target(SPECIAL_EMERGENCY_TARGET);
393 log_warning("Ignoring error.");
399 if (status.si_code == CLD_EXITED && (status.si_status & 1))
400 touch("/run/systemd/quotacheck");
404 udev_device_unref(udev_device);
409 close_pipe(progress_pipe);