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 if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
116 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
120 FOREACH_WORD_QUOTED(w, l, line, state) {
122 if (strneq(w, "fsck.mode=auto", l))
123 arg_force = arg_skip = false;
124 else if (strneq(w, "fsck.mode=force", l))
126 else if (strneq(w, "fsck.mode=skip", l))
128 else if (startswith(w, "fsck.mode"))
129 log_warning("Invalid fsck.mode= parameter. Ignoring.");
130 #if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
131 else if (strneq(w, "fastboot", l))
133 else if (strneq(w, "forcefsck", l))
142 static void test_files(void) {
143 if (access("/fastboot", F_OK) >= 0)
146 if (access("/forcefsck", F_OK) >= 0)
149 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
150 arg_show_progress = true;
153 static double percent(int pass, unsigned long cur, unsigned long max) {
154 /* Values stolen from e2fsck */
156 static const int pass_table[] = {
157 0, 70, 90, 92, 95, 100
163 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
166 return (double) pass_table[pass-1] +
167 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
168 (double) cur / (double) max;
171 static int process_progress(int fd) {
179 close_nointr_nofail(fd);
183 console = fopen("/dev/console", "w");
191 unsigned long cur, max;
196 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
199 /* Only show one progress counter at max */
201 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
209 /* Only update once every 50ms */
210 t = now(CLOCK_MONOTONIC);
211 if (last + 50 * USEC_PER_MSEC > t) {
218 p = percent(pass, cur, max);
219 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
231 fputc('\r', console);
232 for (j = 0; j < (unsigned) clear; j++)
234 fputc('\r', console);
243 int main(int argc, char *argv[]) {
244 const char *cmdline[9];
245 int i = 0, r = EXIT_FAILURE, q;
248 struct udev *udev = NULL;
249 struct udev_device *udev_device = NULL;
252 int progress_pipe[2] = { -1, -1 };
256 log_error("This program expects one or no arguments.");
260 log_set_target(LOG_TARGET_AUTO);
261 log_parse_environment();
266 parse_proc_cmdline();
269 if (!arg_force && arg_skip)
274 root_directory = false;
277 struct timespec times[2];
279 /* Find root device */
281 if (stat("/", &st) < 0) {
282 log_error("Failed to stat() the root directory: %m");
286 /* Virtual root devices don't need an fsck */
287 if (major(st.st_dev) == 0)
290 /* check if we are already writable */
291 times[0] = st.st_atim;
292 times[1] = st.st_mtim;
293 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
294 log_info("Root directory is writable, skipping check.");
298 if (!(udev = udev_new())) {
299 log_error("Out of memory");
303 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
304 log_error("Failed to detect root device.");
308 if (!(device = udev_device_get_devnode(udev_device))) {
309 log_error("Failed to detect device node of root directory.");
313 root_directory = true;
316 if (arg_show_progress)
317 if (pipe(progress_pipe) < 0) {
318 log_error("pipe(): %m");
322 cmdline[i++] = "/sbin/fsck";
333 if (progress_pipe[1] >= 0) {
334 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
335 char_array_0(dash_c);
336 cmdline[i++] = dash_c;
339 cmdline[i++] = device;
344 log_error("fork(): %m");
346 } else if (pid == 0) {
348 if (progress_pipe[0] >= 0)
349 close_nointr_nofail(progress_pipe[0]);
350 execv(cmdline[0], (char**) cmdline);
351 _exit(8); /* Operational error */
354 if (progress_pipe[1] >= 0) {
355 close_nointr_nofail(progress_pipe[1]);
356 progress_pipe[1] = -1;
359 if (progress_pipe[0] >= 0) {
360 process_progress(progress_pipe[0]);
361 progress_pipe[0] = -1;
364 q = wait_for_terminate(pid, &status);
366 log_error("waitid(): %s", strerror(-q));
370 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
372 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
373 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
374 else if (status.si_code == CLD_EXITED)
375 log_error("fsck failed with error code %i.", status.si_status);
377 log_error("fsck failed due to unknown reason.");
379 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
380 /* System should be rebooted. */
381 start_target(SPECIAL_REBOOT_TARGET, false);
382 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
383 /* Some other problem */
384 start_target(SPECIAL_EMERGENCY_TARGET, true);
387 log_warning("Ignoring error.");
393 if (status.si_code == CLD_EXITED && (status.si_status & 1))
394 touch("/run/systemd/quotacheck");
398 udev_device_unref(udev_device);
403 close_pipe(progress_pipe);