[Patches] [UPDATE] Revised openpty patch

Thomas Wouters thomas@xs4all.net
Sun, 11 Jun 2000 23:52:40 +0200


--FL5UXtIhxfXey3p5
Content-Type: text/plain; charset=us-ascii


An updated version of my patch to add openpty() and forkpty() to the posix
module. (See http://www.python.org/pipermail/patches/2000-June/000896.html)

Diff does not include configure or config.h.in this time ;) But it does
include a patch to pty.py, which attempts to use os.openpty() and falls back
to the old methods. I've renamed most of the functions to emphasize the fact
that they're supposed to be internal, but wasn't sure wether removing
master_open() and slave_open() was going to break anything; I can imagine
people using them, in lieu of any other semi-portable method of opening a
pty pair. So I've added backwards-compatibility for those two functions.
(os.open(os.ttyname(slave_fd)) works correctly even with Unix98 pty's)

I've also added pty.openpty() as the proper way of getting a pseudo-terminal
pair, also including fallback. Documentation for the new pty and os
functions isn't included in the patch, but is being sent to python-docs.

I've tested this code on Linux (with glibc-2.1), FreeBSD 4.0 and BSDI4.x,
and made sure it compiled without (additional) errors on those three +
Solaris 2.6 -- I couldn't test it on Solaris because Python fails to compile
on our sole, aging Solaris machine. It has turned up one issue: though not
documented as doing so, forkpty() sets the new process as session leader,
causing the setsid() in pty.py to fail. I'm not sure wether to remove it or
not, because that behaviour isn't documented :P



           I confirm that, to the best of my knowledge and belief, this
           contribution is free of any claims of third parties under
           copyright, patent or other rights or interests ("claims").  To
           the extent that I have any such claims, I hereby grant to CNRI a
           nonexclusive, irrevocable, royalty-free, worldwide license to
           reproduce, distribute, perform and/or display publicly, prepare
           derivative versions, and otherwise use this contribution as part
           of the Python software and its related documentation, or any
           derivative versions thereof, at no cost to CNRI or its licensed
           users, and to authorize others to do so.

           I acknowledge that CNRI may, at its sole discretion, decide
           whether or not to incorporate this contribution in the Python
           software and its related documentation.  I further grant CNRI
           permission to use my name and other identifying information
           provided to CNRI by me for use in connection with the Python
           software and its related documentation.


-- 
Thomas Wouters <thomas@xs4all.net>

Hi! I'm a .signature virus! copy me into your .signature file to help me spread!

--FL5UXtIhxfXey3p5
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="openpty.diff"

Index: configure.in
===================================================================
RCS file: /cvsroot/python/python/dist/src/configure.in,v
retrieving revision 1.124
diff -u -r1.124 configure.in
--- configure.in	2000/05/26 12:22:54	1.124
+++ configure.in	2000/06/11 21:33:48
@@ -357,7 +357,7 @@
 signal.h stdarg.h stddef.h stdlib.h thread.h unistd.h utime.h \
 sys/audioio.h sys/file.h sys/lock.h \
 sys/param.h sys/select.h sys/socket.h sys/time.h sys/times.h \
-sys/un.h sys/utsname.h sys/wait.h)
+sys/un.h sys/utsname.h sys/wait.h pty.h libutil.h)
 AC_HEADER_DIRENT
 
 # checks for typedefs
@@ -769,6 +769,11 @@
  sigaction siginterrupt sigrelse strftime strptime symlink sysconf \
  tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \
  truncate uname waitpid)
+
+# check for openpty and forkpty
+
+AC_CHECK_FUNCS(openpty,, AC_CHECK_LIB(util,openpty, [AC_DEFINE(HAVE_OPENPTY)] [LIBS="$LIBS -lutil"]))
+AC_CHECK_FUNCS(forkpty,, AC_CHECK_LIB(util,forkpty, [AC_DEFINE(HAVE_FORKPTY)] [LIBS="$LIBS -lutil"]))
 
 # check for long file support functions
 AC_CHECK_FUNCS(fseek64 fseeko fstatvfs ftell64 ftello statvfs)
Index: Lib/pty.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/pty.py,v
retrieving revision 1.3
diff -u -r1.3 pty.py
--- Lib/pty.py	2000/02/04 15:10:34	1.3
+++ Lib/pty.py	2000/06/11 21:33:49
@@ -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:
@@ -40,22 +68,35 @@
 	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.
@@ -68,17 +109,17 @@
 	# 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)
@@ -91,9 +132,9 @@
 			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,)
@@ -103,6 +144,6 @@
 	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)
Index: Modules/posixmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/posixmodule.c,v
retrieving revision 2.136
diff -u -r2.136 posixmodule.c
--- Modules/posixmodule.c	2000/06/06 20:52:17	2.136
+++ Modules/posixmodule.c	2000/06/11 21:33:51
@@ -1731,6 +1731,64 @@
 }
 #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__[] =
@@ -4514,6 +4572,12 @@
 #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 */

--FL5UXtIhxfXey3p5--