diff options
Diffstat (limited to 'drivers/tty/tty_io.c')
-rw-r--r-- | drivers/tty/tty_io.c | 311 |
1 files changed, 181 insertions, 130 deletions
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 05085beb83db..e41b9bbc107d 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -790,19 +790,24 @@ static void session_clear_tty(struct pid *session) void disassociate_ctty(int on_exit) { struct tty_struct *tty; - struct pid *tty_pgrp = NULL; if (!current->signal->leader) return; tty = get_current_tty(); if (tty) { - tty_pgrp = get_pid(tty->pgrp); + struct pid *tty_pgrp = get_pid(tty->pgrp); if (on_exit) { if (tty->driver->type != TTY_DRIVER_TYPE_PTY) tty_vhangup(tty); } tty_kref_put(tty); + if (tty_pgrp) { + kill_pgrp(tty_pgrp, SIGHUP, on_exit); + if (!on_exit) + kill_pgrp(tty_pgrp, SIGCONT, on_exit); + put_pid(tty_pgrp); + } } else if (on_exit) { struct pid *old_pgrp; spin_lock_irq(¤t->sighand->siglock); @@ -816,12 +821,6 @@ void disassociate_ctty(int on_exit) } return; } - if (tty_pgrp) { - kill_pgrp(tty_pgrp, SIGHUP, on_exit); - if (!on_exit) - kill_pgrp(tty_pgrp, SIGCONT, on_exit); - put_pid(tty_pgrp); - } spin_lock_irq(¤t->sighand->siglock); put_pid(current->signal->tty_old_pgrp); @@ -1558,6 +1557,59 @@ static void release_tty(struct tty_struct *tty, int idx) } /** + * tty_release_checks - check a tty before real release + * @tty: tty to check + * @o_tty: link of @tty (if any) + * @idx: index of the tty + * + * Performs some paranoid checking before true release of the @tty. + * This is a no-op unless TTY_PARANOIA_CHECK is defined. + */ +static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty, + int idx) +{ +#ifdef TTY_PARANOIA_CHECK + if (idx < 0 || idx >= tty->driver->num) { + printk(KERN_DEBUG "%s: bad idx when trying to free (%s)\n", + __func__, tty->name); + return -1; + } + + /* not much to check for devpts */ + if (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) + return 0; + + if (tty != tty->driver->ttys[idx]) { + printk(KERN_DEBUG "%s: driver.table[%d] not tty for (%s)\n", + __func__, idx, tty->name); + return -1; + } + if (tty->termios != tty->driver->termios[idx]) { + printk(KERN_DEBUG "%s: driver.termios[%d] not termios for (%s)\n", + __func__, idx, tty->name); + return -1; + } + if (tty->driver->other) { + if (o_tty != tty->driver->other->ttys[idx]) { + printk(KERN_DEBUG "%s: other->table[%d] not o_tty for (%s)\n", + __func__, idx, tty->name); + return -1; + } + if (o_tty->termios != tty->driver->other->termios[idx]) { + printk(KERN_DEBUG "%s: other->termios[%d] not o_termios for (%s)\n", + __func__, idx, tty->name); + return -1; + } + if (o_tty->link != tty) { + printk(KERN_DEBUG "%s: bad pty pointers\n", __func__); + return -1; + } + } +#endif + return 0; +} + +/** * tty_release - vfs callback for close * @inode: inode of tty * @filp: file pointer for handle to tty @@ -1585,11 +1637,11 @@ int tty_release(struct inode *inode, struct file *filp) int idx; char buf[64]; - if (tty_paranoia_check(tty, inode, "tty_release_dev")) + if (tty_paranoia_check(tty, inode, __func__)) return 0; tty_lock(); - check_tty_count(tty, "tty_release_dev"); + check_tty_count(tty, __func__); __tty_fasync(-1, filp, 0); @@ -1599,59 +1651,16 @@ int tty_release(struct inode *inode, struct file *filp) devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; o_tty = tty->link; -#ifdef TTY_PARANOIA_CHECK - if (idx < 0 || idx >= tty->driver->num) { - printk(KERN_DEBUG "tty_release_dev: bad idx when trying to " - "free (%s)\n", tty->name); + if (tty_release_checks(tty, o_tty, idx)) { tty_unlock(); return 0; } - if (!devpts) { - if (tty != tty->driver->ttys[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty " - "for (%s)\n", idx, tty->name); - return 0; - } - if (tty->termios != tty->driver->termios[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios " - "for (%s)\n", - idx, tty->name); - return 0; - } - } -#endif #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...", - tty_name(tty, buf), tty->count); + printk(KERN_DEBUG "%s: %s (tty count=%d)...\n", __func__, + tty_name(tty, buf), tty->count); #endif -#ifdef TTY_PARANOIA_CHECK - if (tty->driver->other && - !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { - if (o_tty != tty->driver->other->ttys[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: other->table[%d] " - "not o_tty for (%s)\n", - idx, tty->name); - return 0 ; - } - if (o_tty->termios != tty->driver->other->termios[idx]) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: other->termios[%d] " - "not o_termios for (%s)\n", - idx, tty->name); - return 0; - } - if (o_tty->link != tty) { - tty_unlock(); - printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n"); - return 0; - } - } -#endif if (tty->ops->close) tty->ops->close(tty, filp); @@ -1707,8 +1716,8 @@ int tty_release(struct inode *inode, struct file *filp) if (!do_sleep) break; - printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue " - "active!\n", tty_name(tty, buf)); + printk(KERN_WARNING "%s: %s: read/write wait queue active!\n", + __func__, tty_name(tty, buf)); tty_unlock(); mutex_unlock(&tty_mutex); schedule(); @@ -1721,15 +1730,14 @@ int tty_release(struct inode *inode, struct file *filp) */ if (pty_master) { if (--o_tty->count < 0) { - printk(KERN_WARNING "tty_release_dev: bad pty slave count " - "(%d) for %s\n", - o_tty->count, tty_name(o_tty, buf)); + printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n", + __func__, o_tty->count, tty_name(o_tty, buf)); o_tty->count = 0; } } if (--tty->count < 0) { - printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n", - tty->count, tty_name(tty, buf)); + printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n", + __func__, tty->count, tty_name(tty, buf)); tty->count = 0; } @@ -1778,7 +1786,7 @@ int tty_release(struct inode *inode, struct file *filp) } #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "freeing tty structure..."); + printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__); #endif /* * Ask the line discipline code to release its structures @@ -1798,6 +1806,83 @@ int tty_release(struct inode *inode, struct file *filp) } /** + * tty_open_current_tty - get tty of current task for open + * @device: device number + * @filp: file pointer to tty + * @return: tty of the current task iff @device is /dev/tty + * + * We cannot return driver and index like for the other nodes because + * devpts will not work then. It expects inodes to be from devpts FS. + */ +static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) +{ + struct tty_struct *tty; + + if (device != MKDEV(TTYAUX_MAJOR, 0)) + return NULL; + + tty = get_current_tty(); + if (!tty) + return ERR_PTR(-ENXIO); + + filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ + /* noctty = 1; */ + tty_kref_put(tty); + /* FIXME: we put a reference and return a TTY! */ + return tty; +} + +/** + * tty_lookup_driver - lookup a tty driver for a given device file + * @device: device number + * @filp: file pointer to tty + * @noctty: set if the device should not become a controlling tty + * @index: index for the device in the @return driver + * @return: driver for this inode (with increased refcount) + * + * If @return is not erroneous, the caller is responsible to decrement the + * refcount by tty_driver_kref_put. + * + * Locking: tty_mutex protects get_tty_driver + */ +static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, + int *noctty, int *index) +{ + struct tty_driver *driver; + + switch (device) { +#ifdef CONFIG_VT + case MKDEV(TTY_MAJOR, 0): { + extern struct tty_driver *console_driver; + driver = tty_driver_kref_get(console_driver); + *index = fg_console; + *noctty = 1; + break; + } +#endif + case MKDEV(TTYAUX_MAJOR, 1): { + struct tty_driver *console_driver = console_device(index); + if (console_driver) { + driver = tty_driver_kref_get(console_driver); + if (driver) { + /* Don't let /dev/console block */ + filp->f_flags |= O_NONBLOCK; + *noctty = 1; + break; + } + } + return ERR_PTR(-ENODEV); + } + default: + driver = get_tty_driver(device, index); + if (!driver) + return ERR_PTR(-ENODEV); + break; + } + return driver; +} + +/** * tty_open - open a tty device * @inode: inode of device file * @filp: file pointer to tty @@ -1813,16 +1898,16 @@ int tty_release(struct inode *inode, struct file *filp) * The termios state of a pty is reset on first open so that * settings don't persist across reuse. * - * Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work. + * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand */ static int tty_open(struct inode *inode, struct file *filp) { - struct tty_struct *tty = NULL; + struct tty_struct *tty; int noctty, retval; - struct tty_driver *driver; + struct tty_driver *driver = NULL; int index; dev_t device = inode->i_rdev; unsigned saved_flags = filp->f_flags; @@ -1841,66 +1926,22 @@ retry_open: mutex_lock(&tty_mutex); tty_lock(); - if (device == MKDEV(TTYAUX_MAJOR, 0)) { - tty = get_current_tty(); - if (!tty) { - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_free_file(filp); - return -ENXIO; - } - driver = tty_driver_kref_get(tty->driver); - index = tty->index; - filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ - /* noctty = 1; */ - /* FIXME: Should we take a driver reference ? */ - tty_kref_put(tty); - goto got_driver; - } -#ifdef CONFIG_VT - if (device == MKDEV(TTY_MAJOR, 0)) { - extern struct tty_driver *console_driver; - driver = tty_driver_kref_get(console_driver); - index = fg_console; - noctty = 1; - goto got_driver; - } -#endif - if (device == MKDEV(TTYAUX_MAJOR, 1)) { - struct tty_driver *console_driver = console_device(&index); - if (console_driver) { - driver = tty_driver_kref_get(console_driver); - if (driver) { - /* Don't let /dev/console block */ - filp->f_flags |= O_NONBLOCK; - noctty = 1; - goto got_driver; - } + tty = tty_open_current_tty(device, filp); + if (IS_ERR(tty)) { + retval = PTR_ERR(tty); + goto err_unlock; + } else if (!tty) { + driver = tty_lookup_driver(device, filp, &noctty, &index); + if (IS_ERR(driver)) { + retval = PTR_ERR(driver); + goto err_unlock; } - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_free_file(filp); - return -ENODEV; - } - driver = get_tty_driver(device, &index); - if (!driver) { - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_free_file(filp); - return -ENODEV; - } -got_driver: - if (!tty) { /* check whether we're reopening an existing tty */ tty = tty_driver_lookup_tty(driver, inode, index); - if (IS_ERR(tty)) { - tty_unlock(); - mutex_unlock(&tty_mutex); - tty_driver_kref_put(driver); - tty_free_file(filp); - return PTR_ERR(tty); + retval = PTR_ERR(tty); + goto err_unlock; } } @@ -1912,21 +1953,22 @@ got_driver: tty = tty_init_dev(driver, index, 0); mutex_unlock(&tty_mutex); - tty_driver_kref_put(driver); + if (driver) + tty_driver_kref_put(driver); if (IS_ERR(tty)) { tty_unlock(); - tty_free_file(filp); - return PTR_ERR(tty); + retval = PTR_ERR(tty); + goto err_file; } tty_add_file(tty, filp); - check_tty_count(tty, "tty_open"); + check_tty_count(tty, __func__); if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) noctty = 1; #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "opening %s...", tty->name); + printk(KERN_DEBUG "%s: opening %s...\n", __func__, tty->name); #endif if (tty->ops->open) retval = tty->ops->open(tty, filp); @@ -1940,8 +1982,8 @@ got_driver: if (retval) { #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "error %d in opening %s...", retval, - tty->name); + printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, + retval, tty->name); #endif tty_unlock(); /* need to call tty_release without BTM */ tty_release(inode, filp); @@ -1976,6 +2018,15 @@ got_driver: tty_unlock(); mutex_unlock(&tty_mutex); return 0; +err_unlock: + tty_unlock(); + mutex_unlock(&tty_mutex); + /* after locks to avoid deadlock */ + if (!IS_ERR_OR_NULL(driver)) + tty_driver_kref_put(driver); +err_file: + tty_free_file(filp); + return retval; } @@ -3267,7 +3318,7 @@ void __init console_init(void) } } -static char *tty_devnode(struct device *dev, mode_t *mode) +static char *tty_devnode(struct device *dev, umode_t *mode) { if (!mode) return NULL; |