Subject: Re: tty layer bogons from the depths of the abyss
To: Charles M. Hannum <mycroft@mit.edu>
From: Michael Eriksson <eramer@era-t.ericsson.se>
List: tech-kern
Date: 03/19/1998 15:21:30
> So I decided to research the history and usage of WOPEN a bit more
> before redefining it.

Great that somebody looks at this. It sure is a mess right now.

You might be interested in some local changes I've made. My main goal
was to be able to select() for write on a tty, so a process could have
a tty open and wait/check for DCD. I added a TS_ZOMBIE flag (and
state), which gets set when the DCD drops. The state diagram got a bit
confusing if the DCD got switched back and forth, so I did a zombie
state instead (I don't remember the exact reasoning, but I had to do
something about the WOPEN and ISOPEN weirdness).

I also made it possible to do ioctl(TIOCSPGRP) on a tty that's not the
process's own controlling tty, iff it's not any other process's
controlling tty. If this isn't allowed, it is impossible to get
SIGIO's for serial communication.

In addition, I fixed a bug in the TIOCSET* handling. The
(*tp->t_param)(tp, t) call changed the tp->t_cflag, so the
``ISSET(tp->t_cflag, CLOCAL) && !ISSET(t->c_cflag, CLOCAL)'' test
always failed. I also added a selwakeup() call when CLOCAL got set (as
CLOCAL means that carrier is ignored, and somebody waiting for carrier
should wake up).

One other thing that might need some looking at is the calls to
ttwakeup() (and thereby to selwakeup()). I didn't analyse it
thoroughly, but to me it seemed like ttwakeup() unnecessarily gets
called for every incoming character (when in raw mode). If there is
already characters in the queue (t_rawq or t_canq), is there really
any need to call ttwakeup() and thereby send a SIGIO again?

My diffs (from NetBSD 1.3) are below. I planned to eventually send-pr
them, but I wanted to try them out for a while first (I have now).

--- sys/tty.h.orig	Fri Oct 10 01:34:55 1997
+++ sys/tty.h	Sat Jan 31 19:45:27 1998
@@ -150,13 +150,14 @@
 #define	TS_TTSTOP	0x00100		/* Output paused. */
 #define	TS_WOPEN	0x00200		/* Open in progress. */
 #define	TS_XCLUDE	0x00400		/* Tty requires exclusivity. */
+#define	TS_ZOMBIE	0x00800		/* Lost connection. */
 
 /* State for intra-line fancy editing work. */
-#define	TS_BKSL		0x00800		/* State for lowercase \ work. */
-#define	TS_CNTTB	0x01000		/* Counting tab width, ignore FLUSHO. */
-#define	TS_ERASE	0x02000		/* Within a \.../ for PRTRUB. */
-#define	TS_LNCH		0x04000		/* Next character is literal. */
-#define	TS_TYPEN	0x08000		/* Retyping suspended input (PENDIN). */
+#define	TS_BKSL		0x010000	/* State for lowercase \ work. */
+#define	TS_CNTTB	0x020000	/* Counting tab width, ignore FLUSHO. */
+#define	TS_ERASE	0x040000	/* Within a \.../ for PRTRUB. */
+#define	TS_LNCH		0x080000	/* Next character is literal. */
+#define	TS_TYPEN	0x100000	/* Retyping suspended input (PENDIN). */
 #define	TS_LOCAL	(TS_BKSL | TS_CNTTB | TS_ERASE | TS_LNCH | TS_TYPEN)
 
 /* Character type information. */


--- kern/tty.c.orig	Tue Oct 28 13:30:59 1997
+++ kern/tty.c	Sat Jan 31 19:42:37 1998
@@ -78,6 +78,7 @@
 #endif
 const char	ttyin[] = "ttyin";
 const char	ttyout[] = "ttyout";
+const char	ttydcd[] = "ttydcd";
 
 /*
  * Used to determine whether we still have a connection.  This is true in
@@ -786,7 +787,7 @@
 		*(struct winsize *)data = tp->t_winsize;
 		break;
 	case TIOCGPGRP:			/* get pgrp of tty */
