#include <sys/cdefs.h>
__RCSID("$NetBSD: t_socketpair.c,v 1.3 2025/07/17 19:50:40 kre Exp $");
#include <atf-c.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#ifndef SOCK_CLOFORK
#define SOCK_CLOFORK 0
#endif
#ifndef FD_CLOFORK
#define FD_CLOFORK 0
#endif
static void
connected(int fd)
{
struct sockaddr_un addr;
socklen_t len = (socklen_t)sizeof(addr);
ATF_REQUIRE(getpeername(fd, (struct sockaddr*)(void *)&addr,
&len) == 0);
}
static void
run(int flags)
{
int fd[2], i;
while ((i = open("/", O_RDONLY)) < 3)
ATF_REQUIRE(i != -1);
ATF_REQUIRE(closefrom(3) != -1);
ATF_REQUIRE(socketpair(AF_UNIX, SOCK_DGRAM | flags, 0, fd) == 0);
ATF_REQUIRE(fd[0] == 3);
ATF_REQUIRE(fd[1] == 4);
connected(fd[0]);
connected(fd[1]);
if (flags & SOCK_CLOEXEC) {
ATF_REQUIRE((fcntl(fd[0], F_GETFD) & FD_CLOEXEC) != 0);
ATF_REQUIRE((fcntl(fd[1], F_GETFD) & FD_CLOEXEC) != 0);
} else {
ATF_REQUIRE((fcntl(fd[0], F_GETFD) & FD_CLOEXEC) == 0);
ATF_REQUIRE((fcntl(fd[1], F_GETFD) & FD_CLOEXEC) == 0);
}
if (flags & SOCK_CLOFORK) {
ATF_REQUIRE((fcntl(fd[0], F_GETFD) & FD_CLOFORK) != 0);
ATF_REQUIRE((fcntl(fd[1], F_GETFD) & FD_CLOFORK) != 0);
} else {
ATF_REQUIRE((fcntl(fd[0], F_GETFD) & FD_CLOFORK) == 0);
ATF_REQUIRE((fcntl(fd[1], F_GETFD) & FD_CLOFORK) == 0);
}
if (flags & SOCK_NONBLOCK) {
ATF_REQUIRE((fcntl(fd[0], F_GETFL) & O_NONBLOCK) != 0);
ATF_REQUIRE((fcntl(fd[1], F_GETFL) & O_NONBLOCK) != 0);
} else {
ATF_REQUIRE((fcntl(fd[0], F_GETFL) & O_NONBLOCK) == 0);
ATF_REQUIRE((fcntl(fd[1], F_GETFL) & O_NONBLOCK) == 0);
}
ATF_REQUIRE(close(fd[0]) != -1);
ATF_REQUIRE(close(fd[1]) != -1);
}
ATF_TC(socketpair_basic);
ATF_TC_HEAD(socketpair_basic, tc)
{
atf_tc_set_md_var(tc, "descr", "A basic test of socketpair(2)");
}
ATF_TC_BODY(socketpair_basic, tc)
{
run(0);
}
ATF_TC(socketpair_nonblock);
ATF_TC_HEAD(socketpair_nonblock, tc)
{
atf_tc_set_md_var(tc, "descr", "A non-blocking test of socketpair(2)");
}
ATF_TC_BODY(socketpair_nonblock, tc)
{
run(SOCK_NONBLOCK);
}
ATF_TC(socketpair_cloexec);
ATF_TC_HEAD(socketpair_cloexec, tc)
{
atf_tc_set_md_var(tc, "descr", "A close-on-exec of socketpair(2)");
}
ATF_TC_BODY(socketpair_cloexec, tc)
{
run(SOCK_CLOEXEC);
}
ATF_TC(socketpair_clofork);
ATF_TC_HEAD(socketpair_clofork, tc)
{
atf_tc_set_md_var(tc, "descr", "A close-on-fork of socketpair(2)");
}
ATF_TC_BODY(socketpair_clofork, tc)
{
#if defined(SOCK_CLOFORK) && SOCK_CLOFORK != 0
run(SOCK_CLOFORK);
#else
atf_tc_skip("SOCK_CLOFORK not defined");
#endif
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, socketpair_basic);
ATF_TP_ADD_TC(tp, socketpair_nonblock);
ATF_TP_ADD_TC(tp, socketpair_cloexec);
ATF_TP_ADD_TC(tp, socketpair_clofork);
return atf_no_error();
}