Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 4 Jul 2001 01:15:17 +1000 (EST)
From:      Bruce Evans <bde@zeta.org.au>
To:        Ruslan Ermilov <ru@FreeBSD.ORG>
Cc:        Alfred Perlstein <bright@sneakerz.org>, current@FreeBSD.ORG
Subject:   Re: TIOCSCTTY
Message-ID:  <Pine.BSF.4.21.0107032328020.41126-100000@besplex.bde.org>
In-Reply-To: <20010703152523.C39090@sunbay.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Tue, 3 Jul 2001, Ruslan Ermilov wrote:

> Weird.  I figured out what causes this, it's moused(8).  I have inserted
> some debug code into it to see what's going on:
> ...
>   LINE RAW CAN OUT IHIWT ILOWT OHWT LWT     COL STATE  SESS      PGID DISC
>   cuaa1  0   0   0   512   448  216  60       0 OCcl          0     0 term
> consolectl  0   0   0   512   448 1296 256      98 OCc    c0b14700     6 term
>       0  0   0   0     0     0    0   0       0 -             0     0 term
>   ttyp0  0   0   0     0     0    0   0       0 -             0     0 term
>   ttyv0  0   0   0   512   448 1296 256       0 -             0     0 term
> 
> As you can see, process 6 (sh /etc/rc autoboot) is the session leader with
> /dev/console as the controlling terminal, and the same session is referenced
> from the `tty' structure for the consolectl device.

The bug seems to be caused by a combination of sloppy code in moused,
dubious aliasing in syscons and a known bug in cnclose():
1. moused opens /dev/consolectl before becoming a daemon.  This shouldn't
   be a problem, since /dev/consolectl should be a completely different device
   from the controlling terminal (which is /dev/console, although you can't
   really see that from the ps output since "consolectl" is a poorly chosen
   name which is indistinguishable from "console" after ps truncates it to
   3 characters).  However:
2. syscons.c makes /dev/consolectl a sort of alias for /dev/consolectl:

    dev = make_dev(&sc_cdevsw, SC_CONSOLECTL,
		   UID_ROOT, GID_WHEEL, 0600, "consolectl");
    dev->si_tty = sc_console_tty = ttymalloc(sc_console_tty);

   This obviously breaks the pstat output and complicates debugging (pstat
   should display "console" instead of "consolectl").  It apparently also
   breaks last-close stuff when /dev/console is closed.
3. cnclose() already has broken handling of controlling terminals when
   /dev/console is last-closed while the physical device underlying the
   console is open and /dev/console is a controlling terminal.
   /dev/consolectl is different from the physical device underlying
   /dev/console (even if the latter is /dev/ttyv0), and is not understood
   by cnclose(), but I think the same problem and fix apply.

> Next, this is after moused(8) calls daemon(3):
> 
> USER     PID  PPID  PGID   SESS JOBC STAT  TT       TIME COMMAND
> ...
> root     221     1   221 bf8280    0 Ss    ??    0:00.00 moused -3 -p /dev/cuaa1 -t auto
> root     223   221   221 bf8280    0 S     ??    0:00.00 sh -c (ps axjww; pstat -t) >>/1
> root     225   223   221 bf8280    0 S     ??    0:00.00 sh -c (ps axjww; pstat -t) >>/1
> root     227   225   221 bf8280    0 R     ??    0:00.00 ps axjww
> root       6     1     6 b14700    0 Ss+  con    0:00.14 sh /etc/rc autoboot
> root     228     6     6 b14700    0 R+   con    0:00.00 sh /etc/rc autoboot

No problem here.

>   LINE RAW CAN OUT IHIWT ILOWT OHWT LWT     COL STATE  SESS      PGID DISC
>   cuaa1  0   0   0   512   448  216  60       0 OCcl          0     0 term
> consolectl  0   0   0   512   448 1296 256     109 OCc    c0b14700     6 term
>       0  0   0   0     0     0    0   0       0 -             0     0 term
>   ttyp0  0   0   0     0     0    0   0       0 -             0     0 term
>   ttyv0  0   0   0   512   448 1296 256       0 -             0     0 term
> 
> Nothing has changed re: consolectl.  It still has session c0b14700 (with
> the session leader PID = 6) bound to it.

Nothing should have changed, since moused only closed a dup'ed descriptor
for /dev/console.  The last-close bugs will bite later when ancestors of
moused all close all their descriptors for /dev/console.  Here we can still
see the consolectl vs console confusion.

> Next, this is the output after the system has booted into multi-user:
> ...
> The session c0b14700 has disappeared from the ps(1) output, but it is still
> referenced from the `tty' structure:
> 
>   LINE RAW CAN OUT IHIWT ILOWT OHWT LWT     COL STATE  SESS      PGID DISC
>   ttyvb  0   0   0   512   448 1296 256       0 -             0     0 term
>   ttyva  0   0   0   512   448 2052 256       7 OCc    c0c02c80   263 term
>   ttyv9  0   0   0   512   448 2052 256       7 OCc    c0c02d40   262 term
>   ttyv8  0   0   0   512   448 2052 256       7 OCc    c0c02e00   261 term
>   ttyv7  0   0   0   512   448 2052 256       7 OCc    c0c02800   260 term
>   ttyv6  0   0   0   512   448 2052 256       7 OCc    c0c028c0   259 term
>   ttyv5  0   0   0   512   448 2052 256       7 OCc    c0c02900   258 term
>   ttyv4  0   0   0   512   448 2052 256       7 OCc    c0c02940   257 term
>   ttyv3  0   0   0   512   448 2052 256       7 OCc    c0c02980   256 term
>   ttyv2  0   0   0   512   448 2052 256       7 OCc    c0c02a40   255 term
>   ttyv1  0   0   0   512   448 2052 256       0 OCc    c0c02b00   279 term
>   cuaa1  0   0   0   512   448  216  60       0 OCcl          0     0 term
> consolectl  0   0   0   512   448 1296 256       0 OCc    c0b14700     0 term
>       0  0   0   0     0     0    0   0       0 -             0     0 term
>   ttyp0  0   0   0     0     0    0   0       0 -             0     0 term
>   ttyv0  0   0   0   512   448 2052 256       7 OCc    c0c02bc0   253 term

