813 lines · 20.6 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.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>  #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 (test_bit(TTY_IO_ERROR, &tty->flags))			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);	tty->packet = 0;	/* 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_unlock(tty);		tty_vhangup(tty->link);		tty_lock(tty);	}} /* * 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_space	-	report space left for writing *	@to: tty we are writing into * *	Limit the buffer space used by ptys to 8k. */ static int pty_space(struct tty_struct *to){	int n = tty_buffer_space_avail(to->port);	return min(n, 8192);} /** *	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 pty_space(tty->link);} /** *	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){	unsigned long flags;	int pktmode; 	if (get_user(pktmode, arg))		return -EFAULT; 	spin_lock_irqsave(&tty->ctrl_lock, flags);	if (pktmode) {		if (!tty->packet) {			tty->packet = 1;			tty->link->ctrl_status = 0;		}	} else		tty->packet = 0;	spin_unlock_irqrestore(&tty->ctrl_lock, flags); 	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){	unsigned long flags;	struct pid *pgrp; 	if (tty->link) {		spin_lock_irqsave(&tty->link->ctrl_lock, flags);		pgrp = get_pid(tty->link->pgrp);		spin_unlock_irqrestore(&tty->link->ctrl_lock, flags); 		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;	unsigned long flags; 	if (!to)		return;	/* tty_buffer_flush(to); FIXME */	if (to->packet) {		spin_lock_irqsave(&tty->ctrl_lock, flags);		tty->ctrl_status |= TIOCPKT_FLUSHWRITE;		wake_up_interruptible(&to->read_wait);		spin_unlock_irqrestore(&tty->ctrl_lock, flags);	}} 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){	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;	unsigned long flags;	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; 	/* Get the PID values and reference them so we can	   avoid holding the tty ctrl lock while sending signals.	   We need to lock these individually however. */ 	spin_lock_irqsave(&tty->ctrl_lock, flags);	pgrp = get_pid(tty->pgrp);	spin_unlock_irqrestore(&tty->ctrl_lock, flags); 	spin_lock_irqsave(&pty->ctrl_lock, flags);	rpgrp = get_pid(pty->pgrp);	spin_unlock_irqrestore(&pty->ctrl_lock, flags); 	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_common_install		-	set up the pty pair *	@driver: the pty driver *	@tty: the tty being instantiated *	@bool: 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; 	o_tty = alloc_tty_struct();	if (!o_tty)		goto err;	ports[0] = kmalloc(sizeof **ports, GFP_KERNEL);	ports[1] = kmalloc(sizeof **ports, GFP_KERNEL);	if (!ports[0] || !ports[1])		goto err_free_tty;	if (!try_module_get(driver->other->owner)) {		/* This cannot in fact currently happen */		goto err_free_tty;	}	initialize_tty_struct(o_tty, driver->other, idx); 	if (legacy) {		/* We always use new tty termios data so we can do this		   the easy way .. */		retval = tty_init_termios(tty);		if (retval)			goto err_deinit_tty; 		retval = tty_init_termios(o_tty);		if (retval)			goto err_free_termios; 		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);	if (driver->subtype == PTY_TYPE_MASTER)		o_tty->count++;	/* Establish the links in both directions */	tty->link   = o_tty;	o_tty->link = tty;	tty_port_init(ports[0]);	tty_port_init(ports[1]);	o_tty->port = ports[0];	tty->port = ports[1];	o_tty->port->itty = o_tty; 	tty_driver_kref_get(driver);	tty->count++;	return 0;err_free_termios:	if (legacy)		tty_free_termios(tty);err_deinit_tty:	deinitialize_tty_struct(o_tty);	module_put(o_tty->driver->owner);err_free_tty:	kfree(ports[0]);	kfree(ports[1]);	free_tty_struct(o_tty);err:	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 int legacy_count = CONFIG_LEGACY_PTY_COUNT;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,	.chars_in_buffer = pty_chars_in_buffer,	.unthrottle = pty_unthrottle,	.set_termios = pty_set_termios,	.ioctl = pty_bsd_ioctl,	.cleanup = pty_cleanup,	.resize = pty_resize,	.remove = pty_remove}; static const struct tty_operations slave_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,	.chars_in_buffer = pty_chars_in_buffer,	.unthrottle = pty_unthrottle,	.set_termios = pty_set_termios,	.cleanup = pty_cleanup,	.resize = pty_resize,	.remove = pty_remove}; static void __init legacy_pty_init(void){	struct tty_driver *pty_driver, *pty_slave_driver; 	if (legacy_count <= 0)		return; 	pty_driver = tty_alloc_driver(legacy_count,			TTY_DRIVER_RESET_TERMIOS |			TTY_DRIVER_REAL_RAW |			TTY_DRIVER_DYNAMIC_ALLOC);	if (IS_ERR(pty_driver))		panic("Couldn't allocate pty driver"); 	pty_slave_driver = tty_alloc_driver(legacy_count,			TTY_DRIVER_RESET_TERMIOS |			TTY_DRIVER_REAL_RAW |			TTY_DRIVER_DYNAMIC_ALLOC);	if (IS_ERR(pty_slave_driver))		panic("Couldn't allocate pty slave driver"); 	pty_driver->driver_name = "pty_master";	pty_driver->name = "pty";

Lines 1–500 of 813.