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"
40 #include "udev-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_unref_ sd_bus
*bus
= NULL
;
48 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
53 r
= bus_connect_system(&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");
72 /* Don't print a warning if we aren't called during
74 if (!sd_bus_error_has_name(&error
, BUS_ERROR_NO_SUCH_JOB
))
75 log_error("Failed to start unit: %s", bus_error_message(&error
, -r
));
81 static int parse_proc_cmdline(void) {
82 char *line
, *w
, *state
;
86 if (detect_container(NULL
) > 0)
89 r
= read_one_line_file("/proc/cmdline", &line
);
91 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r
));
95 FOREACH_WORD_QUOTED(w
, l
, line
, state
) {
97 if (strneq(w
, "fsck.mode=auto", l
))
98 arg_force
= arg_skip
= false;
99 else if (strneq(w
, "fsck.mode=force", l
))
101 else if (strneq(w
, "fsck.mode=skip", l
))
103 else if (startswith(w
, "fsck"))
104 log_warning("Invalid fsck parameter. Ignoring.");
105 #ifdef HAVE_SYSV_COMPAT
106 else if (strneq(w
, "fastboot", l
)) {
107 log_error("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
109 } else if (strneq(w
, "forcefsck", l
)) {
110 log_error("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
120 static void test_files(void) {
121 #ifdef HAVE_SYSV_COMPAT
122 if (access("/fastboot", F_OK
) >= 0) {
123 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
127 if (access("/forcefsck", F_OK
) >= 0) {
128 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
133 if (access("/run/systemd/show-status", F_OK
) >= 0 || plymouth_running())
134 arg_show_progress
= true;
137 static double percent(int pass
, unsigned long cur
, unsigned long max
) {
138 /* Values stolen from e2fsck */
140 static const int pass_table
[] = {
141 0, 70, 90, 92, 95, 100
147 if ((unsigned) pass
>= ELEMENTSOF(pass_table
) || max
== 0)
150 return (double) pass_table
[pass
-1] +
151 ((double) pass_table
[pass
] - (double) pass_table
[pass
-1]) *
152 (double) cur
/ (double) max
;
155 static int process_progress(int fd
) {
163 close_nointr_nofail(fd
);
167 console
= fopen("/dev/console", "w");
175 unsigned long cur
, max
;
180 if (fscanf(f
, "%i %lu %lu %ms", &pass
, &cur
, &max
, &device
) != 4)
183 /* Only show one progress counter at max */
185 if (flock(fileno(console
), LOCK_EX
|LOCK_NB
) < 0) {
193 /* Only update once every 50ms */
194 t
= now(CLOCK_MONOTONIC
);
195 if (last
+ 50 * USEC_PER_MSEC
> t
) {
202 p
= percent(pass
, cur
, max
);
203 fprintf(console
, "\r%s: fsck %3.1f%% complete...\r%n", device
, p
, &m
);
215 fputc('\r', console
);
216 for (j
= 0; j
< (unsigned) clear
; j
++)
218 fputc('\r', console
);
227 int main(int argc
, char *argv
[]) {
228 const char *cmdline
[9];
229 int i
= 0, r
= EXIT_FAILURE
, q
;
232 _cleanup_udev_unref_
struct udev
*udev
= NULL
;
233 _cleanup_udev_device_unref_
struct udev_device
*udev_device
= NULL
;
236 int progress_pipe
[2] = { -1, -1 };
240 log_error("This program expects one or no arguments.");
244 log_set_target(LOG_TARGET_AUTO
);
245 log_parse_environment();
250 parse_proc_cmdline();
253 if (!arg_force
&& arg_skip
)
258 root_directory
= false;
261 struct timespec times
[2];
263 /* Find root device */
265 if (stat("/", &st
) < 0) {
266 log_error("Failed to stat() the root directory: %m");
270 /* Virtual root devices don't need an fsck */
271 if (major(st
.st_dev
) == 0)
274 /* check if we are already writable */
275 times
[0] = st
.st_atim
;
276 times
[1] = st
.st_mtim
;
277 if (utimensat(AT_FDCWD
, "/", times
, 0) == 0) {
278 log_info("Root directory is writable, skipping check.");
282 if (!(udev
= udev_new())) {
287 if (!(udev_device
= udev_device_new_from_devnum(udev
, 'b', st
.st_dev
))) {
288 log_error("Failed to detect root device.");
292 if (!(device
= udev_device_get_devnode(udev_device
))) {
293 log_error("Failed to detect device node of root directory.");
297 root_directory
= true;
300 if (arg_show_progress
)
301 if (pipe(progress_pipe
) < 0) {
302 log_error("pipe(): %m");
306 cmdline
[i
++] = "/sbin/fsck";
317 if (progress_pipe
[1] >= 0) {
318 snprintf(dash_c
, sizeof(dash_c
), "-C%i", progress_pipe
[1]);
319 char_array_0(dash_c
);
320 cmdline
[i
++] = dash_c
;
323 cmdline
[i
++] = device
;
328 log_error("fork(): %m");
330 } else if (pid
== 0) {
332 if (progress_pipe
[0] >= 0)
333 close_nointr_nofail(progress_pipe
[0]);
334 execv(cmdline
[0], (char**) cmdline
);
335 _exit(8); /* Operational error */
338 if (progress_pipe
[1] >= 0) {
339 close_nointr_nofail(progress_pipe
[1]);
340 progress_pipe
[1] = -1;
343 if (progress_pipe
[0] >= 0) {
344 process_progress(progress_pipe
[0]);
345 progress_pipe
[0] = -1;
348 q
= wait_for_terminate(pid
, &status
);
350 log_error("waitid(): %s", strerror(-q
));
354 if (status
.si_code
!= CLD_EXITED
|| (status
.si_status
& ~1)) {
356 if (status
.si_code
== CLD_KILLED
|| status
.si_code
== CLD_DUMPED
)
357 log_error("fsck terminated by signal %s.", signal_to_string(status
.si_status
));
358 else if (status
.si_code
== CLD_EXITED
)
359 log_error("fsck failed with error code %i.", status
.si_status
);
361 log_error("fsck failed due to unknown reason.");
363 if (status
.si_code
== CLD_EXITED
&& (status
.si_status
& 2) && root_directory
)
364 /* System should be rebooted. */
365 start_target(SPECIAL_REBOOT_TARGET
);
366 else if (status
.si_code
== CLD_EXITED
&& (status
.si_status
& 6))
367 /* Some other problem */
368 start_target(SPECIAL_EMERGENCY_TARGET
);
371 log_warning("Ignoring error.");
377 if (status
.si_code
== CLD_EXITED
&& (status
.si_status
& 1))
378 touch("/run/systemd/quotacheck");
381 close_pipe(progress_pipe
);