📄 tty_io.c
字号:
#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_driver.h>#include <linux/tty_flip.h>#include <linux/devpts_fs.h>#include <linux/file.h>#include <linux/console.h>#include <linux/timer.h>#include <linux/ctype.h>#include <linux/kd.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <linux/module.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>#include <asm/system.h>#include <asm/bitops.h>#include <linux/kbd_kern.h>#include <linux/vt_kern.h>#include <linux/selection.h>#include <linux/devfs_fs_kernel.h>#include <linux/kmod.h>#ifdef CONFIG_VTextern void con_init_devfs (void);#endif#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)#define PTMX_DEV MKDEV(TTYAUX_MAJOR,2)#undef TTY_DEBUG_HANGUP#define TTY_PARANOIA_CHECK 1#define CHECK_TTY_COUNT 1struct termios tty_std_termios; /* for the benefit of tty drivers */struct tty_driver *tty_drivers; /* linked list of tty drivers */struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */#ifdef CONFIG_UNIX98_PTYSextern struct tty_driver ptm_driver[]; /* Unix98 pty masters; for /dev/ptmx */extern struct tty_driver pts_driver[]; /* Unix98 pty slaves; for /dev/ptmx */#endif/* * redirect is the pseudo-tty that console output * is redirected to if asked by TIOCCONS. */struct tty_struct * redirect;static void initialize_tty_struct(struct tty_struct *tty);static ssize_t tty_read(struct file *, char *, size_t, loff_t *);static ssize_t tty_write(struct file *, const char *, size_t, loff_t *);static unsigned int tty_poll(struct file *, poll_table *);static int tty_open(struct inode *, struct file *);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);extern int vme_scc_init (void);extern long vme_scc_console_init(void);extern int serial167_init(void);extern long serial167_console_init(void);extern void console_8xx_init(void);extern int rs_8xx_init(void);extern void mac_scc_console_init(void);extern void hwc_console_init(void);extern void hwc_tty_init(void);extern void con3215_init(void);extern void tty3215_init(void);extern void tub3270_con_init(void);extern void tub3270_init(void);extern void uart_console_init(void);extern void sgi_serial_console_init(void);extern void sci_console_init(void);extern void tx3912_console_init(void);extern void tx3912_rs_init(void);extern void txx927_console_init(void);extern void sb1250_serial_console_init(void);#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endif#ifndef MAX#define MAX(a,b) ((a) < (b) ? (b) : (a))#endifstatic struct tty_struct *alloc_tty_struct(void){ struct tty_struct *tty; tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL); if (tty) memset(tty, 0, sizeof(struct tty_struct)); return tty;}static inline void free_tty_struct(struct tty_struct *tty){ kfree(tty);}/* * This routine returns the name of tty. */static char *_tty_make_name(struct tty_struct *tty, const char *name, char *buf){ int idx = (tty)?MINOR(tty->device) - tty->driver.minor_start:0; if (!tty) /* Hmm. NULL pointer. That's fun. */ strcpy(buf, "NULL tty"); else sprintf(buf, name, idx + tty->driver.name_base); return buf;}#define TTY_NUMBER(tty) (MINOR((tty)->device) - (tty)->driver.minor_start + \ (tty)->driver.name_base)char *tty_name(struct tty_struct *tty, char *buf){ return _tty_make_name(tty, (tty)?tty->driver.name:NULL, buf);}inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device, const char *routine){#ifdef TTY_PARANOIA_CHECK static const char badmagic[] = KERN_WARNING "Warning: bad magic number for tty struct (%s) in %s\n"; static const char badtty[] = KERN_WARNING "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 list_head *p; int count = 0; file_list_lock(); for(p = tty->tty_files.next; p != &tty->tty_files; p = p->next) { if(list_entry(p, struct file, f_list)->private_data == tty) count++; } file_list_unlock(); 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(KERN_WARNING "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;}EXPORT_SYMBOL(tty_register_ldisc);/* 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; char buf[64]; if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS)) return -EINVAL; /* Eduardo Blanco <ejbs@cs.cs.com.uy> */ /* Cyrus Durgin <cider@speakeasy.org> */ if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) { char modname [20]; sprintf(modname, "tty-ldisc-%d", ldisc); request_module (modname); } 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, buf), 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(KERN_WARNING "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 ssize_t hung_up_tty_read(struct file * file, char * buf, size_t count, loff_t *ppos){ /* Can't seek (pread) on ttys. */ if (ppos != &file->f_pos) return -ESPIPE; return 0;}static ssize_t hung_up_tty_write(struct file * file, const char * buf, size_t count, loff_t *ppos){ /* Can't seek (pwrite) on ttys. */ if (ppos != &file->f_pos) return -ESPIPE; return -EIO;}/* No kernel lock held - none needed ;) */static unsigned int hung_up_tty_poll(struct file * filp, poll_table * wait){ return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;}static int hung_up_tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ return cmd == TIOCSPGRP ? -ENOTTY : -EIO;}static struct file_operations tty_fops = { llseek: no_llseek, read: tty_read, write: tty_write, poll: tty_poll, ioctl: tty_ioctl, open: tty_open, release: tty_release, fasync: tty_fasync,};static struct file_operations hung_up_tty_fops = { llseek: no_llseek, read: hung_up_tty_read, write: hung_up_tty_write, poll: hung_up_tty_poll, ioctl: hung_up_tty_ioctl, release: tty_release,};/* * This can be called by the "eventd" kernel thread. That is process synchronous, * but doesn't hold any locks, so we need to make sure we have the appropriate * locks for what we're doing.. */void do_tty_hangup(void *data){ struct tty_struct *tty = (struct tty_struct *) data; struct file * cons_filp = NULL; struct task_struct *p; struct list_head *l; int closecount = 0, n; if (!tty) return; /* inuse_filps is protected by the single kernel lock */ lock_kernel(); check_tty_count(tty, "do_tty_hangup"); file_list_lock(); for (l = tty->tty_files.next; l != &tty->tty_files; l = l->next) { struct file * filp = list_entry(l, struct file, f_list); if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV || filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV) { cons_filp = filp; continue; } if (filp->f_op != &tty_fops) continue; closecount++; tty_fasync(-1, filp, 0); /* can't block */ filp->f_op = &hung_up_tty_fops; } file_list_unlock(); /* FIXME! What are the locking issues here? This may me overdoing things.. */ { unsigned long flags; save_flags(flags); cli(); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); restore_flags(flags); } 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->driver.flags & TTY_DRIVER_RESET_TERMIOS) *tty->termios = tty->driver.init_termios; 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) { int i = (tty->ldisc.open)(tty); if (i < 0) printk(KERN_ERR "do_tty_hangup: N_TTY open: " "error %d\n", -i); } } read_lock(&tasklist_lock); 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; } read_unlock(&tasklist_lock); tty->flags = 0; tty->session = 0; tty->pgrp = -1; tty->ctrl_status = 0; /* * If one of the devices matches a console pointer, we * cannot just call hangup() because that will cause * tty->count and state->count to go out of sync. * So we just call close() the right number of times. */ if (cons_filp) { if (tty->driver.close) for (n = 0; n < closecount; n++) tty->driver.close(tty, cons_filp); } else if (tty->driver.hangup) (tty->driver.hangup)(tty); unlock_kernel();}void tty_hangup(struct tty_struct * tty){#ifdef TTY_DEBUG_HANGUP char buf[64]; printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf));#endif schedule_task(&tty->tq_hangup);}void tty_vhangup(struct tty_struct * tty){#ifdef TTY_DEBUG_HANGUP char buf[64]; printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));#endif do_tty_hangup((void *) tty);}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; read_lock(&tasklist_lock); for_each_task(p) if (p->session == current->session) p->tty = NULL; read_unlock(&tasklist_lock);}void stop_tty(struct tty_struct *tty){ if (tty->stopped) return; tty->stopped = 1; if (tty->link && tty->link->packet) { tty->ctrl_status &= ~TIOCPKT_START; tty->ctrl_status |= TIOCPKT_STOP; wake_up_interruptible(&tty->link->read_wait); } if (tty->driver.stop) (tty->driver.stop)(tty);}void start_tty(struct tty_struct *tty){ if (!tty->stopped || tty->flow_stopped) return; tty->stopped = 0; if (tty->link && tty->link->packet) { tty->ctrl_status &= ~TIOCPKT_STOP; tty->ctrl_status |= TIOCPKT_START; wake_up_interruptible(&tty->link->read_wait); } if (tty->driver.start) (tty->driver.start)(tty); if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait);}static ssize_t tty_read(struct file * file, char * buf, size_t count, loff_t *ppos){ int i; struct tty_struct * tty; struct inode *inode; /* Can't seek (pread) on ttys. */ if (ppos != &file->f_pos) return -ESPIPE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -