blob: d31efe4b2e2895ebba9b2fce75d5a513176c988f [file] [log] [blame]
Guido van Rossum54f22ed2000-02-04 15:10:34 +00001"""Pseudo terminal utilities."""
Guido van Rossum23cb2a81994-09-12 10:36:35 +00002
3# Bugs: No signal handling. Doesn't set slave termios and window size.
4# Only tested on Linux.
5# See: W. Richard Stevens. 1992. Advanced Programming in the
6# UNIX Environment. Chapter 19.
7# Author: Steen Lumholt -- with additions by Guido.
8
9from select import select
Guido van Rossum96d80f91999-05-03 18:13:51 +000010import os, FCNTL
Guido van Rossum23cb2a81994-09-12 10:36:35 +000011import tty
12
13STDIN_FILENO = 0
14STDOUT_FILENO = 1
15STDERR_FILENO = 2
16
17CHILD = 0
18
Guido van Rossum23cb2a81994-09-12 10:36:35 +000019def master_open():
Guido van Rossum54f22ed2000-02-04 15:10:34 +000020 """Open pty master and return (master_fd, tty_name).
21 SGI and Linux/BSD version."""
Guido van Rossum23cb2a81994-09-12 10:36:35 +000022 try:
23 import sgi
24 except ImportError:
25 pass
26 else:
27 try:
28 tty_name, master_fd = sgi._getpty(FCNTL.O_RDWR, 0666, 0)
29 except IOError, msg:
30 raise os.error, msg
31 return master_fd, tty_name
32 for x in 'pqrstuvwxyzPQRST':
33 for y in '0123456789abcdef':
34 pty_name = '/dev/pty' + x + y
35 try:
36 fd = os.open(pty_name, FCNTL.O_RDWR)
37 except os.error:
38 continue
39 return (fd, '/dev/tty' + x + y)
40 raise os.error, 'out of pty devices'
41
Guido van Rossum23cb2a81994-09-12 10:36:35 +000042def slave_open(tty_name):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000043 """Open the pty slave and acquire the controlling terminal.
44 Return the file descriptor. Linux version."""
45 # (Should be universal? --Guido)
Guido van Rossum23cb2a81994-09-12 10:36:35 +000046 return os.open(tty_name, FCNTL.O_RDWR)
47
Guido van Rossum23cb2a81994-09-12 10:36:35 +000048def fork():
Guido van Rossum54f22ed2000-02-04 15:10:34 +000049 """Fork and make the child a session leader with a controlling terminal.
50 Return (pid, master_fd)."""
Guido van Rossum23cb2a81994-09-12 10:36:35 +000051 master_fd, tty_name = master_open()
52 pid = os.fork()
53 if pid == CHILD:
54 # Establish a new session.
55 os.setsid()
56
57 # Acquire controlling terminal.
58 slave_fd = slave_open(tty_name)
59 os.close(master_fd)
60
61 # Slave becomes stdin/stdout/stderr of child.
62 os.dup2(slave_fd, STDIN_FILENO)
63 os.dup2(slave_fd, STDOUT_FILENO)
64 os.dup2(slave_fd, STDERR_FILENO)
65 if (slave_fd > STDERR_FILENO):
66 os.close (slave_fd)
67
68 # Parent and child process.
69 return pid, master_fd
70
Guido van Rossum23cb2a81994-09-12 10:36:35 +000071def writen(fd, data):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000072 """Write all the data to a descriptor."""
Guido van Rossum23cb2a81994-09-12 10:36:35 +000073 while data != '':
74 n = os.write(fd, data)
75 data = data[n:]
76
Guido van Rossum23cb2a81994-09-12 10:36:35 +000077def read(fd):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000078 """Default read function."""
Guido van Rossum23cb2a81994-09-12 10:36:35 +000079 return os.read(fd, 1024)
80
Guido van Rossum23cb2a81994-09-12 10:36:35 +000081def copy(master_fd, master_read=read, stdin_read=read):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000082 """Parent copy loop.
83 Copies
84 pty master -> standard output (master_read)
85 standard input -> pty master (stdin_read)"""
Guido van Rossum23cb2a81994-09-12 10:36:35 +000086 while 1:
87 rfds, wfds, xfds = select(
88 [master_fd, STDIN_FILENO], [], [])
89 if master_fd in rfds:
90 data = master_read(master_fd)
91 os.write(STDOUT_FILENO, data)
92 if STDIN_FILENO in rfds:
93 data = stdin_read(STDIN_FILENO)
94 writen(master_fd, data)
95
Guido van Rossum23cb2a81994-09-12 10:36:35 +000096def spawn(argv, master_read=read, stdin_read=read):
Guido van Rossum54f22ed2000-02-04 15:10:34 +000097 """Create a spawned process."""
Guido van Rossum23cb2a81994-09-12 10:36:35 +000098 if type(argv) == type(''):
99 argv = (argv,)
100 pid, master_fd = fork()
101 if pid == CHILD:
102 apply(os.execlp, (argv[0],) + argv)
103 mode = tty.tcgetattr(STDIN_FILENO)
104 tty.setraw(STDIN_FILENO)
105 try:
106 copy(master_fd, master_read, stdin_read)
107 except:
108 tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)