NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/60784: pty doesn't deliver SIGIO when writable
>Number: 60784
>Category: kern
>Synopsis: pty doesn't deliver SIGIO when writable
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Thu Sep 24 17:30:01 +0000 2026
>Originator: Taylor R Campbell
>Release: current, 11, 10, ...
>Organization:
The Pseudo-Terminal Bugs LLC (it's never the last one)
>Environment:
>Description:
When either side of a pty (host or application) becomes
writable, presumably it should deliver SIGIO if subscribed,
just like poll reports POLLOUT. But it doesn't.
>How-To-Repeat:
#include <sys/ioctl.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#define REQUIRE_LIBC(x, fail) do \
{ \
if ((x) == (fail)) \
err(EXIT_FAILURE, "%s", #x); \
} while (0)
#define RL(x) REQUIRE_LIBC(x, -1)
#define RZ(x) do \
{ \
errno = (x); \
if (errno) \
err(EXIT_FAILURE, "%s", #x); \
} while (0)
static void
on_sigio(int signo, siginfo_t *si, void *ctx)
{
(void)signo;
(void)ctx;
fprintf(stderr, "SIGIO code=%d band=0x%lx\n",
si->si_code, si->si_band);
}
struct context {
pthread_barrier_t *bar;
int fd;
int lowat;
};
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");
sleep(1);
fprintf(stderr, "[thread] reading first byte\n");
RL(nread = read(C->fd, buf, 1));
if (nread != 1)
errx(EXIT_FAILURE, "nread=%zd", nread);
resid -= (size_t)nread;
RL(usleep(1));
if (C->lowat <= 1) {
fprintf(stderr, "[thread] lowat=1, our job is done\n");
goto out;
}
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);
if ((size_t)nread > resid) {
errx(EXIT_FAILURE, "nread=%zd resid=%zu",
nread, resid);
}
resid -= (size_t)nread;
}
out: free(buf);
return NULL;
}
#if 0
#else
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));
}
#endif
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;
if (errno != EAGAIN)
err(EXIT_FAILURE, "wite");
RL(fcntl(writefd, F_SETFL, flags));
fprintf(stderr, "filled %d with %zu bytes\n", writefd, n);
}
int
main(void)
{
sigset_t omask;
struct sigaction sa;
int ptyhost, ptyapp, flags;
pthread_barrier_t bar;
struct context ctx, *C = &ctx;
pthread_t t;
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));
#if 0
int pipefd[2];
RL(pipe(pipefd));
ptyhost = pipefd[1];
ptyapp = pipefd[0];
#else
ptysetup(&ptyhost, &ptyapp);
#endif
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;
#if 0
C->lowat = PIPE_BUF;
#else
RL(ioctl(ptyhost, TIOCGQSIZE, &C->lowat));
#endif
RZ(pthread_create(&t, NULL, &start_reader, C));
REQUIRE_LIBC(alarm(2), (unsigned)-1);
(void)pthread_barrier_wait(&bar);
#if 0
struct pollfd pfd = { .fd = ptyhost, .events = POLLOUT };
int nfds;
RL(nfds = poll(&pfd, 1, -1));
if (nfds != 1)
errx(EXIT_FAILURE, "nfds=%d", nfds);
if ((pfd.events & POLLOUT) == 0)
errx(EXIT_FAILURE, "pfd.events=0x%x", pfd.events);
printf("poll says writable\n");
fflush(stdout);
#else
if (sigsuspend(&omask) != -1)
errx(EXIT_FAILURE, "sigsuspend succeeded");
if (errno != EINTR)
err(EXIT_FAILURE, "sigsuspend");
#endif
RL(write(ptyhost, (const char[]){1}, 1));
RZ(pthread_join(t, NULL));
return 0;
}
>Fix:
Something broken about ttwakeup or ttysig? Something specific
to ptys and not other terminals?
Home |
Main Index |
Thread Index |
Old Index