mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-10-02 14:23:19 +08:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
+377
-154
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
menu "Network"
|
||||
|
||||
rsource "sal/Kconfig"
|
||||
rsource "af_unix/Kconfig"
|
||||
rsource "netdev/Kconfig"
|
||||
rsource "lwip/Kconfig"
|
||||
rsource "at/Kconfig"
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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')
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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__ */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user