aboutsummaryrefslogtreecommitdiff
path: root/drivers/tty/pty.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/pty.c')
-rw-r--r--drivers/tty/pty.c124
1 files changed, 86 insertions, 38 deletions
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 4219f040adb..25c9bc78372 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -38,16 +38,18 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
if (tty->driver->subtype == PTY_TYPE_MASTER)
WARN_ON(tty->count > 1);
else {
+ if (test_bit(TTY_IO_ERROR, &tty->flags))
+ return;
if (tty->count > 2)
return;
}
+ set_bit(TTY_IO_ERROR, &tty->flags);
wake_up_interruptible(&tty->read_wait);
wake_up_interruptible(&tty->write_wait);
tty->packet = 0;
/* Review - krefs on tty_link ?? */
if (!tty->link)
return;
- tty->link->packet = 0;
set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
wake_up_interruptible(&tty->link->read_wait);
wake_up_interruptible(&tty->link->write_wait);
@@ -55,9 +57,10 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
set_bit(TTY_OTHER_CLOSED, &tty->flags);
#ifdef CONFIG_UNIX98_PTYS
if (tty->driver == ptm_driver) {
- mutex_lock(&devpts_mutex);
- devpts_pty_kill(tty->link->driver_data);
- mutex_unlock(&devpts_mutex);
+ mutex_lock(&devpts_mutex);
+ if (tty->link->driver_data)
+ devpts_pty_kill(tty->link->driver_data);
+ mutex_unlock(&devpts_mutex);
}
#endif
tty_unlock(tty);
@@ -86,17 +89,13 @@ static void pty_unthrottle(struct tty_struct *tty)
* pty_space - report space left for writing
* @to: tty we are writing into
*
- * The tty buffers allow 64K but we sneak a peak and clip at 8K this
- * allows a lot of overspill room for echo and other fun messes to
- * be handled properly
+ * Limit the buffer space used by ptys to 8k.
*/
static int pty_space(struct tty_struct *to)
{
- int n = 8192 - to->port->buf.memory_used;
- if (n < 0)
- return 0;
- return n;
+ int n = tty_buffer_space_avail(to->port);
+ return min(n, 8192);
}
/**
@@ -120,12 +119,10 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c)
if (c > 0) {
/* Stuff the data into the input queue of the other end */
- c = tty_insert_flip_string(to, buf, c);
+ c = tty_insert_flip_string(to->port, buf, c);
/* And shovel */
- if (c) {
- tty_flip_buffer_push(to);
- tty_wakeup(tty);
- }
+ if (c)
+ tty_flip_buffer_push(to->port);
}
return c;
}
@@ -171,6 +168,41 @@ static int pty_set_lock(struct tty_struct *tty, int __user *arg)
return 0;
}
+static int pty_get_lock(struct tty_struct *tty, int __user *arg)
+{
+ int locked = test_bit(TTY_PTY_LOCK, &tty->flags);
+ return put_user(locked, arg);
+}
+
+/* Set the packet mode on a pty */
+static int pty_set_pktmode(struct tty_struct *tty, int __user *arg)
+{
+ unsigned long flags;
+ int pktmode;
+
+ if (get_user(pktmode, arg))
+ return -EFAULT;
+
+ spin_lock_irqsave(&tty->ctrl_lock, flags);
+ if (pktmode) {
+ if (!tty->packet) {
+ tty->packet = 1;
+ tty->link->ctrl_status = 0;
+ }
+ } else
+ tty->packet = 0;
+ spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+
+ return 0;
+}
+
+/* Get the packet mode of a pty */
+static int pty_get_pktmode(struct tty_struct *tty, int __user *arg)
+{
+ int pktmode = tty->packet;
+ return put_user(pktmode, arg);
+}
+
/* Send a signal to the slave */
static int pty_signal(struct tty_struct *tty, int sig)
{
@@ -206,24 +238,24 @@ static void pty_flush_buffer(struct tty_struct *tty)
static int pty_open(struct tty_struct *tty, struct file *filp)
{
- int retval = -ENODEV;
-
if (!tty || !tty->link)
- goto out;
+ return -ENODEV;
- retval = -EIO;
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
goto out;
if (test_bit(TTY_PTY_LOCK, &tty->link->flags))
goto out;
- if (tty->link->count != 1)
+ if (tty->driver->subtype == PTY_TYPE_SLAVE && tty->link->count != 1)
goto out;
+ clear_bit(TTY_IO_ERROR, &tty->flags);
clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
set_bit(TTY_THROTTLED, &tty->flags);
- retval = 0;
+ return 0;
+
out:
- return retval;
+ set_bit(TTY_IO_ERROR, &tty->flags);
+ return -EIO;
}
static void pty_set_termios(struct tty_struct *tty,
@@ -242,14 +274,14 @@ static void pty_set_termios(struct tty_struct *tty,
* peform a terminal resize correctly
*/
-int pty_resize(struct tty_struct *tty, struct winsize *ws)
+static int pty_resize(struct tty_struct *tty, struct winsize *ws)
{
struct pid *pgrp, *rpgrp;
unsigned long flags;
struct tty_struct *pty = tty->link;
/* For a PTY we need to lock the tty side */
- mutex_lock(&tty->termios_mutex);
+ mutex_lock(&tty->winsize_mutex);
if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
goto done;
@@ -276,7 +308,7 @@ int pty_resize(struct tty_struct *tty, struct winsize *ws)
tty->winsize = *ws;
pty->winsize = *ws; /* Never used so will go away soon */
done:
- mutex_unlock(&tty->termios_mutex);
+ mutex_unlock(&tty->winsize_mutex);
return 0;
}
@@ -364,16 +396,9 @@ err:
return retval;
}
-/* this is called once with whichever end is closed last */
-static void pty_unix98_shutdown(struct tty_struct *tty)
-{
- devpts_kill_index(tty->driver_data, tty->index);
-}
-
static void pty_cleanup(struct tty_struct *tty)
{
- tty->port->itty = NULL;
- kfree(tty->port);
+ tty_port_put(tty->port);
}
/* Traditional BSD devices */
@@ -398,8 +423,16 @@ static int pty_bsd_ioctl(struct tty_struct *tty,
switch (cmd) {
case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
return pty_set_lock(tty, (int __user *) arg);
+ case TIOCGPTLCK: /* Get PT Lock status */
+ return pty_get_lock(tty, (int __user *)arg);
+ case TIOCPKT: /* Set PT packet mode */
+ return pty_set_pktmode(tty, (int __user *)arg);
+ case TIOCGPKT: /* Get PT packet mode */
+ return pty_get_pktmode(tty, (int __user *)arg);
case TIOCSIG: /* Send signal to other side of pty */
return pty_signal(tty, (int) arg);
+ case TIOCGPTN: /* TTY returns ENOTTY, but glibc expects EINVAL here */
+ return -EINVAL;
}
return -ENOIOCTLCMD;
}
@@ -512,6 +545,12 @@ static int pty_unix98_ioctl(struct tty_struct *tty,
switch (cmd) {
case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
return pty_set_lock(tty, (int __user *)arg);
+ case TIOCGPTLCK: /* Get PT Lock status */
+ return pty_get_lock(tty, (int __user *)arg);
+ case TIOCPKT: /* Set PT packet mode */
+ return pty_set_pktmode(tty, (int __user *)arg);
+ case TIOCGPKT: /* Get PT packet mode */
+ return pty_get_pktmode(tty, (int __user *)arg);
case TIOCGPTN: /* Get PT Number */
return put_user(tty->index, (unsigned int __user *)arg);
case TIOCSIG: /* Send signal to other side of pty */
@@ -572,6 +611,12 @@ static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
{
}
+/* this is called once with whichever end is closed last */
+static void pty_unix98_shutdown(struct tty_struct *tty)
+{
+ devpts_kill_index(tty->driver_data, tty->index);
+}
+
static const struct tty_operations ptm_unix98_ops = {
.lookup = ptm_unix98_lookup,
.install = pty_unix98_install,
@@ -614,7 +659,7 @@ static const struct tty_operations pty_unix98_ops = {
* Allocate a unix98 pty master device from the ptmx driver.
*
* Locking: tty_mutex protects the init_dev work. tty->count should
- * protect the rest.
+ * protect the rest.
* allocated_ptys_lock handles the list of free pty numbers
*/
@@ -627,6 +672,9 @@ static int ptmx_open(struct inode *inode, struct file *filp)
nonseekable_open(inode, filp);
+ /* We refuse fsnotify events on ptmx, since it's a shared resource */
+ filp->f_mode |= FMODE_NONOTIFY;
+
retval = tty_alloc_file(filp);
if (retval)
return retval;
@@ -655,6 +703,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
mutex_unlock(&tty_mutex);
set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
+ tty->driver_data = inode;
tty_add_file(tty, filp);
@@ -665,14 +714,13 @@ static int ptmx_open(struct inode *inode, struct file *filp)
retval = PTR_ERR(slave_inode);
goto err_release;
}
+ tty->link->driver_data = slave_inode;
retval = ptm_driver->ops->open(tty, filp);
if (retval)
goto err_release;
tty_unlock(tty);
- tty->driver_data = inode;
- tty->link->driver_data = slave_inode;
return 0;
err_release:
tty_unlock(tty);
@@ -748,7 +796,7 @@ static void __init unix98_pty_init(void)
cdev_init(&ptmx_cdev, &ptmx_fops);
if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
- panic("Couldn't register /dev/ptmx driver\n");
+ panic("Couldn't register /dev/ptmx driver");
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
}