blob: a2f21c9bf6ef44e301ef277b20e768d955ffdcd4 [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
Fred Drake66aaaae2001-05-10 05:17:02 +000089 return os.open(tty_name, os.O_RDWR)
Guido van Rossum23cb2a81994-09-12 10:36:35 +000090
Guido van Rossum23cb2a81994-09-12 10:36:35 +000091def fork():
Tim Peters2344fae2001-01-15 00:50:52 +000092 """fork() -> (pid, master_fd)
93 Fork and make the child a session leader with a controlling terminal."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000094
Tim Peters2344fae2001-01-15 00:50:52 +000095 try:
96 pid, fd = os.forkpty()
97 except (AttributeError, OSError):
98 pass
99 else:
100 if pid == CHILD:
101 try:
102 os.setsid()
103 except OSError:
104 # os.forkpty() already set us session leader
105 pass
106 return pid, fd
Fred Drake8cef4cf2000-06-28 16:40:38 +0000107
Tim Peters2344fae2001-01-15 00:50:52 +0000108 master_fd, slave_fd = openpty()
109 pid = os.fork()
110 if pid == CHILD:
111 # Establish a new session.
112 os.setsid()
113 os.close(master_fd)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000114
Tim Peters2344fae2001-01-15 00:50:52 +0000115 # Slave becomes stdin/stdout/stderr of child.
116 os.dup2(slave_fd, STDIN_FILENO)
117 os.dup2(slave_fd, STDOUT_FILENO)
118 os.dup2(slave_fd, STDERR_FILENO)
119 if (slave_fd > STDERR_FILENO):
120 os.close (slave_fd)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000121
Tim Peters2344fae2001-01-15 00:50:52 +0000122 # Parent and child process.
123 return pid, master_fd
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000124
Fred Drake8cef4cf2000-06-28 16:40:38 +0000125def _writen(fd, data):
Tim Peters2344fae2001-01-15 00:50:52 +0000126 """Write all the data to a descriptor."""
127 while data != '':
128 n = os.write(fd, data)
129 data = data[n:]
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000130
Fred Drake8cef4cf2000-06-28 16:40:38 +0000131def _read(fd):
Tim Peters2344fae2001-01-15 00:50:52 +0000132 """Default read function."""
133 return os.read(fd, 1024)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000134
Fred Drake8cef4cf2000-06-28 16:40:38 +0000135def _copy(master_fd, master_read=_read, stdin_read=_read):
Tim Peters2344fae2001-01-15 00:50:52 +0000136 """Parent copy loop.
137 Copies
138 pty master -> standard output (master_read)
139 standard input -> pty master (stdin_read)"""
140 while 1:
141 rfds, wfds, xfds = select(
142 [master_fd, STDIN_FILENO], [], [])
143 if master_fd in rfds:
144 data = master_read(master_fd)
145 os.write(STDOUT_FILENO, data)
146 if STDIN_FILENO in rfds:
147 data = stdin_read(STDIN_FILENO)
148 _writen(master_fd, data)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000149
Fred Drake8cef4cf2000-06-28 16:40:38 +0000150def spawn(argv, master_read=_read, stdin_read=_read):
Tim Peters2344fae2001-01-15 00:50:52 +0000151 """Create a spawned process."""
152 if type(argv) == type(''):
153 argv = (argv,)
154 pid, master_fd = fork()
155 if pid == CHILD:
156 apply(os.execlp, (argv[0],) + argv)
157 mode = tty.tcgetattr(STDIN_FILENO)
158 tty.setraw(STDIN_FILENO)
159 try:
160 _copy(master_fd, master_read, stdin_read)
Fred Drake65255312001-05-11 19:15:28 +0000161 except IOError:
Tim Peters2344fae2001-01-15 00:50:52 +0000162 tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)