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(&current->sighand->siglock);		old_pgrp = current->signal->tty_old_pgrp;		current->signal->tty_old_pgrp = NULL;		spin_unlock_irq(&current->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(&current->sighand->siglock);	put_pid(current->signal->tty_old_pgrp);	current->signal->tty_old_pgrp = NULL;	spin_unlock_irq(&current->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 + -
显示快捷键?