1 """Pseudo terminal utilities."""
\r
3 # Bugs: No signal handling. Doesn't set slave termios and window size.
\r
4 # Only tested on Linux.
\r
5 # See: W. Richard Stevens. 1992. Advanced Programming in the
\r
6 # UNIX Environment. Chapter 19.
\r
7 # Author: Steen Lumholt -- with additions by Guido.
\r
9 from select import select, error
\r
12 # Absurd: import termios and then delete it. This is to force an attempt
\r
13 # to import pty to raise an ImportError on platforms that lack termios.
\r
14 # Without this explicit import of termios here, some other module may
\r
15 # import tty first, which in turn imports termios and dies with an
\r
16 # ImportError then. But since tty *does* exist across platforms, that
\r
17 # leaves a damaged module object for tty in sys.modules, and the import
\r
18 # of tty here then appears to work despite that the tty imported is junk.
\r
24 __all__ = ["openpty","fork","spawn","th_spawn","popen2"]
\r
33 """openpty() -> (master_fd, slave_fd)
\r
34 Open a pty master/slave pair, using os.openpty() if possible."""
\r
38 except (AttributeError, OSError):
\r
40 master_fd, slave_name = _open_terminal()
\r
41 slave_fd = slave_open(slave_name)
\r
42 return master_fd, slave_fd
\r
45 """master_open() -> (master_fd, slave_name)
\r
46 Open a pty master and return the fd, and the filename of the slave end.
\r
47 Deprecated, use openpty() instead."""
\r
50 master_fd, slave_fd = os.openpty()
\r
51 except (AttributeError, OSError):
\r
54 slave_name = os.ttyname(slave_fd)
\r
56 return master_fd, slave_name
\r
58 return _open_terminal()
\r
60 def _open_terminal():
\r
61 """Open pty master and return (master_fd, tty_name).
\r
62 SGI and generic BSD version, for when openpty() fails."""
\r
69 tty_name, master_fd = sgi._getpty(os.O_RDWR, 0666, 0)
\r
70 except IOError, msg:
\r
72 return master_fd, tty_name
\r
73 for x in 'pqrstuvwxyzPQRST':
\r
74 for y in '0123456789abcdef':
\r
75 pty_name = '/dev/pty' + x + y
\r
77 fd = os.open(pty_name, os.O_RDWR)
\r
80 return (fd, '/dev/tty' + x + y)
\r
81 raise os.error, 'out of pty devices'
\r
83 def slave_open(tty_name):
\r
84 """slave_open(tty_name) -> slave_fd
\r
85 Open the pty slave and acquire the controlling terminal, returning
\r
86 opened filedescriptor.
\r
87 Deprecated, use openpty() instead."""
\r
89 return os.open(tty_name, os.O_RDWR)
\r
92 """fork() -> (pid, master_fd)
\r
93 Fork and make the child a session leader with a controlling terminal."""
\r
96 pid, fd = os.forkpty()
\r
97 except (AttributeError, OSError):
\r
104 # os.forkpty() already set us session leader
\r
108 master_fd, slave_fd = openpty()
\r
111 # Establish a new session.
\r
113 os.close(master_fd)
\r
115 # Slave becomes stdin/stdout/stderr of child.
\r
116 os.dup2(slave_fd, STDIN_FILENO)
\r
117 os.dup2(slave_fd, STDOUT_FILENO)
\r
118 os.dup2(slave_fd, STDERR_FILENO)
\r
119 if (slave_fd > STDERR_FILENO):
\r
120 os.close (slave_fd)
\r
122 # Parent and child process.
\r
123 return pid, master_fd
\r
125 def _writen(fd, data):
\r
126 """Write all the data to a descriptor."""
\r
128 n = os.write(fd, data)
\r
132 """Default read function."""
\r
133 return os.read(fd, 1024)
\r
135 def _copy(master_fd, master_read=_read, stdin_read=_read, stdin_fd=STDIN_FILENO,
\r
136 stdout_fd=STDOUT_FILENO):
\r
137 """Parent copy loop.
\r
139 pty master -> stdout_fd (master_read)
\r
140 stdin_fd -> pty master (stdin_read)"""
\r
142 mode = tty.tcgetattr(stdin_fd)
\r
143 tty.setraw(stdin_fd)
\r
145 except tty.error: # This is the same as termios.error
\r
149 rfds, wfds, xfds = select(
\r
150 [master_fd, stdin_fd], [], [])
\r
151 if master_fd in rfds:
\r
152 data = master_read(master_fd)
\r
153 os.write(stdout_fd, data)
\r
154 if stdin_fd in rfds:
\r
155 data = stdin_read(stdin_fd)
\r
156 _writen(master_fd, data)
\r
157 except (IOError, OSError, error): # The last entry is select.error
\r
159 tty.tcsetattr(stdin_fd, tty.TCSAFLUSH, mode)
\r
160 if stdin_fd > STDERR_FILENO:
\r
162 if stdout_fd > STDERR_FILENO:
\r
163 os.close(stdout_fd)
\r
165 def spawn(argv, master_read=_read, stdin_read=_read, stdin_fd=STDIN_FILENO,
\r
166 stdout_fd=STDOUT_FILENO):
\r
167 """Create a spawned process. The controlling terminal reads and
\r
168 writes its data to stdin_fd and stdout_fd respectively.
\r
170 NOTE: This function does not return until one of the input or output file
\r
171 descriptors are closed, or the child process exits."""
\r
172 if type(argv) == type(''):
\r
174 pid, master_fd = fork()
\r
176 apply(os.execlp, (argv[0],) + argv)
\r
177 _copy(master_fd, master_read, stdin_read, stdin_fd, stdout_fd)
\r
179 def th_spawn(argv, master_read=_read, stdin_read=_read, stdin_fd=STDIN_FILENO,
\r
180 stdout_fd=STDOUT_FILENO):
\r
181 """Create a spawned process. The controlling terminal reads and
\r
182 writes its data to stdin_fd and stdout_fd respectively. The function
\r
183 returns the pid of the spawned process. (It returns immediately.)"""
\r
185 if type(argv) == type(''):
\r
187 pid, master_fd = fork()
\r
189 apply(os.execlp, (argv[0],) + argv)
\r
190 thread.start_new_thread(_copy, (master_fd, master_read, stdin_read, \
\r
191 stdin_fd, stdout_fd))
\r
194 def popen2(cmd, bufsize=1024, master_read=_read, stdin_read=_read):
\r
195 """Execute the shell command 'cmd' in a sub-process.
\r
197 If 'bufsize' is specified, it sets the buffer size for the I/O pipes.
\r
198 The function returns (child_stdin, child_stdout, child_pid), where the
\r
199 file objects are pipes connected to the spawned process's controling
\r
200 terminal, and the child_pid is the pid of the child process.
\r
202 argv = ('/bin/sh', '-c', cmd)
\r
203 child_stdin_rfd, child_stdin_wfd = os.pipe()
\r
204 child_stdout_rfd, child_stdout_wfd = os.pipe()
\r
205 child_pid = th_spawn(argv, master_read, stdin_read, child_stdin_rfd, \
\r
207 child_stdin = os.fdopen(child_stdin_wfd, 'w', bufsize)
\r
208 child_stdout = os.fdopen(child_stdout_rfd, 'r', bufsize)
\r
209 return child_stdin, child_stdout, child_pid
\r