1 """Pseudo terminal utilities."""
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.
9 from select
import select
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.
24 __all__
= ["openpty","fork","spawn"]
33 """openpty() -> (master_fd, slave_fd)
34 Open a pty master/slave pair, using os.openpty() if possible."""
38 except (AttributeError, OSError):
40 master_fd
, slave_name
= _open_terminal()
41 slave_fd
= slave_open(slave_name
)
42 return master_fd
, slave_fd
45 """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."""
50 master_fd
, slave_fd
= os
.openpty()
51 except (AttributeError, OSError):
54 slave_name
= os
.ttyname(slave_fd
)
56 return master_fd
, slave_name
58 return _open_terminal()
61 """Open pty master and return (master_fd, tty_name).
62 SGI and generic BSD version, for when openpty() fails."""
69 tty_name
, master_fd
= sgi
._getpty
(os
.O_RDWR
, 0666, 0)
72 return master_fd
, tty_name
73 for x
in 'pqrstuvwxyzPQRST':
74 for y
in '0123456789abcdef':
75 pty_name
= '/dev/pty' + x
+ y
77 fd
= os
.open(pty_name
, os
.O_RDWR
)
80 return (fd
, '/dev/tty' + x
+ y
)
81 raise os
.error
, 'out of pty devices'
83 def slave_open(tty_name
):
84 """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."""
89 return os
.open(tty_name
, os
.O_RDWR
)
92 """fork() -> (pid, master_fd)
93 Fork and make the child a session leader with a controlling terminal."""
96 pid
, fd
= os
.forkpty()
97 except (AttributeError, OSError):
104 # os.forkpty() already set us session leader
108 master_fd
, slave_fd
= openpty()
111 # Establish a new session.
115 # 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
):
122 # Parent and child process.
123 return pid
, master_fd
125 def _writen(fd
, data
):
126 """Write all the data to a descriptor."""
128 n
= os
.write(fd
, data
)
132 """Default read function."""
133 return os
.read(fd
, 1024)
135 def _copy(master_fd
, master_read
=_read
, stdin_read
=_read
):
138 pty master -> standard output (master_read)
139 standard input -> pty master (stdin_read)"""
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
)
150 def spawn(argv
, master_read
=_read
, stdin_read
=_read
):
151 """Create a spawned process."""
152 if type(argv
) == type(''):
154 pid
, master_fd
= fork()
156 apply(os
.execlp
, (argv
[0],) + argv
)
157 mode
= tty
.tcgetattr(STDIN_FILENO
)
158 tty
.setraw(STDIN_FILENO
)
160 _copy(master_fd
, master_read
, stdin_read
)
162 tty
.tcsetattr(STDIN_FILENO
, tty
.TCSAFLUSH
, mode
)