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"

Attachment: signature.asc
Description: PGP signature

Reply via email to