Add AF_UNIX sockets implementation (#11681)

* components/dfs: support unix socket nodes

AF_UNIX pathname binding and descriptor passing require DFSv2 socket
nodes and retained open file descriptions across fd tables.

Add socket-node creation for tmpfs and devtmpfs, fd reference helpers,
and socket F_SETFL forwarding.

Impact: DFSv2 socket nodes and descriptor reference handling.
Validation: git diff --cached --check.

* components/net/sal: support local protocol providers

Local IPC protocol families do not have a backing network device.
Store the selected provider in each SAL socket and add a local provider
registry while preserving netdev checks for Internet sockets.

Handle DFSv2 close semantics, socketpair flags, and MSG_CTRUNC for
AF_UNIX integration.

Impact: SAL protocol dispatch for all socket families.
Validation: git diff --cached --check.

* components/lwp: support unix socket messages

Musl AF_UNIX addresses and ancillary data require explicit ABI and
user-memory conversion at the LWP syscall boundary.

Add the musl msghdr layout, bounded address and message copying,
control-message level conversion, and MSG_CTRUNC translation. Correct
receive buffer allocation and copy lengths while handling messages.

Impact: LWP socket syscalls when SAL is enabled.
Validation: git diff --cached --check.

* components/net/af_unix: add local sockets

Add an opt-in AF_UNIX provider for pathname-based local IPC without
a synthetic network device.

Support datagram and stream sockets, blocking and nonblocking I/O,
timeouts, poll, socketpair, and SCM_RIGHTS descriptor passing. Include
bounded Kconfig settings, component documentation, and utest coverage.

Impact: enabled only by RT_USING_AF_UNIX and requires SAL POSIX with
DFSv2.
Validation: git diff --cached --check.

* Fix AF_UNIX CI checks

Apply the repository clang-format rules to the affected source lines.
Suppress the cppcheck false positive for the devtmpfs list iterator.

No functional behavior is changed.

* components/lwp: preserve NULL optional msghdr buffers

When msg_name or msg_control is NULL, keep the kernel pointer NULL
instead of substituting an uninitialized buffer. Reject a NULL control
buffer with a nonzero length as EFAULT.

Impact: LWP sendmsg/recvmsg conversion on MMU targets.
Validation: git diff --cached --check.

* components/net/af_unix: fix poll UAF, namespace leak, and SCM_RIGHTS cycles

Poll only the local wait queue and wake writers from the receiver.
Free namespace entries on detach, and reject AF_UNIX descriptors in
SCM_RIGHTS until cycle collection exists.

Impact: AF_UNIX poll, bind lifetime, and descriptor passing.
Validation: git diff --cached --check.

* components/dfs: add DFSv2 AF_UNIX utest suite

Add opt-in tests under dfs/utest/v2/af_unix for socket nodes, IPC,
poll peer-close, namespace cleanup, and SCM_RIGHTS rejection.

Sources are built only when RT_UTEST_TC_USING_DFS_V2_AF_UNIX and the
selected test groups are enabled.

Impact: DFS utest menu and build; no production AF_UNIX behavior change.
Validation: git diff --cached --check.
This commit is contained in:
Bernard Xiong
2026-08-17 17:15:34 +08:00
committed by GitHub
parent d2acd7587e
commit 991f0ffca6
36 changed files with 5727 additions and 242 deletions
@@ -27,6 +27,7 @@
#define TMPFS_TYPE_FILE 0x00
#define TMPFS_TYPE_DIR 0x01
#define TMPFS_TYPE_DYN_DEV 0x02 /* dynamic device */
#define TMPFS_TYPE_SOCKET 0x03
struct devtmpfs_sb;
@@ -223,6 +224,7 @@ find_subpath:
if (rt_strcmp(file->name, filename) == 0)
{
rt_spin_unlock(&superblock->lock);
/* cppcheck-suppress uninitvar */
return file;
}
}
@@ -325,6 +327,10 @@ static int devtmpfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_
{
d->d_type = DT_DIR;
}
if (n_file->type == TMPFS_TYPE_SOCKET)
{
d->d_type = DT_SOCK;
}
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
rt_strncpy(d->d_name, n_file->name, DIRENT_NAME_MAX);
@@ -540,6 +546,14 @@ static struct dfs_vnode *devtmpfs_create_vnode(struct dfs_dentry *dentry, int ty
vnode->mode &= ~S_IFMT;
vnode->mode |= S_IFDIR;
}
else if (type == FT_SOCKET ||
(type == FT_REGULAR && S_ISSOCK(mode)))
{
d_file->type = TMPFS_TYPE_SOCKET;
vnode->type = FT_SOCKET;
vnode->mode &= ~S_IFMT;
vnode->mode |= S_IFSOCK;
}
else
{
d_file->type = TMPFS_TYPE_FILE;
@@ -585,6 +599,10 @@ static struct dfs_vnode *devtmpfs_lookup(struct dfs_dentry *dentry)
{
vnode->type = FT_DIRECTORY;
}
else if (d_file->type == TMPFS_TYPE_SOCKET)
{
vnode->type = FT_SOCKET;
}
else if (d_file->link)
{
vnode->type = FT_SYMLINK;
@@ -535,6 +535,10 @@ static int dfs_tmpfs_getdents(struct dfs_file *file,
{
d->d_type = DT_DIR;
}
if (n_file->type == TMPFS_TYPE_SOCKET)
{
d->d_type = DT_SOCK;
}
d->d_namlen = RT_NAME_MAX;
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
rt_strncpy(d->d_name, n_file->name, TMPFS_NAME_MAX);
@@ -664,6 +668,11 @@ static struct dfs_vnode *_dfs_tmpfs_lookup(struct dfs_dentry *dentry)
vnode->mode = S_IFDIR | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
vnode->type = FT_DIRECTORY;
}
else if (d_file->type == TMPFS_TYPE_SOCKET)
{
vnode->mode = S_IFSOCK | (S_IRWXU | S_IRWXG | S_IRWXO);
vnode->type = FT_SOCKET;
}
else
{
vnode->mode = S_IFREG | (S_IRWXU | S_IRWXG | S_IRWXO);
@@ -750,6 +759,13 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t
vnode->mode = S_IFDIR | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
vnode->type = FT_DIRECTORY;
}
else if (type == FT_SOCKET ||
(type == FT_REGULAR && S_ISSOCK(mode)))
{
d_file->type = TMPFS_TYPE_SOCKET;
vnode->mode = S_IFSOCK | (mode & (S_IRWXU | S_IRWXG | S_IRWXO));
vnode->type = FT_SOCKET;
}
else
{
d_file->type = TMPFS_TYPE_FILE;
@@ -19,6 +19,7 @@
#define TMPFS_TYPE_FILE 0x00
#define TMPFS_TYPE_DIR 0x01
#define TMPFS_TYPE_SOCKET 0x02
struct tmpfs_sb;
@@ -46,4 +47,3 @@ struct tmpfs_sb
int dfs_tmpfs_init(void);
#endif
+6
View File
@@ -134,6 +134,12 @@ int fdt_fd_associate_file(struct dfs_fdtable *fdt, int fd, struct dfs_file *file
struct dfs_file *fd_get(int fd);
void fd_release(int fd);
/* Reference helpers used when an open file description crosses fd tables. */
int dfs_file_get_refs(const int *fds, size_t count, struct dfs_file **files);
/* Successful installation transfers the supplied references to the fd table. */
int dfs_file_install_refs(struct dfs_file **files, size_t count, int *fds);
void dfs_file_put_ref(struct dfs_file *file);
void fd_init(struct dfs_file *fd);
struct dfs_fdtable *dfs_fdtable_get(void);
+1
View File
@@ -149,6 +149,7 @@ void dfs_file_init(struct dfs_file *file);
void dfs_file_deinit(struct dfs_file *file);
int dfs_file_open(struct dfs_file *file, const char *path, int flags, mode_t mode);
int dfs_file_mknod(const char *path, int type, mode_t mode);
int dfs_file_close(struct dfs_file *file);
off_t dfs_file_get_fpos(struct dfs_file *file);
+110 -4
View File
@@ -349,23 +349,29 @@ int fdt_fd_new(struct dfs_fdtable *fdt)
*/
void fdt_fd_release(struct dfs_fdtable *fdt, int fd)
{
if (fd < fdt->maxfd)
if (fdt == RT_NULL || dfs_file_lock() != RT_EOK)
{
return;
}
if (fd >= 0 && fd < (int)fdt->maxfd)
{
struct dfs_file *file;
file = fdt_get_file(fdt, fd);
if (file && file->ref_count == 1)
if (file != RT_NULL && file->ref_count == 1)
{
dfs_file_destroy(file);
}
else
else if (file != RT_NULL)
{
rt_atomic_sub(&(file->ref_count), 1);
}
fdt->fds[fd] = RT_NULL;
}
dfs_file_unlock();
}
/**
@@ -493,6 +499,106 @@ struct dfs_file *fd_get(int fd)
return fdt_get_file(fdt, fd);
}
int dfs_file_get_refs(const int *fds, size_t count, struct dfs_file **files)
{
size_t index;
struct dfs_fdtable *fdt;
if ((count != 0 && (fds == RT_NULL || files == RT_NULL)) ||
dfs_file_lock() != RT_EOK)
{
return -EINVAL;
}
fdt = dfs_fdtable_get();
for (index = 0; index < count; index++)
{
files[index] = fdt_get_file(fdt, fds[index]);
if (files[index] == RT_NULL ||
(files[index]->dentry == RT_NULL && files[index]->vnode == RT_NULL))
{
dfs_file_unlock();
return -EBADF;
}
}
for (index = 0; index < count; index++)
{
rt_atomic_add(&files[index]->ref_count, 1);
}
dfs_file_unlock();
return 0;
}
int dfs_file_install_refs(struct dfs_file **files, size_t count, int *fds)
{
int fd;
int startfd;
size_t index;
struct dfs_fdtable *fdt;
if ((count != 0 && (files == RT_NULL || fds == RT_NULL)) ||
dfs_file_lock() != RT_EOK)
{
return -EINVAL;
}
fdt = dfs_fdtable_get();
startfd = (fdt == &_fdtab) ? DFS_STDIO_OFFSET : 0;
for (index = 0; index < count; index++)
{
if (files[index] == RT_NULL || files[index]->magic != DFS_FD_MAGIC)
{
break;
}
fd = _fdt_slot_alloc(fdt, startfd);
if (fd < 0)
{
break;
}
fdt->fds[fd] = files[index];
fds[index] = fd;
}
if (index != count)
{
while (index > 0)
{
index--;
fdt->fds[fds[index]] = RT_NULL;
}
dfs_file_unlock();
return -EMFILE;
}
dfs_file_unlock();
return 0;
}
void dfs_file_put_ref(struct dfs_file *file)
{
if (file == RT_NULL || dfs_file_lock() != RT_EOK)
{
return;
}
if (file->magic == DFS_FD_MAGIC &&
rt_atomic_load(&file->ref_count) > 0 &&
dfs_file_close(file) == 0)
{
if (rt_atomic_load(&file->ref_count) == 1)
{
dfs_file_destroy(file);
}
else
{
rt_atomic_sub(&file->ref_count, 1);
}
}
dfs_file_unlock();
}
/**
* This function will get the file descriptor table of current process.
*/
@@ -1238,4 +1344,4 @@ MSH_CMD_EXPORT(dfs_dlog, dfs dlog on|off);
#endif
#endif
/** @} */
/** @} */
+132 -1
View File
@@ -823,6 +823,128 @@ _ERR_RET:
return ret;
}
int dfs_file_mknod(const char *path, int type, mode_t mode)
{
int ret = -EINVAL;
int create_type = type;
mode_t create_mode = mode;
char *fullpath;
struct dfs_mnt *mnt;
struct dfs_dentry *dentry;
if (path == RT_NULL || type < FT_REGULAR || type > FT_NONLOCK)
{
return -EINVAL;
}
fullpath = dfs_normalize_path(RT_NULL, path);
if (fullpath == RT_NULL)
{
return -ENOMEM;
}
mnt = dfs_mnt_lookup(fullpath);
if (mnt == RT_NULL)
{
ret = -ENOENT;
goto __exit;
}
{
char *realpath;
realpath = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
if (realpath != RT_NULL)
{
rt_free(fullpath);
fullpath = realpath;
}
}
if (strcmp(mnt->fullpath, fullpath) == 0)
{
ret = -EEXIST;
goto __exit;
}
dentry = dfs_dentry_lookup(mnt, fullpath, 0);
if (dentry != RT_NULL)
{
dfs_dentry_unref(dentry);
ret = -EEXIST;
goto __exit;
}
if (mnt->fs_ops->create_vnode == RT_NULL)
{
ret = -ENOSYS;
goto __exit;
}
if (type == FT_SOCKET)
{
create_type = FT_REGULAR;
create_mode = (mode & ~S_IFMT) | S_IFSOCK;
}
ret = dfs_file_lock();
if (ret != RT_EOK)
{
goto __exit;
}
dentry = dfs_dentry_create(mnt, fullpath);
if (dentry != RT_NULL)
{
struct dfs_vnode *vnode = RT_NULL;
if (dfs_is_mounted(mnt) == 0)
{
vnode = mnt->fs_ops->create_vnode(dentry, create_type,
create_mode);
}
if (vnode != RT_NULL)
{
if (type == FT_SOCKET && !S_ISSOCK(vnode->mode))
{
if (mnt->fs_ops->unlink != RT_NULL)
{
dentry->vnode = vnode;
(void)mnt->fs_ops->unlink(dentry);
dentry->vnode = RT_NULL;
}
dfs_vnode_unref(vnode);
ret = -EOPNOTSUPP;
}
else
{
vnode->type = type;
dentry->vnode = vnode;
dfs_dentry_insert(dentry);
ret = RT_EOK;
}
}
else
{
ret = -ENOENT;
}
}
else
{
ret = -ENOMEM;
}
dfs_file_unlock();
if (dentry != RT_NULL)
{
dfs_dentry_unref(dentry);
}
__exit:
rt_free(fullpath);
return ret;
}
/**
* @brief Close a file and release associated resources
*
@@ -1588,6 +1710,15 @@ int dfs_file_fcntl(int fd, int cmd, unsigned long arg)
O_APPEND | O_NONBLOCK;
flags &= mask;
if (file->vnode->type == FT_SOCKET && file->fops != RT_NULL &&
file->fops->ioctl != RT_NULL)
{
ret = file->fops->ioctl(file, F_SETFL, (void *)(rt_base_t)flags);
if (ret < 0)
{
break;
}
}
file->flags &= ~mask;
file->flags |= flags;
break;
@@ -3035,4 +3166,4 @@ void copy(const char *src, const char *dst)
}
FINSH_FUNCTION_EXPORT(copy, copy file or dir)
#endif
#endif
+3
View File
@@ -40,5 +40,8 @@ if RT_USING_DFS
This device will be formatted and mounted during testing
endif
rsource "v2/af_unix/Kconfig"
endmenu
endif
+4 -2
View File
@@ -1,5 +1,6 @@
Import('rtconfig')
from building import *
import os
cwd = GetCurrentDir()
src = []
@@ -15,6 +16,7 @@ if GetDepend('RT_UTEST_TC_USING_DFS_API'):
src += ['tc_posix_api.c']
# Define the test group with proper dependencies
group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES', 'RT_USING_DFS'], CPPPATH = CPPPATH)
objs = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES', 'RT_USING_DFS'], CPPPATH = CPPPATH)
objs = objs + SConscript(os.path.join('v2', 'af_unix', 'SConscript'))
Return('group')
Return('objs')
+95
View File
@@ -0,0 +1,95 @@
menuconfig RT_UTEST_TC_USING_DFS_V2_AF_UNIX
bool "DFSv2 AF_UNIX Test"
default n
depends on RT_USING_DFS_V2
depends on RT_USING_AF_UNIX
depends on SAL_USING_POSIX
help
Enable DFSv2 AF_UNIX unit tests. Sources are compiled only when
this option and the selected test groups below are enabled.
Coverage includes socket-node creation, transferable file
references, datagram and stream IPC, poll readiness, pathname
lifetime, namespace cleanup, and SCM_RIGHTS handling.
if RT_UTEST_TC_USING_DFS_V2_AF_UNIX
config RT_UTEST_DFS_V2_AF_UNIX_DIR
string "Directory for AF_UNIX pathname tests"
default "/dev"
help
Directory used to create temporary AF_UNIX pathname nodes.
The filesystem mounted here must support S_IFSOCK nodes
through create_vnode(), such as tmpfs or devtmpfs.
config RT_UTEST_DFS_V2_AF_UNIX_NODE
bool "Socket node and file-reference tests"
default y
help
Build tests for DFSv2 socket nodes and transferable file
references:
* dfs_file_mknod(FT_SOCKET) creates an S_ISSOCK node
* creating the same path again returns EEXIST
* unlink() removes the socket node
* dfs_file_get_refs() rejects an invalid fd
* get_refs() + put_ref() keeps the original fd usable
* get_refs() + install_refs() transfers a pipe and shares
the open file description
config RT_UTEST_DFS_V2_AF_UNIX_IPC
bool "Datagram, stream and socketpair IPC tests"
default y
help
Build tests for AF_UNIX IPC operations:
* SOCK_DGRAM bind, connect, send and recvfrom
* SOCK_STREAM listen, accept, send, recv and shutdown
* socketpair() for both SOCK_DGRAM and SOCK_STREAM
* sendmsg()/recvmsg() scatter-gather I/O
* connected sendmsg() with a NULL msg_name
* sendmsg() with a NULL msg_control and nonzero length
* listen() on SOCK_DGRAM returns EOPNOTSUPP
* connect() to a missing pathname returns ENOENT
* non-blocking recv() returns EAGAIN
config RT_UTEST_DFS_V2_AF_UNIX_POLL
bool "Poll readiness and peer-close tests"
default y
help
Build tests for AF_UNIX poll/select readiness:
* empty stream socket reports POLLOUT and not POLLIN
* received data reports POLLIN
* filling the stream buffer clears POLLOUT; a peer recv
makes POLLOUT ready again
* blocking poll(POLLIN) wakes with POLLHUP/POLLIN when
the peer is closed (covers the peer-queue UAF case)
* datagram queue-full and POLLIN readiness
config RT_UTEST_DFS_V2_AF_UNIX_NAMESPACE
bool "Pathname bind, unlink and namespace tests"
default y
help
Build tests for pathname lifetime and namespace cleanup:
* bind() creates a persistent S_IFSOCK node
* close() leaves the pathname in the filesystem
* rebinding without unlink() returns EADDRINUSE
* unlink() then bind() succeeds
* connect() after close without unlink() returns
ECONNREFUSED
* connect() after unlink() returns ENOENT
* repeated bind/close/unlink on distinct paths does not
leak namespace entries
config RT_UTEST_DFS_V2_AF_UNIX_RIGHTS
bool "SCM_RIGHTS transfer and rejection tests"
default y
help
Build tests for SCM_RIGHTS descriptor passing:
* transfer pipe descriptors over SOCK_DGRAM and
SOCK_STREAM
* recvmsg() without a control buffer reports MSG_CTRUNC
* MSG_PEEK does not install or consume descriptors
* an invalid fd returns EBADF
* passing an AF_UNIX socket descriptor returns
EOPNOTSUPP and does not create a reference cycle
endif
@@ -0,0 +1,27 @@
Import('rtconfig')
from building import *
cwd = GetCurrentDir()
src = []
CPPPATH = [cwd]
if GetDepend('RT_UTEST_TC_USING_DFS_V2_AF_UNIX'):
cases = []
if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_NODE'):
cases += ['tc_af_unix_node.c']
if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_IPC'):
cases += ['tc_af_unix_ipc.c']
if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_POLL'):
cases += ['tc_af_unix_poll.c']
if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_NAMESPACE'):
cases += ['tc_af_unix_namespace.c']
if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_RIGHTS'):
cases += ['tc_af_unix_rights.c']
if cases:
src += ['tc_af_unix_common.c'] + cases
group = DefineGroup('utestcases', src,
depend = ['RT_USING_UTESTCASES', 'RT_USING_DFS_V2', 'RT_USING_AF_UNIX'],
CPPPATH = CPPPATH)
Return('group')
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef TC_AF_UNIX_H__
#define TC_AF_UNIX_H__
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dfs.h>
#include <dfs_file.h>
#include <rtthread.h>
#include <utest.h>
#ifndef RT_UTEST_DFS_V2_AF_UNIX_DIR
#define RT_UTEST_DFS_V2_AF_UNIX_DIR "/dev"
#endif
#define TC_AF_UNIX_PATH_MAX 64
void tc_af_unix_path(char *buf, rt_size_t size, const char *name);
void tc_af_unix_make_address(struct sockaddr_un *address, const char *path);
void tc_af_unix_closesocket(int *fd);
void tc_af_unix_close(int *fd);
int tc_af_unix_send_rights(int socket_fd, const int *fds, size_t fd_count,
char payload);
int tc_af_unix_receive_rights(int socket_fd, int flags, size_t control_length,
int *fds, size_t *fd_count, int *message_flags,
char *payload);
#endif /* TC_AF_UNIX_H__ */
@@ -0,0 +1,98 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "tc_af_unix.h"
void tc_af_unix_path(char *buf, rt_size_t size, const char *name)
{
rt_snprintf(buf, size, "%s/%s", RT_UTEST_DFS_V2_AF_UNIX_DIR, name);
}
void tc_af_unix_make_address(struct sockaddr_un *address, const char *path)
{
rt_memset(address, 0, sizeof(*address));
address->sa_family = AF_UNIX;
rt_strncpy(address->sun_path, path, sizeof(address->sun_path) - 1);
}
void tc_af_unix_closesocket(int *fd)
{
if (*fd >= 0)
{
closesocket(*fd);
*fd = -1;
}
}
void tc_af_unix_close(int *fd)
{
if (*fd >= 0)
{
close(*fd);
*fd = -1;
}
}
int tc_af_unix_send_rights(int socket_fd, const int *fds, size_t fd_count,
char payload)
{
char control[CMSG_SPACE(2 * sizeof(int))];
struct cmsghdr *cmsg;
struct iovec iov;
struct msghdr message;
rt_memset(&message, 0, sizeof(message));
rt_memset(control, 0, sizeof(control));
iov.iov_base = &payload;
iov.iov_len = sizeof(payload);
message.msg_iov = &iov;
message.msg_iovlen = 1;
message.msg_control = control;
message.msg_controllen = CMSG_SPACE(fd_count * sizeof(int));
cmsg = CMSG_FIRSTHDR(&message);
cmsg->cmsg_len = CMSG_LEN(fd_count * sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
rt_memcpy(CMSG_DATA(cmsg), fds, fd_count * sizeof(int));
return sendmsg(socket_fd, &message, 0);
}
int tc_af_unix_receive_rights(int socket_fd, int flags, size_t control_length,
int *fds, size_t *fd_count, int *message_flags,
char *payload)
{
char control[CMSG_SPACE(2 * sizeof(int))];
int result;
size_t count = 0;
struct cmsghdr *cmsg;
struct iovec iov;
struct msghdr message;
rt_memset(&message, 0, sizeof(message));
rt_memset(control, 0, sizeof(control));
iov.iov_base = payload;
iov.iov_len = sizeof(*payload);
message.msg_iov = &iov;
message.msg_iovlen = 1;
message.msg_control = control_length != 0 ? control : RT_NULL;
message.msg_controllen = control_length;
result = recvmsg(socket_fd, &message, flags);
if (result >= 0)
{
cmsg = CMSG_FIRSTHDR(&message);
if (cmsg != RT_NULL && cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS &&
cmsg->cmsg_len >= CMSG_LEN(sizeof(int)))
{
count = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(int);
rt_memcpy(fds, CMSG_DATA(cmsg), count * sizeof(int));
}
*fd_count = count;
*message_flags = message.msg_flags;
}
return result;
}
@@ -0,0 +1,291 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "tc_af_unix.h"
static void tc_af_unix_dgram_ipc(void)
{
char buffer[16];
char client_path[TC_AF_UNIX_PATH_MAX];
char server_path[TC_AF_UNIX_PATH_MAX];
const char payload[] = "datagram";
int client = -1;
int error;
int result;
int server = -1;
socklen_t source_length;
struct sockaddr_un client_address;
struct sockaddr_un server_address;
struct sockaddr_un source_address;
tc_af_unix_path(server_path, sizeof(server_path), "utafudgs");
tc_af_unix_path(client_path, sizeof(client_path), "utafudgc");
(void)unlink(server_path);
(void)unlink(client_path);
tc_af_unix_make_address(&server_address, server_path);
tc_af_unix_make_address(&client_address, client_path);
server = socket(AF_UNIX, SOCK_DGRAM, 0);
client = socket(AF_UNIX, SOCK_DGRAM, 0);
uassert_true(server >= 0);
uassert_true(client >= 0);
if (server < 0 || client < 0)
{
goto __exit;
}
uassert_int_equal(bind(server, (struct sockaddr *)&server_address,
sizeof(server_address)),
0);
uassert_int_equal(bind(client, (struct sockaddr *)&client_address,
sizeof(client_address)),
0);
uassert_int_equal(connect(client, (struct sockaddr *)&server_address,
sizeof(server_address)),
0);
uassert_int_equal(send(client, payload, sizeof(payload), 0),
sizeof(payload));
source_length = sizeof(source_address);
result = recvfrom(server, buffer, sizeof(buffer), 0,
(struct sockaddr *)&source_address, &source_length);
uassert_int_equal(result, sizeof(payload));
uassert_buf_equal(buffer, payload, sizeof(payload));
uassert_str_equal(source_address.sun_path, client_path);
result = fcntl(server, F_SETFL, O_NONBLOCK);
uassert_int_equal(result, 0);
result = recv(server, buffer, sizeof(buffer), 0);
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_true(error == EAGAIN || error == EWOULDBLOCK);
result = listen(server, 1);
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_int_equal(error, EOPNOTSUPP);
__exit:
tc_af_unix_closesocket(&client);
tc_af_unix_closesocket(&server);
(void)unlink(client_path);
(void)unlink(server_path);
}
static void tc_af_unix_stream_ipc(void)
{
char accepted_path[TC_AF_UNIX_PATH_MAX];
char buffer[16];
const char request[] = "request";
const char response[] = "response";
int accepted = -1;
int client = -1;
int listener = -1;
struct sockaddr_un address;
struct sockaddr_un local_address;
struct sockaddr_un peer_address;
socklen_t local_length;
socklen_t peer_length;
tc_af_unix_path(accepted_path, sizeof(accepted_path), "utafustr");
(void)unlink(accepted_path);
tc_af_unix_make_address(&address, accepted_path);
listener = socket(AF_UNIX, SOCK_STREAM, 0);
client = socket(AF_UNIX, SOCK_STREAM, 0);
uassert_true(listener >= 0);
uassert_true(client >= 0);
if (listener < 0 || client < 0)
{
goto __exit;
}
uassert_int_equal(bind(listener, (struct sockaddr *)&address,
sizeof(address)),
0);
uassert_int_equal(listen(listener, 2), 0);
uassert_int_equal(connect(client, (struct sockaddr *)&address,
sizeof(address)),
0);
accepted = accept(listener, RT_NULL, RT_NULL);
uassert_true(accepted >= 0);
if (accepted < 0)
{
goto __exit;
}
local_length = sizeof(local_address);
peer_length = sizeof(peer_address);
uassert_int_equal(getsockname(accepted, (struct sockaddr *)&local_address,
&local_length),
0);
uassert_int_equal(getpeername(client, (struct sockaddr *)&peer_address,
&peer_length),
0);
uassert_str_equal(local_address.sun_path, accepted_path);
uassert_str_equal(peer_address.sun_path, accepted_path);
uassert_int_equal(send(client, request, sizeof(request), 0),
sizeof(request));
uassert_int_equal(recv(accepted, buffer, sizeof(buffer), 0),
sizeof(request));
uassert_buf_equal(buffer, request, sizeof(request));
uassert_int_equal(send(accepted, response, sizeof(response), 0),
sizeof(response));
uassert_int_equal(recv(client, buffer, sizeof(buffer), 0),
sizeof(response));
uassert_buf_equal(buffer, response, sizeof(response));
uassert_int_equal(shutdown(client, SHUT_WR), 0);
uassert_int_equal(recv(accepted, buffer, sizeof(buffer), 0), 0);
__exit:
tc_af_unix_closesocket(&accepted);
tc_af_unix_closesocket(&client);
tc_af_unix_closesocket(&listener);
(void)unlink(accepted_path);
}
static void tc_af_unix_socketpair_msg(void)
{
char buffer[8];
char first_part[3];
char second_part[3];
const char payload[] = "pair";
int sockets[2] = { -1, -1 };
int type_index;
int types[2] = { SOCK_DGRAM, SOCK_STREAM };
struct iovec receive_iov[2];
struct iovec send_iov[2];
struct msghdr receive_message;
struct msghdr send_message;
for (type_index = 0; type_index < 2; type_index++)
{
uassert_int_equal(socketpair(AF_UNIX, types[type_index], 0, sockets),
0);
uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
sizeof(payload));
uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
sizeof(payload));
uassert_buf_equal(buffer, payload, sizeof(payload));
rt_memset(&send_message, 0, sizeof(send_message));
send_iov[0].iov_base = (void *)payload;
send_iov[0].iov_len = 2;
send_iov[1].iov_base = (void *)(payload + 2);
send_iov[1].iov_len = sizeof(payload) - 2;
send_message.msg_iov = send_iov;
send_message.msg_iovlen = 2;
send_message.msg_name = RT_NULL;
send_message.msg_namelen = sizeof(struct sockaddr_un);
uassert_int_equal(sendmsg(sockets[0], &send_message, 0),
sizeof(payload));
rt_memset(&receive_message, 0, sizeof(receive_message));
receive_iov[0].iov_base = first_part;
receive_iov[0].iov_len = sizeof(first_part);
receive_iov[1].iov_base = second_part;
receive_iov[1].iov_len = sizeof(second_part);
receive_message.msg_iov = receive_iov;
receive_message.msg_iovlen = 2;
uassert_int_equal(recvmsg(sockets[1], &receive_message, 0),
sizeof(payload));
uassert_buf_equal(first_part, payload, sizeof(first_part));
uassert_buf_equal(second_part, payload + sizeof(first_part),
sizeof(payload) - sizeof(first_part));
send_message.msg_control = RT_NULL;
send_message.msg_controllen = CMSG_SPACE(sizeof(int));
uassert_int_equal(sendmsg(sockets[0], &send_message, 0), -1);
uassert_int_equal(rt_get_errno(), EINVAL);
tc_af_unix_closesocket(&sockets[0]);
tc_af_unix_closesocket(&sockets[1]);
}
}
static void tc_af_unix_connect_missing(void)
{
char path[TC_AF_UNIX_PATH_MAX];
int client = -1;
int error;
int result;
struct sockaddr_un address;
tc_af_unix_path(path, sizeof(path), "utafumiss");
(void)unlink(path);
tc_af_unix_make_address(&address, path);
client = socket(AF_UNIX, SOCK_STREAM, 0);
uassert_true(client >= 0);
if (client < 0)
{
return;
}
result = connect(client, (struct sockaddr *)&address, sizeof(address));
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_int_equal(error, ENOENT);
tc_af_unix_closesocket(&client);
}
static void tc_af_unix_pending_close(void)
{
char path[TC_AF_UNIX_PATH_MAX];
const char payload[] = "pending";
int client = -1;
int error;
int listener = -1;
int result;
struct sockaddr_un address;
tc_af_unix_path(path, sizeof(path), "utafupend");
(void)unlink(path);
tc_af_unix_make_address(&address, path);
listener = socket(AF_UNIX, SOCK_STREAM, 0);
client = socket(AF_UNIX, SOCK_STREAM, 0);
uassert_true(listener >= 0);
uassert_true(client >= 0);
if (listener < 0 || client < 0)
{
goto __exit;
}
uassert_int_equal(bind(listener, (struct sockaddr *)&address,
sizeof(address)),
0);
uassert_int_equal(listen(listener, 1), 0);
uassert_int_equal(connect(client, (struct sockaddr *)&address,
sizeof(address)),
0);
tc_af_unix_closesocket(&listener);
result = send(client, payload, sizeof(payload), 0);
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_true(error == ENOTCONN || error == EPIPE);
__exit:
tc_af_unix_closesocket(&client);
tc_af_unix_closesocket(&listener);
(void)unlink(path);
}
static void tc_af_unix_ipc_testcase(void)
{
UTEST_UNIT_RUN(tc_af_unix_dgram_ipc);
UTEST_UNIT_RUN(tc_af_unix_stream_ipc);
UTEST_UNIT_RUN(tc_af_unix_socketpair_msg);
UTEST_UNIT_RUN(tc_af_unix_connect_missing);
UTEST_UNIT_RUN(tc_af_unix_pending_close);
}
UTEST_TC_EXPORT(tc_af_unix_ipc_testcase, "components.dfs.v2.af_unix.ipc",
RT_NULL, RT_NULL, 15);
@@ -0,0 +1,175 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "tc_af_unix.h"
#define TC_AF_UNIX_NAMESPACE_REPEAT 8
static void tc_af_unix_pathname_lifetime(void)
{
char path[TC_AF_UNIX_PATH_MAX];
int error;
int first = -1;
int result;
int second = -1;
struct sockaddr_un address;
struct stat file_stat;
tc_af_unix_path(path, sizeof(path), "utafupath");
(void)unlink(path);
tc_af_unix_make_address(&address, path);
first = socket(AF_UNIX, SOCK_DGRAM, 0);
uassert_true(first >= 0);
if (first < 0)
{
goto __exit;
}
uassert_int_equal(bind(first, (struct sockaddr *)&address, sizeof(address)),
0);
uassert_int_equal(stat(path, &file_stat), 0);
uassert_true(S_ISSOCK(file_stat.st_mode));
tc_af_unix_closesocket(&first);
uassert_int_equal(stat(path, &file_stat), 0);
uassert_true(S_ISSOCK(file_stat.st_mode));
second = socket(AF_UNIX, SOCK_DGRAM, 0);
uassert_true(second >= 0);
if (second < 0)
{
goto __exit;
}
result = bind(second, (struct sockaddr *)&address, sizeof(address));
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_int_equal(error, EADDRINUSE);
uassert_int_equal(unlink(path), 0);
uassert_int_equal(bind(second, (struct sockaddr *)&address,
sizeof(address)),
0);
__exit:
tc_af_unix_closesocket(&second);
tc_af_unix_closesocket(&first);
(void)unlink(path);
}
static void tc_af_unix_lookup_after_close(void)
{
char path[TC_AF_UNIX_PATH_MAX];
int client = -1;
int error;
int result;
int server = -1;
struct sockaddr_un address;
tc_af_unix_path(path, sizeof(path), "utafuref");
(void)unlink(path);
tc_af_unix_make_address(&address, path);
server = socket(AF_UNIX, SOCK_STREAM, 0);
client = socket(AF_UNIX, SOCK_STREAM, 0);
uassert_true(server >= 0);
uassert_true(client >= 0);
if (server < 0 || client < 0)
{
goto __exit;
}
uassert_int_equal(bind(server, (struct sockaddr *)&address,
sizeof(address)),
0);
uassert_int_equal(listen(server, 1), 0);
tc_af_unix_closesocket(&server);
result = connect(client, (struct sockaddr *)&address, sizeof(address));
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_int_equal(error, ECONNREFUSED);
uassert_int_equal(unlink(path), 0);
result = connect(client, (struct sockaddr *)&address, sizeof(address));
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_int_equal(error, ENOENT);
__exit:
tc_af_unix_closesocket(&client);
tc_af_unix_closesocket(&server);
(void)unlink(path);
}
static void tc_af_unix_namespace_reuse(void)
{
char name[16];
char path[TC_AF_UNIX_PATH_MAX];
int client = -1;
int error;
int index;
int result;
int sock = -1;
struct sockaddr_un address;
for (index = 0; index < TC_AF_UNIX_NAMESPACE_REPEAT; index++)
{
rt_snprintf(name, sizeof(name), "utafuns%d", index);
tc_af_unix_path(path, sizeof(path), name);
(void)unlink(path);
tc_af_unix_make_address(&address, path);
sock = socket(AF_UNIX, SOCK_DGRAM, 0);
uassert_true(sock >= 0);
if (sock < 0)
{
return;
}
uassert_int_equal(bind(sock, (struct sockaddr *)&address,
sizeof(address)),
0);
tc_af_unix_closesocket(&sock);
uassert_int_equal(unlink(path), 0);
}
tc_af_unix_path(path, sizeof(path), "utafuns0");
tc_af_unix_make_address(&address, path);
client = socket(AF_UNIX, SOCK_DGRAM, 0);
uassert_true(client >= 0);
if (client < 0)
{
return;
}
result = connect(client, (struct sockaddr *)&address, sizeof(address));
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_int_equal(error, ENOENT);
tc_af_unix_closesocket(&client);
tc_af_unix_path(path, sizeof(path), "utafunew");
(void)unlink(path);
tc_af_unix_make_address(&address, path);
sock = socket(AF_UNIX, SOCK_DGRAM, 0);
uassert_true(sock >= 0);
if (sock < 0)
{
return;
}
uassert_int_equal(bind(sock, (struct sockaddr *)&address, sizeof(address)),
0);
tc_af_unix_closesocket(&sock);
(void)unlink(path);
}
static void tc_af_unix_namespace_testcase(void)
{
UTEST_UNIT_RUN(tc_af_unix_pathname_lifetime);
UTEST_UNIT_RUN(tc_af_unix_lookup_after_close);
UTEST_UNIT_RUN(tc_af_unix_namespace_reuse);
}
UTEST_TC_EXPORT(tc_af_unix_namespace_testcase,
"components.dfs.v2.af_unix.namespace", RT_NULL, RT_NULL, 15);
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "tc_af_unix.h"
static void tc_af_unix_mknod_socket(void)
{
char path[TC_AF_UNIX_PATH_MAX];
int result;
struct stat file_stat;
tc_af_unix_path(path, sizeof(path), "utafumkn");
(void)unlink(path);
result = dfs_file_mknod(path, FT_SOCKET, S_IRWXU | S_IRWXG | S_IRWXO);
uassert_int_equal(result, 0);
if (result != 0)
{
return;
}
uassert_int_equal(stat(path, &file_stat), 0);
uassert_true(S_ISSOCK(file_stat.st_mode));
result = dfs_file_mknod(path, FT_SOCKET, S_IRWXU | S_IRWXG | S_IRWXO);
uassert_int_equal(result, -EEXIST);
uassert_int_equal(unlink(path), 0);
uassert_true(stat(path, &file_stat) < 0);
}
static void tc_af_unix_file_refs(void)
{
char data[4];
int installed = -1;
int invalid_fd = -1;
int pipe_fds[2] = { -1, -1 };
struct dfs_file *files[1];
files[0] = RT_NULL;
uassert_int_equal(dfs_file_get_refs(&invalid_fd, 1, files), -EBADF);
uassert_true(files[0] == RT_NULL);
uassert_int_equal(pipe(pipe_fds), 0);
if (pipe_fds[0] < 0)
{
return;
}
uassert_int_equal(write(pipe_fds[1], "abcd", 4), 4);
uassert_int_equal(dfs_file_get_refs(&pipe_fds[0], 1, files), 0);
uassert_true(files[0] != RT_NULL);
dfs_file_put_ref(files[0]);
uassert_int_equal(read(pipe_fds[0], data, 2), 2);
uassert_buf_equal(data, "ab", 2);
uassert_int_equal(dfs_file_get_refs(&pipe_fds[0], 1, files), 0);
uassert_int_equal(dfs_file_install_refs(files, 1, &installed), 0);
uassert_true(installed >= 0);
uassert_int_equal(read(installed, data, 2), 2);
uassert_buf_equal(data, "cd", 2);
tc_af_unix_close(&installed);
tc_af_unix_close(&pipe_fds[0]);
tc_af_unix_close(&pipe_fds[1]);
}
static void tc_af_unix_node_testcase(void)
{
UTEST_UNIT_RUN(tc_af_unix_mknod_socket);
UTEST_UNIT_RUN(tc_af_unix_file_refs);
}
UTEST_TC_EXPORT(tc_af_unix_node_testcase, "components.dfs.v2.af_unix.node",
RT_NULL, RT_NULL, 10);
@@ -0,0 +1,211 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "tc_af_unix.h"
#define TC_AF_UNIX_POLL_STACK 4096
#define TC_AF_UNIX_POLL_PRIO 20
struct tc_af_unix_poll_ctx
{
int fd;
int result;
short revents;
struct rt_event done;
};
static void tc_af_unix_poll_entry(void *parameter)
{
struct pollfd poll_fd;
struct tc_af_unix_poll_ctx *ctx;
ctx = (struct tc_af_unix_poll_ctx *)parameter;
poll_fd.fd = ctx->fd;
poll_fd.events = POLLIN;
poll_fd.revents = 0;
ctx->result = poll(&poll_fd, 1, 3000);
ctx->revents = poll_fd.revents;
rt_event_send(&ctx->done, 0x1);
}
static void tc_af_unix_stream_readiness(void)
{
char buffer[8];
char *fill;
const char payload[] = "poll";
int result;
int sockets[2] = { -1, -1 };
rt_size_t sent = 0;
struct pollfd poll_fd;
result = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
uassert_int_equal(result, 0);
if (result < 0)
{
return;
}
poll_fd.fd = sockets[1];
poll_fd.events = POLLIN | POLLOUT;
poll_fd.revents = 0;
uassert_int_equal(poll(&poll_fd, 1, 0), 1);
uassert_true((poll_fd.revents & POLLOUT) != 0);
uassert_true((poll_fd.revents & POLLIN) == 0);
uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
sizeof(payload));
poll_fd.revents = 0;
uassert_int_equal(poll(&poll_fd, 1, 0), 1);
uassert_true((poll_fd.revents & POLLIN) != 0);
uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
sizeof(payload));
fill = (char *)rt_malloc(AF_UNIX_STREAM_BUFFER_SIZE);
uassert_true(fill != RT_NULL);
if (fill != RT_NULL)
{
rt_memset(fill, 'x', AF_UNIX_STREAM_BUFFER_SIZE);
uassert_int_equal(fcntl(sockets[0], F_SETFL, O_NONBLOCK), 0);
while (sent < AF_UNIX_STREAM_BUFFER_SIZE)
{
result = send(sockets[0], fill + sent,
AF_UNIX_STREAM_BUFFER_SIZE - sent, 0);
if (result <= 0)
{
break;
}
sent += (rt_size_t)result;
}
uassert_int_equal(sent, AF_UNIX_STREAM_BUFFER_SIZE);
poll_fd.fd = sockets[0];
poll_fd.events = POLLOUT;
poll_fd.revents = 0;
uassert_int_equal(poll(&poll_fd, 1, 0), 0);
uassert_int_equal(recv(sockets[1], fill, 64, 0), 64);
poll_fd.revents = 0;
uassert_int_equal(poll(&poll_fd, 1, 0), 1);
uassert_true((poll_fd.revents & POLLOUT) != 0);
rt_free(fill);
}
uassert_int_equal(shutdown(sockets[0], SHUT_WR), 0);
poll_fd.fd = sockets[1];
poll_fd.events = POLLIN;
poll_fd.revents = 0;
uassert_int_equal(poll(&poll_fd, 1, 0), 1);
uassert_true((poll_fd.revents & POLLIN) != 0);
tc_af_unix_closesocket(&sockets[0]);
tc_af_unix_closesocket(&sockets[1]);
}
static void tc_af_unix_dgram_readiness(void)
{
char buffer[2];
const char payload[] = "x";
int error;
int index;
int result;
int sockets[2] = { -1, -1 };
struct pollfd poll_fd;
result = socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, sockets);
uassert_int_equal(result, 0);
if (result < 0)
{
return;
}
poll_fd.fd = sockets[1];
poll_fd.events = POLLIN;
poll_fd.revents = 0;
uassert_int_equal(poll(&poll_fd, 1, 0), 0);
for (index = 0; index < AF_UNIX_DGRAM_QUEUE_LEN; index++)
{
uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
sizeof(payload));
}
result = send(sockets[0], payload, sizeof(payload), 0);
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_int_equal(error, EAGAIN);
poll_fd.revents = 0;
uassert_int_equal(poll(&poll_fd, 1, 0), 1);
uassert_true((poll_fd.revents & POLLIN) != 0);
uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
sizeof(payload));
uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
sizeof(payload));
result = send(sockets[0], payload, AF_UNIX_DGRAM_MAX_SIZE + 1, 0);
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_int_equal(error, EMSGSIZE);
tc_af_unix_closesocket(&sockets[0]);
tc_af_unix_closesocket(&sockets[1]);
}
static void tc_af_unix_poll_peer_close(void)
{
int sockets[2] = { -1, -1 };
rt_err_t wait;
rt_thread_t thread;
rt_uint32_t recved;
struct tc_af_unix_poll_ctx ctx;
uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), 0);
if (sockets[0] < 0)
{
return;
}
rt_memset(&ctx, 0, sizeof(ctx));
ctx.fd = sockets[0];
ctx.result = -2;
uassert_int_equal(rt_event_init(&ctx.done, "utafup", RT_IPC_FLAG_PRIO),
RT_EOK);
thread = rt_thread_create("utafup", tc_af_unix_poll_entry, &ctx,
TC_AF_UNIX_POLL_STACK, TC_AF_UNIX_POLL_PRIO, 10);
uassert_true(thread != RT_NULL);
if (thread == RT_NULL)
{
rt_event_detach(&ctx.done);
tc_af_unix_closesocket(&sockets[0]);
tc_af_unix_closesocket(&sockets[1]);
return;
}
rt_thread_startup(thread);
rt_thread_mdelay(50);
tc_af_unix_closesocket(&sockets[1]);
wait = rt_event_recv(&ctx.done, 0x1,
RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
rt_tick_from_millisecond(3000), &recved);
uassert_int_equal(wait, RT_EOK);
uassert_int_equal(ctx.result, 1);
uassert_true((ctx.revents & (POLLIN | POLLHUP)) != 0);
rt_event_detach(&ctx.done);
tc_af_unix_closesocket(&sockets[0]);
tc_af_unix_closesocket(&sockets[1]);
}
static void tc_af_unix_poll_testcase(void)
{
UTEST_UNIT_RUN(tc_af_unix_stream_readiness);
UTEST_UNIT_RUN(tc_af_unix_dgram_readiness);
UTEST_UNIT_RUN(tc_af_unix_poll_peer_close);
}
UTEST_TC_EXPORT(tc_af_unix_poll_testcase, "components.dfs.v2.af_unix.poll",
RT_NULL, RT_NULL, 20);
@@ -0,0 +1,219 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "tc_af_unix.h"
static void tc_af_unix_rights_transfer(void)
{
char data;
char payload;
int message_flags;
int pipe_fds[2][2];
int received_fds[2];
int send_fds[2];
int sockets[2];
int type_index;
int types[2] = { SOCK_DGRAM, SOCK_STREAM };
size_t fd_count;
for (type_index = 0; type_index < 2; type_index++)
{
received_fds[0] = -1;
received_fds[1] = -1;
pipe_fds[0][0] = -1;
pipe_fds[0][1] = -1;
pipe_fds[1][0] = -1;
pipe_fds[1][1] = -1;
sockets[0] = -1;
sockets[1] = -1;
uassert_int_equal(socketpair(AF_UNIX, types[type_index], 0, sockets),
0);
uassert_int_equal(pipe(pipe_fds[0]), 0);
uassert_int_equal(pipe(pipe_fds[1]), 0);
if (sockets[0] < 0 || pipe_fds[0][0] < 0 || pipe_fds[1][0] < 0)
{
goto __iteration_exit;
}
uassert_int_equal(write(pipe_fds[0][1], "a", 1), 1);
uassert_int_equal(write(pipe_fds[1][1], "b", 1), 1);
send_fds[0] = pipe_fds[0][0];
send_fds[1] = pipe_fds[1][0];
uassert_int_equal(tc_af_unix_send_rights(sockets[0], send_fds, 2, 'r'),
1);
tc_af_unix_close(&pipe_fds[0][0]);
tc_af_unix_close(&pipe_fds[1][0]);
fd_count = 0;
message_flags = 0;
uassert_int_equal(tc_af_unix_receive_rights(
sockets[1], 0, CMSG_SPACE(2 * sizeof(int)),
received_fds, &fd_count, &message_flags,
&payload),
1);
uassert_int_equal(payload, 'r');
uassert_int_equal(fd_count, 2);
uassert_int_equal(message_flags & MSG_CTRUNC, 0);
uassert_int_equal(read(received_fds[0], &data, 1), 1);
uassert_int_equal(data, 'a');
uassert_int_equal(read(received_fds[1], &data, 1), 1);
uassert_int_equal(data, 'b');
__iteration_exit:
tc_af_unix_close(&received_fds[0]);
tc_af_unix_close(&received_fds[1]);
tc_af_unix_close(&pipe_fds[0][0]);
tc_af_unix_close(&pipe_fds[0][1]);
tc_af_unix_close(&pipe_fds[1][0]);
tc_af_unix_close(&pipe_fds[1][1]);
tc_af_unix_closesocket(&sockets[0]);
tc_af_unix_closesocket(&sockets[1]);
}
}
static void tc_af_unix_rights_truncation(void)
{
char payload;
int message_flags = 0;
int pipe_fds[2][2] = { { -1, -1 }, { -1, -1 } };
int received_fds[2] = { -1, -1 };
int send_fds[2];
int sockets[2] = { -1, -1 };
size_t fd_count = 0;
uassert_int_equal(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets), 0);
uassert_int_equal(pipe(pipe_fds[0]), 0);
uassert_int_equal(pipe(pipe_fds[1]), 0);
if (sockets[0] < 0 || pipe_fds[0][0] < 0 || pipe_fds[1][0] < 0)
{
goto __exit;
}
send_fds[0] = pipe_fds[0][0];
send_fds[1] = pipe_fds[1][0];
uassert_int_equal(tc_af_unix_send_rights(sockets[0], send_fds, 2, 't'), 1);
uassert_int_equal(tc_af_unix_receive_rights(sockets[1], 0, 0, received_fds,
&fd_count, &message_flags,
&payload),
1);
uassert_int_equal(fd_count, 0);
uassert_true((message_flags & MSG_CTRUNC) != 0);
__exit:
tc_af_unix_close(&received_fds[0]);
tc_af_unix_close(&pipe_fds[0][0]);
tc_af_unix_close(&pipe_fds[0][1]);
tc_af_unix_close(&pipe_fds[1][0]);
tc_af_unix_close(&pipe_fds[1][1]);
tc_af_unix_closesocket(&sockets[0]);
tc_af_unix_closesocket(&sockets[1]);
}
static void tc_af_unix_rights_peek_and_invalid(void)
{
char payload;
int error;
int invalid_fd = -1;
int message_flags = 0;
int pipe_fds[2] = { -1, -1 };
int received_fds[2] = { -1, -1 };
int result;
int sockets[2] = { -1, -1 };
size_t fd_count = 0;
uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
sockets),
0);
uassert_int_equal(pipe(pipe_fds), 0);
if (sockets[0] < 0 || pipe_fds[0] < 0)
{
goto __exit;
}
result = tc_af_unix_send_rights(sockets[0], &invalid_fd, 1, 'x');
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_int_equal(error, EBADF);
uassert_int_equal(tc_af_unix_send_rights(sockets[0], pipe_fds, 1, 'p'), 1);
fd_count = 0;
uassert_int_equal(tc_af_unix_receive_rights(
sockets[1], MSG_PEEK, CMSG_SPACE(sizeof(int)),
received_fds, &fd_count, &message_flags, &payload),
1);
uassert_int_equal(payload, 'p');
uassert_int_equal(fd_count, 0);
fd_count = 0;
message_flags = 0;
uassert_int_equal(tc_af_unix_receive_rights(
sockets[1], 0, CMSG_SPACE(sizeof(int)),
received_fds, &fd_count, &message_flags, &payload),
1);
uassert_int_equal(fd_count, 1);
__exit:
tc_af_unix_close(&received_fds[0]);
tc_af_unix_close(&pipe_fds[0]);
tc_af_unix_close(&pipe_fds[1]);
tc_af_unix_closesocket(&sockets[0]);
tc_af_unix_closesocket(&sockets[1]);
}
static void tc_af_unix_rights_reject_socket(void)
{
char payload;
int carrier[2] = { -1, -1 };
int error;
int message_flags = 0;
int passed[2] = { -1, -1 };
int received_fds[2] = { -1, -1 };
int result;
size_t fd_count = 0;
uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM, 0, carrier), 0);
uassert_int_equal(socketpair(AF_UNIX, SOCK_DGRAM, 0, passed), 0);
if (carrier[0] < 0 || passed[0] < 0)
{
goto __exit;
}
result = tc_af_unix_send_rights(carrier[0], &passed[1], 1, 's');
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_int_equal(error, EOPNOTSUPP);
result = tc_af_unix_receive_rights(carrier[1], MSG_DONTWAIT,
CMSG_SPACE(sizeof(int)), received_fds,
&fd_count, &message_flags, &payload);
error = rt_get_errno();
uassert_int_equal(result, -1);
uassert_true(error == EAGAIN || error == EWOULDBLOCK);
uassert_int_equal(fd_count, 0);
uassert_int_equal(send(passed[0], "q", 1, 0), 1);
uassert_int_equal(recv(passed[1], &payload, 1, 0), 1);
uassert_int_equal(payload, 'q');
__exit:
tc_af_unix_close(&received_fds[0]);
tc_af_unix_closesocket(&passed[0]);
tc_af_unix_closesocket(&passed[1]);
tc_af_unix_closesocket(&carrier[0]);
tc_af_unix_closesocket(&carrier[1]);
}
static void tc_af_unix_rights_testcase(void)
{
UTEST_UNIT_RUN(tc_af_unix_rights_transfer);
UTEST_UNIT_RUN(tc_af_unix_rights_truncation);
UTEST_UNIT_RUN(tc_af_unix_rights_peek_and_invalid);
UTEST_UNIT_RUN(tc_af_unix_rights_reject_socket);
}
UTEST_TC_EXPORT(tc_af_unix_rights_testcase, "components.dfs.v2.af_unix.rights",
RT_NULL, RT_NULL, 15);
+27
View File
@@ -108,6 +108,33 @@ struct musl_sockaddr
char sa_data[14];
};
struct musl_msghdr
{
void *msg_name;
socklen_t msg_namelen;
struct iovec *msg_iov;
#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
int padding1;
#endif
int msg_iovlen;
#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
int padding1;
#endif
void *msg_control;
#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
int padding2;
#endif
socklen_t msg_controllen;
#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
int padding2;
#endif
int msg_flags;
};
struct musl_ifmap {
unsigned long int mem_start;
unsigned long int mem_end;
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1,6 +1,7 @@
menu "Network"
rsource "sal/Kconfig"
rsource "af_unix/Kconfig"
rsource "netdev/Kconfig"
rsource "lwip/Kconfig"
rsource "at/Kconfig"
+42
View File
@@ -0,0 +1,42 @@
menuconfig RT_USING_AF_UNIX
bool "AF_UNIX local sockets"
depends on RT_USING_SAL
depends on SAL_USING_POSIX
depends on RT_USING_DFS_V2
default n
help
Enable pathname-based AF_UNIX SOCK_DGRAM and SOCK_STREAM sockets.
if RT_USING_AF_UNIX
config AF_UNIX_DGRAM_MAX_SIZE
int "Maximum datagram size"
range 128 65535
default 4096
config AF_UNIX_DGRAM_QUEUE_LEN
int "Datagram receive queue length"
range 1 256
default 16
config AF_UNIX_STREAM_BUFFER_SIZE
int "Stream receive buffer size"
range 256 65535
default 4096
config AF_UNIX_LISTEN_BACKLOG_MAX
int "Maximum stream listen backlog"
range 1 128
default 16
config AF_UNIX_RIGHTS_MAX
int "Maximum file descriptors per SCM_RIGHTS message"
range 1 253
default 16
config RT_AF_UNIX_USING_TESTCASES
bool "Build AF_UNIX test cases"
depends on RT_USING_UTESTCASES
default n
endif
+57
View File
@@ -0,0 +1,57 @@
# AF_UNIX local sockets
The AF_UNIX component provides pathname-based local IPC through the existing
SAL and POSIX socket APIs. It supports `SOCK_DGRAM`, `SOCK_STREAM`, and
`socketpair()` for both socket types.
## Configuration
Enable `RT_USING_AF_UNIX`. The component requires `RT_USING_SAL`,
`SAL_USING_POSIX`, and `RT_USING_DFS_V2`.
- `AF_UNIX_DGRAM_MAX_SIZE` bounds one datagram.
- `AF_UNIX_DGRAM_QUEUE_LEN` bounds queued datagrams per socket.
- `AF_UNIX_STREAM_BUFFER_SIZE` bounds each stream receive buffer.
- `AF_UNIX_LISTEN_BACKLOG_MAX` caps the stream accept queue.
- `AF_UNIX_RIGHTS_MAX` bounds the file descriptors in one `SCM_RIGHTS` send.
- `RT_AF_UNIX_USING_TESTCASES` builds the component utest suite.
## Pathname behavior
`bind()` creates an `S_IFSOCK` node through DFSv2. The mounted filesystem must
support special nodes through `create_vnode()`; tmpfs and devtmpfs support
socket nodes directly. Closing a bound socket leaves its pathname in the
filesystem. Applications should call `unlink()` before rebinding, which
matches common Unix daemon behavior.
Removing a pathname prevents new lookups. Existing stream connections and
connected datagram endpoints continue to reference their established peers.
## Descriptor passing
`sendmsg()` and `recvmsg()` support one or more file descriptors in
`SOL_SOCKET`/`SCM_RIGHTS` control messages. The queued reference remains valid
after the sender closes its descriptor. On receive, each reference is installed
as a new descriptor in the receiving process and retains the same open file
description, including its shared file position. Passing an AF_UNIX socket
descriptor is not supported and returns `EOPNOTSUPP`.
For datagram sockets, the control message is atomic with its datagram. For
stream sockets, it is associated with the first byte written by `sendmsg()` and
is delivered when a receive consumes that byte. A receive without a control
buffer discards associated descriptors and reports `MSG_CTRUNC`; `MSG_PEEK`
does not install or consume descriptors. At least one payload byte is required
when sending `SCM_RIGHTS`.
Only `SCM_RIGHTS` ancillary data is supported. Credentials and other control
message types return `EOPNOTSUPP`.
## Supported operations
The component implements bind, connect, listen, accept, send/receive,
sendto/recvfrom, sendmsg/recvmsg with descriptor passing, shutdown, socket
options, nonblocking I/O, timeouts, poll/select readiness, address queries, and
socketpair.
Linux abstract namespace addresses and credential ancillary data are not
supported. The component requires DFSv2.
+12
View File
@@ -0,0 +1,12 @@
from building import *
cwd = GetCurrentDir()
src = Glob('src/*.c')
CPPPATH = [cwd + '/include', cwd + '/src']
if GetDepend('RT_AF_UNIX_USING_TESTCASES'):
src += Glob('testcases/*.c')
group = DefineGroup('AF_UNIX', src, depend=['RT_USING_AF_UNIX'], CPPPATH=CPPPATH)
Return('group')
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef AF_UNIX_H__
#define AF_UNIX_H__
#ifdef __cplusplus
extern "C" {
#endif
int af_unix_init(void);
#ifdef __cplusplus
}
#endif
#endif /* AF_UNIX_H__ */
File diff suppressed because it is too large Load Diff
+253
View File
@@ -0,0 +1,253 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "af_unix_internal.h"
int af_unix_dgram_connect(struct af_unix_socket *sock,
const struct sockaddr *address, socklen_t length)
{
int result = 0;
struct sockaddr_un peer_address;
socklen_t peer_length;
struct af_unix_socket *peer;
peer = af_unix_namespace_lookup(address, length, SOCK_DGRAM,
&peer_address, &peer_length);
if (peer == RT_NULL)
{
return -1;
}
af_unix_lock();
if (sock->closed)
{
result = af_unix_error(EBADF);
}
else if (sock->write_shutdown)
{
result = af_unix_error(EPIPE);
}
else
{
af_unix_set_peer_locked(sock, peer);
sock->peer_address = peer_address;
sock->peer_length = peer_length;
sock->connected = 1;
}
af_unix_unlock();
af_unix_socket_put(peer);
return result;
}
static struct af_unix_socket *af_unix_dgram_target_get(
struct af_unix_socket *sock, const struct sockaddr *to,
socklen_t to_length)
{
struct af_unix_socket *peer = RT_NULL;
if (to != RT_NULL)
{
return af_unix_namespace_lookup(to, to_length, SOCK_DGRAM,
RT_NULL, RT_NULL);
}
af_unix_lock();
if (sock->connected && sock->peer != RT_NULL)
{
peer = sock->peer;
af_unix_socket_ref_locked(peer);
}
af_unix_unlock();
if (peer == RT_NULL)
{
af_unix_error(EDESTADDRREQ);
}
return peer;
}
int af_unix_dgram_send(struct af_unix_socket *sock, const void *data,
size_t size, int flags, const struct sockaddr *to,
socklen_t to_length, struct af_unix_rights *rights)
{
int error;
int result;
int nonblocking;
int timeout;
struct af_unix_socket *peer;
struct af_unix_message *message;
if (size > AF_UNIX_DGRAM_MAX_SIZE)
{
return af_unix_error(EMSGSIZE);
}
if (size != 0 && data == RT_NULL)
{
return af_unix_error(EINVAL);
}
peer = af_unix_dgram_target_get(sock, to, to_length);
if (peer == RT_NULL)
{
return -1;
}
message = (struct af_unix_message *)rt_malloc(sizeof(*message) + size);
if (message == RT_NULL)
{
af_unix_socket_put(peer);
return af_unix_error(ENOMEM);
}
rt_list_init(&message->node);
message->length = size;
message->rights = rights;
if (size != 0)
{
rt_memcpy(message->data, data, size);
}
for (;;)
{
af_unix_lock();
if (sock->closed)
{
result = af_unix_error(EBADF);
af_unix_unlock();
break;
}
if (sock->write_shutdown)
{
result = af_unix_error(EPIPE);
af_unix_unlock();
break;
}
if (peer->closed)
{
result = af_unix_error(ECONNREFUSED);
af_unix_unlock();
break;
}
if (peer->read_shutdown)
{
result = af_unix_error(EPIPE);
af_unix_unlock();
break;
}
if (peer->message_count < AF_UNIX_DGRAM_QUEUE_LEN)
{
message->source = sock->local_address;
message->source_length = sock->local_length;
rt_list_insert_before(&peer->message_queue, &message->node);
peer->message_count++;
rt_wqueue_wakeup_all(&peer->wait_queue,
(void *)(rt_ubase_t)POLLIN);
af_unix_unlock();
af_unix_socket_put(peer);
return (int)size;
}
nonblocking = af_unix_is_nonblocking(sock, flags);
timeout = sock->send_timeout;
af_unix_unlock();
if (nonblocking)
{
result = af_unix_error(EAGAIN);
break;
}
if (af_unix_wait(&peer->wait_queue, timeout) < 0)
{
result = -1;
break;
}
}
error = rt_get_errno();
rt_free(message);
af_unix_socket_put(peer);
rt_set_errno(error);
return result;
}
int af_unix_dgram_receive(struct af_unix_socket *sock, void *data,
size_t size, int flags, struct sockaddr *from,
socklen_t *from_length, rt_list_t *rights)
{
int result;
int nonblocking;
int timeout;
size_t copy_length;
struct af_unix_message *message;
if (size != 0 && data == RT_NULL)
{
return af_unix_error(EINVAL);
}
for (;;)
{
af_unix_lock();
if (sock->closed)
{
af_unix_unlock();
return af_unix_error(EBADF);
}
if (!rt_list_isempty(&sock->message_queue))
{
message = rt_list_entry(sock->message_queue.next,
struct af_unix_message, node);
copy_length = message->length;
if (copy_length > size)
{
copy_length = size;
}
if (copy_length != 0)
{
rt_memcpy(data, message->data, copy_length);
}
result = af_unix_address_copy(from, from_length,
&message->source,
message->source_length);
if (result < 0)
{
af_unix_unlock();
return result;
}
if ((flags & MSG_PEEK) == 0)
{
rt_list_remove(&message->node);
sock->message_count--;
if (message->rights != RT_NULL)
{
rt_list_insert_before(rights, &message->rights->node);
message->rights = RT_NULL;
}
af_unix_wakeup_writable_locked(sock);
rt_free(message);
}
af_unix_unlock();
return (int)copy_length;
}
if (sock->read_shutdown)
{
af_unix_unlock();
return 0;
}
nonblocking = af_unix_is_nonblocking(sock, flags);
timeout = sock->receive_timeout;
af_unix_unlock();
if (nonblocking)
{
return af_unix_error(EAGAIN);
}
if (af_unix_wait(&sock->wait_queue, timeout) < 0)
{
return -1;
}
}
}
@@ -0,0 +1,164 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef AF_UNIX_INTERNAL_H__
#define AF_UNIX_INTERNAL_H__
#include <rtthread.h>
#include <dfs.h>
#include <dfs_file.h>
#include <poll.h>
#include <sal_low_lvl.h>
#include <sal_socket.h>
#ifndef AF_UNIX_DGRAM_MAX_SIZE
#define AF_UNIX_DGRAM_MAX_SIZE 4096
#endif
#ifndef AF_UNIX_DGRAM_QUEUE_LEN
#define AF_UNIX_DGRAM_QUEUE_LEN 16
#endif
#ifndef AF_UNIX_STREAM_BUFFER_SIZE
#define AF_UNIX_STREAM_BUFFER_SIZE 4096
#endif
#ifndef AF_UNIX_LISTEN_BACKLOG_MAX
#define AF_UNIX_LISTEN_BACKLOG_MAX 16
#endif
#ifndef AF_UNIX_RIGHTS_MAX
#define AF_UNIX_RIGHTS_MAX 16
#endif
#define AF_UNIX_PATH_MAX ((int)sizeof(((struct sockaddr_un *)0)->sun_path))
struct af_unix_rights
{
rt_list_t node;
rt_uint64_t offset;
size_t count;
struct dfs_file *files[1];
};
struct af_unix_message
{
rt_list_t node;
size_t length;
struct sockaddr_un source;
socklen_t source_length;
struct af_unix_rights *rights;
char data[1];
};
struct af_unix_socket
{
int handle;
int type;
int flags;
int ref_count;
int closed;
int bound;
int connected;
int listening;
int read_shutdown;
int write_shutdown;
int socket_error;
int receive_timeout;
int send_timeout;
struct sockaddr_un local_address;
socklen_t local_length;
struct sockaddr_un peer_address;
socklen_t peer_length;
struct af_unix_socket *peer;
void *namespace_entry;
rt_wqueue_t wait_queue;
rt_list_t message_queue;
rt_size_t message_count;
char *stream_buffer;
rt_size_t stream_head;
rt_size_t stream_length;
rt_uint64_t stream_read_offset;
rt_uint64_t stream_write_offset;
rt_list_t rights_queue;
rt_list_t pending_queue;
rt_list_t pending_node;
int pending;
int backlog;
int pending_count;
};
void af_unix_lock(void);
void af_unix_unlock(void);
int af_unix_error(int error);
int af_unix_is_nonblocking(const struct af_unix_socket *sock, int flags);
int af_unix_wait(rt_wqueue_t *queue, int timeout);
int af_unix_rights_create(const struct msghdr *message,
struct af_unix_rights **rights);
void af_unix_rights_release(struct af_unix_rights *rights);
void af_unix_rights_list_release(rt_list_t *list);
void af_unix_rights_defer_locked(struct af_unix_rights *rights);
void af_unix_rights_list_defer_locked(rt_list_t *list);
void af_unix_rights_drain(void);
int af_unix_rights_deliver(rt_list_t *list, struct msghdr *message);
int af_unix_rights_init(void);
struct af_unix_socket *af_unix_socket_create_locked(int type);
struct af_unix_socket *af_unix_socket_get(int handle);
void af_unix_socket_ref_locked(struct af_unix_socket *sock);
void af_unix_socket_unref_locked(struct af_unix_socket *sock);
void af_unix_socket_put(struct af_unix_socket *sock);
int af_unix_handle_alloc_locked(struct af_unix_socket *sock);
void af_unix_handle_remove_locked(struct af_unix_socket *sock);
void af_unix_socket_close_locked(struct af_unix_socket *sock);
void af_unix_wakeup_writable_locked(struct af_unix_socket *sock);
int af_unix_connect_peers_locked(struct af_unix_socket *first,
struct af_unix_socket *second);
void af_unix_set_peer_locked(struct af_unix_socket *sock,
struct af_unix_socket *peer);
int af_unix_address_parse(const struct sockaddr *address, socklen_t length,
char path[AF_UNIX_PATH_MAX]);
void af_unix_address_set(struct sockaddr_un *address, socklen_t *length,
const char *path);
int af_unix_address_copy(struct sockaddr *address, socklen_t *length,
const struct sockaddr_un *source,
socklen_t source_length);
int af_unix_namespace_bind(struct af_unix_socket *sock,
const struct sockaddr *address, socklen_t length);
struct af_unix_socket *af_unix_namespace_lookup(
const struct sockaddr *address, socklen_t length, int type,
struct sockaddr_un *normalized_address, socklen_t *normalized_length);
void af_unix_namespace_detach_locked(struct af_unix_socket *sock);
int af_unix_namespace_init(void);
int af_unix_dgram_connect(struct af_unix_socket *sock,
const struct sockaddr *address, socklen_t length);
int af_unix_dgram_send(struct af_unix_socket *sock, const void *data,
size_t size, int flags, const struct sockaddr *to,
socklen_t to_length, struct af_unix_rights *rights);
int af_unix_dgram_receive(struct af_unix_socket *sock, void *data,
size_t size, int flags, struct sockaddr *from,
socklen_t *from_length, rt_list_t *rights);
int af_unix_stream_listen(struct af_unix_socket *sock, int backlog);
int af_unix_stream_connect(struct af_unix_socket *sock,
const struct sockaddr *address, socklen_t length);
int af_unix_stream_accept(struct af_unix_socket *sock,
struct sockaddr *address, socklen_t *length);
int af_unix_stream_send(struct af_unix_socket *sock, const void *data,
size_t size, int flags,
struct af_unix_rights *rights);
int af_unix_stream_receive(struct af_unix_socket *sock, void *data,
size_t size, int flags, rt_list_t *rights);
int af_unix_stream_shutdown(struct af_unix_socket *sock, int how);
#endif /* AF_UNIX_INTERNAL_H__ */
@@ -0,0 +1,292 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include <string.h>
#include "af_unix_internal.h"
struct af_unix_namespace_entry
{
rt_list_t node;
char path[AF_UNIX_PATH_MAX];
struct af_unix_socket *sock;
};
static rt_list_t af_unix_namespace;
static struct af_unix_namespace_entry *af_unix_namespace_find_locked(
const char *path)
{
struct af_unix_namespace_entry *entry;
rt_list_for_each_entry(entry, &af_unix_namespace, node)
{
if (rt_strcmp(entry->path, path) == 0)
{
return entry;
}
}
return RT_NULL;
}
int af_unix_address_parse(const struct sockaddr *address, socklen_t length,
char path[AF_UNIX_PATH_MAX])
{
int index;
int path_length;
char input_path[AF_UNIX_PATH_MAX];
char *normalized;
const struct sockaddr_un *unix_address;
const int path_offset = (int)offsetof(struct sockaddr_un, sun_path);
if (address == RT_NULL || length <= (socklen_t)path_offset ||
length > sizeof(struct sockaddr_un))
{
return af_unix_error(EINVAL);
}
unix_address = (const struct sockaddr_un *)address;
if (unix_address->sa_family != AF_UNIX)
{
return af_unix_error(EAFNOSUPPORT);
}
path_length = (int)length - path_offset;
if (path_length > AF_UNIX_PATH_MAX)
{
path_length = AF_UNIX_PATH_MAX;
}
for (index = 0; index < path_length; index++)
{
if (unix_address->sun_path[index] == '\0')
{
break;
}
}
if (index == 0)
{
return af_unix_error(EINVAL);
}
if (index == path_length || index >= AF_UNIX_PATH_MAX)
{
return af_unix_error(ENAMETOOLONG);
}
rt_memcpy(input_path, unix_address->sun_path, index);
input_path[index] = '\0';
normalized = dfs_normalize_path(RT_NULL, input_path);
if (normalized == RT_NULL)
{
return af_unix_error(ENOMEM);
}
if (rt_strlen(normalized) >= AF_UNIX_PATH_MAX)
{
rt_free(normalized);
return af_unix_error(ENAMETOOLONG);
}
rt_strncpy(path, normalized, AF_UNIX_PATH_MAX);
path[AF_UNIX_PATH_MAX - 1] = '\0';
rt_free(normalized);
return 0;
}
void af_unix_address_set(struct sockaddr_un *address, socklen_t *length,
const char *path)
{
size_t path_length = 0;
rt_memset(address, 0, sizeof(*address));
address->sa_family = AF_UNIX;
if (path != RT_NULL)
{
path_length = rt_strlen(path);
if (path_length >= sizeof(address->sun_path))
{
path_length = sizeof(address->sun_path) - 1;
}
rt_memcpy(address->sun_path, path, path_length);
}
*length = (socklen_t)(offsetof(struct sockaddr_un, sun_path) +
path_length + (path != RT_NULL ? 1 : 0));
}
int af_unix_address_copy(struct sockaddr *address, socklen_t *length,
const struct sockaddr_un *source,
socklen_t source_length)
{
socklen_t copy_length;
if (address == RT_NULL)
{
return 0;
}
if (length == RT_NULL)
{
return af_unix_error(EINVAL);
}
copy_length = *length;
if (copy_length > source_length)
{
copy_length = source_length;
}
if (copy_length > 0)
{
rt_memcpy(address, source, copy_length);
}
*length = source_length;
return 0;
}
int af_unix_namespace_bind(struct af_unix_socket *sock,
const struct sockaddr *address, socklen_t length)
{
int result;
char path[AF_UNIX_PATH_MAX];
struct af_unix_namespace_entry *entry;
struct af_unix_namespace_entry *new_entry;
result = af_unix_address_parse(address, length, path);
if (result < 0)
{
return result;
}
af_unix_lock();
if (sock->closed)
{
af_unix_unlock();
return af_unix_error(EBADF);
}
if (sock->bound)
{
af_unix_unlock();
return af_unix_error(EINVAL);
}
af_unix_unlock();
result = dfs_file_mknod(path, FT_SOCKET,
S_IRWXU | S_IRWXG | S_IRWXO);
if (result < 0)
{
return af_unix_error(result == -EEXIST ? EADDRINUSE : -result);
}
new_entry = (struct af_unix_namespace_entry *)rt_calloc(1,
sizeof(*new_entry));
if (new_entry == RT_NULL)
{
(void)dfs_file_unlink(path);
return af_unix_error(ENOMEM);
}
rt_strncpy(new_entry->path, path, sizeof(new_entry->path));
new_entry->path[sizeof(new_entry->path) - 1] = '\0';
rt_list_init(&new_entry->node);
af_unix_lock();
if (sock->closed || sock->bound)
{
af_unix_unlock();
rt_free(new_entry);
(void)dfs_file_unlink(path);
return af_unix_error(sock->closed ? EBADF : EINVAL);
}
entry = af_unix_namespace_find_locked(path);
if (entry == RT_NULL)
{
entry = new_entry;
new_entry = RT_NULL;
rt_list_insert_before(&af_unix_namespace, &entry->node);
}
entry->sock = sock;
sock->namespace_entry = entry;
sock->bound = 1;
af_unix_address_set(&sock->local_address, &sock->local_length, path);
af_unix_unlock();
if (new_entry != RT_NULL)
{
rt_free(new_entry);
}
return 0;
}
struct af_unix_socket *af_unix_namespace_lookup(
const struct sockaddr *address, socklen_t length, int type,
struct sockaddr_un *normalized_address, socklen_t *normalized_length)
{
int result;
char path[AF_UNIX_PATH_MAX];
struct stat file_stat;
struct af_unix_socket *sock = RT_NULL;
struct af_unix_namespace_entry *entry;
result = af_unix_address_parse(address, length, path);
if (result < 0)
{
return RT_NULL;
}
result = dfs_file_stat(path, &file_stat);
if (result < 0)
{
af_unix_error(ENOENT);
return RT_NULL;
}
if (!S_ISSOCK(file_stat.st_mode))
{
af_unix_error(EPROTOTYPE);
return RT_NULL;
}
af_unix_lock();
entry = af_unix_namespace_find_locked(path);
if (entry != RT_NULL && entry->sock != RT_NULL && !entry->sock->closed)
{
if (entry->sock->type == type)
{
sock = entry->sock;
af_unix_socket_ref_locked(sock);
}
else
{
af_unix_error(EPROTOTYPE);
}
}
else
{
af_unix_error(ECONNREFUSED);
}
af_unix_unlock();
if (sock != RT_NULL && normalized_address != RT_NULL &&
normalized_length != RT_NULL)
{
af_unix_address_set(normalized_address, normalized_length, path);
}
return sock;
}
void af_unix_namespace_detach_locked(struct af_unix_socket *sock)
{
struct af_unix_namespace_entry *entry;
entry = (struct af_unix_namespace_entry *)sock->namespace_entry;
if (entry != RT_NULL && entry->sock == sock)
{
entry->sock = RT_NULL;
rt_list_remove(&entry->node);
rt_free(entry);
}
sock->namespace_entry = RT_NULL;
}
int af_unix_namespace_init(void)
{
rt_list_init(&af_unix_namespace);
return RT_EOK;
}
+364
View File
@@ -0,0 +1,364 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <string.h>
#include <dfs_net.h>
#include "af_unix_internal.h"
static struct rt_mutex af_unix_deferred_lock;
static rt_list_t af_unix_deferred_rights;
static size_t af_unix_cmsg_next(size_t offset, size_t length,
size_t control_length)
{
size_t next;
next = offset + CMSG_ALIGN(length);
if (next > control_length)
{
next = offset + length;
}
return next;
}
static int af_unix_file_is_unix_socket(const struct dfs_file *file)
{
int sal_handle;
struct sal_socket *sal_sock;
if (file == RT_NULL || file->vnode == RT_NULL ||
file->vnode->type != FT_SOCKET)
{
return 0;
}
if (file->vnode->fops != dfs_net_get_fops() &&
file->fops != dfs_net_get_fops())
{
return 0;
}
sal_handle = (int)(size_t)file->vnode->data;
sal_sock = sal_get_socket(sal_handle);
return (sal_sock != RT_NULL && sal_sock->domain == AF_UNIX);
}
int af_unix_rights_create(const struct msghdr *message,
struct af_unix_rights **out_rights)
{
int fds[AF_UNIX_RIGHTS_MAX];
int result;
size_t count = 0;
size_t file_index;
size_t offset = 0;
const char *control;
struct af_unix_rights *rights;
*out_rights = RT_NULL;
if (message->msg_controllen == 0)
{
return 0;
}
if (message->msg_control == RT_NULL)
{
return af_unix_error(EINVAL);
}
control = (const char *)message->msg_control;
while (offset < message->msg_controllen)
{
size_t data_length;
size_t fd_count;
size_t index;
size_t next;
const struct cmsghdr *cmsg;
if (message->msg_controllen - offset < sizeof(*cmsg))
{
return af_unix_error(EINVAL);
}
cmsg = (const struct cmsghdr *)(control + offset);
if (cmsg->cmsg_len < sizeof(*cmsg) ||
cmsg->cmsg_len > message->msg_controllen - offset)
{
return af_unix_error(EINVAL);
}
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
{
return af_unix_error(EOPNOTSUPP);
}
data_length = cmsg->cmsg_len - sizeof(*cmsg);
if (data_length == 0 || data_length % sizeof(int) != 0)
{
return af_unix_error(EINVAL);
}
fd_count = data_length / sizeof(int);
if (fd_count > AF_UNIX_RIGHTS_MAX - count)
{
return af_unix_error(EMSGSIZE);
}
for (index = 0; index < fd_count; index++)
{
rt_memcpy(&fds[count],
(const char *)CMSG_DATA(cmsg) + index * sizeof(int),
sizeof(int));
count++;
}
next = af_unix_cmsg_next(offset, cmsg->cmsg_len,
message->msg_controllen);
if (next <= offset || next > message->msg_controllen)
{
return af_unix_error(EINVAL);
}
offset = next;
}
rights = (struct af_unix_rights *)rt_calloc(
1, sizeof(*rights) + (count - 1) * sizeof(rights->files[0]));
if (rights == RT_NULL)
{
return af_unix_error(ENOMEM);
}
rt_list_init(&rights->node);
rights->count = count;
result = dfs_file_get_refs(fds, count, rights->files);
if (result < 0)
{
rt_free(rights);
return af_unix_error(-result);
}
/* Passing AF_UNIX sockets can form an unreclaimable reference
* cycle between the in-flight right and the destination queue. */
for (file_index = 0; file_index < count; file_index++)
{
if (af_unix_file_is_unix_socket(rights->files[file_index]))
{
af_unix_rights_release(rights);
return af_unix_error(EOPNOTSUPP);
}
}
*out_rights = rights;
return 0;
}
void af_unix_rights_release(struct af_unix_rights *rights)
{
size_t index;
if (rights == RT_NULL)
{
return;
}
for (index = 0; index < rights->count; index++)
{
if (rights->files[index] != RT_NULL)
{
dfs_file_put_ref(rights->files[index]);
}
}
rt_free(rights);
}
void af_unix_rights_list_release(rt_list_t *list)
{
while (!rt_list_isempty(list))
{
struct af_unix_rights *rights;
rights = rt_list_entry(list->next, struct af_unix_rights, node);
rt_list_remove(&rights->node);
af_unix_rights_release(rights);
}
}
void af_unix_rights_defer_locked(struct af_unix_rights *rights)
{
if (rights == RT_NULL)
{
return;
}
rt_mutex_take(&af_unix_deferred_lock, RT_WAITING_FOREVER);
rt_list_insert_before(&af_unix_deferred_rights, &rights->node);
rt_mutex_release(&af_unix_deferred_lock);
}
void af_unix_rights_list_defer_locked(rt_list_t *list)
{
while (!rt_list_isempty(list))
{
struct af_unix_rights *rights;
rights = rt_list_entry(list->next, struct af_unix_rights, node);
rt_list_remove(&rights->node);
af_unix_rights_defer_locked(rights);
}
}
void af_unix_rights_drain(void)
{
rt_list_t pending;
rt_list_init(&pending);
rt_mutex_take(&af_unix_deferred_lock, RT_WAITING_FOREVER);
while (!rt_list_isempty(&af_unix_deferred_rights))
{
rt_list_t *node = af_unix_deferred_rights.next;
rt_list_remove(node);
rt_list_insert_before(&pending, node);
}
rt_mutex_release(&af_unix_deferred_lock);
af_unix_rights_list_release(&pending);
}
static size_t af_unix_rights_count(const rt_list_t *list)
{
size_t count = 0;
const rt_list_t *node;
for (node = list->next; node != list; node = node->next)
{
const struct af_unix_rights *rights;
rights = rt_list_entry(node, struct af_unix_rights, node);
count += rights->count;
}
return count;
}
static size_t af_unix_rights_capacity(const struct msghdr *message,
size_t total)
{
size_t capacity;
if (message->msg_control == RT_NULL ||
message->msg_controllen < CMSG_SPACE(sizeof(int)))
{
return 0;
}
capacity = (message->msg_controllen - sizeof(struct cmsghdr)) /
sizeof(int);
if (capacity > total)
{
capacity = total;
}
while (capacity != 0 &&
CMSG_SPACE(capacity * sizeof(int)) > message->msg_controllen)
{
capacity--;
}
return capacity;
}
int af_unix_rights_deliver(rt_list_t *list, struct msghdr *message)
{
int install_result = 0;
int *fds = RT_NULL;
size_t capacity;
size_t delivered = 0;
size_t index;
size_t total;
struct dfs_file **files = RT_NULL;
struct cmsghdr *cmsg;
rt_list_t *node;
total = af_unix_rights_count(list);
capacity = af_unix_rights_capacity(message, total);
if (capacity != 0)
{
files = (struct dfs_file **)rt_malloc(capacity * sizeof(*files));
fds = (int *)rt_malloc(capacity * sizeof(*fds));
if (files == RT_NULL || fds == RT_NULL)
{
capacity = 0;
}
}
for (node = list->next; node != list && delivered < capacity;
node = node->next)
{
struct af_unix_rights *rights;
rights = rt_list_entry(node, struct af_unix_rights, node);
for (index = 0; index < rights->count && delivered < capacity; index++)
{
files[delivered] = rights->files[index];
delivered++;
}
}
if (delivered != 0)
{
install_result = dfs_file_install_refs(files, delivered, fds);
if (install_result < 0)
{
delivered = 0;
}
}
if (delivered != 0)
{
size_t transferred = 0;
for (node = list->next; node != list && transferred < delivered;
node = node->next)
{
struct af_unix_rights *rights;
rights = rt_list_entry(node, struct af_unix_rights, node);
for (index = 0;
index < rights->count && transferred < delivered; index++)
{
rights->files[index] = RT_NULL;
transferred++;
}
}
cmsg = (struct cmsghdr *)message->msg_control;
cmsg->cmsg_len = CMSG_LEN(delivered * sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
rt_memcpy(CMSG_DATA(cmsg), fds, delivered * sizeof(int));
message->msg_controllen = CMSG_SPACE(delivered * sizeof(int));
}
else
{
message->msg_controllen = 0;
}
if (delivered < total)
{
message->msg_flags |= MSG_CTRUNC;
}
if (files != RT_NULL)
{
rt_free(files);
}
if (fds != RT_NULL)
{
rt_free(fds);
}
af_unix_rights_list_release(list);
return install_result;
}
int af_unix_rights_init(void)
{
rt_list_init(&af_unix_deferred_rights);
return rt_mutex_init(&af_unix_deferred_lock, "afuright",
RT_IPC_FLAG_PRIO);
}
+483
View File
@@ -0,0 +1,483 @@
/*
* Copyright (c) 2006-2026, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "af_unix_internal.h"
static void af_unix_stream_buffer_write_locked(struct af_unix_socket *sock,
const char *data,
size_t length)
{
size_t first_length;
size_t tail;
tail = (sock->stream_head + sock->stream_length) %
AF_UNIX_STREAM_BUFFER_SIZE;
first_length = AF_UNIX_STREAM_BUFFER_SIZE - tail;
if (first_length > length)
{
first_length = length;
}
rt_memcpy(sock->stream_buffer + tail, data, first_length);
if (length > first_length)
{
rt_memcpy(sock->stream_buffer, data + first_length,
length - first_length);
}
sock->stream_length += length;
sock->stream_write_offset += length;
}
static void af_unix_stream_buffer_read_locked(struct af_unix_socket *sock,
char *data, size_t length,
int peek)
{
size_t first_length;
first_length = AF_UNIX_STREAM_BUFFER_SIZE - sock->stream_head;
if (first_length > length)
{
first_length = length;
}
rt_memcpy(data, sock->stream_buffer + sock->stream_head, first_length);
if (length > first_length)
{
rt_memcpy(data + first_length, sock->stream_buffer,
length - first_length);
}
if (!peek)
{
sock->stream_head = (sock->stream_head + length) %
AF_UNIX_STREAM_BUFFER_SIZE;
sock->stream_length -= length;
sock->stream_read_offset += length;
}
}
static void af_unix_stream_collect_rights_locked(struct af_unix_socket *sock,
rt_list_t *rights)
{
while (!rt_list_isempty(&sock->rights_queue))
{
struct af_unix_rights *entry;
entry = rt_list_entry(sock->rights_queue.next,
struct af_unix_rights, node);
if (entry->offset >= sock->stream_read_offset)
{
break;
}
rt_list_remove(&entry->node);
rt_list_insert_before(rights, &entry->node);
}
}
int af_unix_stream_listen(struct af_unix_socket *sock, int backlog)
{
int result = 0;
af_unix_lock();
if (sock->closed)
{
result = af_unix_error(EBADF);
}
else if (sock->type != SOCK_STREAM)
{
result = af_unix_error(EOPNOTSUPP);
}
else if (!sock->bound)
{
result = af_unix_error(EINVAL);
}
else if (sock->connected)
{
result = af_unix_error(EINVAL);
}
else
{
if (backlog < 1)
{
backlog = 1;
}
if (backlog > AF_UNIX_LISTEN_BACKLOG_MAX)
{
backlog = AF_UNIX_LISTEN_BACKLOG_MAX;
}
sock->backlog = backlog;
sock->listening = 1;
}
af_unix_unlock();
return result;
}
int af_unix_stream_connect(struct af_unix_socket *sock,
const struct sockaddr *address, socklen_t length)
{
int handle;
int nonblocking;
int timeout;
int result = -1;
struct sockaddr_un peer_address;
socklen_t peer_length;
struct af_unix_socket *listener;
struct af_unix_socket *accepted;
listener = af_unix_namespace_lookup(address, length, SOCK_STREAM,
&peer_address, &peer_length);
if (listener == RT_NULL)
{
return -1;
}
for (;;)
{
af_unix_lock();
if (sock->closed)
{
result = af_unix_error(EBADF);
af_unix_unlock();
break;
}
if (sock->connected)
{
result = af_unix_error(EISCONN);
af_unix_unlock();
break;
}
if (listener->closed || !listener->listening)
{
result = af_unix_error(ECONNREFUSED);
af_unix_unlock();
break;
}
if (listener->pending_count < listener->backlog)
{
accepted = af_unix_socket_create_locked(SOCK_STREAM);
if (accepted == RT_NULL)
{
result = af_unix_error(ENOMEM);
af_unix_unlock();
break;
}
handle = af_unix_handle_alloc_locked(accepted);
if (handle < 0)
{
af_unix_socket_unref_locked(accepted);
result = af_unix_error(EMFILE);
af_unix_unlock();
break;
}
result = af_unix_connect_peers_locked(sock, accepted);
if (result < 0)
{
af_unix_handle_remove_locked(accepted);
af_unix_unlock();
break;
}
sock->peer_address = peer_address;
sock->peer_length = peer_length;
accepted->local_address = listener->local_address;
accepted->local_length = listener->local_length;
accepted->peer_address = sock->local_address;
accepted->peer_length = sock->local_length;
accepted->pending = 1;
rt_list_insert_before(&listener->pending_queue,
&accepted->pending_node);
listener->pending_count++;
rt_wqueue_wakeup_all(&listener->wait_queue,
(void *)(rt_ubase_t)POLLIN);
af_unix_unlock();
result = 0;
break;
}
nonblocking = af_unix_is_nonblocking(sock, 0);
timeout = sock->send_timeout;
af_unix_unlock();
if (nonblocking)
{
result = af_unix_error(EAGAIN);
break;
}
if (af_unix_wait(&listener->wait_queue, timeout) < 0)
{
result = -1;
break;
}
}
af_unix_socket_put(listener);
return result;
}
int af_unix_stream_accept(struct af_unix_socket *sock,
struct sockaddr *address, socklen_t *length)
{
int handle;
int nonblocking;
int timeout;
int result;
struct af_unix_socket *accepted;
for (;;)
{
af_unix_lock();
if (sock->closed)
{
af_unix_unlock();
return af_unix_error(EBADF);
}
if (!sock->listening)
{
af_unix_unlock();
return af_unix_error(EINVAL);
}
if (!rt_list_isempty(&sock->pending_queue))
{
accepted = rt_list_entry(sock->pending_queue.next,
struct af_unix_socket, pending_node);
result = af_unix_address_copy(address, length,
&accepted->peer_address,
accepted->peer_length);
if (result < 0)
{
af_unix_unlock();
return result;
}
rt_list_remove(&accepted->pending_node);
accepted->pending = 0;
sock->pending_count--;
handle = accepted->handle;
rt_wqueue_wakeup_all(&sock->wait_queue,
(void *)(rt_ubase_t)POLLOUT);
af_unix_unlock();
return handle;
}
nonblocking = af_unix_is_nonblocking(sock, 0);
timeout = sock->receive_timeout;
af_unix_unlock();
if (nonblocking)
{
return af_unix_error(EAGAIN);
}
if (af_unix_wait(&sock->wait_queue, timeout) < 0)
{
return -1;
}
}
}
int af_unix_stream_send(struct af_unix_socket *sock, const void *data,
size_t size, int flags,
struct af_unix_rights *rights)
{
int nonblocking;
int timeout;
size_t copy_length;
size_t space;
struct af_unix_socket *peer;
if (size != 0 && data == RT_NULL)
{
return af_unix_error(EINVAL);
}
if (size == 0)
{
return 0;
}
for (;;)
{
af_unix_lock();
if (sock->closed)
{
af_unix_unlock();
return af_unix_error(EBADF);
}
if (sock->write_shutdown)
{
af_unix_unlock();
return af_unix_error(EPIPE);
}
peer = sock->peer;
if (!sock->connected || peer == RT_NULL)
{
af_unix_unlock();
return af_unix_error(ENOTCONN);
}
if (peer->closed || peer->read_shutdown)
{
af_unix_unlock();
return af_unix_error(EPIPE);
}
space = AF_UNIX_STREAM_BUFFER_SIZE - peer->stream_length;
if (space != 0)
{
copy_length = size;
if (copy_length > space)
{
copy_length = space;
}
if (rights != RT_NULL)
{
rights->offset = peer->stream_write_offset;
rt_list_insert_before(&peer->rights_queue, &rights->node);
}
af_unix_stream_buffer_write_locked(peer, (const char *)data,
copy_length);
rt_wqueue_wakeup_all(&peer->wait_queue,
(void *)(rt_ubase_t)POLLIN);
af_unix_unlock();
return (int)copy_length;
}
nonblocking = af_unix_is_nonblocking(sock, flags);
timeout = sock->send_timeout;
af_unix_socket_ref_locked(peer);
af_unix_unlock();
if (nonblocking)
{
af_unix_socket_put(peer);
return af_unix_error(EAGAIN);
}
if (af_unix_wait(&peer->wait_queue, timeout) < 0)
{
af_unix_socket_put(peer);
return -1;
}
af_unix_socket_put(peer);
}
}
int af_unix_stream_receive(struct af_unix_socket *sock, void *data,
size_t size, int flags, rt_list_t *rights)
{
int nonblocking;
int timeout;
int wait_all;
size_t copy_length;
size_t received = 0;
struct af_unix_socket *peer;
if (size != 0 && data == RT_NULL)
{
return af_unix_error(EINVAL);
}
if (size == 0)
{
return 0;
}
wait_all = ((flags & MSG_WAITALL) != 0 && (flags & MSG_PEEK) == 0);
for (;;)
{
af_unix_lock();
if (sock->closed)
{
af_unix_unlock();
return received != 0 ? (int)received : af_unix_error(EBADF);
}
if (sock->read_shutdown)
{
af_unix_unlock();
return (int)received;
}
if (sock->stream_length != 0)
{
copy_length = size - received;
if (copy_length > sock->stream_length)
{
copy_length = sock->stream_length;
}
af_unix_stream_buffer_read_locked(sock,
(char *)data + received,
copy_length,
(flags & MSG_PEEK) != 0);
received += copy_length;
if ((flags & MSG_PEEK) == 0)
{
af_unix_stream_collect_rights_locked(sock, rights);
af_unix_wakeup_writable_locked(sock);
}
if (!wait_all || received == size)
{
af_unix_unlock();
return (int)received;
}
}
peer = sock->peer;
if (peer == RT_NULL || peer->closed || peer->write_shutdown)
{
af_unix_unlock();
return (int)received;
}
nonblocking = af_unix_is_nonblocking(sock, flags);
timeout = sock->receive_timeout;
af_unix_unlock();
if (nonblocking)
{
return received != 0 ? (int)received : af_unix_error(EAGAIN);
}
if (af_unix_wait(&sock->wait_queue, timeout) < 0)
{
return received != 0 ? (int)received : -1;
}
}
}
int af_unix_stream_shutdown(struct af_unix_socket *sock, int how)
{
int result = 0;
struct af_unix_socket *peer;
if (how < SHUT_RD || how > SHUT_RDWR)
{
return af_unix_error(EINVAL);
}
af_unix_lock();
if (sock->closed)
{
result = af_unix_error(EBADF);
}
else if (!sock->connected)
{
result = af_unix_error(ENOTCONN);
}
else
{
if (how == SHUT_RD || how == SHUT_RDWR)
{
sock->read_shutdown = 1;
sock->stream_head = 0;
sock->stream_read_offset += sock->stream_length;
sock->stream_length = 0;
af_unix_rights_list_defer_locked(&sock->rights_queue);
}
if (how == SHUT_WR || how == SHUT_RDWR)
{
sock->write_shutdown = 1;
}
rt_wqueue_wakeup_all(&sock->wait_queue,
(void *)(rt_ubase_t)(POLLIN | POLLOUT));
peer = sock->peer;
if (peer != RT_NULL)
{
rt_wqueue_wakeup_all(&peer->wait_queue,
(void *)(rt_ubase_t)(POLLIN | POLLOUT));
}
}
af_unix_unlock();
return result;
}
File diff suppressed because it is too large Load Diff
+5
View File
@@ -48,6 +48,7 @@ typedef uint32_t socklen_t;
struct sockaddr;
struct msghdr;
struct addrinfo;
struct sal_proto_family;
struct sal_socket
{
uint32_t magic; /* SAL socket magic word */
@@ -58,6 +59,7 @@ struct sal_socket
int protocol;
struct netdev *netdev; /* SAL network interface device */
const struct sal_proto_family *protocol_family; /* selected protocol provider */
void *user_data; /* user-specific data */
#ifdef SAL_USING_TLS
@@ -109,6 +111,9 @@ struct sal_proto_family
/* SAL(Socket Abstraction Layer) initialize */
int sal_init(void);
/* Register and find protocol providers which do not require a netdev. */
int sal_proto_family_register(const struct sal_proto_family *pf);
const struct sal_proto_family *sal_proto_family_find(int family);
/* Get SAL socket object by socket descriptor */
struct sal_socket *sal_get_socket(int sock);
+3
View File
@@ -119,6 +119,9 @@ typedef uint16_t in_port_t;
#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */
#define MSG_MORE 0x10 /* Sender will send more */
/* Output-only flags returned through struct msghdr. */
#define MSG_CTRUNC 0x08 /* Control data was discarded due to truncation */
#define MSG_ERRQUEUE 0x2000 /* Fetch message from error queue */
#define MSG_CONFIRM 0x0800 /* Confirm path validity */
+20
View File
@@ -725,7 +725,11 @@ int closesocket(int s)
return -1;
}
#ifdef RT_USING_DFS_V2
if (dfs_file_close(d) == 0)
#else
if (sal_closesocket(socket) == 0)
#endif
{
error = 0;
}
@@ -771,8 +775,19 @@ RTM_EXPORT(closesocket);
int socketpair(int domain, int type, int protocol, int *fds)
{
rt_err_t ret = 0;
int nonblocking = 0;
int sock_fds[2];
if ((type & SOCK_CLOEXEC) != 0)
{
type &= ~SOCK_CLOEXEC;
}
if ((type & SOCK_NONBLOCK) != 0)
{
nonblocking = 1;
type &= ~SOCK_NONBLOCK;
}
fds[0] = socket(domain, type, protocol);
if (fds[0] < 0)
{
@@ -799,6 +814,11 @@ int socketpair(int domain, int type, int protocol, int *fds)
closesocket(fds[0]);
closesocket(fds[1]);
}
else if (nonblocking)
{
(void)fcntl(fds[0], F_SETFL, O_NONBLOCK);
(void)fcntl(fds[1], F_SETFL, O_NONBLOCK);
}
return ret;
}
File diff suppressed because it is too large Load Diff