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