# pty.py -- Pseudo terminal utilities. # Bugs: No signal handling. Doesn't set slave termios and window size. # Only tested on Linux. # See: W. Richard Stevens. 1992. Advanced Programming in the # UNIX Environment. Chapter 19. # Author: Steen Lumholt -- with additions by Guido. from select import select import os, sys, FCNTL import tty STDIN_FILENO = 0 STDOUT_FILENO = 1 STDERR_FILENO = 2 CHILD = 0 # Open pty master. Returns (master_fd, tty_name). SGI and Linux/BSD version. def master_open(): try: import sgi except ImportError: pass else: try: tty_name, master_fd = sgi._getpty(FCNTL.O_RDWR, 0666, 0) except IOError, msg: raise os.error, msg return master_fd, tty_name for x in 'pqrstuvwxyzPQRST': for y in '0123456789abcdef': pty_name = '/dev/pty' + x + y try: fd = os.open(pty_name, FCNTL.O_RDWR) except os.error: continue return (fd, '/dev/tty' + x + y) raise os.error, 'out of pty devices' # Open the pty slave. Acquire the controlling terminal. # Returns file descriptor. Linux version. (Should be universal? --Guido) def slave_open(tty_name): return os.open(tty_name, FCNTL.O_RDWR) # Fork and make the child a session leader with a controlling terminal. # Returns (pid, master_fd) def fork(): master_fd, tty_name = master_open() pid = os.fork() if pid == CHILD: # Establish a new session. os.setsid() # Acquire controlling terminal. slave_fd = slave_open(tty_name) os.close(master_fd) # Slave becomes stdin/stdout/stderr of child. 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) # Parent and child process. return pid, master_fd # Write all the data to a descriptor. def writen(fd, data): while data != '': n = os.write(fd, data) data = data[n:] # Default read function. def read(fd): return os.read(fd, 1024) # Parent copy loop. # Copies # pty master -> standard output (master_read) # standard input -> pty master (stdin_read) def copy(master_fd, master_read=read, stdin_read=read): while 1: rfds, wfds, xfds = select( [master_fd, STDIN_FILENO], [], []) if master_fd in rfds: data = master_read(master_fd) os.write(STDOUT_FILENO, data) if STDIN_FILENO in rfds: data = stdin_read(STDIN_FILENO) writen(master_fd, data) # Create a spawned process. def spawn(argv, master_read=read, stdin_read=read): if type(argv) == type(''): argv = (argv,) pid, master_fd = fork() if pid == CHILD: apply(os.execlp, (argv[0],) + argv) mode = tty.tcgetattr(STDIN_FILENO) tty.setraw(STDIN_FILENO) try: copy(master_fd, master_read, stdin_read) except: tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)