📄 tty_io.c
字号:
}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;}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 (;;) { /* why should this depend on the page size? (PAGE_SIZE*2) */ unsigned int size = 8192; 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 = alloc_tty_struct (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 = alloc_tty_struct (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) kfree_s(o_tp, sizeof(struct termios)); if (o_tty) free_tty_struct(o_tty); if (ltp) kfree_s(ltp, sizeof(struct termios)); if (tp) kfree_s(tp, sizeof(struct termios)); free_tty_struct(tty);fail_no_mem: retval = -ENOMEM; goto end_init; /* call the tty release_mem routine to clean out this slot */release_mem_out: printk("init_dev: ldisc open failed, clearing slot %d\n", idx); release_mem(tty, idx); goto end_init;}/* * Releases memory associated with a tty structure, and clears out the * driver table slots. */static void release_mem(struct tty_struct *tty, int idx){ struct tty_struct *o_tty; struct termios *tp; if ((o_tty = tty->link) != NULL) { o_tty->driver.table[idx] = NULL; if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { tp = o_tty->driver.termios[idx]; o_tty->driver.termios[idx] = NULL; kfree_s(tp, sizeof(struct termios)); } o_tty->magic = 0; (*o_tty->driver.refcount)--; free_tty_struct(o_tty); } tty->driver.table[idx] = NULL; if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { tp = tty->driver.termios[idx]; tty->driver.termios[idx] = NULL; kfree_s(tp, sizeof(struct termios)); } tty->magic = 0; (*tty->driver.refcount)--; free_tty_struct(tty);}/* * Even releasing the tty structures is a tricky business.. We have * to be very careful that the structures are all released at the * same time, as interrupts might otherwise get the wrong pointers. */static void release_dev(struct file * filp){ struct tty_struct *tty, *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; int idx; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev")) return; check_tty_count(tty, "release_dev"); tty_fasync(filp->f_inode, filp, 0); idx = MINOR(tty->device) - tty->driver.minor_start; pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY && tty->driver.subtype == PTY_TYPE_MASTER); o_tty = tty->link;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -