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
* 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;
}
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);
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
*/
set_bit(TTY_HUPPED, &tty->flags);
clear_bit(TTY_HUPPING, &tty->flags);
- tty_ldisc_enable(tty);
tty_unlock(tty);
struct tty_struct *tty =
container_of(work, struct tty_struct, hangup_work);
- __tty_hangup(tty);
+ __tty_hangup(tty, 0);
}
/**
printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
#endif
- __tty_hangup(tty);
+ __tty_hangup(tty, 0);
}
EXPORT_SYMBOL(tty_vhangup);
}
}
+ /**
+ * 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.
+ */
+
+ static 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
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(¤t->sighand->siglock);
EXPORT_SYMBOL(start_tty);
+static void tty_update_time(struct timespec *time)
+{
+ unsigned long sec = get_seconds();
+ sec -= sec % 60;
+ if ((long)(sec - time->tv_sec) > 0)
+ time->tv_sec = sec;
+}
+
/**
* tty_read - read method for tty device files
* @file: pointer to tty file
loff_t *ppos)
{
int i;
+ struct inode *inode = file_inode(file);
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
- if (tty_paranoia_check(tty, file_inode(file), "tty_read"))
+ if (tty_paranoia_check(tty, inode, "tty_read"))
return -EIO;
if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
i = -EIO;
tty_ldisc_deref(ld);
+ if (i > 0)
+ tty_update_time(&inode->i_atime);
+
return i;
}
break;
cond_resched();
}
- if (written)
+ if (written) {
+ tty_update_time(&file_inode(file)->i_mtime);
ret = written;
+ }
out:
tty_write_unlock(tty);
return ret;
}
tty->count++;
- mutex_lock(&tty->ldisc_mutex);
WARN_ON(!test_bit(TTY_LDISC, &tty->flags));
- mutex_unlock(&tty->ldisc_mutex);
return 0;
}
}
EXPORT_SYMBOL(tty_free_termios);
+ /**
+ * tty_flush_works - flush all works of a tty
+ * @tty: tty device to flush works for
+ *
+ * Sync flush all works belonging to @tty.
+ */
+ static void tty_flush_works(struct tty_struct *tty)
+ {
+ flush_work(&tty->SAK_work);
+ flush_work(&tty->hangup_work);
+ }
/**
* release_one_tty - release tty structure memory
tty_free_termios(tty);
tty_driver_remove_tty(tty->driver, tty);
tty->port->itty = NULL;
+ cancel_work_sync(&tty->port->buf.work);
if (tty->link)
tty_kref_put(tty->link);
return 0;
#ifdef TTY_DEBUG_HANGUP
- printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__);
+ printk(KERN_DEBUG "%s: %s: final close\n", __func__, tty_name(tty, buf));
#endif
/*
* Ask the line discipline code to release its structures
*/
tty_ldisc_release(tty, o_tty);
+
+ /* Wait for pending work before tty destruction commmences */
+ tty_flush_works(tty);
+ if (o_tty)
+ tty_flush_works(o_tty);
+
+ #ifdef TTY_DEBUG_HANGUP
+ printk(KERN_DEBUG "%s: %s: freeing structure...\n", __func__, tty_name(tty, buf));
+ #endif
/*
* The release_tty function takes care of the details of clearing
* the slots and preserving the termios structure. The tty_unlock_pair