blob: a70a865dc36188d614d7940417d9b7ca884293c1 [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
Fred Drake66aaaae2001-05-10 05:17:02 +000010import os
Tim Petersa0599572001-05-13 09:01:06 +000011
12# Absurd: import termios and then delete it. This is to force an attempt
13# to import pty to raise an ImportError on platforms that lack termios.
14# Without this explicit import of termios here, some other module may
15# import tty first, which in turn imports termios and dies with an
16# ImportError then. But since tty *does* exist across platforms, that
17# leaves a damaged module object for tty in sys.modules, and the import
18# of tty here then appears to work despite that the tty imported is junk.
Tim Peters3a2ab1a2001-05-29 06:06:54 +000019import termios
Tim Petersa0599572001-05-13 09:01:06 +000020del termios
21
Guido van Rossum23cb2a81994-09-12 10:36:35 +000022import tty
23
Skip Montanaroc62c81e2001-02-12 02:00:42 +000024__all__ = ["openpty","fork","spawn"]
25
Guido van Rossum23cb2a81994-09-12 10:36:35 +000026STDIN_FILENO = 0
27STDOUT_FILENO = 1
28STDERR_FILENO = 2
29
30CHILD = 0
31
Fred Drake8cef4cf2000-06-28 16:40:38 +000032def openpty():
Tim Peters2344fae2001-01-15 00:50:52 +000033 """openpty() -> (master_fd, slave_fd)
34 Open a pty master/slave pair, using os.openpty() if possible."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000035
Tim Peters2344fae2001-01-15 00:50:52 +000036 try:
37 return os.openpty()
38 except (AttributeError, OSError):
39 pass
40 master_fd, slave_name = _open_terminal()
41 slave_fd = slave_open(slave_name)
42 return master_fd, slave_fd
Fred Drake8cef4cf2000-06-28 16:40:38 +000043
Guido van Rossum23cb2a81994-09-12 10:36:35 +000044def master_open():
Tim Peters2344fae2001-01-15 00:50:52 +000045 """master_open() -> (master_fd, slave_name)
46 Open a pty master and return the fd, and the filename of the slave end.
47 Deprecated, use openpty() instead."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000048
Tim Peters2344fae2001-01-15 00:50:52 +000049 try:
50 master_fd, slave_fd = os.openpty()
51 except (AttributeError, OSError):
52 pass
53 else:
54 slave_name = os.ttyname(slave_fd)
55 os.close(slave_fd)
56 return master_fd, slave_name
Fred Drake8cef4cf2000-06-28 16:40:38 +000057
Tim Peters2344fae2001-01-15 00:50:52 +000058 return _open_terminal()
Fred Drake8cef4cf2000-06-28 16:40:38 +000059
60def _open_terminal():
Tim Peters2344fae2001-01-15 00:50:52 +000061 """Open pty master and return (master_fd, tty_name).
62 SGI and generic BSD version, for when openpty() fails."""
63 try:
64 import sgi
65 except ImportError:
66 pass
67 else:
68 try:
Fred Drake66aaaae2001-05-10 05:17:02 +000069 tty_name, master_fd = sgi._getpty(os.O_RDWR, 0666, 0)
Tim Peters2344fae2001-01-15 00:50:52 +000070 except IOError, msg:
71 raise os.error, msg
72 return master_fd, tty_name
73 for x in 'pqrstuvwxyzPQRST':
74 for y in '0123456789abcdef':
75 pty_name = '/dev/pty' + x + y
76 try:
Fred Drake66aaaae2001-05-10 05:17:02 +000077 fd = os.open(pty_name, os.O_RDWR)
Tim Peters2344fae2001-01-15 00:50:52 +000078 except os.error:
79 continue
80 return (fd, '/dev/tty' + x + y)
81 raise os.error, 'out of pty devices'
Guido van Rossum23cb2a81994-09-12 10:36:35 +000082
Guido van Rossum23cb2a81994-09-12 10:36:35 +000083def slave_open(tty_name):
Tim Peters2344fae2001-01-15 00:50:52 +000084 """slave_open(tty_name) -> slave_fd
85 Open the pty slave and acquire the controlling terminal, returning
86 opened filedescriptor.
87 Deprecated, use openpty() instead."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000088
Martin v. Löwis14e73b12003-01-01 09:51:12 +000089 result = os.open(tty_name, os.O_RDWR)
90 try:
91 from fcntl import ioctl, I_PUSH
92 except ImportError:
93 return result
94 try:
Tim Peters2c60f7a2003-01-29 03:49:43 +000095 ioctl(result, I_PUSH, "ptem")
96 ioctl(result, I_PUSH, "ldterm")
Martin v. Löwis14e73b12003-01-01 09:51:12 +000097 except IOError:
98 pass
99 return result
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000100
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000101def fork():
Tim Peters2344fae2001-01-15 00:50:52 +0000102 """fork() -> (pid, master_fd)
103 Fork and make the child a session leader with a controlling terminal."""
Fred Drake8cef4cf2000-06-28 16:40:38 +0000104
Tim Peters2344fae2001-01-15 00:50:52 +0000105 try:
106 pid, fd = os.forkpty()
107 except (AttributeError, OSError):
108 pass
109 else:
110 if pid == CHILD:
111 try:
112 os.setsid()
113 except OSError:
114 # os.forkpty() already set us session leader
115 pass
116 return pid, fd
Fred Drake8cef4cf2000-06-28 16:40:38 +0000117
Tim Peters2344fae2001-01-15 00:50:52 +0000118 master_fd, slave_fd = openpty()
119 pid = os.fork()
120 if pid == CHILD:
121 # Establish a new session.
122 os.setsid()
123 os.close(master_fd)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000124
Tim Peters2344fae2001-01-15 00:50:52 +0000125 # Slave becomes stdin/stdout/stderr of child.
126 os.dup2(slave_fd, STDIN_FILENO)
127 os.dup2(slave_fd, STDOUT_FILENO)
128 os.dup2(slave_fd, STDERR_FILENO)
129 if (slave_fd > STDERR_FILENO):
130 os.close (slave_fd)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000131
Tim Peters2344fae2001-01-15 00:50:52 +0000132 # Parent and child process.
133 return pid, master_fd
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000134
Fred Drake8cef4cf2000-06-28 16:40:38 +0000135def _writen(fd, data):
Tim Peters2344fae2001-01-15 00:50:52 +0000136 """Write all the data to a descriptor."""
137 while data != '':
138 n = os.write(fd, data)
139 data = data[n:]
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000140
Fred Drake8cef4cf2000-06-28 16:40:38 +0000141def _read(fd):
Tim Peters2344fae2001-01-15 00:50:52 +0000142 """Default read function."""
143 return os.read(fd, 1024)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000144
Fred Drake8cef4cf2000-06-28 16:40:38 +0000145def _copy(master_fd, master_read=_read, stdin_read=_read):
Tim Peters2344fae2001-01-15 00:50:52 +0000146 """Parent copy loop.
147 Copies
148 pty master -> standard output (master_read)
149 standard input -> pty master (stdin_read)"""
150 while 1:
151 rfds, wfds, xfds = select(
152 [master_fd, STDIN_FILENO], [], [])
153 if master_fd in rfds:
154 data = master_read(master_fd)
155 os.write(STDOUT_FILENO, data)
156 if STDIN_FILENO in rfds:
157 data = stdin_read(STDIN_FILENO)
158 _writen(master_fd, data)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000159
Fred Drake8cef4cf2000-06-28 16:40:38 +0000160def spawn(argv, master_read=_read, stdin_read=_read):
Tim Peters2344fae2001-01-15 00:50:52 +0000161 """Create a spawned process."""
162 if type(argv) == type(''):
163 argv = (argv,)
164 pid, master_fd = fork()
165 if pid == CHILD:
Guido van Rossum68468eb2003-02-27 20:14:51 +0000166 os.execlp(argv[0], *argv)
Martin v. Löwis6c611fa2002-07-28 09:42:57 +0000167 try:
168 mode = tty.tcgetattr(STDIN_FILENO)
169 tty.setraw(STDIN_FILENO)
170 restore = 1
171 except tty.error: # This is the same as termios.error
172 restore = 0
Tim Peters2344fae2001-01-15 00:50:52 +0000173 try:
174 _copy(master_fd, master_read, stdin_read)
Martin v. Löwis6c611fa2002-07-28 09:42:57 +0000175 except (IOError, OSError):
176 if restore:
177 tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)