]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fsck/fsck.c
Do no isolate in case of emergency or severe problems
[thirdparty/systemd.git] / src / fsck / fsck.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <stdio.h>
23 #include <stdbool.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/file.h>
29
30 #include <libudev.h>
31 #include <dbus/dbus.h>
32
33 #include "util.h"
34 #include "dbus-common.h"
35 #include "special.h"
36 #include "bus-errors.h"
37 #include "virt.h"
38 #include "fileio.h"
39
40 static bool arg_skip = false;
41 static bool arg_force = false;
42 static bool arg_show_progress = false;
43
44 static void start_target(const char *target) {
45 DBusMessage *m = NULL, *reply = NULL;
46 DBusError error;
47 const char *mode = "replace", *basic_target = "basic.target";
48 DBusConnection *bus = NULL;
49
50 assert(target);
51
52 dbus_error_init(&error);
53
54 if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) {
55 log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
56 goto finish;
57 }
58
59 log_info("Running request %s/start/%s", target, mode);
60
61 if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnitReplace"))) {
62 log_error("Could not allocate message.");
63 goto finish;
64 }
65
66 /* Start these units only if we can replace base.target with it */
67
68 if (!dbus_message_append_args(m,
69 DBUS_TYPE_STRING, &basic_target,
70 DBUS_TYPE_STRING, &target,
71 DBUS_TYPE_STRING, &mode,
72 DBUS_TYPE_INVALID)) {
73 log_error("Could not attach target and flag information to message.");
74 goto finish;
75 }
76
77 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
78
79 /* Don't print a warning if we aren't called during
80 * startup */
81 if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
82 log_error("Failed to start unit: %s", bus_error_message(&error));
83
84 goto finish;
85 }
86
87 finish:
88 if (m)
89 dbus_message_unref(m);
90
91 if (reply)
92 dbus_message_unref(reply);
93
94 if (bus) {
95 dbus_connection_flush(bus);
96 dbus_connection_close(bus);
97 dbus_connection_unref(bus);
98 }
99
100 dbus_error_free(&error);
101 }
102
103 static int parse_proc_cmdline(void) {
104 char *line, *w, *state;
105 int r;
106 size_t l;
107
108 if (detect_container(NULL) > 0)
109 return 0;
110
111 r = read_one_line_file("/proc/cmdline", &line);
112 if (r < 0) {
113 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
114 return 0;
115 }
116
117 FOREACH_WORD_QUOTED(w, l, line, state) {
118
119 if (strneq(w, "fsck.mode=auto", l))
120 arg_force = arg_skip = false;
121 else if (strneq(w, "fsck.mode=force", l))
122 arg_force = true;
123 else if (strneq(w, "fsck.mode=skip", l))
124 arg_skip = true;
125 else if (startswith(w, "fsck"))
126 log_warning("Invalid fsck parameter. Ignoring.");
127 #ifdef HAVE_SYSV_COMPAT
128 else if (strneq(w, "fastboot", l)) {
129 log_error("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
130 arg_skip = true;
131 } else if (strneq(w, "forcefsck", l)) {
132 log_error("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
133 arg_force = true;
134 }
135 #endif
136 }
137
138 free(line);
139 return 0;
140 }
141
142 static void test_files(void) {
143 #ifdef HAVE_SYSV_COMPAT
144 if (access("/fastboot", F_OK) >= 0) {
145 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
146 arg_skip = true;
147 }
148
149 if (access("/forcefsck", F_OK) >= 0) {
150 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
151 arg_force = true;
152 }
153 #endif
154
155 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
156 arg_show_progress = true;
157 }
158
159 static double percent(int pass, unsigned long cur, unsigned long max) {
160 /* Values stolen from e2fsck */
161
162 static const int pass_table[] = {
163 0, 70, 90, 92, 95, 100
164 };
165
166 if (pass <= 0)
167 return 0.0;
168
169 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
170 return 100.0;
171
172 return (double) pass_table[pass-1] +
173 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
174 (double) cur / (double) max;
175 }
176
177 static int process_progress(int fd) {
178 FILE *f, *console;
179 usec_t last = 0;
180 bool locked = false;
181 int clear = 0;
182
183 f = fdopen(fd, "r");
184 if (!f) {
185 close_nointr_nofail(fd);
186 return -errno;
187 }
188
189 console = fopen("/dev/console", "w");
190 if (!console) {
191 fclose(f);
192 return -ENOMEM;
193 }
194
195 while (!feof(f)) {
196 int pass, m;
197 unsigned long cur, max;
198 char *device;
199 double p;
200 usec_t t;
201
202 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
203 break;
204
205 /* Only show one progress counter at max */
206 if (!locked) {
207 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
208 free(device);
209 continue;
210 }
211
212 locked = true;
213 }
214
215 /* Only update once every 50ms */
216 t = now(CLOCK_MONOTONIC);
217 if (last + 50 * USEC_PER_MSEC > t) {
218 free(device);
219 continue;
220 }
221
222 last = t;
223
224 p = percent(pass, cur, max);
225 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
226 fflush(console);
227
228 free(device);
229
230 if (m > clear)
231 clear = m;
232 }
233
234 if (clear > 0) {
235 unsigned j;
236
237 fputc('\r', console);
238 for (j = 0; j < (unsigned) clear; j++)
239 fputc(' ', console);
240 fputc('\r', console);
241 fflush(console);
242 }
243
244 fclose(f);
245 fclose(console);
246 return 0;
247 }
248
249 int main(int argc, char *argv[]) {
250 const char *cmdline[9];
251 int i = 0, r = EXIT_FAILURE, q;
252 pid_t pid;
253 siginfo_t status;
254 struct udev *udev = NULL;
255 struct udev_device *udev_device = NULL;
256 const char *device;
257 bool root_directory;
258 int progress_pipe[2] = { -1, -1 };
259 char dash_c[2+10+1];
260
261 if (argc > 2) {
262 log_error("This program expects one or no arguments.");
263 return EXIT_FAILURE;
264 }
265
266 log_set_target(LOG_TARGET_AUTO);
267 log_parse_environment();
268 log_open();
269
270 umask(0022);
271
272 parse_proc_cmdline();
273 test_files();
274
275 if (!arg_force && arg_skip)
276 return 0;
277
278 if (argc > 1) {
279 device = argv[1];
280 root_directory = false;
281 } else {
282 struct stat st;
283 struct timespec times[2];
284
285 /* Find root device */
286
287 if (stat("/", &st) < 0) {
288 log_error("Failed to stat() the root directory: %m");
289 goto finish;
290 }
291
292 /* Virtual root devices don't need an fsck */
293 if (major(st.st_dev) == 0)
294 return 0;
295
296 /* check if we are already writable */
297 times[0] = st.st_atim;
298 times[1] = st.st_mtim;
299 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
300 log_info("Root directory is writable, skipping check.");
301 return 0;
302 }
303
304 if (!(udev = udev_new())) {
305 log_oom();
306 goto finish;
307 }
308
309 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
310 log_error("Failed to detect root device.");
311 goto finish;
312 }
313
314 if (!(device = udev_device_get_devnode(udev_device))) {
315 log_error("Failed to detect device node of root directory.");
316 goto finish;
317 }
318
319 root_directory = true;
320 }
321
322 if (arg_show_progress)
323 if (pipe(progress_pipe) < 0) {
324 log_error("pipe(): %m");
325 goto finish;
326 }
327
328 cmdline[i++] = "/sbin/fsck";
329 cmdline[i++] = "-a";
330 cmdline[i++] = "-T";
331 cmdline[i++] = "-l";
332
333 if (!root_directory)
334 cmdline[i++] = "-M";
335
336 if (arg_force)
337 cmdline[i++] = "-f";
338
339 if (progress_pipe[1] >= 0) {
340 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
341 char_array_0(dash_c);
342 cmdline[i++] = dash_c;
343 }
344
345 cmdline[i++] = device;
346 cmdline[i++] = NULL;
347
348 pid = fork();
349 if (pid < 0) {
350 log_error("fork(): %m");
351 goto finish;
352 } else if (pid == 0) {
353 /* Child */
354 if (progress_pipe[0] >= 0)
355 close_nointr_nofail(progress_pipe[0]);
356 execv(cmdline[0], (char**) cmdline);
357 _exit(8); /* Operational error */
358 }
359
360 if (progress_pipe[1] >= 0) {
361 close_nointr_nofail(progress_pipe[1]);
362 progress_pipe[1] = -1;
363 }
364
365 if (progress_pipe[0] >= 0) {
366 process_progress(progress_pipe[0]);
367 progress_pipe[0] = -1;
368 }
369
370 q = wait_for_terminate(pid, &status);
371 if (q < 0) {
372 log_error("waitid(): %s", strerror(-q));
373 goto finish;
374 }
375
376 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
377
378 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
379 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
380 else if (status.si_code == CLD_EXITED)
381 log_error("fsck failed with error code %i.", status.si_status);
382 else
383 log_error("fsck failed due to unknown reason.");
384
385 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
386 /* System should be rebooted. */
387 start_target(SPECIAL_REBOOT_TARGET);
388 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
389 /* Some other problem */
390 start_target(SPECIAL_EMERGENCY_TARGET);
391 else {
392 r = EXIT_SUCCESS;
393 log_warning("Ignoring error.");
394 }
395
396 } else
397 r = EXIT_SUCCESS;
398
399 if (status.si_code == CLD_EXITED && (status.si_status & 1))
400 touch("/run/systemd/quotacheck");
401
402 finish:
403 if (udev_device)
404 udev_device_unref(udev_device);
405
406 if (udev)
407 udev_unref(udev);
408
409 close_pipe(progress_pipe);
410
411 return r;
412 }