diff options
Diffstat (limited to 'drivers/char/tty_io.c')
-rw-r--r-- | drivers/char/tty_io.c | 247 |
1 files changed, 146 insertions, 101 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 47a6eacb10b..5289254e7ab 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -154,7 +154,9 @@ static int tty_release(struct inode *, struct file *); int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); static int tty_fasync(int fd, struct file * filp, int on); -static void release_mem(struct tty_struct *tty, int idx); +static void release_tty(struct tty_struct *tty, int idx); +static struct pid *__proc_set_tty(struct task_struct *tsk, + struct tty_struct *tty); /** * alloc_tty_struct - allocate a tty object @@ -1109,17 +1111,17 @@ int tty_check_change(struct tty_struct * tty) { if (current->signal->tty != tty) return 0; - if (tty->pgrp <= 0) { - printk(KERN_WARNING "tty_check_change: tty->pgrp <= 0!\n"); + if (!tty->pgrp) { + printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n"); return 0; } - if (process_group(current) == tty->pgrp) + if (task_pgrp(current) == tty->pgrp) return 0; if (is_ignored(SIGTTOU)) return 0; - if (is_orphaned_pgrp(process_group(current))) + if (is_current_pgrp_orphaned()) return -EIO; - (void) kill_pg(process_group(current), SIGTTOU, 1); + (void) kill_pgrp(task_pgrp(current), SIGTTOU, 1); return -ERESTARTSYS; } @@ -1354,8 +1356,8 @@ static void do_tty_hangup(struct work_struct *work) tty_release is called */ read_lock(&tasklist_lock); - if (tty->session > 0) { - do_each_task_pid(tty->session, PIDTYPE_SID, p) { + 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; @@ -1365,16 +1367,17 @@ static void do_tty_hangup(struct work_struct *work) } __group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p); __group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p); - if (tty->pgrp > 0) - p->signal->tty_old_pgrp = tty->pgrp; + put_pid(p->signal->tty_old_pgrp); /* A noop */ + if (tty->pgrp) + p->signal->tty_old_pgrp = get_pid(tty->pgrp); spin_unlock_irq(&p->sighand->siglock); - } while_each_task_pid(tty->session, PIDTYPE_SID, p); + } while_each_pid_task(tty->session, PIDTYPE_SID, p); } read_unlock(&tasklist_lock); tty->flags = 0; - tty->session = 0; - tty->pgrp = -1; + tty->session = NULL; + tty->pgrp = NULL; tty->ctrl_status = 0; /* * If one of the devices matches a console pointer, we @@ -1459,12 +1462,12 @@ int tty_hung_up_p(struct file * filp) EXPORT_SYMBOL(tty_hung_up_p); -static void session_clear_tty(pid_t session) +static void session_clear_tty(struct pid *session) { struct task_struct *p; - do_each_task_pid(session, PIDTYPE_SID, p) { + do_each_pid_task(session, PIDTYPE_SID, p) { proc_clear_tty(p); - } while_each_task_pid(session, PIDTYPE_SID, p); + } while_each_pid_task(session, PIDTYPE_SID, p); } /** @@ -1494,46 +1497,54 @@ static void session_clear_tty(pid_t session) void disassociate_ctty(int on_exit) { struct tty_struct *tty; - int tty_pgrp = -1; - int session; + struct pid *tty_pgrp = NULL; lock_kernel(); mutex_lock(&tty_mutex); tty = get_current_tty(); if (tty) { - tty_pgrp = tty->pgrp; + tty_pgrp = get_pid(tty->pgrp); mutex_unlock(&tty_mutex); /* XXX: here we race, there is nothing protecting tty */ if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) tty_vhangup(tty); - } else { - pid_t old_pgrp = current->signal->tty_old_pgrp; + } else if (on_exit) { + struct pid *old_pgrp; + spin_lock_irq(¤t->sighand->siglock); + old_pgrp = current->signal->tty_old_pgrp; + current->signal->tty_old_pgrp = NULL; + spin_unlock_irq(¤t->sighand->siglock); if (old_pgrp) { - kill_pg(old_pgrp, SIGHUP, on_exit); - kill_pg(old_pgrp, SIGCONT, on_exit); + kill_pgrp(old_pgrp, SIGHUP, on_exit); + kill_pgrp(old_pgrp, SIGCONT, on_exit); + put_pid(old_pgrp); } mutex_unlock(&tty_mutex); unlock_kernel(); return; } - if (tty_pgrp > 0) { - kill_pg(tty_pgrp, SIGHUP, on_exit); + if (tty_pgrp) { + kill_pgrp(tty_pgrp, SIGHUP, on_exit); if (!on_exit) - kill_pg(tty_pgrp, SIGCONT, on_exit); + kill_pgrp(tty_pgrp, SIGCONT, on_exit); + put_pid(tty_pgrp); } spin_lock_irq(¤t->sighand->siglock); + tty_pgrp = current->signal->tty_old_pgrp; current->signal->tty_old_pgrp = 0; - session = process_session(current); spin_unlock_irq(¤t->sighand->siglock); + put_pid(tty_pgrp); mutex_lock(&tty_mutex); /* It is possible that do_tty_hangup has free'd this tty */ tty = get_current_tty(); if (tty) { - tty->session = 0; - tty->pgrp = 0; + put_pid(tty->session); + put_pid(tty->pgrp); + tty->session = NULL; + tty->pgrp = NULL; } else { #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "error attempted to write to tty [0x%p]" @@ -1544,7 +1555,7 @@ void disassociate_ctty(int on_exit) /* Now clear signal->tty under the lock */ read_lock(&tasklist_lock); - session_clear_tty(session); + session_clear_tty(task_session(current)); read_unlock(&tasklist_lock); unlock_kernel(); } @@ -1612,7 +1623,6 @@ void start_tty(struct tty_struct *tty) /* If we have a running line discipline it may need kicking */ tty_wakeup(tty); - wake_up_interruptible(&tty->write_wait); } EXPORT_SYMBOL(start_tty); @@ -2003,7 +2013,7 @@ static int init_dev(struct tty_driver *driver, int idx, /* * All structures have been allocated, so now we install them. - * Failures after this point use release_mem to clean up, so + * Failures after this point use release_tty to clean up, so * there's no need to null out the local pointers. */ if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) { @@ -2024,8 +2034,8 @@ static int init_dev(struct tty_driver *driver, int idx, /* * Structures all installed ... call the ldisc open routines. - * If we fail here just call release_mem to clean up. No need - * to decrement the use counts, as release_mem doesn't care. + * If we fail here just call release_tty to clean up. No need + * to decrement the use counts, as release_tty doesn't care. */ if (tty->ldisc.open) { @@ -2095,17 +2105,17 @@ fail_no_mem: retval = -ENOMEM; goto end_init; - /* call the tty release_mem routine to clean out this slot */ + /* call the tty release_tty routine to clean out this slot */ release_mem_out: if (printk_ratelimit()) printk(KERN_INFO "init_dev: ldisc open failed, " "clearing slot %d\n", idx); - release_mem(tty, idx); + release_tty(tty, idx); goto end_init; } /** - * release_mem - release tty structure memory + * release_one_tty - release tty structure memory * * Releases memory associated with a tty structure, and clears out the * driver table slots. This function is called when a device is no longer @@ -2117,37 +2127,14 @@ release_mem_out: * of ttys that the driver keeps. * FIXME: should we require tty_mutex is held here ?? */ - -static void release_mem(struct tty_struct *tty, int idx) +static void release_one_tty(struct tty_struct *tty, int idx) { - struct tty_struct *o_tty; - struct ktermios *tp; int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM; - - if ((o_tty = tty->link) != NULL) { - if (!devpts) - o_tty->driver->ttys[idx] = NULL; - if (o_tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - tp = o_tty->termios; - if (!devpts) - o_tty->driver->termios[idx] = NULL; - kfree(tp); - - tp = o_tty->termios_locked; - if (!devpts) - o_tty->driver->termios_locked[idx] = NULL; - kfree(tp); - } - o_tty->magic = 0; - o_tty->driver->refcount--; - file_list_lock(); - list_del_init(&o_tty->tty_files); - file_list_unlock(); - free_tty_struct(o_tty); - } + struct ktermios *tp; if (!devpts) tty->driver->ttys[idx] = NULL; + if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { tp = tty->termios; if (!devpts) @@ -2160,15 +2147,39 @@ static void release_mem(struct tty_struct *tty, int idx) kfree(tp); } + tty->magic = 0; tty->driver->refcount--; + file_list_lock(); list_del_init(&tty->tty_files); file_list_unlock(); - module_put(tty->driver->owner); + free_tty_struct(tty); } +/** + * release_tty - release tty structure memory + * + * Release both @tty and a possible linked partner (think pty pair), + * and decrement the refcount of the backing module. + * + * Locking: + * tty_mutex - sometimes only + * takes the file list lock internally when working on the list + * of ttys that the driver keeps. + * FIXME: should we require tty_mutex is held here ?? + */ +static void release_tty(struct tty_struct *tty, int idx) +{ + struct tty_driver *driver = tty->driver; + + if (tty->link) + release_one_tty(tty->link, idx); + release_one_tty(tty, idx); + module_put(driver->owner); +} + /* * Even releasing the tty structures is a tricky business.. We have * to be very careful that the structures are all released at the @@ -2436,10 +2447,10 @@ static void release_dev(struct file * filp) tty_set_termios_ldisc(o_tty,N_TTY); } /* - * The release_mem function takes care of the details of clearing + * The release_tty function takes care of the details of clearing * the slots and preserving the termios structure. */ - release_mem(tty, idx); + release_tty(tty, idx); #ifdef CONFIG_UNIX98_PTYS /* Make this pty number available for reallocation */ @@ -2481,6 +2492,7 @@ static int tty_open(struct inode * inode, struct file * filp) int index; dev_t device = inode->i_rdev; unsigned short saved_flags = filp->f_flags; + struct pid *old_pgrp; nonseekable_open(inode, filp); @@ -2574,15 +2586,17 @@ got_driver: goto retry_open; } + old_pgrp = NULL; mutex_lock(&tty_mutex); spin_lock_irq(¤t->sighand->siglock); if (!noctty && current->signal->leader && !current->signal->tty && - tty->session == 0) - __proc_set_tty(current, tty); + tty->session == NULL) + old_pgrp = __proc_set_tty(current, tty); spin_unlock_irq(¤t->sighand->siglock); mutex_unlock(&tty_mutex); + put_pid(old_pgrp); return 0; } @@ -2721,9 +2735,18 @@ static int tty_fasync(int fd, struct file * filp, int on) return retval; if (on) { + enum pid_type type; + struct pid *pid; if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = 1; - retval = f_setown(filp, (-tty->pgrp) ? : current->pid, 0); + if (tty->pgrp) { + pid = tty->pgrp; + type = PIDTYPE_PGID; + } else { + pid = task_pid(current); + type = PIDTYPE_PID; + } + retval = __f_setown(filp, pid, type, 0); if (retval) return retval; } else { @@ -2825,10 +2848,10 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, } } #endif - if (tty->pgrp > 0) - kill_pg(tty->pgrp, SIGWINCH, 1); - if ((real_tty->pgrp != tty->pgrp) && (real_tty->pgrp > 0)) - kill_pg(real_tty->pgrp, SIGWINCH, 1); + if (tty->pgrp) + kill_pgrp(tty->pgrp, SIGWINCH, 1); + if ((real_tty->pgrp != tty->pgrp) && real_tty->pgrp) + kill_pgrp(real_tty->pgrp, SIGWINCH, 1); tty->winsize = tmp_ws; real_tty->winsize = tmp_ws; done: @@ -2913,8 +2936,7 @@ static int fionbio(struct file *file, int __user *p) static int tiocsctty(struct tty_struct *tty, int arg) { int ret = 0; - if (current->signal->leader && - (process_session(current) == tty->session)) + if (current->signal->leader && (task_session(current) == tty->session)) return ret; mutex_lock(&tty_mutex); @@ -2927,7 +2949,7 @@ static int tiocsctty(struct tty_struct *tty, int arg) goto unlock; } - if (tty->session > 0) { + if (tty->session) { /* * This tty is already the controlling * tty for another session group! @@ -2970,7 +2992,7 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t */ if (tty == real_tty && current->signal->tty != real_tty) return -ENOTTY; - return put_user(real_tty->pgrp, p); + return put_user(pid_nr(real_tty->pgrp), p); } /** @@ -2987,7 +3009,8 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) { - pid_t pgrp; + struct pid *pgrp; + pid_t pgrp_nr; int retval = tty_check_change(real_tty); if (retval == -EIO) @@ -2996,16 +3019,26 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t return retval; if (!current->signal->tty || (current->signal->tty != real_tty) || - (real_tty->session != process_session(current))) + (real_tty->session != task_session(current))) return -ENOTTY; - if (get_user(pgrp, p)) + if (get_user(pgrp_nr, p)) return -EFAULT; - if (pgrp < 0) + if (pgrp_nr < 0) return -EINVAL; - if (session_of_pgrp(pgrp) != process_session(current)) - return -EPERM; - real_tty->pgrp = pgrp; - return 0; + rcu_read_lock(); + pgrp = find_pid(pgrp_nr); + retval = -ESRCH; + if (!pgrp) + goto out_unlock; + retval = -EPERM; + if (session_of_pgrp(pgrp) != task_session(current)) + goto out_unlock; + retval = 0; + put_pid(real_tty->pgrp); + real_tty->pgrp = get_pid(pgrp); +out_unlock: + rcu_read_unlock(); + return retval; } /** @@ -3028,9 +3061,9 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _ */ if (tty == real_tty && current->signal->tty != real_tty) return -ENOTTY; - if (real_tty->session <= 0) + if (!real_tty->session) return -ENOTTY; - return put_user(real_tty->session, p); + return put_user(pid_nr(real_tty->session), p); } /** @@ -3324,15 +3357,13 @@ int tty_ioctl(struct inode * inode, struct file * file, * Nasty bug: do_SAK is being called in interrupt context. This can * deadlock. We punt it up to process context. AKPM - 16Mar2001 */ -static void __do_SAK(struct work_struct *work) +void __do_SAK(struct tty_struct *tty) { - struct tty_struct *tty = - container_of(work, struct tty_struct, SAK_work); #ifdef TTY_SOFT_SAK tty_hangup(tty); #else struct task_struct *g, *p; - int session; + struct pid *session; int i; struct file *filp; struct fdtable *fdt; @@ -3348,12 +3379,12 @@ static void __do_SAK(struct work_struct *work) read_lock(&tasklist_lock); /* Kill the entire session */ - do_each_task_pid(session, PIDTYPE_SID, p) { + do_each_pid_task(session, PIDTYPE_SID, p) { printk(KERN_NOTICE "SAK: killed process %d" " (%s): process_session(p)==tty->session\n", p->pid, p->comm); send_sig(SIGKILL, p, 1); - } while_each_task_pid(session, PIDTYPE_SID, p); + } while_each_pid_task(session, PIDTYPE_SID, p); /* Now kill any processes that happen to have the * tty open. */ @@ -3394,6 +3425,13 @@ static void __do_SAK(struct work_struct *work) #endif } +static void do_SAK_work(struct work_struct *work) +{ + struct tty_struct *tty = + container_of(work, struct tty_struct, SAK_work); + __do_SAK(tty); +} + /* * The tq handling here is a little racy - tty->SAK_work may already be queued. * Fortunately we don't need to worry, because if ->SAK_work is already queued, @@ -3404,7 +3442,6 @@ void do_SAK(struct tty_struct *tty) { if (!tty) return; - PREPARE_WORK(&tty->SAK_work, __do_SAK); schedule_work(&tty->SAK_work); } @@ -3515,7 +3552,8 @@ static void initialize_tty_struct(struct tty_struct *tty) memset(tty, 0, sizeof(struct tty_struct)); tty->magic = TTY_MAGIC; tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); - tty->pgrp = -1; + tty->session = NULL; + tty->pgrp = NULL; tty->overrun_time = jiffies; tty->buf.head = tty->buf.tail = NULL; tty_buffer_init(tty); @@ -3529,7 +3567,7 @@ static void initialize_tty_struct(struct tty_struct *tty) mutex_init(&tty->atomic_write_lock); spin_lock_init(&tty->read_lock); INIT_LIST_HEAD(&tty->tty_files); - INIT_WORK(&tty->SAK_work, NULL); + INIT_WORK(&tty->SAK_work, do_SAK_work); } /* @@ -3786,21 +3824,28 @@ void proc_clear_tty(struct task_struct *p) } EXPORT_SYMBOL(proc_clear_tty); -void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) +static struct pid *__proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) { + struct pid *old_pgrp; if (tty) { - tty->session = process_session(tsk); - tty->pgrp = process_group(tsk); + tty->session = get_pid(task_session(tsk)); + tty->pgrp = get_pid(task_pgrp(tsk)); } + old_pgrp = tsk->signal->tty_old_pgrp; tsk->signal->tty = tty; - tsk->signal->tty_old_pgrp = 0; + tsk->signal->tty_old_pgrp = NULL; + return old_pgrp; } void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) { + struct pid *old_pgrp; + spin_lock_irq(&tsk->sighand->siglock); - __proc_set_tty(tsk, tty); + old_pgrp = __proc_set_tty(tsk, tty); spin_unlock_irq(&tsk->sighand->siglock); + + put_pid(old_pgrp); } struct tty_struct *get_current_tty(void) |