📄 tty_io.c
字号:
/* * linux/drivers/char/tty_io.c * * Copyright (C) 1991, 1992 Linus Torvalds *//* * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles * or rs-channels. It also implements echoing, cooked mode etc. * * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0. * * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the * tty_struct and tty_queue structures. Previously there was an array * of 256 tty_struct's which was statically allocated, and the * tty_queue structures were allocated at boot time. Both are now * dynamically allocated only when the tty is open. * * Also restructured routines so that there is more of a separation * between the high-level tty routines (tty_io.c and tty_ioctl.c) and * the low-level tty routines (serial.c, pty.c, console.c). This * makes for cleaner and more compact code. -TYT, 9/17/92 * * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines * which can be dynamically activated and de-activated by the line * discipline handling modules (like SLIP). * * NOTE: pay no attention to the line discipline code (yet); its * interface is still subject to change in this version... * -- TYT, 1/31/92 * * Added functionality to the OPOST tty handling. No delays, but all * other bits should be there. * -- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993. * * Rewrote canonical mode and added more termios flags. * -- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94 * * Reorganized FASYNC support so mouse code can share it. * -- ctm@ardi.com, 9Sep95 * * New TIOCLINUX variants added. * -- mj@k332.feld.cvut.cz, 19-Nov-95 * * Restrict vt switching via ioctl() * -- grif@cs.ucr.edu, 5-Dec-95 * * Rewrote init_dev and release_dev to eliminate races. * -- Bill Hawes <whawes@star.net>, June 97 */#include <linux/config.h>#include <linux/types.h>#include <linux/major.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/fcntl.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/timer.h>#include <linux/ctype.h>#include <linux/kd.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/malloc.h>#include <asm/segment.h>#include <asm/system.h>#include <asm/bitops.h>#include <linux/scc.h>#include "kbd_kern.h"#include "vt_kern.h"#include "selection.h"#ifdef CONFIG_KERNELD#include <linux/kerneld.h>#endif#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)#undef TTY_DEBUG_HANGUP#define TTY_PARANOIA_CHECK#define CHECK_TTY_COUNTextern void do_blank_screen(int nopowersave);extern void set_vesa_blanking(const unsigned long arg);struct termios tty_std_termios; /* for the benefit of tty drivers */struct tty_driver *tty_drivers = NULL; /* linked list of tty drivers */struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table *//* * fg_console is the current virtual console, * last_console is the last used one, * want_console is the console we want to switch to, * kmsg_redirect is the console for kernel messages, * redirect is the pseudo-tty that console output * is redirected to if asked by TIOCCONS. */int fg_console = 0;int last_console = 0;int want_console = -1;int kmsg_redirect = 0;struct tty_struct * redirect = NULL;struct wait_queue * keypress_wait = NULL;char vt_dont_switch = 0;static void initialize_tty_struct(struct tty_struct *tty);static int tty_read(struct inode *, struct file *, char *, int);static int tty_write(struct inode *, struct file *, const char *, int);static int tty_select(struct inode *, struct file *, int, select_table *);static int tty_open(struct inode *, struct file *);static void tty_release(struct inode *, struct file *);static int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg);static int tty_fasync(struct inode * inode, struct file * filp, int on);extern void reset_palette(int currcons) ;extern void set_palette(void) ;#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endif/* * These two routines return the name of tty. tty_name() should NOT * be used in interrupt drivers, since it's not re-entrant. Use * _tty_name() instead. */char *_tty_name(struct tty_struct *tty, char *buf){ if (tty) sprintf(buf, "%s%d", tty->driver.name, MINOR(tty->device) - tty->driver.minor_start + tty->driver.name_base); else strcpy(buf, "NULL tty"); return buf;}char *tty_name(struct tty_struct *tty){ static char buf[64]; return(_tty_name(tty, buf));}inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device, const char *routine){#ifdef TTY_PARANOIA_CHECK static const char *badmagic = "Warning: bad magic number for tty struct (%s) in %s\n"; static const char *badtty = "Warning: null TTY for (%s) in %s\n"; if (!tty) { printk(badtty, kdevname(device), routine); return 1; } if (tty->magic != TTY_MAGIC) { printk(badmagic, kdevname(device), routine); return 1; }#endif return 0;}static int check_tty_count(struct tty_struct *tty, const char *routine){#ifdef CHECK_TTY_COUNT struct file *f; int i, count = 0; for (f = first_file, i=0; i<nr_files; i++, f = f->f_next) { if (!f->f_count) continue; if (f->private_data == tty) { count++; } } if (tty->driver.type == TTY_DRIVER_TYPE_PTY && tty->driver.subtype == PTY_TYPE_SLAVE && tty->link && tty->link->count) count++; if (tty->count != count) { printk("Warning: dev (%s) tty->count(%d) != #fd's(%d) in %s\n", kdevname(tty->device), tty->count, count, routine); return count; } #endif return 0;}int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc){ if (disc < N_TTY || disc >= NR_LDISCS) return -EINVAL; if (new_ldisc) { ldiscs[disc] = *new_ldisc; ldiscs[disc].flags |= LDISC_FLAG_DEFINED; ldiscs[disc].num = disc; } else memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc)); return 0;}/* Set the discipline of a tty line. */static int tty_set_ldisc(struct tty_struct *tty, int ldisc){ int retval = 0; struct tty_ldisc o_ldisc; if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS)) return -EINVAL;#ifdef CONFIG_KERNELD /* Eduardo Blanco <ejbs@cs.cs.com.uy> */ if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) { char modname [20]; sprintf(modname, "tty-ldisc-%d", ldisc); request_module (modname); }#endif if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) return -EINVAL; if (tty->ldisc.num == ldisc) return 0; /* We are already in the desired discipline */ o_ldisc = tty->ldisc; tty_wait_until_sent(tty, 0); /* Shutdown the current discipline. */ if (tty->ldisc.close) (tty->ldisc.close)(tty); /* Now set up the new line discipline. */ tty->ldisc = ldiscs[ldisc]; tty->termios->c_line = ldisc; if (tty->ldisc.open) retval = (tty->ldisc.open)(tty); if (retval < 0) { tty->ldisc = o_ldisc; tty->termios->c_line = tty->ldisc.num; if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) { tty->ldisc = ldiscs[N_TTY]; tty->termios->c_line = N_TTY; if (tty->ldisc.open) { int r = tty->ldisc.open(tty); if (r < 0) panic("Couldn't open N_TTY ldisc for " "%s --- error %d.", tty_name(tty), r); } } } if (tty->ldisc.num != o_ldisc.num && tty->driver.set_ldisc) tty->driver.set_ldisc(tty); return retval;}/* * This routine returns a tty driver structure, given a device number */struct tty_driver *get_tty_driver(kdev_t device){ int major, minor; struct tty_driver *p; minor = MINOR(device); major = MAJOR(device); for (p = tty_drivers; p; p = p->next) { if (p->major != major) continue; if (minor < p->minor_start) continue; if (minor >= p->minor_start + p->num) continue; return p; } return NULL;}/* * If we try to write to, or set the state of, a terminal and we're * not in the foreground, send a SIGTTOU. If the signal is blocked or * ignored, go ahead and perform the operation. (POSIX 7.2) */int tty_check_change(struct tty_struct * tty){ if (current->tty != tty) return 0; if (tty->pgrp <= 0) { printk("tty_check_change: tty->pgrp <= 0!\n"); return 0; } if (current->pgrp == tty->pgrp) return 0; if (is_ignored(SIGTTOU)) return 0; if (is_orphaned_pgrp(current->pgrp)) return -EIO; (void) kill_pg(current->pgrp,SIGTTOU,1); return -ERESTARTSYS;}static int hung_up_tty_read(struct inode * inode, struct file * file, char * buf, int count){ return 0;}static int hung_up_tty_write(struct inode * inode, struct file * file, const char * buf, int count){ return -EIO;}static int hung_up_tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait){ return 1;}static int hung_up_tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ return cmd == TIOCSPGRP ? -ENOTTY : -EIO;}static int tty_lseek(struct inode * inode, struct file * file, off_t offset, int orig){ return -ESPIPE;}static struct file_operations tty_fops = { tty_lseek, tty_read, tty_write, NULL, /* tty_readdir */ tty_select, tty_ioctl, NULL, /* tty_mmap */ tty_open, tty_release, NULL, /* tty_fsync */ tty_fasync};static struct file_operations hung_up_tty_fops = { tty_lseek, hung_up_tty_read, hung_up_tty_write, NULL, /* hung_up_tty_readdir */ hung_up_tty_select, hung_up_tty_ioctl, NULL, /* hung_up_tty_mmap */ NULL, /* hung_up_tty_open */ tty_release, /* hung_up_tty_release */ NULL, /* hung_up_tty_fsync */ NULL /* hung_up_tty_fasync */};void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops){ int i; struct file * filp; struct task_struct *p; if (!tty) return; check_tty_count(tty, "do_tty_hangup"); for (filp = first_file, i=0; i<nr_files; i++, filp = filp->f_next) { if (!filp->f_count) continue; if (filp->private_data != tty) continue; if (!filp->f_inode) continue; if (filp->f_inode->i_rdev == CONSOLE_DEV) continue; if (filp->f_op != &tty_fops) continue; tty_fasync(filp->f_inode, filp, 0); filp->f_op = fops; } if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); wake_up_interruptible(&tty->read_wait); /* * Shutdown the current line discipline, and reset it to * N_TTY. */ if (tty->ldisc.num != ldiscs[N_TTY].num) { if (tty->ldisc.close) (tty->ldisc.close)(tty); tty->ldisc = ldiscs[N_TTY]; tty->termios->c_line = N_TTY; if (tty->ldisc.open) { i = (tty->ldisc.open)(tty); if (i < 0) printk("do_tty_hangup: N_TTY open: error %d\n", -i); } } for_each_task(p) { if ((tty->session > 0) && (p->session == tty->session) && p->leader) { send_sig(SIGHUP,p,1); send_sig(SIGCONT,p,1); if (tty->pgrp > 0) p->tty_old_pgrp = tty->pgrp; } if (p->tty == tty) p->tty = NULL; } tty->flags = 0; tty->session = 0; tty->pgrp = -1; tty->ctrl_status = 0; if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) *tty->termios = tty->driver.init_termios; if (tty->driver.hangup) (tty->driver.hangup)(tty);}void tty_hangup(struct tty_struct * tty){#ifdef TTY_DEBUG_HANGUP printk("%s hangup...\n", tty_name(tty));#endif do_tty_hangup(tty, &hung_up_tty_fops);}void tty_vhangup(struct tty_struct * tty){#ifdef TTY_DEBUG_HANGUP printk("%s vhangup...\n", tty_name(tty));#endif do_tty_hangup(tty, &hung_up_tty_fops);}int tty_hung_up_p(struct file * filp){ return (filp->f_op == &hung_up_tty_fops);}/* * This function is typically called only by the session leader, when * it wants to disassociate itself from its controlling tty. * * It performs the following functions: * (1) Sends a SIGHUP and SIGCONT to the foreground process group * (2) Clears the tty from being controlling the session * (3) Clears the controlling tty for all processes in the * session group. * * The argument on_exit is set to 1 if called when a process is * exiting; it is 0 if called by the ioctl TIOCNOTTY. */void disassociate_ctty(int on_exit){ struct tty_struct *tty = current->tty; struct task_struct *p; int tty_pgrp = -1; if (tty) { tty_pgrp = tty->pgrp; if (on_exit && tty->driver.type != TTY_DRIVER_TYPE_PTY) tty_vhangup(tty); } else { if (current->tty_old_pgrp) { kill_pg(current->tty_old_pgrp, SIGHUP, on_exit); kill_pg(current->tty_old_pgrp, SIGCONT, on_exit); } return; } if (tty_pgrp > 0) { kill_pg(tty_pgrp, SIGHUP, on_exit); if (!on_exit) kill_pg(tty_pgrp, SIGCONT, on_exit); } current->tty_old_pgrp = 0; tty->session = 0; tty->pgrp = -1; for_each_task(p) if (p->session == current->session) p->tty = NULL;}/* * Sometimes we want to wait until a particular VT has been activated. We * do it in a very simple manner. Everybody waits on a single queue and * get woken up at once. Those that are satisfied go on with their business, * while those not ready go back to sleep. Seems overkill to add a wait * to each vt just for this - usually this does nothing! */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -