Subject: dialin/dialout crapola
To: None <tech-kern@NetBSD.ORG>
From: Charles M. Hannum <mycroft@mit.edu>
List: tech-kern
Date: 03/21/1998 05:56:26
So, against my better judgement, I'm going to waste a bit more time
explaining the difference and implementation details between various
methods of handling dialin and dialout on the same port.
The `SunOS method' refers to having two device nodes in the file
system, which a bit in the minor number determining whether the
particular device node is for dialin or dialout use.
The `lock file method' requires all programs that use the port to
create UUCP-style lock files.
The `ISOPEN method' is what I referred to in my previous mail as the
`O_NONBLOCK method' (renamed here to distinguish it from other methods
that may also require the O_NONBLOCK flag).
I'm going to mostly avoid discussing the use of TIOCEXCL in 4.4BSD,
except to point out that all it really does in practice is make sure
that a non-root process doesn't try to use the same port; it's
primarily used to prevent someone mucking with a port used by lpd or a
refclock even if they have permissions to. It's not really applicable
for shared dialin/dialout ports, and in fact probably shouldn't be
used in tip(1) at all.
So, first, an executive summary of how the 3 methods are used by
programs:
dialin dialout
SunOS fd = open("/dev/ttyXXX", 0); fd = open("/dev/cuaXXX", 0);
lock file start: start:
fd = open("/dev/ttyXXX", 0); if (!create_lock_file())
if (!create_lock_file()) sleep(N), goto start;
sleep(N), goto start; fd = open("/dev/ttyXXX", O_NONBLOCK);
ISOPEN fd = open("/dev/ttyXXX", 0); fd = open("/dev/ttyXXX", O_NONBLOCK);
There are several desirable properties of a particular locking method:
1) If the device is open for dialin, then opening the device for
dialout should fail (with EBUSY).
* For the SunOS method, this is done by random magic in the kernel.
The implementation details aren't important; it's trivial to
track whether the dialin device is open.
* For the lock file method, this depends on whether we succeeded in
acquiring the lock file. A dialin user would have created a lock
file after they succeeded in opening the device (possibly after
waiting for carrier).
* For the ISOPEN method, we fail if the open was non-blocking and
ISOPEN was already set. (This screws over `stty -f', though.)
2) If the device is open for dialout, then an attempt to open the
device for dialin should wait (or fail, but e.g. getty really
expects to just wait, and would have to poll periodically).
* For the SunOS method, this is also easy to implement by random
magic in the kernel.
* For the lock file method, this is implemented by repeatedly
trying to acquire the lock file.
* For the ISOPEN method, if the open was blocking and ISOPEN is
set, we wait. (This is trivial to do in the same loop that
already waits for carrier.)
3) If the device is already open for dialout, then another attempt to
open it for dialout should fail (with EBUSY).
* For the SunOS method, this is also easy to implement by random
magic in the kernel.
* For the lock file method, this depends on whether we succeeded in
acquiring the lock file. The first dialout user would have
created a lock file.
* For the ISOPEN method, we fail if the open was non-blocking and
ISOPEN was already set. (This screws over `stty -f', though.)
4) If the device is already open for dialin, then another attempt to
open it for dialin should fail.
* For the SunOS and ISOPEN methods, this is impossible, because
there's no way to distinguish between another getty and a random
process reopening the tty (which *must* be allowed).
* The the lock file method, the dialin processes all use lock
files, so the second one would just fail to acquire the lock.
5) When carrier is raised, if the device is open for dialout, then any
dialin waiters should not be woken up.
* For the SunOS method, more magic.
* For the lock file method, they would succeed in the open(2), but
fail to acquire the lock file, and would have to close the port
and poll again.
* For the ISOPEN method, the driver would not wake up processes
waiting for carrier if ISOPEN is set.
So there are some tradeoffs:
* The SunOS method implements this as a black box in the kernel. It
uses extra device nodes, but it provides the desired semantics
(except for preventing multiple gettys on the same port) without
requiring a lot of magic in each program.
* The lock file method requires every program to know about lock
files, which is annoying, especially for third party software. It's
also annoying that you're forced to poll the lock periodically.
More importantly, though, you have to create a more or less
free-for-all lock directory if you want to allow non-privileged
processes to use dialin and dialout ports -- or, in the traditional
solution, make all such programs setuid uucp. This is unacceptable.
* The ISOPEN method has problems with things that don't really obey
the locking semantics (like `stty -f'), unless we choose to elide
some of those semantics; e.g. not caring about multiple simultaneous
dialout users because `stty -f' looks like one and otherwise
wouldn't work. It probably works sufficiently to permit shared
dialin and dialout use of a port provided you don't look at it
funny.
Weighing the pros and cons, I have to say that the SunOS method looks
like the most attractive of the options.