/* $NetBSD: t_sigio.c,v 1.2 2026/09/25 01:09:57 riastradh Exp $ */ /*- * Copyright (c) 2026 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __RCSID("$NetBSD: t_sigio.c,v 1.2 2026/09/25 01:09:57 riastradh Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "h_macros.h" static int expected_si_fd = -1; static int expected_si_code = -1; static long expected_si_band = 0; static long si_band_mask = 0; static int siginfo_received; static void formatbit(char **bufp, size_t *lenp, bool *firstp, const char *name) { int n; size_t k; n = snprintf(*bufp, *lenp, "%s%s", *firstp ? "" : ",", name); *firstp = false; k = ((unsigned)n >= *lenp ? *lenp : (unsigned)n); *bufp += k; *lenp -= k; } static char * formatpollevents(char *buf, size_t len, long events) { char *start = buf; bool first = true; int n; n = snprintf(buf, len, "0x%lx<", events); if ((unsigned)n >= len) goto out; buf += (unsigned)n; len -= (unsigned)n; if (events & POLLIN) formatbit(&buf, &len, &first, "POLLIN"); if (events & POLLOUT) formatbit(&buf, &len, &first, "POLLOUT"); if (events & POLLHUP) formatbit(&buf, &len, &first, "POLLHUP"); if (events & POLLERR) formatbit(&buf, &len, &first, "POLLERR"); if (events & POLLRDNORM) formatbit(&buf, &len, &first, "POLLRDNORM"); if (events & POLLWRNORM) formatbit(&buf, &len, &first, "POLLWRNORM"); if (events & POLLRDBAND) formatbit(&buf, &len, &first, "POLLRDBAND"); if (events & POLLWRBAND) formatbit(&buf, &len, &first, "POLLWRBAND"); if (events & POLLNVAL) formatbit(&buf, &len, &first, "POLLNVAL"); snprintf(buf, len, ">"); out: return start; } struct context { pthread_barrier_t *bar; int fd; int lowat; }; static void * start_closer(void *cookie) { struct context *C = cookie; sigset_t mask; RL(sigemptyset(&mask)); RL(sigaddset(&mask, SIGIO)); RL(pthread_sigmask(SIG_BLOCK, &mask, NULL)); fprintf(stderr, "[thread] waiting for barrier\n"); (void)pthread_barrier_wait(C->bar); fprintf(stderr, "[thread] giving other thread a head start\n"); RL(usleep(1)); fprintf(stderr, "[thread] closing\n"); RL(close(C->fd)); fprintf(stderr, "[thread] closed\n"); return NULL; } static void * start_shutdownrd(void *cookie) { struct context *C = cookie; sigset_t mask; RL(sigemptyset(&mask)); RL(sigaddset(&mask, SIGIO)); RL(pthread_sigmask(SIG_BLOCK, &mask, NULL)); fprintf(stderr, "[thread] waiting for barrier\n"); (void)pthread_barrier_wait(C->bar); fprintf(stderr, "[thread] giving other thread a head start\n"); RL(usleep(1)); fprintf(stderr, "[thread] shutdown(SHUT_RD)\n"); RL(shutdown(C->fd, SHUT_RD)); fprintf(stderr, "[thread] shutdown\n"); return NULL; } static void * start_shutdownwr(void *cookie) { struct context *C = cookie; sigset_t mask; RL(sigemptyset(&mask)); RL(sigaddset(&mask, SIGIO)); RL(pthread_sigmask(SIG_BLOCK, &mask, NULL)); fprintf(stderr, "[thread] waiting for barrier\n"); (void)pthread_barrier_wait(C->bar); fprintf(stderr, "[thread] giving other thread a head start\n"); RL(usleep(1)); fprintf(stderr, "[thread] shutdown(SHUT_WR)\n"); RL(shutdown(C->fd, SHUT_WR)); fprintf(stderr, "[thread] shutdown\n"); return NULL; } static void * start_reader(void *cookie) { struct context *C = cookie; sigset_t mask; char *buf; ssize_t nread; size_t resid = C->lowat; REQUIRE_LIBC(buf = malloc(C->lowat), NULL); RL(sigemptyset(&mask)); RL(sigaddset(&mask, SIGIO)); RL(pthread_sigmask(SIG_BLOCK, &mask, NULL)); fprintf(stderr, "[thread] waiting for barrier\n"); (void)pthread_barrier_wait(C->bar); fprintf(stderr, "[thread] giving other thread a head start\n"); RL(usleep(1)); fprintf(stderr, "[thread] reading first byte\n"); RL(nread = read(C->fd, buf, 1)); fprintf(stderr, "[thread] read first byte\n"); ATF_CHECK_EQ_MSG(nread, 1, "nread=%zd", nread); resid -= (size_t)nread; RL(usleep(1)); fprintf(stderr, "[thread] slept\n"); if (C->lowat <= 1) { fprintf(stderr, "[thread] lowat=1, our job is done\n"); goto out; } /* * Reading a single byte out of the buffer shouldn't be enough * to deliver SIGIO -- only clearing space for C->lowat bytes * should be. */ ATF_CHECK_EQ_MSG(siginfo_received, 0, "siginfo_received=%d", siginfo_received); while (resid > 0) { fprintf(stderr, "[thread] reading more bytes, %zu remain\n", resid); RL(nread = read(C->fd, buf + C->lowat - resid, resid)); fprintf(stderr, "[thread] read %zd bytes\n", nread); ATF_REQUIRE_MSG((size_t)nread <= resid, "nread=%zd resid=%zu", nread, resid); resid -= (size_t)nread; } out: fprintf(stderr, "[thread] done reading\n"); free(buf); return NULL; } static void * start_writer(void *cookie) { struct context *C = cookie; sigset_t mask; char ch = 1; ssize_t nwrit; RL(sigemptyset(&mask)); RL(sigaddset(&mask, SIGIO)); RL(pthread_sigmask(SIG_BLOCK, &mask, NULL)); fprintf(stderr, "[thread] waiting for barrier\n"); (void)pthread_barrier_wait(C->bar); fprintf(stderr, "[thread] giving other thread a head start\n"); RL(usleep(1)); fprintf(stderr, "[thread] writing\n"); RL(nwrit = write(C->fd, &ch, 1)); fprintf(stderr, "[thread] wrote %zd bytes\n", nwrit); ATF_CHECK_EQ_MSG(nwrit, 1, "nwrit=%zd", nwrit); return NULL; } static void ptysetup(int *hostfd, int *appfd) { struct termios t; char *pts; RL(*hostfd = posix_openpt(O_RDWR|O_NOCTTY)); RL(grantpt(*hostfd)); RL(unlockpt(*hostfd)); REQUIRE_LIBC(pts = ptsname(*hostfd), NULL); RL(*appfd = open(pts, O_RDWR|O_NOCTTY)); RL(tcgetattr(*appfd, &t)); t.c_lflag &= ~ICANON; /* block rather than drop input */ RL(tcsetattr(*appfd, TCSANOW, &t)); } static void socksetup(int s[static 2], int af, const char *host) { struct addrinfo *ai, hints = { .ai_family = af, .ai_socktype = SOCK_STREAM, .ai_flags = AI_NUMERICHOST|AI_PASSIVE, }; union { struct sockaddr sa; struct sockaddr_storage ss; } addr; socklen_t addrlen = sizeof(addr); int server = -1; int error; error = getaddrinfo(host, NULL, &hints, &ai); ATF_REQUIRE_MSG(error == 0, "getaddrinfo: %d (%s)", error, gai_strerror(errno)); RL(server = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)); RL(bind(server, ai->ai_addr, ai->ai_addrlen)); RL(getsockname(server, &addr.sa, &addrlen)); RL(listen(server, 1)); RL(s[0] = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)); RL(connect(s[0], &addr.sa, addrlen)); RL(s[1] = accept(server, NULL, NULL)); RL(close(server)); freeaddrinfo(ai); } static void fillpipebuf(int writefd) { char buf[BUFSIZ] = {0}; size_t n = 0; ssize_t nwrit; int flags; RL(flags = fcntl(writefd, F_GETFL)); RL(fcntl(writefd, F_SETFL, flags|O_NONBLOCK)); while ((nwrit = write(writefd, buf, sizeof(buf))) != -1) n += (size_t)nwrit; ATF_CHECK_EQ_MSG(errno, EAGAIN, "errno=%d", errno); RL(fcntl(writefd, F_SETFL, flags)); fprintf(stderr, "filled %d with %zu bytes\n", writefd, n); } static void check_write(int writefd) { int flags; char c = 0; ssize_t nwrit; fprintf(stderr, "checking that we can write a byte...\n"); RL(flags = fcntl(writefd, F_GETFL)); RL(fcntl(writefd, F_SETFL, flags|O_NONBLOCK)); RL(nwrit = write(writefd, &c, 1)); ATF_CHECK_EQ_MSG(nwrit, 1, "nwrit=%zd", nwrit); RL(fcntl(writefd, F_SETFL, flags)); } static void check_sendnonblock(int sendfd) { char c = 0; ssize_t nsent; fprintf(stderr, "checking that we can send a single byte...\n"); RL(nsent = send(sendfd, &c, 1, MSG_DONTWAIT)); ATF_CHECK_EQ_MSG(nsent, 1, "nsent=%zd", nsent); } static void check_read(int readfd) { char c; ssize_t nread; fprintf(stderr, "checking that we can read a byte...\n"); RL(nread = read(readfd, &c, 1)); ATF_CHECK_EQ_MSG(nread, 1, "nread=%zd", nread); } static void check_write_fail(int writefd, int error) { int flags; void (*sighandler)(int); char c = 0; ssize_t nwrit; fprintf(stderr, "checking that write fails with %d (%s)...\n", error, strerror(error)); RL(flags = fcntl(writefd, F_GETFL)); RL(fcntl(writefd, F_SETFL, flags|O_NONBLOCK)); REQUIRE_LIBC(sighandler = signal(SIGPIPE, SIG_IGN), SIG_ERR); ATF_CHECK_ERRNO(error, (nwrit = write(writefd, &c, 1)) == -1); ATF_CHECK_EQ_MSG(nwrit, -1, "nwrit=%zd", nwrit); REQUIRE_LIBC(signal(SIGPIPE, sighandler), SIG_ERR); RL(fcntl(writefd, F_SETFL, flags)); } static void check_read_eof(int readfd) { int flags; char c; ssize_t nread; fprintf(stderr, "checking that read returns EOF...\n"); RL(flags = fcntl(readfd, F_GETFL)); RL(fcntl(readfd, F_SETFL, flags|O_NONBLOCK)); RL(nread = read(readfd, &c, 1)); ATF_CHECK_EQ_MSG(nread, 0, "nread=%zu", nread); RL(fcntl(readfd, F_SETFL, flags)); } static void check_sigio(int signo, siginfo_t *si) { char actbuf[128], expbuf[128]; fprintf(stderr, "[signal] signo=%d (%s)" " si_code=%d si_fd=%d si_band=%s\n", signo, strsignal(signo), si->si_code, si->si_fd, formatpollevents(actbuf, sizeof(actbuf), si->si_band)); ATF_CHECK_EQ_MSG(signo, SIGIO, "signo=%d (%s)", signo, strsignal(signo)); ATF_CHECK_EQ_MSG(si->si_signo, SIGIO, "signo=%d (%s)", si->si_signo, strsignal(si->si_signo)); if (expected_si_fd != -1) { ATF_CHECK_EQ_MSG(si->si_fd, expected_si_fd, "si_fd=%d, expected %d", si->si_fd, expected_si_fd); } if (expected_si_code != -1) { ATF_CHECK_EQ_MSG(si->si_code, expected_si_code, "si_code=%d, expected %d", si->si_code, expected_si_code); } ATF_CHECK_EQ_MSG((si->si_band & si_band_mask), expected_si_band, "si_band=%s, expected %s", formatpollevents(actbuf, sizeof(actbuf), si->si_band), formatpollevents(expbuf, sizeof(expbuf), expected_si_band)); } static void on_sigio(int signo, siginfo_t *si, void *ctx) { check_sigio(signo, si); siginfo_received++; } static void setup(sigset_t *omask) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = &on_sigio; sa.sa_flags = SA_SIGINFO|SA_RESTART; RL(sigemptyset(&sa.sa_mask)); RL(sigaddset(&sa.sa_mask, SIGIO)); RL(pthread_sigmask(SIG_BLOCK, &sa.sa_mask, omask)); RL(sigaction(SIGIO, &sa, NULL)); } static void wait_for_io(sigset_t *omask) { ATF_CHECK_ERRNO(EINTR, sigsuspend(omask) == -1); ATF_CHECK_EQ_MSG(siginfo_received, 1, "siginfo_received=%d", siginfo_received); } ATF_TC(fifo_read); ATF_TC_HEAD(fifo_read, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when fifo is readable"); } ATF_TC_BODY(fifo_read, tc) { sigset_t omask; int fiford, fifowr, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(mkfifo("fifo", 0600)); RL(fiford = open("fifo", O_RDONLY|O_NONBLOCK)); RL(flags = fcntl(fiford, F_GETFL)); RL(fcntl(fiford, F_SETFL, flags & ~O_ASYNC)); RL(fifowr = open("fifo", O_WRONLY)); RL(fcntl(fiford, F_SETOWN, getpid())); RL(flags = fcntl(fiford, F_GETFL)); RL(fcntl(fiford, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = fifowr; RZ(pthread_create(&t, NULL, &start_writer, C)); expected_si_fd = fiford; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; /* * SIGIO is delivered with wrong si_fd = -1, but correct * si_code and si_band. */ atf_tc_expect_fail("PR kern/60785:" " fifo delivers SIGIO with wrong si_fd"); REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read(fiford); RZ(pthread_join(t, NULL)); } ATF_TC(fifo_read_closed); ATF_TC_HEAD(fifo_read_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when fifo is readable because closed"); } ATF_TC_BODY(fifo_read_closed, tc) { sigset_t omask; int fiford, fifowr, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(mkfifo("fifo", 0600)); RL(fiford = open("fifo", O_RDONLY|O_NONBLOCK)); RL(flags = fcntl(fiford, F_GETFL)); RL(fcntl(fiford, F_SETFL, flags & ~O_ASYNC)); RL(fifowr = open("fifo", O_WRONLY)); RL(fcntl(fifowr, F_SETOWN, getpid())); RL(flags = fcntl(fifowr, F_GETFL)); RL(fcntl(fifowr, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = fifowr; RZ(pthread_create(&t, NULL, &start_closer, C)); expected_si_fd = fiford; expected_si_code = POLL_HUP; expected_si_band = POLLHUP; si_band_mask = ~0; /* * Missing wakeups similar to some poll bugs. Not really the * same issue but we're tracking a lot of related stuff * there... */ atf_tc_expect_signal(SIGALRM, "PR kern/59056: poll POLLHUP bugs"); REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read_eof(fiford); RZ(pthread_join(t, NULL)); } ATF_TC(fifo_write); ATF_TC_HEAD(fifo_write, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when fifo is writable"); } ATF_TC_BODY(fifo_write, tc) { sigset_t omask; int fiford, fifowr, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(mkfifo("fifo", 0600)); RL(fiford = open("fifo", O_RDONLY|O_NONBLOCK)); RL(flags = fcntl(fiford, F_GETFL)); RL(fcntl(fiford, F_SETFL, flags & ~O_ASYNC)); RL(fifowr = open("fifo", O_WRONLY)); fillpipebuf(fifowr); RL(fcntl(fifowr, F_SETOWN, getpid())); RL(flags = fcntl(fifowr, F_GETFL)); RL(fcntl(fifowr, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = fiford; C->lowat = PIPE_BUF; RZ(pthread_create(&t, NULL, &start_reader, C)); expected_si_fd = fifowr; expected_si_code = POLL_OUT; expected_si_band = POLLOUT|POLLWRNORM; si_band_mask = ~0; /* * SIGIO is delivered with wrong si_fd = -1, but correct * si_code and si_band. Also delivered too early. */ atf_tc_expect_fail("PR kern/60785:" " fifo delivers SIGIO with wrong si_fd" "; " "PR kern/60782:" " SIGIO says socket ready for send before send does"); REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_write(fifowr); RZ(pthread_join(t, NULL)); } ATF_TC(fifo_write_closed); ATF_TC_HEAD(fifo_write_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when fifo is writable because closed"); } ATF_TC_BODY(fifo_write_closed, tc) { sigset_t omask; int fiford, fifowr, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(mkfifo("fifo", 0600)); RL(fiford = open("fifo", O_RDONLY|O_NONBLOCK)); RL(flags = fcntl(fiford, F_GETFL)); RL(fcntl(fiford, F_SETFL, flags & ~O_ASYNC)); RL(fifowr = open("fifo", O_WRONLY)); fillpipebuf(fifowr); RL(fcntl(fifowr, F_SETOWN, getpid())); RL(flags = fcntl(fifowr, F_GETFL)); RL(fcntl(fifowr, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = fiford; RZ(pthread_create(&t, NULL, &start_closer, C)); expected_si_fd = fifowr; expected_si_code = POLL_ERR; expected_si_band = POLLERR; si_band_mask = ~0; /* * SIGIO is delivered with: * * - si_fd = -1 * - si_code = POLL_OUT (2) * - si_band = POLLOUT|POLLWRNORM (0x40, POLLWRNORM is just an alias) * * which (except for the fd part) is consistent with sockets * but not consistent with the POSIX.1-2024 definition of * POLLERR, and is the same issue as various poll-related * POLLHUP things. */ atf_tc_expect_fail("PR kern/59056: poll POLLHUP bugs"); REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_write_fail(fifowr, EPIPE); RZ(pthread_join(t, NULL)); } ATF_TC(pipe_read); ATF_TC_HEAD(pipe_read, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when pipe is readable"); } ATF_TC_BODY(pipe_read, tc) { sigset_t omask; int pipefd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(pipe(pipefd)); RL(fcntl(pipefd[0], F_SETOWN, getpid())); RL(flags = fcntl(pipefd[0], F_GETFL)); RL(fcntl(pipefd[0], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = pipefd[1]; RZ(pthread_create(&t, NULL, &start_writer, C)); expected_si_fd = pipefd[0]; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read(pipefd[0]); RZ(pthread_join(t, NULL)); } ATF_TC(pipe_read_closed); ATF_TC_HEAD(pipe_read_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when pipe is readable because closed"); } ATF_TC_BODY(pipe_read_closed, tc) { sigset_t omask; int pipefd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(pipe(pipefd)); RL(fcntl(pipefd[0], F_SETOWN, getpid())); RL(flags = fcntl(pipefd[0], F_GETFL)); RL(fcntl(pipefd[0], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = pipefd[1]; RZ(pthread_create(&t, NULL, &start_closer, C)); expected_si_fd = pipefd[0]; expected_si_code = POLL_HUP; expected_si_band = POLLHUP; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read_eof(pipefd[0]); RZ(pthread_join(t, NULL)); } ATF_TC(pipe_write); ATF_TC_HEAD(pipe_write, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when pipe is writable"); } ATF_TC_BODY(pipe_write, tc) { sigset_t omask; int pipefd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(pipe(pipefd)); fillpipebuf(pipefd[1]); RL(fcntl(pipefd[1], F_SETOWN, getpid())); RL(flags = fcntl(pipefd[1], F_GETFL)); RL(fcntl(pipefd[1], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = pipefd[0]; C->lowat = PIPE_BUF; RZ(pthread_create(&t, NULL, &start_reader, C)); expected_si_fd = pipefd[1]; expected_si_code = POLL_OUT; expected_si_band = POLLOUT|POLLWRNORM; si_band_mask = ~0; /* * OK, not quite the same bug but we're tracking a bunch of * related issues there... The wrong fd is reported in SIGIO, * because the sys_pipe.c logic has a bunch of variables * backwards and confused about which side of the pipe is * which. */ atf_tc_expect_fail("PR kern/59056: poll POLLHUP bugs"); REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_write(pipefd[1]); RZ(pthread_join(t, NULL)); } ATF_TC(pipe_write_closed); ATF_TC_HEAD(pipe_write_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when pipe is writable because closed"); } ATF_TC_BODY(pipe_write_closed, tc) { sigset_t omask; int pipefd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(pipe(pipefd)); fillpipebuf(pipefd[1]); RL(fcntl(pipefd[1], F_SETOWN, getpid())); RL(flags = fcntl(pipefd[1], F_GETFL)); RL(fcntl(pipefd[1], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = pipefd[0]; RZ(pthread_create(&t, NULL, &start_closer, C)); expected_si_fd = pipefd[1]; expected_si_code = POLL_ERR; expected_si_band = POLLERR; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); atf_tc_expect_fail("PR kern/59056: poll POLLHUP bugs"); wait_for_io(&omask); check_write_fail(pipefd[1], EPIPE); RZ(pthread_join(t, NULL)); } ATF_TC(ptyapp_read); ATF_TC_HEAD(ptyapp_read, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when app side of pty is readable"); } ATF_TC_BODY(ptyapp_read, tc) { sigset_t omask; int ptyhost, ptyapp, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); ptysetup(&ptyhost, &ptyapp); RL(fcntl(ptyapp, F_SETOWN, getpid())); RL(flags = fcntl(ptyapp, F_GETFL)); RL(fcntl(ptyapp, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = ptyhost; RZ(pthread_create(&t, NULL, &start_writer, C)); expected_si_fd = ptyapp; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); atf_tc_expect_fail("PR kern/60783: tty: missing siginfo_t in SIGIO"); wait_for_io(&omask); check_read(ptyapp); RZ(pthread_join(t, NULL)); } ATF_TC(ptyapp_read_closed); ATF_TC_HEAD(ptyapp_read_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when app side of pty is readable because host closed"); } ATF_TC_BODY(ptyapp_read_closed, tc) { sigset_t omask; int ptyhost, ptyapp, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); ptysetup(&ptyhost, &ptyapp); RL(fcntl(ptyapp, F_SETOWN, getpid())); RL(flags = fcntl(ptyapp, F_GETFL)); RL(fcntl(ptyapp, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = ptyhost; RZ(pthread_create(&t, NULL, &start_closer, C)); expected_si_fd = ptyapp; expected_si_code = POLL_HUP; expected_si_band = POLLHUP; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); atf_tc_expect_fail("PR kern/60783: tty: missing siginfo_t in SIGIO"); wait_for_io(&omask); check_read_eof(ptyapp); RZ(pthread_join(t, NULL)); } ATF_TC(ptyapp_write); ATF_TC_HEAD(ptyapp_write, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when app side of pty is writable"); } ATF_TC_BODY(ptyapp_write, tc) { sigset_t omask; int ptyhost, ptyapp, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); ptysetup(&ptyhost, &ptyapp); fillpipebuf(ptyapp); RL(fcntl(ptyapp, F_SETOWN, getpid())); RL(flags = fcntl(ptyapp, F_GETFL)); RL(fcntl(ptyapp, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = ptyhost; RL(ioctl(ptyapp, TIOCGQSIZE, &C->lowat)); RZ(pthread_create(&t, NULL, &start_reader, C)); expected_si_fd = ptyapp; expected_si_code = POLL_OUT; expected_si_band = POLLOUT|POLLWRNORM; si_band_mask = ~0; atf_tc_expect_signal(SIGALRM, "PR kern/60784:" " pty doesn't deliver SIGIO when writable"); REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_write(ptyapp); RZ(pthread_join(t, NULL)); } ATF_TC(ptyapp_write_closed); ATF_TC_HEAD(ptyapp_write_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when app side of pty is writable because host closed"); } ATF_TC_BODY(ptyapp_write_closed, tc) { sigset_t omask; int ptyhost, ptyapp, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); ptysetup(&ptyhost, &ptyapp); fillpipebuf(ptyapp); RL(fcntl(ptyapp, F_SETOWN, getpid())); RL(flags = fcntl(ptyapp, F_GETFL)); RL(fcntl(ptyapp, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = ptyhost; RZ(pthread_create(&t, NULL, &start_closer, C)); expected_si_fd = ptyapp; expected_si_code = POLL_ERR; expected_si_band = POLLERR; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); atf_tc_expect_fail("PR kern/60783: tty: missing siginfo_t in SIGIO"); wait_for_io(&omask); check_write_fail(ptyapp, EPIPE); RZ(pthread_join(t, NULL)); } ATF_TC(ptyhost_read); ATF_TC_HEAD(ptyhost_read, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when host side of pty is readable"); } ATF_TC_BODY(ptyhost_read, tc) { sigset_t omask; int ptyhost, ptyapp, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); ptysetup(&ptyhost, &ptyapp); RL(fcntl(ptyhost, F_SETOWN, getpid())); RL(flags = fcntl(ptyhost, F_GETFL)); RL(fcntl(ptyhost, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = ptyapp; RZ(pthread_create(&t, NULL, &start_writer, C)); expected_si_fd = ptyhost; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; atf_tc_expect_signal(SIGALRM, "PR kern/60784:" " pty doesn't deliver SIGIO when writable"); REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read(ptyhost); RZ(pthread_join(t, NULL)); } ATF_TC(ptyhost_read_closed); ATF_TC_HEAD(ptyhost_read_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when host side of pty is readable because app closed"); } ATF_TC_BODY(ptyhost_read_closed, tc) { sigset_t omask; int ptyhost, ptyapp, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); ptysetup(&ptyhost, &ptyapp); RL(fcntl(ptyhost, F_SETOWN, getpid())); RL(flags = fcntl(ptyhost, F_GETFL)); RL(fcntl(ptyhost, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = ptyapp; RZ(pthread_create(&t, NULL, &start_closer, C)); expected_si_fd = ptyhost; expected_si_code = POLL_HUP; expected_si_band = POLLHUP; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); atf_tc_expect_fail("PR kern/60783: tty: missing siginfo_t in SIGIO"); wait_for_io(&omask); check_read_eof(ptyhost); RZ(pthread_join(t, NULL)); } ATF_TC(ptyhost_write); ATF_TC_HEAD(ptyhost_write, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when host side of pty is writable"); } ATF_TC_BODY(ptyhost_write, tc) { sigset_t omask; int ptyhost, ptyapp, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); ptysetup(&ptyhost, &ptyapp); fillpipebuf(ptyhost); RL(fcntl(ptyhost, F_SETOWN, getpid())); RL(flags = fcntl(ptyhost, F_GETFL)); RL(fcntl(ptyhost, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = ptyapp; RL(ioctl(ptyhost, TIOCGQSIZE, &C->lowat)); RZ(pthread_create(&t, NULL, &start_reader, C)); expected_si_fd = ptyhost; expected_si_code = POLL_OUT; expected_si_band = POLLOUT|POLLWRNORM; si_band_mask = ~0; atf_tc_expect_signal(SIGALRM, "PR kern/60784:" " pty doesn't deliver SIGIO when writable"); REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_write(ptyhost); RZ(pthread_join(t, NULL)); } ATF_TC(ptyhost_write_closed); ATF_TC_HEAD(ptyhost_write_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when host side of pty is writable because app closed"); } ATF_TC_BODY(ptyhost_write_closed, tc) { sigset_t omask; int ptyhost, ptyapp, flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); ptysetup(&ptyhost, &ptyapp); fillpipebuf(ptyhost); RL(fcntl(ptyhost, F_SETOWN, getpid())); RL(flags = fcntl(ptyhost, F_GETFL)); RL(fcntl(ptyhost, F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = ptyapp; RZ(pthread_create(&t, NULL, &start_closer, C)); expected_si_fd = ptyhost; expected_si_code = POLL_ERR; expected_si_band = POLLERR; si_band_mask = ~0; atf_tc_expect_signal(SIGALRM, "PR kern/60784:" " pty doesn't deliver SIGIO when writable"); REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_write_fail(ptyhost, EPIPE); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet6_read); ATF_TC_HEAD(socket_inet6_read, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is readable"); } ATF_TC_BODY(socket_inet6_read, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET6, "::1"); RL(fcntl(sockfd[0], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[0], F_GETFL)); RL(fcntl(sockfd[0], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[1]; RZ(pthread_create(&t, NULL, &start_writer, C)); expected_si_fd = sockfd[0]; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read(sockfd[0]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet6_read_closed); ATF_TC_HEAD(socket_inet6_read_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is readable because closed"); } ATF_TC_BODY(socket_inet6_read_closed, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET6, "::1"); RL(fcntl(sockfd[0], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[0], F_GETFL)); RL(fcntl(sockfd[0], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[1]; RZ(pthread_create(&t, NULL, &start_closer, C)); /* * XXX Is this really sensible? No POLLHUP? Different from * pipes? POLLIN is right in one sense that we can read * without blocking, and it will report EOF -- but that is also * the scenario in which POLLHUP is right. */ expected_si_fd = sockfd[0]; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read_eof(sockfd[0]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet6_read_shutdown); ATF_TC_HEAD(socket_inet6_read_shutdown, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is readable because shutdown"); } ATF_TC_BODY(socket_inet6_read_shutdown, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET6, "::1"); RL(fcntl(sockfd[0], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[0], F_GETFL)); RL(fcntl(sockfd[0], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[1]; RZ(pthread_create(&t, NULL, &start_shutdownwr, C)); /* * XXX Is this really sensible? No POLLHUP? Different from * pipes? POLLIN is right in one sense that we can read * without blocking, and it will report EOF -- but that is also * the scenario in which POLLHUP is right. */ expected_si_fd = sockfd[0]; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read_eof(sockfd[0]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet6_write); ATF_TC_HEAD(socket_inet6_write, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is writable"); } ATF_TC_BODY(socket_inet6_write, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; socklen_t optlen; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET6, "::1"); fillpipebuf(sockfd[1]); RL(fcntl(sockfd[1], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[1], F_GETFL)); RL(fcntl(sockfd[1], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[0]; optlen = sizeof(C->lowat); RL(getsockopt(sockfd[1], SOL_SOCKET, SO_SNDLOWAT, &C->lowat, &optlen)); ATF_REQUIRE_EQ_MSG(optlen, sizeof(C->lowat), "optlen=%zu sizeof(C->lowat)=%zu", (size_t)optlen, sizeof(C->lowat)); RZ(pthread_create(&t, NULL, &start_reader, C)); expected_si_fd = sockfd[1]; expected_si_code = POLL_OUT; expected_si_band = POLLOUT|POLLWRNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_sendnonblock(sockfd[1]); check_write(sockfd[1]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet6_write_closed); ATF_TC_HEAD(socket_inet6_write_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is writable because closed"); } ATF_TC_BODY(socket_inet6_write_closed, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET6, "::1"); fillpipebuf(sockfd[1]); RL(fcntl(sockfd[1], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[1], F_GETFL)); RL(fcntl(sockfd[1], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[0]; RZ(pthread_create(&t, NULL, &start_closer, C)); /* * We get POLLIN|POLLRDNORM because the TCP peer has sent FIN * to indicate they won't write any more, which will manifest * on our end by read/recv reporting EOF; TCP has no mechanism * to indicate that they won't _read_ any more as in POLLERR. * * XXX However, the peer may ack some data, leading us to think * we can write more. So, to avoid a flaky test, let's shut * down in the write direction. Except, oops, that doesn't * work because of PR kern/60786: poll reports socket writable * after shutdown(SHUT_WR). And while we could si_band for * POLLIN, we don't get all bits at once like poll() does; we * get _either_ code=POLL_IN band=POLLIN|POLLRDNORM _or_ * code=POLL_OUT band=POLLOUT|POLLWRNORM. * * I give up; I'll just not test the code and band for now... */ RL(shutdown(sockfd[1], SHUT_WR)); expected_si_fd = sockfd[1]; expected_si_code = -1; /* POLL_IN */ expected_si_band = 0; /* POLLIN|POLLRDNORM */ si_band_mask = 0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_write_fail(sockfd[1], EPIPE); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet6_write_shutdown); ATF_TC_HEAD(socket_inet6_write_shutdown, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is writable because shutdown"); } ATF_TC_BODY(socket_inet6_write_shutdown, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET6, "::1"); fillpipebuf(sockfd[1]); RL(fcntl(sockfd[1], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[1], F_GETFL)); RL(fcntl(sockfd[1], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[0]; RZ(pthread_create(&t, NULL, &start_shutdownrd, C)); /* * TCP doesn't have a way to say `I will discard any further * bytes you throw at me, so please stop throwing them'. But * it can ack some data making us think we can continue sending * more. */ expected_si_fd = sockfd[1]; expected_si_code = POLL_OUT; expected_si_band = POLLOUT|POLLWRNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_write(sockfd[1]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet_read); ATF_TC_HEAD(socket_inet_read, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is readable"); } ATF_TC_BODY(socket_inet_read, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET, "127.0.0.1"); RL(fcntl(sockfd[0], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[0], F_GETFL)); RL(fcntl(sockfd[0], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[1]; RZ(pthread_create(&t, NULL, &start_writer, C)); expected_si_fd = sockfd[0]; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read(sockfd[0]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet_read_closed); ATF_TC_HEAD(socket_inet_read_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is readable because closed"); } ATF_TC_BODY(socket_inet_read_closed, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET, "127.0.0.1"); RL(fcntl(sockfd[0], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[0], F_GETFL)); RL(fcntl(sockfd[0], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[1]; RZ(pthread_create(&t, NULL, &start_closer, C)); /* * XXX Is this really sensible? No POLLHUP? Different from * pipes? POLLIN is right in one sense that we can read * without blocking, and it will report EOF -- but that is also * the scenario in which POLLHUP is right. */ expected_si_fd = sockfd[0]; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read_eof(sockfd[0]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet_read_shutdown); ATF_TC_HEAD(socket_inet_read_shutdown, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is readable because shutdown"); } ATF_TC_BODY(socket_inet_read_shutdown, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET, "127.0.0.1"); RL(fcntl(sockfd[0], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[0], F_GETFL)); RL(fcntl(sockfd[0], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[1]; RZ(pthread_create(&t, NULL, &start_shutdownwr, C)); /* * XXX Is this really sensible? No POLLHUP? Different from * pipes? POLLIN is right in one sense that we can read * without blocking, and it will report EOF -- but that is also * the scenario in which POLLHUP is right. */ expected_si_fd = sockfd[0]; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read_eof(sockfd[0]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet_write); ATF_TC_HEAD(socket_inet_write, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is writable"); } ATF_TC_BODY(socket_inet_write, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; socklen_t optlen; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET, "127.0.0.1"); fillpipebuf(sockfd[1]); RL(fcntl(sockfd[1], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[1], F_GETFL)); RL(fcntl(sockfd[1], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[0]; optlen = sizeof(C->lowat); RL(getsockopt(sockfd[1], SOL_SOCKET, SO_SNDLOWAT, &C->lowat, &optlen)); ATF_REQUIRE_EQ_MSG(optlen, sizeof(C->lowat), "optlen=%zu sizeof(C->lowat)=%zu", (size_t)optlen, sizeof(C->lowat)); RZ(pthread_create(&t, NULL, &start_reader, C)); expected_si_fd = sockfd[1]; expected_si_code = POLL_OUT; expected_si_band = POLLOUT|POLLWRNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_sendnonblock(sockfd[1]); check_write(sockfd[1]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet_write_closed); ATF_TC_HEAD(socket_inet_write_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is writable because closed"); } ATF_TC_BODY(socket_inet_write_closed, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET, "127.0.0.1"); fillpipebuf(sockfd[1]); RL(fcntl(sockfd[1], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[1], F_GETFL)); RL(fcntl(sockfd[1], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[0]; RZ(pthread_create(&t, NULL, &start_closer, C)); /* * We get POLLIN|POLLRDNORM because the TCP peer has sent FIN * to indicate they won't write any more, which will manifest * on our end by read/recv reporting EOF; TCP has no mechanism * to indicate that they won't _read_ any more as in POLLERR. * * XXX However, the peer may ack some data, leading us to think * we can write more. So, to avoid a flaky test, let's shut * down in the write direction. Except, oops, that doesn't * work because of PR kern/60786: poll reports socket writable * after shutdown(SHUT_WR). And while we could si_band for * POLLIN, we don't get all bits at once like poll() does; we * get _either_ code=POLL_IN band=POLLIN|POLLRDNORM _or_ * code=POLL_OUT band=POLLOUT|POLLWRNORM. * * I give up; I'll just not test the code and band for now... */ RL(shutdown(sockfd[1], SHUT_WR)); expected_si_fd = sockfd[1]; expected_si_code = -1; /* POLL_IN */ expected_si_band = 0; /* POLLIN|POLLRDNORM */ si_band_mask = 0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_write_fail(sockfd[1], EPIPE); RZ(pthread_join(t, NULL)); } ATF_TC(socket_inet_write_shutdown); ATF_TC_HEAD(socket_inet_write_shutdown, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is writable because shutdown"); } ATF_TC_BODY(socket_inet_write_shutdown, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); socksetup(sockfd, AF_INET, "127.0.0.1"); fillpipebuf(sockfd[1]); RL(fcntl(sockfd[1], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[1], F_GETFL)); RL(fcntl(sockfd[1], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[0]; RZ(pthread_create(&t, NULL, &start_shutdownrd, C)); /* * TCP doesn't have a way to say `I will discard any further * bytes you throw at me, so please stop throwing them', so it * can't report POLLERR. But it can ack some data making us * think we can continue sending more, so it does report * POLLIN. */ expected_si_fd = sockfd[1]; expected_si_code = POLL_OUT; expected_si_band = POLLOUT|POLLWRNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_write(sockfd[1]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_local_read); ATF_TC_HEAD(socket_local_read, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is readable"); } ATF_TC_BODY(socket_local_read, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd)); RL(fcntl(sockfd[0], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[0], F_GETFL)); RL(fcntl(sockfd[0], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[1]; RZ(pthread_create(&t, NULL, &start_writer, C)); expected_si_fd = sockfd[0]; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read(sockfd[0]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_local_read_closed); ATF_TC_HEAD(socket_local_read_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is readable because closed"); } ATF_TC_BODY(socket_local_read_closed, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd)); RL(fcntl(sockfd[0], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[0], F_GETFL)); RL(fcntl(sockfd[0], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[1]; RZ(pthread_create(&t, NULL, &start_closer, C)); /* * XXX Is this really sensible? No POLLHUP? Different from * pipes? POLLIN is right in one sense that we can read * without blocking, and it will report EOF -- but that is also * the scenario in which POLLHUP is right. */ expected_si_fd = sockfd[0]; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read_eof(sockfd[0]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_local_read_shutdown); ATF_TC_HEAD(socket_local_read_shutdown, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is readable because shutdown"); } ATF_TC_BODY(socket_local_read_shutdown, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd)); RL(fcntl(sockfd[0], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[0], F_GETFL)); RL(fcntl(sockfd[0], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[1]; RZ(pthread_create(&t, NULL, &start_shutdownwr, C)); /* * XXX Is this really sensible? No POLLHUP? Different from * pipes? POLLIN is right in one sense that we can read * without blocking, and it will report EOF -- but that is also * the scenario in which POLLHUP is right. */ expected_si_fd = sockfd[0]; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_read_eof(sockfd[0]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_local_write); ATF_TC_HEAD(socket_local_write, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is writable"); } ATF_TC_BODY(socket_local_write, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; socklen_t optlen; pthread_t t; setup(&omask); RL(socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd)); fillpipebuf(sockfd[1]); RL(fcntl(sockfd[1], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[1], F_GETFL)); RL(fcntl(sockfd[1], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[0]; optlen = sizeof(C->lowat); RL(getsockopt(sockfd[1], SOL_SOCKET, SO_SNDLOWAT, &C->lowat, &optlen)); ATF_REQUIRE_EQ_MSG(optlen, sizeof(C->lowat), "optlen=%zu sizeof(C->lowat)=%zu", (size_t)optlen, sizeof(C->lowat)); atf_tc_expect_fail("PR kern/60782:" " SIGIO says socket ready for send before send does"); RZ(pthread_create(&t, NULL, &start_reader, C)); expected_si_fd = sockfd[0]; expected_si_code = POLL_OUT; expected_si_band = POLLOUT|POLLWRNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_sendnonblock(sockfd[1]); check_write(sockfd[1]); RZ(pthread_join(t, NULL)); } ATF_TC(socket_local_write_closed); ATF_TC_HEAD(socket_local_write_closed, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is writable because closed"); } ATF_TC_BODY(socket_local_write_closed, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd)); fillpipebuf(sockfd[1]); RL(fcntl(sockfd[1], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[1], F_GETFL)); RL(fcntl(sockfd[1], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[0]; RZ(pthread_create(&t, NULL, &start_closer, C)); /* * Note: This matches the TCP semantics but not the pipe * semantics. */ expected_si_fd = sockfd[1]; expected_si_code = POLL_IN; expected_si_band = POLLIN|POLLRDNORM; si_band_mask = ~0; REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); wait_for_io(&omask); check_write_fail(sockfd[1], EPIPE); RZ(pthread_join(t, NULL)); } ATF_TC(socket_local_write_shutdown); ATF_TC_HEAD(socket_local_write_shutdown, tc) { atf_tc_set_md_var(tc, "descr", "Test SIGIO when socket is writable because shutdown"); } ATF_TC_BODY(socket_local_write_shutdown, tc) { sigset_t omask; int sockfd[2], flags; pthread_barrier_t bar; struct context ctx, *C = &ctx; pthread_t t; setup(&omask); RL(socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd)); fillpipebuf(sockfd[1]); RL(fcntl(sockfd[1], F_SETOWN, getpid())); RL(flags = fcntl(sockfd[1], F_GETFL)); RL(fcntl(sockfd[1], F_SETFL, flags | O_ASYNC)); RZ(pthread_barrier_init(&bar, NULL, 2)); memset(C, 0, sizeof(*C)); C->bar = &bar; C->fd = sockfd[0]; RZ(pthread_create(&t, NULL, &start_shutdownrd, C)); /* * XXX Should AF_LOCAL sockets report anything to the peer on * shutdown(SHUT_RD)? */ REQUIRE_LIBC(alarm(1), (unsigned)-1); (void)pthread_barrier_wait(&bar); /* * XXX Why does this return EAGAIN and not EPIPE? Shouldn't * this refuse to send any more data now that the reader has * shutdown that direction? */ check_write_fail(sockfd[1], EAGAIN); RZ(pthread_join(t, NULL)); } ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, fifo_read); ATF_TP_ADD_TC(tp, fifo_read_closed); ATF_TP_ADD_TC(tp, fifo_write); ATF_TP_ADD_TC(tp, fifo_write_closed); ATF_TP_ADD_TC(tp, pipe_read); ATF_TP_ADD_TC(tp, pipe_read_closed); ATF_TP_ADD_TC(tp, pipe_write); ATF_TP_ADD_TC(tp, pipe_write_closed); ATF_TP_ADD_TC(tp, ptyapp_read); ATF_TP_ADD_TC(tp, ptyapp_read_closed); ATF_TP_ADD_TC(tp, ptyapp_write); ATF_TP_ADD_TC(tp, ptyapp_write_closed); ATF_TP_ADD_TC(tp, ptyhost_read); ATF_TP_ADD_TC(tp, ptyhost_read_closed); ATF_TP_ADD_TC(tp, ptyhost_write); ATF_TP_ADD_TC(tp, ptyhost_write_closed); ATF_TP_ADD_TC(tp, socket_inet6_read); ATF_TP_ADD_TC(tp, socket_inet6_read_closed); ATF_TP_ADD_TC(tp, socket_inet6_read_shutdown); ATF_TP_ADD_TC(tp, socket_inet6_write); ATF_TP_ADD_TC(tp, socket_inet6_write_closed); ATF_TP_ADD_TC(tp, socket_inet6_write_shutdown); ATF_TP_ADD_TC(tp, socket_inet_read); ATF_TP_ADD_TC(tp, socket_inet_read_closed); ATF_TP_ADD_TC(tp, socket_inet_read_shutdown); ATF_TP_ADD_TC(tp, socket_inet_write); ATF_TP_ADD_TC(tp, socket_inet_write_closed); ATF_TP_ADD_TC(tp, socket_inet_write_shutdown); ATF_TP_ADD_TC(tp, socket_local_read); ATF_TP_ADD_TC(tp, socket_local_read_closed); ATF_TP_ADD_TC(tp, socket_local_read_shutdown); ATF_TP_ADD_TC(tp, socket_local_write); ATF_TP_ADD_TC(tp, socket_local_write_closed); ATF_TP_ADD_TC(tp, socket_local_write_shutdown); return atf_no_error(); }