]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/fsck/fsck.c
tree-wide: whenever we fork off a foreign child process reset signal mask/handlers
[thirdparty/systemd.git] / src / fsck / fsck.c
index 594f21e070ee90846984e1b954bc13529e887f1a..30254b6680c22e7f27257365e3bbadafc6f24e59 100644 (file)
 
 #include <stdio.h>
 #include <stdbool.h>
-#include <string.h>
 #include <errno.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/prctl.h>
 
 #include "sd-bus.h"
-#include "libudev.h"
+#include "sd-device.h"
 
 #include "util.h"
+#include "process-util.h"
+#include "signal-util.h"
 #include "special.h"
 #include "bus-util.h"
 #include "bus-error.h"
-#include "bus-errors.h"
-#include "fileio.h"
-#include "udev-util.h"
+#include "bus-common-errors.h"
+#include "device-util.h"
 #include "path-util.h"
+#include "socket-util.h"
+
+/* exit codes as defined in fsck(8) */
+enum {
+        FSCK_SUCCESS = 0,
+        FSCK_ERROR_CORRECTED = 1,
+        FSCK_SYSTEM_SHOULD_REBOOT = 2,
+        FSCK_ERRORS_LEFT_UNCORRECTED = 4,
+        FSCK_OPERATIONAL_ERROR = 8,
+        FSCK_USAGE_OR_SYNTAX_ERROR = 16,
+        FSCK_USER_CANCELLED = 32,
+        FSCK_SHARED_LIB_ERROR = 128,
+};
 
 static bool arg_skip = false;
 static bool arg_force = false;
@@ -47,14 +62,14 @@ static const char *arg_repair = "-a";
 
 static void start_target(const char *target) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_bus_unref_ sd_bus *bus = NULL;
+        _cleanup_bus_close_unref_ sd_bus *bus = NULL;
         int r;
 
         assert(target);
 
         r = bus_open_system_systemd(&bus);
         if (r < 0) {
-                log_error("Failed to get D-Bus connection: %s", strerror(-r));
+                log_error_errno(r, "Failed to get D-Bus connection: %m");
                 return;
         }
 
