chiark / gitweb /
Remove SysV compat
[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   Copyright 2014 Holger Hans Peter Freyther
8
9   systemd is free software; you can redistribute it and/or modify it
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
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
17   Lesser General Public License for more details.
18
19   You should have received a copy of the GNU Lesser General Public License
20   along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <stdio.h>
24 #include <stdbool.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/file.h>
29 #include <sys/stat.h>
30
31 #include "sd-bus.h"
32 #include "sd-device.h"
33
34 #include "util.h"
35 #include "special.h"
36 #include "bus-util.h"
37 #include "bus-error.h"
38 #include "bus-common-errors.h"
39 #include "device-util.h"
40 #include "path-util.h"
41 #include "socket-util.h"
42 #include "fsckd/fsckd.h"
43
44 static bool arg_skip = false;
45 static bool arg_force = false;
46 static const char *arg_repair = "-a";
47
48 static void start_target(const char *target) {
49         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
50         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
51         int r;
52
53         assert(target);
54
55         r = bus_open_system_systemd(&bus);
56         if (r < 0) {
57                 log_error_errno(r, "Failed to get D-Bus connection: %m");
58                 return;
59         }
60
61         log_info("Running request %s/start/replace", target);
62
63         /* Start these units only if we can replace base.target with it */
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");
72
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));
76 }
77
78 static 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
89                         log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value);
90
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
100                         log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value);
101         }
102
103         return 0;
104 }
105
106 static void test_files(void) {
107
108 }
109
110 static int process_progress(int fd, pid_t fsck_pid, dev_t device_num) {
111         _cleanup_fclose_ FILE *f = NULL;
112         usec_t last = 0;
113         _cleanup_close_ int fsckd_fd = -1;
114         static const union sockaddr_union sa = {
115                 .un.sun_family = AF_UNIX,
116                 .un.sun_path = FSCKD_SOCKET_PATH,
117         };
118
119         fsckd_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
120         if (fsckd_fd < 0)
121                 return log_warning_errno(errno, "Cannot open fsckd socket, we won't report fsck progress: %m");
122         if (connect(fsckd_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
123                 return log_warning_errno(errno, "Cannot connect to fsckd socket, we won't report fsck progress: %m");
124
125         f = fdopen(fd, "r");
126         if (!f)
127                 return log_warning_errno(errno, "Cannot connect to fsck, we won't report fsck progress: %m");
128
129         while (!feof(f)) {
130                 int pass;
131                 size_t buflen;
132                 size_t cur, max;
133                 ssize_t r;
134                 usec_t t;
135                 _cleanup_free_ char *device = NULL;
136                 FsckProgress progress;
137                 FsckdMessage fsckd_message;
138
139                 if (fscanf(f, "%i %zu %zu %ms", &pass, &cur, &max, &device) != 4)
140                         break;
141
142                 /* Only update once every 50ms */
143                 t = now(CLOCK_MONOTONIC);
144                 if (last + 50 * USEC_PER_MSEC > t)
145                         continue;
146
147                 last = t;
148
149                 /* send progress to fsckd */
150                 progress.devnum = device_num;
151                 progress.cur = cur;
152                 progress.max = max;
153                 progress.pass = pass;
154
155                 r = send(fsckd_fd, &progress, sizeof(FsckProgress), 0);
156                 if (r < 0 || (size_t) r < sizeof(FsckProgress))
157                         log_warning_errno(errno, "Cannot communicate fsck progress to fsckd: %m");
158
159                 /* get fsckd requests, only read when we have coherent size data */
160                 r = ioctl(fsckd_fd, FIONREAD, &buflen);
161                 if (r == 0 && (size_t) buflen >= sizeof(FsckdMessage)) {
162                         r = recv(fsckd_fd, &fsckd_message, sizeof(FsckdMessage), 0);
163                         if (r > 0 && fsckd_message.cancel == 1) {
164                                 log_info("Request to cancel fsck from fsckd");
165                                 kill(fsck_pid, SIGTERM);
166                         }
167                 }
168         }
169
170         return 0;
171 }
172
173 int main(int argc, char *argv[]) {
174         const char *cmdline[9];
175         int i = 0, r = EXIT_FAILURE, q;
176         pid_t pid;
177         int progress_rc;
178         siginfo_t status;
179         _cleanup_device_unref_ sd_device *dev = NULL;
180         const char *device, *type;
181         bool root_directory;
182         _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 };
183         char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1];
184         struct stat st;
185
186         if (argc > 2) {
187                 log_error("This program expects one or no arguments.");
188                 return EXIT_FAILURE;
189         }
190
191         log_set_target(LOG_TARGET_AUTO);
192         log_parse_environment();
193         log_open();
194
195         umask(0022);
196
197         q = parse_proc_cmdline(parse_proc_cmdline_item);
198         if (q < 0)
199                 log_warning_errno(q, "Failed to parse kernel command line, ignoring: %m");
200
201         test_files();
202
203         if (!arg_force && arg_skip) {
204                 r = 0;
205                 goto finish;
206         }
207
208         if (argc > 1) {
209                 device = argv[1];
210                 root_directory = false;
211
212                 if (stat(device, &st) < 0) {
213                         r = log_error_errno(errno, "Failed to stat '%s': %m", device);
214                         goto finish;
215                 }
216
217                 r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev);
218                 if (r < 0) {
219                         log_error_errno(r, "Failed to detect device %s: %m", device);
220                         goto finish;
221                 }
222         } else {
223                 struct timespec times[2];
224
225                 /* Find root device */
226
227                 if (stat("/", &st) < 0) {
228                         r = log_error_errno(errno, "Failed to stat() the root directory: %m");
229                         goto finish;
230                 }
231
232                 /* Virtual root devices don't need an fsck */
233                 if (major(st.st_dev) == 0) {
234                         log_debug("Root directory is virtual, skipping check.");
235                         r = 0;
236                         goto finish;
237                 }
238
239                 /* check if we are already writable */
240                 times[0] = st.st_atim;
241                 times[1] = st.st_mtim;
242                 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
243                         log_info("Root directory is writable, skipping check.");
244                         r = 0;
245                         goto finish;
246                 }
247
248                 r = sd_device_new_from_devnum(&dev, 'b', st.st_dev);
249                 if (r < 0) {
250                         log_error_errno(r, "Failed to detect root device: %m");
251                         goto finish;
252                 }
253
254                 r = sd_device_get_devname(dev, &device);
255                 if (r < 0) {
256                         log_error_errno(r, "Failed to detect device node of root directory: %m");
257                         r = -ENXIO;
258                         goto finish;
259                 }
260
261                 root_directory = true;
262         }
263
264         r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type);
265         if (r >= 0) {
266                 r = fsck_exists(type);
267                 if (r == -ENOENT) {
268                         log_info("fsck.%s doesn't exist, not checking file system on %s", type, device);
269                         r = 0;
270                         goto finish;
271                 } else if (r < 0)
272                         log_warning_errno(r, "fsck.%s cannot be used for %s: %m", type, device);
273         }
274
275         if (pipe(progress_pipe) < 0) {
276                 r = log_error_errno(errno, "pipe(): %m");
277                 goto finish;
278         }
279
280         cmdline[i++] = "/sbin/fsck";
281         cmdline[i++] =  arg_repair;
282         cmdline[i++] = "-T";
283
284         /*
285          * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files.
286          * The previous versions use flock for the device and conflict with
287          * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5
288          */
289         cmdline[i++] = "-l";
290
291         if (!root_directory)
292                 cmdline[i++] = "-M";
293
294         if (arg_force)
295                 cmdline[i++] = "-f";
296
297         xsprintf(dash_c, "-C%i", progress_pipe[1]);
298         cmdline[i++] = dash_c;
299
300         cmdline[i++] = device;
301         cmdline[i++] = NULL;
302
303         pid = fork();
304         if (pid < 0) {
305                 r = log_error_errno(errno, "fork(): %m");
306                 goto finish;
307         } else if (pid == 0) {
308                 /* Child */
309                 progress_pipe[0] = safe_close(progress_pipe[0]);
310                 execv(cmdline[0], (char**) cmdline);
311                 _exit(8); /* Operational error */
312         }
313
314         progress_pipe[1] = safe_close(progress_pipe[1]);
315
316         progress_rc = process_progress(progress_pipe[0], pid, st.st_rdev);
317         progress_pipe[0] = -1;
318
319         r = wait_for_terminate(pid, &status);
320         if (r < 0) {
321                 log_error_errno(r, "waitid(): %m");
322                 goto finish;
323         }
324
325         if (status.si_code != CLD_EXITED || (status.si_status & ~1) || progress_rc != 0) {
326
327                 /* cancel will kill fsck (but process_progress returns 0) */
328                 if ((progress_rc != 0 && status.si_code == CLD_KILLED) || status.si_code == CLD_DUMPED)
329                         log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
330                 else if (status.si_code == CLD_EXITED)
331                         log_error("fsck failed with error code %i.", status.si_status);
332                 else if (progress_rc != 0)
333                         log_error("fsck failed due to unknown reason.");
334
335                 r = -EINVAL;
336
337                 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
338                         /* System should be rebooted. */
339                         start_target(SPECIAL_REBOOT_TARGET);
340                 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
341                         /* Some other problem */
342                         start_target(SPECIAL_EMERGENCY_TARGET);
343                 else {
344                         r = 0;
345                         if (progress_rc != 0)
346                                 log_warning("Ignoring error.");
347                 }
348
349         } else
350                 r = 0;
351
352         if (status.si_code == CLD_EXITED && (status.si_status & 1))
353                 touch("/run/systemd/quotacheck");
354
355 finish:
356         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
357 }