blob: 8d8ce40df541c1872b52ae6ea069e0e975bd8a6b [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.
Miss Islington (bot)d4128482021-08-13 04:21:06 -07004# Only tested on Linux, FreeBSD, and macOS.
Tim Peters2344fae2001-01-15 00:50:52 +00005# 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
Saiyang Gou95f60012020-02-04 16:15:00 -080011import sys
Guido van Rossum23cb2a81994-09-12 10:36:35 +000012import tty
13
Miss Islington (bot)5d444432021-08-12 05:36:04 -070014# names imported directly for test mocking purposes
15from os import close, waitpid
16from tty import setraw, tcgetattr, tcsetattr
17
18__all__ = ["openpty", "fork", "spawn"]
Skip Montanaroc62c81e2001-02-12 02:00:42 +000019
Guido van Rossum23cb2a81994-09-12 10:36:35 +000020STDIN_FILENO = 0
21STDOUT_FILENO = 1
22STDERR_FILENO = 2
23
24CHILD = 0
25
Fred Drake8cef4cf2000-06-28 16:40:38 +000026def openpty():
Tim Peters2344fae2001-01-15 00:50:52 +000027 """openpty() -> (master_fd, slave_fd)
28 Open a pty master/slave pair, using os.openpty() if possible."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000029
Tim Peters2344fae2001-01-15 00:50:52 +000030 try:
31 return os.openpty()
32 except (AttributeError, OSError):
33 pass
34 master_fd, slave_name = _open_terminal()
35 slave_fd = slave_open(slave_name)
36 return master_fd, slave_fd
Fred Drake8cef4cf2000-06-28 16:40:38 +000037
Guido van Rossum23cb2a81994-09-12 10:36:35 +000038def master_open():
Tim Peters2344fae2001-01-15 00:50:52 +000039 """master_open() -> (master_fd, slave_name)
40 Open a pty master and return the fd, and the filename of the slave end.
41 Deprecated, use openpty() instead."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000042
Tim Peters2344fae2001-01-15 00:50:52 +000043 try:
44 master_fd, slave_fd = os.openpty()
45 except (AttributeError, OSError):
46 pass
47 else:
48 slave_name = os.ttyname(slave_fd)
49 os.close(slave_fd)
50 return master_fd, slave_name
Fred Drake8cef4cf2000-06-28 16:40:38 +000051
Tim Peters2344fae2001-01-15 00:50:52 +000052 return _open_terminal()
Fred Drake8cef4cf2000-06-28 16:40:38 +000053
54def _open_terminal():
Andrew Kuchlingedf33c02013-06-01 13:52:30 -040055 """Open pty master and return (master_fd, tty_name)."""
Tim Peters2344fae2001-01-15 00:50:52 +000056 for x in 'pqrstuvwxyzPQRST':
57 for y in '0123456789abcdef':
58 pty_name = '/dev/pty' + x + y
59 try:
Fred Drake66aaaae2001-05-10 05:17:02 +000060 fd = os.open(pty_name, os.O_RDWR)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +020061 except OSError:
Tim Peters2344fae2001-01-15 00:50:52 +000062 continue
63 return (fd, '/dev/tty' + x + y)
Andrew Svetlovad28c7f2012-12-18 22:02:39 +020064 raise OSError('out of pty devices')
Guido van Rossum23cb2a81994-09-12 10:36:35 +000065
Guido van Rossum23cb2a81994-09-12 10:36:35 +000066def slave_open(tty_name):
Tim Peters2344fae2001-01-15 00:50:52 +000067 """slave_open(tty_name) -> slave_fd
68 Open the pty slave and acquire the controlling terminal, returning
69 opened filedescriptor.
70 Deprecated, use openpty() instead."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000071
Martin v. Löwis14e73b12003-01-01 09:51:12 +000072 result = os.open(tty_name, os.O_RDWR)
73 try:
74 from fcntl import ioctl, I_PUSH
Brett Cannoncd171c82013-07-04 17:43:24 -040075 except ImportError:
Martin v. Löwis14e73b12003-01-01 09:51:12 +000076 return result
77 try:
Tim Peters2c60f7a2003-01-29 03:49:43 +000078 ioctl(result, I_PUSH, "ptem")
79 ioctl(result, I_PUSH, "ldterm")
Andrew Svetlovf7a17b42012-12-25 16:47:37 +020080 except OSError:
Martin v. Löwis14e73b12003-01-01 09:51:12 +000081 pass
82 return result
Guido van Rossum23cb2a81994-09-12 10:36:35 +000083
Guido van Rossum23cb2a81994-09-12 10:36:35 +000084def fork():
Tim Peters2344fae2001-01-15 00:50:52 +000085 """fork() -> (pid, master_fd)
86 Fork and make the child a session leader with a controlling terminal."""
Fred Drake8cef4cf2000-06-28 16:40:38 +000087
Tim Peters2344fae2001-01-15 00:50:52 +000088 try:
89 pid, fd = os.forkpty()
90 except (AttributeError, OSError):
91 pass
92 else:
93 if pid == CHILD:
94 try:
95 os.setsid()
96 except OSError:
97 # os.forkpty() already set us session leader
98 pass
99 return pid, fd
Fred Drake8cef4cf2000-06-28 16:40:38 +0000100
Tim Peters2344fae2001-01-15 00:50:52 +0000101 master_fd, slave_fd = openpty()
102 pid = os.fork()
103 if pid == CHILD:
104 # Establish a new session.
105 os.setsid()
106 os.close(master_fd)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000107
Tim Peters2344fae2001-01-15 00:50:52 +0000108 # Slave becomes stdin/stdout/stderr of child.
109 os.dup2(slave_fd, STDIN_FILENO)
110 os.dup2(slave_fd, STDOUT_FILENO)
111 os.dup2(slave_fd, STDERR_FILENO)
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700112 if slave_fd > STDERR_FILENO:
113 os.close(slave_fd)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000114
Thomas Wouters89f507f2006-12-13 04:49:30 +0000115 # Explicitly open the tty to make it become a controlling tty.
116 tmp_fd = os.open(os.ttyname(STDOUT_FILENO), os.O_RDWR)
117 os.close(tmp_fd)
Thomas Wouters902d6eb2007-01-09 23:18:33 +0000118 else:
119 os.close(slave_fd)
Thomas Wouters9fe394c2007-02-05 01:24:16 +0000120
Tim Peters2344fae2001-01-15 00:50:52 +0000121 # Parent and child process.
122 return pid, master_fd
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000123
Fred Drake8cef4cf2000-06-28 16:40:38 +0000124def _writen(fd, data):
Tim Peters2344fae2001-01-15 00:50:52 +0000125 """Write all the data to a descriptor."""
Antoine Pitrou9cadb1b2008-09-15 23:02:56 +0000126 while data:
Tim Peters2344fae2001-01-15 00:50:52 +0000127 n = os.write(fd, data)
128 data = data[n:]
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000129
Fred Drake8cef4cf2000-06-28 16:40:38 +0000130def _read(fd):
Tim Peters2344fae2001-01-15 00:50:52 +0000131 """Default read function."""
132 return os.read(fd, 1024)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000133
Fred Drake8cef4cf2000-06-28 16:40:38 +0000134def _copy(master_fd, master_read=_read, stdin_read=_read):
Tim Peters2344fae2001-01-15 00:50:52 +0000135 """Parent copy loop.
136 Copies
137 pty master -> standard output (master_read)
138 standard input -> pty master (stdin_read)"""
Gregory P. Smith05f59532012-02-16 00:29:12 -0800139 fds = [master_fd, STDIN_FILENO]
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700140 while fds:
141 rfds, _wfds, _xfds = select(fds, [], [])
142
Tim Peters2344fae2001-01-15 00:50:52 +0000143 if master_fd in rfds:
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700144 # Some OSes signal EOF by returning an empty byte string,
145 # some throw OSErrors.
146 try:
147 data = master_read(master_fd)
148 except OSError:
149 data = b""
Gregory P. Smith05f59532012-02-16 00:29:12 -0800150 if not data: # Reached EOF.
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700151 return # Assume the child process has exited and is
152 # unreachable, so we clean up.
Gregory P. Smith05f59532012-02-16 00:29:12 -0800153 else:
154 os.write(STDOUT_FILENO, data)
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700155
Tim Peters2344fae2001-01-15 00:50:52 +0000156 if STDIN_FILENO in rfds:
157 data = stdin_read(STDIN_FILENO)
Gregory P. Smith05f59532012-02-16 00:29:12 -0800158 if not data:
159 fds.remove(STDIN_FILENO)
160 else:
161 _writen(master_fd, data)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000162
Fred Drake8cef4cf2000-06-28 16:40:38 +0000163def spawn(argv, master_read=_read, stdin_read=_read):
Tim Peters2344fae2001-01-15 00:50:52 +0000164 """Create a spawned process."""
165 if type(argv) == type(''):
166 argv = (argv,)
Saiyang Gou95f60012020-02-04 16:15:00 -0800167 sys.audit('pty.spawn', argv)
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700168
Tim Peters2344fae2001-01-15 00:50:52 +0000169 pid, master_fd = fork()
170 if pid == CHILD:
Guido van Rossum68468eb2003-02-27 20:14:51 +0000171 os.execlp(argv[0], *argv)
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700172
Martin v. Löwis6c611fa2002-07-28 09:42:57 +0000173 try:
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700174 mode = tcgetattr(STDIN_FILENO)
175 setraw(STDIN_FILENO)
176 restore = True
Martin v. Löwis6c611fa2002-07-28 09:42:57 +0000177 except tty.error: # This is the same as termios.error
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700178 restore = False
179
Tim Peters2344fae2001-01-15 00:50:52 +0000180 try:
181 _copy(master_fd, master_read, stdin_read)
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700182 finally:
Martin v. Löwis6c611fa2002-07-28 09:42:57 +0000183 if restore:
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700184 tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)
Andrew M. Kuchlinga2c9a982004-06-05 16:27:16 +0000185
Miss Islington (bot)5d444432021-08-12 05:36:04 -0700186 close(master_fd)
187 return waitpid(pid, 0)[1]