?? tty_io.c
字號:
} } tty->write_data_cnt = 0; /* Clear out pending trash */ if (tty->close) tty->close(tty, filp); if (IS_A_PTY_MASTER(dev)) { if (--tty->link->count < 0) { printk("release_dev: bad tty slave count (dev = %d): %d\n", dev, tty->count); tty->link->count = 0; } } if (--tty->count < 0) { printk("release_dev: bad tty_table[%d]->count: %d\n", dev, tty->count); tty->count = 0; } if (tty->count) return; #ifdef TTY_DEBUG_HANGUP printk("freeing tty structure...");#endif /* * Make sure there aren't any processes that still think this * tty is their controlling tty. */ for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { if ((*p) && (*p)->tty == tty->line) (*p)->tty = -1; } /* * Shutdown the current line discipline, and reset it to * N_TTY. */ if (ldiscs[tty->disc].close != NULL) ldiscs[tty->disc].close(tty); tty->disc = N_TTY; tty->termios->c_line = N_TTY; if (o_tty) { if (o_tty->count) return; else { tty_table[PTY_OTHER(dev)] = NULL; tty_termios[PTY_OTHER(dev)] = NULL; } } tty_table[dev] = NULL; if (IS_A_PTY(dev)) { tty_termios[dev] = NULL; kfree_s(tp, sizeof(struct termios)); } if (tty == redirect || o_tty == redirect) redirect = NULL; free_page((unsigned long) tty); if (o_tty) free_page((unsigned long) o_tty); if (o_tp) kfree_s(o_tp, sizeof(struct termios));}/* * 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 major, minor; int noctty, retval;retry_open: minor = MINOR(inode->i_rdev); major = MAJOR(inode->i_rdev); noctty = filp->f_flags & O_NOCTTY; if (major == TTYAUX_MAJOR) { if (!minor) { major = TTY_MAJOR; minor = current->tty; } /* noctty = 1; */ } else if (major == TTY_MAJOR) { if (!minor) { minor = fg_console + 1; noctty = 1; } } else { printk("Bad major #%d in tty_open\n", MAJOR(inode->i_rdev)); return -ENODEV; } if (minor <= 0) return -ENXIO; if (IS_A_PTY_MASTER(minor)) noctty = 1; filp->f_rdev = (major << 8) | minor; retval = init_dev(minor); if (retval) return retval; tty = tty_table[minor];#ifdef TTY_DEBUG_HANGUP printk("opening tty%d...", tty->line);#endif if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser()) return -EBUSY;#if 0 /* clean up the packet stuff. */ /* * Why is this not done in init_dev? Right here, if another * process opens up a tty in packet mode, all the packet * variables get cleared. Come to think of it, is anything * using the packet mode at all??? - Ted, 1/27/93 * * Not to worry, a pty master can only be opened once. * And rlogind and telnetd both use packet mode. -- jrs * * Not needed. These are cleared in initialize_tty_struct. -- jlc */ tty->ctrl_status = 0; tty->packet = 0;#endif if (tty->open) { retval = tty->open(tty, filp); } else { retval = -ENODEV; } if (retval) {#ifdef TTY_DEBUG_HANGUP printk("error %d in opening tty%d...", retval, tty->line);#endif release_dev(minor, filp); if (retval != -ERESTARTSYS) return retval; if (current->signal & ~current->blocked) return retval; schedule(); goto retry_open; } if (!noctty && current->leader && current->tty<0 && tty->session==0) { current->tty = minor; tty->session = current->session; tty->pgrp = current->pgrp; } filp->f_rdev = MKDEV(TTY_MAJOR,minor); /* Set it to something normal */ return 0;}/* * Note that releasing a pty master also releases the child, so * we have to make the redirection checks after that and on both * sides of a pty. */static void tty_release(struct inode * inode, struct file * filp){ int dev; dev = filp->f_rdev; if (MAJOR(dev) != TTY_MAJOR) { printk("tty_release: tty pseudo-major != TTY_MAJOR\n"); return; } dev = MINOR(filp->f_rdev); if (!dev) { printk("tty_release: bad f_rdev\n"); return; } release_dev(dev, filp);}static int tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait){ int dev; struct tty_struct * tty; dev = filp->f_rdev; if (MAJOR(dev) != TTY_MAJOR) { printk("tty_select: tty pseudo-major != TTY_MAJOR\n"); return 0; } dev = MINOR(filp->f_rdev); tty = TTY_TABLE(dev); if (!tty) { printk("tty_select: tty struct for dev %d was NULL\n", dev); return 0; } if (ldiscs[tty->disc].select) return (ldiscs[tty->disc].select)(tty, inode, filp, sel_type, wait); return 0;}static int normal_select(struct tty_struct * tty, struct inode * inode, struct file * file, int sel_type, select_table *wait){ switch (sel_type) { case SEL_IN: if (input_available_p(tty)) return 1; /* fall through */ case SEL_EX: if (tty->packet && tty->link->ctrl_status) return 1; if (tty->flags & (1 << TTY_SLAVE_CLOSED)) return 1; if (tty_hung_up_p(file)) return 1; select_wait(&tty->secondary.proc_list, wait); return 0; case SEL_OUT: if (LEFT(&tty->write_q) > WAKEUP_CHARS) return 1; select_wait(&tty->write_q.proc_list, wait); return 0; } return 0;}/* * 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. * * This code could be nicer; ideally it should send a HUP, wait a few * seconds, then send a INT, and then a KILL signal. But you then * have to coordinate with the init process, since all processes associated * with the current tty must be dead before the new getty is allowed * to spawn. */void do_SAK( struct tty_struct *tty){#ifdef TTY_SOFT_SAK tty_hangup(tty);#else struct task_struct **p; int line = tty->line; int session = tty->session; int i; struct file *filp; flush_input(tty); flush_output(tty); for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) { if (!(*p)) continue; if (((*p)->tty == line) || ((session > 0) && ((*p)->session == session))) send_sig(SIGKILL, *p, 1); else { for (i=0; i < NR_OPEN; i++) { filp = (*p)->filp[i]; if (filp && (filp->f_op == &tty_fops) && (MINOR(filp->f_rdev) == line)) { send_sig(SIGKILL, *p, 1); break; } } } }#endif}/* * This routine allows a kernel routine to send a large chunk of data * to a particular tty; if all of the data can be queued up for ouput * immediately, tty_write_data() will return 0. If, however, not all * of the data can be immediately queued for delivery, the number of * bytes left to be queued up will be returned, and the rest of the * data will be queued up when there is room. The callback function * will be called (with the argument callarg) when the last of the * data is finally in the queue. * * Note that the callback routine will _not_ be called if all of the * data could be queued immediately. This is to avoid a problem with * the kernel stack getting too deep, which might happen if the * callback routine calls tty_write_data with itself as an argument. */int tty_write_data(struct tty_struct *tty, char *bufp, int buflen, void (*callback)(void * data), void * callarg){ int head, tail, count; unsigned long flags; char *p;#define VLEFT ((tail-head-1)&(TTY_BUF_SIZE-1)) save_flags(flags); cli(); if (tty->write_data_cnt) { restore_flags(flags); return -EBUSY; } head = tty->write_q.head; tail = tty->write_q.tail; count = buflen; p = bufp; while (count && VLEFT > 0) { tty->write_q.buf[head++] = *p++; head &= TTY_BUF_SIZE-1; count--; } tty->write_q.head = head; if (count) { tty->write_data_cnt = count; tty->write_data_ptr = (unsigned char *) p; tty->write_data_callback = callback; tty->write_data_arg = callarg; } restore_flags(flags); tty->write(tty); return count;}/* * This routine routine is called after an interrupt has drained a * tty's write queue, so that there is more space for data waiting to * be sent in tty->write_data_ptr. * * tty_check_write[8] is a bitstring which indicates which ttys * needs to be processed. */void tty_bh_routine(void * unused){ int i, j, line, mask; int head, tail, count; unsigned char * p; struct tty_struct * tty; for (i = 0, line = 0; i < MAX_TTYS / 32; i++) { if (!tty_check_write[i]) { line += 32; continue; } for (j=0, mask=0; j < 32; j++, line++, mask <<= 1) { if (clear_bit(j, &tty_check_write[i])) { tty = tty_table[line]; if (!tty || !tty->write_data_cnt) continue; cli(); head = tty->write_q.head; tail = tty->write_q.tail; count = tty->write_data_cnt; p = tty->write_data_ptr; while (count && VLEFT > 0) { tty->write_q.buf[head++] = *p++; head &= TTY_BUF_SIZE-1; count--; } tty->write_q.head = head; tty->write_data_ptr = p; tty->write_data_cnt = count; sti(); if (!count) (tty->write_data_callback) (tty->write_data_arg); } } } }/* * This subroutine initializes a tty structure. We have to set up * things correctly for each different type of tty. */static void initialize_tty_struct(int line, struct tty_struct *tty){ memset(tty, 0, sizeof(struct tty_struct)); tty->line = line; tty->disc = N_TTY; tty->pgrp = -1; if (IS_A_CONSOLE(line)) { tty->open = con_open; tty->winsize.ws_row = video_num_lines; tty->winsize.ws_col = video_num_columns; } else if IS_A_SERIAL(line) { tty->open = rs_open; } else if IS_A_PTY(line) { tty->open = pty_open; }}static void initialize_termios(int line, struct termios * tp){ memset(tp, 0, sizeof(struct termios)); memcpy(tp->c_cc, INIT_C_CC, NCCS); if (IS_A_CONSOLE(line) || IS_A_PTY_SLAVE(line)) { tp->c_iflag = ICRNL | IXON; tp->c_oflag = OPOST | ONLCR; tp->c_cflag = B38400 | CS8 | CREAD; tp->c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; } else if (IS_A_SERIAL(line)) { tp->c_iflag = ICRNL | IXON; tp->c_oflag = OPOST | ONLCR | XTABS; tp->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tp->c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; } else if (IS_A_PTY_MASTER(line)) tp->c_cflag = B9600 | CS8 | CREAD;}static struct tty_ldisc tty_ldisc_N_TTY = { 0, /* flags */ NULL, /* open */ NULL, /* close */ read_chan, /* read */ write_chan, /* write */ NULL, /* ioctl */ normal_select, /* select */ copy_to_cooked /* handler */}; long tty_init(long kmem_start){ int i; if (sizeof(struct tty_struct) > PAGE_SIZE) panic("size of tty structure > PAGE_SIZE!"); if (register_chrdev(TTY_MAJOR,"tty",&tty_fops)) panic("unable to get major %d for tty device", TTY_MAJOR); if (register_chrdev(TTYAUX_MAJOR,"tty",&tty_fops)) panic("unable to get major %d for tty device", TTYAUX_MAJOR); for (i=0 ; i< MAX_TTYS ; i++) { tty_table[i] = 0; tty_termios[i] = 0; } memset(tty_check_write, 0, sizeof(tty_check_write)); bh_base[TTY_BH].routine = tty_bh_routine; /* Setup the default TTY line discipline. */ memset(ldiscs, 0, sizeof(ldiscs)); (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); kmem_start = kbd_init(kmem_start); kmem_start = con_init(kmem_start); kmem_start = rs_init(kmem_start); return kmem_start;}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -