tty: Signal SIGHUP before hanging up ldisc
[linux-2.6-block.git] / drivers / tty / tty_io.c
index fd473639ab709ad97c895b9b8cb484259699a3af..d3ddb31e363e1181594e5ce0d768537a6ae7e5ee 100644 (file)
@@ -532,6 +532,60 @@ void tty_wakeup(struct tty_struct *tty)
 
 EXPORT_SYMBOL_GPL(tty_wakeup);
 
+/**
+ *     tty_signal_session_leader       - sends SIGHUP to session leader
+ *     @tty            controlling tty
+ *     @exit_session   if non-zero, signal all foreground group processes
+ *
+ *     Send SIGHUP and SIGCONT to the session leader and its process group.
+ *     Optionally, signal all processes in the foreground process group.
+ *
+ *     Returns the number of processes in the session with this tty
+ *     as their controlling terminal. This value is used to drop
+ *     tty references for those processes.
+ */
+static int tty_signal_session_leader(struct tty_struct *tty, int exit_session)
+{
+       struct task_struct *p;
+       int refs = 0;
+       struct pid *tty_pgrp = NULL;
+
+       read_lock(&tasklist_lock);
+       if (tty->session) {
+               do_each_pid_task(tty->session, PIDTYPE_SID, p) {
+                       spin_lock_irq(&p->sighand->siglock);
+                       if (p->signal->tty == tty) {
+                               p->signal->tty = NULL;
+                               /* We defer the dereferences outside fo
+                                  the tasklist lock */
+                               refs++;
+                       }
+                       if (!p->signal->leader) {
+                               spin_unlock_irq(&p->sighand->siglock);
+                               continue;
+                       }
+                       __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
+                       __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
+                       put_pid(p->signal->tty_old_pgrp);  /* A noop */
+                       spin_lock(&tty->ctrl_lock);
+                       tty_pgrp = get_pid(tty->pgrp);
+                       if (tty->pgrp)
+                               p->signal->tty_old_pgrp = get_pid(tty->pgrp);
+                       spin_unlock(&tty->ctrl_lock);
+                       spin_unlock_irq(&p->sighand->siglock);
+               } while_each_pid_task(tty->session, PIDTYPE_SID, p);
+       }
+       read_unlock(&tasklist_lock);
+
+       if (tty_pgrp) {
+               if (exit_session)
+                       kill_pgrp(tty_pgrp, SIGHUP, exit_session);
+               put_pid(tty_pgrp);
+       }
+
+       return refs;
+}
+
 /**
  *     __tty_hangup            -       actual handler for hangup events
  *     @work: tty device
@@ -554,15 +608,13 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
  *               tasklist_lock to walk task list for hangup event
  *                 ->siglock to protect ->signal/->sighand
  */
-static void __tty_hangup(struct tty_struct *tty)
+static void __tty_hangup(struct tty_struct *tty, int exit_session)
 {
        struct file *cons_filp = NULL;
        struct file *filp, *f = NULL;
-       struct task_struct *p;
        struct tty_file_private *priv;
        int    closecount = 0, n;
-       unsigned long flags;
-       int refs = 0;
+       int refs;
 
        if (!tty)
                return;
@@ -599,39 +651,18 @@ static void __tty_hangup(struct tty_struct *tty)
        }
        spin_unlock(&tty_files_lock);
 
+       refs = tty_signal_session_leader(tty, exit_session);
+       /* Account for the p->signal references we killed */
+       while (refs--)
+               tty_kref_put(tty);
+
        /*
         * it drops BTM and thus races with reopen
         * we protect the race by TTY_HUPPING
         */
        tty_ldisc_hangup(tty);
 
-       read_lock(&tasklist_lock);
-       if (tty->session) {
-               do_each_pid_task(tty->session, PIDTYPE_SID, p) {
-                       spin_lock_irq(&p->sighand->siglock);
-                       if (p->signal->tty == tty) {
-                               p->signal->tty = NULL;
-                               /* We defer the dereferences outside fo
-                                  the tasklist lock */
-                               refs++;
-                       }
-                       if (!p->signal->leader) {
-                               spin_unlock_irq(&p->sighand->siglock);
-                               continue;
-                       }
-                       __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
-                       __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
-                       put_pid(p->signal->tty_old_pgrp);  /* A noop */
-                       spin_lock_irqsave(&tty->ctrl_lock, flags);
-                       if (tty->pgrp)
-                               p->signal->tty_old_pgrp = get_pid(tty->pgrp);
-                       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-                       spin_unlock_irq(&p->sighand->siglock);
-               } while_each_pid_task(tty->session, PIDTYPE_SID, p);
-       }
-       read_unlock(&tasklist_lock);
-
-       spin_lock_irqsave(&tty->ctrl_lock, flags);
+       spin_lock_irq(&tty->ctrl_lock);
        clear_bit(TTY_THROTTLED, &tty->flags);
        clear_bit(TTY_PUSH, &tty->flags);
        clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
@@ -640,11 +671,7 @@ static void __tty_hangup(struct tty_struct *tty)
        tty->session = NULL;
        tty->pgrp = NULL;
        tty->ctrl_status = 0;
