From de7e033286520caa1824b376b1afa143b3d71aa1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 2 Nov 2023 11:08:29 +0100 Subject: [PATCH] udevadm-lock: switch things over to lock_generic_with_timeout() 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 | 85 +++++++---------------------------------- 1 file changed, 13 insertions(+), 72 deletions(-) diff --git a/src/udev/udevadm-lock.c b/src/udev/udevadm-lock.c index 8993cc4571a..bc2d5e76bde 100644 --- a/src/udev/udevadm-lock.c +++ b/src/udev/udevadm-lock.c @@ -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)); -- 2.47.3