]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev: serialize queued events on exit, and deserialize them in the next invocation
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 5 Apr 2025 13:42:26 +0000 (22:42 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 18 Apr 2025 16:51:50 +0000 (01:51 +0900)
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.

src/udev/udev-manager.c

index 234deb1378ddcfd5d5afd401807163eccb5d46e9..8f96cad6590c0467ca6c09763da29ff5f174765f 100644 (file)
@@ -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");