The Pedigree Project 0.1
mqueue-netlink.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "mqueue-netlink.h"
3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/process/TerminationDeferral.h"
5#include "pedigree/kernel/syscallError.h"
6#include "pedigree/kernel/utilities/assert.h"
7#include "pedigree/kernel/utilities/utility.h"
8
9#include <errno.h>
10
11namespace {
12int unsupported() {
13 SYSCALL_ERROR(OperationNotSupported);
14 return -1;
15}
16} // namespace
17
18MqueueNetlinkSocket::MqueueNetlinkSocket(int type, int protocol)
19 : NetworkSyscalls(16, type, protocol),
20 m_Lock(),
21 m_Changed(),
22 m_Cookies(),
23 m_Head(0),
24 m_Count(0),
25 m_Reserved(0),
26 m_Closed(false),
27 m_Generations() {}
28
29MqueueNetlinkSocket::~MqueueNetlinkSocket() {
31}
32
34 if ((getType() != SOCK_RAW && getType() != SOCK_DGRAM) || getProtocol()) {
35 SYSCALL_ERROR(ProtocolNotAvailable);
36 return false;
37 }
38 return true;
39}
40
41bool MqueueNetlinkSocket::reserveCookie() {
42 LockGuard<Mutex> guard(m_Lock);
43 if (m_Closed) {
44 SYSCALL_ERROR(BadFileDescriptor);
45 return false;
46 }
47 if (m_Count + m_Reserved == 64) {
48 SYSCALL_ERROR(NoMoreBuffers);
49 return false;
50 }
51 ++m_Reserved;
52 return true;
53}
54
55void MqueueNetlinkSocket::deliverCookie(const uint8_t cookie[32], bool removed) {
56 {
57 LockGuard<Mutex> guard(m_Lock);
58 assert(m_Reserved);
59 --m_Reserved;
60 if (m_Closed) {
61 return;
62 }
63 uint8_t* target = m_Cookies[(m_Head + m_Count) % 64];
64 MemoryCopy(target, cookie, 32);
65 target[31] = removed ? 2 : 1;
66 if (!m_Count++) {
67 ++m_Generations.read;
68 }
69 }
70 m_Changed.broadcast();
71 notifyReadiness(ReadyRead);
72}
73
75 if (!beginDescriptorClose()) {
76 return;
77 }
78 {
79 LockGuard<Mutex> guard(m_Lock);
80 m_Closed = true;
81 }
82 m_Changed.broadcast();
83 // beginDescriptorClose() makes the base destructor skip its close path.
84 // Every derived close must therefore drain the inherited barrier itself.
87}
88
89bool MqueueNetlinkSocket::canPoll() const {
90 return true;
91}
92
93ReadyMask MqueueNetlinkSocket::queryReady(bool reading, bool writing) {
94 (void)writing;
95 LockGuard<Mutex> guard(m_Lock);
96 const ReadyMask ready = m_Closed ? ReadyInvalid | ReadyHangup
97 : reading && m_Count ? ReadyRead
98 : ReadyNone;
99 return ready | pendingReceiveReadiness();
100}
101
103 LockGuard<Mutex> guard(m_Lock);
104 return withReceiveErrorGeneration(m_Generations);
105}
106
107ssize_t MqueueNetlinkSocket::recvfrom_msg(struct msghdr* message,
109 TerminationDeferral lifetime;
110 if (rights) {
111 rights->reset();
112 }
113 const int inputFlags = message->msg_flags;
114 const int allowedFlags = MSG_DONTWAIT | MSG_NOSIGNAL | MSG_WAITALL | MSG_PEEK | MSG_TRUNC;
115 if (inputFlags & ~allowedFlags) {
116 return unsupported();
117 }
118 size_t capacity = 0;
119 for (size_t n = 0; n < static_cast<size_t>(message->msg_iovlen); ++n) {
120 capacity += message->msg_iov[n].iov_len;
121 }
122 m_Lock.acquire();
123 while (!m_Count) {
124 if (m_Closed) {
125 m_Lock.release();
126 SYSCALL_ERROR(BadFileDescriptor);
127 return -1;
128 }
129 if (!isBlocking() || (inputFlags & MSG_DONTWAIT)) {
130 m_Lock.release();
131 SYSCALL_ERROR(NoMoreProcesses);
132 return -1;
133 }
134 ConditionVariable::Error error = ConditionVariable::NoError;
135 if (!m_Changed.wait(m_Lock, error)) {
137 m_Lock.release();
138 }
139 SYSCALL_ERROR(Interrupted);
140 return -1;
141 }
142 }
143 const size_t length = capacity < 32 ? capacity : 32;
144 size_t copied = 0;
145 for (size_t n = 0; n < static_cast<size_t>(message->msg_iovlen) && copied < length; ++n) {
146 const auto& vector = message->msg_iov[n];
147 const size_t amount = vector.iov_len < length - copied ? vector.iov_len : length - copied;
148 MemoryCopy(vector.iov_base, m_Cookies[m_Head] + copied, amount);
149 copied += amount;
150 }
151 if (!(inputFlags & MSG_PEEK)) {
152 m_Head = (m_Head + 1) % 64;
153 --m_Count;
154 }
155 m_Lock.release();
156 message->msg_flags = length < 32 ? MSG_TRUNC : 0;
157 message->msg_controllen = 0;
158 message->msg_namelen = 0;
159 return inputFlags & MSG_TRUNC ? 32 : static_cast<ssize_t>(length);
160}
161
162int MqueueNetlinkSocket::connect(const struct sockaddr_storage*, socklen_t) {
163 return unsupported();
164}
165ssize_t MqueueNetlinkSocket::sendto_msg(const struct msghdr*, const SharedPointer<SocketRights>&) {
166 return unsupported();
167}
168int MqueueNetlinkSocket::listen(int) {
169 return unsupported();
170}
171int MqueueNetlinkSocket::bind(const struct sockaddr_storage*, socklen_t) {
172 return unsupported();
173}
174int MqueueNetlinkSocket::accept(struct sockaddr_storage*, socklen_t*, int, DescriptorLease*) {
175 return unsupported();
176}
177int MqueueNetlinkSocket::shutdown(int) {
178 return unsupported();
179}
180int MqueueNetlinkSocket::getpeername(struct sockaddr_storage*, socklen_t*) {
181 SYSCALL_ERROR(NotConnected);
182 return -1;
183}
184int MqueueNetlinkSocket::getsockname(struct sockaddr_storage* address, socklen_t* length) {
185 struct NetlinkAddress {
186 uint16_t family, padding;
187 uint32_t pid, groups;
188 } result = {16, 0, 0, 0};
189 const size_t amount = *length < sizeof(result) ? *length : sizeof(result);
190 MemoryCopy(address, &result, amount);
191 *length = sizeof(result);
192 return 0;
193}
194int MqueueNetlinkSocket::setsockopt(int, int, const void*, socklen_t) {
195 SYSCALL_ERROR(ProtocolNotAvailable);
196 return -1;
197}
198int MqueueNetlinkSocket::getsockopt(int level, int option, void* value, socklen_t* length) {
199 if (level == SOL_SOCKET && (option == SO_TYPE || option == SO_ERROR) && *length >= sizeof(int)) {
200 const int result = option == SO_TYPE ? getType() : 0;
201 MemoryCopy(value, &result, sizeof(result));
202 *length = sizeof(result);
203 return 0;
204 }
205 SYSCALL_ERROR(ProtocolNotAvailable);
206 return -1;
207}
MUST_USE_RESULT bool wait(Mutex &mutex, Time::Timestamp &timeout, Error &error, WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
static bool mutexAcquired(Error error)
void lastDescriptorClosed() override
ReadinessGenerations readinessGenerations() override
bool create() override
ReadyMask queryReady(bool reading, bool writing) override
bool beginDescriptorClose()
OperationBarrier m_ReadinessNotifications
void notifyReadiness(ReadyMask mask)
Definition Readiness.cc:201
void closeReadiness(ReadyMask mask=ReadyInvalid|ReadyHangup)
Definition Readiness.cc:208
void release(size_t n=1)
Definition Semaphore.cc:546
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:352