diff options
Diffstat (limited to 'components/lwp/syscall/syscall_mqueue.c')
| -rw-r--r-- | components/lwp/syscall/syscall_mqueue.c | 413 |
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); +} |
