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