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/>.
36 #include "bus-error.h"
37 #include "bus-errors.h"
39 #include "udev-util.h"
40 #include "path-util.h"
42 static bool arg_skip = false;
43 static bool arg_force = false;
44 static bool arg_show_progress = false;
46 static void start_target(const char *target) {
47 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
48 _cleanup_bus_unref_ sd_bus *bus = NULL;
53 r = bus_open_system_systemd(&bus);
55 log_error("Failed to get D-Bus connection: %s", strerror(-r));
59 log_info("Running request %s/start/replace", target);
61 /* Start these units only if we can replace base.target with it */
62 r = sd_bus_call_method(bus,
63 "org.freedesktop.systemd1",
64 "/org/freedesktop/systemd1",
65 "org.freedesktop.systemd1.Manager",
69 "sss", "basic.target", target, "replace");
71 /* Don't print a warning if we aren't called during startup */
72 if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
73 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
76 static int parse_proc_cmdline_item(const char *key, const char *value) {
78 if (streq(key, "fsck.mode") && value) {
80 if (streq(value, "auto"))
81 arg_force = arg_skip = false;
82 else if (streq(value, "force"))
84 else if (streq(value, "skip"))
87 log_warning("Invalid fsck.mode= parameter. Ignoring.");
88 } else if (startswith(key, "fsck."))
89 log_warning("Invalid fsck parameter. Ignoring.");
90 #ifdef HAVE_SYSV_COMPAT
91 else if (streq(key, "fastboot") && !value) {
92 log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
94 } else if (streq(key, "forcefsck") && !value) {
95 log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
103 static void test_files(void) {
104 #ifdef HAVE_SYSV_COMPAT
105 if (access("/fastboot", F_OK) >= 0) {
106 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
110 if (access("/forcefsck", F_OK) >= 0) {
111 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
116 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
117 arg_show_progress = true;
120 static double percent(int pass, unsigned long cur, unsigned long max) {
121 /* Values stolen from e2fsck */
123 static const int pass_table[] = {
124 0, 70, 90, 92, 95, 100
130 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
133 return (double) pass_table[pass-1] +
134 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
135 (double) cur / (double) max;
138 static int process_progress(int fd) {
139 _cleanup_fclose_ FILE *console = NULL, *f = NULL;
150 console = fopen("/dev/console", "we");
156 unsigned long cur, max;
157 _cleanup_free_ char *device = NULL;
161 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
164 /* Only show one progress counter at max */
166 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0)
172 /* Only update once every 50ms */
173 t = now(CLOCK_MONOTONIC);
174 if (last + 50 * USEC_PER_MSEC > t)
179 p = percent(pass, cur, max);
180 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
190 fputc('\r', console);
191 for (j = 0; j < (unsigned) clear; j++)
193 fputc('\r', console);
200 int main(int argc, char *argv[]) {
201 const char *cmdline[9];
202 int i = 0, r = EXIT_FAILURE, q;
205 _cleanup_udev_unref_ struct udev *udev = NULL;
206 _cleanup_udev_device_unref_ struct udev_device *udev_device = NULL;
207 const char *device, *type;
209 int progress_pipe[2] = { -1, -1 };
214 log_error("This program expects one or no arguments.");
218 log_set_target(LOG_TARGET_AUTO);
219 log_parse_environment();
224 parse_proc_cmdline(parse_proc_cmdline_item);
227 if (!arg_force && arg_skip)
238 root_directory = false;
240 if (stat(device, &st) < 0) {
241 log_error("Failed to stat '%s': %m", device);
245 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
247 log_error("Failed to detect device %s", device);
251 struct timespec times[2];
253 /* Find root device */
255 if (stat("/", &st) < 0) {
256 log_error("Failed to stat() the root directory: %m");
260 /* Virtual root devices don't need an fsck */
261 if (major(st.st_dev) == 0)
264 /* check if we are already writable */
265 times[0] = st.st_atim;
266 times[1] = st.st_mtim;
267 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
268 log_info("Root directory is writable, skipping check.");
272 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
274 log_error("Failed to detect root device.");
278 device = udev_device_get_devnode(udev_device);
280 log_error("Failed to detect device node of root directory.");
284 root_directory = true;
287 type = udev_device_get_property_value(udev_device, "ID_FS_TYPE");
289 r = fsck_exists(type);
292 log_info("fsck.%s doesn't exist, not checking file system.", type);
295 log_warning("fsck.%s cannot be used: %s", type, strerror(-r));
299 if (arg_show_progress)
300 if (pipe(progress_pipe) < 0) {
301 log_error("pipe(): %m");
305 cmdline[i++] = "/sbin/fsck";
316 if (progress_pipe[1] >= 0) {
317 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
318 char_array_0(dash_c);
319 cmdline[i++] = dash_c;
322 cmdline[i++] = device;
327 log_error("fork(): %m");
329 } else if (pid == 0) {
331 if (progress_pipe[0] >= 0)
332 safe_close(progress_pipe[0]);
333 execv(cmdline[0], (char**) cmdline);
334 _exit(8); /* Operational error */
337 progress_pipe[1] = safe_close(progress_pipe[1]);
339 if (progress_pipe[0] >= 0) {
340 process_progress(progress_pipe[0]);
341 progress_pipe[0] = -1;
344 q = wait_for_terminate(pid, &status);
346 log_error("waitid(): %s", strerror(-q));
350 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
352 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
353 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
354 else if (status.si_code == CLD_EXITED)
355 log_error("fsck failed with error code %i.", status.si_status);
357 log_error("fsck failed due to unknown reason.");
359 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
360 /* System should be rebooted. */
361 start_target(SPECIAL_REBOOT_TARGET);
362 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
363 /* Some other problem */
364 start_target(SPECIAL_EMERGENCY_TARGET);
367 log_warning("Ignoring error.");
373 if (status.si_code == CLD_EXITED && (status.si_status & 1))
374 touch("/run/systemd/quotacheck");
377 safe_close_pair(progress_pipe);