mixing O_NONBLOCK and raw tty causes portability problems
Matthias Drochner <[email protected]>
| Newsgroups | gmane.network.lsh.bugs |
|---|---|
| Message-ID | <[email protected]> |
Hi - I thought I'd give lsh a try, just to see how it compares to openssh... The client didn't work well on NetBSD, got a message like "unexpected EWOULDBLOCK" on each keystroke. Looked a bit deeper and found that stdin is set to O_NONBLOCK and a raw tty mode with c_cc[VMIN] > 1 and c_cc[VTIME] > 0. I'll append a little test program which does the same. I've tried it on 3 operating systems (Linux, NetBSD, Digital UNIX), and it behaves differently on each: -on Linux, if a key is pressed, the read returns immediately with that one character -on NetBSD, the read returns with no data but EWOULDBLOCK -on D'UNIX, the poll() doesn't teturn before 4 keypresses are done; the read() returns these 4 characters Indeed, in SUSv2's termios page is a sentence which says that if both O_NONBLOCK and VTIME>0 are set, the behaviour is more or less undefined. I've solved my immediate problems by setting VMIN to 1 instead of 4 in unix_interact.c:do_make_raw(), but VTIME is still pointless, so I wouldn't call this a clean solution. (Don't know what liboop uses under the hood, but in case it does poll(), anything with VMIN>1 wouldn't work with D'Unix...) best regards Matthias
ttytest.c
(text/plain, 1.2 KB)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <poll.h>
#include <string.h>
#include <errno.h>
struct termios tsav;
void
dump(fd, save)
int fd;
{
struct termios t;
int res;
res = tcgetattr(fd, &t);
if (res < 0)
exit(errno);
if (save)
tsav = t;
fprintf(stderr, "fd %d: %x/%d/%d\n", fd, t.c_iflag,
t.c_cc[VMIN], t.c_cc[VTIME]);
}
void
setraw(fd)
int fd;
{
struct termios t;
int res;
res = tcgetattr(fd, &t);
if (res < 0)
exit(errno);
cfmakeraw(&t);
t.c_cc[VMIN] = 4;
t.c_cc[VTIME] = 1;
res = tcsetattr(fd, TCSAFLUSH, &t);
if (res < 0)
exit(errno);
}
void
restore(fd)
int fd;
{
tcsetattr(fd, TCSAFLUSH, &tsav);
}
int
main()
{
int in;
int flags, res;
struct pollfd inp;
char buf[100];
in = STDIN_FILENO;
flags = fcntl(in, F_GETFL);
if (flags == -1)
exit(errno);
flags |= O_NONBLOCK;
res = fcntl(in, F_SETFL, flags);
if (res < 0)
exit(errno);
dump(in, 1);
setraw(in);
dump(in, 0);
inp.fd = in;
inp.events = POLLIN;
res = poll(&inp, 1, -1);
fprintf(stderr, "poll: %d (%s)\n", res, strerror(errno));
res = read(in, buf, sizeof(buf));
fprintf(stderr, "read: %d (%s)\n", res, strerror(errno));
restore(in);
exit(0);
}