tty_io.c
来自「linux 内核源代码」· C语言 代码 · 共 2,498 行 · 第 1/5 页
C
2,498 行
/** * tty_vhangup - process vhangup * @tty: tty to hangup * * The user has asked via system call for the terminal to be hung up. * We do this synchronously so that when the syscall returns the process * is complete. That guarantee is necessary for security reasons. */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(&tty->hangup_work);}EXPORT_SYMBOL(tty_vhangup);/** * tty_hung_up_p - was tty hung up * @filp: file pointer of tty * * Return true if the tty has been subject to a vhangup or a carrier * loss */int tty_hung_up_p(struct file * filp){ return (filp->f_op == &hung_up_tty_fops);}EXPORT_SYMBOL(tty_hung_up_p);/** * is_tty - checker whether file is a TTY */int is_tty(struct file *filp){ return filp->f_op->read == tty_read || filp->f_op->read == hung_up_tty_read;}static void session_clear_tty(struct pid *session){ struct task_struct *p; do_each_pid_task(session, PIDTYPE_SID, p) { proc_clear_tty(p); } while_each_pid_task(session, PIDTYPE_SID, p);}/** * disassociate_ctty - disconnect controlling tty * @on_exit: true if exiting so need to "hang up" the session * * 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. * * Locking: * BKL is taken for hysterical raisins * tty_mutex is taken to protect tty * ->siglock is taken to protect ->signal/->sighand * tasklist_lock is taken to walk process list for sessions * ->siglock is taken to protect ->signal/->sighand */void disassociate_ctty(int on_exit){ struct tty_struct *tty; struct pid *tty_pgrp = NULL; lock_kernel(); mutex_lock(&tty_mutex); tty = get_current_tty(); if (tty) { tty_pgrp = get_pid(tty->pgrp); mutex_unlock(&tty_mutex); /* XXX: here we race, there is nothing protecting tty */ if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) tty_vhangup(tty); } else if (on_exit) { struct pid *old_pgrp; spin_lock_irq(¤t->sighand->siglock); old_pgrp = current->signal->tty_old_pgrp; current->signal->tty_old_pgrp = NULL; spin_unlock_irq(¤t->sighand->siglock); if (old_pgrp) { kill_pgrp(old_pgrp, SIGHUP, on_exit); kill_pgrp(old_pgrp, SIGCONT, on_exit); put_pid(old_pgrp); } mutex_unlock(&tty_mutex); unlock_kernel(); return; } if (tty_pgrp) { kill_pgrp(tty_pgrp, SIGHUP, on_exit); if (!on_exit) kill_pgrp(tty_pgrp, SIGCONT, on_exit); put_pid(tty_pgrp); } spin_lock_irq(¤t->sighand->siglock); put_pid(current->signal->tty_old_pgrp); current->signal->tty_old_pgrp = NULL; spin_unlock_irq(¤t->sighand->siglock); mutex_lock(&tty_mutex); /* It is possible that do_tty_hangup has free'd this tty */ tty = get_current_tty(); if (tty) { put_pid(tty->session); put_pid(tty->pgrp); tty->session = NULL; tty->pgrp = NULL; } else {#ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "error attempted to write to tty [0x%p]" " = NULL", tty);#endif } mutex_unlock(&tty_mutex); /* Now clear signal->tty under the lock */ read_lock(&tasklist_lock); session_clear_tty(task_session(current)); read_unlock(&tasklist_lock); unlock_kernel();}/** * * no_tty - Ensure the current process does not have a controlling tty */void no_tty(void){ struct task_struct *tsk = current; if (tsk->signal->leader) disassociate_ctty(0); proc_clear_tty(tsk);}/** * stop_tty - propagate flow control * @tty: tty to stop * * Perform flow control to the driver. For PTY/TTY pairs we * must also propagate the TIOCKPKT status. May be called * on an already stopped device and will not re-call the driver * method. * * This functionality is used by both the line disciplines for * halting incoming flow and by the driver. It may therefore be * called from any context, may be under the tty atomic_write_lock * but not always. * * Locking: * Broken. Relies on BKL which is unsafe here. */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);}EXPORT_SYMBOL(stop_tty);/** * start_tty - propagate flow control * @tty: tty to start * * Start a tty that has been stopped if at all possible. Perform * any necessary wakeups and propagate the TIOCPKT status. If this * is the tty was previous stopped and is being started then the * driver start method is invoked and the line discipline woken. * * Locking: * Broken. Relies on BKL which is unsafe here. */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 we have a running line discipline it may need kicking */ tty_wakeup(tty);}EXPORT_SYMBOL(start_tty);/** * tty_read - read method for tty device files * @file: pointer to tty file * @buf: user buffer * @count: size of user buffer * @ppos: unused * * Perform the read system call function on this terminal device. Checks * for hung up devices before calling the line discipline method. * * Locking: * Locks the line discipline internally while needed * For historical reasons the line discipline read method is * invoked under the BKL. This will go away in time so do not rely on it * in new code. Multiple read calls may be outstanding in parallel. */static ssize_t tty_read(struct file * file, char __user * buf, size_t count, loff_t *ppos){ int i; struct tty_struct * tty; struct inode *inode; struct tty_ldisc *ld; tty = (struct tty_struct *)file->private_data; inode = file->f_path.dentry->d_inode; if (tty_paranoia_check(tty, inode, "tty_read")) return -EIO; if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; /* We want to wait for the line discipline to sort out in this situation */ ld = tty_ldisc_ref_wait(tty); lock_kernel(); if (ld->read) i = (ld->read)(tty,file,buf,count); else i = -EIO; tty_ldisc_deref(ld); unlock_kernel(); if (i > 0) inode->i_atime = current_fs_time(inode->i_sb); return i;}void tty_write_unlock(struct tty_struct *tty){ mutex_unlock(&tty->atomic_write_lock); wake_up_interruptible(&tty->write_wait);}int tty_write_lock(struct tty_struct *tty, int ndelay){ if (!mutex_trylock(&tty->atomic_write_lock)) { if (ndelay) return -EAGAIN; if (mutex_lock_interruptible(&tty->atomic_write_lock)) return -ERESTARTSYS; } return 0;}/* * Split writes up in sane blocksizes to avoid * denial-of-service type attacks */static inline ssize_t do_tty_write( ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t), struct tty_struct *tty, struct file *file, const char __user *buf, size_t count){ ssize_t ret, written = 0; unsigned int chunk; ret = tty_write_lock(tty, file->f_flags & O_NDELAY); if (ret < 0) return ret; /* * We chunk up writes into a temporary buffer. This * simplifies low-level drivers immensely, since they * don't have locking issues and user mode accesses. * * But if TTY_NO_WRITE_SPLIT is set, we should use a * big chunk-size.. * * The default chunk-size is 2kB, because the NTTY * layer has problems with bigger chunks. It will * claim to be able to handle more characters than * it actually does. * * FIXME: This can probably go away now except that 64K chunks * are too likely to fail unless switched to vmalloc... */ chunk = 2048; if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags)) chunk = 65536; if (count < chunk) chunk = count; /* write_buf/write_cnt is protected by the atomic_write_lock mutex */ if (tty->write_cnt < chunk) { unsigned char *buf; if (chunk < 1024) chunk = 1024; buf = kmalloc(chunk, GFP_KERNEL); if (!buf) { ret = -ENOMEM; goto out; } kfree(tty->write_buf); tty->write_cnt = chunk; tty->write_buf = buf; } /* Do the write .. */ for (;;) { size_t size = count; if (size > chunk) size = chunk; ret = -EFAULT; if (copy_from_user(tty->write_buf, buf, size)) break; lock_kernel(); ret = write(tty, file, tty->write_buf, size); unlock_kernel(); if (ret <= 0) break; written += ret; buf += ret; count -= ret; if (!count) break; ret = -ERESTARTSYS; if (signal_pending(current)) break; cond_resched(); } if (written) { struct inode *inode = file->f_path.dentry->d_inode; inode->i_mtime = current_fs_time(inode->i_sb); ret = written; }out: tty_write_unlock(tty); return ret;}/** * tty_write - write method for tty device file * @file: tty file pointer * @buf: user data to write * @count: bytes to write * @ppos: unused * * Write data to a tty device via the line discipline. * * Locking: * Locks the line discipline as required * Writes to the tty driver are serialized by the atomic_write_lock * and are then processed in chunks to the device. The line discipline * write method will not be involked in parallel for each device * The line discipline write method is called under the big * kernel lock for historical reasons. New code should not rely on this. */static ssize_t tty_write(struct file * file, const char __user * buf, size_t count, loff_t *ppos){ struct tty_struct * tty; struct inode *inode = file->f_path.dentry->d_inode; ssize_t ret; struct tty_ldisc *ld; tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode, "tty_write")) return -EIO; if (!tty || !tty->driver->write || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; ld = tty_ldisc_ref_wait(tty); if (!ld->write) ret = -EIO; else ret = do_tty_write(ld->write, tty, file, buf, count); tty_ldisc_deref(ld); return ret;}ssize_t redirected_tty_write(struct file * file, const char __user * buf, size_t count, loff_t *ppos){ struct file *p = NULL; spin_lock(&redirect_lock); if (redirect) { get_file(redirect); p = redirect; } spin_unlock(&redirect_lock); if (p) { ssize_t res; res = vfs_write(p, buf, count, &p->f_pos); fput(p); return res; } return tty_write(file, buf, count, ppos);}static char ptychar[] = "pqrstuvwxyzabcde";/** * pty_line_name - generate name for a pty * @driver: the tty driver in use * @index: the minor number * @p: output buffer of at least 6 bytes * * Generate a name from a driver reference and write it to the output * buffer. * * Locking: None */static void pty_line_name(struct tty_driver *driver, int index, char *p){ int i = index + driver->name_base; /* ->name is initialized to "ttyp", but "tty" is expected */ sprintf(p, "%s%c%x", driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name, ptychar[i >> 4 & 0xf], i & 0xf);}/** * pty_line_name - generate name for a tty * @driver: the tty driver in use * @index: the minor number * @p: output buffer of at least 7 bytes * * Generate a name from a driver reference and write it to the output * buffer. * * Locking: None */static void tty_line_name(struct tty_driver *driver, int index, char *p){ sprintf(p, "%s%d", driver->name, index + driver->name_base);}/** * init_dev - initialise a tty device * @driver: tty driver we are opening a device on * @idx: device index * @tty: returned tty structure * * Prepare a tty device. This may not be a "new" clean device but * could also be an active device. The pty drivers require special * handling because of this. * * Locking: * The function is called under the tty_mutex, which * protects us from the tty struct or driver itself going away. * * On exit the tty device has the line discipline attached and * a reference count of 1. If a pair was created for pty/tty use * and the other was a pty master then it too has a reference count of 1. * * WSH 06/09/97: Rewritten to remove races and properly clean up after a * failed open. The new code protects the open with a mutex, so it's * really quite straightforward. The mutex locking can probably be * relaxed for the (most common) case of reopening a tty. */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?