]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udevadm-lock: switch things over to lock_generic_with_timeout()
authorLennart Poettering <lennart@poettering.net>
Thu, 2 Nov 2023 10:08:29 +0000 (11:08 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 2 Nov 2023 13:19:32 +0000 (14:19 +0100)
This replaces the local implementation of a timeout file lock with our
new generic one.

Note that a comment in the old code claimed we couldn't use alarm()-like timeouts,
but htat's not entirely true: we can if we use SIGKILL, and thus know
for sure that the process will be dead in case the timer is hit before
we actually enter the file lock syscall. But we also know it will be
delivered if we hit after.

src/udev/udevadm-lock.c

index 8993cc4571aa699204820e5f2dd115c63343fd37..bc2d5e76bdef5b6f6568df503461c48efecf4a14 100644 (file)
@@ -10,6 +10,7 @@
 #include "device-util.h"
 #include "fd-util.h"
 #include "fdset.h"
+#include "lock-util.h"
 #include "main-func.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -194,91 +195,31 @@ static int lock_device(
         if (!S_ISBLK(st.st_mode) || st.st_rdev != devno)
                 return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "Path '%s' no longer refers to specified block device %u:%u: %m", path, major(devno), minor(devno));
 
-        if (flock(fd, LOCK_EX|LOCK_NB) < 0) {
-
-                if (errno != EAGAIN)
-                        return log_error_errno(errno, "Failed to lock device '%s': %m", path);
+        r = lock_generic(fd, LOCK_BSD, LOCK_EX|LOCK_NB);
+        if (r < 0) {
+                if (r != -EAGAIN)
+                        return log_error_errno(r, "Failed to lock device '%s': %m", path);
 
                 if (deadline == 0)
                         return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Device '%s' is currently locked.", path);
 
                 if (deadline == USEC_INFINITY)  {
-
                         log_info("Device '%s' is currently locked, waiting%s", path, special_glyph(SPECIAL_GLYPH_ELLIPSIS));
 
-                        if (flock(fd, LOCK_EX) < 0)
-                                return log_error_errno(errno, "Failed to lock device '%s': %m", path);
-
+                        r = lock_generic(fd, LOCK_BSD, LOCK_EX);
                 } else {
-                        _cleanup_(sigkill_waitp) pid_t flock_pid = 0;
-
-                        /* flock() doesn't support a time-out. Let's fake one then. The traditional way to do
-                         * this is via alarm()/setitimer()/timer_create(), but that's racy, given that the
-                         * SIGALRM might already fire between the alarm() and the flock() in which case the
-                         * flock() is never cancelled and we lock up (this is a short time window, but with
-                         * short timeouts on a loaded machine we might run into it, who knows?). Let's
-                         * instead do the lock out-of-process: fork off a child that does the locking, and
-                         * that we'll wait on and kill if it takes too long. */
+                        usec_t left = usec_sub_unsigned(deadline, now(CLOCK_MONOTONIC));
 
                         log_info("Device '%s' is currently locked, waiting %s%s",
-                                 path, FORMAT_TIMESPAN(usec_sub_unsigned(deadline, now(CLOCK_MONOTONIC)), 0),
+                                 path, FORMAT_TIMESPAN(left, 0),
                                  special_glyph(SPECIAL_GLYPH_ELLIPSIS));
 
-                        BLOCK_SIGNALS(SIGCHLD);
-
-                        r = safe_fork("(timed-flock)", FORK_DEATHSIG_SIGKILL|FORK_LOG, &flock_pid);
-                        if (r < 0)
-                                return r;
-                        if (r == 0) {
-                                /* Child */
-
-                                if (flock(fd, LOCK_EX) < 0) {
-                                        log_error_errno(errno, "Failed to lock device '%s': %m", path);
-                                        _exit(EXIT_FAILURE);
-                                }
-
-                                _exit(EXIT_SUCCESS);
-                        }
-
-                        for (;;) {
-                                siginfo_t si;
-                                sigset_t ss;
-                                usec_t n;
-
-                                assert_se(sigemptyset(&ss) >= 0);
-                                assert_se(sigaddset(&ss, SIGCHLD) >= 0);
-
-                                n = now(CLOCK_MONOTONIC);
-                                if (n >= deadline)
-                                        return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT), "Timeout reached.");
-
-                                r = sigtimedwait(&ss, NULL, TIMESPEC_STORE(deadline - n));
-                                if (r < 0) {
-                                        if (errno != EAGAIN)
-                                                return log_error_errno(errno, "Failed to wait for SIGCHLD: %m");
-
-                                        return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT), "Timeout reached.");
-                                }
-
-                                assert(r == SIGCHLD);
-
-                                zero(si);
-
-                                if (waitid(P_PID, flock_pid, &si, WEXITED|WNOHANG|WNOWAIT) < 0)
-                                        return log_error_errno(errno, "Failed to wait for child: %m");
-
-                                if (si.si_pid != 0) {
-                                        assert(si.si_pid == flock_pid);
-
-                                        if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
-                                                return log_error_errno(SYNTHETIC_ERRNO(EPROTO), "Unexpected exit status of file lock child.");
-
-                                        break;
-                                }
-
-                                log_debug("Got SIGCHLD for other child, continuing.");
-                        }
+                        r = lock_generic_with_timeout(fd, LOCK_BSD, LOCK_EX, left);
+                        if (r == -ETIMEDOUT)
+                                return log_error_errno(r, "Timeout reached.");
                 }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to lock device '%s': %m", path);
         }
 
         log_debug("Successfully locked %s (%u:%u)%s", path, major(devno), minor(devno), special_glyph(SPECIAL_GLYPH_ELLIPSIS));