Commit 562123b5 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] dynamic pty allocation

From: "H. Peter Anvin" <hpa@transmeta.com>

Remove the limit of 2048 pty's - allocate them on demand up to the 12:20
dev_t limit: a million.
parent d6ca3890
...@@ -445,7 +445,8 @@ config A2232 ...@@ -445,7 +445,8 @@ config A2232
source "drivers/serial/Kconfig" source "drivers/serial/Kconfig"
config UNIX98_PTYS config UNIX98_PTYS
bool "Unix98 PTY support" bool "Unix98 PTY support" if EMBEDDED
default y
---help--- ---help---
A pseudo terminal (PTY) is a software device consisting of two A pseudo terminal (PTY) is a software device consisting of two
halves: a master and a slave. The slave device behaves identical to halves: a master and a slave. The slave device behaves identical to
...@@ -463,28 +464,38 @@ config UNIX98_PTYS ...@@ -463,28 +464,38 @@ config UNIX98_PTYS
terminal slave can be accessed as /dev/pts/<number>. What was terminal slave can be accessed as /dev/pts/<number>. What was
traditionally /dev/ttyp2 will then be /dev/pts/2, for example. traditionally /dev/ttyp2 will then be /dev/pts/2, for example.
The entries in /dev/pts/ are created on the fly by a virtual All modern Linux systems use the Unix98 ptys. Say Y unless
file system; therefore, if you say Y here you should say Y to you're on an embedded system and want to conserve memory.
"/dev/pts file system for Unix98 PTYs" as well.
If you want to say Y here, you need to have the C library glibc 2.1 config LEGACY_PTYS
or later (equal to libc-6.1, check with "ls -l /lib/libc.so.*"). bool "Legacy (BSD) PTY support"
Read the instructions in <file:Documentation/Changes> pertaining to default y
pseudo terminals. It's safe to say N. ---help---
A pseudo terminal (PTY) is a software device consisting of two
halves: a master and a slave. The slave device behaves identical to
a physical terminal; the master device is used by a process to
read data from and write data to the slave, thereby emulating a
terminal. Typical programs for the master side are telnet servers
and xterms.
Linux has traditionally used the BSD-like names /dev/ptyxx
for masters and /dev/ttyxx for slaves of pseudo
terminals. This scheme has a number of problems, including
security. This option enables these legacy devices; on most
systems, it is safe to say N.
config UNIX98_PTY_COUNT
int "Maximum number of Unix98 PTYs in use (0-2048)" config LEGACY_PTY_COUNT
depends on UNIX98_PTYS int "Maximum number of legacy PTY in use"
depends on LEGACY_PTYS
default "256" default "256"
help ---help---
The maximum number of Unix98 PTYs that can be used at any one time. The maximum number of legacy PTYs that can be used at any one time.
The default is 256, and should be enough for desktop systems. Server The default is 256, and should be more than enough. Embedded
machines which support incoming telnet/rlogin/ssh connections and/or systems may want to reduce this to save memory.
serve several X terminals may want to increase this: every incoming
connection and every xterm uses up one PTY.
When not in use, each additional set of 256 PTYs occupy When not in use, each legacy PTY occupies 12 bytes on 32-bit
approximately 8 KB of kernel memory on 32-bit architectures. architectures and 24 bytes on 64-bit architectures.
config PRINTER config PRINTER
tristate "Parallel printer support" tristate "Parallel printer support"
......
...@@ -25,16 +25,21 @@ ...@@ -25,16 +25,21 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/devfs_fs_kernel.h> #include <linux/devfs_fs_kernel.h>
#include <linux/sysctl.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/bitops.h> #include <asm/bitops.h>
#include <linux/devpts_fs.h> #include <linux/devpts_fs.h>
#if defined(CONFIG_LEGACY_PTYS) || defined(CONFIG_UNIX98_PTYS)
#ifdef CONFIG_LEGACY_PTYS
static struct tty_driver *pty_driver, *pty_slave_driver; static struct tty_driver *pty_driver, *pty_slave_driver;
#endif
#ifdef CONFIG_UNIX98_PTYS
/* These are global because they are accessed in tty_io.c */ /* These are global because they are accessed in tty_io.c */
#ifdef CONFIG_UNIX98_PTYS
struct tty_driver *ptm_driver; struct tty_driver *ptm_driver;
struct tty_driver *pts_driver; struct tty_driver *pts_driver;
#endif #endif
...@@ -226,6 +231,7 @@ static int pty_set_lock(struct tty_struct *tty, int * arg) ...@@ -226,6 +231,7 @@ static int pty_set_lock(struct tty_struct *tty, int * arg)
return 0; return 0;
} }
#ifdef CONFIG_LEGACY_PTYS
static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file, static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
...@@ -239,6 +245,7 @@ static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file, ...@@ -239,6 +245,7 @@ static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file,
} }
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
#endif
#ifdef CONFIG_UNIX98_PTYS #ifdef CONFIG_UNIX98_PTYS
static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file,
...@@ -249,11 +256,13 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, ...@@ -249,11 +256,13 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file,
return -EIO; return -EIO;
} }
switch(cmd) { switch(cmd) {
case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
return pty_set_lock(tty, (int *)arg);
case TIOCGPTN: /* Get PT Number */ case TIOCGPTN: /* Get PT Number */
return pty_get_device_number(tty, (unsigned int *)arg); return pty_get_device_number(tty, (unsigned int *)arg);
} }
return pty_bsd_ioctl(tty,file,cmd,arg); return -ENOIOCTLCMD;
} }
#endif #endif
...@@ -313,8 +322,41 @@ static struct tty_operations pty_ops = { ...@@ -313,8 +322,41 @@ static struct tty_operations pty_ops = {
.set_termios = pty_set_termios, .set_termios = pty_set_termios,
}; };
/* sysctl support for setting limits on the number of Unix98 ptys allocated.
Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly. */
#ifdef CONFIG_UNIX98_PTYS
int pty_limit = NR_UNIX98_PTY_DEFAULT;
static int pty_limit_min = 0;
static int pty_limit_max = NR_UNIX98_PTY_MAX;
ctl_table pty_table[] = {
{
.ctl_name = PTY_MAX,
.procname = "max",
.maxlen = sizeof(int),
.mode = 0644,
.data = &pty_limit,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &pty_limit_min,
.extra2 = &pty_limit_max,
}, {
.ctl_name = PTY_NR,
.procname = "nr",
.maxlen = sizeof(int),
.mode = 0444,
.proc_handler = &proc_dointvec,
}, {
.ctl_name = 0
}
};
#endif
/* Initialization */
static int __init pty_init(void) static int __init pty_init(void)
{ {
#ifdef CONFIG_LEGACY_PTYS
/* Traditional BSD devices */ /* Traditional BSD devices */
pty_driver = alloc_tty_driver(NR_PTYS); pty_driver = alloc_tty_driver(NR_PTYS);
...@@ -363,15 +405,15 @@ static int __init pty_init(void) ...@@ -363,15 +405,15 @@ static int __init pty_init(void)
if (tty_register_driver(pty_slave_driver)) if (tty_register_driver(pty_slave_driver))
panic("Couldn't register pty slave driver"); panic("Couldn't register pty slave driver");
#endif /* CONFIG_LEGACY_PTYS */
/* Unix98 devices */
#ifdef CONFIG_UNIX98_PTYS #ifdef CONFIG_UNIX98_PTYS
/* Unix98 devices */
devfs_mk_dir("pts"); devfs_mk_dir("pts");
printk("pty: %d Unix98 ptys configured\n", UNIX98_NR_MAJORS*NR_PTYS); ptm_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX);
ptm_driver = alloc_tty_driver(UNIX98_NR_MAJORS * NR_PTYS);
if (!ptm_driver) if (!ptm_driver)
panic("Couldn't allocate Unix98 ptm driver"); panic("Couldn't allocate Unix98 ptm driver");
pts_driver = alloc_tty_driver(UNIX98_NR_MAJORS * NR_PTYS); pts_driver = alloc_tty_driver(NR_UNIX98_PTY_MAX);
if (!pts_driver) if (!pts_driver)
panic("Couldn't allocate Unix98 pts driver"); panic("Couldn't allocate Unix98 pts driver");
...@@ -388,7 +430,7 @@ static int __init pty_init(void) ...@@ -388,7 +430,7 @@ static int __init pty_init(void)
ptm_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; ptm_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
ptm_driver->init_termios.c_lflag = 0; ptm_driver->init_termios.c_lflag = 0;
ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
TTY_DRIVER_NO_DEVFS; TTY_DRIVER_NO_DEVFS | TTY_DRIVER_DEVPTS_MEM;
ptm_driver->other = pts_driver; ptm_driver->other = pts_driver;
tty_set_operations(ptm_driver, &pty_ops); tty_set_operations(ptm_driver, &pty_ops);
ptm_driver->ioctl = pty_unix98_ioctl; ptm_driver->ioctl = pty_unix98_ioctl;
...@@ -402,8 +444,8 @@ static int __init pty_init(void) ...@@ -402,8 +444,8 @@ static int __init pty_init(void)
pts_driver->subtype = PTY_TYPE_SLAVE; pts_driver->subtype = PTY_TYPE_SLAVE;
pts_driver->init_termios = tty_std_termios; pts_driver->init_termios = tty_std_termios;
pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; TTY_DRIVER_NO_DEVFS | TTY_DRIVER_DEVPTS_MEM;
pts_driver->other = ptm_driver; pts_driver->other = ptm_driver;
tty_set_operations(pts_driver, &pty_ops); tty_set_operations(pts_driver, &pty_ops);
...@@ -411,7 +453,12 @@ static int __init pty_init(void) ...@@ -411,7 +453,12 @@ static int __init pty_init(void)
panic("Couldn't register Unix98 ptm driver"); panic("Couldn't register Unix98 ptm driver");
if (tty_register_driver(pts_driver)) if (tty_register_driver(pts_driver))
panic("Couldn't register Unix98 pts driver"); panic("Couldn't register Unix98 pts driver");
#endif
pty_table[1].data = &ptm_driver->refcount;
#endif /* CONFIG_UNIX98_PTYS */
return 0; return 0;
} }
module_init(pty_init); module_init(pty_init);
#endif /* CONFIG_LEGACY_PTYS || CONFIG_UNIX98_PTYS */
...@@ -124,7 +124,7 @@ struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */ ...@@ -124,7 +124,7 @@ struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */
#ifdef CONFIG_UNIX98_PTYS #ifdef CONFIG_UNIX98_PTYS
extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */ extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */
extern struct tty_driver *pts_driver; /* Unix98 pty slaves; for /dev/ptmx */ extern int pty_limit; /* Config limit on Unix98 ptys */
#endif #endif
extern void disable_early_printk(void); extern void disable_early_printk(void);
...@@ -799,7 +799,13 @@ static int init_dev(struct tty_driver *driver, int idx, ...@@ -799,7 +799,13 @@ static int init_dev(struct tty_driver *driver, int idx,
down_tty_sem(idx); down_tty_sem(idx);
/* check whether we're reopening an existing tty */ /* check whether we're reopening an existing tty */
if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
tty = devpts_get_tty(idx);
if (tty && driver->subtype == PTY_TYPE_MASTER)
tty = tty->link;
} else {
tty = driver->ttys[idx]; tty = driver->ttys[idx];
}
if (tty) goto fast_track; if (tty) goto fast_track;
/* /*
...@@ -827,7 +833,14 @@ static int init_dev(struct tty_driver *driver, int idx, ...@@ -827,7 +833,14 @@ static int init_dev(struct tty_driver *driver, int idx,
tty->index = idx; tty->index = idx;
tty_line_name(driver, idx, tty->name); tty_line_name(driver, idx, tty->name);
if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
tp_loc = &tty->termios;
ltp_loc = &tty->termios_locked;
} else {
tp_loc = &driver->termios[idx]; tp_loc = &driver->termios[idx];
ltp_loc = &driver->termios_locked[idx];
}
if (!*tp_loc) { if (!*tp_loc) {
tp = (struct termios *) kmalloc(sizeof(struct termios), tp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL); GFP_KERNEL);
...@@ -836,7 +849,6 @@ static int init_dev(struct tty_driver *driver, int idx, ...@@ -836,7 +849,6 @@ static int init_dev(struct tty_driver *driver, int idx,
*tp = driver->init_termios; *tp = driver->init_termios;
} }
ltp_loc = &driver->termios_locked[idx];
if (!*ltp_loc) { if (!*ltp_loc) {
ltp = (struct termios *) kmalloc(sizeof(struct termios), ltp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL); GFP_KERNEL);
...@@ -854,7 +866,14 @@ static int init_dev(struct tty_driver *driver, int idx, ...@@ -854,7 +866,14 @@ static int init_dev(struct tty_driver *driver, int idx,
o_tty->index = idx; o_tty->index = idx;
tty_line_name(driver->other, idx, o_tty->name); tty_line_name(driver->other, idx, o_tty->name);
if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
o_tp_loc = &o_tty->termios;
o_ltp_loc = &o_tty->termios_locked;
} else {
o_tp_loc = &driver->other->termios[idx]; o_tp_loc = &driver->other->termios[idx];
o_ltp_loc = &driver->other->termios_locked[idx];
}
if (!*o_tp_loc) { if (!*o_tp_loc) {
o_tp = (struct termios *) o_tp = (struct termios *)
kmalloc(sizeof(struct termios), GFP_KERNEL); kmalloc(sizeof(struct termios), GFP_KERNEL);
...@@ -863,7 +882,6 @@ static int init_dev(struct tty_driver *driver, int idx, ...@@ -863,7 +882,6 @@ static int init_dev(struct tty_driver *driver, int idx,
*o_tp = driver->other->init_termios; *o_tp = driver->other->init_termios;
} }
o_ltp_loc = &driver->other->termios_locked[idx];
if (!*o_ltp_loc) { if (!*o_ltp_loc) {
o_ltp = (struct termios *) o_ltp = (struct termios *)
kmalloc(sizeof(struct termios), GFP_KERNEL); kmalloc(sizeof(struct termios), GFP_KERNEL);
...@@ -875,7 +893,9 @@ static int init_dev(struct tty_driver *driver, int idx, ...@@ -875,7 +893,9 @@ static int init_dev(struct tty_driver *driver, int idx,
/* /*
* Everything allocated ... set up the o_tty structure. * Everything allocated ... set up the o_tty structure.
*/ */
if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) {
driver->other->ttys[idx] = o_tty; driver->other->ttys[idx] = o_tty;
}
if (!*o_tp_loc) if (!*o_tp_loc)
*o_tp_loc = o_tp; *o_tp_loc = o_tp;
if (!*o_ltp_loc) if (!*o_ltp_loc)
...@@ -896,7 +916,9 @@ static int init_dev(struct tty_driver *driver, int idx, ...@@ -896,7 +916,9 @@ static int init_dev(struct tty_driver *driver, int idx,
* Failures after this point use release_mem to clean up, so * Failures after this point use release_mem to clean up, so
* there's no need to null out the local pointers. * there's no need to null out the local pointers.
*/ */
if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
driver->ttys[idx] = tty; driver->ttys[idx] = tty;
}
if (!*tp_loc) if (!*tp_loc)
*tp_loc = tp; *tp_loc = tp;
...@@ -994,13 +1016,21 @@ static void release_mem(struct tty_struct *tty, int idx) ...@@ -994,13 +1016,21 @@ static void release_mem(struct tty_struct *tty, int idx)
{ {
struct tty_struct *o_tty; struct tty_struct *o_tty;
struct termios *tp; struct termios *tp;
int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
if ((o_tty = tty->link) != NULL) { if ((o_tty = tty->link) != NULL) {
if (!devpts)
o_tty->driver->ttys[idx] = NULL; o_tty->driver->ttys[idx] = NULL;
if (o_tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { if (o_tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
tp = o_tty->driver->termios[idx]; tp = o_tty->termios;
if (!devpts)
o_tty->driver->termios[idx] = NULL; o_tty->driver->termios[idx] = NULL;
kfree(tp); kfree(tp);
tp = o_tty->termios_locked;
if (!devpts)
o_tty->driver->termios_locked[idx] = NULL;
kfree(tp);
} }
o_tty->magic = 0; o_tty->magic = 0;
o_tty->driver->refcount--; o_tty->driver->refcount--;
...@@ -1010,12 +1040,20 @@ static void release_mem(struct tty_struct *tty, int idx) ...@@ -1010,12 +1040,20 @@ static void release_mem(struct tty_struct *tty, int idx)
free_tty_struct(o_tty); free_tty_struct(o_tty);
} }
if (!devpts)
tty->driver->ttys[idx] = NULL; tty->driver->ttys[idx] = NULL;
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
tp = tty->driver->termios[idx]; tp = tty->termios;
if (!devpts)
tty->driver->termios[idx] = NULL; tty->driver->termios[idx] = NULL;
kfree(tp); kfree(tp);
tp = tty->termios_locked;
if (!devpts)
tty->driver->termios_locked[idx] = NULL;
kfree(tp);
} }
tty->magic = 0; tty->magic = 0;
tty->driver->refcount--; tty->driver->refcount--;
file_list_lock(); file_list_lock();
...@@ -1059,6 +1097,7 @@ static void release_dev(struct file * filp) ...@@ -1059,6 +1097,7 @@ static void release_dev(struct file * filp)
"free (%s)\n", tty->name); "free (%s)\n", tty->name);
return; return;
} }
if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
if (tty != tty->driver->ttys[idx]) { if (tty != tty->driver->ttys[idx]) {
printk(KERN_DEBUG "release_dev: driver.table[%d] not tty " printk(KERN_DEBUG "release_dev: driver.table[%d] not tty "
"for (%s)\n", idx, tty->name); "for (%s)\n", idx, tty->name);
...@@ -1076,6 +1115,7 @@ static void release_dev(struct file * filp) ...@@ -1076,6 +1115,7 @@ static void release_dev(struct file * filp)
idx, tty->name); idx, tty->name);
return; return;
} }
}
#endif #endif
#ifdef TTY_DEBUG_HANGUP #ifdef TTY_DEBUG_HANGUP
...@@ -1084,7 +1124,8 @@ static void release_dev(struct file * filp) ...@@ -1084,7 +1124,8 @@ static void release_dev(struct file * filp)
#endif #endif
#ifdef TTY_PARANOIA_CHECK #ifdef TTY_PARANOIA_CHECK
if (tty->driver->other) { if (tty->driver->other &&
!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
if (o_tty != tty->driver->other->ttys[idx]) { if (o_tty != tty->driver->other->ttys[idx]) {
printk(KERN_DEBUG "release_dev: other->table[%d] " printk(KERN_DEBUG "release_dev: other->table[%d] "
"not o_tty for (%s)\n", "not o_tty for (%s)\n",
...@@ -1328,23 +1369,29 @@ static int tty_open(struct inode * inode, struct file * filp) ...@@ -1328,23 +1369,29 @@ static int tty_open(struct inode * inode, struct file * filp)
return -ENODEV; return -ENODEV;
} }
if (device == MKDEV(TTYAUX_MAJOR,2)) {
#ifdef CONFIG_UNIX98_PTYS #ifdef CONFIG_UNIX98_PTYS
if (device == MKDEV(TTYAUX_MAJOR,2)) {
/* find a device that is not in use. */ /* find a device that is not in use. */
static int next_ptmx_dev = 0;
retval = -1; retval = -1;
driver = ptm_driver; driver = ptm_driver;
for (index = 0; index < driver->num ; index++) while (driver->refcount < pty_limit) {
index = next_ptmx_dev;
next_ptmx_dev = (next_ptmx_dev+1) % driver->num;
if (!init_dev(driver, index, &tty)) if (!init_dev(driver, index, &tty))
goto ptmx_found; /* ok! */ goto ptmx_found; /* ok! */
}
return -EIO; /* no free ptys */ return -EIO; /* no free ptys */
ptmx_found: ptmx_found:
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
devpts_pty_new(index, MKDEV(pts_driver->major, pts_driver->minor_start) + index); if (devpts_pty_new(tty->link)) {
/* BADNESS - need to destroy both ptm and pts! */
return -ENOMEM;
}
noctty = 1; noctty = 1;
#else } else
return -ENODEV; #endif
#endif /* CONFIG_UNIX_98_PTYS */ {
} else {
driver = get_tty_driver(device, &index); driver = get_tty_driver(device, &index);
if (!driver) if (!driver)
return -ENODEV; return -ENODEV;
...@@ -2190,15 +2237,17 @@ int tty_register_driver(struct tty_driver *driver) ...@@ -2190,15 +2237,17 @@ int tty_register_driver(struct tty_driver *driver)
int i; int i;
dev_t dev; dev_t dev;
char *s; char *s;
void **p; void **p = NULL;
if (driver->flags & TTY_DRIVER_INSTALLED) if (driver->flags & TTY_DRIVER_INSTALLED)
return 0; return 0;
if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
p = kmalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL); p = kmalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL);
if (!p) if (!p)
return -ENOMEM; return -ENOMEM;
memset(p, 0, driver->num * 3 * sizeof(void *)); memset(p, 0, driver->num * 3 * sizeof(void *));
}
if (!driver->major) { if (!driver->major) {
error = alloc_chrdev_region(&dev, driver->minor_start, driver->num, error = alloc_chrdev_region(&dev, driver->minor_start, driver->num,
...@@ -2217,9 +2266,15 @@ int tty_register_driver(struct tty_driver *driver) ...@@ -2217,9 +2266,15 @@ int tty_register_driver(struct tty_driver *driver)
return error; return error;
} }
if (p) {
driver->ttys = (struct tty_struct **)p; driver->ttys = (struct tty_struct **)p;
driver->termios = (struct termios **)(p + driver->num); driver->termios = (struct termios **)(p + driver->num);
driver->termios_locked = (struct termios **)(p + driver->num * 2); driver->termios_locked = (struct termios **)(p + driver->num * 2);
} else {
driver->ttys = NULL;
driver->termios = NULL;
driver->termios_locked = NULL;
}
driver->cdev.kobj.parent = &tty_kobj; driver->cdev.kobj.parent = &tty_kobj;
strcpy(driver->cdev.kobj.name, driver->name); strcpy(driver->cdev.kobj.name, driver->name);
......
...@@ -797,8 +797,7 @@ config DEVFS_FS ...@@ -797,8 +797,7 @@ config DEVFS_FS
the file README there. the file README there.
Note that devfs no longer manages /dev/pts! If you are using UNIX98 Note that devfs no longer manages /dev/pts! If you are using UNIX98
ptys, you will also need to enable (and mount) the /dev/pts ptys, you will also need to mount the /dev/pts filesystem (devpts).
filesystem (CONFIG_DEVPTS_FS).
Note that devfs has been obsoleted by udev, Note that devfs has been obsoleted by udev,
<http://www.kernel.org/pub/linux/utils/kernel/hotplug/>. <http://www.kernel.org/pub/linux/utils/kernel/hotplug/>.
...@@ -831,32 +830,9 @@ config DEVFS_DEBUG ...@@ -831,32 +830,9 @@ config DEVFS_DEBUG
If unsure, say N. If unsure, say N.
config DEVPTS_FS
# It compiles as a module for testing only. It should not be used
# as a module in general. If we make this "tristate", a bunch of people
# who don't know what they are doing turn it on and complain when it
# breaks.
bool "/dev/pts file system for Unix98 PTYs"
depends on UNIX98_PTYS
---help---
You should say Y here if you said Y to "Unix98 PTY support" above.
You'll then get a virtual file system which can be mounted on
/dev/pts with "mount -t devpts". This, together with the pseudo
terminal master multiplexer /dev/ptmx, is used for pseudo terminal
support as described in The Open Group's Unix98 standard: in order
to acquire a pseudo terminal, a process opens /dev/ptmx; the number
of the pseudo terminal is then made available to the process and the
pseudo terminal slave can be accessed as /dev/pts/<number>. What was
traditionally /dev/ttyp2 will then be /dev/pts/2, for example.
The GNU C library glibc 2.1 contains the requisite support for this
mode of operation; you also need client programs that use the Unix98
API. Please read <file:Documentation/Changes> for more information
about the Unix98 pty devices.
config DEVPTS_FS_XATTR config DEVPTS_FS_XATTR
bool "/dev/pts Extended Attributes" bool "/dev/pts Extended Attributes"
depends on DEVPTS_FS depends on UNIX98_PTYS
help help
Extended attributes are name:value pairs associated with inodes by Extended attributes are name:value pairs associated with inodes by
the kernel or by users (see the attr(5) manual page, or visit the kernel or by users (see the attr(5) manual page, or visit
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
# Makefile for the Linux /dev/pts virtual filesystem. # Makefile for the Linux /dev/pts virtual filesystem.
# #
obj-$(CONFIG_DEVPTS_FS) += devpts.o obj-$(CONFIG_UNIX98_PTYS) += devpts.o
devpts-y := inode.o devpts-$(CONFIG_UNIX98_PTYS) := inode.o
devpts-$(CONFIG_DEVPTS_FS_XATTR) += xattr.o devpts-$(CONFIG_DEVPTS_FS_XATTR) += xattr.o
devpts-$(CONFIG_DEVPTS_FS_SECURITY) += xattr_security.o devpts-$(CONFIG_DEVPTS_FS_SECURITY) += xattr_security.o
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* *
* linux/fs/devpts/inode.c * linux/fs/devpts/inode.c
* *
* Copyright 1998 H. Peter Anvin -- All Rights Reserved * Copyright 1998-2004 H. Peter Anvin -- All Rights Reserved
* *
* This file is part of the Linux kernel and is made available under * This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your * the terms of the GNU General Public License, version 2, or at your
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/tty.h>
#include <linux/devpts_fs.h>
#include "xattr.h" #include "xattr.h"
#define DEVPTS_SUPER_MAGIC 0x1cd1 #define DEVPTS_SUPER_MAGIC 0x1cd1
...@@ -126,7 +128,7 @@ static struct file_system_type devpts_fs_type = { ...@@ -126,7 +128,7 @@ static struct file_system_type devpts_fs_type = {
static struct dentry *get_node(int num) static struct dentry *get_node(int num)
{ {
char s[10]; char s[12];
struct dentry *root = devpts_root; struct dentry *root = devpts_root;
down(&root->d_inode->i_sem); down(&root->d_inode->i_sem);
return lookup_one_len(s, root, sprintf(s, "%d", num)); return lookup_one_len(s, root, sprintf(s, "%d", num));
...@@ -139,12 +141,21 @@ static struct inode_operations devpts_file_inode_operations = { ...@@ -139,12 +141,21 @@ static struct inode_operations devpts_file_inode_operations = {
.removexattr = devpts_removexattr, .removexattr = devpts_removexattr,
}; };
void devpts_pty_new(int number, dev_t device) int devpts_pty_new(struct tty_struct *tty)
{ {
int number = tty->index;
struct tty_driver *driver = tty->driver;
dev_t device = MKDEV(driver->major, driver->minor_start+number);
struct dentry *dentry; struct dentry *dentry;
struct inode *inode = new_inode(devpts_mnt->mnt_sb); struct inode *inode = new_inode(devpts_mnt->mnt_sb);
/* We're supposed to be given the slave end of a pty */
BUG_ON(driver->type != TTY_DRIVER_TYPE_PTY);
BUG_ON(driver->subtype != PTY_TYPE_SLAVE);
if (!inode) if (!inode)
return; return -ENOMEM;
inode->i_ino = number+2; inode->i_ino = number+2;
inode->i_blksize = 1024; inode->i_blksize = 1024;
inode->i_uid = config.setuid ? config.uid : current->fsuid; inode->i_uid = config.setuid ? config.uid : current->fsuid;
...@@ -152,11 +163,28 @@ void devpts_pty_new(int number, dev_t device) ...@@ -152,11 +163,28 @@ void devpts_pty_new(int number, dev_t device)
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
init_special_inode(inode, S_IFCHR|config.mode, device); init_special_inode(inode, S_IFCHR|config.mode, device);
inode->i_op = &devpts_file_inode_operations; inode->i_op = &devpts_file_inode_operations;
inode->u.generic_ip = tty;
dentry = get_node(number); dentry = get_node(number);
if (!IS_ERR(dentry) && !dentry->d_inode) if (!IS_ERR(dentry) && !dentry->d_inode)
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
up(&devpts_root->d_inode->i_sem); up(&devpts_root->d_inode->i_sem);
return 0;
}
struct tty_struct *devpts_get_tty(int number)
{
struct dentry *dentry = get_node(number);
struct tty_struct *tty;
tty = (IS_ERR(dentry) || !dentry->d_inode) ? NULL :
dentry->d_inode->u.generic_ip;
up(&devpts_root->d_inode->i_sem);
return tty;
} }
void devpts_pty_kill(int number) void devpts_pty_kill(int number)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* *
* linux/include/linux/devpts_fs.h * linux/include/linux/devpts_fs.h
* *
* Copyright 1998 H. Peter Anvin -- All Rights Reserved * Copyright 1998-2004 H. Peter Anvin -- All Rights Reserved
* *
* This file is part of the Linux kernel and is made available under * This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your * the terms of the GNU General Public License, version 2, or at your
...@@ -13,21 +13,22 @@ ...@@ -13,21 +13,22 @@
#ifndef _LINUX_DEVPTS_FS_H #ifndef _LINUX_DEVPTS_FS_H
#define _LINUX_DEVPTS_FS_H 1 #define _LINUX_DEVPTS_FS_H 1
#ifdef CONFIG_DEVPTS_FS #include <linux/errno.h>
void devpts_pty_new(int, dev_t); /* mknod in devpts */ #if CONFIG_UNIX98_PTYS
int devpts_pty_new(struct tty_struct *); /* mknod in devpts */
struct tty_struct *devpts_get_tty(int); /* get tty structure */
void devpts_pty_kill(int); /* unlink */ void devpts_pty_kill(int); /* unlink */
#else #else
static inline void devpts_pty_new(int line, dev_t device) /* Dummy stubs in the no-pty case */
{ static inline int devpts_pty_new(struct tty_struct *) { return -EINVAL; }
} static inline struct tty_struct *devpts_get_tty(int) { return NULL; }
static inline void devpts_pty_kill(int) { }
static inline void devpts_pty_kill(int line)
{
}
#endif #endif
#endif /* _LINUX_DEVPTS_FS_H */ #endif /* _LINUX_DEVPTS_FS_H */
...@@ -129,6 +129,7 @@ enum ...@@ -129,6 +129,7 @@ enum
KERN_HPPA_UNALIGNED=59, /* int: hppa unaligned-trap enable */ KERN_HPPA_UNALIGNED=59, /* int: hppa unaligned-trap enable */
KERN_PRINTK_RATELIMIT=60, /* int: tune printk ratelimiting */ KERN_PRINTK_RATELIMIT=60, /* int: tune printk ratelimiting */
KERN_PRINTK_RATELIMIT_BURST=61, /* int: tune printk ratelimiting */ KERN_PRINTK_RATELIMIT_BURST=61, /* int: tune printk ratelimiting */
KERN_PTY=62, /* dir: pty driver */
}; };
...@@ -192,6 +193,13 @@ enum ...@@ -192,6 +193,13 @@ enum
RANDOM_UUID=6 RANDOM_UUID=6
}; };
/* /proc/sys/kernel/pty */
enum
{
PTY_MAX=1,
PTY_NR=2
};
/* /proc/sys/bus/isa */ /* /proc/sys/bus/isa */
enum enum
{ {
......
...@@ -28,30 +28,14 @@ ...@@ -28,30 +28,14 @@
/* /*
* Note: don't mess with NR_PTYS until you understand the tty minor
* number allocation game...
* (Note: the *_driver.minor_start values 1, 64, 128, 192 are * (Note: the *_driver.minor_start values 1, 64, 128, 192 are
* hardcoded at present.) * hardcoded at present.)
*/ */
#define NR_PTYS 256 /* ptys/major */ #define NR_PTYS CONFIG_LEGACY_PTY_COUNT /* Number of legacy ptys */
#define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */
#define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */
#define NR_LDISCS 16 #define NR_LDISCS 16
/*
* Unix98 PTY's can be defined as any multiple of NR_PTYS up to
* UNIX98_PTY_MAJOR_COUNT; this section defines what we need from the
* config options
*/
#ifdef CONFIG_UNIX98_PTYS
# define UNIX98_NR_MAJORS ((CONFIG_UNIX98_PTY_COUNT+NR_PTYS-1)/NR_PTYS)
# if UNIX98_NR_MAJORS <= 0
# undef CONFIG_UNIX98_PTYS
# elif UNIX98_NR_MAJORS > UNIX98_PTY_MAJOR_COUNT
# error Too many Unix98 ptys defined
# undef UNIX98_NR_MAJORS
# define UNIX98_NR_MAJORS UNIX98_PTY_MAJOR_COUNT
# endif
#endif
/* /*
* These are set up by the setup-routine at boot-time: * These are set up by the setup-routine at boot-time:
*/ */
......
...@@ -160,9 +160,10 @@ struct tty_driver { ...@@ -160,9 +160,10 @@ struct tty_driver {
const char *devfs_name; const char *devfs_name;
const char *name; const char *name;
int name_base; /* offset of printed name */ int name_base; /* offset of printed name */
short major; /* major device number */ int major; /* major device number */
short minor_start; /* start of minor device number*/ int minor_start; /* start of minor device number */
short num; /* number of devices */ int minor_num; /* number of *possible* devices */
int num; /* number of devices allocated */
short type; /* type of tty driver */ short type; /* type of tty driver */
short subtype; /* subtype of tty driver */ short subtype; /* subtype of tty driver */
struct termios init_termios; /* Initial termios */ struct termios init_termios; /* Initial termios */
...@@ -244,11 +245,15 @@ void tty_set_operations(struct tty_driver *driver, struct tty_operations *op); ...@@ -244,11 +245,15 @@ void tty_set_operations(struct tty_driver *driver, struct tty_operations *op);
* TTY_DRIVER_NO_DEVFS --- if set, do not create devfs entries. This * TTY_DRIVER_NO_DEVFS --- if set, do not create devfs entries. This
* is only used by tty_register_driver(). * is only used by tty_register_driver().
* *
* TTY_DRIVER_DEVPTS_MEM -- don't use the standard arrays, instead
* use dynamic memory keyed through the devpts filesystem. This
* is only applicable to the pty driver.
*/ */
#define TTY_DRIVER_INSTALLED 0x0001 #define TTY_DRIVER_INSTALLED 0x0001
#define TTY_DRIVER_RESET_TERMIOS 0x0002 #define TTY_DRIVER_RESET_TERMIOS 0x0002
#define TTY_DRIVER_REAL_RAW 0x0004 #define TTY_DRIVER_REAL_RAW 0x0004
#define TTY_DRIVER_NO_DEVFS 0x0008 #define TTY_DRIVER_NO_DEVFS 0x0008
#define TTY_DRIVER_DEVPTS_MEM 0x0010
/* tty driver types */ /* tty driver types */
#define TTY_DRIVER_TYPE_SYSTEM 0x0001 #define TTY_DRIVER_TYPE_SYSTEM 0x0001
......
...@@ -133,6 +133,9 @@ static ctl_table fs_table[]; ...@@ -133,6 +133,9 @@ static ctl_table fs_table[];
static ctl_table debug_table[]; static ctl_table debug_table[];
static ctl_table dev_table[]; static ctl_table dev_table[];
extern ctl_table random_table[]; extern ctl_table random_table[];
#ifdef CONFIG_UNIX98_PTYS
extern ctl_table pty_table[];
#endif
/* /proc declarations: */ /* /proc declarations: */
...@@ -518,6 +521,14 @@ static ctl_table kern_table[] = { ...@@ -518,6 +521,14 @@ static ctl_table kern_table[] = {
.mode = 0555, .mode = 0555,
.child = random_table, .child = random_table,
}, },
#ifdef CONFIG_UNIX98_PTYS
{
.ctl_name = KERN_PTY,
.procname = "pty",
.mode = 0555,
.child = pty_table,
},
#endif
{ {
.ctl_name = KERN_OVERFLOWUID, .ctl_name = KERN_OVERFLOWUID,
.procname = "overflowuid", .procname = "overflowuid",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment