git: e8923c7346a8 - main - devfs: make persistent controlling terminal an ABI property
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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))