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);
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.