-		if (!isctty(p, tp))
+		if (tp->t_session && !isctty(p, tp))
 			return (ENOTTY);
 		*(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : NO_PID;
 		break;
@@ -820,6 +821,7 @@
 				ttyflush(tp, FREAD);
 		}
 		if (!ISSET(t->c_cflag, CIGNORE)) {
+			int oflag = tp->t_cflag;
 			/*
 			 * Set device hardware.
 			 */
@@ -828,11 +830,10 @@
 				return (error);
 			} else {
 				if (!ISSET(tp->t_state, TS_CARR_ON) &&
-				    ISSET(tp->t_cflag, CLOCAL) &&
-				    !ISSET(t->c_cflag, CLOCAL)) {
-					CLR(tp->t_state, TS_ISOPEN);
-					SET(tp->t_state, TS_WOPEN);
+				    !ISSET(oflag, CLOCAL | MDMBUF) &&
+				    ISSET(t->c_cflag, CLOCAL | MDMBUF)) {
 					ttwakeup(tp);
+					selwakeup(&tp->t_wsel);
 				}
 				tp->t_cflag = t->c_cflag;
 				tp->t_ispeed = t->c_ispeed;
@@ -933,7 +934,7 @@
 	case TIOCSPGRP: {		/* set pgrp of tty */
 		register struct pgrp *pgrp = pgfind(*(int *)data);
 
-		if (!isctty(p, tp))
+		if (tp->t_session && !isctty(p, tp))
 			return (ENOTTY);
 		else if (pgrp == NULL)
 			return (EINVAL);
@@ -977,11 +978,11 @@
 			revents |= events & (POLLIN | POLLRDNORM);
 
 	if (events & (POLLOUT | POLLWRNORM))
-		if (tp->t_outq.c_cc <= tp->t_lowat)
+		if (CONNECTED(tp) && tp->t_outq.c_cc <= tp->t_lowat)
 			revents |= events & (POLLOUT | POLLWRNORM);
 
 	if (events & POLLHUP)
-		if (!CONNECTED(tp))
+		if (ISSET(tp->t_state, TS_ZOMBIE))
 			revents |= POLLHUP;
 
 	if (revents == 0) {
@@ -1188,8 +1189,10 @@
 			 */
 			CLR(tp->t_state, TS_CARR_ON);
 			if (ISSET(tp->t_state, TS_ISOPEN) && !CONNECTED(tp)) {
+				SET(tp->t_state, TS_ZOMBIE);
 				if (tp->t_session && tp->t_session->s_leader)
-					psignal(tp->t_session->s_leader, SIGHUP);
+					psignal(tp->t_session->s_leader,
+						SIGHUP);
 				ttyflush(tp, FREAD | FWRITE);
 				return (0);
 			}
@@ -1201,6 +1204,7 @@
 			 */
 			SET(tp->t_state, TS_CARR_ON);
 			ttwakeup(tp);
+			selwakeup(&tp->t_wsel);
 		}
 	}
 	return (1);
@@ -1358,16 +1362,13 @@
 			goto sleep;
 		}
 	} else if ((qp = &tp->t_canq)->c_cc <= 0) {
-		int carrier;
-
 sleep:
 		/*
 		 * If there is no input, sleep on rawq
 		 * awaiting hardware receipt and notification.
 		 * If we have data, we don't need to check for carrier.
 		 */
-		carrier = CONNECTED(tp);
-		if (!carrier && ISSET(tp->t_state, TS_ISOPEN)) {
+		if (ISSET(tp->t_state, TS_ZOMBIE)) {
 			splx(s);
 			return (0);	/* EOF */
 		}
@@ -1375,8 +1376,7 @@
 			splx(s);
 			return (EWOULDBLOCK);
 		}
-		error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH,
-		    carrier ? ttyin : ttopen, slp);
+		error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH, ttyin, slp);
 		splx(s);
 		/* VMIN == 0: any quantity read satisfies */
 		if (cc[VMIN] == 0 && error == EWOULDBLOCK)
@@ -1504,18 +1504,22 @@
 	cc = 0;
 loop:
 	s = spltty();
+
+	if (ISSET(tp->t_state, TS_ZOMBIE)) {
+		splx(s);
+		if (uio->uio_resid == cnt)
+			error = EIO;
+		goto out;
+	}
 	if (!CONNECTED(tp)) {
-		if (ISSET(tp->t_state, TS_ISOPEN)) {
-			splx(s);
-			return (EIO);
-		} else if (flag & IO_NDELAY) {
+		if (flag & IO_NDELAY) {
 			splx(s);
 			error = EWOULDBLOCK;
 			goto out;
 		} else {
 			/* Sleep awaiting carrier. */
 			error = ttysleep(tp,
-			    &tp->t_rawq, TTIPRI | PCATCH, ttopen, 0);
+			    &tp->t_rawq, TTIPRI | PCATCH, ttydcd, 0);
 			splx(s);
 			if (error)
 				goto out;

/Michael