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