From 4a3bf440f26c0577808c16da84a57f731c48eaeb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 Apr 2025 12:18:35 +0200 Subject: [PATCH] socket-util: add af_unix_get_qlen() helper to determine number of queued connections on AF_UNIX listener socket --- src/shared/socket-netlink.c | 65 ++++++++++++++++++++++++++++++++++ src/shared/socket-netlink.h | 2 ++ src/test/test-socket-netlink.c | 31 ++++++++++++++++ 3 files changed, 98 insertions(+) diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c index 0054641c22c..1ecb2284fe3 100644 --- a/src/shared/socket-netlink.c +++ b/src/shared/socket-netlink.c @@ -3,8 +3,12 @@ #include #include #include +#include #include #include +#include + +#include "sd-netlink.h" #include "alloc-util.h" #include "errno-util.h" @@ -13,6 +17,7 @@ #include "log.h" #include "memory-util.h" #include "namespace-util.h" +#include "netlink-sock-diag.h" #include "netlink-util.h" #include "parse-util.h" #include "socket-netlink.h" @@ -479,3 +484,63 @@ int netns_get_nsid(int netnsfd, uint32_t *ret) { return -ENXIO; } + +int af_unix_get_qlen(int fd, uint32_t *ret) { + int r; + + assert(fd >= 0); + assert(ret); + + /* Returns the current queue length for an AF_UNIX listening socket */ + + struct stat st; + if (fstat(fd, &st) < 0) + return -errno; + if (!S_ISSOCK(st.st_mode)) + return -ENOTSOCK; + + _cleanup_(sd_netlink_unrefp) sd_netlink *nl = NULL; + r = sd_sock_diag_socket_open(&nl); + if (r < 0) + return r; + + uint64_t cookie = 0; + socklen_t cookie_len = sizeof(cookie); + if (getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len) < 0) + return -errno; + + assert(cookie_len == sizeof(cookie)); + + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; + r = sd_sock_diag_message_new_unix(nl, &message, st.st_ino, cookie, UDIAG_SHOW_RQLEN); + if (r < 0) + return r; + + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *reply = NULL; + r = sd_netlink_call(nl, message, /* usec= */ 0, &reply); + if (r < 0) + return r; + + for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) { + r = sd_netlink_message_get_errno(m); + if (r < 0) + return r; + + _cleanup_free_ void *data = NULL; + size_t size = 0; + + r = sd_netlink_message_read_data(m, UNIX_DIAG_RQLEN, &size, &data); + if (r == -ENODATA) + continue; + if (r < 0) + return r; + + assert(size == sizeof(struct unix_diag_rqlen)); + const struct unix_diag_rqlen *udrql = ASSERT_PTR(data); + + *ret = udrql->udiag_rqueue; + return 0; + } + + return -ENODATA; +} diff --git a/src/shared/socket-netlink.h b/src/shared/socket-netlink.h index 12647b3d14c..88b9c7688bf 100644 --- a/src/shared/socket-netlink.h +++ b/src/shared/socket-netlink.h @@ -45,3 +45,5 @@ int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret); const char* in_addr_full_to_string(struct in_addr_full *a); int netns_get_nsid(int netnsfd, uint32_t *ret); + +int af_unix_get_qlen(int fd, uint32_t *ret); diff --git a/src/test/test-socket-netlink.c b/src/test/test-socket-netlink.c index 659e583fa32..45144a16ea2 100644 --- a/src/test/test-socket-netlink.c +++ b/src/test/test-socket-netlink.c @@ -1,6 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + #include "alloc-util.h" +#include "fd-util.h" #include "missing_network.h" #include "socket-netlink.h" #include "string-util.h" @@ -381,4 +384,32 @@ TEST(netns_get_nsid) { log_info("Our NSID is %" PRIu32, u); } +TEST(af_unix_get_qlen) { + _cleanup_close_ int unix_fd = ASSERT_FD(socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0)); + ASSERT_OK(socket_autobind(unix_fd, /* ret_name= */ NULL)); + ASSERT_OK_ERRNO(listen(unix_fd, 123)); + + uint32_t q; + ASSERT_OK(af_unix_get_qlen(unix_fd, &q)); + ASSERT_EQ(q, 0U); + + _cleanup_close_ int conn_fd = ASSERT_FD(socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)); + union sockaddr_union sa; + socklen_t salen = sizeof(sa); + ASSERT_OK_ERRNO(getsockname(unix_fd, &sa.sa, &salen)); + ASSERT_OK(connect(conn_fd, &sa.sa, salen)); + + ASSERT_OK(af_unix_get_qlen(unix_fd, &q)); + ASSERT_EQ(q, 1U); + + _cleanup_close_ int conn2_fd = ASSERT_FD(socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)); + ASSERT_OK(connect(conn2_fd, &sa.sa, salen)); + + ASSERT_OK(af_unix_get_qlen(unix_fd, &q)); + ASSERT_EQ(q, 2U); + + _cleanup_close_ int efd = ASSERT_FD(eventfd(0, EFD_CLOEXEC)); + ASSERT_ERROR(af_unix_get_qlen(efd, &q), ENOTSOCK); +} + DEFINE_TEST_MAIN(LOG_DEBUG); -- 2.47.3