tty_io.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,610 行 · 第 1/5 页
C
2,610 行
* The closing flags are now consistent with the open counts on * both sides, and we've completed the last operation that could * block, so it's safe to proceed with closing. */ if (pty_master) { if (--o_tty->count < 0) { printk(KERN_WARNING "release_dev: bad pty slave count " "(%d) for %s\n", o_tty->count, tty_name(o_tty, buf)); o_tty->count = 0; } } if (--tty->count < 0) { printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n", tty->count, tty_name(tty, buf)); tty->count = 0; } /* * We've decremented tty->count, so we need to remove this file * descriptor off the tty->tty_files list; this serves two * purposes: * - check_tty_count sees the correct number of file descriptors * associated with this tty. * - do_tty_hangup no longer sees this file descriptor as * something that needs to be handled for hangups. */ file_kill(filp); filp->private_data = NULL; /* * Perform some housekeeping before deciding whether to return. * * Set the TTY_CLOSING flag if this was the last open. In the * case of a pty we may have to wait around for the other side * to close, and TTY_CLOSING makes sure we can't be reopened. */ if(tty_closing) set_bit(TTY_CLOSING, &tty->flags); if(o_tty_closing) set_bit(TTY_CLOSING, &o_tty->flags); /* * If _either_ side is closing, make sure there aren't any * processes that still think tty or o_tty is their controlling * tty. */ if (tty_closing || o_tty_closing) { struct task_struct *p; read_lock(&tasklist_lock); do_each_task_pid(tty->session, PIDTYPE_SID, p) { p->signal->tty = NULL; } while_each_task_pid(tty->session, PIDTYPE_SID, p); if (o_tty) do_each_task_pid(o_tty->session, PIDTYPE_SID, p) { p->signal->tty = NULL; } while_each_task_pid(o_tty->session, PIDTYPE_SID, p); read_unlock(&tasklist_lock); } /* check whether both sides are closing ... */ if (!tty_closing || (o_tty && !o_tty_closing)) return; #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "freeing tty structure...");#endif /* * Prevent flush_to_ldisc() from rescheduling the work for later. Then * kill any delayed work. As this is the final close it does not * race with the set_ldisc code path. */ clear_bit(TTY_LDISC, &tty->flags); clear_bit(TTY_DONT_FLIP, &tty->flags); cancel_delayed_work(&tty->flip.work); /* * Wait for ->hangup_work and ->flip.work handlers to terminate */ flush_scheduled_work(); /* * Wait for any short term users (we know they are just driver * side waiters as the file is closing so user count on the file * side is zero. */ spin_lock_irqsave(&tty_ldisc_lock, flags); while(tty->ldisc.refcount) { spin_unlock_irqrestore(&tty_ldisc_lock, flags); wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0); spin_lock_irqsave(&tty_ldisc_lock, flags); } spin_unlock_irqrestore(&tty_ldisc_lock, flags); /* * Shutdown the current line discipline, and reset it to N_TTY. * N.B. why reset ldisc when we're releasing the memory?? * * FIXME: this MUST get fixed for the new reflocking */ if (tty->ldisc.close) (tty->ldisc.close)(tty); tty_ldisc_put(tty->ldisc.num); /* * Switch the line discipline back */ tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); tty_set_termios_ldisc(tty,N_TTY); if (o_tty) { /* FIXME: could o_tty be in setldisc here ? */ clear_bit(TTY_LDISC, &o_tty->flags); if (o_tty->ldisc.close) (o_tty->ldisc.close)(o_tty); tty_ldisc_put(o_tty->ldisc.num); tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY)); tty_set_termios_ldisc(o_tty,N_TTY); } /* * The release_mem function takes care of the details of clearing * the slots and preserving the termios structure. */ release_mem(tty, idx);#ifdef CONFIG_UNIX98_PTYS /* Make this pty number available for reallocation */ if (devpts) { down(&allocated_ptys_lock); idr_remove(&allocated_ptys, idx); up(&allocated_ptys_lock); }#endif}/* * tty_open and tty_release keep up the tty count that contains the * number of opens done on a tty. We cannot use the inode-count, as * different inodes might point to the same tty. * * Open-counting is needed for pty masters, as well as for keeping * track of serial lines: DTR is dropped when the last close happens. * (This is not done solely through tty->count, now. - Ted 1/27/92) * * The termios state of a pty is reset on first open so that * settings don't persist across reuse. */static int tty_open(struct inode * inode, struct file * filp){ struct tty_struct *tty; int noctty, retval; struct tty_driver *driver; int index; dev_t device = inode->i_rdev; unsigned short saved_flags = filp->f_flags; nonseekable_open(inode, filp); retry_open: noctty = filp->f_flags & O_NOCTTY; index = -1; retval = 0; if (device == MKDEV(TTYAUX_MAJOR,0)) { if (!current->signal->tty) return -ENXIO; driver = current->signal->tty->driver; index = current->signal->tty->index; filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ /* noctty = 1; */ goto got_driver; }#ifdef CONFIG_VT if (device == MKDEV(TTY_MAJOR,0)) { extern int fg_console; extern struct tty_driver *console_driver; driver = console_driver; index = fg_console; noctty = 1; goto got_driver; }#endif if (device == MKDEV(TTYAUX_MAJOR,1)) { driver = console_device(&index); if (driver) { /* Don't let /dev/console block */ filp->f_flags |= O_NONBLOCK; noctty = 1; goto got_driver; } return -ENODEV; } driver = get_tty_driver(device, &index); if (!driver) return -ENODEV;got_driver: retval = init_dev(driver, index, &tty); if (retval) return retval; filp->private_data = tty; file_move(filp, &tty->tty_files); check_tty_count(tty, "tty_open"); 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);#endif if (!retval) { if (tty->driver->open) retval = tty->driver->open(tty, filp); else retval = -ENODEV; } filp->f_flags = saved_flags; if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN)) retval = -EBUSY; if (retval) {#ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "error %d in opening %s...", retval, tty->name);#endif release_dev(filp); if (retval != -ERESTARTSYS) return retval; if (signal_pending(current)) return retval; schedule(); /* * Need to reset f_op in case a hangup happened. */ if (filp->f_op == &hung_up_tty_fops) filp->f_op = &tty_fops; goto retry_open; } if (!noctty && current->signal->leader && !current->signal->tty && tty->session == 0) { task_lock(current); current->signal->tty = tty; task_unlock(current); current->signal->tty_old_pgrp = 0; tty->session = current->signal->session; tty->pgrp = process_group(current); } return 0;}#ifdef CONFIG_UNIX98_PTYSstatic int ptmx_open(struct inode * inode, struct file * filp){ struct tty_struct *tty; int retval; int index; int idr_ret; nonseekable_open(inode, filp); /* find a device that is not in use. */ down(&allocated_ptys_lock); if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) { up(&allocated_ptys_lock); return -ENOMEM; } idr_ret = idr_get_new(&allocated_ptys, NULL, &index); if (idr_ret < 0) { up(&allocated_ptys_lock); if (idr_ret == -EAGAIN) return -ENOMEM; return -EIO; } if (index >= pty_limit) { idr_remove(&allocated_ptys, index); up(&allocated_ptys_lock); return -EIO; } up(&allocated_ptys_lock); retval = init_dev(ptm_driver, index, &tty); if (retval) goto out; set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ filp->private_data = tty; file_move(filp, &tty->tty_files); retval = -ENOMEM; if (devpts_pty_new(tty->link)) goto out1; check_tty_count(tty, "tty_open"); retval = ptm_driver->open(tty, filp); if (!retval) return 0;out1: release_dev(filp);out: down(&allocated_ptys_lock); idr_remove(&allocated_ptys, index); up(&allocated_ptys_lock); return retval;}#endifstatic int tty_release(struct inode * inode, struct file * filp){ lock_kernel(); release_dev(filp); unlock_kernel(); return 0;}/* No kernel lock held - fine */static unsigned int tty_poll(struct file * filp, poll_table * wait){ struct tty_struct * tty; struct tty_ldisc *ld; int ret = 0; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_poll")) return 0; ld = tty_ldisc_ref_wait(tty); if (ld->poll) ret = (ld->poll)(tty, filp, wait); tty_ldisc_deref(ld); return ret;}static int tty_fasync(int fd, struct file * filp, int on){ struct tty_struct * tty; int retval; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "tty_fasync")) return 0; retval = fasync_helper(fd, filp, on, &tty->fasync); if (retval <= 0) return retval; if (on) { if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = 1; retval = f_setown(filp, (-tty->pgrp) ? : current->pid, 0); if (retval) return retval; } else { if (!tty->fasync && !waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = N_TTY_BUF_SIZE; } return 0;}static int tiocsti(struct tty_struct *tty, char __user *p){ char ch, mbz = 0; struct tty_ldisc *ld; if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN)) return -EPERM; if (get_user(ch, p)) return -EFAULT; ld = tty_ldisc_ref_wait(tty); ld->receive_buf(tty, &ch, &mbz, 1); tty_ldisc_deref(ld); return 0;}static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg){ if (copy_to_user(arg, &tty->winsize, sizeof(*arg))) return -EFAULT; return 0;}static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, struct winsize __user * arg){ struct winsize tmp_ws; if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) return -EFAULT; if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg))) return 0;#ifdef CONFIG_VT if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) { unsigned int currcons = tty->index; int rc; acquire_console_sem(); rc = vc_resize(currcons, tmp_ws.ws_col, tmp_ws.ws_row); release_console_sem(); if (rc) return -ENXIO; }#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); tty->winsize = tmp_ws; real_tty->winsize = tmp_ws; return 0;}static int tioccons(struct file *file){ if (file->f_op->write == redirected_tty_write) { struct file *f; if (!capable(CAP_SYS_ADMIN)) return -EPERM; spin_lock(&redirect_lock); f = redirect; redirect = NULL; spin_unlock(&redirect_lock); if (f) fput(f); return 0; } spin_lock(&redirect_lock); if (redirect) { spin_unlock(&redirect_lock); return -EBUSY; } get_file(file); redirect = file; spin_unlock(&redirect_lock); return 0;}static int fionbio(struct file *file, int __user *p){ int nonblock; if (get_user(nonblock, p)) return -EFAULT; if (nonblock) file->f_flags |= O_NONBLOCK; else file->f_flags &= ~O_NONBLOCK; return 0;}static int tiocsctty(struct tty_struct *tty, int arg){ task_t *p; if (current->signal->leader && (current->signal->session == tty->session)) return 0; /* * The process must be a session leader and * not have a controlling tty already. */ if (!current->signal->leader || current->signal->tty) return -EPERM; if (tty->session > 0) { /* * This tty is already the controlling * tty for another session group! */ if ((arg == 1) && capable(CAP_SYS_ADMIN)) { /* * Steal it away */ read_lock(&tasklist_lock); do_each_task_pid(tty->session, PIDTYPE_SID, p) { p->signal->tty = NULL; } while_each_task_pid(tty->session, PIDTYPE_SID, p); read_unlock(&tasklist_lock); } else return -EPERM; } task_lock(current); current->signal->tty = tty; task_unlock(current); current->signal->tty_old_pgrp = 0; tty->session = current->signal->session; tty->pgrp = process_group(current); return 0;}static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p){ /* * (tty == real_tty) is a cheap way of * testing if the tty is NOT a master pty. */ if (tty == real_tty && current->signal->tty != real_tty) return -ENOTTY; return put_user(real_tty->pgrp, p);}static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p){ pid_t pgrp; int retval = tty_check_change(real_tty); if (retval == -EIO) return -ENOTTY; if (retval) return retval; if (!current->signal->tty || (current->signal->tty != real_tty) || (real_tty->session != current->signal->session)) return -ENOTTY; if (get_user(pgrp, p)) return -EFAULT; if (pgrp < 0)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?