git: e8923c7346a8 - main - devfs: make persistent controlling terminal an ABI property

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Tue, 20 Oct 2026 17:40:35 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=e8923c7346a882418352683f72e7c4f8e68d6907

commit e8923c7346a882418352683f72e7c4f8e68d6907
Author:     Vladimir Grebenshchikov <vova@zote.me>
AuthorDate: 2026-10-13 23:04:27 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-10-20 17:38:09 +0000

    devfs: make persistent controlling terminal an ABI property
    
    Linux leaves the session's controlling terminal in place after the
    last file descriptor on the slave is closed, so a later open of
    /dev/tty succeeds.  FreeBSD drops the session vnode reference on that
    close. /dev/tty then fails, which breaks Linux programs such as xterm
    that close the pty slave during startup and expect /dev/tty to keep
    working.
    
    When the closer is a Linux process, leave the session reference in
    place.  Still pass FLASTCLOSE to the driver so the master sees hangup.
    Native close behavior is unchanged.
    
    Add the SV_PERSISTENT_CTTY sysentvec flag for ABIs that retain the
    controlling terminal after the last slave descriptor is closed.  This
    keeps the devfs behavior ABI-agnostic and allows other ABIs to request
    the same semantics if needed.  Use the flag in devfs_close().
    
    Reviewed by:    kib
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D60701
---
 sys/amd64/linux/linux_sysvec.c     | 2 +-
 sys/amd64/linux32/linux32_sysvec.c | 3 ++-
 sys/arm64/linux/linux_sysvec.c     | 2 +-
 sys/fs/devfs/devfs_vnops.c         | 8 +++++++-
 sys/i386/linux/linux_sysvec.c      | 5 +++--
 sys/sys/sysent.h                   | 1 +
 6 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/sys/amd64/linux/linux_sysvec.c b/sys/amd64/linux/linux_sysvec.c
