3647 lines · 89.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *  Copyright (C) 1991, 1992  Linus Torvalds */ /* * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles * or rs-channels. It also implements echoing, cooked mode etc. * * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0. * * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the * tty_struct and tty_queue structures.  Previously there was an array * of 256 tty_struct's which was statically allocated, and the * tty_queue structures were allocated at boot time.  Both are now * dynamically allocated only when the tty is open. * * Also restructured routines so that there is more of a separation * between the high-level tty routines (tty_io.c and tty_ioctl.c) and * the low-level tty routines (serial.c, pty.c, console.c).  This * makes for cleaner and more compact code.  -TYT, 9/17/92 * * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines * which can be dynamically activated and de-activated by the line * discipline handling modules (like SLIP). * * NOTE: pay no attention to the line discipline code (yet); its * interface is still subject to change in this version... * -- TYT, 1/31/92 * * Added functionality to the OPOST tty handling.  No delays, but all * other bits should be there. *	-- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993. * * Rewrote canonical mode and added more termios flags. *	-- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94 * * Reorganized FASYNC support so mouse code can share it. *	-- ctm@ardi.com, 9Sep95 * * New TIOCLINUX variants added. *	-- mj@k332.feld.cvut.cz, 19-Nov-95 * * Restrict vt switching via ioctl() *      -- grif@cs.ucr.edu, 5-Dec-95 * * Move console and virtual terminal code to more appropriate files, * implement CONFIG_VT and generalize console device interface. *	-- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97 * * Rewrote tty_init_dev and tty_release_dev to eliminate races. *	-- Bill Hawes <whawes@star.net>, June 97 * * Added devfs support. *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998 * * Added support for a Unix98-style ptmx device. *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998 * * Reduced memory usage for older ARM systems *      -- Russell King <rmk@arm.linux.org.uk> * * Move do_SAK() into process context.  Less stack use in devfs functions. * alloc_tty_struct() always uses kmalloc() *			 -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01 */ #include <linux/types.h>#include <linux/major.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/fcntl.h>#include <linux/sched/signal.h>#include <linux/sched/task.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/tty_flip.h>#include <linux/devpts_fs.h>#include <linux/file.h>#include <linux/fdtable.h>#include <linux/console.h>#include <linux/timer.h>#include <linux/ctype.h>#include <linux/kd.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/ppp-ioctl.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <linux/module.h>#include <linux/device.h>#include <linux/wait.h>#include <linux/bitops.h>#include <linux/delay.h>#include <linux/seq_file.h>#include <linux/serial.h>#include <linux/ratelimit.h>#include <linux/compat.h>#include <linux/uaccess.h>#include <linux/termios_internal.h>#include <linux/fs.h> #include <linux/kbd_kern.h>#include <linux/vt_kern.h>#include <linux/selection.h> #include <linux/kmod.h>#include <linux/nsproxy.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 #define TTY_PARANOIA_CHECK 1#define CHECK_TTY_COUNT 1 struct ktermios tty_std_termios = {	/* for the benefit of tty drivers  */	.c_iflag = ICRNL | IXON,	.c_oflag = OPOST | ONLCR,	.c_cflag = B38400 | CS8 | CREAD | HUPCL,	.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |		   ECHOCTL | ECHOKE | IEXTEN,	.c_cc = INIT_C_CC,	.c_ispeed = 38400,	.c_ospeed = 38400,	/* .c_line = N_TTY, */};EXPORT_SYMBOL(tty_std_termios); /* This list gets poked at by procfs and various bits of boot up code. This * could do with some rationalisation such as pulling the tty proc function * into this file. */ LIST_HEAD(tty_drivers);			/* linked list of tty drivers */ /* Mutex to protect creating and releasing a tty */DEFINE_MUTEX(tty_mutex); static ssize_t tty_read(struct kiocb *, struct iov_iter *);static ssize_t tty_write(struct kiocb *, struct iov_iter *);static __poll_t tty_poll(struct file *, poll_table *);static int tty_open(struct inode *, struct file *);#ifdef CONFIG_COMPATstatic long tty_compat_ioctl(struct file *file, unsigned int cmd,				unsigned long arg);#else#define tty_compat_ioctl NULL#endifstatic int __tty_fasync(int fd, struct file *filp, int on);static int tty_fasync(int fd, struct file *filp, int on);static void release_tty(struct tty_struct *tty, int idx); /** * free_tty_struct - free a disused tty * @tty: tty struct to free * * Free the write buffers, tty queue and tty memory itself. * * Locking: none. Must be called after tty is definitely unused */static void free_tty_struct(struct tty_struct *tty){	tty_ldisc_deinit(tty);	put_device(tty->dev);	kvfree(tty->write_buf);	kfree(tty);} static inline struct tty_struct *file_tty(struct file *file){	return ((struct tty_file_private *)file->private_data)->tty;} int tty_alloc_file(struct file *file){	struct tty_file_private *priv; 	priv = kmalloc_obj(*priv);	if (!priv)		return -ENOMEM; 	file->private_data = priv; 	return 0;} /* Associate a new file with the tty structure */void tty_add_file(struct tty_struct *tty, struct file *file){	struct tty_file_private *priv = file->private_data; 	priv->tty = tty;	priv->file = file; 	spin_lock(&tty->files_lock);	list_add(&priv->list, &tty->tty_files);	spin_unlock(&tty->files_lock);} /** * tty_free_file - free file->private_data * @file: to free private_data of * * This shall be used only for fail path handling when tty_add_file was not * called yet. */void tty_free_file(struct file *file){	struct tty_file_private *priv = file->private_data; 	file->private_data = NULL;	kfree(priv);} /* Delete file from its tty */static void tty_del_file(struct file *file){	struct tty_file_private *priv = file->private_data;	struct tty_struct *tty = priv->tty; 	spin_lock(&tty->files_lock);	list_del(&priv->list);	spin_unlock(&tty->files_lock);	tty_free_file(file);} /** * tty_name - return tty naming * @tty: tty structure * * Convert a tty structure into a name. The name reflects the kernel naming * policy and if udev is in use may not reflect user space * * Locking: none */const char *tty_name(const struct tty_struct *tty){	if (!tty) /* Hmm.  NULL pointer.  That's fun. */		return "NULL tty";	return tty->name;}EXPORT_SYMBOL(tty_name); const char *tty_driver_name(const struct tty_struct *tty){	if (!tty || !tty->driver)		return "";	return tty->driver->name;} static int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,			      const char *routine){#ifdef TTY_PARANOIA_CHECK	if (!tty) {		pr_warn("(%d:%d): %s: NULL tty\n",			imajor(inode), iminor(inode), routine);		return 1;	}#endif	return 0;} /* Caller must hold tty_lock */static void check_tty_count(struct tty_struct *tty, const char *routine){#ifdef CHECK_TTY_COUNT	struct list_head *p;	int count = 0, kopen_count = 0; 	scoped_guard(spinlock, &tty->files_lock)		list_for_each(p, &tty->tty_files)			count++; 	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&	    tty->driver->subtype == PTY_TYPE_SLAVE &&	    tty->link && tty->link->count)		count++;	if (tty_port_kopened(tty->port))		kopen_count++;	if (tty->count != (count + kopen_count)) {		tty_warn(tty, "%s: tty->count(%d) != (#fd's(%d) + #kopen's(%d))\n",			 routine, tty->count, count, kopen_count);	}#endif} /** * get_tty_driver - find device of a tty * @device: device identifier * @index: returns the index of the tty * * This routine returns a tty driver structure, given a device number and also * passes back the index number. * * Locking: caller must hold tty_mutex */static struct tty_driver *get_tty_driver(dev_t device, int *index){	struct tty_driver *p; 	list_for_each_entry(p, &tty_drivers, tty_drivers) {		dev_t base = MKDEV(p->major, p->minor_start); 		if (device < base || device >= base + p->num)			continue;		*index = device - base;		return tty_driver_kref_get(p);	}	return NULL;} /** * tty_dev_name_to_number - return dev_t for device name * @name: user space name of device under /dev * @number: pointer to dev_t that this function will populate * * This function converts device names like ttyS0 or ttyUSB1 into dev_t like * (4, 64) or (188, 1). If no corresponding driver is registered then the * function returns -%ENODEV. * * Locking: this acquires tty_mutex to protect the tty_drivers list from *	being modified while we are traversing it, and makes sure to *	release it before exiting. */int tty_dev_name_to_number(const char *name, dev_t *number){	struct tty_driver *p;	int ret;	int index, prefix_length = 0;	const char *str; 	for (str = name; *str && !isdigit(*str); str++)		; 	if (!*str)		return -EINVAL; 	ret = kstrtoint(str, 10, &index);	if (ret)		return ret; 	prefix_length = str - name; 	guard(mutex)(&tty_mutex); 	list_for_each_entry(p, &tty_drivers, tty_drivers)		if (prefix_length == strlen(p->name) && strncmp(name,					p->name, prefix_length) == 0) {			if (index < p->num) {				*number = MKDEV(p->major, p->minor_start + index);				return 0;			}		} 	return -ENODEV;}EXPORT_SYMBOL_GPL(tty_dev_name_to_number); #ifdef CONFIG_CONSOLE_POLL /** * tty_find_polling_driver - find device of a polled tty * @name: name string to match * @line: pointer to resulting tty line nr * * This routine returns a tty driver structure, given a name and the condition * that the tty driver is capable of polled operation. */struct tty_driver *tty_find_polling_driver(char *name, int *line){	struct tty_driver *p;	int tty_line = 0;	int len;	char *str, *stp; 	for (str = name; *str; str++)		if ((*str >= '0' && *str <= '9') || *str == ',')			break;	if (!*str)		return NULL; 	len = str - name;	tty_line = simple_strtoul(str, &str, 10); 	guard(mutex)(&tty_mutex); 	/* Search through the tty devices to look for a match */	list_for_each_entry(p, &tty_drivers, tty_drivers) {		if (!len || strncmp(name, p->name, len) != 0)			continue;		stp = str;		if (*stp == ',')			stp++;		if (*stp == '\0')			stp = NULL; 		if (tty_line >= 0 && tty_line < p->num && p->ops &&		    p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {			*line = tty_line;			return tty_driver_kref_get(p);		}	} 	return NULL;}EXPORT_SYMBOL_GPL(tty_find_polling_driver);#endif static ssize_t hung_up_tty_read(struct kiocb *iocb, struct iov_iter *to){	return 0;} static ssize_t hung_up_tty_write(struct kiocb *iocb, struct iov_iter *from){	return -EIO;} /* No kernel lock held - none needed ;) */static __poll_t hung_up_tty_poll(struct file *filp, poll_table *wait){	return EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | EPOLLWRNORM;} static long hung_up_tty_ioctl(struct file *file, unsigned int cmd,		unsigned long arg){	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;} static long hung_up_tty_compat_ioctl(struct file *file,				     unsigned int cmd, unsigned long arg){	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;} static int hung_up_tty_fasync(int fd, struct file *file, int on){	return -ENOTTY;} static void tty_show_fdinfo(struct seq_file *m, struct file *file){	struct tty_struct *tty = file_tty(file); 	if (tty && tty->ops && tty->ops->show_fdinfo)		tty->ops->show_fdinfo(tty, m);} static const struct file_operations tty_fops = {	.read_iter	= tty_read,	.write_iter	= tty_write,	.splice_read	= copy_splice_read,	.splice_write	= iter_file_splice_write,	.poll		= tty_poll,	.unlocked_ioctl	= tty_ioctl,	.compat_ioctl	= tty_compat_ioctl,	.open		= tty_open,	.release	= tty_release,	.fasync		= tty_fasync,	.show_fdinfo	= tty_show_fdinfo,}; static const struct file_operations console_fops = {	.read_iter	= tty_read,	.write_iter	= redirected_tty_write,	.splice_read	= copy_splice_read,	.splice_write	= iter_file_splice_write,	.poll		= tty_poll,	.unlocked_ioctl	= tty_ioctl,	.compat_ioctl	= tty_compat_ioctl,	.open		= tty_open,	.release	= tty_release,	.fasync		= tty_fasync,}; static const struct file_operations hung_up_tty_fops = {	.read_iter	= hung_up_tty_read,	.write_iter	= hung_up_tty_write,	.poll		= hung_up_tty_poll,	.unlocked_ioctl	= hung_up_tty_ioctl,	.compat_ioctl	= hung_up_tty_compat_ioctl,	.release	= tty_release,	.fasync		= hung_up_tty_fasync,}; static DEFINE_SPINLOCK(redirect_lock);static struct file *redirect; /** * tty_wakeup - request more data

Lines 1–500 of 3647.