Re: Warning, cua2 opened, ...

Theodore Y. Ts'o (tytso@MIT.EDU)
Sat, 14 Mar 1998 22:34:08 -0500


Date: Sat, 14 Mar 1998 21:22:13 -0500 (EST)
From: Kenneth Albanowski <kjahds@kjahds.com>

Perhaps a handy little kernel warning on first use of a /dev/cuaxx (a la
broken flock), along with some Documentation describing exactly _what_ is
wrong is the cua devices, _why_ the tty devices should be used instead,
and _how_ programs should be converted to do this. Without that sort of
documentation, a conversion like this isn't much fun, or very fair to the
folks who just use the things without understanding them.

What started this thread was the change in 2.1.90 which warns on the use
of a /dev/cuaxx that the device is deprecated; that's going to be the
standard behaviour for 2.2, with the expected removal of the cuaxx
devices in the 2.4 release. So yes, there will be a kernel warning.

It's fair that there be an explanation in the Documentation directory
describing what's wrong with the cua devices, and how the tty devices
should be used instead. I'll work on writing that up.

Put simply, the problem with the /dev/cuaXX devices is that the locking
between the cua devices and the ttySxx device is horribly complicated
and doeosn't work all that well. It works if you have a single callout
application, and a single dialin application, but there's nothing that
locks out two different callout applications wanting to use the same tty
device. The right answer is to use a tty lock file, as documented in
the Linux Filesystem Standard.

The other use of the /dev/cuaXX device is for people who want to be able
to open the tty device as if CLOCAL is always set. The kernel sets
CLOCAL by default, but sometimes other programs clear CLOCAL, and then
shell scripts which assume that you can just let the shell open the tty
device start hanging. This is admittedly a slightly more reasonable
way to use the cuaXX devices, but not all systems support the callout
devices, and POSIX doesn't require it.

I suppose at this point I could take a page from the glibc folks, and
say that gratuitously breaking programs that make assumptions beyond
what is promised by various POSIX and ANSI standards is _good_, because
ultimately makes the programs/shell scripts/whatever more portable.
However, since I despise this attitude, it would be most hypocritical
for me to take it. But for those of you who do subscribe to this
philosophy, see below --- I'll tell you how to make your programs
portable. (And those people who defend glibc on these grounds are
hereby forbidden to complain to me that I'm deprecating the cua
devices. :-)

Instead, I'll note that many programs support using the proper POSIX tty
devices directly:

* pppd/chat works just fine with ttySxx. I've even written up a
quick guide for how to configure the latest version of
pppd, and it doesn't require using the callout devices.
Said quick hide was sent out to linux-ppp for comments,
but it didn't get any. I'll be happy to resend it.
(Why the HOWTO guide recommends using /dev/cuaXX I'm
not sure; but it should be fixed.)

* mgetty not only works fine with the POSIX tty devices, it
strongly recommends that you not use the callout devices
at all, since they only cause problems with mgetty's tty
locking.

* kermit works just fine with POSIX ttys.

Also, all tty programs will work fine as long as the CLOCAL flag is set
on a particular tty. If you have some device which is not a modem, and
which permanently needs to have the CLOCAL flag set, you can either
just simply make sure your programs don't clear it for some wrong-headed
reason, or you can use the TIOCSLCKTERMIOS to lock the CLOCAL flag and
hence prevent other programs from accidentally clearing the CLOCAL flag.
This could be done at boot-time, since TICOSLCKTERMIOS must be executed
by root.

Fixing your programs
====================

The proper way to open POSIX tty devices is as follows. First, open
them with the O_NONBLOCK flag; this guarantees that they won't block
waiting for carrier detect. Secondly, use the fcntl() call to clear the
O_NONBLOCK flag (unless you really want to do non-blocking i/o). Yes,
POSIX overloaded the meaning of O_NONBLOCK; so sue them. It means that
you have to execute an extra system call or two to clear O_NONBLOCK
after the open finishes, but that really isn't that hard to do.

Fixing your shell scripts is a little bit harder. In general, you
should think about avoiding statements like:

echo "foo bar" > /dev/cua0

If there's a way to let the program (like PPPD) take over that
functionality, that's probably the right way to go. Failing that, make
sure CLOCAL is set before letting the shell open a tty. The stty
command ideally should have a -f option which would allow stty to open
the tty device for you. The BSDI stty has this feature; the GNU
shell-utils' stty doesn't (yet) --- I intend to add this feature to our
stty command. In the meantime, you can use the following program to set
the CLOCAL flag. It also serves as a good demonstration of how to open
a tty from a C program:

/*
* clocal.c
*
* Usage: clocal <device>
*
*/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char **argv)
{
int ttyfd, i, fdflags;
struct termios tios;

if (argc != 2) {
fprintf(stderr, "usage: %s device\n", argv[0]);
exit(1);
}

while ((ttyfd = open(argv[1], O_NONBLOCK | O_RDONLY, 0)) < 0) {
if (errno == EINTR)
continue;
perror(argv[1]);
exit(1);
}
if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
|| fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
perror("couldn't reset non-blocking mode");

if (tcgetattr(ttyfd, &tios) < 0) {
fprintf(stderr, "tcgetattr: %m(%d)", strerror(errno), errno);
exit(1);
}
tios.c_cflag |= CLOCAL;
if (tcsetattr(ttyfd, TCSAFLUSH, &tios) < 0) {
fprintf(stderr, "tcsetattr: %m(%d)", strerror(errno), errno);
exit(1);
}
close(ttyfd);
return 0;
}

- Ted

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu