diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c index df9fefc8d00..9de25ae6780 100644 --- a/src/udev/udevadm-settle.c +++ b/src/udev/udevadm-settle.c @@ -4,32 +4,25 @@ * Copyright © 2009 Scott James Remnant */ -#include #include -#include -#include -#include -#include #include #include "sd-bus.h" +#include "sd-event.h" #include "sd-login.h" #include "sd-messages.h" #include "bus-util.h" -#include "fd-util.h" -#include "io-util.h" -#include "string-util.h" +#include "path-util.h" #include "strv.h" #include "time-util.h" #include "udev-ctrl.h" #include "udev-util.h" #include "udevadm.h" #include "unit-def.h" -#include "util.h" #include "virt.h" -static usec_t arg_timeout = 120 * USEC_PER_SEC; +static usec_t arg_timeout_usec = 120 * USEC_PER_SEC; static const char *arg_exists = NULL; static int help(void) { @@ -61,11 +54,14 @@ static int parse_argv(int argc, char *argv[]) { while ((c = getopt_long(argc, argv, "t:E:Vhs:e:q", options, NULL)) >= 0) { switch (c) { case 't': - r = parse_sec(optarg, &arg_timeout); + r = parse_sec(optarg, &arg_timeout_usec); if (r < 0) return log_error_errno(r, "Failed to parse timeout value '%s': %m", optarg); break; case 'E': + if (!path_is_valid(optarg)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid path: %s", optarg); + arg_exists = optarg; break; case 'V': @@ -158,9 +154,36 @@ static int emit_deprecation_warning(void) { return 0; } +static bool check(void) { + int r; + + if (arg_exists) { + if (access(arg_exists, F_OK) >= 0) + return true; + + if (errno != ENOENT) + log_warning_errno(errno, "Failed to check the existence of \"%s\", ignoring: %m", arg_exists); + } + + /* exit if queue is empty */ + r = udev_queue_is_empty(); + if (r < 0) + log_warning_errno(r, "Failed to check if udev queue is empty, ignoring: %m"); + + return r > 0; +} + +static int on_inotify(sd_event_source *s, const struct inotify_event *event, void *userdata) { + assert(s); + + if (check()) + return sd_event_exit(sd_event_source_get_event(s), 0); + + return 0; +} + int settle_main(int argc, char *argv[], void *userdata) { - _cleanup_close_ int fd = -1; - usec_t deadline; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; int r; r = parse_argv(argc, argv); @@ -172,55 +195,56 @@ int settle_main(int argc, char *argv[], void *userdata) { return 0; } - deadline = now(CLOCK_MONOTONIC) + arg_timeout; + (void) emit_deprecation_warning(); - /* guarantee that the udev daemon isn't pre-processing */ if (getuid() == 0) { _cleanup_(udev_ctrl_unrefp) UdevCtrl *uctrl = NULL; - if (udev_ctrl_new(&uctrl) >= 0) { - r = udev_ctrl_send_ping(uctrl); - if (r < 0) { - log_debug_errno(r, "Failed to connect to udev daemon: %m"); - return 0; - } + /* guarantee that the udev daemon isn't pre-processing */ - r = udev_ctrl_wait(uctrl, MAX(5 * USEC_PER_SEC, arg_timeout)); - if (r < 0) - return log_error_errno(r, "Failed to wait for daemon to reply: %m"); + r = udev_ctrl_new(&uctrl); + if (r < 0) + return log_error_errno(r, "Failed to create control socket for udev daemon: %m"); + + r = udev_ctrl_send_ping(uctrl); + if (r < 0) { + log_debug_errno(r, "Failed to connect to udev daemon, ignoring: %m"); + return 0; } + + r = udev_ctrl_wait(uctrl, MAX(5 * USEC_PER_SEC, arg_timeout_usec)); + if (r < 0) + return log_error_errno(r, "Failed to wait for daemon to reply: %m"); + } else { + /* For non-privileged users, at least check if udevd is running. */ + if (access("/run/udev/control", F_OK) < 0) + return log_error_errno(errno, "udevd is not running."); } - fd = udev_queue_init(); - if (fd < 0) { - log_debug_errno(fd, "Queue is empty, nothing to watch: %m"); + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to get default sd-event object: %m"); + + r = sd_event_add_inotify(event, NULL, "/run/udev" , IN_DELETE, on_inotify, NULL); + if (r < 0) + return log_error_errno(r, "Failed to add inotify watch for /run/udev: %m"); + + if (arg_timeout_usec != USEC_INFINITY) { + r = sd_event_add_time_relative(event, NULL, CLOCK_BOOTTIME, arg_timeout_usec, 0, + NULL, INT_TO_PTR(-ETIMEDOUT)); + if (r < 0) + return log_error_errno(r, "Failed to add timer event source: %m"); + } + + /* Check before entering the event loop, as the udev queue may be already empty. */ + if (check()) return 0; - } - (void) emit_deprecation_warning(); + r = sd_event_loop(event); + if (r == -ETIMEDOUT) + return log_error_errno(r, "Timed out for waiting the udev queue being empty."); + if (r < 0) + return log_error_errno(r, "Event loop failed: %m"); - for (;;) { - if (arg_exists && access(arg_exists, F_OK) >= 0) - return 0; - - /* exit if queue is empty */ - r = udev_queue_is_empty(); - if (r < 0) - return log_error_errno(r, "Failed to check queue is empty: %m"); - if (r > 0) - return 0; - - if (now(CLOCK_MONOTONIC) >= deadline) - return -ETIMEDOUT; - - /* wake up when queue becomes empty */ - r = fd_wait_for_event(fd, POLLIN, MSEC_PER_SEC); - if (r < 0) - return r; - if (r & POLLIN) { - r = flush_fd(fd); - if (r < 0) - return log_error_errno(r, "Failed to flush queue: %m"); - } - } + return 0; }