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"
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 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
47 _cleanup_bus_unref_ sd_bus *bus = NULL;
52 r = bus_open_system_systemd(&bus);
54 log_error("Failed to get D-Bus connection: %s", strerror(-r));
58 log_info("Running request %s/start/replace", target);
60 /* Start these units only if we can replace base.target with it */
61 r = sd_bus_call_method(bus,
62 "org.freedesktop.systemd1",
63 "/org/freedesktop/systemd1",
64 "org.freedesktop.systemd1.Manager",
68 "sss", "basic.target", target, "replace");
70 /* Don't print a warning if we aren't called during startup */
71 if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
72 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
75 static int parse_proc_cmdline_word(const char *w) {
76 if (streq(w, "fsck.mode=auto"))
77 arg_force = arg_skip = false;
78 else if (streq(w, "fsck.mode=force"))
80 else if (streq(w, "fsck.mode=skip"))
82 else if (startswith(w, "fsck"))
83 log_warning("Invalid fsck parameter. Ignoring.");
84 #ifdef HAVE_SYSV_COMPAT
85 else if (streq(w, "fastboot")) {
86 log_error("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
88 } else if (streq(w, "forcefsck")) {
89 log_error("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
97 static void test_files(void) {
98 #ifdef HAVE_SYSV_COMPAT
99 if (access("/fastboot", F_OK) >= 0) {
100 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
104 if (access("/forcefsck", F_OK) >= 0) {
105 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
110 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
111 arg_show_progress = true;
114 static double percent(int pass, unsigned long cur, unsigned long max) {
115 /* Values stolen from e2fsck */
117 static const int pass_table[] = {
118 0, 70, 90, 92, 95, 100
124 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
127 return (double) pass_table[pass-1] +
128 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
129 (double) cur / (double) max;
132 static int process_progress(int fd) {
133 _cleanup_fclose_ FILE *console = NULL, *f = NULL;
140 close_nointr_nofail(fd);
144 console = fopen("/dev/console", "we");
150 unsigned long cur, max;
151 _cleanup_free_ char *device = NULL;
155 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
158 /* Only show one progress counter at max */
160 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0)
166 /* Only update once every 50ms */
167 t = now(CLOCK_MONOTONIC);
168 if (last + 50 * USEC_PER_MSEC > t)
173 p = percent(pass, cur, max);
174 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
184 fputc('\r', console);
185 for (j = 0; j < (unsigned) clear; j++)
187 fputc('\r', console);
194 int main(int argc, char *argv[]) {
195 const char *cmdline[9];
196 int i = 0, r = EXIT_FAILURE, q;
199 _cleanup_udev_unref_ struct udev *udev = NULL;
200 _cleanup_udev_device_unref_ struct udev_device *udev_device = NULL;
201 const char *device, *type;
203 int progress_pipe[2] = { -1, -1 };
208 log_error("This program expects one or no arguments.");
212 log_set_target(LOG_TARGET_AUTO);
213 log_parse_environment();
218 parse_proc_cmdline(parse_proc_cmdline_word);
221 if (!arg_force && arg_skip)
232 root_directory = false;
234 if (stat(device, &st) < 0) {
235 log_error("Failed to stat '%s': %m", device);
239 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
241 log_error("Failed to detect device %s", device);
245 struct timespec times[2];
247 /* Find root device */
249 if (stat("/", &st) < 0) {
250 log_error("Failed to stat() the root directory: %m");
254 /* Virtual root devices don't need an fsck */
255 if (major(st.st_dev) == 0)
258 /* check if we are already writable */
259 times[0] = st.st_atim;
260 times[1] = st.st_mtim;
261 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
262 log_info("Root directory is writable, skipping check.");
266 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
268 log_error("Failed to detect root device.");
272 device = udev_device_get_devnode(udev_device);
274 log_error("Failed to detect device node of root directory.");
278 root_directory = true;
281 type = udev_device_get_property_value(udev_device, "ID_FS_TYPE");
283 const char *checker = strappenda("/sbin/fsck.", type);
284 r = access(checker, X_OK);
286 if (errno == ENOENT) {
287 log_info("%s doesn't exist, not checking file system.", checker);
290 log_warning("%s cannot be used: %m", checker);
294 if (arg_show_progress)
295 if (pipe(progress_pipe) < 0) {
296 log_error("pipe(): %m");
300 cmdline[i++] = "/sbin/fsck";
311 if (progress_pipe[1] >= 0) {
312 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
313 char_array_0(dash_c);
314 cmdline[i++] = dash_c;
317 cmdline[i++] = device;
322 log_error("fork(): %m");
324 } else if (pid == 0) {
326 if (progress_pipe[0] >= 0)
327 close_nointr_nofail(progress_pipe[0]);
328 execv(cmdline[0], (char**) cmdline);
329 _exit(8); /* Operational error */
332 if (progress_pipe[1] >= 0) {
333 close_nointr_nofail(progress_pipe[1]);
334 progress_pipe[1] = -1;
337 if (progress_pipe[0] >= 0) {
338 process_progress(progress_pipe[0]);
339 progress_pipe[0] = -1;
342 q = wait_for_terminate(pid, &status);
344 log_error("waitid(): %s", strerror(-q));
348 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
350 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
351 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
352 else if (status.si_code == CLD_EXITED)
353 log_error("fsck failed with error code %i.", status.si_status);
355 log_error("fsck failed due to unknown reason.");
357 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
358 /* System should be rebooted. */
359 start_target(SPECIAL_REBOOT_TARGET);
360 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
361 /* Some other problem */
362 start_target(SPECIAL_EMERGENCY_TARGET);
365 log_warning("Ignoring error.");
371 if (status.si_code == CLD_EXITED && (status.si_status & 1))
372 touch("/run/systemd/quotacheck");
375 close_pipe(progress_pipe);