]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fsck/fsck.c
fsck: remove distro specific hacks from fsck/quotacheck
[thirdparty/systemd.git] / src / fsck / fsck.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
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>
27 #include <fcntl.h>
28 #include <sys/file.h>
29
30 #include <libudev.h>
31 #include <dbus/dbus.h>
32
33 #include "util.h"
34 #include "dbus-common.h"
35 #include "special.h"
36 #include "bus-errors.h"
37 #include "virt.h"
38
39 static bool arg_skip = false;
40 static bool arg_force = false;
41 static bool arg_show_progress = false;
42
43 static void start_target(const char *target, bool isolate) {
44 DBusMessage *m = NULL, *reply = NULL;
45 DBusError error;
46 const char *mode, *basic_target = "basic.target";
47 DBusConnection *bus = NULL;
48
49 assert(target);
50
51 dbus_error_init(&error);
52
53 if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) {
54 log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
55 goto finish;
56 }
57
58 if (isolate)
59 mode = "isolate";
60 else
61 mode = "replace";
62
63 log_info("Running request %s/start/%s", target, mode);
64
65 if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnitReplace"))) {
66 log_error("Could not allocate message.");
67 goto finish;
68 }
69
70 /* Start these units only if we can replace base.target with it */
71
72 if (!dbus_message_append_args(m,
73 DBUS_TYPE_STRING, &basic_target,
74 DBUS_TYPE_STRING, &target,
75 DBUS_TYPE_STRING, &mode,
76 DBUS_TYPE_INVALID)) {
77 log_error("Could not attach target and flag information to message.");
78 goto finish;
79 }
80
81 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
82
83 /* Don't print a warning if we aren't called during
84 * startup */
85 if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
86 log_error("Failed to start unit: %s", bus_error_message(&error));
87
88 goto finish;
89 }
90
91 finish:
92 if (m)
93 dbus_message_unref(m);
94
95 if (reply)
96 dbus_message_unref(reply);
97
98 if (bus) {
99 dbus_connection_flush(bus);
100 dbus_connection_close(bus);
101 dbus_connection_unref(bus);
102 }
103
104 dbus_error_free(&error);
105 }
106
107 static int parse_proc_cmdline(void) {
108 char *line, *w, *state;
109 int r;
110 size_t l;
111
112 if (detect_container(NULL) > 0)
113 return 0;
114
115 r = read_one_line_file("/proc/cmdline", &line);
116 if (r < 0) {
117 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
118 return 0;
119 }
120
121 FOREACH_WORD_QUOTED(w, l, line, state) {
122
123 if (strneq(w, "fsck.mode=auto", l))
124 arg_force = arg_skip = false;
125 else if (strneq(w, "fsck.mode=force", l))
126 arg_force = true;
127 else if (strneq(w, "fsck.mode=skip", l))
128 arg_skip = true;
129 else if (startswith(w, "fsck"))
130 log_warning("Invalid fsck parameter. Ignoring.");
131 #ifdef HAVE_SYSV_COMPAT
132 else if (strneq(w, "fastboot", l)) {
133 log_error("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
134 arg_skip = true;
135 } else if (strneq(w, "forcefsck", l)) {
136 log_error("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
137 arg_force = true;
138 }
139 #endif
140 }
141
142 free(line);
143 return 0;
144 }
145
146 static void test_files(void) {
147 #ifdef HAVE_SYSV_COMPAT
148 if (access("/fastboot", F_OK) >= 0) {
149 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
150 arg_skip = true;
151 }
152
153 if (access("/forcefsck", F_OK) >= 0) {
154 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
155 arg_force = true;
156 }
157 #endif
158
159 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
160 arg_show_progress = true;
161 }
162
163 static double percent(int pass, unsigned long cur, unsigned long max) {
164 /* Values stolen from e2fsck */
165
166 static const int pass_table[] = {
167 0, 70, 90, 92, 95, 100
168 };
169
170 if (pass <= 0)
171 return 0.0;
172
173 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
174 return 100.0;
175
176 return (double) pass_table[pass-1] +
177 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
178 (double) cur / (double) max;
179 }
180
181 static int process_progress(int fd) {
182 FILE *f, *console;
183 usec_t last = 0;
184 bool locked = false;
185 int clear = 0;
186
187 f = fdopen(fd, "r");
188 if (!f) {
189 close_nointr_nofail(fd);
190 return -errno;
191 }
192
193 console = fopen("/dev/console", "w");
194 if (!console) {
195 fclose(f);
196 return -ENOMEM;
197 }
198
199 while (!feof(f)) {
200 int pass, m;
201 unsigned long cur, max;
202 char *device;
203 double p;
204 usec_t t;
205
206 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
207 break;
208
209 /* Only show one progress counter at max */
210 if (!locked) {
211 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
212 free(device);
213 continue;
214 }
215
216 locked = true;
217 }
218
219 /* Only update once every 50ms */
220 t = now(CLOCK_MONOTONIC);
221 if (last + 50 * USEC_PER_MSEC > t) {
222 free(device);
223 continue;
224 }
225
226 last = t;
227
228 p = percent(pass, cur, max);
229 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
230 fflush(console);
231
232 free(device);
233
234 if (m > clear)
235 clear = m;
236 }
237
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 fclose(f);
249 fclose(console);
250 return 0;
251 }
252
253 int main(int argc, char *argv[]) {
254 const char *cmdline[9];
255 int i = 0, r = EXIT_FAILURE, q;
256 pid_t pid;
257 siginfo_t status;
258 struct udev *udev = NULL;
259 struct udev_device *udev_device = NULL;
260 const char *device;
261 bool root_directory;
262 int progress_pipe[2] = { -1, -1 };
263 char dash_c[2+10+1];
264
265 if (argc > 2) {
266 log_error("This program expects one or no arguments.");
267 return EXIT_FAILURE;
268 }
269
270 log_set_target(LOG_TARGET_AUTO);
271 log_parse_environment();
272 log_open();
273
274 umask(0022);
275
276 parse_proc_cmdline();
277 test_files();
278
279 if (!arg_force && arg_skip)
280 return 0;
281
282 if (argc > 1) {
283 device = argv[1];
284 root_directory = false;
285 } else {
286 struct stat st;
287 struct timespec times[2];
288
289 /* Find root device */
290
291 if (stat("/", &st) < 0) {
292 log_error("Failed to stat() the root directory: %m");
293 goto finish;
294 }
295
296 /* Virtual root devices don't need an fsck */
297 if (major(st.st_dev) == 0)
298 return 0;
299
300 /* check if we are already writable */
301 times[0] = st.st_atim;
302 times[1] = st.st_mtim;
303 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
304 log_info("Root directory is writable, skipping check.");
305 return 0;
306 }
307
308 if (!(udev = udev_new())) {
309 log_oom();
310 goto finish;
311 }
312
313 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
314 log_error("Failed to detect root device.");
315 goto finish;
316 }
317
318 if (!(device = udev_device_get_devnode(udev_device))) {
319 log_error("Failed to detect device node of root directory.");
320 goto finish;
321 }
322
323 root_directory = true;
324 }
325
326 if (arg_show_progress)
327 if (pipe(progress_pipe) < 0) {
328 log_error("pipe(): %m");
329 goto finish;
330 }
331
332 cmdline[i++] = "/sbin/fsck";
333 cmdline[i++] = "-a";
334 cmdline[i++] = "-T";
335 cmdline[i++] = "-l";
336
337 if (!root_directory)
338 cmdline[i++] = "-M";
339
340 if (arg_force)
341 cmdline[i++] = "-f";
342
343 if (progress_pipe[1] >= 0) {
344 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
345 char_array_0(dash_c);
346 cmdline[i++] = dash_c;
347 }
348
349 cmdline[i++] = device;
350 cmdline[i++] = NULL;
351
352 pid = fork();
353 if (pid < 0) {
354 log_error("fork(): %m");
355 goto finish;
356 } else if (pid == 0) {
357 /* Child */
358 if (progress_pipe[0] >= 0)
359 close_nointr_nofail(progress_pipe[0]);
360 execv(cmdline[0], (char**) cmdline);
361 _exit(8); /* Operational error */
362 }
363
364 if (progress_pipe[1] >= 0) {
365 close_nointr_nofail(progress_pipe[1]);
366 progress_pipe[1] = -1;
367 }
368
369 if (progress_pipe[0] >= 0) {
370 process_progress(progress_pipe[0]);
371 progress_pipe[0] = -1;
372 }
373
374 q = wait_for_terminate(pid, &status);
375 if (q < 0) {
376 log_error("waitid(): %s", strerror(-q));
377 goto finish;
378 }
379
380 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
381
382 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
383 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
384 else if (status.si_code == CLD_EXITED)
385 log_error("fsck failed with error code %i.", status.si_status);
386 else
387 log_error("fsck failed due to unknown reason.");
388
389 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
390 /* System should be rebooted. */
391 start_target(SPECIAL_REBOOT_TARGET, false);
392 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
393 /* Some other problem */
394 start_target(SPECIAL_EMERGENCY_TARGET, true);
395 else {
396 r = EXIT_SUCCESS;
397 log_warning("Ignoring error.");
398 }
399
400 } else
401 r = EXIT_SUCCESS;
402
403 if (status.si_code == CLD_EXITED && (status.si_status & 1))
404 touch("/run/systemd/quotacheck");
405
406 finish:
407 if (udev_device)
408 udev_device_unref(udev_device);
409
410 if (udev)
411 udev_unref(udev);
412
413 close_pipe(progress_pipe);
414
415 return r;
416 }