]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fsck/fsck.c
5c21c7e41f26c2dbf5f8e1afc002caa01dd31b38
[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 "sd-bus.h"
31 #include "libudev.h"
32
33 #include "util.h"
34 #include "special.h"
35 #include "bus-util.h"
36 #include "bus-error.h"
37 #include "bus-errors.h"
38 #include "virt.h"
39 #include "fileio.h"
40 #include "udev-util.h"
41
42 static bool arg_skip = false;
43 static bool arg_force = false;
44 static bool arg_show_progress = false;
45
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;
49 int r;
50
51 assert(target);
52
53 r = bus_connect_system(&bus);
54 if (r < 0) {
55 log_error("Failed to get D-Bus connection: %s", strerror(-r));
56 return;
57 }
58
59 log_info("Running request %s/start/replace", target);
60
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",
66 "StartUnitReplace",
67 &error,
68 NULL,
69 "sss", "basic.target", target, "replace");
70 if (r < 0) {
71
72 /* Don't print a warning if we aren't called during
73 * startup */
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));
76 }
77
78 return;
79 }
80
81 static int parse_proc_cmdline(void) {
82 char *line, *w, *state;
83 int r;
84 size_t l;
85
86 if (detect_container(NULL) > 0)
87 return 0;
88
89 r = read_one_line_file("/proc/cmdline", &line);
90 if (r < 0) {
91 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
92 return 0;
93 }
94
95 FOREACH_WORD_QUOTED(w, l, line, state) {
96
97 if (strneq(w, "fsck.mode=auto", l))
98 arg_force = arg_skip = false;
99 else if (strneq(w, "fsck.mode=force", l))
100 arg_force = true;
101 else if (strneq(w, "fsck.mode=skip", l))
102 arg_skip = true;
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.");
108 arg_skip = true;
109 } else if (strneq(w, "forcefsck", l)) {
110 log_error("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
111 arg_force = true;
112 }
113 #endif
114 }
115
116 free(line);
117 return 0;
118 }
119
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.");
124 arg_skip = true;
125 }
126
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.");
129 arg_force = true;
130 }
131 #endif
132
133 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
134 arg_show_progress = true;
135 }
136
137 static double percent(int pass, unsigned long cur, unsigned long max) {
138 /* Values stolen from e2fsck */
139
140 static const int pass_table[] = {
141 0, 70, 90, 92, 95, 100
142 };
143
144 if (pass <= 0)
145 return 0.0;
146
147 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
148 return 100.0;
149
150 return (double) pass_table[pass-1] +
151 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
152 (double) cur / (double) max;
153 }
154
155 static int process_progress(int fd) {
156 FILE *f, *console;
157 usec_t last = 0;
158 bool locked = false;
159 int clear = 0;
160
161 f = fdopen(fd, "r");
162 if (!f) {
163 close_nointr_nofail(fd);
164 return -errno;
165 }
166
167 console = fopen("/dev/console", "w");
168 if (!console) {
169 fclose(f);
170 return -ENOMEM;
171 }
172
173 while (!feof(f)) {
174 int pass, m;
175 unsigned long cur, max;
176 char *device;
177 double p;
178 usec_t t;
179
180 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
181 break;
182
183 /* Only show one progress counter at max */
184 if (!locked) {
185 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
186 free(device);
187 continue;
188 }
189
190 locked = true;
191 }
192
193 /* Only update once every 50ms */
194 t = now(CLOCK_MONOTONIC);
195 if (last + 50 * USEC_PER_MSEC > t) {
196 free(device);
197 continue;
198 }
199
200 last = t;
201
202 p = percent(pass, cur, max);
203 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
204 fflush(console);
205
206 free(device);
207
208 if (m > clear)
209 clear = m;
210 }
211
212 if (clear > 0) {
213 unsigned j;
214
215 fputc('\r', console);
216 for (j = 0; j < (unsigned) clear; j++)
217 fputc(' ', console);
218 fputc('\r', console);
219 fflush(console);
220 }
221
222 fclose(f);
223 fclose(console);
224 return 0;
225 }
226
227 int main(int argc, char *argv[]) {
228 const char *cmdline[9];
229 int i = 0, r = EXIT_FAILURE, q;
230 pid_t pid;
231 siginfo_t status;
232 _cleanup_udev_unref_ struct udev *udev = NULL;
233 _cleanup_udev_device_unref_ struct udev_device *udev_device = NULL;
234 const char *device;
235 bool root_directory;
236 int progress_pipe[2] = { -1, -1 };
237 char dash_c[2+10+1];
238
239 if (argc > 2) {
240 log_error("This program expects one or no arguments.");
241 return EXIT_FAILURE;
242 }
243
244 log_set_target(LOG_TARGET_AUTO);
245 log_parse_environment();
246 log_open();
247
248 umask(0022);
249
250 parse_proc_cmdline();
251 test_files();
252
253 if (!arg_force && arg_skip)
254 return 0;
255
256 if (argc > 1) {
257 device = argv[1];
258 root_directory = false;
259 } else {
260 struct stat st;
261 struct timespec times[2];
262
263 /* Find root device */
264
265 if (stat("/", &st) < 0) {
266 log_error("Failed to stat() the root directory: %m");
267 goto finish;
268 }
269
270 /* Virtual root devices don't need an fsck */
271 if (major(st.st_dev) == 0)
272 return 0;
273
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.");
279 return 0;
280 }
281
282 if (!(udev = udev_new())) {
283 log_oom();
284 goto finish;
285 }
286
287 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
288 log_error("Failed to detect root device.");
289 goto finish;
290 }
291
292 if (!(device = udev_device_get_devnode(udev_device))) {
293 log_error("Failed to detect device node of root directory.");
294 goto finish;
295 }
296
297 root_directory = true;
298 }
299
300 if (arg_show_progress)
301 if (pipe(progress_pipe) < 0) {
302 log_error("pipe(): %m");
303 goto finish;
304 }
305
306 cmdline[i++] = "/sbin/fsck";
307 cmdline[i++] = "-a";
308 cmdline[i++] = "-T";
309 cmdline[i++] = "-l";
310
311 if (!root_directory)
312 cmdline[i++] = "-M";
313
314 if (arg_force)
315 cmdline[i++] = "-f";
316
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;
321 }
322
323 cmdline[i++] = device;
324 cmdline[i++] = NULL;
325
326 pid = fork();
327 if (pid < 0) {
328 log_error("fork(): %m");
329 goto finish;
330 } else if (pid == 0) {
331 /* Child */
332 if (progress_pipe[0] >= 0)
333 close_nointr_nofail(progress_pipe[0]);
334 execv(cmdline[0], (char**) cmdline);
335 _exit(8); /* Operational error */
336 }
337
338 if (progress_pipe[1] >= 0) {
339 close_nointr_nofail(progress_pipe[1]);
340 progress_pipe[1] = -1;
341 }
342
343 if (progress_pipe[0] >= 0) {
344 process_progress(progress_pipe[0]);
345 progress_pipe[0] = -1;
346 }
347
348 q = wait_for_terminate(pid, &status);
349 if (q < 0) {
350 log_error("waitid(): %s", strerror(-q));
351 goto finish;
352 }
353
354 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
355
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);
360 else
361 log_error("fsck failed due to unknown reason.");
362
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);
369 else {
370 r = EXIT_SUCCESS;
371 log_warning("Ignoring error.");
372 }
373
374 } else
375 r = EXIT_SUCCESS;
376
377 if (status.si_code == CLD_EXITED && (status.si_status & 1))
378 touch("/run/systemd/quotacheck");
379
380 finish:
381 close_pipe(progress_pipe);
382
383 return r;
384 }