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>