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