index 1134efd1c726..ca1608ab8ac8 100644
--- a/sys/amd64/linux/linux_sysvec.c
+++ b/sys/amd64/linux/linux_sysvec.c
@@ -705,7 +705,7 @@ struct sysentvec elf_linux_sysvec = {
 	.sv_fixlimit	= NULL,
 	.sv_maxssiz	= NULL,
 	.sv_flags	= SV_ABI_LINUX | SV_LP64 | SV_SHP | SV_SIG_DISCIGN |
-	    SV_SIG_WAITNDQ | SV_TIMEKEEP,
+	    SV_SIG_WAITNDQ | SV_TIMEKEEP | SV_PERSISTENT_CTTY,
 	.sv_set_syscall_retval = linux_set_syscall_retval,
 	.sv_fetch_syscall_args = linux_fetch_syscall_args,
 	.sv_syscallnames = linux_syscallnames,
diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c
index f42654967e8f..82b4b7522503 100644
--- a/sys/amd64/linux32/linux32_sysvec.c
+++ b/sys/amd64/linux32/linux32_sysvec.c
@@ -805,7 +805,8 @@ struct sysentvec elf_linux_sysvec = {
 	.sv_fixlimit	= linux32_fixlimit,
 	.sv_maxssiz	= &linux32_maxssiz,
 	.sv_flags	= SV_ABI_LINUX | SV_ILP32 | SV_SHP |
-	    SV_SIG_DISCIGN | SV_SIG_WAITNDQ | SV_TIMEKEEP,
+	    SV_SIG_DISCIGN | SV_SIG_WAITNDQ | SV_TIMEKEEP |
+	    SV_PERSISTENT_CTTY,
 	.sv_set_syscall_retval = linux32_set_syscall_retval,
 	.sv_fetch_syscall_args = linux32_fetch_syscall_args,
 	.sv_syscallnames = linux32_syscallnames,
diff --git a/sys/arm64/linux/linux_sysvec.c b/sys/arm64/linux/linux_sysvec.c
index 9afb6fb143d6..0c441bf2d071 100644
--- a/sys/arm64/linux/linux_sysvec.c
+++ b/sys/arm64/linux/linux_sysvec.c
@@ -449,7 +449,7 @@ struct sysentvec elf_linux_sysvec = {
 	.sv_fixlimit	= NULL,
 	.sv_maxssiz	= NULL,
 	.sv_flags	= SV_ABI_LINUX | SV_LP64 | SV_SHP | SV_SIG_DISCIGN |
-	    SV_SIG_WAITNDQ | SV_TIMEKEEP,
+	    SV_SIG_WAITNDQ | SV_TIMEKEEP | SV_PERSISTENT_CTTY,
 	.sv_set_syscall_retval = linux_set_syscall_retval,
 	.sv_fetch_syscall_args = linux_fetch_syscall_args,
 	.sv_syscallnames = linux_syscallnames,
diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c
index 9ed7ef9f0aed..721353344b07 100644
--- a/sys/fs/devfs/devfs_vnops.c
+++ b/sys/fs/devfs/devfs_vnops.c
@@ -59,6 +59,7 @@
 #include <sys/stat.h>
 #include <sys/sx.h>
 #include <sys/sysctl.h>
+#include <sys/sysent.h>
 #include <sys/time.h>
 #include <sys/ttycom.h>
 #include <sys/unistd.h>
@@ -716,8 +717,13 @@ devfs_close(struct vop_close_args *ap)
 	 * process' controlling terminal.  In that case,
 	 * if the reference count is 2 (this last descriptor
 	 * plus the session), release the reference from the session.
+	 *
+	 * Some ABIs keep the controlling terminal after the last
+	 * slave descriptor is closed.  A closer with SV_PERSISTENT_CTTY
+	 * leaves the session reference in place.
 	 */
-	if (de->de_usecount == 2 && td != NULL) {
+	if (!SV_PROC_FLAG(curproc, SV_PERSISTENT_CTTY) &&
+	    de->de_usecount == 2 && td != NULL) {
 		p = td->td_proc;
 		PROC_LOCK(p);
 		if (vp == p->p_session->s_ttyvp) {
diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c
index 14b5f64388d2..333f8d21cdb3 100644
--- a/sys/i386/linux/linux_sysvec.c
+++ b/sys/i386/linux/linux_sysvec.c
@@ -597,7 +597,7 @@ struct sysentvec linux_sysvec = {
 	.sv_fixlimit	= NULL,
 	.sv_maxssiz	= NULL,
 	.sv_flags	= SV_ABI_LINUX | SV_AOUT | SV_ILP32 |
-	    SV_SIG_DISCIGN | SV_SIG_WAITNDQ,
+	    SV_SIG_DISCIGN | SV_SIG_WAITNDQ | SV_PERSISTENT_CTTY,
 	.sv_set_syscall_retval = linux_set_syscall_retval,
 	.sv_fetch_syscall_args = linux_fetch_syscall_args,
 	.sv_syscallnames = linux_syscallnames,
@@ -641,7 +641,8 @@ struct sysentvec elf_linux_sysvec = {
 	.sv_fixlimit	= NULL,
 	.sv_maxssiz	= NULL,
 	.sv_flags	= SV_ABI_LINUX | SV_ILP32 | SV_SHP |
-	    SV_SIG_DISCIGN | SV_SIG_WAITNDQ | SV_TIMEKEEP,
+	    SV_SIG_DISCIGN | SV_SIG_WAITNDQ | SV_TIMEKEEP |
+	    SV_PERSISTENT_CTTY,
 	.sv_set_syscall_retval = linux_set_syscall_retval,
 	.sv_fetch_syscall_args = linux_fetch_syscall_args,
 	.sv_syscallnames = NULL,
diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h
index b2a559da7844..8b45d0cb4dff 100644
--- a/sys/sys/sysent.h
+++ b/sys/sys/sysent.h
@@ -175,6 +175,7 @@ struct sysentvec {
 #define	SV_SIG_DISCIGN	0x200000	/* Do not discard ignored signals */
 #define	SV_SIG_WAITNDQ	0x400000	/* Wait does not dequeue SIGCHLD */
 #define	SV_DSO_SIG	0x800000	/* Signal trampoline packed in dso */
+#define	SV_PERSISTENT_CTTY 0x1000000 /* Keep ctty after the last close */
 
 #define	SV_ABI_MASK	0xff
 #define	SV_PROC_FLAG(p, x)	((p)->p_sysent->sv_flags & (x))