123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969 |
- // 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>
- #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_PTYS
- static 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;
- unsigned long flags;
- if (tty->stopped)
- return 0;
- if (c > 0) {
- spin_lock_irqsave(&to->port->lock, flags);
- /* Stuff the data into the input queue of the other end */
- c = tty_insert_flip_string(to->port, buf, c);
- spin_unlock_irqrestore(&to->port->lock, flags);
- /* 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, EPOLLIN);
- }
- }
- 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, 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(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;
- }
- #ifdef CONFIG_COMPAT
- 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, (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,
- .chars_in_buffer = pty_chars_in_buffer,
- .unthrottle = pty_unthrottle,
- .ioctl = pty_bsd_ioctl,
- .compat_ioctl = pty_bsd_compat_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,
- .start = pty_start,
- .stop = pty_stop,
- .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";
- pty_driver->major = PTY_MASTER_MAJOR;
- pty_driver->minor_start = 0;
- pty_driver->type = TTY_DRIVER_TYPE_PTY;
- pty_driver->subtype = PTY_TYPE_MASTER;
- pty_driver->init_termios = tty_std_termios;
- pty_driver->init_termios.c_iflag = 0;
- pty_driver->init_termios.c_oflag = 0;
- pty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
- pty_driver->init_termios.c_lflag = 0;
- pty_driver->init_termios.c_ispeed = 38400;
- pty_driver->init_termios.c_ospeed = 38400;
- pty_driver->other = pty_slave_driver;
- tty_set_operations(pty_driver, &master_pty_ops_bsd);
- pty_slave_driver->driver_name = "pty_slave";
- pty_slave_driver->name = "ttyp";
- pty_slave_driver->major = PTY_SLAVE_MAJOR;
- pty_slave_driver->minor_start = 0;
- pty_slave_driver->type = TTY_DRIVER_TYPE_PTY;
- pty_slave_driver->subtype = PTY_TYPE_SLAVE;
- pty_slave_driver->init_termios = tty_std_termios;
- pty_slave_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
- pty_slave_driver->init_termios.c_ispeed = 38400;
- pty_slave_driver->init_termios.c_ospeed = 38400;
- pty_slave_driver->other = pty_driver;
- tty_set_operations(pty_slave_driver, &slave_pty_ops_bsd);
- if (tty_register_driver(pty_driver))
- panic("Couldn't register pty driver");
- if (tty_register_driver(pty_slave_driver))
- panic("Couldn't register pty slave driver");
- }
- #else
- static inline void legacy_pty_init(void) { }
- #endif
- /* Unix98 devices */
- #ifdef CONFIG_UNIX98_PTYS
- static struct cdev ptmx_cdev;
- /**
- * ptm_open_peer - open the peer of a pty
- * @master: the open struct file of the ptmx device node
- * @tty: the master of the pty being opened
- * @flags: the flags for open
- *
- * Provide a race free way for userspace to open the slave end of a pty
- * (where they have the master fd and cannot access or trust the mount
- * namespace /dev/pts was mounted inside).
- */
- int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags)
- {
- int fd = -1;
- struct file *filp;
- int retval = -EINVAL;
- struct path path;
- if (tty->driver != ptm_driver)
- return -EIO;
- fd = get_unused_fd_flags(flags);
- if (fd < 0) {
- retval = fd;
- goto err;
- }
- /* Compute the slave's path */
- path.mnt = devpts_mntget(master, tty->driver_data);
- if (IS_ERR(path.mnt)) {
- retval = PTR_ERR(path.mnt);
- goto err_put;
- }
- path.dentry = tty->link->driver_data;
- filp = dentry_open(&path, flags, current_cred());
- mntput(path.mnt);
- if (IS_ERR(filp)) {
- retval = PTR_ERR(filp);
- goto err_put;
- }
- fd_install(fd, filp);
- return fd;
- err_put:
- put_unused_fd(fd);
- err:
- return retval;
- }
- static int pty_unix98_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 TIOCGPTN: /* Get PT Number */
- return put_user(tty->index, (unsigned int __user *)arg);
- case TIOCSIG: /* Send signal to other side of pty */
- return pty_signal(tty, (int) arg);
- }
- return -ENOIOCTLCMD;
- }
- #ifdef CONFIG_COMPAT
- static long pty_unix98_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_unix98_ioctl(tty, cmd,
- cmd == TIOCSIG ? arg : (unsigned long)compat_ptr(arg));
- }
- #else
- #define pty_unix98_compat_ioctl NULL
- #endif
- /**
- * ptm_unix98_lookup - find a pty master
- * @driver: ptm driver
- * @idx: tty index
- *
- * Look up a pty master device. Called under the tty_mutex for now.
- * This provides our locking.
- */
- static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver,
- struct file *file, int idx)
- {
- /* Master must be open via /dev/ptmx */
- return ERR_PTR(-EIO);
- }
- /**
- * pts_unix98_lookup - find a pty slave
- * @driver: pts driver
- * @idx: tty index
- *
- * Look up a pty master device. Called under the tty_mutex for now.
- * This provides our locking for the tty pointer.
- */
- static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
- struct file *file, int idx)
- {
- struct tty_struct *tty;
- mutex_lock(&devpts_mutex);
- tty = devpts_get_priv(file->f_path.dentry);
- mutex_unlock(&devpts_mutex);
- /* Master must be open before slave */
- if (!tty)
- return ERR_PTR(-EIO);
- return tty;
- }
- static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
- {
- return pty_common_install(driver, tty, false);
- }
- /* this is called once with whichever end is closed last */
- static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
- {
- struct pts_fs_info *fsi;
- if (tty->driver->subtype == PTY_TYPE_MASTER)
- fsi = tty->driver_data;
- else
- fsi = tty->link->driver_data;
- if (fsi) {
- devpts_kill_index(fsi, tty->index);
- devpts_release(fsi);
- }
- }
- static void pty_show_fdinfo(struct tty_struct *tty, struct seq_file *m)
- {
- seq_printf(m, "tty-index:\t%d\n", tty->index);
- }
- static const struct tty_operations ptm_unix98_ops = {
- .lookup = ptm_unix98_lookup,
- .install = pty_unix98_install,
- .remove = pty_unix98_remove,
- .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,
- .ioctl = pty_unix98_ioctl,
- .compat_ioctl = pty_unix98_compat_ioctl,
- .resize = pty_resize,
- .cleanup = pty_cleanup,
- .show_fdinfo = pty_show_fdinfo,
- };
- static const struct tty_operations pty_unix98_ops = {
- .lookup = pts_unix98_lookup,
- .install = pty_unix98_install,
- .remove = pty_unix98_remove,
- .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,
- .start = pty_start,
- .stop = pty_stop,
- .cleanup = pty_cleanup,
- };
- /**
- * ptmx_open - open a unix 98 pty master
- * @inode: inode of device file
- * @filp: file pointer to tty
- *
- * Allocate a unix98 pty master device from the ptmx driver.
- *
- * Locking: tty_mutex protects the init_dev work. tty->count should
- * protect the rest.
- * allocated_ptys_lock handles the list of free pty numbers
- */
- static int ptmx_open(struct inode *inode, struct file *filp)
- {
- struct pts_fs_info *fsi;
- struct tty_struct *tty;
- struct dentry *dentry;
- int retval;
- int index;
- nonseekable_open(inode, filp);
- /* We refuse fsnotify events on ptmx, since it's a shared resource */
- filp->f_mode |= FMODE_NONOTIFY;
- retval = tty_alloc_file(filp);
- if (retval)
- return retval;
- fsi = devpts_acquire(filp);
- if (IS_ERR(fsi)) {
- retval = PTR_ERR(fsi);
- goto out_free_file;
- }
- /* find a device that is not in use. */
- mutex_lock(&devpts_mutex);
- index = devpts_new_index(fsi);
- mutex_unlock(&devpts_mutex);
- retval = index;
- if (index < 0)
- goto out_put_fsi;
- mutex_lock(&tty_mutex);
- tty = tty_init_dev(ptm_driver, index);
- /* The tty returned here is locked so we can safely
- drop the mutex */
- mutex_unlock(&tty_mutex);
- retval = PTR_ERR(tty);
- if (IS_ERR(tty))
- goto out;
- /*
- * From here on out, the tty is "live", and the index and
- * fsi will be killed/put by the tty_release()
- */
- set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
- tty->driver_data = fsi;
- tty_add_file(tty, filp);
- dentry = devpts_pty_new(fsi, index, tty->link);
- if (IS_ERR(dentry)) {
- retval = PTR_ERR(dentry);
- goto err_release;
- }
- tty->link->driver_data = dentry;
- retval = ptm_driver->ops->open(tty, filp);
- if (retval)
- goto err_release;
- tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
- tty_unlock(tty);
- return 0;
- err_release:
- tty_unlock(tty);
- // This will also put-ref the fsi
- tty_release(inode, filp);
- return retval;
- out:
- devpts_kill_index(fsi, index);
- out_put_fsi:
- devpts_release(fsi);
- out_free_file:
- tty_free_file(filp);
- return retval;
- }
- static struct file_operations ptmx_fops __ro_after_init;
- static void __init unix98_pty_init(void)
- {
- ptm_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX,
- TTY_DRIVER_RESET_TERMIOS |
- TTY_DRIVER_REAL_RAW |
- TTY_DRIVER_DYNAMIC_DEV |
- TTY_DRIVER_DEVPTS_MEM |
- TTY_DRIVER_DYNAMIC_ALLOC);
- if (IS_ERR(ptm_driver))
- panic("Couldn't allocate Unix98 ptm driver");
- pts_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX,
- TTY_DRIVER_RESET_TERMIOS |
- TTY_DRIVER_REAL_RAW |
- TTY_DRIVER_DYNAMIC_DEV |
- TTY_DRIVER_DEVPTS_MEM |
- TTY_DRIVER_DYNAMIC_ALLOC);
- if (IS_ERR(pts_driver))
- panic("Couldn't allocate Unix98 pts driver");
- ptm_driver->driver_name = "pty_master";
- ptm_driver->name = "ptm";
- ptm_driver->major = UNIX98_PTY_MASTER_MAJOR;
- ptm_driver->minor_start = 0;
- ptm_driver->type = TTY_DRIVER_TYPE_PTY;
- ptm_driver->subtype = PTY_TYPE_MASTER;
- ptm_driver->init_termios = tty_std_termios;
- ptm_driver->init_termios.c_iflag = 0;
- ptm_driver->init_termios.c_oflag = 0;
- ptm_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
- ptm_driver->init_termios.c_lflag = 0;
- ptm_driver->init_termios.c_ispeed = 38400;
- ptm_driver->init_termios.c_ospeed = 38400;
- ptm_driver->other = pts_driver;
- tty_set_operations(ptm_driver, &ptm_unix98_ops);
- pts_driver->driver_name = "pty_slave";
- pts_driver->name = "pts";
- pts_driver->major = UNIX98_PTY_SLAVE_MAJOR;
- pts_driver->minor_start = 0;
- pts_driver->type = TTY_DRIVER_TYPE_PTY;
- pts_driver->subtype = PTY_TYPE_SLAVE;
- pts_driver->init_termios = tty_std_termios;
- pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
- pts_driver->init_termios.c_ispeed = 38400;
- pts_driver->init_termios.c_ospeed = 38400;
- pts_driver->other = ptm_driver;
- tty_set_operations(pts_driver, &pty_unix98_ops);
- if (tty_register_driver(ptm_driver))
- panic("Couldn't register Unix98 ptm driver");
- if (tty_register_driver(pts_driver))
- panic("Couldn't register Unix98 pts driver");
- /* Now create the /dev/ptmx special device */
- tty_default_fops(&ptmx_fops);
- ptmx_fops.open = ptmx_open;
- cdev_init(&ptmx_cdev, &ptmx_fops);
- if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
- register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
- panic("Couldn't register /dev/ptmx driver");
- device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
- }
- #else
- static inline void unix98_pty_init(void) { }
- #endif
- static int __init pty_init(void)
- {
- legacy_pty_init();
- unix98_pty_init();
- return 0;
- }
- device_initcall(pty_init);
|