@@ -72,10 +87,13 @@ static void start_target(const char *target) {
 
         /* Don't print a warning if we aren't called during startup */
         if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
-                log_error("Failed to start unit: %s", bus_error_message(&error, -r));
+                log_error("Failed to start unit: %s", bus_error_message(&error, r));
 }
 
 static int parse_proc_cmdline_item(const char *key, const char *value) {
+        int r;
+
+        assert(key);
 
         if (streq(key, "fsck.mode") && value) {
 
@@ -86,23 +104,28 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
                 else if (streq(value, "skip"))
                         arg_skip = true;
                 else
-                        log_warning("Invalid fsck.mode= parameter. Ignoring.");
+                        log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value);
+
         } else if (streq(key, "fsck.repair") && value) {
 
                 if (streq(value, "preen"))
                         arg_repair = "-a";
-                else if (streq(value, "yes"))
-                        arg_repair = "-y";
-                else if (streq(value, "no"))
-                        arg_repair = "-n";
-                else
-                        log_warning("Invalid fsck.repair= parameter. Ignoring.");
-        } else if (startswith(key, "fsck."))
-                log_warning("Invalid fsck parameter. Ignoring.");
+                else {
+                        r = parse_boolean(value);
+                        if (r > 0)
+                                arg_repair = "-y";
+                        else if (r == 0)
+                                arg_repair = "-n";
+                        else
+                                log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value);
+                }
+        }
+
 #ifdef HAVE_SYSV_COMPAT
         else if (streq(key, "fastboot") && !value) {
                 log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
                 arg_skip = true;
+
         } else if (streq(key, "forcefsck") && !value) {
                 log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
                 arg_force = true;
@@ -113,6 +136,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value) {
 }
 
 static void test_files(void) {
+
 #ifdef HAVE_SYSV_COMPAT
         if (access("/fastboot", F_OK) >= 0) {
                 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
@@ -125,8 +149,7 @@ static void test_files(void) {
         }
 #endif
 
-        if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
-                arg_show_progress = true;
+        arg_show_progress = access("/run/systemd/show-status", F_OK) >= 0;
 }
 
 static double percent(int pass, unsigned long cur, unsigned long max) {
@@ -151,9 +174,13 @@ static int process_progress(int fd) {
         _cleanup_fclose_ FILE *console = NULL, *f = NULL;
         usec_t last = 0;
         bool locked = false;
-        int clear = 0;
+        int clear = 0, r;
+
+        /* No progress pipe to process? Then we are a NOP. */
+        if (fd < 0)
+                return 0;
 
-        f = fdopen(fd, "r");
+        f = fdopen(fd, "re");
         if (!f) {
                 safe_close(fd);
                 return -errno;
@@ -163,15 +190,25 @@ static int process_progress(int fd) {
         if (!console)
                 return -ENOMEM;
 
-        while (!feof(f)) {
+        for (;;) {
                 int pass, m;
                 unsigned long cur, max;
                 _cleanup_free_ char *device = NULL;
                 double p;
                 usec_t t;
 
-                if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
+                if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4) {
+
+                        if (ferror(f))
+                                r = log_warning_errno(errno, "Failed to read from progress pipe: %m");
+                        else if (feof(f))
+                                r = 0;
+                        else {
+                                log_warning("Failed to parse progress pipe data");
+                                r = -EBADMSG;
+                        }
                         break;
+                }
 
                 /* Only show one progress counter at max */
                 if (!locked) {
@@ -206,21 +243,40 @@ static int process_progress(int fd) {
                 fflush(console);
         }
 
-        return 0;
+        return r;
+}
+
+static int fsck_progress_socket(void) {
+        static const union sockaddr_union sa = {
+                .un.sun_family = AF_UNIX,
+                .un.sun_path = "/run/systemd/fsck.progress",
+        };
+
+        int fd, r;
+
+        fd = socket(AF_UNIX, SOCK_STREAM, 0);
+        if (fd < 0)
+                return log_warning_errno(errno, "socket(): %m");
+
+        if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) {
+                r = log_full_errno(errno == ECONNREFUSED || errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
+                                   errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path);
+                safe_close(fd);
+                return r;
+        }
+
+        return fd;
 }
 
 int main(int argc, char *argv[]) {
-        const char *cmdline[9];
-        int i = 0, r = EXIT_FAILURE, q;
-        pid_t pid;
-        siginfo_t status;
-        _cleanup_udev_unref_ struct udev *udev = NULL;
-        _cleanup_udev_device_unref_ struct udev_device *udev_device = NULL;
+        _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 };
+        _cleanup_device_unref_ sd_device *dev = NULL;
         const char *device, *type;
         bool root_directory;
-        int progress_pipe[2] = { -1, -1 };
-        char dash_c[2+10+1];
+        siginfo_t status;
         struct stat st;
+        int r;
+        pid_t pid;
 
         if (argc > 2) {
                 log_error("This program expects one or no arguments.");
@@ -233,129 +289,164 @@ int main(int argc, char *argv[]) {
 
         umask(0022);
 
-        parse_proc_cmdline(parse_proc_cmdline_item);
-        test_files();
+        r = parse_proc_cmdline(parse_proc_cmdline_item);
+        if (r < 0)
+                log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
 
-        if (!arg_force && arg_skip)
-                return 0;
+        test_files();
 
-        udev = udev_new();
-        if (!udev) {
-                log_oom();
-                return EXIT_FAILURE;
+        if (!arg_force && arg_skip) {
+                r = 0;
+                goto finish;
         }
 
         if (argc > 1) {
                 device = argv[1];
-                root_directory = false;
 
                 if (stat(device, &st) < 0) {
-                        log_error("Failed to stat '%s': %m", device);
-                        return EXIT_FAILURE;
+                        r = log_error_errno(errno, "Failed to stat %s: %m", device);
+                        goto finish;
                 }
 
-                udev_device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
-                if (!udev_device) {
-                        log_error("Failed to detect device %s", device);
-                        return EXIT_FAILURE;
+                if (!S_ISBLK(st.st_mode)) {
+                        log_error("%s is not a block device.", device);
+                        r = -EINVAL;
+                        goto finish;
                 }
+
+                r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to detect device %s: %m", device);
+                        goto finish;
+                }
+
+                root_directory = false;
         } else {
                 struct timespec times[2];
 
                 /* Find root device */
 
                 if (stat("/", &st) < 0) {
-                        log_error("Failed to stat() the root directory: %m");
-                        return EXIT_FAILURE;
+                        r = log_error_errno(errno, "Failed to stat() the root directory: %m");
+                        goto finish;
                 }
 
                 /* Virtual root devices don't need an fsck */
-                if (major(st.st_dev) == 0)
-                        return EXIT_SUCCESS;
+                if (major(st.st_dev) == 0) {
+                        log_debug("Root directory is virtual or btrfs, skipping check.");
+                        r = 0;
+                        goto finish;
+                }
 
                 /* check if we are already writable */
                 times[0] = st.st_atim;
                 times[1] = st.st_mtim;
+
                 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
                         log_info("Root directory is writable, skipping check.");
-                        return EXIT_SUCCESS;
+                        r = 0;
+                        goto finish;
                 }
 
-                udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
-                if (!udev_device) {
-                        log_error("Failed to detect root device.");
-                        return EXIT_FAILURE;
+                r = sd_device_new_from_devnum(&dev, 'b', st.st_dev);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to detect root device: %m");
+                        goto finish;
                 }
 
-                device = udev_device_get_devnode(udev_device);
-                if (!device) {
-                        log_error("Failed to detect device node of root directory.");
-                        return EXIT_FAILURE;
+                r = sd_device_get_devname(dev, &device);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to detect device node of root directory: %m");
+                        goto finish;
                 }
 
                 root_directory = true;
         }
 
-        type = udev_device_get_property_value(udev_device, "ID_FS_TYPE");
-        if (type) {
+        r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type);
+        if (r >= 0) {
                 r = fsck_exists(type);
-                if (r < 0) {
-                        if (r == -ENOENT) {
-                                log_info("fsck.%s doesn't exist, not checking file system.", type);
-                                return EXIT_SUCCESS;
-                        } else
-                                log_warning("fsck.%s cannot be used: %s", type, strerror(-r));
-                }
+                if (r == -ENOENT) {
+                        log_info("fsck.%s doesn't exist, not checking file system on %s", type, device);
+                        r = 0;
+                        goto finish;
+                } else if (r < 0)
+                        log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s: %m", type, device);
         }
 
-        if (arg_show_progress)
+        if (arg_show_progress) {
                 if (pipe(progress_pipe) < 0) {
-                        log_error("pipe(): %m");
-                        return EXIT_FAILURE;
+                        r = log_error_errno(errno, "pipe(): %m");
+                        goto finish;
                 }
-
-        cmdline[i++] = "/sbin/fsck";
-        cmdline[i++] =  arg_repair;
-        cmdline[i++] = "-T";
-        cmdline[i++] = "-l";
-
-        if (!root_directory)
-                cmdline[i++] = "-M";
-
-        if (arg_force)
-                cmdline[i++] = "-f";
-
-        if (progress_pipe[1] >= 0) {
-                snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
-                char_array_0(dash_c);
-                cmdline[i++] = dash_c;
         }
 
-        cmdline[i++] = device;
-        cmdline[i++] = NULL;
-
         pid = fork();
         if (pid < 0) {
-                log_error("fork(): %m");
+                r = log_error_errno(errno, "fork(): %m");
                 goto finish;
-        } else if (pid == 0) {
+        }
+        if (pid == 0) {
+                char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1];
+                int progress_socket = -1;
+                const char *cmdline[9];
+                int i = 0;
+
                 /* Child */
-                if (progress_pipe[0] >= 0)
-                        safe_close(progress_pipe[0]);
+
+                (void) reset_all_signal_handlers();
+                (void) reset_signal_mask();
+                assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+                /* Close the reading side of the progress pipe */
+                progress_pipe[0] = safe_close(progress_pipe[0]);
+
+                /* Try to connect to a progress management daemon, if there is one */
+                progress_socket = fsck_progress_socket();
+                if (progress_socket >= 0) {
+                        /* If this worked we close the progress pipe early, and just use the socket */
+                        progress_pipe[1] = safe_close(progress_pipe[1]);
+                        xsprintf(dash_c, "-C%i", progress_socket);
+                } else if (progress_pipe[1] >= 0) {
+                        /* Otherwise if we have the progress pipe to our own local handle, we use it */
+                        xsprintf(dash_c, "-C%i", progress_pipe[1]);
+                } else
+                        dash_c[0] = 0;
+
+                cmdline[i++] = "/sbin/fsck";
+                cmdline[i++] =  arg_repair;
+                cmdline[i++] = "-T";
+
+                /*
+                 * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files.
+                 * The previous versions use flock for the device and conflict with
+                 * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5
+                 */
+                cmdline[i++] = "-l";
+
+                if (!root_directory)
+                        cmdline[i++] = "-M";
+
+                if (arg_force)
+                        cmdline[i++] = "-f";
+
+                if (!isempty(dash_c))
+                        cmdline[i++] = dash_c;
+
+                cmdline[i++] = device;
+                cmdline[i++] = NULL;
+
                 execv(cmdline[0], (char**) cmdline);
-                _exit(8); /* Operational error */
+                _exit(FSCK_OPERATIONAL_ERROR);
         }
 
         progress_pipe[1] = safe_close(progress_pipe[1]);
+        (void) process_progress(progress_pipe[0]);
+        progress_pipe[0] = -1;
 
-        if (progress_pipe[0] >= 0) {
-                process_progress(progress_pipe[0]);
-                progress_pipe[0] = -1;
-        }
-
-        q = wait_for_terminate(pid, &status);
-        if (q < 0) {
-                log_error("waitid(): %s", strerror(-q));
+        r = wait_for_terminate(pid, &status);
+        if (r < 0) {
+                log_error_errno(r, "waitid(): %m");
                 goto finish;
         }
 
@@ -368,25 +459,25 @@ int main(int argc, char *argv[]) {
                 else
                         log_error("fsck failed due to unknown reason.");
 
-                if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
+                r = -EINVAL;
+
+                if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory)
                         /* System should be rebooted. */
                         start_target(SPECIAL_REBOOT_TARGET);
-                else if (status.si_code == CLD_EXITED && (status.si_status & 6))
+                else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)))
                         /* Some other problem */
                         start_target(SPECIAL_EMERGENCY_TARGET);
                 else {
-                        r = EXIT_SUCCESS;
                         log_warning("Ignoring error.");
+                        r = 0;
                 }
 
         } else
-                r = EXIT_SUCCESS;
+                r = 0;
 
-        if (status.si_code == CLD_EXITED && (status.si_status & 1))
-                touch("/run/systemd/quotacheck");
+        if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED))
+                (void) touch("/run/systemd/quotacheck");
 
 finish:
-        safe_close_pair(progress_pipe);
-
-        return r;
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }