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"
39 static bool arg_skip = false;
40 static bool arg_force = false;
41 static bool arg_show_progress = false;
43 static void start_target(const char *target, bool isolate) {
44 DBusMessage *m = NULL, *reply = NULL;
46 const char *mode, *basic_target = "basic.target";
47 DBusConnection *bus = NULL;
51 dbus_error_init(&error);
53 if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) {
54 log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
63 log_info("Running request %s/start/%s", target, mode);
65 if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnitReplace"))) {
66 log_error("Could not allocate message.");
70 /* Start these units only if we can replace base.target with it */
72 if (!dbus_message_append_args(m,
73 DBUS_TYPE_STRING, &basic_target,
74 DBUS_TYPE_STRING, &target,
75 DBUS_TYPE_STRING, &mode,
77 log_error("Could not attach target and flag information to message.");
81 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
83 /* Don't print a warning if we aren't called during
85 if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
86 log_error("Failed to start unit: %s", bus_error_message(&error));
93 dbus_message_unref(m);
96 dbus_message_unref(reply);
99 dbus_connection_flush(bus);
100 dbus_connection_close(bus);
101 dbus_connection_unref(bus);
104 dbus_error_free(&error);
107 static int parse_proc_cmdline(void) {
108 char *line, *w, *state;
112 if (detect_container(NULL) > 0)
115 r = read_one_line_file("/proc/cmdline", &line);
117 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
121 FOREACH_WORD_QUOTED(w, l, line, state) {
123 if (strneq(w, "fsck.mode=auto", l))
124 arg_force = arg_skip = false;
125 else if (strneq(w, "fsck.mode=force", l))
127 else if (strneq(w, "fsck.mode=skip", l))
129 else if (startswith(w, "fsck"))
130 log_warning("Invalid fsck parameter. Ignoring.");
131 #ifdef HAVE_SYSV_COMPAT
132 else if (strneq(w, "fastboot", l)) {
133 log_error("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
135 } else if (strneq(w, "forcefsck", l)) {
136 log_error("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
146 static void test_files(void) {
147 #ifdef HAVE_SYSV_COMPAT
148 if (access("/fastboot", F_OK) >= 0) {
149 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
153 if (access("/forcefsck", F_OK) >= 0) {
154 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
159 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
160 arg_show_progress = true;
163 static double percent(int pass, unsigned long cur, unsigned long max) {
164 /* Values stolen from e2fsck */
166 static const int pass_table[] = {
167 0, 70, 90, 92, 95, 100
173 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
176 return (double) pass_table[pass-1] +
177 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
178 (double) cur / (double) max;
181 static int process_progress(int fd) {
189 close_nointr_nofail(fd);
193 console = fopen("/dev/console", "w");
201 unsigned long cur, max;
206 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
209 /* Only show one progress counter at max */
211 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
219 /* Only update once every 50ms */
220 t = now(CLOCK_MONOTONIC);
221 if (last + 50 * USEC_PER_MSEC > t) {
228 p = percent(pass, cur, max);
229 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
241 fputc('\r', console);
242 for (j = 0; j < (unsigned) clear; j++)
244 fputc('\r', console);
253 int main(int argc, char *argv[]) {
254 const char *cmdline[9];
255 int i = 0, r = EXIT_FAILURE, q;
258 struct udev *udev = NULL;
259 struct udev_device *udev_device = NULL;
262 int progress_pipe[2] = { -1, -1 };
266 log_error("This program expects one or no arguments.");
270 log_set_target(LOG_TARGET_AUTO);
271 log_parse_environment();
276 parse_proc_cmdline();
279 if (!arg_force && arg_skip)
284 root_directory = false;
287 struct timespec times[2];
289 /* Find root device */
291 if (stat("/", &st) < 0) {
292 log_error("Failed to stat() the root directory: %m");
296 /* Virtual root devices don't need an fsck */
297 if (major(st.st_dev) == 0)
300 /* check if we are already writable */
301 times[0] = st.st_atim;
302 times[1] = st.st_mtim;
303 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
304 log_info("Root directory is writable, skipping check.");
308 if (!(udev = udev_new())) {
313 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
314 log_error("Failed to detect root device.");
318 if (!(device = udev_device_get_devnode(udev_device))) {
319 log_error("Failed to detect device node of root directory.");
323 root_directory = true;
326 if (arg_show_progress)
327 if (pipe(progress_pipe) < 0) {
328 log_error("pipe(): %m");
332 cmdline[i++] = "/sbin/fsck";
343 if (progress_pipe[1] >= 0) {
344 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
345 char_array_0(dash_c);
346 cmdline[i++] = dash_c;
349 cmdline[i++] = device;
354 log_error("fork(): %m");
356 } else if (pid == 0) {
358 if (progress_pipe[0] >= 0)
359 close_nointr_nofail(progress_pipe[0]);
360 execv(cmdline[0], (char**) cmdline);
361 _exit(8); /* Operational error */
364 if (progress_pipe[1] >= 0) {
365 close_nointr_nofail(progress_pipe[1]);
366 progress_pipe[1] = -1;
369 if (progress_pipe[0] >= 0) {
370 process_progress(progress_pipe[0]);
371 progress_pipe[0] = -1;
374 q = wait_for_terminate(pid, &status);
376 log_error("waitid(): %s", strerror(-q));
380 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
382 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
383 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
384 else if (status.si_code == CLD_EXITED)
385 log_error("fsck failed with error code %i.", status.si_status);
387 log_error("fsck failed due to unknown reason.");
389 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
390 /* System should be rebooted. */
391 start_target(SPECIAL_REBOOT_TARGET, false);
392 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
393 /* Some other problem */
394 start_target(SPECIAL_EMERGENCY_TARGET, true);
397 log_warning("Ignoring error.");
403 if (status.si_code == CLD_EXITED && (status.si_status & 1))
404 touch("/run/systemd/quotacheck");
408 udev_device_unref(udev_device);
413 close_pipe(progress_pipe);