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/>.
30 #include <dbus/dbus.h>
33 #include "dbus-common.h"
35 #include "bus-errors.h"
39 #include "udev-util.h"
41 static bool arg_skip = false;
42 static bool arg_force = false;
43 static bool arg_show_progress = false;
45 static void start_target(const char *target) {
46 DBusMessage *m = NULL, *reply = NULL;
48 const char *mode = "replace", *basic_target = "basic.target";
49 DBusConnection *bus = NULL;
53 dbus_error_init(&error);
55 if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) {
56 log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
60 log_info("Running request %s/start/%s", target, mode);
62 if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnitReplace"))) {
63 log_error("Could not allocate message.");
67 /* Start these units only if we can replace base.target with it */
69 if (!dbus_message_append_args(m,
70 DBUS_TYPE_STRING, &basic_target,
71 DBUS_TYPE_STRING, &target,
72 DBUS_TYPE_STRING, &mode,
74 log_error("Could not attach target and flag information to message.");
78 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
80 /* Don't print a warning if we aren't called during
82 if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
83 log_error("Failed to start unit: %s", bus_error_message(&error));
90 dbus_message_unref(m);
93 dbus_message_unref(reply);
96 dbus_connection_flush(bus);
97 dbus_connection_close(bus);
98 dbus_connection_unref(bus);
101 dbus_error_free(&error);
104 static int parse_proc_cmdline(void) {
105 char *line, *w, *state;
109 if (detect_container(NULL) > 0)
112 r = read_one_line_file("/proc/cmdline", &line);
114 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
118 FOREACH_WORD_QUOTED(w, l, line, state) {
120 if (strneq(w, "fsck.mode=auto", l))
121 arg_force = arg_skip = false;
122 else if (strneq(w, "fsck.mode=force", l))
124 else if (strneq(w, "fsck.mode=skip", l))
126 else if (startswith(w, "fsck"))
127 log_warning("Invalid fsck parameter. Ignoring.");
128 #ifdef HAVE_SYSV_COMPAT
129 else if (strneq(w, "fastboot", l)) {
130 log_error("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
132 } else if (strneq(w, "forcefsck", l)) {
133 log_error("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
143 static void test_files(void) {
144 #ifdef HAVE_SYSV_COMPAT
145 if (access("/fastboot", F_OK) >= 0) {
146 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
150 if (access("/forcefsck", F_OK) >= 0) {
151 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
156 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
157 arg_show_progress = true;
160 static double percent(int pass, unsigned long cur, unsigned long max) {
161 /* Values stolen from e2fsck */
163 static const int pass_table[] = {
164 0, 70, 90, 92, 95, 100
170 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
173 return (double) pass_table[pass-1] +
174 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
175 (double) cur / (double) max;
178 static int process_progress(int fd) {
186 close_nointr_nofail(fd);
190 console = fopen("/dev/console", "w");
198 unsigned long cur, max;
203 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
206 /* Only show one progress counter at max */
208 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
216 /* Only update once every 50ms */
217 t = now(CLOCK_MONOTONIC);
218 if (last + 50 * USEC_PER_MSEC > t) {
225 p = percent(pass, cur, max);
226 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
238 fputc('\r', console);
239 for (j = 0; j < (unsigned) clear; j++)
241 fputc('\r', console);
250 int main(int argc, char *argv[]) {
251 const char *cmdline[9];
252 int i = 0, r = EXIT_FAILURE, q;
255 _cleanup_udev_unref_ struct udev *udev = NULL;
256 _cleanup_udev_device_unref_ struct udev_device *udev_device = NULL;
259 int progress_pipe[2] = { -1, -1 };
263 log_error("This program expects one or no arguments.");
267 log_set_target(LOG_TARGET_AUTO);
268 log_parse_environment();
273 parse_proc_cmdline();
276 if (!arg_force && arg_skip)
281 root_directory = false;
284 struct timespec times[2];
286 /* Find root device */
288 if (stat("/", &st) < 0) {
289 log_error("Failed to stat() the root directory: %m");
293 /* Virtual root devices don't need an fsck */
294 if (major(st.st_dev) == 0)
297 /* check if we are already writable */
298 times[0] = st.st_atim;
299 times[1] = st.st_mtim;
300 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
301 log_info("Root directory is writable, skipping check.");
305 if (!(udev = udev_new())) {
310 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
311 log_error("Failed to detect root device.");
315 if (!(device = udev_device_get_devnode(udev_device))) {
316 log_error("Failed to detect device node of root directory.");
320 root_directory = true;
323 if (arg_show_progress)
324 if (pipe(progress_pipe) < 0) {
325 log_error("pipe(): %m");
329 cmdline[i++] = "/sbin/fsck";
340 if (progress_pipe[1] >= 0) {
341 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
342 char_array_0(dash_c);
343 cmdline[i++] = dash_c;
346 cmdline[i++] = device;
351 log_error("fork(): %m");
353 } else if (pid == 0) {
355 if (progress_pipe[0] >= 0)
356 close_nointr_nofail(progress_pipe[0]);
357 execv(cmdline[0], (char**) cmdline);
358 _exit(8); /* Operational error */
361 if (progress_pipe[1] >= 0) {
362 close_nointr_nofail(progress_pipe[1]);
363 progress_pipe[1] = -1;
366 if (progress_pipe[0] >= 0) {
367 process_progress(progress_pipe[0]);
368 progress_pipe[0] = -1;
371 q = wait_for_terminate(pid, &status);
373 log_error("waitid(): %s", strerror(-q));
377 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
379 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
380 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
381 else if (status.si_code == CLD_EXITED)
382 log_error("fsck failed with error code %i.", status.si_status);
384 log_error("fsck failed due to unknown reason.");
386 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
387 /* System should be rebooted. */
388 start_target(SPECIAL_REBOOT_TARGET);
389 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
390 /* Some other problem */
391 start_target(SPECIAL_EMERGENCY_TARGET);
394 log_warning("Ignoring error.");
400 if (status.si_code == CLD_EXITED && (status.si_status & 1))
401 touch("/run/systemd/quotacheck");
404 close_pipe(progress_pipe);