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 #if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
132 else if (strneq(w, "fastboot", l))
134 else if (strneq(w, "forcefsck", l))
143 static void test_files(void) {
144 if (access("/fastboot", F_OK) >= 0)
147 if (access("/forcefsck", F_OK) >= 0)
150 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
151 arg_show_progress = true;
154 static double percent(int pass, unsigned long cur, unsigned long max) {
155 /* Values stolen from e2fsck */
157 static const int pass_table[] = {
158 0, 70, 90, 92, 95, 100
164 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
167 return (double) pass_table[pass-1] +
168 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
169 (double) cur / (double) max;
172 static int process_progress(int fd) {
180 close_nointr_nofail(fd);
184 console = fopen("/dev/console", "w");
192 unsigned long cur, max;
197 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
200 /* Only show one progress counter at max */
202 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
210 /* Only update once every 50ms */
211 t = now(CLOCK_MONOTONIC);
212 if (last + 50 * USEC_PER_MSEC > t) {
219 p = percent(pass, cur, max);
220 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
232 fputc('\r', console);
233 for (j = 0; j < (unsigned) clear; j++)
235 fputc('\r', console);
244 int main(int argc, char *argv[]) {
245 const char *cmdline[9];
246 int i = 0, r = EXIT_FAILURE, q;
249 struct udev *udev = NULL;
250 struct udev_device *udev_device = NULL;
253 int progress_pipe[2] = { -1, -1 };
257 log_error("This program expects one or no arguments.");
261 log_set_target(LOG_TARGET_AUTO);
262 log_parse_environment();
267 parse_proc_cmdline();
270 if (!arg_force && arg_skip)
275 root_directory = false;
278 struct timespec times[2];
280 /* Find root device */
282 if (stat("/", &st) < 0) {
283 log_error("Failed to stat() the root directory: %m");
287 /* Virtual root devices don't need an fsck */
288 if (major(st.st_dev) == 0)
291 /* check if we are already writable */
292 times[0] = st.st_atim;
293 times[1] = st.st_mtim;
294 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
295 log_info("Root directory is writable, skipping check.");
299 if (!(udev = udev_new())) {
300 log_error("Out of memory");
304 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
305 log_error("Failed to detect root device.");
309 if (!(device = udev_device_get_devnode(udev_device))) {
310 log_error("Failed to detect device node of root directory.");
314 root_directory = true;
317 if (arg_show_progress)
318 if (pipe(progress_pipe) < 0) {
319 log_error("pipe(): %m");
323 cmdline[i++] = "/sbin/fsck";
334 if (progress_pipe[1] >= 0) {
335 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
336 char_array_0(dash_c);
337 cmdline[i++] = dash_c;
340 cmdline[i++] = device;
345 log_error("fork(): %m");
347 } else if (pid == 0) {
349 if (progress_pipe[0] >= 0)
350 close_nointr_nofail(progress_pipe[0]);
351 execv(cmdline[0], (char**) cmdline);
352 _exit(8); /* Operational error */
355 if (progress_pipe[1] >= 0) {
356 close_nointr_nofail(progress_pipe[1]);
357 progress_pipe[1] = -1;
360 if (progress_pipe[0] >= 0) {
361 process_progress(progress_pipe[0]);
362 progress_pipe[0] = -1;
365 q = wait_for_terminate(pid, &status);
367 log_error("waitid(): %s", strerror(-q));
371 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
373 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
374 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
375 else if (status.si_code == CLD_EXITED)
376 log_error("fsck failed with error code %i.", status.si_status);
378 log_error("fsck failed due to unknown reason.");
380 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
381 /* System should be rebooted. */
382 start_target(SPECIAL_REBOOT_TARGET, false);
383 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
384 /* Some other problem */
385 start_target(SPECIAL_EMERGENCY_TARGET, true);
388 log_warning("Ignoring error.");
394 if (status.si_code == CLD_EXITED && (status.si_status & 1))
395 touch("/run/systemd/quotacheck");
399 udev_device_unref(udev_device);
404 close_pipe(progress_pipe);