Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8cef4cf

Browse files
committed
Thomas Wouters <[email protected]>:
This patch adds the openpty() and forkpty() library calls to posixmodule.c, when they are available on the target system. (glibc-2.1-based Linux systems, FreeBSD and BSDI at least, probably the other BSD-based systems as well.) Lib/pty.py is also rewritten to use openpty when available, but falls back to the old SGI method or the "manual" BSD open-a-pty code. Openpty() is necessary to use the Unix98 ptys under Linux 2.2, or when using non-standard tty names under (at least) BSDI, which is why I needed it, myself ;-) forkpty() is included for symmetry.
1 parent 5782386 commit 8cef4cf

File tree

4 files changed

+715
-380
lines changed

4 files changed

+715
-380
lines changed

Lib/pty.py

+57-16
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,37 @@
1616

1717
CHILD = 0
1818

19+
def 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()
28+
slave_fd = _slave_open(slave_name)
29+
return master_fd, slave_fd
30+
1931
def master_open():
32+
"""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+
47+
def _open_terminal():
2048
"""Open pty master and return (master_fd, tty_name).
21-
SGI and Linux/BSD version."""
49+
SGI and generic BSD version, for when openpty() fails."""
2250
try:
2351
import sgi
2452
except ImportError:
@@ -40,22 +68,35 @@ def master_open():
4068
raise os.error, 'out of pty devices'
4169

4270
def slave_open(tty_name):
43-
"""Open the pty slave and acquire the controlling terminal.
44-
Return the file descriptor. Linux version."""
45-
# (Should be universal? --Guido)
71+
"""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+
4676
return os.open(tty_name, FCNTL.O_RDWR)
4777

4878
def fork():
49-
"""Fork and make the child a session leader with a controlling terminal.
50-
Return (pid, master_fd)."""
51-
master_fd, tty_name = master_open()
79+
"""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()
5296
pid = os.fork()
5397
if pid == CHILD:
5498
# Establish a new session.
5599
os.setsid()
56-
57-
# Acquire controlling terminal.
58-
slave_fd = slave_open(tty_name)
59100
os.close(master_fd)
60101

61102
# Slave becomes stdin/stdout/stderr of child.
@@ -68,17 +109,17 @@ def fork():
68109
# Parent and child process.
69110
return pid, master_fd
70111

71-
def writen(fd, data):
112+
def _writen(fd, data):
72113
"""Write all the data to a descriptor."""
73114
while data != '':
74115
n = os.write(fd, data)
75116
data = data[n:]
76117

77-
def read(fd):
118+
def _read(fd):
78119
"""Default read function."""
79120
return os.read(fd, 1024)
80121

81-
def copy(master_fd, master_read=read, stdin_read=read):
122+
def _copy(master_fd, master_read=_read, stdin_read=_read):
82123
"""Parent copy loop.
83124
Copies
84125
pty master -> standard output (master_read)
@@ -91,9 +132,9 @@ def copy(master_fd, master_read=read, stdin_read=read):
91132
os.write(STDOUT_FILENO, data)
92133
if STDIN_FILENO in rfds:
93134
data = stdin_read(STDIN_FILENO)
94-
writen(master_fd, data)
135+
_writen(master_fd, data)
95136

96-
def spawn(argv, master_read=read, stdin_read=read):
137+
def spawn(argv, master_read=_read, stdin_read=_read):
97138
"""Create a spawned process."""
98139
if type(argv) == type(''):
99140
argv = (argv,)
@@ -103,6 +144,6 @@ def spawn(argv, master_read=read, stdin_read=read):
103144
mode = tty.tcgetattr(STDIN_FILENO)
104145
tty.setraw(STDIN_FILENO)
105146
try:
106-
copy(master_fd, master_read, stdin_read)
147+
_copy(master_fd, master_read, stdin_read)
107148
except:
108149
tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)

Modules/posixmodule.c

+64
Original file line numberDiff line numberDiff line change
@@ -1731,6 +1731,64 @@ posix_fork(self, args)
17311731
}
17321732
#endif
17331733

1734+
#if defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY)
1735+
#ifdef HAVE_PTY_H
1736+
#include <pty.h>
1737+
#else
1738+
#ifdef HAVE_LIBUTIL_H
1739+
#include <libutil.h>
1740+
#else
1741+
/* BSDI does not supply a prototype for the 'openpty' and 'forkpty'
1742+
functions, eventhough they are included in libutil. */
1743+
#include <termios.h>
1744+
extern int openpty(int *, int *, char *, struct termios *, struct winsize *);
1745+
extern int forkpty(int *, char *, struct termios *, struct winsize *);
1746+
#endif /* HAVE_LIBUTIL_H */
1747+
#endif /* HAVE_PTY_H */
1748+
#endif /* defined(HAVE_OPENPTY) or defined(HAVE_FORKPTY) */
1749+
1750+
#ifdef HAVE_OPENPTY
1751+
static char posix_openpty__doc__[] =
1752+
"openpty() -> (master_fd, slave_fd)\n\
1753+
Open a pseudo-terminal, returning open fd's for both master and slave end.\n";
1754+
1755+
static PyObject *
1756+
posix_openpty(self, args)
1757+
PyObject *self;
1758+
PyObject *args;
1759+
{
1760+
int master_fd, slave_fd;
1761+
if (!PyArg_ParseTuple(args, ":openpty"))
1762+
return NULL;
1763+
if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0)
1764+
return posix_error();
1765+
return Py_BuildValue("(ii)", master_fd, slave_fd);
1766+
}
1767+
#endif
1768+
1769+
#ifdef HAVE_FORKPTY
1770+
static char posix_forkpty__doc__[] =
1771+
"forkpty() -> (pid, master_fd)\n\
1772+
Fork a new process with a new pseudo-terminal as controlling tty.\n\n\
1773+
Like fork(), return 0 as pid to child process, and PID of child to parent.\n\
1774+
To both, return fd of newly opened pseudo-terminal.\n";
1775+
1776+
static PyObject *
1777+
posix_forkpty(self, args)
1778+
PyObject *self;
1779+
PyObject *args;
1780+
{
1781+
int master_fd, pid;
1782+
1783+
if (!PyArg_ParseTuple(args, ":forkpty"))
1784+
return NULL;
1785+
pid = forkpty(&master_fd, NULL, NULL, NULL);
1786+
if (pid == -1)
1787+
return posix_error();
1788+
PyOS_AfterFork();
1789+
return Py_BuildValue("(ii)", pid, master_fd);
1790+
}
1791+
#endif
17341792

17351793
#ifdef HAVE_GETEGID
17361794
static char posix_getegid__doc__[] =
@@ -4514,6 +4572,12 @@ static PyMethodDef posix_methods[] = {
45144572
#ifdef HAVE_FORK
45154573
{"fork", posix_fork, METH_VARARGS, posix_fork__doc__},
45164574
#endif /* HAVE_FORK */
4575+
#ifdef HAVE_OPENPTY
4576+
{"openpty", posix_openpty, METH_VARARGS, posix_openpty__doc__},
4577+
#endif /* HAVE_OPENPTY */
4578+
#ifdef HAVE_FORKPTY
4579+
{"forkpty", posix_forkpty, METH_VARARGS, posix_forkpty__doc__},
4580+
#endif /* HAVE_FORKPTY */
45174581
#ifdef HAVE_GETEGID
45184582
{"getegid", posix_getegid, METH_VARARGS, posix_getegid__doc__},
45194583
#endif /* HAVE_GETEGID */

0 commit comments

Comments
 (0)