From 4fde0cf158fe61c65b4ae45bba6c73ec788b53b4 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 20 Jul 2026 11:48:30 +0300 Subject: [PATCH] gh-154225: Fix os.openpty() acquiring a controlling terminal on Solaris (GH-154229) Co-Authored-By: Claude Fable 5 (cherry picked from commit a0caab9db6c2655d8429279892848d817343f99d) --- ...07-20-14-10-00.gh-issue-154225.openpty.rst | 2 ++ Modules/posixmodule.c | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-07-20-14-10-00.gh-issue-154225.openpty.rst diff --git a/Misc/NEWS.d/next/Library/2026-07-20-14-10-00.gh-issue-154225.openpty.rst b/Misc/NEWS.d/next/Library/2026-07-20-14-10-00.gh-issue-154225.openpty.rst new file mode 100644 index 000000000000000..f15022b66320062 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-20-14-10-00.gh-issue-154225.openpty.rst @@ -0,0 +1,2 @@ +Fix :func:`os.openpty` on Solaris and illumos: it no longer leaves the +pseudo-terminal as the controlling terminal of the calling process. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 668383889a7763c..00889f9cf6689c2 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -8863,11 +8863,30 @@ os_openpty_impl(PyObject *module) goto posix_error; #if !defined(__CYGWIN__) && !defined(__ANDROID__) && !defined(HAVE_DEV_PTC) + // Pushing "ptem" makes the slave a terminal, which a session leader + // without a controlling terminal then acquires as one despite O_NOCTTY. + // Note whether we already had one, so a new one can be disowned below. + int had_ctty = 0; +#ifdef TIOCNOTTY + int tty_fd = open("/dev/tty", O_RDONLY | O_NOCTTY); + if (tty_fd >= 0) { + had_ctty = 1; + close(tty_fd); + } +#endif ioctl(slave_fd, I_PUSH, "ptem"); /* push ptem */ ioctl(slave_fd, I_PUSH, "ldterm"); /* push ldterm */ #ifndef __hpux ioctl(slave_fd, I_PUSH, "ttcompat"); /* push ttcompat */ #endif /* __hpux */ +#ifdef TIOCNOTTY + if (!had_ctty && getsid(0) == getpid()) { + // Disown it; TIOCNOTTY sends SIGHUP to the session leader. + PyOS_sighandler_t sig_saved = PyOS_setsig(SIGHUP, SIG_IGN); + ioctl(slave_fd, TIOCNOTTY); + PyOS_setsig(SIGHUP, sig_saved); + } +#endif #endif /* HAVE_CYGWIN */ #endif /* HAVE_OPENPTY */