summaryrefslogtreecommitdiff
path: root/components/lwp/syscall/syscall_mqueue.c
diff options
context:
space:
mode:
Diffstat (limited to 'components/lwp/syscall/syscall_mqueue.c')
-rw-r--r--components/lwp/syscall/syscall_mqueue.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/components/lwp/syscall/syscall_mqueue.c b/components/lwp/syscall/syscall_mqueue.c
new file mode 100644
index 0000000000..f6d4dde29b
--- /dev/null
+++ b/components/lwp/syscall/syscall_mqueue.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2006-2025 RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "syscall_internal.h"
+
+/**
+ * @brief Open a message queue.
+ *
+ * This function opens a message queue for communication between processes.
+ * It can create a new message queue or open an existing one, depending on the specified
+ * flags.
+ *
+ * @param[in] name The name of the message queue. The name should be a null-terminated
+ * string and is subject to system-specific naming conventions.
+ * @param[in] flags Flags that control the behavior of the message queue. Common flags include:
+ * - `O_CREAT`: Create the message queue if it does not exist.
+ * - `O_EXCL`: Fail if the message queue already exists.
+ * - `O_RDONLY`: Open the queue for reading.
+ * - `O_WRONLY`: Open the queue for writing.
+ * - `O_RDWR`: Open the queue for both reading and writing.
+ * @param[in] mode The mode to be applied when creating the message queue, which defines
+ * the permissions for the message queue (e.g., read, write).
+ * @param[in] attr A pointer to a `struct mq_attr` that defines the attributes of the
+ * message queue, such as the maximum number of messages and the size of
+ * each message. If `NULL`, default values are used.
+ *
+ * @return mqd_t Returns a non-negative file descriptor for the message queue on success.
+ * On failure, returns `-1` and sets `errno` to indicate the error.
+ *
+ * @note If the message queue is created, its attributes (such as the maximum number of
+ * messages and message size) must be defined in the `mq_attr` structure. If the
+ * `O_CREAT` flag is not specified and the queue does not exist, the function will
+ * return `-1`.
+ */
+mqd_t sys_mq_open(const char *name, int flags, mode_t mode, struct mq_attr *attr)
+{
+ mqd_t mqdes;
+ sysret_t ret = 0;
+#ifdef ARCH_MM_MMU
+ char *kname = RT_NULL;
+ rt_size_t len = 0;
+ struct mq_attr attr_k;
+
+ len = lwp_user_strlen(name);
+ if (!len)
+ {
+ return (mqd_t)-EINVAL;
+ }
+
+ kname = (char *)kmem_get(len + 1);
+ if (!kname)
+ {
+ return (mqd_t)-ENOMEM;
+ }
+
+ if (attr == NULL)
+ {
+ attr_k.mq_maxmsg = 10;
+ attr_k.mq_msgsize = 8192;
+ attr_k.mq_flags = 0;
+ attr = &attr_k;
+ }
+ else
+ {
+ if (!lwp_get_from_user(&attr_k, (void *)attr, sizeof(struct mq_attr)))
+ {
+ return -EINVAL;
+ }
+ }
+
+ lwp_get_from_user(kname, (void *)name, len + 1);
+ mqdes = mq_open(kname, flags, mode, &attr_k);
+ if (mqdes == -1)
+ {
+ ret = GET_ERRNO();
+ }
+ lwp_put_to_user(attr, &attr_k, sizeof(struct mq_attr));
+ kmem_put(kname);
+#else
+ mqdes = mq_open(name, flags, mode, attr);
+#endif
+ if (mqdes == -1)
+ {
+ return (mqd_t)ret;
+ }
+ else
+ {
+ return mqdes;
+ }
+}
+
+/**
+ * @brief Remove a message queue.
+ *
+ * This function removes a message queue identified by its name. If the message queue
+ * is open by any process, it will be removed only when all the processes close their
+ * file descriptors associated with the message queue.
+ *
+ * @param[in] name The name of the message queue to be removed. It should be a null-terminated
+ * string that conforms to system-specific naming conventions.
+ *
+ * @return sysret_t Returns `0` on success. On failure, returns a negative
+ * error code.
+ *
+ * @note After a successful call, the message queue is removed from the system. However,
+ * the removal will not take effect until all processes close their file descriptors
+ * associated with the queue. The function will fail if the message queue is still
+ * open by other processes.
+ */
+sysret_t sys_mq_unlink(const char *name)
+{
+ int ret = 0;
+#ifdef ARCH_MM_MMU
+ char *kname = RT_NULL;
+ rt_size_t len = 0;
+
+ len = lwp_user_strlen(name);
+ if (!len)
+ {
+ return -EINVAL;
+ }
+ kname = (char *)kmem_get(len + 1);
+ if (!kname)
+ {
+ return -ENOMEM;
+ }
+
+ lwp_get_from_user(kname, (void *)name, len + 1);
+ ret = mq_unlink(kname);
+ if (ret < 0)
+ {
+ ret = GET_ERRNO();
+ }
+ kmem_put(kname);
+ return ret;
+#else
+ ret = mq_unlink(name);
+ return (ret < 0 ? GET_ERRNO() : ret);
+#endif
+}
+
+/**
+ * @brief Send a message to a message queue with a timeout.
+ *
+ * This function sends a message to the specified message queue, but it allows the sender
+ * to specify a timeout. If the message queue is full, the function will block until either
+ * space becomes available or the specified timeout expires. If the timeout expires without
+ * space being available, the function returns an error.
+ *
+ * @param[in] mqd The message queue descriptor returned by `sys_mq_open`.
+ * @param[in] msg A pointer to the message to be sent.
+ * @param[in] len The length of the message to send.
+ * @param[in] prio The priority of the message (higher values indicate higher priority).
+ * @param[in] at A pointer to a `timespec` structure that specifies the absolute timeout.
+ * If the timeout expires before the message can be sent, the function returns
+ * an error.
+ *
+ * @return sysret_t Returns `0` on success. On failure, returns a negative error
+ * code.
+ *
+ * @note The function uses the `timespec` structure to specify the absolute timeout. The
+ * `at` parameter should indicate the time at which the operation should time out.
+ * If the timeout is `NULL`, the function will not apply any timeout (it will block
+ * indefinitely until the message is sent).
+ *
+ * @see sys_mq_send
+ */
+sysret_t sys_mq_timedsend(mqd_t mqd, const char *msg, size_t len, unsigned prio, const struct timespec *at)
+{
+ int ret = 0;
+#ifdef ARCH_MM_MMU
+ char *kmsg = RT_NULL;
+ struct timespec at_k;
+
+ kmsg = (char *)kmem_get(len + 1);
+ if (!kmsg)
+ {
+ return -ENOMEM;
+ }
+
+ lwp_get_from_user(&at_k, (void *)at, sizeof(struct timespec));
+ lwp_get_from_user(kmsg, (void *)msg, len + 1);
+ ret = mq_timedsend(mqd, kmsg, len, prio, &at_k);
+ if (ret < 0)
+ {
+ ret = GET_ERRNO();
+ }
+
+ kmem_put(kmsg);
+
+ return ret;
+#else
+ ret = mq_timedsend(mqd, msg, len, prio, at);
+ return (ret < 0 ? GET_ERRNO() : ret);
+#endif
+}
+
+/**
+ * @brief Receive a message from a message queue with a timeout.
+ *
+ * This function attempts to receive a message from the specified message queue, but it
+ * allows the receiver to specify a timeout. If the queue is empty, the function will block
+ * until either a message becomes available or the specified timeout expires. If the timeout
+ * expires without receiving a message, the function returns an error.
+ *
+ * @param[in] mqd The message queue descriptor returned by `sys_mq_open`.
+ * @param[out] msg A pointer to the buffer where the received message will be stored.
+ * @param[in] len The maximum length of the buffer to store the received message.
+ * @param[out] prio A pointer to an unsigned integer that will be set to the priority
+ * of the received message.
+ * @param[in] at A pointer to a `timespec` structure that specifies the absolute timeout.
+ * If the timeout expires before a message is received, the function will
+ * return an error.
+ *
+ * @return sysret_t Returns `0` on success. On failure, returns a negative error code.
+ *
+ * @note The function uses the `timespec` structure to specify the absolute timeout. The
+ * `at` parameter should indicate the time at which the operation should time out.
+ * If the timeout is `NULL`, the function will block indefinitely until a message is
+ * received.
+ *
+ * @see sys_mq_receive
+ */
+sysret_t sys_mq_timedreceive(mqd_t mqd, char * restrict msg, size_t len, unsigned * restrict prio, const struct timespec * restrict at)
+{
+ int ret = 0;
+#ifdef ARCH_MM_MMU
+ char * restrict kmsg = RT_NULL;
+
+ struct timespec at_k;
+
+ kmsg = (char * restrict)kmem_get(len + 1);
+ if (!kmsg)
+ {
+ return -ENOMEM;
+ }
+
+ lwp_get_from_user(kmsg, (void *)msg, len + 1);
+ if (at == RT_NULL)
+ {
+ ret = mq_timedreceive(mqd, kmsg, len, prio, RT_NULL);
+ }
+ else
+ {
+ if (!lwp_get_from_user(&at_k, (void *)at, sizeof(struct timespec)))
+ {
+ return -EINVAL;
+ }
+ ret = mq_timedreceive(mqd, kmsg, len, prio, &at_k);
+ }
+
+ if (ret > 0)
+ {
+ lwp_put_to_user(msg, kmsg, len + 1);
+ }
+
+ if (ret < 0)
+ {
+ ret = GET_ERRNO();
+ }
+
+ kmem_put(kmsg);
+
+ return ret;
+#else
+ ret = mq_timedreceive(mqd, msg, len, prio, at);
+ return (ret < 0 ? GET_ERRNO() : ret);
+#endif
+}
+
+/**
+ * @brief Set up asynchronous notification for a message queue.
+ *
+ * This function configures asynchronous notification for a message queue. When a message
+ * is available in the queue, the system can notify the calling process through a signal
+ * or another method specified in the `sigevent` structure. This is typically used to allow
+ * a process to be notified when a message arrives without having to block in the receive call.
+ *
+ * @param[in] mqd The message queue descriptor returned by `sys_mq_open`.
+ * @param[in] sev A pointer to a `sigevent` structure that specifies the notification
+ * mechanism to be used when a message is received. It could include
+ * signals or other notification types such as event flags or message passing.
+ *
+ * @return sysret_t Returns `0` on success. On failure, returns a negative error code.
+ *
+ * @note This function enables asynchronous notification, but the specific behavior depends
+ * on the configuration specified in the `sev` parameter, which could involve signals
+ * or other forms of notification.
+ *
+ * @see sys_mq_send, sys_mq_timedreceive
+ */
+sysret_t sys_mq_notify(mqd_t mqd, const struct sigevent *sev)
+{
+ int ret = 0;
+#ifdef ARCH_MM_MMU
+ struct sigevent sev_k;
+ lwp_get_from_user(&sev_k, (void *)sev, sizeof(struct timespec));
+ ret = mq_notify(mqd, &sev_k);
+#else
+ ret = mq_notify(mqd, sev);
+#endif
+ return (ret < 0 ? GET_ERRNO() : ret);
+}
+
+/**
+ * @brief Get or set attributes of a message queue.
+ *
+ * This function allows you to get or set the attributes of an existing message queue.
+ * If the `new` attribute structure is non-NULL, it updates the message queue with the new
+ * attributes. Otherwise, If the `old` attribute structure is non-NULL, it will return the current
+ * attributes of the message queue.
+ *
+ * @param[in] mqd The message queue descriptor returned by `sys_mq_open`.
+ * @param[in] new A pointer to a `mq_attr` structure containing the new attributes to set.
+ * If `NULL`, the function will not modify the message queue attributes.
+ * @param[out] old A pointer to a `mq_attr` structure where the current message queue
+ * attributes will be returned. If `NULL`, the current attributes will not
+ * be returned.
+ *
+ * @return sysret_t Returns `0` on success. On failure, returns a negative error code.
+ *
+ * @note The `mq_attr` structure contains parameters like the maximum number of messages,
+ * the maximum message size, and other attributes that control the behavior of the
+ * message queue.
+ *
+ * @see sys_mq_open, sys_mq_notify
+ */
+sysret_t sys_mq_getsetattr(mqd_t mqd, const struct mq_attr * restrict new, struct mq_attr * restrict old)
+{
+ int ret = 0;
+#ifdef ARCH_MM_MMU
+ size_t size = sizeof(struct mq_attr);
+ struct mq_attr * restrict knew = NULL;
+ struct mq_attr * restrict kold = NULL;
+
+ if (new != RT_NULL)
+ {
+ if (!lwp_user_accessable((void *)new, size))
+ {
+ return -EFAULT;
+ }
+ knew = kmem_get(size);
+ if (!knew)
+ {
+ return -ENOMEM;
+ }
+ lwp_get_from_user(knew, (void *)new, size);
+ }
+
+ if (!lwp_user_accessable((void *)old, size))
+ {
+ return -EFAULT;
+ }
+ kold = kmem_get(size);
+ if (!kold)
+ {
+ return -ENOMEM;
+ }
+
+ lwp_get_from_user(kold, (void *)old, size);
+ ret = mq_setattr(mqd, knew, kold);
+ if (ret != -1)
+ {
+ lwp_put_to_user(old, kold, size);
+ }
+
+ if (ret < 0)
+ {
+ ret = GET_ERRNO();
+ }
+
+ kmem_put(kold);
+ if (new != RT_NULL)
+ {
+ kmem_put(knew);
+ }
+
+ return ret;
+#else
+ ret = mq_setattr(mqd, new, old);
+ return (ret < 0 ? GET_ERRNO() : ret);
+#endif
+}
+
+/**
+ * @brief Close a message queue descriptor.
+ *
+ * This function closes a message queue descriptor. After calling this function, the
+ * descriptor can no longer be used to interact with the message queue. Any resources
+ * associated with the descriptor are released. If the message queue was opened with the
+ * `O_CLOEXEC` flag, it will be automatically closed when the calling process executes
+ * an `exec` system call.
+ *
+ * @param[in] mqd The message queue descriptor to be closed. It was previously returned
+ * by `sys_mq_open`.
+ *
+ * @return sysret_t Returns `0` on success. On failure, returns a negative error code.
+ *
+ * @see sys_mq_open, sys_mq_unlink
+ */
+sysret_t sys_mq_close(mqd_t mqd)
+{
+ int ret = 0;
+#ifdef ARCH_MM_MMU
+ ret = mq_close(mqd);
+#else
+ ret = mq_close(mqd);
+#endif
+ return (ret < 0 ? GET_ERRNO() : ret);
+}