]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
socket-util: add af_unix_get_qlen() helper to determine number of queued connections...
authorLennart Poettering <lennart@poettering.net>
Tue, 8 Apr 2025 10:18:35 +0000 (12:18 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 13 May 2025 12:42:34 +0000 (14:42 +0200)
src/shared/socket-netlink.c
src/shared/socket-netlink.h
src/test/test-socket-netlink.c

index 0054641c22c96721cee7ac95fbd87e40d5c30d24..1ecb2284fe3d52f56f0c77c3a98322310c73abbe 100644 (file)
@@ -3,8 +3,12 @@
 #include <arpa/inet.h>
 #include <errno.h>
 #include <linux/net_namespace.h>
+#include <linux/unix_diag.h>
 #include <net/if.h>
 #include <string.h>
+#include <sys/stat.h>
+
+#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;
+}
index 12647b3d14c6dd769da30af5471f6a2b2f960f5a..88b9c7688bfa4b2c98eeef7ad64d3b91c9d2344e 100644 (file)
@@ -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);
index 659e583fa3211a1fe3d01e48806dbf061fc77887..45144a16ea2dfd58fbe01b44af352f438bdd9966 100644 (file)
@@ -1,6 +1,9 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <sys/eventfd.h>
+
 #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);