]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/fsck/fsck.c
util: simplify proc_cmdline() to reuse get_process_cmdline()
[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>
25#include <string.h>
26#include <errno.h>
27#include <unistd.h>
a84f5192 28#include <fcntl.h>
27d340c7 29#include <sys/file.h>
3d20ed6d 30
0c842e0a
TG
31#include "sd-bus.h"
32#include "libudev.h"
3d20ed6d
LP
33
34#include "util.h"
3d20ed6d 35#include "special.h"
0c842e0a
TG
36#include "bus-util.h"
37#include "bus-error.h"
ef1de59b 38#include "bus-errors.h"
a5c32cff 39#include "fileio.h"
1ca208fb 40#include "udev-util.h"
eb66db55 41#include "path-util.h"
3d20ed6d
LP
42
43static bool arg_skip = false;
44static bool arg_force = false;
27d340c7 45static bool arg_show_progress = false;
f1f0198c 46static const char *arg_repair = "-a";
3d20ed6d 47
80cfe9e1 48static void start_target(const char *target) {
0c842e0a 49 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
24996861 50 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
0c842e0a 51 int r;
3d20ed6d
LP
52
53 assert(target);
54
0f8bd8de 55 r = bus_open_system_systemd(&bus);
0c842e0a
TG
56 if (r < 0) {
57 log_error("Failed to get D-Bus connection: %s", strerror(-r));
58 return;
3d20ed6d
LP
59 }
60
0c842e0a 61 log_info("Running request %s/start/replace", target);
3d20ed6d 62
90bb85e1 63 /* Start these units only if we can replace base.target with it */
0c842e0a
TG
64 r = sd_bus_call_method(bus,
65 "org.freedesktop.systemd1",
66 "/org/freedesktop/systemd1",
67 "org.freedesktop.systemd1.Manager",
68 "StartUnitReplace",
69 &error,
70 NULL,
71 "sss", "basic.target", target, "replace");
3d20ed6d 72
5220a6f3
LP
73 /* Don't print a warning if we aren't called during startup */
74 if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
75 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
3d20ed6d
LP
76}
77
059cb385
LP
78static int parse_proc_cmdline_item(const char *key, const char *value) {
79
80 if (streq(key, "fsck.mode") && value) {
81
82 if (streq(value, "auto"))
83 arg_force = arg_skip = false;
84 else if (streq(value, "force"))
85 arg_force = true;
86 else if (streq(value, "skip"))
87 arg_skip = true;
88 else
85013844
LP
89 log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value);
90
f1f0198c
HHPF
91 } else if (streq(key, "fsck.repair") && value) {
92
93 if (streq(value, "preen"))
94 arg_repair = "-a";
95 else if (streq(value, "yes"))
96 arg_repair = "-y";
97 else if (streq(value, "no"))
98 arg_repair = "-n";
99 else
85013844
LP
100 log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value);
101 }
102
32f992a5 103#ifdef HAVE_SYSV_COMPAT
059cb385
LP
104 else if (streq(key, "fastboot") && !value) {
105 log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
141a79f4 106 arg_skip = true;
85013844 107
059cb385
LP
108 } else if (streq(key, "forcefsck") && !value) {
109 log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
141a79f4 110 arg_force = true;
3d20ed6d 111 }
141a79f4 112#endif
3d20ed6d 113
3d20ed6d
LP
114 return 0;
115}
116
117static void test_files(void) {
85013844 118
32f992a5
LP
119#ifdef HAVE_SYSV_COMPAT
120 if (access("/fastboot", F_OK) >= 0) {
121 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
3d20ed6d 122 arg_skip = true;
32f992a5 123 }
3d20ed6d 124
32f992a5
LP
125 if (access("/forcefsck", F_OK) >= 0) {
126 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
3d20ed6d 127 arg_force = true;
32f992a5
LP
128 }
129#endif
27d340c7
LP
130
131 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
132 arg_show_progress = true;
133}
134
135static double percent(int pass, unsigned long cur, unsigned long max) {
136 /* Values stolen from e2fsck */
137
138 static const int pass_table[] = {
139 0, 70, 90, 92, 95, 100
140 };
141
142 if (pass <= 0)
143 return 0.0;
144
145 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
146 return 100.0;
147
148 return (double) pass_table[pass-1] +
149 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
150 (double) cur / (double) max;
151}
152
153static int process_progress(int fd) {
e375825d 154 _cleanup_fclose_ FILE *console = NULL, *f = NULL;
27d340c7
LP
155 usec_t last = 0;
156 bool locked = false;
157 int clear = 0;
158
159 f = fdopen(fd, "r");
160 if (!f) {
03e334a1 161 safe_close(fd);
27d340c7
LP
162 return -errno;
163 }
164
c8a202b7 165 console = fopen("/dev/console", "we");
e375825d 166 if (!console)
27d340c7 167 return -ENOMEM;
27d340c7
LP
168
169 while (!feof(f)) {
170 int pass, m;
171 unsigned long cur, max;
e375825d 172 _cleanup_free_ char *device = NULL;
27d340c7
LP
173 double p;
174 usec_t t;
175
176 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
177 break;
178
179 /* Only show one progress counter at max */
180 if (!locked) {
e375825d 181 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0)
27d340c7 182 continue;
27d340c7
LP
183
184 locked = true;
185 }
186
187 /* Only update once every 50ms */
188 t = now(CLOCK_MONOTONIC);
e375825d 189 if (last + 50 * USEC_PER_MSEC > t)
27d340c7 190 continue;
27d340c7
LP
191
192 last = t;
193
194 p = percent(pass, cur, max);
195 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
196 fflush(console);
197
27d340c7
LP
198 if (m > clear)
199 clear = m;
200 }
201
202 if (clear > 0) {
203 unsigned j;
204
205 fputc('\r', console);
206 for (j = 0; j < (unsigned) clear; j++)
207 fputc(' ', console);
208 fputc('\r', console);
209 fflush(console);
210 }
211
27d340c7 212 return 0;
3d20ed6d
LP
213}
214
215int main(int argc, char *argv[]) {
27d340c7 216 const char *cmdline[9];
3d20ed6d
LP
217 int i = 0, r = EXIT_FAILURE, q;
218 pid_t pid;
219 siginfo_t status;
1ca208fb
ZJS
220 _cleanup_udev_unref_ struct udev *udev = NULL;
221 _cleanup_udev_device_unref_ struct udev_device *udev_device = NULL;
94192cda 222 const char *device, *type;
dc8e15c2 223 bool root_directory;
27d340c7
LP
224 int progress_pipe[2] = { -1, -1 };
225 char dash_c[2+10+1];
94192cda 226 struct stat st;
3d20ed6d 227
a9e1f5ec
LP
228 if (argc > 2) {
229 log_error("This program expects one or no arguments.");
3d20ed6d
LP
230 return EXIT_FAILURE;
231 }
232
27d340c7 233 log_set_target(LOG_TARGET_AUTO);
3d20ed6d
LP
234 log_parse_environment();
235 log_open();
236
4c12626c
LP
237 umask(0022);
238
b5884878
LP
239 q = parse_proc_cmdline(parse_proc_cmdline_item);
240 if (q < 0)
241 log_warning("Failed to parse kernel command line, ignoring: %s", strerror(-q));
242
3d20ed6d
LP
243 test_files();
244
a9e1f5ec
LP
245 if (!arg_force && arg_skip)
246 return 0;
247
94192cda
ZJS
248 udev = udev_new();
249 if (!udev) {
250 log_oom();
251 return EXIT_FAILURE;
252 }
253
dc8e15c2 254 if (argc > 1) {
a9e1f5ec 255 device = argv[1];
dc8e15c2 256 root_directory = false;
94192cda
ZJS
257
258 if (stat(device, &st) < 0) {
259 log_error("Failed to stat '%s': %m", device);
260 return EXIT_FAILURE;
261 }
262
263 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
264 if (!udev_device) {
265 log_error("Failed to detect device %s", device);
266 return EXIT_FAILURE;
267 }
dc8e15c2 268 } else {
a84f5192 269 struct timespec times[2];
3d20ed6d 270
a9e1f5ec
LP
271 /* Find root device */
272
273 if (stat("/", &st) < 0) {
274 log_error("Failed to stat() the root directory: %m");
e375825d 275 return EXIT_FAILURE;
a9e1f5ec
LP
276 }
277
278 /* Virtual root devices don't need an fsck */
279 if (major(st.st_dev) == 0)
e375825d 280 return EXIT_SUCCESS;
a9e1f5ec 281
a84f5192
KS
282 /* check if we are already writable */
283 times[0] = st.st_atim;
284 times[1] = st.st_mtim;
285 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
cf1a1055 286 log_info("Root directory is writable, skipping check.");
e375825d 287 return EXIT_SUCCESS;
a84f5192
KS
288 }
289
e375825d
ZJS
290 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
291 if (!udev_device) {
a9e1f5ec 292 log_error("Failed to detect root device.");
e375825d 293 return EXIT_FAILURE;
a9e1f5ec
LP
294 }
295
e375825d
ZJS
296 device = udev_device_get_devnode(udev_device);
297 if (!device) {
a9e1f5ec 298 log_error("Failed to detect device node of root directory.");
e375825d 299 return EXIT_FAILURE;
3e33a44a 300 }
dc8e15c2
LP
301
302 root_directory = true;
3d20ed6d
LP
303 }
304
94192cda
ZJS
305 type = udev_device_get_property_value(udev_device, "ID_FS_TYPE");
306 if (type) {
eb66db55 307 r = fsck_exists(type);
571d0134
LP
308 if (r == -ENOENT) {
309 log_info("fsck.%s doesn't exist, not checking file system on %s", type, device);
310 return EXIT_SUCCESS;
311 } else if (r < 0)
312 log_warning("fsck.%s cannot be used for %s: %s", type, device, strerror(-r));
94192cda
ZJS
313 }
314
27d340c7
LP
315 if (arg_show_progress)
316 if (pipe(progress_pipe) < 0) {
317 log_error("pipe(): %m");
e375825d 318 return EXIT_FAILURE;
27d340c7
LP
319 }
320
3d20ed6d 321 cmdline[i++] = "/sbin/fsck";
f1f0198c 322 cmdline[i++] = arg_repair;
3d20ed6d 323 cmdline[i++] = "-T";
c343be28
KS
324
325 /*
48d3e8d0
KZ
326 * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files.
327 * The previous versions use flock for the device and conflict with
328 * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5
c343be28 329 */
48d3e8d0 330 cmdline[i++] = "-l";
dc8e15c2
LP
331
332 if (!root_directory)
333 cmdline[i++] = "-M";
3d20ed6d
LP
334
335 if (arg_force)
336 cmdline[i++] = "-f";
337
27d340c7
LP
338 if (progress_pipe[1] >= 0) {
339 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
340 char_array_0(dash_c);
341 cmdline[i++] = dash_c;
342 }
343
a9e1f5ec 344 cmdline[i++] = device;
3d20ed6d
LP
345 cmdline[i++] = NULL;
346
27d340c7
LP
347 pid = fork();
348 if (pid < 0) {
3d20ed6d
LP
349 log_error("fork(): %m");
350 goto finish;
351 } else if (pid == 0) {
352 /* Child */
27d340c7 353 if (progress_pipe[0] >= 0)
03e334a1 354 safe_close(progress_pipe[0]);
3d20ed6d
LP
355 execv(cmdline[0], (char**) cmdline);
356 _exit(8); /* Operational error */
357 }
358
03e334a1 359 progress_pipe[1] = safe_close(progress_pipe[1]);
27d340c7
LP
360
361 if (progress_pipe[0] >= 0) {
362 process_progress(progress_pipe[0]);
363 progress_pipe[0] = -1;
364 }
365
366 q = wait_for_terminate(pid, &status);
367 if (q < 0) {
3d20ed6d
LP
368 log_error("waitid(): %s", strerror(-q));
369 goto finish;
370 }
371
a9e1f5ec 372 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
3d20ed6d 373
a9e1f5ec
LP
374 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
375 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
376 else if (status.si_code == CLD_EXITED)
377 log_error("fsck failed with error code %i.", status.si_status);
378 else
379 log_error("fsck failed due to unknown reason.");
3d20ed6d 380
a4c24ff7 381 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
3d20ed6d 382 /* System should be rebooted. */
80cfe9e1 383 start_target(SPECIAL_REBOOT_TARGET);
a4c24ff7 384 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
3d20ed6d 385 /* Some other problem */
80cfe9e1 386 start_target(SPECIAL_EMERGENCY_TARGET);
a4c24ff7
LP
387 else {
388 r = EXIT_SUCCESS;
389 log_warning("Ignoring error.");
390 }
3d20ed6d
LP
391
392 } else
393 r = EXIT_SUCCESS;
394
a9e1f5ec 395 if (status.si_code == CLD_EXITED && (status.si_status & 1))
2b583ce6 396 touch("/run/systemd/quotacheck");
3d20ed6d
LP
397
398finish:
3d94f76c 399 safe_close_pair(progress_pipe);
27d340c7 400
3d20ed6d
LP
401 return r;
402}