📄 tty_io.c
字号:
o_tty_closing = o_tty && (o_tty->count <= (pty_master ? 1 : 0)); do_sleep = 0; if (tty_closing) { if (waitqueue_active(&tty->read_wait)) { wake_up(&tty->read_wait); do_sleep++; } if (waitqueue_active(&tty->write_wait)) { wake_up(&tty->write_wait); do_sleep++; } } if (o_tty_closing) { if (waitqueue_active(&o_tty->read_wait)) { wake_up(&o_tty->read_wait); do_sleep++; } if (waitqueue_active(&o_tty->write_wait)) { wake_up(&o_tty->write_wait); do_sleep++; } } if (!do_sleep) break; printk("release_dev: %s: read/write wait queue active!\n", tty_name(tty, buf)); schedule(); } /* * 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("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("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 should zero out * filp->private_data, to break the link between the tty and * the file descriptor. Otherwise if filp_close() blocks before * the the file descriptor is removed from the inuse_filp * list, check_tty_count() could observe a discrepancy and * printk a warning message to the user. */ filp->private_data = 0; /* * 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. Also, clear redirect if it points to either tty. */ if (tty_closing || o_tty_closing) { struct task_struct *p; 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("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); run_task_queue(&tq_scheduler); /* * 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)); 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; 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("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("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) { current->tty = tty; 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 = 0; 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){ release_dev(filp); return 0;}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;}/* * fasync_helper() is used by some character device drivers (mainly mice) * to set up the fasync queue. It returns negative on error, 0 if it did * no changes and positive if it added/deleted the entry. */int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp){ struct fasync_struct *fa, **fp; unsigned long flags; for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { if (fa->fa_file == filp) break; } if (on) { if (fa) { fa->fa_fd = fd; return 0; } fa = (struct fasync_struct *)kmalloc(sizeof(struct fasync_struct), GFP_KERNEL); if (!fa) return -ENOMEM; fa->magic = FASYNC_MAGIC; fa->fa_file = filp; fa->fa_fd = fd; save_flags(flags); cli(); fa->fa_next = *fapp; *fapp = fa; restore_flags(flags); return 1; } if (!fa) return 0; save_flags(flags); cli(); *fp = fa->fa_next; restore_flags(flags); kfree(fa); return 1;}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 tty_struct *tty, struct tty_struct *real_tty){ if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE || tty->driver.type == TTY_DRIVER_TYPE_SYSCONS) { 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; } current->tty = tty; 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; get_user(pgrp, (pid_t *) arg); 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 retval, ldisc; retval = get_user(ldisc, arg); if (retval) return retval; return tty_set_ldisc(tty, ldisc);}static int send_break(struct tty_struct *tty, int duration){ 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;}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -