Skip to content

Commit

Permalink
Thomas Wouters <thomas@xs4all.net>:
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
freddrake committed Jun 28, 2000
1 parent 5782386 commit 8cef4cf
Show file tree
Hide file tree
Showing 4 changed files with 715 additions and 380 deletions.
73 changes: 57 additions & 16 deletions Lib/pty.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,37 @@

CHILD = 0

def openpty():
"""openpty() -> (master_fd, slave_fd)
Open a pty master/slave pair, using os.openpty() if possible."""

try:
return os.openpty()
except (AttributeError, OSError):
pass
master_fd, slave_name = _open_terminal()
slave_fd = _slave_open(slave_name)
return master_fd, slave_fd

def master_open():
"""master_open() -> (master_fd, slave_name)
Open a pty master and return the fd, and the filename of the slave end.
Deprecated, use openpty() instead."""

try:
master_fd, slave_fd = os.openpty()
except (AttributeError, OSError):
pass
else:
slave_name = os.ttyname(slave_fd)
os.close(slave_fd)
return master_fd, slave_name

return _open_terminal()

def _open_terminal():
"""Open pty master and return (master_fd, tty_name).
SGI and Linux/BSD version."""
SGI and generic BSD version, for when openpty() fails."""
try:
import sgi
except ImportError:
Expand All @@ -40,22 +68,35 @@ def master_open():
raise os.error, 'out of pty devices'

def slave_open(tty_name):
"""Open the pty slave and acquire the controlling terminal.
Return the file descriptor. Linux version."""
# (Should be universal? --Guido)
"""slave_open(tty_name) -> slave_fd
Open the pty slave and acquire the controlling terminal, returning
opened filedescriptor.
Deprecated, use openpty() instead."""

return os.open(tty_name, FCNTL.O_RDWR)

def fork():
"""Fork and make the child a session leader with a controlling terminal.
Return (pid, master_fd)."""
master_fd, tty_name = master_open()
"""fork() -> (pid, master_fd)
Fork and make the child a session leader with a controlling terminal."""

try:
pid, fd = os.forkpty()
except (AttributeError, OSError):
pass
else:
if pid == CHILD:
try:
os.setsid()
except OSError:
# os.forkpty() already set us session leader
pass
return pid, fd

master_fd, slave_fd = openpty()
pid = os.fork()
if pid == CHILD:
# Establish a new session.
os.setsid()

# Acquire controlling terminal.
slave_fd = slave_open(tty_name)
os.close(master_fd)

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

def writen(fd, data):
def _writen(fd, data):
"""Write all the data to a descriptor."""
while data != '':
n = os.write(fd, data)
data = data[n:]

def read(fd):
def _read(fd):
"""Default read function."""
return os.read(fd, 1024)

def copy(master_fd, master_read=read, stdin_read=read):
def _copy(master_fd, master_read=_read, stdin_read=_read):
"""Parent copy loop.
Copies
pty master -> standard output (master_read)
Expand All @@ -91,9 +132,9 @@ def copy(master_fd, master_read=read, stdin_read=read):
os.write(STDOUT_FILENO, data)
if STDIN_FILENO in rfds:
data = stdin_read(STDIN_FILENO)
writen(master_fd, data)
_writen(master_fd, data)

def spawn(argv, master_read=read, stdin_read=read):
def spawn(argv, master_read=_read, stdin_read=_read):
"""Create a spawned process."""
if type(argv) == type(''):
argv = (argv,)
Expand All @@ -103,6 +144,6 @@ def spawn(argv, master_read=read, stdin_read=read):
mode = tty.tcgetattr(STDIN_FILENO)
tty.setraw(STDIN_FILENO)
try:
copy(master_fd, master_read, stdin_read)
_copy(master_fd, master_read, stdin_read)
except:
tty.tcsetattr(STDIN_FILENO, tty.TCSAFLUSH, mode)
64 changes: 64 additions & 0 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1731,6 +1731,64 @@ posix_fork(self, args)
}
#endif

#if defined(HAVE_OPENPTY) || defined(HAVE_FORKPTY)
#ifdef HAVE_PTY_H
#include <pty.h>
#else
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
#else
/* BSDI does not supply a prototype for the 'openpty' and 'forkpty'
functions, eventhough they are included in libutil. */
#include <termios.h>
extern int openpty(int *, int *, char *, struct termios *, struct winsize *);
extern int forkpty(int *, char *, struct termios *, struct winsize *);
#endif /* HAVE_LIBUTIL_H */
#endif /* HAVE_PTY_H */
#endif /* defined(HAVE_OPENPTY) or defined(HAVE_FORKPTY) */

#ifdef HAVE_OPENPTY
static char posix_openpty__doc__[] =
"openpty() -> (master_fd, slave_fd)\n\
Open a pseudo-terminal, returning open fd's for both master and slave end.\n";

static PyObject *
posix_openpty(self, args)
PyObject *self;
PyObject *args;
{
int master_fd, slave_fd;
if (!PyArg_ParseTuple(args, ":openpty"))
return NULL;
if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) != 0)
return posix_error();
return Py_BuildValue("(ii)", master_fd, slave_fd);
}
#endif

#ifdef HAVE_FORKPTY
static char posix_forkpty__doc__[] =
"forkpty() -> (pid, master_fd)\n\
Fork a new process with a new pseudo-terminal as controlling tty.\n\n\
Like fork(), return 0 as pid to child process, and PID of child to parent.\n\
To both, return fd of newly opened pseudo-terminal.\n";

static PyObject *
posix_forkpty(self, args)
PyObject *self;
PyObject *args;
{
int master_fd, pid;

if (!PyArg_ParseTuple(args, ":forkpty"))
return NULL;
pid = forkpty(&master_fd, NULL, NULL, NULL);
if (pid == -1)
return posix_error();
PyOS_AfterFork();
return Py_BuildValue("(ii)", pid, master_fd);
}
#endif

#ifdef HAVE_GETEGID
static char posix_getegid__doc__[] =
Expand Down Expand Up @@ -4514,6 +4572,12 @@ static PyMethodDef posix_methods[] = {
#ifdef HAVE_FORK
{"fork", posix_fork, METH_VARARGS, posix_fork__doc__},
#endif /* HAVE_FORK */
#ifdef HAVE_OPENPTY
{"openpty", posix_openpty, METH_VARARGS, posix_openpty__doc__},
#endif /* HAVE_OPENPTY */
#ifdef HAVE_FORKPTY
{"forkpty", posix_forkpty, METH_VARARGS, posix_forkpty__doc__},
#endif /* HAVE_FORKPTY */
#ifdef HAVE_GETEGID
{"getegid", posix_getegid, METH_VARARGS, posix_getegid__doc__},
#endif /* HAVE_GETEGID */
Expand Down
Loading

0 comments on commit 8cef4cf

Please sign in to comment.