socket-util: add af_unix_get_qlen() helper to determine number of queued connections on AF_UNIX listener socket

This commit is contained in:
Lennart Poettering
2025-05-13 14:42:34 +02:00
parent 89e546e927
commit 4a3bf440f2
3 changed files with 98 additions and 0 deletions
+65
View 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;
}
+2
View 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);
+31
View 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);