diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 5849799f05..c59c6a16a1 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -1767,3 +1767,38 @@ int vsock_get_local_cid(unsigned *ret) { return 0; } + +int netlink_socket_get_multicast_groups(int fd, size_t *ret_len, uint32_t **ret_groups) { + _cleanup_free_ uint32_t *groups = NULL; + socklen_t len = 0, old_len; + + assert(fd >= 0); + + /* This returns ENOPROTOOPT if the kernel is older than 4.2. */ + + if (getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len) < 0) + return -errno; + + if (len == 0) + goto finalize; + + groups = new0(uint32_t, len); + if (!groups) + return -ENOMEM; + + old_len = len; + + if (getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len) < 0) + return -errno; + + if (old_len != len) + return -EIO; + +finalize: + if (ret_len) + *ret_len = len; + if (ret_groups) + *ret_groups = TAKE_PTR(groups); + + return 0; +} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index c784125ccb..18699a5349 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -396,3 +396,5 @@ int socket_address_parse_vsock(SocketAddress *ret_address, const char *s); #define SOMAXCONN_DELUXE INT_MAX int vsock_get_local_cid(unsigned *ret); + +int netlink_socket_get_multicast_groups(int fd, size_t *ret_len, uint32_t **ret_groups); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index af2ea430f5..65b81db464 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -1044,4 +1044,7 @@ global: sd_varlink_take_fd; sd_varlink_unref; sd_varlink_wait; + sd_device_monitor_is_running; + sd_device_monitor_get_fd; + sd_device_monitor_receive; } LIBSYSTEMD_256; diff --git a/src/libsystemd/sd-device/device-monitor-private.h b/src/libsystemd/sd-device/device-monitor-private.h index 33e2714605..79f9d834ff 100644 --- a/src/libsystemd/sd-device/device-monitor-private.h +++ b/src/libsystemd/sd-device/device-monitor-private.h @@ -5,6 +5,8 @@ #include "sd-device.h" +#include "socket-util.h" + typedef enum MonitorNetlinkGroup { MONITOR_GROUP_NONE, MONITOR_GROUP_KERNEL, @@ -14,9 +16,6 @@ typedef enum MonitorNetlinkGroup { } MonitorNetlinkGroup; int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group, int fd); -int device_monitor_disconnect(sd_device_monitor *m); +int device_monitor_get_address(sd_device_monitor *m, union sockaddr_union *ret); int device_monitor_allow_unicast_sender(sd_device_monitor *m, sd_device_monitor *sender); -int device_monitor_enable_receiving(sd_device_monitor *m); -int device_monitor_get_fd(sd_device_monitor *m); -int device_monitor_send_device(sd_device_monitor *m, sd_device_monitor *destination, sd_device *device); -int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret); +int device_monitor_send(sd_device_monitor *m, const union sockaddr_union *destination, sd_device *device); diff --git a/src/libsystemd/sd-device/device-monitor.c b/src/libsystemd/sd-device/device-monitor.c index a7ef03abac..9d598f5ac1 100644 --- a/src/libsystemd/sd-device/device-monitor.c +++ b/src/libsystemd/sd-device/device-monitor.c @@ -20,11 +20,11 @@ #include "fd-util.h" #include "format-util.h" #include "hashmap.h" +#include "io-util.h" #include "iovec-util.h" #include "missing_socket.h" #include "mountpoint-util.h" #include "set.h" -#include "socket-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -45,7 +45,6 @@ struct sd_device_monitor { int sock; union sockaddr_union snl; union sockaddr_union snl_trusted_sender; - bool bound; UIDRange *mapped_userns_uid_range; @@ -57,6 +56,10 @@ struct sd_device_monitor { Set *nomatch_parent_filter; bool filter_uptodate; + bool multicast_group_dropped; + size_t multicast_group_len; + uint32_t *multicast_groups; + sd_event *event; sd_event_source *event_source; char *description; @@ -101,12 +104,19 @@ static int monitor_set_nl_address(sd_device_monitor *m) { return 0; } +int device_monitor_get_address(sd_device_monitor *m, union sockaddr_union *ret) { + assert(m); + assert(ret); + + *ret = m->snl; + return 0; +} + int device_monitor_allow_unicast_sender(sd_device_monitor *m, sd_device_monitor *sender) { assert(m); assert(sender); - m->snl_trusted_sender.nl.nl_pid = sender->snl.nl.nl_pid; - return 0; + return device_monitor_get_address(sender, &m->snl_trusted_sender); } _public_ int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size) { @@ -115,15 +125,8 @@ _public_ int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, siz return fd_set_rcvbuf(m->sock, size, false); } -int device_monitor_disconnect(sd_device_monitor *m) { - assert(m); - - m->sock = safe_close(m->sock); - return 0; -} - -int device_monitor_get_fd(sd_device_monitor *m) { - assert(m); +_public_ int sd_device_monitor_get_fd(sd_device_monitor *m) { + assert_return(m, -EINVAL); return m->sock; } @@ -170,17 +173,24 @@ int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group, *m = (sd_device_monitor) { .n_ref = 1, .sock = fd >= 0 ? fd : TAKE_FD(sock), - .bound = fd >= 0, .snl.nl.nl_family = AF_NETLINK, .snl.nl.nl_groups = group, }; - if (fd >= 0) { - r = monitor_set_nl_address(m); - if (r < 0) { - log_monitor_errno(m, r, "Failed to set netlink address: %m"); - goto fail; - } + if (fd < 0) { + /* enable receiving of sender credentials */ + r = setsockopt_int(m->sock, SOL_SOCKET, SO_PASSCRED, true); + if (r < 0) + return log_monitor_errno(m, r, "Failed to set socket option SO_PASSCRED: %m"); + + if (bind(m->sock, &m->snl.sa, sizeof(struct sockaddr_nl)) < 0) + return log_monitor_errno(m, errno, "Failed to bind monitoring socket: %m"); + } + + r = monitor_set_nl_address(m); + if (r < 0) { + log_monitor_errno(m, r, "Failed to set netlink address: %m"); + goto fail; } if (DEBUG_LOGGING) { @@ -231,22 +241,66 @@ fail: /* Let's unset the socket fd in the monitor object before we destroy it so that the fd passed in is * not closed on failure. */ if (fd >= 0) - m->sock = -1; + m->sock = -EBADF; return r; } _public_ int sd_device_monitor_new(sd_device_monitor **ret) { - return device_monitor_new_full(ret, MONITOR_GROUP_UDEV, -1); + return device_monitor_new_full(ret, MONITOR_GROUP_UDEV, -EBADF); +} + +_public_ int sd_device_monitor_is_running(sd_device_monitor *m) { + if (!m) + return 0; + + return sd_event_source_get_enabled(m->event_source, NULL); +} + +static int device_monitor_update_multicast_groups(sd_device_monitor *m, bool add) { + int r, opt = add ? NETLINK_ADD_MEMBERSHIP : NETLINK_DROP_MEMBERSHIP; + + assert(m); + assert(m->sock >= 0); + + for (size_t i = 0; i < m->multicast_group_len; i++) + for (unsigned j = 0; j < sizeof(uint32_t) * 8; j++) + if (m->multicast_groups[i] & (1U << j)) { + unsigned group = i * sizeof(uint32_t) * 8 + j + 1; + + /* group is "unsigned", but netlink(7) says the argument is "int". */ + r = setsockopt_int(m->sock, SOL_NETLINK, opt, group); + if (r < 0) + return r; + } + + return 0; } _public_ int sd_device_monitor_stop(sd_device_monitor *m) { + int r; + assert_return(m, -EINVAL); + assert_return(m->sock >= 0, -ESTALE); - m->event_source = sd_event_source_unref(m->event_source); - (void) device_monitor_disconnect(m); + if (!m->multicast_group_dropped) { + m->multicast_group_len = 0; + m->multicast_groups = mfree(m->multicast_groups); - return 0; + /* Save multicast groups. */ + r = netlink_socket_get_multicast_groups(m->sock, &m->multicast_group_len, &m->multicast_groups); + if (r < 0 && r != -ENOPROTOOPT) + return r; + + /* Leave from all multicast groups to prevent the buffer is filled. */ + r = device_monitor_update_multicast_groups(m, /* add = */ false); + if (r < 0) + return r; + + m->multicast_group_dropped = true; + } + + return sd_event_source_set_enabled(m->event_source, SD_EVENT_OFF); } static int device_monitor_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { @@ -254,7 +308,7 @@ static int device_monitor_event_handler(sd_event_source *s, int fd, uint32_t rev _unused_ _cleanup_(log_context_unrefp) LogContext *c = NULL; sd_device_monitor *m = ASSERT_PTR(userdata); - if (device_monitor_receive_device(m, &device) <= 0) + if (sd_device_monitor_receive(m, &device) <= 0) return 0; if (log_context_enabled()) @@ -270,6 +324,7 @@ _public_ int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_han int r; assert_return(m, -EINVAL); + assert_return(m->sock >= 0, -ESTALE); if (!m->event) { r = sd_device_monitor_attach_event(m, NULL); @@ -277,26 +332,48 @@ _public_ int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_han return r; } - r = device_monitor_enable_receiving(m); + r = sd_device_monitor_filter_update(m); if (r < 0) - return r; + return log_monitor_errno(m, r, "Failed to update filter: %m"); m->callback = callback; m->userdata = userdata; - r = sd_event_add_io(m->event, &m->event_source, m->sock, EPOLLIN, device_monitor_event_handler, m); + if (!m->event_source) { + /* The monitor has never started. Add IO event source. */ + r = sd_event_add_io(m->event, &m->event_source, m->sock, EPOLLIN, device_monitor_event_handler, m); + if (r < 0) + return r; + + (void) sd_event_source_set_description(m->event_source, m->description ?: "sd-device-monitor"); + return 0; + } + + r = sd_device_monitor_is_running(m); if (r < 0) return r; + if (r == 0) { + /* If the monitor was previously started but now it is stopped, flush anything queued during + * the monitor is stopped. */ + r = flush_fd(m->sock); + if (r < 0) + return r; - (void) sd_event_source_set_description(m->event_source, m->description ?: "sd-device-monitor"); + /* Then, join the saved broadcast groups again. */ + r = device_monitor_update_multicast_groups(m, /* add = */ true); + if (r < 0) + return r; - return 0; + m->multicast_group_dropped = false; + } + + return sd_event_source_set_enabled(m->event_source, SD_EVENT_ON); } _public_ int sd_device_monitor_detach_event(sd_device_monitor *m) { assert_return(m, -EINVAL); - (void) sd_device_monitor_stop(m); + m->event_source = sd_event_source_unref(m->event_source); m->event = sd_event_unref(m->event); return 0; @@ -354,38 +431,11 @@ _public_ int sd_device_monitor_get_description(sd_device_monitor *m, const char return 0; } -int device_monitor_enable_receiving(sd_device_monitor *m) { - int r; - - assert(m); - - r = sd_device_monitor_filter_update(m); - if (r < 0) - return log_monitor_errno(m, r, "Failed to update filter: %m"); - - if (!m->bound) { - /* enable receiving of sender credentials */ - r = setsockopt_int(m->sock, SOL_SOCKET, SO_PASSCRED, true); - if (r < 0) - return log_monitor_errno(m, r, "Failed to set socket option SO_PASSCRED: %m"); - - if (bind(m->sock, &m->snl.sa, sizeof(struct sockaddr_nl)) < 0) - return log_monitor_errno(m, errno, "Failed to bind monitoring socket: %m"); - - m->bound = true; - - r = monitor_set_nl_address(m); - if (r < 0) - return log_monitor_errno(m, r, "Failed to set address: %m"); - } - - return 0; -} - static sd_device_monitor *device_monitor_free(sd_device_monitor *m) { assert(m); (void) sd_device_monitor_detach_event(m); + m->sock = safe_close(m->sock); uid_range_free(m->mapped_userns_uid_range); free(m->description); @@ -395,6 +445,7 @@ static sd_device_monitor *device_monitor_free(sd_device_monitor *m) { hashmap_free(m->nomatch_sysattr_filter); set_free(m->match_parent_filter); set_free(m->nomatch_parent_filter); + free(m->multicast_groups); return mfree(m); } @@ -486,7 +537,7 @@ static bool check_sender_uid(sd_device_monitor *m, uid_t uid) { return false; } -int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) { +_public_ int sd_device_monitor_receive(sd_device_monitor *m, sd_device **ret) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; _cleanup_free_ uint8_t *buf_alloc = NULL; union { @@ -511,8 +562,8 @@ int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) { bool is_initialized = false; int r; - assert(m); - assert(ret); + assert_return(m, -EINVAL); + assert_return(ret, -EINVAL); n = next_datagram_size_fd(m->sock); if (n < 0) { @@ -610,9 +661,10 @@ int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) { r = passes_filter(m, device); if (r < 0) return log_device_monitor_errno(device, m, r, "Failed to check received device passing filter: %m"); - if (r == 0) + if (r == 0) { log_device_monitor(device, m, "Received device does not pass filter, ignoring."); - else + *ret = NULL; + } else *ret = TAKE_PTR(device); return r; @@ -634,9 +686,9 @@ static uint64_t string_bloom64(const char *str) { return bits; } -int device_monitor_send_device( +int device_monitor_send( sd_device_monitor *m, - sd_device_monitor *destination, + const union sockaddr_union *destination, sd_device *device) { monitor_netlink_header nlh = { @@ -652,7 +704,7 @@ int device_monitor_send_device( .msg_iovlen = 2, }; /* default destination for sending */ - union sockaddr_union default_destination = { + static const union sockaddr_union default_destination = { .nl.nl_family = AF_NETLINK, .nl.nl_groups = MONITOR_GROUP_UDEV, }; @@ -702,7 +754,7 @@ int device_monitor_send_device( * If we send to a multicast group, we will get * ECONNREFUSED, which is expected. */ - smsg.msg_name = destination ? &destination->snl : &default_destination; + smsg.msg_name = (struct sockaddr_nl*) &(destination ?: &default_destination)->nl; smsg.msg_namelen = sizeof(struct sockaddr_nl); count = sendmsg(m->sock, &smsg, 0); if (count < 0) { diff --git a/src/libsystemd/sd-device/test-sd-device-monitor.c b/src/libsystemd/sd-device/test-sd-device-monitor.c index 3dbf9871dc..d6a6e5d364 100644 --- a/src/libsystemd/sd-device/test-sd-device-monitor.c +++ b/src/libsystemd/sd-device/test-sd-device-monitor.c @@ -9,6 +9,7 @@ #include "device-monitor-private.h" #include "device-private.h" #include "device-util.h" +#include "io-util.h" #include "macro.h" #include "mountpoint-util.h" #include "path-util.h" @@ -17,285 +18,382 @@ #include "tests.h" #include "virt.h" +static void prepare_loopback(sd_device **ret) { + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + + ASSERT_OK(sd_device_new_from_syspath(&dev, "/sys/class/net/lo")); + ASSERT_OK(device_add_property(dev, "ACTION", "add")); + ASSERT_OK(device_add_property(dev, "SEQNUM", "10")); + ASSERT_OK(device_add_tag(dev, "TEST_SD_DEVICE_MONITOR", true)); + + *ret = TAKE_PTR(dev); +} + +static int prepare_sda(sd_device **ret) { + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + int r; + + r = sd_device_new_from_subsystem_sysname(&dev, "block", "sda"); + if (r < 0) + return r; + + ASSERT_OK(device_add_property(dev, "ACTION", "change")); + ASSERT_OK(device_add_property(dev, "SEQNUM", "11")); + + *ret = TAKE_PTR(dev); + return 0; +} + static int monitor_handler(sd_device_monitor *m, sd_device *d, void *userdata) { const char *s, *syspath = userdata; - assert_se(sd_device_get_syspath(d, &s) >= 0); - assert_se(streq(s, syspath)); + ASSERT_OK(sd_device_get_syspath(d, &s)); + ASSERT_TRUE(streq(s, syspath)); return sd_event_exit(sd_device_monitor_get_event(m), 100); } -static void test_receive_device_fail(void) { +static void prepare_monitor(sd_device_monitor **ret_server, sd_device_monitor **ret_client, union sockaddr_union *ret_address) { _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; - _cleanup_(sd_device_unrefp) sd_device *loopback = NULL; + + ASSERT_OK(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1)); + ASSERT_OK(sd_device_monitor_set_description(monitor_server, "sender")); + ASSERT_OK(sd_device_monitor_start(monitor_server, NULL, NULL)); + + ASSERT_OK(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1)); + ASSERT_OK(sd_device_monitor_set_description(monitor_client, "client")); + ASSERT_OK(device_monitor_allow_unicast_sender(monitor_client, monitor_server)); + ASSERT_OK(device_monitor_get_address(monitor_client, ret_address)); + + *ret_server = TAKE_PTR(monitor_server); + *ret_client = TAKE_PTR(monitor_client); +} + +static void send_by_enumerator( + sd_device_monitor *monitor_server, + const union sockaddr_union *address, + sd_device_enumerator *e, + size_t n, + const char *syspath_filter) { + + size_t i = 0; + + FOREACH_DEVICE(e, d) { + const char *p, *s; + + ASSERT_OK(sd_device_get_syspath(d, &p)); + ASSERT_OK(sd_device_get_subsystem(d, &s)); + + if (syspath_filter && path_startswith(p, syspath_filter)) + continue; + + ASSERT_OK(device_add_property(d, "ACTION", "add")); + ASSERT_OK(device_add_property(d, "SEQNUM", "10")); + + log_device_debug(d, "Sending device subsystem:%s syspath:%s", s, p); + ASSERT_OK(device_monitor_send(monitor_server, address, d)); + + /* The sysattr and parent filters are not implemented in BPF yet. So, sending multiple + * devices may fills up buffer and device_monitor_send() may return EAGAIN. Let's send only a + * few devices here, which should be filtered out by the receiver. */ + if (n != SIZE_MAX && ++i >= n) + break; + } +} + +TEST(sd_device_monitor_is_running) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL; + + ASSERT_EQ(sd_device_monitor_is_running(NULL), 0); + + ASSERT_OK(device_monitor_new_full(&m, MONITOR_GROUP_NONE, -1)); + ASSERT_EQ(sd_device_monitor_is_running(m), 0); + ASSERT_OK(sd_device_monitor_start(m, NULL, NULL)); + ASSERT_EQ(sd_device_monitor_is_running(m), 1); + ASSERT_OK(sd_device_monitor_stop(m)); + ASSERT_EQ(sd_device_monitor_is_running(m), 0); +} + +TEST(sd_device_monitor_start_stop) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + union sockaddr_union sa; const char *syspath; - log_info("/* %s */", __func__); + prepare_loopback(&device); + + ASSERT_OK(sd_device_get_syspath(device, &syspath)); + + prepare_monitor(&monitor_server, &monitor_client, &sa); + + /* Sending devices before starting client. */ + ASSERT_OK(sd_device_enumerator_new(&e)); + send_by_enumerator(monitor_server, &sa, e, 5, syspath); + + /* sd_device_monitor_start() can be called multiple times. */ + ASSERT_OK(sd_device_monitor_start(monitor_client, NULL, NULL)); + ASSERT_OK(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath)); + + /* Sending devices after client being started. */ + send_by_enumerator(monitor_server, &sa, e, 5, syspath); + + /* sd_device_monitor_stop() can be called multiple times. */ + ASSERT_OK(sd_device_monitor_stop(monitor_client)); + ASSERT_OK(sd_device_monitor_stop(monitor_client)); + + /* Sending devices before restarting client. */ + send_by_enumerator(monitor_server, &sa, e, 5, syspath); + + /* Restart monitor, and check if the previously sent devices are ignored. */ + ASSERT_OK(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath)); + ASSERT_OK(device_monitor_send(monitor_server, &sa, device)); + ASSERT_EQ(sd_event_loop(sd_device_monitor_get_event(monitor_client)), 100); +} + +TEST(refuse_invalid_device) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; + _cleanup_(sd_device_unrefp) sd_device *loopback = NULL; + union sockaddr_union sa; + const char *syspath; /* Try to send device with invalid action and without seqnum. */ - assert_se(sd_device_new_from_syspath(&loopback, "/sys/class/net/lo") >= 0); - assert_se(device_add_property(loopback, "ACTION", "hoge") >= 0); + ASSERT_OK(sd_device_new_from_syspath(&loopback, "/sys/class/net/lo")); + ASSERT_OK(device_add_property(loopback, "ACTION", "hoge")); - assert_se(sd_device_get_syspath(loopback, &syspath) >= 0); + ASSERT_OK(sd_device_get_syspath(loopback, &syspath)); - assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0); - assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0); + prepare_monitor(&monitor_server, &monitor_client, &sa); - assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0); - assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0); - assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0); - - assert_se(device_monitor_send_device(monitor_server, monitor_client, loopback) >= 0); - assert_se(sd_event_run(sd_device_monitor_get_event(monitor_client), 0) >= 0); + ASSERT_OK(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath)); + ASSERT_OK(device_monitor_send(monitor_server, &sa, loopback)); + ASSERT_OK(sd_event_run(sd_device_monitor_get_event(monitor_client), 0)); } static void test_send_receive_one(sd_device *device, bool subsystem_filter, bool tag_filter, bool use_bpf) { _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; const char *syspath, *subsystem, *devtype = NULL; + union sockaddr_union sa; log_device_info(device, "/* %s(subsystem_filter=%s, tag_filter=%s, use_bpf=%s) */", __func__, true_false(subsystem_filter), true_false(tag_filter), true_false(use_bpf)); - assert_se(sd_device_get_syspath(device, &syspath) >= 0); + ASSERT_OK(sd_device_get_syspath(device, &syspath)); - assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0); - assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0); - - assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0); - assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0); - assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0); + prepare_monitor(&monitor_server, &monitor_client, &sa); if (subsystem_filter) { - assert_se(sd_device_get_subsystem(device, &subsystem) >= 0); + ASSERT_OK(sd_device_get_subsystem(device, &subsystem)); (void) sd_device_get_devtype(device, &devtype); - assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, devtype) >= 0); + ASSERT_OK(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, devtype)); } if (tag_filter) FOREACH_DEVICE_TAG(device, tag) - assert_se(sd_device_monitor_filter_add_match_tag(monitor_client, tag) >= 0); + ASSERT_OK(sd_device_monitor_filter_add_match_tag(monitor_client, tag)); if ((subsystem_filter || tag_filter) && use_bpf) - assert_se(sd_device_monitor_filter_update(monitor_client) >= 0); + ASSERT_OK(sd_device_monitor_filter_update(monitor_client)); - assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0); - assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 100); + ASSERT_OK(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath)); + ASSERT_OK(device_monitor_send(monitor_server, &sa, device)); + ASSERT_EQ(sd_event_loop(sd_device_monitor_get_event(monitor_client)), 100); } -static void test_subsystem_filter(sd_device *device) { - _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; - _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; - const char *syspath, *subsystem; - - log_device_info(device, "/* %s */", __func__); - - assert_se(sd_device_get_syspath(device, &syspath) >= 0); - assert_se(sd_device_get_subsystem(device, &subsystem) >= 0); - - assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0); - assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0); - - assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0); - assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0); - assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, NULL) >= 0); - assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0); - - assert_se(sd_device_enumerator_new(&e) >= 0); - assert_se(sd_device_enumerator_add_match_subsystem(e, subsystem, false) >= 0); - FOREACH_DEVICE(e, d) { - const char *p, *s; - - assert_se(sd_device_get_syspath(d, &p) >= 0); - assert_se(sd_device_get_subsystem(d, &s) >= 0); - - assert_se(device_add_property(d, "ACTION", "add") >= 0); - assert_se(device_add_property(d, "SEQNUM", "10") >= 0); - - log_device_debug(d, "Sending device subsystem:%s syspath:%s", s, p); - assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0); - } - - log_device_info(device, "Sending device subsystem:%s syspath:%s", subsystem, syspath); - assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0); - assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 100); -} - -static void test_tag_filter(sd_device *device) { - _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; - _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; - const char *syspath; - - log_device_info(device, "/* %s */", __func__); - - assert_se(sd_device_get_syspath(device, &syspath) >= 0); - - assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0); - assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0); - - assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0); - assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0); - assert_se(sd_device_monitor_filter_add_match_tag(monitor_client, "TEST_SD_DEVICE_MONITOR") >= 0); - assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0); - - assert_se(sd_device_enumerator_new(&e) >= 0); - FOREACH_DEVICE(e, d) { - const char *p; - - assert_se(sd_device_get_syspath(d, &p) >= 0); - - assert_se(device_add_property(d, "ACTION", "add") >= 0); - assert_se(device_add_property(d, "SEQNUM", "10") >= 0); - - log_device_debug(d, "Sending device syspath:%s", p); - assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0); - } - - log_device_info(device, "Sending device syspath:%s", syspath); - assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0); - assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 100); - -} - -static void test_sysattr_filter(sd_device *device, const char *sysattr) { - _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; - _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; - const char *syspath, *sysattr_value; - - log_device_info(device, "/* %s(%s) */", __func__, sysattr); - - assert_se(sd_device_get_syspath(device, &syspath) >= 0); - assert_se(sd_device_get_sysattr_value(device, sysattr, &sysattr_value) >= 0); - - assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0); - assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0); - - assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0); - assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0); - assert_se(sd_device_monitor_filter_add_match_sysattr(monitor_client, sysattr, sysattr_value, true) >= 0); - assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0); - - assert_se(sd_device_enumerator_new(&e) >= 0); - assert_se(sd_device_enumerator_add_match_sysattr(e, sysattr, sysattr_value, false) >= 0); - FOREACH_DEVICE(e, d) { - const char *p; - - assert_se(sd_device_get_syspath(d, &p) >= 0); - - assert_se(device_add_property(d, "ACTION", "add") >= 0); - assert_se(device_add_property(d, "SEQNUM", "10") >= 0); - - log_device_debug(d, "Sending device syspath:%s", p); - assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0); - - /* The sysattr filter is not implemented in BPF yet. So, sending multiple devices may fills up - * buffer and device_monitor_send_device() may return EAGAIN. Let's send one device here, - * which should be filtered out by the receiver. */ - break; - } - - log_device_info(device, "Sending device syspath:%s", syspath); - assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0); - assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 100); - -} - -static void test_parent_filter(sd_device *device) { - _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; - _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; - const char *syspath, *parent_syspath; - sd_device *parent; - int r; - - log_device_info(device, "/* %s */", __func__); - - assert_se(sd_device_get_syspath(device, &syspath) >= 0); - r = sd_device_get_parent(device, &parent); - if (r < 0) - return (void) log_device_info(device, "Device does not have parent, skipping."); - assert_se(sd_device_get_syspath(parent, &parent_syspath) >= 0); - - assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0); - assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0); - - assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0); - assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0); - assert_se(sd_device_monitor_filter_add_match_parent(monitor_client, parent, true) >= 0); - assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0); - - assert_se(sd_device_enumerator_new(&e) >= 0); - FOREACH_DEVICE(e, d) { - const char *p; - - assert_se(sd_device_get_syspath(d, &p) >= 0); - if (path_startswith(p, parent_syspath)) - continue; - - assert_se(device_add_property(d, "ACTION", "add") >= 0); - assert_se(device_add_property(d, "SEQNUM", "10") >= 0); - - log_device_debug(d, "Sending device syspath:%s", p); - assert_se(device_monitor_send_device(monitor_server, monitor_client, d) >= 0); - - /* The parent filter is not implemented in BPF yet. So, sending multiple devices may fills up - * buffer and device_monitor_send_device() may return EAGAIN. Let's send one device here, - * which should be filtered out by the receiver. */ - break; - } - - log_device_info(device, "Sending device syspath:%s", syspath); - assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0); - assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 100); - -} - -static void test_sd_device_monitor_filter_remove(sd_device *device) { - _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; - const char *syspath; - - log_device_info(device, "/* %s */", __func__); - - assert_se(sd_device_get_syspath(device, &syspath) >= 0); - - assert_se(device_monitor_new_full(&monitor_server, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_server, "sender") >= 0); - assert_se(sd_device_monitor_start(monitor_server, NULL, NULL) >= 0); - - assert_se(device_monitor_new_full(&monitor_client, MONITOR_GROUP_NONE, -1) >= 0); - assert_se(sd_device_monitor_set_description(monitor_client, "receiver") >= 0); - assert_se(device_monitor_allow_unicast_sender(monitor_client, monitor_server) >= 0); - assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0); - - assert_se(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, "hoge", NULL) >= 0); - assert_se(sd_device_monitor_filter_update(monitor_client) >= 0); - - assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0); - assert_se(sd_event_run(sd_device_monitor_get_event(monitor_client), 0) >= 0); - - assert_se(sd_device_monitor_filter_remove(monitor_client) >= 0); - - assert_se(device_monitor_send_device(monitor_server, monitor_client, device) >= 0); - assert_se(sd_event_loop(sd_device_monitor_get_event(monitor_client)) == 100); -} - -int main(int argc, char *argv[]) { +TEST(sd_device_monitor_send_receive) { _cleanup_(sd_device_unrefp) sd_device *loopback = NULL, *sda = NULL; int r; - test_setup_logging(LOG_INFO); + prepare_loopback(&loopback); + test_send_receive_one(loopback, false, false, false); + test_send_receive_one(loopback, true, false, false); + test_send_receive_one(loopback, false, true, false); + test_send_receive_one(loopback, true, true, false); + test_send_receive_one(loopback, true, false, true); + test_send_receive_one(loopback, false, true, true); + test_send_receive_one(loopback, true, true, true); + r = prepare_sda(&sda); + if (r < 0) + return (void) log_tests_skipped_errno(r, "Failed to create sd_device for sda"); + + test_send_receive_one(sda, false, false, false); + test_send_receive_one(sda, true, false, false); + test_send_receive_one(sda, false, true, false); + test_send_receive_one(sda, true, true, false); + test_send_receive_one(sda, true, false, true); + test_send_receive_one(sda, false, true, true); + test_send_receive_one(sda, true, true, true); +} + +TEST(sd_device_monitor_filter_add_match_subsystem_devtype) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + const char *syspath, *subsystem; + union sockaddr_union sa; + + prepare_loopback(&device); + + ASSERT_OK(sd_device_get_syspath(device, &syspath)); + ASSERT_OK(sd_device_get_subsystem(device, &subsystem)); + + prepare_monitor(&monitor_server, &monitor_client, &sa); + + ASSERT_OK(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, subsystem, NULL)); + ASSERT_OK(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath)); + + ASSERT_OK(sd_device_enumerator_new(&e)); + ASSERT_OK(sd_device_enumerator_add_match_subsystem(e, subsystem, false)); + send_by_enumerator(monitor_server, &sa, e, SIZE_MAX, NULL); + + log_device_info(device, "Sending device subsystem:%s syspath:%s", subsystem, syspath); + ASSERT_OK(device_monitor_send(monitor_server, &sa, device)); + ASSERT_EQ(sd_event_loop(sd_device_monitor_get_event(monitor_client)), 100); +} + +TEST(sd_device_monitor_filter_add_match_tag) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + union sockaddr_union sa; + const char *syspath; + + prepare_loopback(&device); + + ASSERT_OK(sd_device_get_syspath(device, &syspath)); + + prepare_monitor(&monitor_server, &monitor_client, &sa); + + ASSERT_OK(sd_device_monitor_filter_add_match_tag(monitor_client, "TEST_SD_DEVICE_MONITOR")); + ASSERT_OK(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath)); + + ASSERT_OK(sd_device_enumerator_new(&e)); + send_by_enumerator(monitor_server, &sa, e, SIZE_MAX, NULL); + + log_device_info(device, "Sending device syspath:%s", syspath); + ASSERT_OK(device_monitor_send(monitor_server, &sa, device)); + ASSERT_EQ(sd_event_loop(sd_device_monitor_get_event(monitor_client)), 100); +} + +TEST(sd_device_monitor_filter_add_match_sysattr) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + static const char *sysattr = "ifindex"; + const char *syspath, *sysattr_value; + union sockaddr_union sa; + + prepare_loopback(&device); + + ASSERT_OK(sd_device_get_syspath(device, &syspath)); + ASSERT_OK(sd_device_get_sysattr_value(device, sysattr, &sysattr_value)); + + prepare_monitor(&monitor_server, &monitor_client, &sa); + + ASSERT_OK(sd_device_monitor_filter_add_match_sysattr(monitor_client, sysattr, sysattr_value, true)); + ASSERT_OK(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath)); + + ASSERT_OK(sd_device_enumerator_new(&e)); + ASSERT_OK(sd_device_enumerator_add_match_sysattr(e, sysattr, sysattr_value, false)); + send_by_enumerator(monitor_server, &sa, e, 5, NULL); + + log_device_info(device, "Sending device syspath:%s", syspath); + ASSERT_OK(device_monitor_send(monitor_server, &sa, device)); + ASSERT_EQ(sd_event_loop(sd_device_monitor_get_event(monitor_client)), 100); +} + +TEST(sd_device_monitor_add_match_parent) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + const char *syspath, *parent_syspath; + union sockaddr_union sa; + sd_device *parent; + int r; + + r = prepare_sda(&device); + if (r < 0) + return (void) log_tests_skipped_errno(r, "Failed to create sd_device for sda"); + + ASSERT_OK(sd_device_get_syspath(device, &syspath)); + + r = sd_device_get_parent(device, &parent); + if (r < 0) + return (void) log_tests_skipped("sda does not have parent"); + + ASSERT_OK(sd_device_get_syspath(parent, &parent_syspath)); + + prepare_monitor(&monitor_server, &monitor_client, &sa); + + ASSERT_OK(sd_device_monitor_filter_add_match_parent(monitor_client, parent, true)); + ASSERT_OK(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath)); + + ASSERT_OK(sd_device_enumerator_new(&e)); + send_by_enumerator(monitor_server, &sa, e, 5, parent_syspath); + + log_device_info(device, "Sending device syspath:%s", syspath); + ASSERT_OK(device_monitor_send(monitor_server, &sa, device)); + ASSERT_EQ(sd_event_loop(sd_device_monitor_get_event(monitor_client)), 100); +} + +TEST(sd_device_monitor_filter_remove) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + union sockaddr_union sa; + const char *syspath; + + prepare_loopback(&device); + + ASSERT_OK(sd_device_get_syspath(device, &syspath)); + + prepare_monitor(&monitor_server, &monitor_client, &sa); + + ASSERT_OK(sd_device_monitor_filter_add_match_subsystem_devtype(monitor_client, "hoge", NULL)); + ASSERT_OK(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath)); + + ASSERT_OK(device_monitor_send(monitor_server, &sa, device)); + ASSERT_OK(sd_event_run(sd_device_monitor_get_event(monitor_client), 0)); + + ASSERT_OK(sd_device_monitor_filter_remove(monitor_client)); + + ASSERT_OK(device_monitor_send(monitor_server, &sa, device)); + ASSERT_EQ(sd_event_loop(sd_device_monitor_get_event(monitor_client)), 100); +} + +TEST(sd_device_monitor_receive) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + union sockaddr_union sa; + const char *syspath; + int fd, r; + + prepare_loopback(&device); + + ASSERT_OK(sd_device_get_syspath(device, &syspath)); + + prepare_monitor(&monitor_server, &monitor_client, &sa); + + ASSERT_OK(device_monitor_send(monitor_server, &sa, device)); + + ASSERT_OK(fd = sd_device_monitor_get_fd(monitor_client)); + for (;;) { + r = fd_wait_for_event(fd, POLLIN, 10 * USEC_PER_SEC); + if (r == -EINTR) + continue; + ASSERT_GT(r, 0); + break; + } + + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + ASSERT_GT(sd_device_monitor_receive(monitor_client, &dev), 0); + + const char *s; + ASSERT_OK(sd_device_get_syspath(dev, &s)); + ASSERT_TRUE(streq(s, syspath)); +} + +static int intro(void) { if (getuid() != 0) return log_tests_skipped("not root"); @@ -305,44 +403,10 @@ int main(int argc, char *argv[]) { if (path_is_read_only_fs("/sys") > 0) return log_tests_skipped("Running in container"); - test_receive_device_fail(); + if (access("/sys/class/net/lo", F_OK) < 0) + return log_tests_skipped_errno(errno, "Loopback network interface 'lo' does not exist"); - assert_se(sd_device_new_from_syspath(&loopback, "/sys/class/net/lo") >= 0); - assert_se(device_add_property(loopback, "ACTION", "add") >= 0); - assert_se(device_add_property(loopback, "SEQNUM", "10") >= 0); - assert_se(device_add_tag(loopback, "TEST_SD_DEVICE_MONITOR", true) >= 0); - - test_send_receive_one(loopback, false, false, false); - test_send_receive_one(loopback, true, false, false); - test_send_receive_one(loopback, false, true, false); - test_send_receive_one(loopback, true, true, false); - test_send_receive_one(loopback, true, false, true); - test_send_receive_one(loopback, false, true, true); - test_send_receive_one(loopback, true, true, true); - - test_subsystem_filter(loopback); - test_tag_filter(loopback); - test_sysattr_filter(loopback, "ifindex"); - test_sd_device_monitor_filter_remove(loopback); - - r = sd_device_new_from_subsystem_sysname(&sda, "block", "sda"); - if (r < 0) { - log_info_errno(r, "Failed to create sd_device for sda, skipping remaining tests: %m"); - return 0; - } - - assert_se(device_add_property(sda, "ACTION", "change") >= 0); - assert_se(device_add_property(sda, "SEQNUM", "11") >= 0); - - test_send_receive_one(sda, false, false, false); - test_send_receive_one(sda, true, false, false); - test_send_receive_one(sda, false, true, false); - test_send_receive_one(sda, true, true, false); - test_send_receive_one(sda, true, false, true); - test_send_receive_one(sda, false, true, true); - test_send_receive_one(sda, true, true, true); - - test_parent_filter(sda); - - return 0; + return EXIT_SUCCESS; } + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c index d09dc31fa3..79729decbc 100644 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ b/src/libsystemd/sd-netlink/netlink-socket.c @@ -17,36 +17,21 @@ static int broadcast_groups_get(sd_netlink *nl) { _cleanup_free_ uint32_t *groups = NULL; - socklen_t len = 0, old_len; + size_t len; int r; assert(nl); assert(nl->fd >= 0); - if (getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len) < 0) { - if (errno != ENOPROTOOPT) - return -errno; - + r = netlink_socket_get_multicast_groups(nl->fd, &len, &groups); + if (r == -ENOPROTOOPT) { nl->broadcast_group_dont_leave = true; return 0; } + if (r < 0) + return r; - if (len == 0) - return 0; - - groups = new0(uint32_t, len); - if (!groups) - return -ENOMEM; - - old_len = len; - - if (getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len) < 0) - return -errno; - - if (old_len != len) - return -EIO; - - for (unsigned i = 0; i < len; i++) + for (size_t i = 0; i < len; i++) for (unsigned j = 0; j < sizeof(uint32_t) * 8; j++) if (groups[i] & (1U << j)) { unsigned group = i * sizeof(uint32_t) * 8 + j + 1; diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c index d7c931d9f9..55b6eb00f6 100644 --- a/src/libudev/libudev-monitor.c +++ b/src/libudev/libudev-monitor.c @@ -109,14 +109,12 @@ _public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor) { * udev_monitor_enable_receiving: * @udev_monitor: the monitor which should receive events * - * Binds the @udev_monitor socket to the event source. + * Deprecated, and alias of udev_monitor_filter_update(). * * Returns: 0 on success, otherwise a negative error value. */ _public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) { - assert_return(udev_monitor, -EINVAL); - - return device_monitor_enable_receiving(udev_monitor->monitor); + return udev_monitor_filter_update(udev_monitor); } /** @@ -188,7 +186,7 @@ _public_ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) { _public_ int udev_monitor_get_fd(struct udev_monitor *udev_monitor) { assert_return(udev_monitor, -EINVAL); - return device_monitor_get_fd(udev_monitor->monitor); + return sd_device_monitor_get_fd(udev_monitor->monitor); } static int udev_monitor_receive_sd_device(struct udev_monitor *udev_monitor, sd_device **ret) { @@ -199,13 +197,13 @@ static int udev_monitor_receive_sd_device(struct udev_monitor *udev_monitor, sd_ for (;;) { /* r == 0 means a device is received but it does not pass the current filter. */ - r = device_monitor_receive_device(udev_monitor->monitor, ret); + r = sd_device_monitor_receive(udev_monitor->monitor, ret); if (r != 0) return r; for (;;) { /* Wait for next message */ - r = fd_wait_for_event(device_monitor_get_fd(udev_monitor->monitor), POLLIN, 0); + r = fd_wait_for_event(sd_device_monitor_get_fd(udev_monitor->monitor), POLLIN, 0); if (r == -EINTR) continue; if (r < 0) diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h index 4a14c39457..c84cc55202 100644 --- a/src/systemd/sd-device.h +++ b/src/systemd/sd-device.h @@ -142,6 +142,7 @@ int sd_device_monitor_new(sd_device_monitor **ret); sd_device_monitor *sd_device_monitor_ref(sd_device_monitor *m); sd_device_monitor *sd_device_monitor_unref(sd_device_monitor *m); +int sd_device_monitor_get_fd(sd_device_monitor *m); int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size); int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *event); int sd_device_monitor_detach_event(sd_device_monitor *m); @@ -149,8 +150,10 @@ sd_event *sd_device_monitor_get_event(sd_device_monitor *m); sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m); int sd_device_monitor_set_description(sd_device_monitor *m, const char *description); int sd_device_monitor_get_description(sd_device_monitor *m, const char **ret); +int sd_device_monitor_is_running(sd_device_monitor *m); int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata); int sd_device_monitor_stop(sd_device_monitor *m); +int sd_device_monitor_receive(sd_device_monitor *m, sd_device **ret); int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype); int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag); diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index bb94478775..cc8f7fdf8f 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -84,7 +84,7 @@ typedef struct Worker { Manager *manager; pid_t pid; sd_event_source *child_event_source; - sd_device_monitor *monitor; + union sockaddr_union address; WorkerState state; Event *event; } Worker; @@ -125,7 +125,6 @@ static Worker *worker_free(Worker *worker) { hashmap_remove(worker->manager->workers, PID_TO_PTR(worker->pid)); sd_event_source_unref(worker->child_event_source); - sd_device_monitor_unref(worker->monitor); event_free(worker->event); return mfree(worker); @@ -173,18 +172,18 @@ static int worker_new(Worker **ret, Manager *manager, sd_device_monitor *worker_ assert(worker_monitor); assert(pid > 1); - /* close monitor, but keep address around */ - device_monitor_disconnect(worker_monitor); - worker = new(Worker, 1); if (!worker) return -ENOMEM; *worker = (Worker) { - .monitor = sd_device_monitor_ref(worker_monitor), .pid = pid, }; + r = device_monitor_get_address(worker_monitor, &worker->address); + if (r < 0) + return r; + r = sd_event_add_child(manager->event, &worker->child_event_source, pid, WEXITED, on_sigchld, worker); if (r < 0) return r; @@ -398,10 +397,6 @@ static int worker_spawn(Manager *manager, Event *event) { if (r < 0) return log_error_errno(r, "Worker: Failed to set unicast sender: %m"); - r = device_monitor_enable_receiving(worker_monitor); - if (r < 0) - return log_error_errno(r, "Worker: Failed to enable receiving of device: %m"); - r = safe_fork("(udev-worker)", FORK_DEATHSIG_SIGTERM, &pid); if (r < 0) { event->state = EVENT_QUEUED; @@ -455,7 +450,7 @@ static int event_run(Event *event) { if (worker->state != WORKER_IDLE) continue; - r = device_monitor_send_device(manager->monitor, worker->monitor, event->dev); + r = device_monitor_send(manager->monitor, &worker->address, event->dev); if (r < 0) { log_device_error_errno(event->dev, r, "Worker ["PID_FMT"] did not accept message, killing the worker: %m", worker->pid); @@ -1262,10 +1257,6 @@ int manager_init(Manager *manager, int fd_ctrl, int fd_uevent) { (void) sd_device_monitor_set_description(manager->monitor, "manager"); - r = device_monitor_enable_receiving(manager->monitor); - if (r < 0) - return log_error_errno(r, "Failed to bind netlink socket: %m"); - manager->log_level = log_get_max_level(); r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); diff --git a/src/udev/udev-worker.c b/src/udev/udev-worker.c index 97c5679b74..5577f5fab8 100644 --- a/src/udev/udev-worker.c +++ b/src/udev/udev-worker.c @@ -272,7 +272,7 @@ void udev_broadcast_result(sd_device_monitor *monitor, sd_device *dev, EventResu } } - r = device_monitor_send_device(monitor, NULL, dev); + r = device_monitor_send(monitor, NULL, dev); if (r < 0) log_device_warning_errno(dev, r, "Failed to broadcast event to libudev listeners, ignoring: %m");