chiark / gitweb /
use "Out of memory." consistantly (or with "\n")
[elogind.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 }