-       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-
-       /* Account for the p->signal references we killed */
-       while (refs--)
-               tty_kref_put(tty);
+       spin_unlock_irq(&tty->ctrl_lock);
 
        /*
         * If one of the devices matches a console pointer, we
@@ -679,7 +706,7 @@ static void do_tty_hangup(struct work_struct *work)
        struct tty_struct *tty =
                container_of(work, struct tty_struct, hangup_work);
 
-       __tty_hangup(tty);
+       __tty_hangup(tty, 0);
 }
 
 /**
@@ -717,7 +744,7 @@ void tty_vhangup(struct tty_struct *tty)
 
        printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
 #endif
-       __tty_hangup(tty);
+       __tty_hangup(tty, 0);
 }
 
 EXPORT_SYMBOL(tty_vhangup);
@@ -740,6 +767,27 @@ void tty_vhangup_self(void)
        }
 }
 
+/**
+ *     tty_vhangup_session             -       hangup session leader exit
+ *     @tty: tty to hangup
+ *
+ *     The session leader is exiting and hanging up its controlling terminal.
+ *     Every process in the foreground process group is signalled SIGHUP.
+ *
+ *     We do this synchronously so that when the syscall returns the process
+ *     is complete. That guarantee is necessary for security reasons.
+ */
+
+void tty_vhangup_session(struct tty_struct *tty)
+{
+#ifdef TTY_DEBUG_HANGUP
+       char    buf[64];
+
+       printk(KERN_DEBUG "%s vhangup session...\n", tty_name(tty, buf));
+#endif
+       __tty_hangup(tty, 1);
+}
+
 /**
  *     tty_hung_up_p           -       was tty hung up
  *     @filp: file pointer of tty
@@ -797,18 +845,18 @@ void disassociate_ctty(int on_exit)
 
        tty = get_current_tty();
        if (tty) {
-               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)
+               if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) {
+                       tty_vhangup_session(tty);
+               } else {
+                       struct pid *tty_pgrp = tty_get_pgrp(tty);
+                       if (tty_pgrp) {
+                               kill_pgrp(tty_pgrp, SIGHUP, on_exit);
                                kill_pgrp(tty_pgrp, SIGCONT, on_exit);
-                       put_pid(tty_pgrp);
+                               put_pid(tty_pgrp);
+                       }
                }
+               tty_kref_put(tty);
+
        } else if (on_exit) {
                struct pid *old_pgrp;
                spin_lock_irq(&current->sighand->siglock);
@@ -960,11 +1008,10 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
                        loff_t *ppos)
 {
        int i;
-       struct inode *inode = file->f_path.dentry->d_inode;
        struct tty_struct *tty = file_tty(file);
        struct tty_ldisc *ld;
 
-       if (tty_paranoia_check(tty, inode, "tty_read"))
+       if (tty_paranoia_check(tty, file_inode(file), "tty_read"))
                return -EIO;
        if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
                return -EIO;
@@ -1132,12 +1179,11 @@ void tty_write_message(struct tty_struct *tty, char *msg)
 static ssize_t tty_write(struct file *file, const char __user *buf,
                                                size_t count, loff_t *ppos)
 {
-       struct inode *inode = file->f_path.dentry->d_inode;
        struct tty_struct *tty = file_tty(file);
        struct tty_ldisc *ld;
        ssize_t ret;
 
-       if (tty_paranoia_check(tty, inode, "tty_write"))
+       if (tty_paranoia_check(tty, file_inode(file), "tty_write"))
                return -EIO;
        if (!tty || !tty->ops->write ||
                (test_bit(TTY_IO_ERROR, &tty->flags)))
@@ -2047,7 +2093,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
        struct tty_ldisc *ld;
        int ret = 0;
 
-       if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_poll"))
+       if (tty_paranoia_check(tty, file_inode(filp), "tty_poll"))
                return 0;
 
        ld = tty_ldisc_ref_wait(tty);
@@ -2063,7 +2109,7 @@ static int __tty_fasync(int fd, struct file *filp, int on)
        unsigned long flags;
        int retval = 0;
 
-       if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "tty_fasync"))
+       if (tty_paranoia_check(tty, file_inode(filp), "tty_fasync"))
                goto out;
 
        retval = fasync_helper(fd, filp, on, &tty->fasync);
@@ -2637,9 +2683,8 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        void __user *p = (void __user *)arg;
        int retval;
        struct tty_ldisc *ld;
-       struct inode *inode = file->f_dentry->d_inode;
 
-       if (tty_paranoia_check(tty, inode, "tty_ioctl"))
+       if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl"))
                return -EINVAL;
 
        real_tty = tty_pair_get_tty(tty);
@@ -2780,12 +2825,11 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 static long tty_compat_ioctl(struct file *file, unsigned int cmd,
                                unsigned long arg)
 {
-       struct inode *inode = file->f_dentry->d_inode;
        struct tty_struct *tty = file_tty(file);
        struct tty_ldisc *ld;
        int retval = -ENOIOCTLCMD;
 
-       if (tty_paranoia_check(tty, inode, "tty_ioctl"))
+       if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl"))
                return -EINVAL;
 
        if (tty->ops->compat_ioctl) {