📄 tty_io.c
字号:
read_lock(&tasklist_lock); for_each_task(p) { if (p->tty == tty || (o_tty && p->tty == o_tty)) p->tty = NULL; } read_unlock(&tasklist_lock); if (redirect == tty || (o_tty && redirect == o_tty)) redirect = NULL; } /* 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 /* * Shutdown the current line discipline, and reset it to N_TTY. * N.B. why reset ldisc when we're releasing the memory?? */ if (tty->ldisc.close) (tty->ldisc.close)(tty); tty->ldisc = ldiscs[N_TTY]; tty->termios->c_line = N_TTY; if (o_tty) { if (o_tty->ldisc.close) (o_tty->ldisc.close)(o_tty); o_tty->ldisc = ldiscs[N_TTY]; } /* * Make sure that the tty's task queue isn't activated. */ run_task_queue(&tq_timer); flush_scheduled_tasks(); /* * The release_mem function takes care of the details of clearing * the slots and preserving the termios structure. */ release_mem(tty, idx);}/* * 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; kdev_t device; unsigned short saved_flags; char buf[64]; saved_flags = filp->f_flags;retry_open: noctty = filp->f_flags & O_NOCTTY; device = inode->i_rdev; if (device == TTY_DEV) { if (!current->tty) return -ENXIO; device = current->tty->device; filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ /* noctty = 1; */ }#ifdef CONFIG_VT if (device == CONSOLE_DEV) { extern int fg_console; device = MKDEV(TTY_MAJOR, fg_console + 1); noctty = 1; }#endif if (device == SYSCONS_DEV) { struct console *c = console_drivers; while(c && !c->device) c = c->next; if (!c) return -ENODEV; device = c->device(c); filp->f_flags |= O_NONBLOCK; /* Don't let /dev/console block */ noctty = 1; } if (device == PTMX_DEV) {#ifdef CONFIG_UNIX98_PTYS /* find a free pty. */ int major, minor; struct tty_driver *driver; /* find a device that is not in use. */ retval = -1; for ( major = 0 ; major < UNIX98_NR_MAJORS ; major++ ) { driver = &ptm_driver[major]; for (minor = driver->minor_start ; minor < driver->minor_start + driver->num ; minor++) { device = MKDEV(driver->major, minor); if (!init_dev(device, &tty)) goto ptmx_found; /* ok! */ } } return -EIO; /* no free ptys */ ptmx_found: set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ minor -= driver->minor_start; devpts_pty_new(driver->other->name_base + minor, MKDEV(driver->other->major, minor + driver->other->minor_start)); tty_register_devfs(&pts_driver[major], DEVFS_FL_DEFAULT, pts_driver[major].minor_start + minor); noctty = 1; goto init_dev_done;#else /* CONFIG_UNIX_98_PTYS */ return -ENODEV;#endif /* CONFIG_UNIX_98_PTYS */ } retval = init_dev(device, &tty); if (retval) return retval;#ifdef CONFIG_UNIX98_PTYSinit_dev_done:#endif 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(tty, buf));#endif 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) && !suser()) retval = -EBUSY; if (retval) {#ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "error %d in opening %s...", retval, tty_name(tty, buf));#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. */ filp->f_op = &tty_fops; goto retry_open; } if (!noctty && current->leader && !current->tty && tty->session == 0) { task_lock(current); current->tty = tty; task_unlock(current); current->tty_old_pgrp = 0; tty->session = current->session; tty->pgrp = current->pgrp; } if ((tty->driver.type == TTY_DRIVER_TYPE_SERIAL) && (tty->driver.subtype == SERIAL_TYPE_CALLOUT) && (tty->count == 1)) { static int nr_warns; if (nr_warns < 5) { printk(KERN_WARNING "tty_io.c: " "process %d (%s) used obsolete /dev/%s - " "update software to use /dev/ttyS%d\n", current->pid, current->comm, tty_name(tty, buf), TTY_NUMBER(tty)); nr_warns++; } } return 0;}static 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; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "tty_poll")) return 0; if (tty->ldisc.poll) return (tty->ldisc.poll)(tty, filp, wait); return 0;}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->i_rdev, "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; if (filp->f_owner.pid == 0) { filp->f_owner.pid = (-tty->pgrp) ? : current->pid; filp->f_owner.uid = current->uid; filp->f_owner.euid = current->euid; } } 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 * arg){ char ch, mbz = 0; if ((current->tty != tty) && !suser()) return -EPERM; if (get_user(ch, arg)) return -EFAULT; tty->ldisc.receive_buf(tty, &ch, &mbz, 1); return 0;}static int tiocgwinsz(struct tty_struct *tty, struct winsize * 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 * 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; 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 inode *inode, struct tty_struct *tty, struct tty_struct *real_tty){ if (inode->i_rdev == SYSCONS_DEV || inode->i_rdev == CONSOLE_DEV) { if (!suser()) return -EPERM; redirect = NULL; return 0; } if (redirect) return -EBUSY; redirect = real_tty; return 0;}static int fionbio(struct file *file, int *arg){ int nonblock; if (get_user(nonblock, arg)) 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){ if (current->leader && (current->session == tty->session)) return 0; /* * The process must be a session leader and * not have a controlling tty already. */ if (!current->leader || current->tty) return -EPERM; if (tty->session > 0) { /* * This tty is already the controlling * tty for another session group! */ if ((arg == 1) && suser()) { /* * Steal it away */ struct task_struct *p; read_lock(&tasklist_lock); for_each_task(p) if (p->tty == tty) p->tty = NULL; read_unlock(&tasklist_lock); } else return -EPERM; } task_lock(current); current->tty = tty; task_unlock(current); current->tty_old_pgrp = 0; tty->session = current->session; tty->pgrp = current->pgrp; return 0;}static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg){ /* * (tty == real_tty) is a cheap way of * testing if the tty is NOT a master pty. */ if (tty == real_tty && current->tty != real_tty) return -ENOTTY; return put_user(real_tty->pgrp, arg);}static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg){ pid_t pgrp; int retval = tty_check_change(real_tty); if (retval == -EIO) return -ENOTTY; if (retval) return retval; if (!current->tty || (current->tty != real_tty) || (real_tty->session != current->session)) return -ENOTTY; if (get_user(pgrp, (pid_t *) arg)) return -EFAULT; if (pgrp < 0) return -EINVAL; if (session_of_pgrp(pgrp) != current->session) return -EPERM; real_tty->pgrp = pgrp; return 0;}static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg){ /* * (tty == real_tty) is a cheap way of * testing if the tty is NOT a master pty. */ if (tty == real_tty && current->tty != real_tty) return -ENOTTY; if (real_tty->session <= 0) return -ENOTTY; return put_user(real_tty->session, arg);}static int tiocttygstruct(struct tty_struct *tty, struct tty_struct *arg){ if (copy_to_user(arg, tty, sizeof(*arg))) return -EFAULT; return 0;}static int tiocsetd(struct tty_struct *tty, int *arg){ int ldisc; if (get_user(ldisc, arg)) return -EFAULT; return tty_set_ldisc(tty, ldisc);}static int send_break(struct tty_struct *tty, int duration){ set_current_state(TASK_INTERRUPTIBLE); tty->driver.break_ctl(tty, -1); if (!signal_pending(current)) schedule_timeout(duration); tty->driver.break_ctl(tty, 0); if (signal_pending(current)) return -EINTR; return 0;}/* * Split this up, as gcc can choke on it otherwise.. */int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ struct tty_struct *tty, *real_tty; int retval; tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl")) return -EINVAL; real_tty = tty; if (tty->driver.type == TTY_DRIVER_TYPE_PTY && tty->driver.subtype == PTY_TYPE_MASTER) real_tty = tty->link; /* * Break handling by driver */ if (!tty->driver.break_ctl) { switch(cmd) { case TIOCSBRK: case TIOCCBRK: if (tty->driver.ioctl) return tty->driver.ioctl(tty, file, cmd, arg); return -EINVAL; /* These two ioctl's always return success; even if */ /* the driver doesn't support them. */ case TCSBRK: case TCSBRKP: if (!tty->driver.ioctl) return 0; retval = tty->driver.ioctl(tty, file, cmd, arg); if (retval == -ENOIOCTLCMD) retval = 0; return retval; } } /* * Factor out some common prep work */ switch (cmd) { case TIOCSETD: case TIOCSBRK: case TIOCCBRK: case TCSBRK: case TCSBRKP: retval = tty_check_change(tty); if (retval) return retval; if (cmd != TIOCCBRK) { tty_wait_until_sent(tty, 0); if (signal_pending(current)) return -EINTR; } break; } switch (cmd) { case TIOCSTI: return tiocsti(tty, (char *)arg); case TIOCGWINSZ: return tiocgwinsz(tty, (struct winsize *) arg); case TIOCSWINSZ: return tiocswinsz(tty, real_tty, (struct winsize *) arg); case TIOCCONS: return tioccons(inode, tty, real_tty); case FIONBIO: return fionbio(file, (int *) arg); case TIOCEXCL: set_bit(TTY_EXCLUSIVE, &tty->flags); return 0; case TIOCNXCL: clear_bit(TTY_EXCLUSIVE, &tty->flags); return 0; case TIOCNOTTY: if (current->tty != tty) return -ENOTTY; if (current->leader) disassociate_ctty(0); task_lock(current); current->tty = NULL; task_unlock(current); return 0; case TIOCSCTTY: return tiocsctty(tty, arg); case TIOCGPGRP: return tiocgpgrp(tty, real_tty, (pid_t *) arg); case TIOCSPGRP: return tiocspgrp(tty, real_tty, (pid_t *) arg); case TIOCGSID: return tiocgsid(tty, real_tty, (pid_t *) arg); case TIOCGETD: return put_user(tty->ldisc.num, (int *) arg); case TIOCSETD: return tiocsetd(tty, (int *) arg);#ifdef CONFIG_VT case TIOCLINUX: return tioclinux(tty, arg);#endif case TIOCTTYGSTRUCT: return tiocttygstruct(tty, (struct tty_struct *) arg); /* * Break handling */ case TIOCSBRK: /* Turn break on, unconditionally */ tty->driver.break_ctl(tty, -1); return 0; case TIOCCBRK: /* Turn break off, unconditionally */ tty->driver.break_ctl(tty, 0); return 0; case TCSBRK: /* SVID version: non-zero arg --> no break */ /* * XXX is the above comment correct, or the * code below correct? Is this ioctl used at * all by anyone? */ if (!arg) return send_break(tty, HZ/4); return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */ return send_break(tty, arg ? arg*(HZ/10) : HZ/4); } if (tty->driver.ioctl) { int retval = (tty->driver.ioctl)(tty, file, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } if (tty->ldisc.ioctl) { int retval = (tty->ldisc.ioctl)(tty, file, cmd, arg); if (retval != -ENOIOCTLCMD) return retval; } return -EINVAL;}/* * This implements the "Secure Attention Key" --- the idea is to * prevent trojan horses by killing all processes associated with this * tty when the user hits the "Secure Attention Key". Required for * super-paranoid applications --- see the Orange Book for more details.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -