]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fsck/fsck.c
use "Out of memory." consistantly (or with "\n")
[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 #if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
132 else if (strneq(w, "fastboot", l))
133 arg_skip = true;
134 else if (strneq(w, "forcefsck", l))
135 arg_force = true;
136 #endif
137 }
138
139 free(line);
140 return 0;
141 }
142
143 static void test_files(void) {
144 if (access("/fastboot", F_OK) >= 0)
145 arg_skip = true;
146
147 if (access("/forcefsck", F_OK) >= 0)
148 arg_force = true;
149
150 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
151 arg_show_progress = true;
152 }
153
154 static double percent(int pass, unsigned long cur, unsigned long max) {
155 /* Values stolen from e2fsck */
156
157 static const int pass_table[] = {
158 0, 70, 90, 92, 95, 100
159 };
160
161 if (pass <= 0)
162 return 0.0;
163
164 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
165 return 100.0;
166
167 return (double) pass_table[pass-1] +
168 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
169 (double) cur / (double) max;
170 }
171
172 static int process_progress(int fd) {
173 FILE *f, *console;
174 usec_t last = 0;
175 bool locked = false;
176 int clear = 0;
177
178 f = fdopen(fd, "r");
179 if (!f) {
180 close_nointr_nofail(fd);
181 return -errno;
182 }
183
184 console = fopen("/dev/console", "w");
185 if (!console) {
186 fclose(f);
187 return -ENOMEM;
188 }
189
190 while (!feof(f)) {
191 int pass, m;
192 unsigned long cur, max;
193 char *device;
194 double p;
195 usec_t t;
196
197 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
198 break;
199
200 /* Only show one progress counter at max */
201 if (!locked) {
202 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
203 free(device);
204 continue;
205 }
206
207 locked = true;
208 }
209
210 /* Only update once every 50ms */
211 t = now(CLOCK_MONOTONIC);
212 if (last + 50 * USEC_PER_MSEC > t) {
213 free(device);
214 continue;
215 }
216
217 last = t;
218
219 p = percent(pass, cur, max);
220 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
221 fflush(console);
222
223 free(device);
224
225 if (m > clear)
226 clear = m;
227 }
228
229 if (clear > 0) {
230 unsigned j;
231
232 fputc('\r', console);
233 for (j = 0; j < (unsigned) clear; j++)
234 fputc(' ', console);
235 fputc('\r', console);
236 fflush(console);
237 }
238
239 fclose(f);
240 fclose(console);
241 return 0;
242 }
243
244 int main(int argc, char *argv[]) {
245 const char *cmdline[9];
246 int i = 0, r = EXIT_FAILURE, q;
247 pid_t pid;
248 siginfo_t status;
249 struct udev *udev = NULL;
250 struct udev_device *udev_device = NULL;
251 const char *device;
252 bool root_directory;
253 int progress_pipe[2] = { -1, -1 };
254 char dash_c[2+10+1];
255
256 if (argc > 2) {
257 log_error("This program expects one or no arguments.");
258 return EXIT_FAILURE;
259 }
260
261 log_set_target(LOG_TARGET_AUTO);
262 log_parse_environment();
263 log_open();
264
265 umask(0022);
266
267 parse_proc_cmdline();
268 test_files();
269
270 if (!arg_force && arg_skip)
271 return 0;
272
273 if (argc > 1) {
274 device = argv[1];
275 root_directory = false;
276 } else {
277 struct stat st;
278 struct timespec times[2];
279
280 /* Find root device */
281
282 if (stat("/", &st) < 0) {
283 log_error("Failed to stat() the root directory: %m");
284 goto finish;
285 }
286
287 /* Virtual root devices don't need an fsck */
288 if (major(st.st_dev) == 0)
289 return 0;
290
291 /* check if we are already writable */
292 times[0] = st.st_atim;
293 times[1] = st.st_mtim;
294 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
295 log_info("Root directory is writable, skipping check.");
296 return 0;
297 }
298
299 if (!(udev = udev_new())) {
300 log_error("Out of memory.");
301 goto finish;
302 }
303
304 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
305 log_error("Failed to detect root device.");
306 goto finish;
307 }
308
309 if (!(device = udev_device_get_devnode(udev_device))) {
310 log_error("Failed to detect device node of root directory.");
311 goto finish;
312 }
313
314 root_directory = true;
315 }
316
317 if (arg_show_progress)
318 if (pipe(progress_pipe) < 0) {
319 log_error("pipe(): %m");
320 goto finish;
321 }
322
323 cmdline[i++] = "/sbin/fsck";
324 cmdline[i++] = "-a";
325 cmdline[i++] = "-T";
326 cmdline[i++] = "-l";
327
328 if (!root_directory)
329 cmdline[i++] = "-M";
330
331 if (arg_force)
332 cmdline[i++] = "-f";
333
334 if (progress_pipe[1] >= 0) {
335 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
336 char_array_0(dash_c);
337 cmdline[i++] = dash_c;
338 }
339
340 cmdline[i++] = device;
341 cmdline[i++] = NULL;
342
343 pid = fork();
344 if (pid < 0) {
345 log_error("fork(): %m");
346 goto finish;
347 } else if (pid == 0) {
348 /* Child */
349 if (progress_pipe[0] >= 0)
350 close_nointr_nofail(progress_pipe[0]);
351 execv(cmdline[0], (char**) cmdline);
352 _exit(8); /* Operational error */
353 }
354
355 if (progress_pipe[1] >= 0) {
356 close_nointr_nofail(progress_pipe[1]);
357 progress_pipe[1] = -1;
358 }
359
360 if (progress_pipe[0] >= 0) {
361 process_progress(progress_pipe[0]);
362 progress_pipe[0] = -1;
363 }
364
365 q = wait_for_terminate(pid, &status);
366 if (q < 0) {
367 log_error("waitid(): %s", strerror(-q));
368 goto finish;
369 }
370
371 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
372
373 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
374 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
375 else if (status.si_code == CLD_EXITED)
376 log_error("fsck failed with error code %i.", status.si_status);
377 else
378 log_error("fsck failed due to unknown reason.");
379
380 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
381 /* System should be rebooted. */
382 start_target(SPECIAL_REBOOT_TARGET, false);
383 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
384 /* Some other problem */
385 start_target(SPECIAL_EMERGENCY_TARGET, true);
386 else {
387 r = EXIT_SUCCESS;
388 log_warning("Ignoring error.");
389 }
390
391 } else
392 r = EXIT_SUCCESS;
393
394 if (status.si_code == CLD_EXITED && (status.si_status & 1))
395 touch("/run/systemd/quotacheck");
396
397 finish:
398 if (udev_device)
399 udev_device_unref(udev_device);
400
401 if (udev)
402 udev_unref(udev);
403
404 close_pipe(progress_pipe);
405
406 return r;
407 }