📄 tty_io.c
字号:
static struct wait_queue *vt_activate_queue = NULL;/* * Sleeps until a vt is activated, or the task is interrupted. Returns * 0 if activation, -1 if interrupted. */int vt_waitactive(void){ interruptible_sleep_on(&vt_activate_queue); return (current->signal & ~current->blocked) ? -1 : 0;}#define vt_wake_waitactive() wake_up(&vt_activate_queue)void reset_vc(unsigned int new_console){ vt_cons[new_console]->vc_mode = KD_TEXT; kbd_table[new_console].kbdmode = VC_XLATE; vt_cons[new_console]->vt_mode.mode = VT_AUTO; vt_cons[new_console]->vt_mode.waitv = 0; vt_cons[new_console]->vt_mode.relsig = 0; vt_cons[new_console]->vt_mode.acqsig = 0; vt_cons[new_console]->vt_mode.frsig = 0; vt_cons[new_console]->vt_pid = -1; vt_cons[new_console]->vt_newvt = -1; reset_palette (new_console) ;}/* * Performs the back end of a vt switch */void complete_change_console(unsigned int new_console){ unsigned char old_vc_mode; if ((new_console == fg_console) || (vt_dont_switch)) return; if (!vc_cons_allocated(new_console)) return; last_console = fg_console; /* * If we're switching, we could be going from KD_GRAPHICS to * KD_TEXT mode or vice versa, which means we need to blank or * unblank the screen later. */ old_vc_mode = vt_cons[fg_console]->vc_mode; update_screen(new_console); /* * If this new console is under process control, send it a signal * telling it that it has acquired. Also check if it has died and * clean up (similar to logic employed in change_console()) */ if (vt_cons[new_console]->vt_mode.mode == VT_PROCESS) { /* * Send the signal as privileged - kill_proc() will * tell us if the process has gone or something else * is awry */ if (kill_proc(vt_cons[new_console]->vt_pid, vt_cons[new_console]->vt_mode.acqsig, 1) != 0) { /* * The controlling process has died, so we revert back to * normal operation. In this case, we'll also change back * to KD_TEXT mode. I'm not sure if this is strictly correct * but it saves the agony when the X server dies and the screen * remains blanked due to KD_GRAPHICS! It would be nice to do * this outside of VT_PROCESS but there is no single process * to account for and tracking tty count may be undesirable. */ reset_vc(new_console); } } /* * We do this here because the controlling process above may have * gone, and so there is now a new vc_mode */ if (old_vc_mode != vt_cons[new_console]->vc_mode) { if (vt_cons[new_console]->vc_mode == KD_TEXT) do_unblank_screen(); else do_blank_screen(1); } /* Set the colour palette for this VT */ if (vt_cons[new_console]->vc_mode == KD_TEXT) set_palette() ; /* * Wake anyone waiting for their VT to activate */ vt_wake_waitactive(); return;}/* * Performs the front-end of a vt switch */void change_console(unsigned int new_console){ if ((new_console == fg_console) || (vt_dont_switch)) return; if (!vc_cons_allocated(new_console)) return; /* * If this vt is in process mode, then we need to handshake with * that process before switching. Essentially, we store where that * vt wants to switch to and wait for it to tell us when it's done * (via VT_RELDISP ioctl). * * We also check to see if the controlling process still exists. * If it doesn't, we reset this vt to auto mode and continue. * This is a cheap way to track process control. The worst thing * that can happen is: we send a signal to a process, it dies, and * the switch gets "lost" waiting for a response; hopefully, the * user will try again, we'll detect the process is gone (unless * the user waits just the right amount of time :-) and revert the * vt to auto control. */ if (vt_cons[fg_console]->vt_mode.mode == VT_PROCESS) { /* * Send the signal as privileged - kill_proc() will * tell us if the process has gone or something else * is awry */ if (kill_proc(vt_cons[fg_console]->vt_pid, vt_cons[fg_console]->vt_mode.relsig, 1) == 0) { /* * It worked. Mark the vt to switch to and * return. The process needs to send us a * VT_RELDISP ioctl to complete the switch. */ vt_cons[fg_console]->vt_newvt = new_console; return; } /* * The controlling process has died, so we revert back to * normal operation. In this case, we'll also change back * to KD_TEXT mode. I'm not sure if this is strictly correct * but it saves the agony when the X server dies and the screen * remains blanked due to KD_GRAPHICS! It would be nice to do * this outside of VT_PROCESS but there is no single process * to account for and tracking tty count may be undesirable. */ reset_vc(fg_console); /* * Fall through to normal (VT_AUTO) handling of the switch... */ } /* * Ignore all switches in KD_GRAPHICS+VT_AUTO mode */ if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS) return; complete_change_console(new_console);}void wait_for_keypress(void){ sleep_on(&keypress_wait);}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) 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 ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait);}static int tty_read(struct inode * inode, struct file * file, char * buf, int count){ int i; struct tty_struct * tty; tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_read")) return -EIO; if (!tty || (tty->flags & (1 << TTY_IO_ERROR))) return -EIO; /* This check not only needs to be done before reading, but also whenever read_chan() gets woken up after sleeping, so I've moved it to there. This should only be done for the N_TTY line discipline, anyway. Same goes for write_chan(). -- jlc. */#if 0 if ((inode->i_rdev != CONSOLE_DEV) && /* don't stop on /dev/console */ (tty->pgrp > 0) && (current->tty == tty) && (tty->pgrp != current->pgrp)) if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp)) return -EIO; else { (void) kill_pg(current->pgrp, SIGTTIN, 1); return -ERESTARTSYS; }#endif if (tty->ldisc.read) /* XXX casts are for what kernel-wide prototypes should be. */ i = (tty->ldisc.read)(tty,file,(unsigned char *)buf,(unsigned int)count); else i = -EIO; if (i > 0) inode->i_atime = CURRENT_TIME; return i;}/* * Split writes up in sane blocksizes to avoid * denial-of-service type attacks */static inline int do_tty_write( int (*write)(struct tty_struct *, struct file *, const unsigned char *, unsigned int), struct inode *inode, struct tty_struct *tty, struct file *file, const unsigned char *buf, unsigned int count){ int ret = 0, written = 0; for (;;) { unsigned int size = PAGE_SIZE*2; if (size > count) size = count; ret = write(tty, file, buf, size); if (ret <= 0) break; written += ret; buf += ret; count -= ret; if (!count) break; ret = -ERESTARTSYS; if (current->signal & ~current->blocked) break; if (need_resched) schedule(); } if (written) { inode->i_mtime = CURRENT_TIME; ret = written; } return ret;}static int tty_write(struct inode * inode, struct file * file, const char * buf, int count){ int is_console; struct tty_struct * tty; is_console = (inode->i_rdev == CONSOLE_DEV); if (is_console && redirect) tty = redirect; else tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_write")) return -EIO; if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR))) return -EIO;#if 0 if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) && (current->tty == tty) && (tty->pgrp != current->pgrp)) { if (is_orphaned_pgrp(current->pgrp)) return -EIO; if (!is_ignored(SIGTTOU)) { (void) kill_pg(current->pgrp, SIGTTOU, 1); return -ERESTARTSYS; } }#endif if (!tty->ldisc.write) return -EIO; return do_tty_write(tty->ldisc.write, inode, tty, file, (const unsigned char *)buf, (unsigned int)count);}/* Semaphore to protect creating and releasing a tty */static struct semaphore tty_sem = MUTEX;static void down_tty_sem(int index){ down(&tty_sem);}static void up_tty_sem(int index){ up(&tty_sem);}static void release_mem(struct tty_struct *tty, int idx);/* * Rewritten to remove races and properly clean up after a failed open. * The new code protects the open with a semaphore, so it's really * quite straightforward. The semaphore locking can probably be * relaxed for the (most common) case of reopening a tty. */static int init_dev(kdev_t device, struct tty_struct **ret_tty){ struct tty_struct *tty, *o_tty; struct termios *tp, **tp_loc, *o_tp, **o_tp_loc; struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; struct tty_driver *driver; int retval; int idx; driver = get_tty_driver(device); if (!driver) return -ENODEV; idx = MINOR(device) - driver->minor_start; /* * Check whether we need to acquire the tty semaphore to avoid * race conditions. For now, play it safe. */ down_tty_sem(idx); /* check whether we're reopening an existing tty */ tty = driver->table[idx]; if(tty) goto fast_track; /* * First time open is complex, especially for PTY devices. * This code guarantees that either everything succeeds and the * TTY is ready for operation, or else the table slots are vacated * and the allocated memory released. (Except that the termios * and locked termios may be retained.) */ o_tty = NULL; tp = o_tp = NULL; ltp = o_ltp = NULL; tty = (struct tty_struct*) get_free_page(GFP_KERNEL); if(!tty) goto fail_no_mem; initialize_tty_struct(tty); tty->device = device; tty->driver = *driver; tp_loc = &driver->termios[idx]; if (!*tp_loc) { tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!tp) goto free_mem_out; *tp = driver->init_termios; } ltp_loc = &driver->termios_locked[idx]; if (!*ltp_loc) { ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!ltp) goto free_mem_out; memset(ltp, 0, sizeof(struct termios)); } if (driver->type == TTY_DRIVER_TYPE_PTY) { o_tty = (struct tty_struct *) get_free_page(GFP_KERNEL); if (!o_tty) goto free_mem_out; initialize_tty_struct(o_tty); o_tty->device = (kdev_t) MKDEV(driver->other->major, driver->other->minor_start + idx); o_tty->driver = *driver->other; o_tp_loc = &driver->other->termios[idx]; if (!*o_tp_loc) { o_tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_tp) goto free_mem_out; *o_tp = driver->other->init_termios; } o_ltp_loc = &driver->other->termios_locked[idx]; if (!*o_ltp_loc) { o_ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_ltp) goto free_mem_out; memset(o_ltp, 0, sizeof(struct termios)); } /* * Everything allocated ... set up the o_tty structure. */ driver->other->table[idx] = o_tty; if (!*o_tp_loc) *o_tp_loc = o_tp; if (!*o_ltp_loc) *o_ltp_loc = o_ltp; o_tty->termios = *o_tp_loc; o_tty->termios_locked = *o_ltp_loc; (*driver->other->refcount)++; if (driver->subtype == PTY_TYPE_MASTER) o_tty->count++; /* Establish the links in both directions */ tty->link = o_tty; o_tty->link = tty; } /* * All structures have been allocated, so now we install them. * Failures after this point use release_mem to clean up, so * there's no need to null out the local pointers. */ driver->table[idx] = tty; if (!*tp_loc) *tp_loc = tp; if (!*ltp_loc) *ltp_loc = ltp; tty->termios = *tp_loc; tty->termios_locked = *ltp_loc; (*driver->refcount)++; tty->count++; /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_mem to clean up. No need * to decrement the use counts, as release_mem doesn't care. */ if (tty->ldisc.open) { retval = (tty->ldisc.open)(tty); if (retval) goto release_mem_out; } if (o_tty && o_tty->ldisc.open) { retval = (o_tty->ldisc.open)(o_tty); if (retval) { if (tty->ldisc.close) (tty->ldisc.close)(tty); goto release_mem_out; } } goto success; /* * This fast open can be used if the tty is already open. * No memory is allocated, and the only failures are from * attempting to open a closing tty or attempting multiple * opens on a pty master. */fast_track: retval = -EIO; if (test_bit(TTY_CLOSING, &tty->flags)) goto end_init; if (driver->type == TTY_DRIVER_TYPE_PTY && driver->subtype == PTY_TYPE_MASTER) { /* * special case for PTY masters: only one open permitted, * and the slave side open count is incremented as well. */ if (tty->count) goto end_init; tty->link->count++; } tty->count++; tty->driver = *driver; /* N.B. why do this every time?? */success: retval = 0; *ret_tty = tty; /* All paths come through here to release the semaphore */end_init: up_tty_sem(idx); return retval; /* Release locally allocated memory ... nothing placed in slots */free_mem_out: if (o_tp)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -