PATCH: new-style ptys.

C. Scott Ananian (cananian@lcs.mit.edu)
Thu, 15 Jan 1998 01:58:55 -0500 (EST)


The attached patch adds support for a /dev/ptmx device and the associated
ptsname function. Opening /dev/ptmx opens the first available master pty;
in Unix98 conventions, you would then call ptsname() on the open file
descriptor to get the name of the associated slave pty. This patch
implements an ioctl to get this information from the open file descriptor;
the library function would look something like (the below code has been
tested to work):
-------------
char *ptsname(int fildes)
{
static const char pty1[]={ "pqrstuvwxyzabcde" };
static const char pty2[]={ "0123456789abcdef" };
static char buf[64];
struct stat s;
unsigned int minor;

if (ioctl(fildes, TIOCPTSNAME, &minor)<0) return NULL;

snprintf(buf, sizeof(buf), "/dev/pts/%u", minor);
if (stat(buf, &s)==0) return buf;

if (!(minor<256)) return NULL;
snprintf(buf, sizeof(buf), "/dev/pty%c%c",pty1[minor/16],pty2[minor%16]);
if (stat(buf, &s)==0) return buf;

return NULL;
}
-----------

But we're *kernel* developers, right? Library implementation is somebody
else's problem. =)

This should help the glibc people bring Linux up to Unix98 standards.
Could this be included in the standard kernel, Linus? I have assigned
/dev/ptmx major 5, minor 2; with your approval I'll register this with the
linux device list maintainer.
--Scott
@ @
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-oOO-(_)-OOo-=-=-=-=-=
C. Scott Ananian: cananian@lcs.mit.edu / Declare the Truth boldly and
Laboratory for Computer Science/Crypto / without hindrance.
Massachusetts Institute of Technology /META-PARRESIAS AKOLUTOS:Acts 28:31
-.-. .-.. .. ..-. ..-. --- .-. -.. ... -.-. --- - - .- -. .- -. .. .- -.
PGP key available via finger and from http://www.pdos.lcs.mit.edu/~cananian

diff -ruHpN -X badboys linux-2.1.78-orig/CREDITS linux/CREDITS
--- linux-2.1.78-orig/CREDITS Wed Jan 14 00:02:50 1998
+++ linux/CREDITS Tue Jan 13 21:22:49 1998
@@ -33,6 +33,15 @@ S: Klaproosstraat 72 c 10
S: B-2610 Wilrijk-Antwerpen
S: Belgium

+N: C. Scott Ananian
+E: cananian@alumni.princeton.edu
+W: http://www.pdos.lcs.mit.edu/~cananian
+P: 1024/85AD9EED AD C0 49 08 91 67 DF D7 FA 04 1A EE 09 E8 44 B0
+D: pty improvements.
+S: 322 N. Riverside Dr.
+S: Neptune, NJ 07753
+S: USA
+
N: Erik Andersen
E: andersee@debian.org
W: http://www.inconnect.com/~andersen
diff -ruHpN -X badboys linux-2.1.78-orig/drivers/char/pty.c linux/drivers/char/pty.c
--- linux-2.1.78-orig/drivers/char/pty.c Mon Jan 12 16:36:05 1998
+++ linux/drivers/char/pty.c Thu Jan 15 00:09:08 1998
@@ -2,6 +2,9 @@
* linux/drivers/char/pty.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * Added support for a Unix98-style ptmx device.
+ * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
*/

#include <linux/errno.h>
@@ -191,6 +194,30 @@ static int pty_chars_in_buffer(struct tt
return ((count < N_TTY_BUF_SIZE/2) ? 0 : count);
}

+/*
+ * Return the minor device number of a given pty. This lets us
+ * open a master pty with the multi-headed ptmx device, than
+ * find out which one we got after it is open, with an ioctl.
+ */
+static int pty_get_device_minor(struct tty_struct *tty, unsigned int *value)
+{
+ unsigned int result = MINOR(tty->device);
+ return put_user(result, value);
+}
+
+static int pty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ if (!tty) {
+ printk("pty_ioctl called with NULL tty!\n");
+ return -EIO;
+ }
+ if (cmd==TIOCPTSNAME)
+ return pty_get_device_minor(tty, (unsigned int *)arg);
+
+ return -ENOIOCTLCMD;
+}
+
static void pty_flush_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
@@ -304,6 +331,12 @@ __initfunc(int pty_init(void))
old_pty_slave_driver.minor_start = 192;
old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS;
old_pty_slave_driver.other = &old_pty_driver;
+
+ /* only the master pty gets this ioctl (which is why we
+ * assign it here, instead of up with the rest of the
+ * pty_driver initialization. <cananian@alumni.princeton.edu>
+ */
+ pty_driver.ioctl = pty_ioctl;

if (tty_register_driver(&pty_driver))
panic("Couldn't register pty driver");
diff -ruHpN -X badboys linux-2.1.78-orig/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- linux-2.1.78-orig/drivers/char/tty_io.c Mon Jan 12 16:36:05 1998
+++ linux/drivers/char/tty_io.c Wed Jan 14 19:47:41 1998
@@ -51,6 +51,9 @@
*
* Rewrote init_dev and release_dev to eliminate races.
* -- Bill Hawes <whawes@star.net>, June 97
+ *
+ * Added support for a Unix98-style ptmx device.
+ * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
*/

#include <linux/config.h>
@@ -91,6 +94,7 @@
#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)
+#define PTMX_DEV MKDEV(TTYAUX_MAJOR,2)

#undef TTY_DEBUG_HANGUP

@@ -1171,7 +1175,6 @@ static void release_dev(struct file * fi
static int tty_open(struct inode * inode, struct file * filp)
{
struct tty_struct *tty;
- int minor;
int noctty, retval;
kdev_t device;
unsigned short saved_flags;
@@ -1203,12 +1206,38 @@ retry_open:
device = c->device(c);
noctty = 1;
}
- minor = MINOR(device);
+ if (device == PTMX_DEV) {
+ /* find a free pty. */
+ struct tty_driver *driver = tty_drivers;
+ int minor;
+
+ /* find the pty driver */
+ for (driver=tty_drivers; driver; driver=driver->next)
+ if (driver->major == PTY_MASTER_MAJOR)
+ break;
+ if (!driver) return -ENODEV;
+
+ /* find a minor device that is not in use. */
+ for (minor=driver->minor_start;
+ minor<driver->minor_start+driver->num;
+ minor++) {
+ device = MKDEV(driver->major, minor);
+ retval = init_dev(device, &tty);
+ if (retval==0) break; /* success! */
+ }
+ if (minor==driver->minor_start+driver->num) /* no success */
+ return -EIO; /* no free ptys */

+ noctty = 1;
+ goto init_dev_done;
+ }
+
retval = init_dev(device, &tty);
if (retval)
return retval;
+
/* N.B. this error exit may leave filp->f_flags with O_NONBLOCK set */
+init_dev_done:
filp->private_data = tty;
check_tty_count(tty, "tty_open");
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
@@ -1932,7 +1961,7 @@ long console_init(long kmem_start, long
}

static struct tty_driver dev_tty_driver, dev_console_driver,
- dev_syscons_driver;
+ dev_syscons_driver, dev_ptmx_driver;

/*
* Ok, now we can initialize the rest of the tty devices and can count
@@ -1973,6 +2002,17 @@ __initfunc(int tty_init(void))

if (tty_register_driver(&dev_syscons_driver))
panic("Couldn't register /dev/console driver\n");
+
+ dev_ptmx_driver = dev_tty_driver;
+ dev_ptmx_driver.driver_name = "/dev/ptmx";
+ dev_ptmx_driver.name = dev_ptmx_driver.driver_name + 5;
+ dev_ptmx_driver.major= MAJOR(PTMX_DEV);
+ dev_ptmx_driver.minor_start = MINOR(PTMX_DEV);
+ dev_ptmx_driver.type = TTY_DRIVER_TYPE_SYSTEM;
+ dev_ptmx_driver.subtype = SYSTEM_TYPE_SYSPTMX;
+
+ if (tty_register_driver(&dev_ptmx_driver))
+ panic("Couldn't register /dev/ptmx driver\n");

#ifdef CONFIG_VT
dev_console_driver = dev_tty_driver;
diff -ruHpN -X badboys linux-2.1.78-orig/include/asm-alpha/ioctls.h linux/include/asm-alpha/ioctls.h
--- linux-2.1.78-orig/include/asm-alpha/ioctls.h Mon Jan 12 16:34:42 1998
+++ linux/include/asm-alpha/ioctls.h Wed Jan 14 23:50:14 1998
@@ -86,6 +86,7 @@
#define TIOCSBRK 0x5427 /* BSD compatibility */
#define TIOCCBRK 0x5428 /* BSD compatibility */
#define TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TIOCPTSNAME 0x5430 /* Get minor device of a pty master's FD */

#define TIOCSERCONFIG 0x5453
#define TIOCSERGWILD 0x5454
diff -ruHpN -X badboys linux-2.1.78-orig/include/asm-i386/ioctls.h linux/include/asm-i386/ioctls.h
--- linux-2.1.78-orig/include/asm-i386/ioctls.h Mon Jan 12 16:34:34 1998
+++ linux/include/asm-i386/ioctls.h Wed Jan 14 23:50:17 1998
@@ -47,6 +47,7 @@
#define TIOCSBRK 0x5427 /* BSD compatibility */
#define TIOCCBRK 0x5428 /* BSD compatibility */
#define TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TIOCPTSNAME 0x5430 /* Get minor device of a pty master's FD */

#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
diff -ruHpN -X badboys linux-2.1.78-orig/include/asm-m68k/ioctls.h linux/include/asm-m68k/ioctls.h
--- linux-2.1.78-orig/include/asm-m68k/ioctls.h Mon Jan 12 16:34:42 1998
+++ linux/include/asm-m68k/ioctls.h Wed Jan 14 23:50:49 1998
@@ -47,6 +47,7 @@
#define TIOCSBRK 0x5427 /* BSD compatibility */
#define TIOCCBRK 0x5428 /* BSD compatibility */
#define TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TIOCPTSNAME 0x5430 /* Get minor device of a pty master's FD */

#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
#define FIOCLEX 0x5451
diff -ruHpN -X badboys linux-2.1.78-orig/include/asm-mips/ioctls.h linux/include/asm-mips/ioctls.h
--- linux-2.1.78-orig/include/asm-mips/ioctls.h Mon Jan 12 16:34:40 1998
+++ linux/include/asm-mips/ioctls.h Wed Jan 14 23:52:10 1998
@@ -98,6 +98,8 @@
#define TIOCTTYGSTRUCT 0x5487 /* For debugging only */
#define TIOCSBRK 0x5427 /* BSD compatibility */
#define TIOCCBRK 0x5428 /* BSD compatibility */
+ /* Do we need TIOCGSID here? */
+#define TIOCPTSNAME 0x5430 /* Get minor device of a pty master's FD */

#define TIOCSERCONFIG 0x5488
#define TIOCSERGWILD 0x5489
diff -ruHpN -X badboys linux-2.1.78-orig/include/asm-ppc/ioctls.h linux/include/asm-ppc/ioctls.h
--- linux-2.1.78-orig/include/asm-ppc/ioctls.h Mon Jan 12 16:34:56 1998
+++ linux/include/asm-ppc/ioctls.h Wed Jan 14 23:52:29 1998
@@ -86,6 +86,7 @@
#define TIOCSBRK 0x5427 /* BSD compatibility */
#define TIOCCBRK 0x5428 /* BSD compatibility */
#define TIOCGSID 0x5429 /* Return the session ID of FD */
+#define TIOCPTSNAME 0x5430 /* Get minor device of a pty master's FD */

#define TIOCSERCONFIG 0x5453
#define TIOCSERGWILD 0x5454
diff -ruHpN -X badboys linux-2.1.78-orig/include/asm-sparc/ioctls.h linux/include/asm-sparc/ioctls.h
--- linux-2.1.78-orig/include/asm-sparc/ioctls.h Mon Jan 12 16:34:43 1998
+++ linux/include/asm-sparc/ioctls.h Wed Jan 14 23:54:57 1998
@@ -75,6 +75,8 @@
#define TIOCGPGRP _IOR('t', 131, int)
#define TIOCSCTTY _IO('t', 132)
#define TIOCGSID _IOR('t', 133, int)
+/* Get minor device of a pty master's FD -- is there a Solaris/SunOS equiv? */
+#define TIOCPTSNAME _IOR('t', 134, int)

/* Little f */
#define FIOCLEX _IO('f', 1)
diff -ruHpN -X badboys linux-2.1.78-orig/include/asm-sparc64/ioctls.h linux/include/asm-sparc64/ioctls.h
--- linux-2.1.78-orig/include/asm-sparc64/ioctls.h Mon Jan 12 16:34:57 1998
+++ linux/include/asm-sparc64/ioctls.h Wed Jan 14 23:54:55 1998
@@ -76,6 +76,8 @@
#define TIOCGPGRP _IOR('t', 131, int)
#define TIOCSCTTY _IO('t', 132)
#define TIOCGSID _IOR('t', 133, int)
+/* Get minor device of a pty master's FD -- is there a Solaris/SunOS equiv? */
+#define TIOCPTSNAME _IOR('t', 134, int)

/* Little f */
#define FIOCLEX _IO('f', 1)
diff -ruHpN -X badboys linux-2.1.78-orig/include/linux/tty_driver.h linux/include/linux/tty_driver.h
--- linux-2.1.78-orig/include/linux/tty_driver.h Mon Jan 12 16:34:23 1998
+++ linux/include/linux/tty_driver.h Wed Jan 14 19:40:35 1998
@@ -213,6 +213,7 @@ struct tty_driver {
#define SYSTEM_TYPE_TTY 0x0001
#define SYSTEM_TYPE_CONSOLE 0x0002
#define SYSTEM_TYPE_SYSCONS 0x0003
+#define SYSTEM_TYPE_SYSPTMX 0x0004

/* pty subtypes (magic, used by tty_io.c) */
#define PTY_TYPE_MASTER 0x0001