Hmm.  ttyv0 apparently doesn't share a tty struct with consolectl/console.
I'm familiar with this feature from old versions of syscons but thought that
it was lost.  This used to prevent bug (3) for syscons (it always affected
sio and maybe pcvt), but we now have it via the consolectl alias.

I wonder if the bogus line of zeros is related to miscounting the single
tty struct for console/consolectl as two structs.

> What I can't understand is how opening a /dev/consolectl in a new session
> doesn't allow the t_session tty pointer to be reset that points to another
> (not existing) session.
> 
> (This is probably somehow relates to the fact that the device's close()
> routine is called only on a last reference drop, but I'm not sure.)

I think I understand the details now:
- on i386's, sccnprobe() sets the physical device for /dev/console to
  /dev/consolectl.  Thus /dev/consolectl is more than "sort of" an
  alias for /dev/console, and bug (3) bites.  I don't understand the
  minor detail that pstat prefers the "consolectl" alias.
- on alphas, sccnattach() sets the physical device for /dev/console to
  /dev/ttyv0.  Thus the bugs are gratuitously different.

I use the following fix for (3).  This is not suitable for committing,
due to probable races clearing the state.  E.g., p_session can apparently
change before d_close() returns, so my closing_ctty flag may become
invalid, but since there is no explicit locking it's not clear that
other fatal changes can't happen.  Hopefully everything is protected
by Giant until d_close() blocks, but the code after d_close() blocks
isn't very careful about the state.  It needs to be, because d_open()
can be called successfully while d_close() is blocked!  Note that
d_close() can block for arbitrarily long waiting for output to drain,
and it is useful for d_open() to succeed while d_close() is busy,
if only to unblock the close by changing the drain wait time.

---
Index: fs/specfs/spec_vnops.c
===================================================================
RCS file: /home/ncvs/src/sys/fs/specfs/spec_vnops.c,v
retrieving revision 1.159
diff -u -2 -r1.159 spec_vnops.c
--- fs/specfs/spec_vnops.c	2001/05/23 22:20:29	1.159
+++ fs/specfs/spec_vnops.c	2001/05/24 11:22:57
@@ -570,4 +570,5 @@
 	struct proc *p = ap->a_p;
 	dev_t dev = vp->v_rdev;
+	int closing_ctty, error;
 
 	/*
@@ -579,9 +580,12 @@
 	 * if the reference count is 2 (this last descriptor
 	 * plus the session), release the reference from the session.
+	 * But don't clear s_ttyvp yet, since at least cnclose()
+	 * needs it.
 	 */
+	closing_ctty = 0;
 	if (vcount(vp) == 2 && p && (vp->v_flag & VXLOCK) == 0 &&
 	    vp == p->p_session->s_ttyvp) {
 		vrele(vp);
-		p->p_session->s_ttyvp = NULL;
+		closing_ctty = 1;
 	}
 	/*
@@ -601,5 +605,8 @@
 		return (0);
 	}
-	return (devsw(dev)->d_close(dev, ap->a_fflag, S_IFCHR, p));
+	error = devsw(dev)->d_close(dev, ap->a_fflag, S_IFCHR, p);
+	if (closing_ctty)
+		p->p_session->s_ttyvp = NULL;
+	return (error);
 }
 
Index: kern/tty_cons.c
===================================================================
RCS file: /home/ncvs/src/sys/kern/tty_cons.c,v
retrieving revision 1.91
diff -u -2 -r1.91 tty_cons.c
--- kern/tty_cons.c	2001/06/13 10:58:36	1.91
+++ kern/tty_cons.c	2001/06/14 04:05:36
@@ -50,4 +50,5 @@
 #include <sys/tty.h>
 #include <sys/uio.h>
+#include <sys/vnode.h>
 
 #include <machine/cpu.h>
@@ -297,5 +298,19 @@
 		return (0);
 	cndev = cn_tab->cn_dev;
+
+	/*
+	 * Close the controlling tty aspects of the tty if the tty is a
+	 * controlling tty and it belongs to the device being closed.
+	 * This is normally done in ttyclose(), but we won't reach there
+	 * if both devices are open.
+	 */
 	cn_tp = cndev->si_tty;
+	if (cn_tp != NULL && cn_tp->t_session != NULL &&
+	    cn_tp->t_session->s_ttyvp != NULL &&
+	    cn_tp->t_session->s_ttyvp->v_rdev == dev) {
+		cn_tp->t_pgrp = NULL;
+		cn_tp->t_session = NULL;
+	}
+
 	/*
 	 * act appropriatly depending on whether it's /dev/console
@@ -307,13 +322,6 @@
 		/* the physical device is about to be closed */
 		cn_phys_is_open = 0;
-		if (cn_is_open) {
-			if (cn_tp) {
-				/* perform a ttyhalfclose() */
-				/* reset session and proc group */
-				cn_tp->t_pgrp = NULL;
-				cn_tp->t_session = NULL;
-			}
+		if (cn_is_open)
 			return (0);
-		}
 	} else if (major(dev) != major(cndev)) {
 		/* the logical console is about to be closed */
---

Bruce


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.4.21.0107032328020.41126-100000>