blob: ef2113bbb08f1dd5d79ab3e6435ae3ec05ea3731 [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.
Tim Peters2344fae2001-01-15 00:50:52 +00004# Only tested on Linux.
5# See: W. Richard Stevens. 1992. Advanced Programming in the
6# UNIX Environment. Chapter 19.
Guido van Rossum23cb2a81994-09-12 10:36:35 +00007# 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
Fred Drake8cef4cf2000-06-28 16:40:38 +000019def openpty():
Tim Peters2344fae2001-01-15 00:50:52 +000020 """openpty() -> (master_fd, slave_fd)
21 Open a pty master/slave pair, using os.openpty() if possible."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000022
Tim Peters2344fae2001-01-15 00:50:52 +000023 try:
24 return os.openpty()
25 except (AttributeError, OSError):
26 pass
27 master_fd, slave_name = _open_terminal()
28 slave_fd = slave_open(slave_name)
29 return master_fd, slave_fd
Fred Drake8cef4cf2000-06-28 16:40:38 +000030
Guido van Rossum23cb2a81994-09-12 10:36:35 +000031def master_open():
Tim Peters2344fae2001-01-15 00:50:52 +000032 """master_open() -> (master_fd, slave_name)
33 Open a pty master and return the fd, and the filename of the slave end.
34 Deprecated, use openpty() instead."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000035
Tim Peters2344fae2001-01-15 00:50:52 +000036 try:
37 master_fd, slave_fd = os.openpty()
38 except (AttributeError, OSError):
39 pass
40 else:
41 slave_name = os.ttyname(slave_fd)
42 os.close(slave_fd)
43 return master_fd, slave_name
Fred Drake8cef4cf2000-06-28 16:40:38 +000044
Tim Peters2344fae2001-01-15 00:50:52 +000045 return _open_terminal()
Fred Drake8cef4cf2000-06-28 16:40:38 +000046
47def _open_terminal():
Tim Peters2344fae2001-01-15 00:50:52 +000048 """Open pty master and return (master_fd, tty_name).
49 SGI and generic BSD version, for when openpty() fails."""
50 try:
51 import sgi
52 except ImportError:
53 pass
54 else:
55 try:
56 tty_name, master_fd = sgi._getpty(FCNTL.O_RDWR, 0666, 0)
57 except IOError, msg:
58 raise os.error, msg
59 return master_fd, tty_name
60 for x in 'pqrstuvwxyzPQRST':
61 for y in '0123456789abcdef':
62 pty_name = '/dev/pty' + x + y
63 try:
64 fd = os.open(pty_name, FCNTL.O_RDWR)
65 except os.error:
66 continue
67 return (fd, '/dev/tty' + x + y)
68 raise os.error, 'out of pty devices'
Guido van Rossum23cb2a81994-09-12 10:36:35 +000069
Guido van Rossum23cb2a81994-09-12 10:36:35 +000070def slave_open(tty_name):
Tim Peters2344fae2001-01-15 00:50:52 +000071 """slave_open(tty_name) -> slave_fd
72 Open the pty slave and acquire the controlling terminal, returning
73 opened filedescriptor.
74 Deprecated, use openpty() instead."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000075
Tim Peters2344fae2001-01-15 00:50:52 +000076 return os.open(tty_name, FCNTL.O_RDWR)
Guido van Rossum23cb2a81994-09-12 10:36:35 +000077
Guido van Rossum23cb2a81994-09-12 10:36:35 +000078def fork():
Tim Peters2344fae2001-01-15 00:50:52 +000079 """fork() -> (pid, master_fd)
80 Fork and make the child a session leader with a controlling terminal."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000081
Tim Peters2344fae2001-01-15 00:50:52 +000082 try:
83 pid, fd = os.forkpty()
84 except (AttributeError, OSError):
85 pass
86 else:
87 if pid == CHILD:
88 try:
89 os.setsid()
90 except OSError:
91 # os.forkpty() already set us session leader
92 pass
93 return pid, fd
Fred Drake8cef4cf2000-06-28 16:40:38 +000094
Tim Peters2344fae2001-01-15 00:50:52 +000095 master_fd, slave_fd = openpty()
96 pid = os.fork()
97 if pid == CHILD:
98 # Establish a new session.
99 os.setsid()
100 os.close(master_fd)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000101
Tim Peters2344fae2001-01-15 00:50:52 +0000102 # Slave becomes stdin/stdout/stderr of child.
103 os.dup2(slave_fd, STDIN_FILENO)
104 os.dup2(slave_fd, STDOUT_FILENO)
105 os.dup2(slave_fd, STDERR_FILENO)
106 if (slave_fd > STDERR_FILENO):
107 os.close (slave_fd)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000108
Tim Peters2344fae2001-01-15 00:50:52 +0000109 # Parent and child process.
110 return pid, master_fd
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000111
Fred Drake8cef4cf2000-06-28 16:40:38 +0000112def _writen(fd, data):
Tim Peters2344fae2001-01-15 00:50:52 +0000113 """Write all the data to a descriptor."""
114 while data != '':
115 n = os.write(fd, data)
116 data = data[n:]
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000117
Fred Drake8cef4cf2000-06-28 16:40:38 +0000118def _read(fd):
Tim Peters2344fae2001-01-15 00:50:52 +0000119 """Default read function."""
120 return os.read(fd, 1024)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000121
Fred Drake8cef4cf2000-06-28 16:40:38 +0000122def _copy(master_fd, master_read=_read, stdin_read=_read):
Tim Peters2344fae2001-01-15 00:50:52 +0000123 """Parent copy loop.
124 Copies
125 pty master -> standard output (master_read)
126 standard input -> pty master (stdin_read)"""
127 while 1:
128 rfds, wfds, xfds = select(
129 [master_fd, STDIN_FILENO], [], [])
130 if master_fd in rfds:
131 data = master_read(master_fd)
132 os.write(STDOUT_FILENO, data)
133 if STDIN_FILENO in rfds:
134 data = stdin_read(STDIN_FILENO)
135 _writen(master_fd, data)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000136
Fred Drake8cef4cf2000-06-28 16:40:38 +0000137def spawn(argv, master_read=_read, stdin_read=_read):
Tim Peters2344fae2001-01-15 00:50:52 +0000138 """Create a spawned process."""
139 if type(argv) == type(''):
140 argv = (argv,)
141 pid, master_fd = fork()
142 if pid == CHILD:
143 apply(os.execlp, (argv[0],) + argv)
144 mode = tty.tcgetattr(STDIN_FILENO)
145 tty.setraw(STDIN_FILENO)
146 try:
147 _copy(master_fd, master_read, stdin_read)
148 except:
149 tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)