Compare commits
3 Commits
af12a0e8bf
...
30f0adf5b9
Author | SHA1 | Date |
---|---|---|
![]() |
30f0adf5b9 | |
![]() |
d3c944ac39 | |
![]() |
e38a171cb7 |
|
@ -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,102 @@ 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)
|
||||
return log_device_warning_errno(event->dev, r, "Failed to save event to socket storage: %m");
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
(void) notify_remove_fd_warn("event-serialization");
|
||||
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");
|
||||
|
||||
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 +1175,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 +1281,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 +1350,10 @@ int manager_main(Manager *manager) {
|
|||
_unused_ _cleanup_(notify_on_cleanup) const char *notify_message =
|
||||
notify_start(NOTIFY_READY, NOTIFY_STOPPING);
|
||||
|
||||
/* Remove the socket from fdstore, to make not future invocations get events on the previous
|
||||
* invocation. Otherwise, the stored 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");
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -ex
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
# Test for queued events serialization on stop and deserialization on start
|
||||
|
||||
rules="/run/udev/rules.d/99-test-17.serialization.rules"
|
||||
|
||||
mkdir -p "${rules%/*}"
|
||||
cat > "$rules" <<'EOF'
|
||||
SUBSYSTEM!="mem", GOTO="end"
|
||||
KERNEL!="null", GOTO="end"
|
||||
ACTION=="remove", GOTO="end"
|
||||
|
||||
IMPORT{db}="INVOCATIONS"
|
||||
IMPORT{program}="/bin/bash -c 'systemctl show --property=InvocationID systemd-udevd.service'"
|
||||
ENV{INVOCATIONS}+="%E{ACTION}_%E{SEQNUM}_%E{InvocationID}"
|
||||
ACTION=="add", RUN+="/bin/bash -c ':> /tmp/marker'", RUN+="/usr/bin/sleep 10"
|
||||
|
||||
LABEL="end"
|
||||
EOF
|
||||
|
||||
udevadm control --reload --log-level=debug
|
||||
|
||||
udevadm settle --timeout 30
|
||||
rm -f /tmp/marker
|
||||
|
||||
INVOCATION_BEFORE="$(systemctl show --property=InvocationID --value systemd-udevd.service)"
|
||||
|
||||
for action in remove add change change change change; do
|
||||
udevadm trigger --action="$action" /dev/null
|
||||
done
|
||||
|
||||
timeout 10 bash -c 'until test -e /tmp/marker; do sleep .1; done'
|
||||
systemctl restart systemd-udevd.service
|
||||
udevadm settle --timeout 30
|
||||
|
||||
INVOCATION_AFTER="$(systemctl show --property=InvocationID --value systemd-udevd.service)"
|
||||
|
||||
udevadm info --no-pager /dev/null
|
||||
|
||||
previous_seqnum=0
|
||||
expected_action=add
|
||||
expected_invocation="$INVOCATION_BEFORE"
|
||||
count=0
|
||||
while read -r action seqnum invocation; do
|
||||
test "$seqnum" -gt "$previous_seqnum"
|
||||
assert_eq "$action" "$expected_action"
|
||||
assert_eq "$invocation" "$expected_invocation"
|
||||
|
||||
previous_seqnum="$seqnum"
|
||||
expected_action=change
|
||||
expected_invocation="$INVOCATION_AFTER"
|
||||
: $((++count))
|
||||
done < <(udevadm info -q property --property=INVOCATIONS --value /dev/null | sed -e 's/ /\n/g; s/_/ /g')
|
||||
|
||||
assert_eq "$count" "5"
|
||||
|
||||
rm -f "$rules" /tmp/marker
|
||||
udevadm control --reload
|
||||
|
||||
exit 0
|
Loading…
Reference in New Issue