2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/ConditionVariable.h"
4#include "pedigree/kernel/process/Mutex.h"
5#include "pedigree/kernel/process/TerminationDeferral.h"
6#include "pedigree/kernel/syscallError.h"
7#include "pedigree/kernel/time/Time.h"
8#include "pedigree/kernel/utilities/List.h"
9#include "pedigree/kernel/utilities/SharedPointer.h"
10#include "pedigree/kernel/utilities/utility.h"
12#include "PosixSubsystem.h"
13#include "ipc-common.h"
14#include "sysv-message-syscalls.h"
17constexpr size_t MaximumQueues = 128;
18constexpr size_t MaximumMessage = 8192;
19constexpr size_t DefaultQueueBytes = 16384;
20constexpr size_t MaximumQueueBytes = 65536;
21constexpr int Create = 01000, Exclusive = 02000, Nowait = 04000;
22constexpr int Noerror = 010000, Except = 020000, Copy = 040000;
23constexpr int Remove = 0, Set = 1, Stat = 2, Info = 3;
24constexpr int MessageStat = 11, MessageInfo = 12, MessageStatAny = 13;
28 int64_t sendTime, receiveTime, changeTime;
29 uint64_t bytes, count, capacity;
30 int32_t sender, receiver;
33static_assert(
sizeof(QueueStatus) == 120,
"Linux amd64 msqid_ds layout");
34static_assert(__builtin_offsetof(QueueStatus, capacity) == 88,
"Linux msg_qbytes offset");
37 int32_t pool, map, maximum, defaultBytes, queues, segmentSize, messages;
38 uint16_t segments, padding;
40static_assert(
sizeof(QueueInfo) == 32,
"Linux msginfo layout");
43 explicit Message(
size_t length) : size(length), data(new uint8_t[length + sizeof(int64_t)]) {}
47 int64_t type()
const {
49 MemoryCopy(&value, data,
sizeof(value));
57 uint64_t namespaceId = posix_ipc_namespace_id();
58 Queue(
int identifier,
int key,
int mode) : id(identifier), removed(false), status{} {
59 PosixIpc::initialize(status.permission, key, mode, identifier / MaximumQueues);
60 status.capacity = DefaultQueueBytes;
61 status.changeTime = Time::getTime();
74uint32_t nextIdentifier = 0;
80 if (
static_cast<size_t>(
id) < MaximumQueues && queues[
id] &&
81 queues[
id]->namespaceId == posix_ipc_namespace_id()) {
85 for (
size_t i = 0; i < MaximumQueues; ++i) {
86 if (queues[i] && queues[i]->namespaceId == posix_ipc_namespace_id() &&
87 queues[i]->
id ==
id) {
93 SYSCALL_ERROR(InvalidArgument);
97bool usable(Queue& queue,
unsigned access) {
99 SYSCALL_ERROR(IdentifierRemoved);
102 if (!PosixIpc::allowed(queue.status.permission, access)) {
103 SYSCALL_ERROR(PermissionDenied);
110 ConditionVariable::Error error = ConditionVariable::NoError;
111 if (queue.changed.wait(queue.lock, error)) {
118 SYSCALL_ERROR(IdentifierRemoved);
120 SYSCALL_ERROR(Interrupted);
125int queueInfo(
int command,
void* buffer) {
126 QueueInfo result = {};
127 result.maximum = MaximumMessage;
128 result.defaultBytes = DefaultQueueBytes;
129 result.queues = MaximumQueues;
130 result.segmentSize = 16;
131 result.segments = 65535;
134 for (
size_t i = 0; i < MaximumQueues; ++i) {
135 if (!queues[i] || queues[i]->namespaceId != posix_ipc_namespace_id()) {
139 if (command == MessageInfo) {
142 result.map += queues[i]->status.count;
143 result.messages += queues[i]->status.bytes;
146 if (command == Info) {
147 result.pool = MaximumQueues * DefaultQueueBytes / 1024;
148 result.map = MaximumQueues;
149 result.messages = MaximumQueues * MaximumQueueBytes;
152 SYSCALL_ERROR(BadAddress);
159int posix_msgget(int32_t key,
int flags) {
161 if (flags & ~(0777 | Create | Exclusive)) {
162 SYSCALL_ERROR(InvalidArgument);
166 size_t freeSlot = MaximumQueues;
167 for (
size_t i = 0; i < MaximumQueues; ++i) {
169 if (freeSlot == MaximumQueues) {
172 }
else if (key && queues[i]->namespaceId == posix_ipc_namespace_id() &&
173 queues[i]->status.permission.key == key) {
174 if ((flags & (Create | Exclusive)) == (Create | Exclusive)) {
175 SYSCALL_ERROR(FileExists);
179 const unsigned access = ((flags >> 6) | (flags >> 3) | flags) & 7;
180 return usable(*queues[i], access) ? queues[i]->id : -1;
183 if (key && !(flags & Create)) {
184 SYSCALL_ERROR(DoesNotExist);
187 if (freeSlot == MaximumQueues || nextIdentifier > 0x7fffffffU) {
188 SYSCALL_ERROR(NoSpaceLeftOnDevice);
191 const int id = nextIdentifier++;
192 queues[freeSlot].
reset(
new Queue(
id, key, flags));
196int posix_msgsnd(
int id,
const void*
message,
size_t size,
int flags) {
198 if (size > MaximumMessage || (flags & ~Nowait)) {
199 SYSCALL_ERROR(InvalidArgument);
204 SYSCALL_ERROR(BadAddress);
207 if (snapshot->type() <= 0) {
208 SYSCALL_ERROR(InvalidArgument);
216 while (usable(*queue, 2)) {
218 if (queue->status.bytes <= queue->status.capacity &&
219 size <= queue->status.capacity - queue->status.bytes &&
220 queue->status.count < queue->status.capacity) {
221 queue->messages.pushBack(snapshot);
222 queue->status.bytes += size;
223 ++queue->status.count;
225 queue->status.sendTime = Time::getTime();
226 queue->changed.broadcast();
229 if (flags & Nowait) {
230 SYSCALL_ERROR(NoMoreProcesses);
233 if (!wait(*queue, guard)) {
240ssize_t posix_msgrcv(
int id,
void*
message,
size_t size, int64_t type,
int flags) {
242 if (size >
static_cast<size_t>(0x7fffffffffffffffULL) ||
243 (flags & ~(Nowait | Noerror | Except | Copy)) ||
244 ((flags & Copy) && (!(flags & Nowait) || (flags & Except)))) {
245 SYSCALL_ERROR(InvalidArgument);
253 while (usable(*queue, 4)) {
254 auto selected = queue->messages.end();
255 uint64_t position = 0;
257 const uint64_t ceiling = 0 -
static_cast<uint64_t
>(type);
258 for (
auto it = queue->messages.begin(); it != queue->messages.end(); ++it, ++position) {
259 const int64_t candidate = (*it)->type();
261 if (position ==
static_cast<uint64_t
>(type)) {
266 (type > 0 && ((flags & Except) ? candidate != type : candidate == type))) {
269 }
else if (type < 0 &&
static_cast<uint64_t
>(candidate) <= ceiling &&
270 (selected == queue->messages.end() || candidate < (*selected)->type())) {
274 if (selected != queue->messages.end()) {
275 Message& record = **selected;
276 if (record.size > size && !(flags & Noerror)) {
277 SYSCALL_ERROR(TooBig);
280 if ((flags & Copy) && record.size > size) {
281 SYSCALL_ERROR(InvalidArgument);
284 const size_t copied = size < record.size ? size : record.size;
288 SYSCALL_ERROR(BadAddress);
291 if (!(flags & Copy)) {
292 queue->status.bytes -= record.size;
293 --queue->status.count;
295 queue->status.receiveTime = Time::getTime();
296 queue->messages.erase(selected);
297 queue->changed.broadcast();
301 if (flags & Nowait) {
302 SYSCALL_ERROR(NoMessage);
305 if (!wait(*queue, guard)) {
312int posix_msgctl(
int id,
int command,
void* buffer) {
314 if (command == Info || command == MessageInfo) {
315 return queueInfo(command, buffer);
317 if (command != Remove && command != Set && command != Stat && command != MessageStat &&
318 command != MessageStatAny) {
319 SYSCALL_ERROR(InvalidArgument);
322 QueueStatus input = {};
324 SYSCALL_ERROR(BadAddress);
327 SharedPointer<Queue> queue = findQueue(
id, command == MessageStat || command == MessageStatAny);
334 const unsigned access = command == Stat || command == MessageStat ? 4 : 0;
335 if (!usable(*queue, access)) {
338 if (command == Stat || command == MessageStat || command == MessageStatAny) {
340 SYSCALL_ERROR(BadAddress);
343 return command == Stat ? 0 : queue->id;
345 if (!PosixIpc::owner(queue->status.permission)) {
346 SYSCALL_ERROR(NotEnoughPermissions);
349 if (command == Remove) {
350 queue->removed =
true;
351 for (
size_t i = 0; i < MaximumQueues; ++i) {
352 if (queues[i] && queues[i]->namespaceId == posix_ipc_namespace_id() && queues[i]->
id ==
id) {
357 queue->messages.clear();
359 if (input.permission.uid == 0xffffffffU || input.permission.gid == 0xffffffffU) {
360 SYSCALL_ERROR(InvalidArgument);
363 if (input.capacity > DefaultQueueBytes &&
364 !posix_namespace_capable(posix_ipc_owner(), PosixCapabilities::SysResource)) {
365 SYSCALL_ERROR(NotEnoughPermissions);
368 if (input.capacity > MaximumQueueBytes) {
369 SYSCALL_ERROR(InvalidArgument);
372 queue->status.permission.uid = input.permission.uid;
373 queue->status.permission.gid = input.permission.gid;
374 queue->status.permission.mode = input.permission.mode & 0777;
375 queue->status.capacity = input.capacity;
376 queue->status.changeTime = Time::getTime();
378 queue->changed.broadcast();
382void posix_msg_namespace_exit(uint64_t
identity) {
384 for (
auto& entry : queues) {
385 if (entry && entry->namespaceId ==
identity) {
388 queue->removed =
true;
389 queue->messages.clear();
390 queue->changed.broadcast();
static bool mutexAcquired(Error error)
static bool copyFromUser(void *destination, const void *source, size_t count, size_t elementSize=1)
static bool copyToUser(void *destination, const void *source, size_t count, size_t elementSize=1)
size_t getUserspaceId() const