923 lines · 23.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *  Copyright (C) 1991, 1992  Linus Torvalds * *  Added support for a Unix98-style ptmx device. *    -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998 * */ #include <linux/module.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/fcntl.h>#include <linux/sched/signal.h>#include <linux/string.h>#include <linux/major.h>#include <linux/mm.h>#include <linux/init.h>#include <linux/device.h>#include <linux/uaccess.h>#include <linux/bitops.h>#include <linux/devpts_fs.h>#include <linux/slab.h>#include <linux/mutex.h>#include <linux/poll.h>#include <linux/mount.h>#include <linux/file.h>#include <linux/ioctl.h>#include <linux/compat.h>#include "tty.h" #undef TTY_DEBUG_HANGUP#ifdef TTY_DEBUG_HANGUP# define tty_debug_hangup(tty, f, args...)	tty_debug(tty, f, ##args)#else# define tty_debug_hangup(tty, f, args...)	do {} while (0)#endif #ifdef CONFIG_UNIX98_PTYSstatic struct tty_driver *ptm_driver;static struct tty_driver *pts_driver;static DEFINE_MUTEX(devpts_mutex);#endif static void pty_close(struct tty_struct *tty, struct file *filp){	if (tty->driver->subtype == PTY_TYPE_MASTER)		WARN_ON(tty->count > 1);	else {		if (tty_io_error(tty))			return;		if (tty->count > 2)			return;	}	set_bit(TTY_IO_ERROR, &tty->flags);	wake_up_interruptible(&tty->read_wait);	wake_up_interruptible(&tty->write_wait);	scoped_guard(spinlock_irq, &tty->ctrl.lock)		tty->ctrl.packet = false;	/* Review - krefs on tty_link ?? */	if (!tty->link)		return;	set_bit(TTY_OTHER_CLOSED, &tty->link->flags);	wake_up_interruptible(&tty->link->read_wait);	wake_up_interruptible(&tty->link->write_wait);	if (tty->driver->subtype == PTY_TYPE_MASTER) {		set_bit(TTY_OTHER_CLOSED, &tty->flags);#ifdef CONFIG_UNIX98_PTYS		if (tty->driver == ptm_driver) {			guard(mutex)(&devpts_mutex);			if (tty->link->driver_data)				devpts_pty_kill(tty->link->driver_data);		}#endif		tty_vhangup(tty->link);	}} /* * The unthrottle routine is called by the line discipline to signal * that it can receive more characters.  For PTY's, the TTY_THROTTLED * flag is always set, to force the line discipline to always call the * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE * characters in the queue.  This is necessary since each time this * happens, we need to wake up any sleeping processes that could be * (1) trying to send data to the pty, or (2) waiting in wait_until_sent() * for the pty buffer to be drained. */static void pty_unthrottle(struct tty_struct *tty){	tty_wakeup(tty->link);	set_bit(TTY_THROTTLED, &tty->flags);} /** *	pty_write		-	write to a pty *	@tty: the tty we write from *	@buf: kernel buffer of data *	@c: bytes to write * *	Our "hardware" write method. Data is coming from the ldisc which *	may be in a non sleeping state. We simply throw this at the other *	end of the link as if we were an IRQ handler receiving stuff for *	the other side of the pty/tty pair. */ static ssize_t pty_write(struct tty_struct *tty, const u8 *buf, size_t c){	struct tty_struct *to = tty->link; 	if (tty->flow.stopped || !c)		return 0; 	return tty_insert_flip_string_and_push_buffer(to->port, buf, c);} /** *	pty_write_room	-	write space *	@tty: tty we are writing from * *	Report how many bytes the ldisc can send into the queue for *	the other device. */ static unsigned int pty_write_room(struct tty_struct *tty){	if (tty->flow.stopped)		return 0;	return tty_buffer_space_avail(tty->link->port);} /* Set the lock flag on a pty */static int pty_set_lock(struct tty_struct *tty, int __user *arg){	int val; 	if (get_user(val, arg))		return -EFAULT;	if (val)		set_bit(TTY_PTY_LOCK, &tty->flags);	else		clear_bit(TTY_PTY_LOCK, &tty->flags);	return 0;} static int pty_get_lock(struct tty_struct *tty, int __user *arg){	int locked = test_bit(TTY_PTY_LOCK, &tty->flags); 	return put_user(locked, arg);} /* Set the packet mode on a pty */static int pty_set_pktmode(struct tty_struct *tty, int __user *arg){	int want_pktmode; 	if (get_user(want_pktmode, arg))		return -EFAULT; 	guard(spinlock_irq)(&tty->ctrl.lock);	if (!want_pktmode) {		tty->ctrl.packet = false;		return 0;	} 	if (tty->ctrl.packet)		return 0; 	tty->link->ctrl.pktstatus = 0;	smp_mb();	tty->ctrl.packet = true; 	return 0;} /* Get the packet mode of a pty */static int pty_get_pktmode(struct tty_struct *tty, int __user *arg){	int pktmode = tty->ctrl.packet; 	return put_user(pktmode, arg);} /* Send a signal to the slave */static int pty_signal(struct tty_struct *tty, int sig){	struct pid *pgrp; 	if (sig != SIGINT && sig != SIGQUIT && sig != SIGTSTP)		return -EINVAL; 	if (tty->link) {		pgrp = tty_get_pgrp(tty->link);		if (pgrp)			kill_pgrp(pgrp, sig, 1);		put_pid(pgrp);	}	return 0;} static void pty_flush_buffer(struct tty_struct *tty){	struct tty_struct *to = tty->link; 	if (!to)		return; 	tty_buffer_flush(to, NULL);	if (to->ctrl.packet) {		guard(spinlock_irq)(&tty->ctrl.lock);		tty->ctrl.pktstatus |= TIOCPKT_FLUSHWRITE;		wake_up_interruptible(&to->read_wait);	}} static int pty_open(struct tty_struct *tty, struct file *filp){	if (!tty || !tty->link)		return -ENODEV; 	if (test_bit(TTY_OTHER_CLOSED, &tty->flags))		goto out;	if (test_bit(TTY_PTY_LOCK, &tty->link->flags))		goto out;	if (tty->driver->subtype == PTY_TYPE_SLAVE && tty->link->count != 1)		goto out; 	clear_bit(TTY_IO_ERROR, &tty->flags);	clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);	set_bit(TTY_THROTTLED, &tty->flags);	return 0; out:	set_bit(TTY_IO_ERROR, &tty->flags);	return -EIO;} static void pty_set_termios(struct tty_struct *tty,			    const struct ktermios *old_termios){	/* See if packet mode change of state. */	if (tty->link && tty->link->ctrl.packet) {		int extproc = (old_termios->c_lflag & EXTPROC) | L_EXTPROC(tty);		int old_flow = ((old_termios->c_iflag & IXON) &&				(old_termios->c_cc[VSTOP] == '\023') &&				(old_termios->c_cc[VSTART] == '\021'));		int new_flow = (I_IXON(tty) &&				STOP_CHAR(tty) == '\023' &&				START_CHAR(tty) == '\021');		if ((old_flow != new_flow) || extproc) {			scoped_guard(spinlock_irq, &tty->ctrl.lock) {				if (old_flow != new_flow) {					tty->ctrl.pktstatus &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);					if (new_flow)						tty->ctrl.pktstatus |= TIOCPKT_DOSTOP;					else						tty->ctrl.pktstatus |= TIOCPKT_NOSTOP;				}				if (extproc)					tty->ctrl.pktstatus |= TIOCPKT_IOCTL;			}			wake_up_interruptible(&tty->link->read_wait);		}	} 	tty->termios.c_cflag &= ~(CSIZE | PARENB);	tty->termios.c_cflag |= (CS8 | CREAD);} /** *	pty_resize		-	resize event *	@tty: tty being resized *	@ws: window size being set. * *	Update the termios variables and send the necessary signals to *	peform a terminal resize correctly */ static int pty_resize(struct tty_struct *tty,  struct winsize *ws){	struct pid *pgrp, *rpgrp;	struct tty_struct *pty = tty->link; 	/* For a PTY we need to lock the tty side */	guard(mutex)(&tty->winsize_mutex);	if (!memcmp(ws, &tty->winsize, sizeof(*ws)))		return 0; 	/* Signal the foreground process group of both ptys */	pgrp = tty_get_pgrp(tty);	rpgrp = tty_get_pgrp(pty); 	if (pgrp)		kill_pgrp(pgrp, SIGWINCH, 1);	if (rpgrp != pgrp && rpgrp)		kill_pgrp(rpgrp, SIGWINCH, 1); 	put_pid(pgrp);	put_pid(rpgrp); 	tty->winsize = *ws;	pty->winsize = *ws;	/* Never used so will go away soon */ 	return 0;} /** *	pty_start - start() handler *	pty_stop  - stop() handler *	@tty: tty being flow-controlled * *	Propagates the TIOCPKT status to the master pty. * *	NB: only the master pty can be in packet mode so only the slave *	    needs start()/stop() handlers */static void pty_start(struct tty_struct *tty){	if (!tty->link || !tty->link->ctrl.packet)		return; 	scoped_guard(spinlock_irqsave, &tty->ctrl.lock) {		tty->ctrl.pktstatus &= ~TIOCPKT_STOP;		tty->ctrl.pktstatus |= TIOCPKT_START;	}	wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);} static void pty_stop(struct tty_struct *tty){	if (!tty->link || !tty->link->ctrl.packet)		return; 	scoped_guard(spinlock_irqsave, &tty->ctrl.lock) {		tty->ctrl.pktstatus &= ~TIOCPKT_START;		tty->ctrl.pktstatus |= TIOCPKT_STOP;	}	wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);} /** *	pty_common_install		-	set up the pty pair *	@driver: the pty driver *	@tty: the tty being instantiated *	@legacy: true if this is BSD style * *	Perform the initial set up for the tty/pty pair. Called from the *	tty layer when the port is first opened. * *	Locking: the caller must hold the tty_mutex */static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,		bool legacy){	struct tty_struct *o_tty;	struct tty_port *ports[2];	int idx = tty->index;	int retval = -ENOMEM; 	/* Opening the slave first has always returned -EIO */	if (driver->subtype != PTY_TYPE_MASTER)		return -EIO; 	ports[0] = kmalloc_obj(**ports);	ports[1] = kmalloc_obj(**ports);	if (!ports[0] || !ports[1])		goto err;	if (!try_module_get(driver->other->owner)) {		/* This cannot in fact currently happen */		goto err;	}	o_tty = alloc_tty_struct(driver->other, idx);	if (!o_tty)		goto err_put_module; 	tty_set_lock_subclass(o_tty);	lockdep_set_subclass(&o_tty->termios_rwsem, TTY_LOCK_SLAVE); 	if (legacy) {		/* We always use new tty termios data so we can do this		   the easy way .. */		tty_init_termios(tty);		tty_init_termios(o_tty); 		driver->other->ttys[idx] = o_tty;		driver->ttys[idx] = tty;	} else {		memset(&tty->termios_locked, 0, sizeof(tty->termios_locked));		tty->termios = driver->init_termios;		memset(&o_tty->termios_locked, 0, sizeof(tty->termios_locked));		o_tty->termios = driver->other->init_termios;	} 	/*	 * Everything allocated ... set up the o_tty structure.	 */	tty_driver_kref_get(driver->other);	/* Establish the links in both directions */	tty->link   = o_tty;	o_tty->link = tty;	tty_port_init(ports[0]);	tty_port_init(ports[1]);	tty_buffer_set_limit(ports[0], 8192);	tty_buffer_set_limit(ports[1], 8192);	o_tty->port = ports[0];	tty->port = ports[1];	o_tty->port->itty = o_tty; 	tty_buffer_set_lock_subclass(o_tty->port); 	tty_driver_kref_get(driver);	tty->count++;	o_tty->count++;	return 0; err_put_module:	module_put(driver->other->owner);err:	kfree(ports[0]);	kfree(ports[1]);	return retval;} static void pty_cleanup(struct tty_struct *tty){	tty_port_put(tty->port);} /* Traditional BSD devices */#ifdef CONFIG_LEGACY_PTYS static int pty_install(struct tty_driver *driver, struct tty_struct *tty){	return pty_common_install(driver, tty, true);} static void pty_remove(struct tty_driver *driver, struct tty_struct *tty){	struct tty_struct *pair = tty->link; 	driver->ttys[tty->index] = NULL;	if (pair)		pair->driver->ttys[pair->index] = NULL;} static int pty_bsd_ioctl(struct tty_struct *tty,			 unsigned int cmd, unsigned long arg){	switch (cmd) {	case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */		return pty_set_lock(tty, (int __user *) arg);	case TIOCGPTLCK: /* Get PT Lock status */		return pty_get_lock(tty, (int __user *)arg);	case TIOCPKT: /* Set PT packet mode */		return pty_set_pktmode(tty, (int __user *)arg);	case TIOCGPKT: /* Get PT packet mode */		return pty_get_pktmode(tty, (int __user *)arg);	case TIOCSIG:    /* Send signal to other side of pty */		return pty_signal(tty, (int) arg);	case TIOCGPTN: /* TTY returns ENOTTY, but glibc expects EINVAL here */		return -EINVAL;	}	return -ENOIOCTLCMD;} #ifdef CONFIG_COMPATstatic long pty_bsd_compat_ioctl(struct tty_struct *tty,				 unsigned int cmd, unsigned long arg){	/*	 * PTY ioctls don't require any special translation between 32-bit and	 * 64-bit userspace, they are already compatible.	 */	return pty_bsd_ioctl(tty, cmd, (unsigned long)compat_ptr(arg));}#else#define pty_bsd_compat_ioctl NULL#endif static int legacy_count = CONFIG_LEGACY_PTY_COUNT;/* * not really modular, but the easiest way to keep compat with existing * bootargs behaviour is to continue using module_param here. */module_param(legacy_count, int, 0); /* * The master side of a pty can do TIOCSPTLCK and thus * has pty_bsd_ioctl. */static const struct tty_operations master_pty_ops_bsd = {	.install = pty_install,	.open = pty_open,	.close = pty_close,	.write = pty_write,	.write_room = pty_write_room,	.flush_buffer = pty_flush_buffer,

Lines 1–500 of 923.