mixing O_NONBLOCK and raw tty causes portability problems

Matthias Drochner M.Drochner@fz-juelich.de
Wed, 15 Oct 2003 12:45:09 +0200


This is a multipart MIME message.

--==_Exmh_10149248080450
Content-Type: text/plain; charset=us-ascii


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



--==_Exmh_10149248080450
Content-Type: text/plain ; name="ttytest.c"; charset=us-ascii
Content-Description: ttytest.c
Content-Disposition: attachment; filename="ttytest.c"

#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);
}

--==_Exmh_10149248080450--