Resolves: RHEL-100553,RHEL-103354,RHEL-104555,RHEL-106260,RHEL-44419,RHEL-72701,RHEL-79976,RHEL-97625,RHEL-97762
120 lines
3.8 KiB
Diff
120 lines
3.8 KiB
Diff
From b2cbc7591414472269260470798e3d0feccdb1f9 Mon Sep 17 00:00:00 2001
|
|
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
|
|
Date: Tue, 6 May 2025 16:39:14 -0600
|
|
Subject: [PATCH] flush_ports: flush POSIX message queues properly
|
|
|
|
On Linux, read() on a message queue descriptor returns the message
|
|
queue statistics, not the actual message queue data. We need to use
|
|
mq_receive() to drain the queues instead.
|
|
|
|
Fixes a problem where a POSIX message queue socket unit with messages
|
|
in the queue at shutdown time could result in a hang on reboot/shutdown.
|
|
|
|
(cherry picked from commit ffb6adb76367d5ab7d43937ccaac5947717b5b78)
|
|
|
|
Resolves: RHEL-100553
|
|
---
|
|
src/basic/socket-util.c | 49 +++++++++++++++++++++++++++++++++++++++++
|
|
src/basic/socket-util.h | 1 +
|
|
src/core/socket.c | 8 +++++--
|
|
3 files changed, 56 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
|
|
index 94089323b4..5c8c788846 100644
|
|
--- a/src/basic/socket-util.c
|
|
+++ b/src/basic/socket-util.c
|
|
@@ -5,6 +5,7 @@
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
+#include <mqueue.h>
|
|
#include <netdb.h>
|
|
#include <netinet/ip.h>
|
|
#include <poll.h>
|
|
@@ -1296,6 +1297,54 @@ int flush_accept(int fd) {
|
|
}
|
|
}
|
|
|
|
+ssize_t flush_mqueue(int fd) {
|
|
+ _cleanup_free_ char *buf = NULL;
|
|
+ struct mq_attr attr;
|
|
+ ssize_t count = 0;
|
|
+ int r;
|
|
+
|
|
+ assert(fd >= 0);
|
|
+
|
|
+ /* Similar to flush_fd() but flushes all messages from a POSIX message queue. */
|
|
+
|
|
+ for (;;) {
|
|
+ ssize_t l;
|
|
+
|
|
+ r = fd_wait_for_event(fd, POLLIN, /* timeout= */ 0);
|
|
+ if (r < 0) {
|
|
+ if (r == -EINTR)
|
|
+ continue;
|
|
+
|
|
+ return r;
|
|
+ }
|
|
+ if (r == 0)
|
|
+ return count;
|
|
+
|
|
+ if (!buf) {
|
|
+ /* Buffer must be at least as large as mq_msgsize. */
|
|
+ if (mq_getattr(fd, &attr) < 0)
|
|
+ return -errno;
|
|
+
|
|
+ buf = malloc(attr.mq_msgsize);
|
|
+ if (!buf)
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ l = mq_receive(fd, buf, attr.mq_msgsize, /* msg_prio = */ NULL);
|
|
+ if (l < 0) {
|
|
+ if (errno == EINTR)
|
|
+ continue;
|
|
+
|
|
+ if (errno == EAGAIN)
|
|
+ return count;
|
|
+
|
|
+ return -errno;
|
|
+ }
|
|
+
|
|
+ count += l;
|
|
+ }
|
|
+}
|
|
+
|
|
struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) {
|
|
struct cmsghdr *cmsg;
|
|
|
|
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
|
|
index 874e559a4b..013edf0d4f 100644
|
|
--- a/src/basic/socket-util.h
|
|
+++ b/src/basic/socket-util.h
|
|
@@ -197,6 +197,7 @@ int receive_many_fds(int transport_fd, int **ret_fds_array, size_t *ret_n_fds_ar
|
|
ssize_t next_datagram_size_fd(int fd);
|
|
|
|
int flush_accept(int fd);
|
|
+ssize_t flush_mqueue(int fd);
|
|
|
|
#define CMSG_FOREACH(cmsg, mh) \
|
|
for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
|
|
diff --git a/src/core/socket.c b/src/core/socket.c
|
|
index 6599527515..1e224b2407 100644
|
|
--- a/src/core/socket.c
|
|
+++ b/src/core/socket.c
|
|
@@ -2295,8 +2295,12 @@ static void flush_ports(Socket *s) {
|
|
if (p->fd < 0)
|
|
continue;
|
|
|
|
- (void) flush_accept(p->fd);
|
|
- (void) flush_fd(p->fd);
|
|
+ if (p->type == SOCKET_MQUEUE)
|
|
+ (void) flush_mqueue(p->fd);
|
|
+ else {
|
|
+ (void) flush_accept(p->fd);
|
|
+ (void) flush_fd(p->fd);
|
|
+ }
|
|
}
|
|
}
|
|
|