From d224e6ab9c925bdc2dcbd2e6a01a7cee1877f75a Mon Sep 17 00:00:00 2001 From: Soumendra Ganguly Date: Thu, 6 Aug 2020 06:51:06 -0500 Subject: [PATCH 1/4] Add window resizing support [ SIGWINCH ] --- Lib/pty.py | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 2 deletions(-) diff --git a/Lib/pty.py b/Lib/pty.py index a32432041fa1db..02578ea2466f84 100644 --- a/Lib/pty.py +++ b/Lib/pty.py @@ -1,6 +1,6 @@ """Pseudo terminal utilities.""" -# Bugs: No signal handling. Doesn't set slave termios and window size. +# Bugs: No signal handling. Doesn't set slave termios. # Only tested on Linux. # See: W. Richard Stevens. 1992. Advanced Programming in the # UNIX Environment. Chapter 19. @@ -10,8 +10,13 @@ import os import sys import tty +import signal +import struct +import fcntl +import termios +import time -__all__ = ["openpty","fork","spawn"] +__all__ = ["openpty","fork","spawn","wspawn"] STDIN_FILENO = 0 STDOUT_FILENO = 1 @@ -170,3 +175,125 @@ def spawn(argv, master_read=_read, stdin_read=_read): os.close(master_fd) return os.waitpid(pid, 0)[1] + +# Author: Soumendra Ganguly. +SIGWINCH = signal.SIGWINCH + +def _login_pty(master_fd, slave_fd): + """Given a pty, makes the calling process a session leader, makes the pty slave + its controlling terminal, stdin, stdout, and stderr. Closes both pty ends.""" + # Establish a new session. + os.setsid() + os.close(master_fd) + + try: + fcntl.ioctl(slave_fd, termios.TIOCSCTTY) # Make the pty slave the controlling terminal. + except: + os.close(slave_fd) + raise + + # Slave becomes stdin/stdout/stderr. + os.dup2(slave_fd, STDIN_FILENO) + os.dup2(slave_fd, STDOUT_FILENO) + os.dup2(slave_fd, STDERR_FILENO) + if (slave_fd > STDERR_FILENO): + os.close(slave_fd) + +def _winresz(pty_slave): + """Resize window.""" + w = struct.pack('HHHH', 0, 0, 0, 0) + s = fcntl.ioctl(STDIN_FILENO, termios.TIOCGWINSZ, w) + fcntl.ioctl(pty_slave, termios.TIOCSWINSZ, s) + +def _create_hwinch(pty_slave): + """Creates SIGWINCH handler.""" + def _hwinch(signum, frame): + try: + _winresz(pty_slave) + except: + pass + return _hwinch + +def _cleanup(master_fd, slave_fd, old_hwinch, tty_mode): + """Performs cleanup in wspawn.""" + + # Close both pty ends. + os.close(master_fd) + os.close(slave_fd) + + # Restore original tty attributes. + if tty_mode != None: + tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, tty_mode) + + # Restore original SIGWINCH signal handler. + try: + signal.signal(SIGWINCH, old_hwinch) + except: + pass + +def _ekill(child_pid): + """Kill spawned process due to exception.""" + try: + os.kill(child_pid, signal.SIGTERM) + time.sleep(1) + os.kill(child_pid, signal.SIGKILL) + os.waitpid(child_pid, 0) + except: + pass + +def _wcopy(master_fd, child_pid, master_read=_read, stdin_read=_read, timeout=0.01): + """Parent copy loop for wspawn.""" + fds = [master_fd, STDIN_FILENO] + ret = (0,0) + while True: + rfds, wfds, xfds = select(fds, [], [], timeout) + if ret == (0,0): + ret = os.waitpid(child_pid, os.WNOHANG) + elif len(rfds) == 0: + break; + if master_fd in rfds: + data = master_read(master_fd) + if not data: # Reached EOF. + fds.remove(master_fd) + else: + os.write(STDOUT_FILENO, data) + if STDIN_FILENO in rfds: + data = stdin_read(STDIN_FILENO) + if not data: + fds.remove(STDIN_FILENO) + else: + _writen(master_fd, data) + return ret + +def wspawn(argv, master_read=_read, stdin_read=_read, timeout=0.01): + """Create a spawned process with + terminal window resizing enabled.""" + if type(argv) == type(''): + argv = (argv,) + sys.audit('pty.wspawn', argv) + bk_hwinch = signal.getsignal(SIGWINCH) + master_fd, slave_fd = openpty() + try: + _winresz(slave_fd) + signal.signal(SIGWINCH, _create_hwinch(slave_fd)) + except: # User should handle exception and try spawn instead. + _cleanup(master_fd, slave_fd, bk_hwinch, None) + raise + pid = os.fork() + if pid == CHILD: + _login_pty(master_fd, slave_fd) + os.execlp(argv[0], *argv) + try: + mode = tty.tcgetattr(STDIN_FILENO) + tty.setraw(STDIN_FILENO) + except tty.error: # This is the same as termios.error + mode = None + try: + ret = _wcopy(master_fd, pid, master_read, stdin_read, timeout)[1] + except: + _ekill(pid) + _cleanup(master_fd, slave_fd, bk_hwinch, mode) + raise + + _cleanup(master_fd, slave_fd, bk_hwinch, mode) + return ret From 7220f139d57ab7c9c9a457222a2051d40a9f422a Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2020 08:47:09 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NEWS.d/next/Library/2020-08-07-08-47-08.bpo-41494.ql-kB9.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2020-08-07-08-47-08.bpo-41494.ql-kB9.rst diff --git a/Misc/NEWS.d/next/Library/2020-08-07-08-47-08.bpo-41494.ql-kB9.rst b/Misc/NEWS.d/next/Library/2020-08-07-08-47-08.bpo-41494.ql-kB9.rst new file mode 100644 index 00000000000000..918118a389c956 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-08-07-08-47-08.bpo-41494.ql-kB9.rst @@ -0,0 +1 @@ +Adds pty.wspawn, which is like pty.spawn with window resizing support [ handles SIGWINCH ]. \ No newline at end of file From 92c8772f8135145d495bd4a4ee29c729995fc9cb Mon Sep 17 00:00:00 2001 From: Soumendra Ganguly Date: Fri, 7 Aug 2020 19:48:07 -0500 Subject: [PATCH 3/4] Modify _ekill() --- Lib/pty.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/pty.py b/Lib/pty.py index 02578ea2466f84..8b15a2a6dcdebd 100644 --- a/Lib/pty.py +++ b/Lib/pty.py @@ -237,6 +237,9 @@ def _ekill(child_pid): os.kill(child_pid, signal.SIGTERM) time.sleep(1) os.kill(child_pid, signal.SIGKILL) + except: + pass + try: os.waitpid(child_pid, 0) except: pass From bf2f1b2ddce04fafb50bdb0b48501379c9463f82 Mon Sep 17 00:00:00 2001 From: Soumendra Ganguly <67527439+8vasu@users.noreply.github.com> Date: Mon, 10 Aug 2020 20:32:09 -0500 Subject: [PATCH 4/4] Update 2020-08-07-08-47-08.bpo-41494.ql-kB9.rst --- .../next/Library/2020-08-07-08-47-08.bpo-41494.ql-kB9.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2020-08-07-08-47-08.bpo-41494.ql-kB9.rst b/Misc/NEWS.d/next/Library/2020-08-07-08-47-08.bpo-41494.ql-kB9.rst index 918118a389c956..d6c2db9615dbed 100644 --- a/Misc/NEWS.d/next/Library/2020-08-07-08-47-08.bpo-41494.ql-kB9.rst +++ b/Misc/NEWS.d/next/Library/2020-08-07-08-47-08.bpo-41494.ql-kB9.rst @@ -1 +1 @@ -Adds pty.wspawn, which is like pty.spawn with window resizing support [ handles SIGWINCH ]. \ No newline at end of file +Adds pty.wspawn, which is like pty.spawn + window resizing support [ handles SIGWINCH ].