blob: 12c9093a24ffa1ddd6fb8c5e0c1f7e02a8706426 [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.
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.
8
9from select import select
Guido van Rossum96d80f91999-05-03 18:13:51 +000010import os, FCNTL
Guido van Rossum23cb2a81994-09-12 10:36:35 +000011import tty
12
13STDIN_FILENO = 0
14STDOUT_FILENO = 1
15STDERR_FILENO = 2
16
17CHILD = 0
18
Fred Drake8cef4cf2000-06-28 16:40:38 +000019def openpty():
20 """openpty() -> (master_fd, slave_fd)
21 Open a pty master/slave pair, using os.openpty() if possible."""
22
23 try:
24 return os.openpty()
25 except (AttributeError, OSError):
26 pass
27 master_fd, slave_name = _open_terminal()
Fred Drake0ea1fc82000-07-03 13:44:25 +000028 slave_fd = slave_open(slave_name)
Fred Drake8cef4cf2000-06-28 16:40:38 +000029 return master_fd, slave_fd
30
Guido van Rossum23cb2a81994-09-12 10:36:35 +000031def master_open():
Fred Drake8cef4cf2000-06-28 16:40:38 +000032 """master_open() -> (master_fd, slave_name)
33 Open a pty master and return the fd, and the filename of the slave end.
34 Deprecated, use openpty() instead."""
35
36 try:
37 master_fd, slave_fd = os.openpty()
38 except (AttributeError, OSError):
39 pass
40 else:
41 slave_name = os.ttyname(slave_fd)
42 os.close(slave_fd)
43 return master_fd, slave_name
44
45 return _open_terminal()
46
47def _open_terminal():
Guido van Rossum54f22ed2000-02-04 15:10:34 +000048 """Open pty master and return (master_fd, tty_name).
Fred Drake8cef4cf2000-06-28 16:40:38 +000049 SGI and generic BSD version, for when openpty() fails."""
Guido van Rossum23cb2a81994-09-12 10:36:35 +000050 try:
51 import sgi
52 except ImportError:
53 pass
54 else:
55 try:
56 tty_name, master_fd = sgi._getpty(FCNTL.O_RDWR, 0666, 0)
57 except IOError, msg:
58 raise os.error, msg
59 return master_fd, tty_name
60 for x in 'pqrstuvwxyzPQRST':
61 for y in '0123456789abcdef':
62 pty_name = '/dev/pty' + x + y
63 try:
64 fd = os.open(pty_name, FCNTL.O_RDWR)
65 except os.error:
66 continue
67 return (fd, '/dev/tty' + x + y)
68 raise os.error, 'out of pty devices'
69
Guido van Rossum23cb2a81994-09-12 10:36:35 +000070def slave_open(tty_name):
Fred Drake8cef4cf2000-06-28 16:40:38 +000071 """slave_open(tty_name) -> slave_fd
72 Open the pty slave and acquire the controlling terminal, returning
73 opened filedescriptor.
74 Deprecated, use openpty() instead."""
75
Guido van Rossum23cb2a81994-09-12 10:36:35 +000076 return os.open(tty_name, FCNTL.O_RDWR)
77
Guido van Rossum23cb2a81994-09-12 10:36:35 +000078def fork():
Fred Drake8cef4cf2000-06-28 16:40:38 +000079 """fork() -> (pid, master_fd)
80 Fork and make the child a session leader with a controlling terminal."""
81
82 try:
83 pid, fd = os.forkpty()
84 except (AttributeError, OSError):
85 pass
86 else:
87 if pid == CHILD:
88 try:
89 os.setsid()
90 except OSError:
91 # os.forkpty() already set us session leader
92 pass
93 return pid, fd
94
95 master_fd, slave_fd = openpty()
Guido van Rossum23cb2a81994-09-12 10:36:35 +000096 pid = os.fork()
97 if pid == CHILD:
98 # Establish a new session.
99 os.setsid()
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000100 os.close(master_fd)
101
102 # Slave becomes stdin/stdout/stderr of child.
103 os.dup2(slave_fd, STDIN_FILENO)
104 os.dup2(slave_fd, STDOUT_FILENO)
105 os.dup2(slave_fd, STDERR_FILENO)
106 if (slave_fd > STDERR_FILENO):
107 os.close (slave_fd)
108
109 # Parent and child process.
110 return pid, master_fd
111
Fred Drake8cef4cf2000-06-28 16:40:38 +0000112def _writen(fd, data):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000113 """Write all the data to a descriptor."""
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000114 while data != '':
115 n = os.write(fd, data)
116 data = data[n:]
117
Fred Drake8cef4cf2000-06-28 16:40:38 +0000118def _read(fd):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000119 """Default read function."""
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000120 return os.read(fd, 1024)
121
Fred Drake8cef4cf2000-06-28 16:40:38 +0000122def _copy(master_fd, master_read=_read, stdin_read=_read):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000123 """Parent copy loop.
124 Copies
125 pty master -> standard output (master_read)
126 standard input -> pty master (stdin_read)"""
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000127 while 1:
128 rfds, wfds, xfds = select(
129 [master_fd, STDIN_FILENO], [], [])
130 if master_fd in rfds:
131 data = master_read(master_fd)
132 os.write(STDOUT_FILENO, data)
133 if STDIN_FILENO in rfds:
134 data = stdin_read(STDIN_FILENO)
Fred Drake8cef4cf2000-06-28 16:40:38 +0000135 _writen(master_fd, data)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000136
Fred Drake8cef4cf2000-06-28 16:40:38 +0000137def spawn(argv, master_read=_read, stdin_read=_read):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000138 """Create a spawned process."""
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000139 if type(argv) == type(''):
140 argv = (argv,)
141 pid, master_fd = fork()
142 if pid == CHILD:
143 apply(os.execlp, (argv[0],) + argv)
144 mode = tty.tcgetattr(STDIN_FILENO)
145 tty.setraw(STDIN_FILENO)
146 try:
Fred Drake8cef4cf2000-06-28 16:40:38 +0000147 _copy(master_fd, master_read, stdin_read)
Guido van Rossum23cb2a81994-09-12 10:36:35 +0000148 except:
149 tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)