Eric W. Biedermanpty: Repair TIOCGPTPEER311fc659 years agoHistory
948 lines · 24.0 KB · C
RawDownload
/* *  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> #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){	BUG_ON(!tty);	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);	spin_lock_irq(&tty->ctrl_lock);	tty->packet = 0;	spin_unlock_irq(&tty->ctrl_lock);	/* 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) {			mutex_lock(&devpts_mutex);			if (tty->link->driver_data)				devpts_pty_kill(tty->link->driver_data);			mutex_unlock(&devpts_mutex);		}#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 *	@count: 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 int pty_write(struct tty_struct *tty, const unsigned char *buf, int c){	struct tty_struct *to = tty->link; 	if (tty->stopped)		return 0; 	if (c > 0) {		/* Stuff the data into the input queue of the other end */		c = tty_insert_flip_string(to->port, buf, c);		/* And shovel */		if (c)			tty_flip_buffer_push(to->port);	}	return 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 int pty_write_room(struct tty_struct *tty){	if (tty->stopped)		return 0;	return tty_buffer_space_avail(tty->link->port);} /** *	pty_chars_in_buffer	-	characters currently in our tx queue *	@tty: our tty * *	Report how much we have in the transmit queue. As everything is *	instantly at the other end this is easy to implement. */ static int pty_chars_in_buffer(struct tty_struct *tty){	return 0;} /* 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 pktmode; 	if (get_user(pktmode, arg))		return -EFAULT; 	spin_lock_irq(&tty->ctrl_lock);	if (pktmode) {		if (!tty->packet) {			tty->link->ctrl_status = 0;			smp_mb();			tty->packet = 1;		}	} else		tty->packet = 0;	spin_unlock_irq(&tty->ctrl_lock); 	return 0;} /* Get the packet mode of a pty */static int pty_get_pktmode(struct tty_struct *tty, int __user *arg){	int pktmode = tty->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->packet) {		spin_lock_irq(&tty->ctrl_lock);		tty->ctrl_status |= TIOCPKT_FLUSHWRITE;		wake_up_interruptible(&to->read_wait);		spin_unlock_irq(&tty->ctrl_lock);	}} 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,					struct ktermios *old_termios){	/* See if packet mode change of state. */	if (tty->link && tty->link->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) {			spin_lock_irq(&tty->ctrl_lock);			if (old_flow != new_flow) {				tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);				if (new_flow)					tty->ctrl_status |= TIOCPKT_DOSTOP;				else					tty->ctrl_status |= TIOCPKT_NOSTOP;			}			if (extproc)				tty->ctrl_status |= TIOCPKT_IOCTL;			spin_unlock_irq(&tty->ctrl_lock);			wake_up_interruptible(&tty->link->read_wait);		}	} 	tty->termios.c_cflag &= ~(CSIZE | PARENB);	tty->termios.c_cflag |= (CS8 | CREAD);} /** *	pty_do_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 */	mutex_lock(&tty->winsize_mutex);	if (!memcmp(ws, &tty->winsize, sizeof(*ws)))		goto done; 	/* 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 */done:	mutex_unlock(&tty->winsize_mutex);	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){	unsigned long flags; 	if (tty->link && tty->link->packet) {		spin_lock_irqsave(&tty->ctrl_lock, flags);		tty->ctrl_status &= ~TIOCPKT_STOP;		tty->ctrl_status |= TIOCPKT_START;		spin_unlock_irqrestore(&tty->ctrl_lock, flags);		wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);	}} static void pty_stop(struct tty_struct *tty){	unsigned long flags; 	if (tty->link && tty->link->packet) {		spin_lock_irqsave(&tty->ctrl_lock, flags);		tty->ctrl_status &= ~TIOCPKT_START;		tty->ctrl_status |= TIOCPKT_STOP;		spin_unlock_irqrestore(&tty->ctrl_lock, flags);		wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);	}} /** *	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(sizeof **ports, GFP_KERNEL);	ports[1] = kmalloc(sizeof **ports, GFP_KERNEL);	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;} static 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, arg);} 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.

Lines 1–500 of 948.