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