0
0
mirror of https://github.com/nodejs/node.git synced 2024-12-01 16:10:02 +01:00
nodejs/tools/pseudo-tty.py
Moshe Atlow 9e5e2f1dc5
test: migrate a pseudo_tty test to use assertSnapshot
PR-URL: https://github.com/nodejs/node/pull/47803
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
2023-05-04 08:34:23 +00:00

105 lines
2.3 KiB
Python
Executable File

#!/usr/bin/env python3
import errno
import os
import pty
import select
import signal
import sys
import termios
STDIN = 0
STDOUT = 1
STDERR = 2
def pipe(sfd, dfd):
try:
data = os.read(sfd, 256)
except OSError as e:
if e.errno != errno.EIO:
raise
return True # EOF
if not data:
return True # EOF
if dfd == STDOUT:
# Work around platform quirks. Some platforms echo ^D as \x04
# (AIX, BSDs) and some don't (Linux).
#
# The comparison against b' '[0] is because |data| is
# a string in python2 but a bytes object in python3.
filt = lambda c: c >= b' '[0] or c in b'\t\n\r\f'
data = filter(filt, data)
data = bytes(data)
while data:
try:
n = os.write(dfd, data)
except OSError as e:
if e.errno != errno.EIO:
raise
return True # EOF
data = data[n:]
if __name__ == '__main__':
argv = sys.argv[1:]
# Make select() interruptable by SIGCHLD.
signal.signal(signal.SIGCHLD, lambda nr, _: None)
parent_fd, child_fd = pty.openpty()
assert parent_fd > STDIN
mode = termios.tcgetattr(child_fd)
# Don't translate \n to \r\n.
mode[1] = mode[1] & ~termios.ONLCR # oflag
# Disable ECHOCTL. It's a BSD-ism that echoes e.g. \x04 as ^D but it
# doesn't work on platforms like AIX and Linux. I checked Linux's tty
# driver and it's a no-op, the driver is just oblivious to the flag.
mode[3] = mode[3] & ~termios.ECHOCTL # lflag
termios.tcsetattr(child_fd, termios.TCSANOW, mode)
pid = os.fork()
if not pid:
os.setsid()
os.close(parent_fd)
# Ensure the pty is a controlling tty.
name = os.ttyname(child_fd)
fd = os.open(name, os.O_RDWR)
os.dup2(fd, child_fd)
os.close(fd)
os.dup2(child_fd, STDIN)
os.dup2(child_fd, STDOUT)
os.dup2(child_fd, STDERR)
if child_fd > STDERR:
os.close(child_fd)
os.execve(argv[0], argv, os.environ)
raise Exception('unreachable')
os.close(child_fd)
fds = [STDIN, parent_fd]
while fds:
try:
rfds, _, _ = select.select(fds, [], [])
except select.error as e:
if e[0] != errno.EINTR:
raise
if pid == os.waitpid(pid, os.WNOHANG)[0]:
break
if STDIN in rfds:
if pipe(STDIN, parent_fd):
fds.remove(STDIN)
if parent_fd in rfds:
if pipe(parent_fd, STDOUT):
break