On Sun 10 Jan 2021 at 10:25:10 -0500, Mouse wrote: > I've got the test program written. On 5.2/amd64 with /dev/ttyE1 as the > test tty (no getty running on it, of course), output works just fine > (this doesn't surprise me; I find it plausible that wscons tty > open/close are pretty much no-ops aside from possible permissions > checking on open).
I tried it too, on 9.1 amd64, and that worked too. I did not expect that. While trying to write down why I thought that, I got even more confused for a while, because there is stuff going on both with vnodes for the ctty, and pointers to its struct tty, and they are not handled together. (I also tried on Linux Ubuntu 20.10 and that worked too... just needed a small change to compile) I'm looking at current-ish source, but I suppose this hardly ever changes. This in /sys/kern/tty_tty.c works with vnodes: #define cttyvp(p) ((p)->p_lflag & PL_CONTROLT ? (p)->p_session->s_ttyvp : NULL) static int cttywrite(dev_t dev, struct uio *uio, int flag) { struct vnode *ttyvp = cttyvp(curproc); int error; if (ttyvp == NULL) return (EIO); vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY); error = VOP_WRITE(ttyvp, uio, flag, NOCRED); VOP_UNLOCK(ttyvp); return (error); } and of course my expectation was that this vnode pointer would be NULLed out somehow when the process closes the tty. In /sys/kern/tty.c, for TIOCSCTTY, there is no vnode pointer being set (s_ttyvp !) but only a struct tty * (s_ttyp !). I see no added reference count on the struct tty * for the tty that is about to become a ctty. I wonder what happens if a USB serial port is the ctty and it gets removed... probably a dangling pointer. case TIOCSCTTY: /* become controlling tty */ mutex_enter(&proc_lock); mutex_spin_enter(&tty_lock); /* Session ctty vnode pointer set in vnode layer. */ if (!SESS_LEADER(p) || ((p->p_session->s_ttyvp || tp->t_session) && (tp->t_session != p->p_session))) { mutex_spin_exit(&tty_lock); mutex_exit(&proc_lock); return (EPERM); } /* * `p_session' acquires a reference. * But note that if `t_session' is set at this point, * it must equal `p_session', in which case the session * already has the correct reference count. */ if (tp->t_session == NULL) { proc_sesshold(p->p_session); } tp->t_session = p->p_session; tp->t_pgrp = p->p_pgrp; p->p_session->s_ttyp = tp; // struct tty *tp p->p_lflag |= PL_CONTROLT; mutex_spin_exit(&tty_lock); mutex_exit(&proc_lock); break; strangely enough, I finally found the setting of p->p_session->s_ttyvp in kern/vfs_vnops.c: case VFIFO: case VCHR: case VBLK: error = VOP_IOCTL(vp, com, data, fp->f_flag, kauth_cred_get()); if (error == 0 && com == TIOCSCTTY) { vref(vp); mutex_enter(&proc_lock); ovp = curproc->p_session->s_ttyvp; curproc->p_session->s_ttyvp = vp; mutex_exit(&proc_lock); if (ovp != NULL) vrele(ovp); } return (error); so you can conceivably set a FIFO or a block device or a random raw disk also as controlling tty? At least it does reference counting here. Why the code for TIOCSCTTY is split in two parts remains unclear. I searched for other devices to handle the lower half of it and found none (it wouldn't make sense anyway). Neither is [4.3BSD] "The Design and Implementation of the 4.3BSD UNIX Operating System" illuminating; its chapter 9 on Terminal handling hardly mentions details about process groups, and sessions not at all. [FBSD] "The Design and Implementation of the FreeBSD Operating System (Version 5.2)" at least briefly describes sessions (section 10.5) but doesn't explain any of this either. Maybe the existence of this split explains why session leaders are not allowed to relinquish their ctty using TIOCNOTTY (because it messes with the current reference counting): kern/tty_tty.c: cttyioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) { struct vnode *ttyvp = cttyvp(l->l_proc); ... if (cmd == TIOCNOTTY) { mutex_enter(&proc_lock); if (!SESS_LEADER(l->l_proc)) { l->l_proc->p_lflag &= ~PL_CONTROLT; rv = 0; } else rv = EINVAL; mutex_exit(&proc_lock); return (rv); } return (VOP_IOCTL(ttyvp, cmd, addr, flag, NOCRED)); } It seems that the ctty vnode pointer from the session, once set, is never allowed to go back to NULL (unless by creating a new session with a fresh NULL pointer in it). This despite that /dev/tty could handle that. [Stevens] "Advanced Programming in the UNIX Environment" from Stevens is more explanatory about sessions etc (but it also says that BSD has no session IDs, but by now NetBSD clearly does have them). ([Stevens] writes on p.246 "The session leader that establishes the connection to the controlling terminal is called the controlling process" and "... the hang-up signal is sent to the controlling process (the session leader)"). In other words, controlling ttys are pretty strongly associated with the session leader process, which seems to match the code. Unfortunately [Stevens] also doesn't cover the corner case either. > /~\ The ASCII Mouse -Olaf. -- Olaf 'Rhialto' Seibert -- rhialto at falu dot nl ___ Anyone who is capable of getting themselves made President should on \X/ no account be allowed to do the job. --Douglas Adams, "THGTTG"
signature.asc
Description: PGP signature