// 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_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)
{
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->ctrl.packet = false;
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
* @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 pktmode;
if (get_user(pktmode, arg))
return -EFAULT;
spin_lock_irq(&tty->ctrl.lock);
if (pktmode) {
if (!tty->ctrl.packet) {
tty->link->ctrl.pktstatus = 0;
smp_mb();
tty->ctrl.packet = true;
}
} else
tty->ctrl.packet = false;
spin_unlock_irq(&tty->ctrl.lock);
return 0;
}
/* Get the packet mode of a pty */
static int