]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/fsck/fsck.c
tree-wide: make parse_proc_cmdline() strip "rd." prefix automatically
[thirdparty/systemd.git] / src / fsck / fsck.c
CommitLineData
3d20ed6d
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
f1f0198c 5 Copyright 2014 Holger Hans Peter Freyther
3d20ed6d
LP
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
3d20ed6d
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
3d20ed6d 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
3d20ed6d
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
3d20ed6d 21#include <errno.h>
a84f5192 22#include <fcntl.h>
cf0fbc49
TA
23#include <stdbool.h>
24#include <stdio.h>
27d340c7 25#include <sys/file.h>
96d9117a 26#include <sys/prctl.h>
cf0fbc49
TA
27#include <sys/stat.h>
28#include <unistd.h>
3d20ed6d 29
0c842e0a 30#include "sd-bus.h"
9102fdc5 31#include "sd-device.h"
3d20ed6d 32
b5efdb8a 33#include "alloc-util.h"
96aad8d1 34#include "bus-common-errors.h"
3ffd4af2
LP
35#include "bus-error.h"
36#include "bus-util.h"
9102fdc5 37#include "device-util.h"
3ffd4af2 38#include "fd-util.h"
f4f15635 39#include "fs-util.h"
6bedfcbb 40#include "parse-util.h"
eb66db55 41#include "path-util.h"
4e731273 42#include "proc-cmdline.h"
3ffd4af2
LP
43#include "process-util.h"
44#include "signal-util.h"
ac6e2f0d 45#include "socket-util.h"
3ffd4af2 46#include "special.h"
15a5e950 47#include "stdio-util.h"
3ffd4af2 48#include "util.h"
3d20ed6d 49
91077af6
TA
50/* exit codes as defined in fsck(8) */
51enum {
52 FSCK_SUCCESS = 0,
53 FSCK_ERROR_CORRECTED = 1,
54 FSCK_SYSTEM_SHOULD_REBOOT = 2,
55 FSCK_ERRORS_LEFT_UNCORRECTED = 4,
56 FSCK_OPERATIONAL_ERROR = 8,
57 FSCK_USAGE_OR_SYNTAX_ERROR = 16,
58 FSCK_USER_CANCELLED = 32,
59 FSCK_SHARED_LIB_ERROR = 128,
60};
61
3d20ed6d
LP
62static bool arg_skip = false;
63static bool arg_force = false;
96d9117a 64static bool arg_show_progress = false;
f1f0198c 65static const char *arg_repair = "-a";
3d20ed6d 66
48db40b3 67static void start_target(const char *target, const char *mode) {
4afd3348
LP
68 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
69 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
0c842e0a 70 int r;
3d20ed6d
LP
71
72 assert(target);
73
266f3e26 74 r = bus_connect_system_systemd(&bus);
0c842e0a 75 if (r < 0) {
da927ba9 76 log_error_errno(r, "Failed to get D-Bus connection: %m");
0c842e0a 77 return;
3d20ed6d
LP
78 }
79
0c842e0a 80 log_info("Running request %s/start/replace", target);
3d20ed6d 81
90bb85e1 82 /* Start these units only if we can replace base.target with it */
0c842e0a
TG
83 r = sd_bus_call_method(bus,
84 "org.freedesktop.systemd1",
85 "/org/freedesktop/systemd1",
86 "org.freedesktop.systemd1.Manager",
87 "StartUnitReplace",
88 &error,
89 NULL,
48db40b3 90 "sss", "basic.target", target, mode);
3d20ed6d 91
5220a6f3
LP
92 /* Don't print a warning if we aren't called during startup */
93 if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
24b52437 94 log_error("Failed to start unit: %s", bus_error_message(&error, r));
3d20ed6d
LP
95}
96
96287a49 97static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
24b52437
LP
98 int r;
99
100 assert(key);
059cb385
LP
101
102 if (streq(key, "fsck.mode") && value) {
103
104 if (streq(value, "auto"))
105 arg_force = arg_skip = false;
106 else if (streq(value, "force"))
107 arg_force = true;
108 else if (streq(value, "skip"))
109 arg_skip = true;
110 else
85013844
LP
111 log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value);
112
f1f0198c
HHPF
113 } else if (streq(key, "fsck.repair") && value) {
114
115 if (streq(value, "preen"))
116 arg_repair = "-a";
24b52437
LP
117 else {
118 r = parse_boolean(value);
119 if (r > 0)
120 arg_repair = "-y";
121 else if (r == 0)
122 arg_repair = "-n";
123 else
124 log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value);
125 }
85013844
LP
126 }
127
32f992a5 128#ifdef HAVE_SYSV_COMPAT
059cb385
LP
129 else if (streq(key, "fastboot") && !value) {
130 log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
141a79f4 131 arg_skip = true;
85013844 132
059cb385
LP
133 } else if (streq(key, "forcefsck") && !value) {
134 log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
141a79f4 135 arg_force = true;
3d20ed6d 136 }
141a79f4 137#endif
3d20ed6d 138
3d20ed6d
LP
139 return 0;
140}
141
142static void test_files(void) {
85013844 143
32f992a5
LP
144#ifdef HAVE_SYSV_COMPAT
145 if (access("/fastboot", F_OK) >= 0) {
146 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
3d20ed6d 147 arg_skip = true;
32f992a5 148 }
3d20ed6d 149
32f992a5
LP
150 if (access("/forcefsck", F_OK) >= 0) {
151 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
3d20ed6d 152 arg_force = true;
32f992a5
LP
153 }
154#endif
27d340c7 155
96d9117a 156 arg_show_progress = access("/run/systemd/show-status", F_OK) >= 0;
27d340c7
LP
157}
158
96d9117a
LP
159static 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
27d340c7
LP
164 };
165
96d9117a
LP
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
177static int process_progress(int fd) {
178 _cleanup_fclose_ FILE *console = NULL, *f = NULL;
179 usec_t last = 0;
180 bool locked = false;
181 int clear = 0, r;
182
183 /* No progress pipe to process? Then we are a NOP. */
184 if (fd < 0)
185 return 0;
186
187 f = fdopen(fd, "re");
188 if (!f) {
189 safe_close(fd);
190 return -errno;
191 }
192
193 console = fopen("/dev/console", "we");
194 if (!console)
195 return -ENOMEM;
196
197 for (;;) {
198 int pass, m;
199 unsigned long cur, max;
ac6e2f0d 200 _cleanup_free_ char *device = NULL;
96d9117a
LP
201 double p;
202 usec_t t;
27d340c7 203
96d9117a
LP
204 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4) {
205
206 if (ferror(f))
207 r = log_warning_errno(errno, "Failed to read from progress pipe: %m");
208 else if (feof(f))
209 r = 0;
210 else {
211 log_warning("Failed to parse progress pipe data");
212 r = -EBADMSG;
213 }
27d340c7 214 break;
96d9117a
LP
215 }
216
217 /* Only show one progress counter at max */
218 if (!locked) {
219 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0)
220 continue;
221
222 locked = true;
223 }
27d340c7 224
27d340c7
LP
225 /* Only update once every 50ms */
226 t = now(CLOCK_MONOTONIC);
e375825d 227 if (last + 50 * USEC_PER_MSEC > t)
27d340c7 228 continue;
27d340c7
LP
229
230 last = t;
231
96d9117a
LP
232 p = percent(pass, cur, max);
233 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
234 fflush(console);
235
236 if (m > clear)
237 clear = m;
27d340c7
LP
238 }
239
96d9117a
LP
240 if (clear > 0) {
241 unsigned j;
242
243 fputc('\r', console);
244 for (j = 0; j < (unsigned) clear; j++)
245 fputc(' ', console);
246 fputc('\r', console);
247 fflush(console);
248 }
249
250 return r;
251}
252
253static int fsck_progress_socket(void) {
254 static const union sockaddr_union sa = {
255 .un.sun_family = AF_UNIX,
256 .un.sun_path = "/run/systemd/fsck.progress",
257 };
258
259 int fd, r;
260
261 fd = socket(AF_UNIX, SOCK_STREAM, 0);
262 if (fd < 0)
263 return log_warning_errno(errno, "socket(): %m");
264
fc2fffe7 265 if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
96d9117a
LP
266 r = log_full_errno(errno == ECONNREFUSED || errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
267 errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path);
268 safe_close(fd);
269 return r;
270 }
271
272 return fd;
3d20ed6d
LP
273}
274
275int main(int argc, char *argv[]) {
96d9117a 276 _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 };
4afd3348 277 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
94192cda 278 const char *device, *type;
dc8e15c2 279 bool root_directory;
96d9117a 280 siginfo_t status;
94192cda 281 struct stat st;
96d9117a
LP
282 int r;
283 pid_t pid;
3d20ed6d 284
a9e1f5ec
LP
285 if (argc > 2) {
286 log_error("This program expects one or no arguments.");
3d20ed6d
LP
287 return EXIT_FAILURE;
288 }
289
27d340c7 290 log_set_target(LOG_TARGET_AUTO);
3d20ed6d
LP
291 log_parse_environment();
292 log_open();
293
4c12626c
LP
294 umask(0022);
295
d7f69e16 296 r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
e7a3aa3d
LP
297 if (r < 0)
298 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
b5884878 299
3d20ed6d
LP
300 test_files();
301
566690fd
LP
302 if (!arg_force && arg_skip) {
303 r = 0;
304 goto finish;
305 }
a9e1f5ec 306
dc8e15c2 307 if (argc > 1) {
a9e1f5ec 308 device = argv[1];
94192cda
ZJS
309
310 if (stat(device, &st) < 0) {
e7a3aa3d
LP
311 r = log_error_errno(errno, "Failed to stat %s: %m", device);
312 goto finish;
313 }
314
315 if (!S_ISBLK(st.st_mode)) {
316 log_error("%s is not a block device.", device);
317 r = -EINVAL;
566690fd 318 goto finish;
94192cda
ZJS
319 }
320
9102fdc5
TG
321 r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev);
322 if (r < 0) {
323 log_error_errno(r, "Failed to detect device %s: %m", device);
566690fd 324 goto finish;
94192cda 325 }
e7a3aa3d
LP
326
327 root_directory = false;
dc8e15c2 328 } else {
a84f5192 329 struct timespec times[2];
3d20ed6d 330
a9e1f5ec
LP
331 /* Find root device */
332
333 if (stat("/", &st) < 0) {
566690fd
LP
334 r = log_error_errno(errno, "Failed to stat() the root directory: %m");
335 goto finish;
a9e1f5ec
LP
336 }
337
338 /* Virtual root devices don't need an fsck */
566690fd 339 if (major(st.st_dev) == 0) {
e7a3aa3d 340 log_debug("Root directory is virtual or btrfs, skipping check.");
566690fd
LP
341 r = 0;
342 goto finish;
343 }
a9e1f5ec 344
a84f5192
KS
345 /* check if we are already writable */
346 times[0] = st.st_atim;
347 times[1] = st.st_mtim;
e7a3aa3d 348
a84f5192 349 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
cf1a1055 350 log_info("Root directory is writable, skipping check.");
566690fd
LP
351 r = 0;
352 goto finish;
a84f5192
KS
353 }
354
9102fdc5
TG
355 r = sd_device_new_from_devnum(&dev, 'b', st.st_dev);
356 if (r < 0) {
357 log_error_errno(r, "Failed to detect root device: %m");
566690fd 358 goto finish;
a9e1f5ec
LP
359 }
360
9102fdc5
TG
361 r = sd_device_get_devname(dev, &device);
362 if (r < 0) {
363 log_error_errno(r, "Failed to detect device node of root directory: %m");
566690fd 364 goto finish;
3e33a44a 365 }
dc8e15c2
LP
366
367 root_directory = true;
3d20ed6d
LP
368 }
369
9102fdc5
TG
370 r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type);
371 if (r >= 0) {
eb66db55 372 r = fsck_exists(type);
85eca92e
LP
373 if (r < 0)
374 log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s, proceeding: %m", type, device);
375 else if (r == 0) {
376 log_info("fsck.%s doesn't exist, not checking file system on %s.", type, device);
566690fd 377 goto finish;
85eca92e 378 }
94192cda
ZJS
379 }
380
96d9117a
LP
381 if (arg_show_progress) {
382 if (pipe(progress_pipe) < 0) {
383 r = log_error_errno(errno, "pipe(): %m");
384 goto finish;
385 }
19e887e7 386 }
27d340c7 387
27d340c7
LP
388 pid = fork();
389 if (pid < 0) {
566690fd 390 r = log_error_errno(errno, "fork(): %m");
3d20ed6d 391 goto finish;
96d9117a
LP
392 }
393 if (pid == 0) {
394 char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1];
395 int progress_socket = -1;
396 const char *cmdline[9];
397 int i = 0;
398
3d20ed6d 399 /* Child */
96d9117a 400
ce30c8dc
LP
401 (void) reset_all_signal_handlers();
402 (void) reset_signal_mask();
96d9117a
LP
403 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
404
405 /* Close the reading side of the progress pipe */
1952708a 406 progress_pipe[0] = safe_close(progress_pipe[0]);
96d9117a
LP
407
408 /* Try to connect to a progress management daemon, if there is one */
409 progress_socket = fsck_progress_socket();
410 if (progress_socket >= 0) {
411 /* If this worked we close the progress pipe early, and just use the socket */
412 progress_pipe[1] = safe_close(progress_pipe[1]);
413 xsprintf(dash_c, "-C%i", progress_socket);
414 } else if (progress_pipe[1] >= 0) {
415 /* Otherwise if we have the progress pipe to our own local handle, we use it */
416 xsprintf(dash_c, "-C%i", progress_pipe[1]);
417 } else
418 dash_c[0] = 0;
419
420 cmdline[i++] = "/sbin/fsck";
421 cmdline[i++] = arg_repair;
422 cmdline[i++] = "-T";
423
424 /*
425 * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files.
426 * The previous versions use flock for the device and conflict with
427 * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5
428 */
429 cmdline[i++] = "-l";
430
431 if (!root_directory)
432 cmdline[i++] = "-M";
433
434 if (arg_force)
435 cmdline[i++] = "-f";
436
437 if (!isempty(dash_c))
438 cmdline[i++] = dash_c;
439
440 cmdline[i++] = device;
441 cmdline[i++] = NULL;
442
3d20ed6d 443 execv(cmdline[0], (char**) cmdline);
91077af6 444 _exit(FSCK_OPERATIONAL_ERROR);
3d20ed6d
LP
445 }
446
03e334a1 447 progress_pipe[1] = safe_close(progress_pipe[1]);
96d9117a 448 (void) process_progress(progress_pipe[0]);
576a13ea 449 progress_pipe[0] = -1;
27d340c7 450
566690fd
LP
451 r = wait_for_terminate(pid, &status);
452 if (r < 0) {
453 log_error_errno(r, "waitid(): %m");
3d20ed6d
LP
454 goto finish;
455 }
456
96d9117a 457 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
3d20ed6d 458
96d9117a 459 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
a9e1f5ec
LP
460 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
461 else if (status.si_code == CLD_EXITED)
462 log_error("fsck failed with error code %i.", status.si_status);
96d9117a 463 else
a9e1f5ec 464 log_error("fsck failed due to unknown reason.");
3d20ed6d 465
566690fd
LP
466 r = -EINVAL;
467
91077af6 468 if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory)
3d20ed6d 469 /* System should be rebooted. */
48db40b3 470 start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly");
91077af6 471 else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)))
3d20ed6d 472 /* Some other problem */
48db40b3 473 start_target(SPECIAL_EMERGENCY_TARGET, "replace");
a4c24ff7 474 else {
96d9117a 475 log_warning("Ignoring error.");
566690fd 476 r = 0;
a4c24ff7 477 }
3d20ed6d
LP
478
479 } else
566690fd 480 r = 0;
3d20ed6d 481
91077af6 482 if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED))
e7a3aa3d 483 (void) touch("/run/systemd/quotacheck");
3d20ed6d
LP
484
485finish:
566690fd 486 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
3d20ed6d 487}