From: Yu Watanabe Date: Sat, 5 Apr 2025 13:42:26 +0000 (+0900) Subject: udev: serialize queued events on exit, and deserialize them in the next invocation X-Git-Tag: v258-rc1~764^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=95e78b5e53320903b22ffefbafc216004593728c;p=thirdparty%2Fsystemd.git udev: serialize queued events on exit, and deserialize them in the next invocation To make systemd-udevd not lose received uevents on restart. This may be important on switching root, even though we typically trigger all devices after switching root by systemd-udev-trigger.service, but it may be disabled or modified by admin. --- diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index 234deb1378d..8f96cad6590 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -11,6 +11,7 @@ #include "fd-util.h" #include "fs-util.h" #include "hashmap.h" +#include "io-util.h" #include "iovec-util.h" #include "list.h" #include "mkdir.h" @@ -148,6 +149,7 @@ Manager* manager_free(Manager *manager) { free(manager->worker_notify_socket_path); sd_device_monitor_unref(manager->monitor); + udev_ctrl_unref(manager->ctrl); sd_varlink_server_unref(manager->varlink_server); @@ -863,6 +865,105 @@ static int event_queue_insert(Manager *manager, sd_device *dev) { return 0; } +static int manager_serialize_events(Manager *manager) { + int r; + + assert(manager); + + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *storage = NULL; + r = device_monitor_new_full(&storage, MONITOR_GROUP_NONE, -EBADF); + if (r < 0) + return log_warning_errno(r, "Failed to create new device monitor instance: %m"); + + union sockaddr_union a; + r = device_monitor_get_address(storage, &a); + if (r < 0) + return log_warning_errno(r, "Failed to get address of device monitor socket: %m"); + + uint64_t n = 0; + LIST_FOREACH(event, event, manager->events) { + if (event->state != EVENT_QUEUED) + continue; + + r = device_monitor_send(storage, &a, event->dev); + if (r < 0) { + log_device_warning_errno(event->dev, r, "Failed to save event to socket storage, ignoring: %m"); + continue; + } + + n++; + } + + if (n == 0) + return 0; + + r = notify_push_fd(sd_device_monitor_get_fd(storage), "event-serialization"); + if (r < 0) + return log_warning_errno(r, "Failed to push event serialization fd to service manager: %m"); + + log_debug("Serialized %"PRIu64" events.", n); + return 0; +} + +static int manager_deserialize_events(Manager *manager, int *fd) { + int r; + + assert(manager); + assert(fd); + assert(*fd >= 0); + + /* This may take and invalidate passed file descriptor even on failure. */ + + /* At this stage, we have not receive any events from the kernel, hence should be empty. */ + if (manager->events) + return log_warning_errno(SYNTHETIC_ERRNO(EALREADY), "Received multiple event storage socket (%i).", *fd); + + r = sd_is_socket(*fd, AF_NETLINK, SOCK_RAW, /* listening = */ -1); + if (r < 0) + return log_warning_errno(r, "Failed to verify type of event storage socket (%i): %m", *fd); + if (r == 0) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Received invalid event storage socket (%i).", *fd); + + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *storage = NULL; + r = device_monitor_new_full(&storage, MONITOR_GROUP_NONE, *fd); + if (r < 0) + return log_warning_errno(r, "Failed to initialize event storage: %m"); + TAKE_FD(*fd); + + r = device_monitor_allow_unicast_sender(storage, storage); + if (r < 0) + return log_warning_errno(r, "Failed to set trusted sender for event storage: %m"); + + uint64_t n = 0; + for (;;) { + r = fd_wait_for_event(sd_device_monitor_get_fd(storage), POLLIN, 0); + if (r == -EINTR) + continue; + if (r < 0) + return log_warning_errno(r, "Failed to wait for event from event storage: %m"); + if (r == 0) + break; + + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + r = sd_device_monitor_receive(storage, &dev); + if (r < 0) { + log_warning_errno(r, "Failed to receive device from event storage, ignoring: %m"); + continue; + } + + r = event_queue_insert(manager, dev); + if (r < 0) { + log_device_warning_errno(dev, r, "Failed to insert device into event queue, ignoring: %m"); + continue; + } + + n++; + } + + log_debug("Deserialized %"PRIu64" events.", n); + return 0; +} + static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) { Manager *manager = ASSERT_PTR(userdata); int r; @@ -1077,6 +1178,8 @@ static int on_post_exit(Manager *manager) { if (!hashmap_isempty(manager->workers)) return 0; /* There still exist running workers. */ + (void) manager_serialize_events(manager); + udev_watch_dump(); return sd_event_exit(manager->event, 0); } @@ -1181,6 +1284,8 @@ static int manager_listen_fds(Manager *manager) { r = manager_init_inotify(manager, fd); else if (streq(names[i], "config-serialization")) r = manager_deserialize_config(manager, &fd); + else if (streq(names[i], "event-serialization")) + r = manager_deserialize_events(manager, &fd); else r = log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Received unexpected fd (%s), ignoring.", names[i]); @@ -1248,6 +1353,11 @@ int manager_main(Manager *manager) { _unused_ _cleanup_(notify_on_cleanup) const char *notify_message = notify_start(NOTIFY_READY, NOTIFY_STOPPING); + /* We will start processing events in the loop below. Before starting processing, let's remove the + * event serialization fd from the fdstore, to avoid retrieving the serialized events again in future + * invocations. Otherwise, the serialized events may be processed multiple times. */ + (void) notify_remove_fd_warn("event-serialization"); + r = sd_event_loop(manager->event); if (r < 0) return log_error_errno(r, "Event loop failed: %m");