1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
296fd7ce5SGreg Kroah-Hartman /*
396fd7ce5SGreg Kroah-Hartman * Copyright (C) 1991, 1992 Linus Torvalds
496fd7ce5SGreg Kroah-Hartman *
596fd7ce5SGreg Kroah-Hartman * Added support for a Unix98-style ptmx device.
696fd7ce5SGreg Kroah-Hartman * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
796fd7ce5SGreg Kroah-Hartman *
896fd7ce5SGreg Kroah-Hartman */
996fd7ce5SGreg Kroah-Hartman
1096fd7ce5SGreg Kroah-Hartman #include <linux/module.h>
1196fd7ce5SGreg Kroah-Hartman #include <linux/errno.h>
1296fd7ce5SGreg Kroah-Hartman #include <linux/interrupt.h>
1396fd7ce5SGreg Kroah-Hartman #include <linux/tty.h>
1496fd7ce5SGreg Kroah-Hartman #include <linux/tty_flip.h>
1596fd7ce5SGreg Kroah-Hartman #include <linux/fcntl.h>
163f07c014SIngo Molnar #include <linux/sched/signal.h>
1796fd7ce5SGreg Kroah-Hartman #include <linux/string.h>
1896fd7ce5SGreg Kroah-Hartman #include <linux/major.h>
1996fd7ce5SGreg Kroah-Hartman #include <linux/mm.h>
2096fd7ce5SGreg Kroah-Hartman #include <linux/init.h>
2196fd7ce5SGreg Kroah-Hartman #include <linux/device.h>
2296fd7ce5SGreg Kroah-Hartman #include <linux/uaccess.h>
2396fd7ce5SGreg Kroah-Hartman #include <linux/bitops.h>
2496fd7ce5SGreg Kroah-Hartman #include <linux/devpts_fs.h>
2596fd7ce5SGreg Kroah-Hartman #include <linux/slab.h>
26d739e65bSAlan Cox #include <linux/mutex.h>
2701adc807SPeter Hurley #include <linux/poll.h>
2854ebbfb1SAleksa Sarai #include <linux/mount.h>
2954ebbfb1SAleksa Sarai #include <linux/file.h>
3054ebbfb1SAleksa Sarai #include <linux/ioctl.h>
3150f45326SAl Viro #include <linux/compat.h>
3298602c01SGreg Kroah-Hartman #include "tty.h"
3396fd7ce5SGreg Kroah-Hartman
34d6847793SPeter Hurley #undef TTY_DEBUG_HANGUP
35d6847793SPeter Hurley #ifdef TTY_DEBUG_HANGUP
36d6847793SPeter Hurley # define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
37d6847793SPeter Hurley #else
38d6847793SPeter Hurley # define tty_debug_hangup(tty, f, args...) do {} while (0)
39d6847793SPeter Hurley #endif
4096fd7ce5SGreg Kroah-Hartman
4196fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_UNIX98_PTYS
4296fd7ce5SGreg Kroah-Hartman static struct tty_driver *ptm_driver;
4396fd7ce5SGreg Kroah-Hartman static struct tty_driver *pts_driver;
44d739e65bSAlan Cox static DEFINE_MUTEX(devpts_mutex);
4596fd7ce5SGreg Kroah-Hartman #endif
4696fd7ce5SGreg Kroah-Hartman
pty_close(struct tty_struct * tty,struct file * filp)4796fd7ce5SGreg Kroah-Hartman static void pty_close(struct tty_struct *tty, struct file *filp)
4896fd7ce5SGreg Kroah-Hartman {
4996fd7ce5SGreg Kroah-Hartman if (tty->driver->subtype == PTY_TYPE_MASTER)
5096fd7ce5SGreg Kroah-Hartman WARN_ON(tty->count > 1);
5196fd7ce5SGreg Kroah-Hartman else {
5218900ca6SPeter Hurley if (tty_io_error(tty))
5369939035SPeter Hurley return;
5496fd7ce5SGreg Kroah-Hartman if (tty->count > 2)
5596fd7ce5SGreg Kroah-Hartman return;
5696fd7ce5SGreg Kroah-Hartman }
5769939035SPeter Hurley set_bit(TTY_IO_ERROR, &tty->flags);
5896fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&tty->read_wait);
5996fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&tty->write_wait);
6064d608dbSJiri Slaby spin_lock_irq(&tty->ctrl.lock);
6164d608dbSJiri Slaby tty->ctrl.packet = false;
6264d608dbSJiri Slaby spin_unlock_irq(&tty->ctrl.lock);
6389c8d91eSAlan Cox /* Review - krefs on tty_link ?? */
6496fd7ce5SGreg Kroah-Hartman if (!tty->link)
6596fd7ce5SGreg Kroah-Hartman return;
6696fd7ce5SGreg Kroah-Hartman set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
670f40fbbcSBrian Bloniarz wake_up_interruptible(&tty->link->read_wait);
6896fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&tty->link->write_wait);
6996fd7ce5SGreg Kroah-Hartman if (tty->driver->subtype == PTY_TYPE_MASTER) {
70c776b77aSGreg Kroah-Hartman set_bit(TTY_OTHER_CLOSED, &tty->flags);
71ce1000ddSGreg Kroah-Hartman #ifdef CONFIG_UNIX98_PTYS
72d739e65bSAlan Cox if (tty->driver == ptm_driver) {
73d739e65bSAlan Cox mutex_lock(&devpts_mutex);
74311fc65cSEric W. Biederman if (tty->link->driver_data)
75311fc65cSEric W. Biederman devpts_pty_kill(tty->link->driver_data);
76d739e65bSAlan Cox mutex_unlock(&devpts_mutex);
77d739e65bSAlan Cox }
78ce1000ddSGreg Kroah-Hartman #endif
79c776b77aSGreg Kroah-Hartman tty_vhangup(tty->link);
8096fd7ce5SGreg Kroah-Hartman }
8196fd7ce5SGreg Kroah-Hartman }
8296fd7ce5SGreg Kroah-Hartman
8396fd7ce5SGreg Kroah-Hartman /*
8496fd7ce5SGreg Kroah-Hartman * The unthrottle routine is called by the line discipline to signal
8596fd7ce5SGreg Kroah-Hartman * that it can receive more characters. For PTY's, the TTY_THROTTLED
8696fd7ce5SGreg Kroah-Hartman * flag is always set, to force the line discipline to always call the
8796fd7ce5SGreg Kroah-Hartman * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE
8896fd7ce5SGreg Kroah-Hartman * characters in the queue. This is necessary since each time this
8996fd7ce5SGreg Kroah-Hartman * happens, we need to wake up any sleeping processes that could be
9096fd7ce5SGreg Kroah-Hartman * (1) trying to send data to the pty, or (2) waiting in wait_until_sent()
9196fd7ce5SGreg Kroah-Hartman * for the pty buffer to be drained.
9296fd7ce5SGreg Kroah-Hartman */
pty_unthrottle(struct tty_struct * tty)9396fd7ce5SGreg Kroah-Hartman static void pty_unthrottle(struct tty_struct *tty)
9496fd7ce5SGreg Kroah-Hartman {
9596fd7ce5SGreg Kroah-Hartman tty_wakeup(tty->link);
9696fd7ce5SGreg Kroah-Hartman set_bit(TTY_THROTTLED, &tty->flags);
9796fd7ce5SGreg Kroah-Hartman }
9896fd7ce5SGreg Kroah-Hartman
9996fd7ce5SGreg Kroah-Hartman /**
10096fd7ce5SGreg Kroah-Hartman * pty_write - write to a pty
10196fd7ce5SGreg Kroah-Hartman * @tty: the tty we write from
10296fd7ce5SGreg Kroah-Hartman * @buf: kernel buffer of data
103fa441954SJiri Slaby * @c: bytes to write
10496fd7ce5SGreg Kroah-Hartman *
10596fd7ce5SGreg Kroah-Hartman * Our "hardware" write method. Data is coming from the ldisc which
10696fd7ce5SGreg Kroah-Hartman * may be in a non sleeping state. We simply throw this at the other
10796fd7ce5SGreg Kroah-Hartman * end of the link as if we were an IRQ handler receiving stuff for
10896fd7ce5SGreg Kroah-Hartman * the other side of the pty/tty pair.
10996fd7ce5SGreg Kroah-Hartman */
11096fd7ce5SGreg Kroah-Hartman
pty_write(struct tty_struct * tty,const u8 * buf,size_t c)111*95713967SJiri Slaby (SUSE) static ssize_t pty_write(struct tty_struct *tty, const u8 *buf, size_t c)
11296fd7ce5SGreg Kroah-Hartman {
11396fd7ce5SGreg Kroah-Hartman struct tty_struct *to = tty->link;
11496fd7ce5SGreg Kroah-Hartman
115a501ab75SJiri Slaby if (tty->flow.stopped || !c)
11696fd7ce5SGreg Kroah-Hartman return 0;
11796fd7ce5SGreg Kroah-Hartman
118a501ab75SJiri Slaby return tty_insert_flip_string_and_push_buffer(to->port, buf, c);
11996fd7ce5SGreg Kroah-Hartman }
12096fd7ce5SGreg Kroah-Hartman
12196fd7ce5SGreg Kroah-Hartman /**
12296fd7ce5SGreg Kroah-Hartman * pty_write_room - write space
12396fd7ce5SGreg Kroah-Hartman * @tty: tty we are writing from
12496fd7ce5SGreg Kroah-Hartman *
12596fd7ce5SGreg Kroah-Hartman * Report how many bytes the ldisc can send into the queue for
12696fd7ce5SGreg Kroah-Hartman * the other device.
12796fd7ce5SGreg Kroah-Hartman */
12896fd7ce5SGreg Kroah-Hartman
pty_write_room(struct tty_struct * tty)12903b3b1a2SJiri Slaby static unsigned int pty_write_room(struct tty_struct *tty)
13096fd7ce5SGreg Kroah-Hartman {
1316e94dbc7SJiri Slaby if (tty->flow.stopped)
13296fd7ce5SGreg Kroah-Hartman return 0;
133c81622acSPeter Hurley return tty_buffer_space_avail(tty->link->port);
13496fd7ce5SGreg Kroah-Hartman }
13596fd7ce5SGreg Kroah-Hartman
13696fd7ce5SGreg Kroah-Hartman /* Set the lock flag on a pty */
pty_set_lock(struct tty_struct * tty,int __user * arg)13796fd7ce5SGreg Kroah-Hartman static int pty_set_lock(struct tty_struct *tty, int __user *arg)
13896fd7ce5SGreg Kroah-Hartman {
13996fd7ce5SGreg Kroah-Hartman int val;
140202680c7SXiaofei Tan
14196fd7ce5SGreg Kroah-Hartman if (get_user(val, arg))
14296fd7ce5SGreg Kroah-Hartman return -EFAULT;
14396fd7ce5SGreg Kroah-Hartman if (val)
14496fd7ce5SGreg Kroah-Hartman set_bit(TTY_PTY_LOCK, &tty->flags);
14596fd7ce5SGreg Kroah-Hartman else
14696fd7ce5SGreg Kroah-Hartman clear_bit(TTY_PTY_LOCK, &tty->flags);
14796fd7ce5SGreg Kroah-Hartman return 0;
14896fd7ce5SGreg Kroah-Hartman }
14996fd7ce5SGreg Kroah-Hartman
pty_get_lock(struct tty_struct * tty,int __user * arg)15084fd7bdfSCyrill Gorcunov static int pty_get_lock(struct tty_struct *tty, int __user *arg)
15184fd7bdfSCyrill Gorcunov {
15284fd7bdfSCyrill Gorcunov int locked = test_bit(TTY_PTY_LOCK, &tty->flags);
153202680c7SXiaofei Tan
15484fd7bdfSCyrill Gorcunov return put_user(locked, arg);
15584fd7bdfSCyrill Gorcunov }
15684fd7bdfSCyrill Gorcunov
15706026d91SCyrill Gorcunov /* Set the packet mode on a pty */
pty_set_pktmode(struct tty_struct * tty,int __user * arg)15806026d91SCyrill Gorcunov static int pty_set_pktmode(struct tty_struct *tty, int __user *arg)
15906026d91SCyrill Gorcunov {
16006026d91SCyrill Gorcunov int pktmode;
16106026d91SCyrill Gorcunov
16206026d91SCyrill Gorcunov if (get_user(pktmode, arg))
16306026d91SCyrill Gorcunov return -EFAULT;
16406026d91SCyrill Gorcunov
16564d608dbSJiri Slaby spin_lock_irq(&tty->ctrl.lock);
16606026d91SCyrill Gorcunov if (pktmode) {
16764d608dbSJiri Slaby if (!tty->ctrl.packet) {
16864d608dbSJiri Slaby tty->link->ctrl.pktstatus = 0;
1692622d73eSPeter Hurley smp_mb();
17064d608dbSJiri Slaby tty->ctrl.packet = true;
17106026d91SCyrill Gorcunov }
17206026d91SCyrill Gorcunov } else
17364d608dbSJiri Slaby tty->ctrl.packet = false;
17464d608dbSJiri Slaby spin_unlock_irq(&tty->ctrl.lock);
17506026d91SCyrill Gorcunov
17606026d91SCyrill Gorcunov return 0;
17706026d91SCyrill Gorcunov }
17806026d91SCyrill Gorcunov
17984fd7bdfSCyrill Gorcunov /* Get the packet mode of a pty */
pty_get_pktmode(struct tty_struct * tty,int __user * arg)18084fd7bdfSCyrill Gorcunov static int pty_get_pktmode(struct tty_struct *tty, int __user *arg)
18184fd7bdfSCyrill Gorcunov {
18264d608dbSJiri Slaby int pktmode = tty->ctrl.packet;
183202680c7SXiaofei Tan
18484fd7bdfSCyrill Gorcunov return put_user(pktmode, arg);
18584fd7bdfSCyrill Gorcunov }
18684fd7bdfSCyrill Gorcunov
18796fd7ce5SGreg Kroah-Hartman /* Send a signal to the slave */
pty_signal(struct tty_struct * tty,int sig)18896fd7ce5SGreg Kroah-Hartman static int pty_signal(struct tty_struct *tty, int sig)
18996fd7ce5SGreg Kroah-Hartman {
19096fd7ce5SGreg Kroah-Hartman struct pid *pgrp;
19196fd7ce5SGreg Kroah-Hartman
19237480a05SPeter Hurley if (sig != SIGINT && sig != SIGQUIT && sig != SIGTSTP)
19337480a05SPeter Hurley return -EINVAL;
19437480a05SPeter Hurley
19596fd7ce5SGreg Kroah-Hartman if (tty->link) {
1965b239542SPeter Hurley pgrp = tty_get_pgrp(tty->link);
1975b239542SPeter Hurley if (pgrp)
19896fd7ce5SGreg Kroah-Hartman kill_pgrp(pgrp, sig, 1);
19996fd7ce5SGreg Kroah-Hartman put_pid(pgrp);
20096fd7ce5SGreg Kroah-Hartman }
20196fd7ce5SGreg Kroah-Hartman return 0;
20296fd7ce5SGreg Kroah-Hartman }
20396fd7ce5SGreg Kroah-Hartman
pty_flush_buffer(struct tty_struct * tty)20496fd7ce5SGreg Kroah-Hartman static void pty_flush_buffer(struct tty_struct *tty)
20596fd7ce5SGreg Kroah-Hartman {
20696fd7ce5SGreg Kroah-Hartman struct tty_struct *to = tty->link;
20796fd7ce5SGreg Kroah-Hartman
20896fd7ce5SGreg Kroah-Hartman if (!to)
20996fd7ce5SGreg Kroah-Hartman return;
2101d1d14daSPeter Hurley
21177dae613SWang YanQing tty_buffer_flush(to, NULL);
21264d608dbSJiri Slaby if (to->ctrl.packet) {
21364d608dbSJiri Slaby spin_lock_irq(&tty->ctrl.lock);
21464d608dbSJiri Slaby tty->ctrl.pktstatus |= TIOCPKT_FLUSHWRITE;
21596fd7ce5SGreg Kroah-Hartman wake_up_interruptible(&to->read_wait);
21664d608dbSJiri Slaby spin_unlock_irq(&tty->ctrl.lock);
21796fd7ce5SGreg Kroah-Hartman }
21896fd7ce5SGreg Kroah-Hartman }
21996fd7ce5SGreg Kroah-Hartman
pty_open(struct tty_struct * tty,struct file * filp)22096fd7ce5SGreg Kroah-Hartman static int pty_open(struct tty_struct *tty, struct file *filp)
22196fd7ce5SGreg Kroah-Hartman {
22296fd7ce5SGreg Kroah-Hartman if (!tty || !tty->link)
2237c61c3d8SPeter Hurley return -ENODEV;
22496fd7ce5SGreg Kroah-Hartman
22596fd7ce5SGreg Kroah-Hartman if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
22696fd7ce5SGreg Kroah-Hartman goto out;
22796fd7ce5SGreg Kroah-Hartman if (test_bit(TTY_PTY_LOCK, &tty->link->flags))
22896fd7ce5SGreg Kroah-Hartman goto out;
22980cace72SPeter Hurley if (tty->driver->subtype == PTY_TYPE_SLAVE && tty->link->count != 1)
23096fd7ce5SGreg Kroah-Hartman goto out;
23196fd7ce5SGreg Kroah-Hartman
23269939035SPeter Hurley clear_bit(TTY_IO_ERROR, &tty->flags);
23396fd7ce5SGreg Kroah-Hartman clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
23496fd7ce5SGreg Kroah-Hartman set_bit(TTY_THROTTLED, &tty->flags);
2357c61c3d8SPeter Hurley return 0;
2367c61c3d8SPeter Hurley
23796fd7ce5SGreg Kroah-Hartman out:
2387c61c3d8SPeter Hurley set_bit(TTY_IO_ERROR, &tty->flags);
2397c61c3d8SPeter Hurley return -EIO;
24096fd7ce5SGreg Kroah-Hartman }
24196fd7ce5SGreg Kroah-Hartman
pty_set_termios(struct tty_struct * tty,const struct ktermios * old_termios)24296fd7ce5SGreg Kroah-Hartman static void pty_set_termios(struct tty_struct *tty,
243a8c11c15SIlpo Järvinen const struct ktermios *old_termios)
24496fd7ce5SGreg Kroah-Hartman {
245dbfcd851SPeter Hurley /* See if packet mode change of state. */
24664d608dbSJiri Slaby if (tty->link && tty->link->ctrl.packet) {
2479db276f8SPeter Hurley int extproc = (old_termios->c_lflag & EXTPROC) | L_EXTPROC(tty);
248dbfcd851SPeter Hurley int old_flow = ((old_termios->c_iflag & IXON) &&
249dbfcd851SPeter Hurley (old_termios->c_cc[VSTOP] == '\023') &&
250dbfcd851SPeter Hurley (old_termios->c_cc[VSTART] == '\021'));
251dbfcd851SPeter Hurley int new_flow = (I_IXON(tty) &&
252dbfcd851SPeter Hurley STOP_CHAR(tty) == '\023' &&
253dbfcd851SPeter Hurley START_CHAR(tty) == '\021');
254dbfcd851SPeter Hurley if ((old_flow != new_flow) || extproc) {
25564d608dbSJiri Slaby spin_lock_irq(&tty->ctrl.lock);
256dbfcd851SPeter Hurley if (old_flow != new_flow) {
25764d608dbSJiri Slaby tty->ctrl.pktstatus &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
258dbfcd851SPeter Hurley if (new_flow)
25964d608dbSJiri Slaby tty->ctrl.pktstatus |= TIOCPKT_DOSTOP;
260dbfcd851SPeter Hurley else
26164d608dbSJiri Slaby tty->ctrl.pktstatus |= TIOCPKT_NOSTOP;
262dbfcd851SPeter Hurley }
263dbfcd851SPeter Hurley if (extproc)
26464d608dbSJiri Slaby tty->ctrl.pktstatus |= TIOCPKT_IOCTL;
26564d608dbSJiri Slaby spin_unlock_irq(&tty->ctrl.lock);
266dbfcd851SPeter Hurley wake_up_interruptible(&tty->link->read_wait);
267dbfcd851SPeter Hurley }
268dbfcd851SPeter Hurley }
269dbfcd851SPeter Hurley
270adc8d746SAlan Cox tty->termios.c_cflag &= ~(CSIZE | PARENB);
271adc8d746SAlan Cox tty->termios.c_cflag |= (CS8 | CREAD);
27296fd7ce5SGreg Kroah-Hartman }
27396fd7ce5SGreg Kroah-Hartman
27496fd7ce5SGreg Kroah-Hartman /**
275f6038cf4SYang Yingliang * pty_resize - resize event
27696fd7ce5SGreg Kroah-Hartman * @tty: tty being resized
27796fd7ce5SGreg Kroah-Hartman * @ws: window size being set.
27896fd7ce5SGreg Kroah-Hartman *
27996fd7ce5SGreg Kroah-Hartman * Update the termios variables and send the necessary signals to
28096fd7ce5SGreg Kroah-Hartman * peform a terminal resize correctly
28196fd7ce5SGreg Kroah-Hartman */
28296fd7ce5SGreg Kroah-Hartman
pty_resize(struct tty_struct * tty,struct winsize * ws)283159a8e92SJosh Triplett static int pty_resize(struct tty_struct *tty, struct winsize *ws)
28496fd7ce5SGreg Kroah-Hartman {
28596fd7ce5SGreg Kroah-Hartman struct pid *pgrp, *rpgrp;
28696fd7ce5SGreg Kroah-Hartman struct tty_struct *pty = tty->link;
28796fd7ce5SGreg Kroah-Hartman
28896fd7ce5SGreg Kroah-Hartman /* For a PTY we need to lock the tty side */
289dee4a0beSPeter Hurley mutex_lock(&tty->winsize_mutex);
29096fd7ce5SGreg Kroah-Hartman if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
29196fd7ce5SGreg Kroah-Hartman goto done;
29296fd7ce5SGreg Kroah-Hartman
2935b239542SPeter Hurley /* Signal the foreground process group of both ptys */
2945b239542SPeter Hurley pgrp = tty_get_pgrp(tty);
2955b239542SPeter Hurley rpgrp = tty_get_pgrp(pty);
29696fd7ce5SGreg Kroah-Hartman
29796fd7ce5SGreg Kroah-Hartman if (pgrp)
29896fd7ce5SGreg Kroah-Hartman kill_pgrp(pgrp, SIGWINCH, 1);
29996fd7ce5SGreg Kroah-Hartman if (rpgrp != pgrp && rpgrp)
30096fd7ce5SGreg Kroah-Hartman kill_pgrp(rpgrp, SIGWINCH, 1);
30196fd7ce5SGreg Kroah-Hartman
30296fd7ce5SGreg Kroah-Hartman put_pid(pgrp);
30396fd7ce5SGreg Kroah-Hartman put_pid(rpgrp);
30496fd7ce5SGreg Kroah-Hartman
30596fd7ce5SGreg Kroah-Hartman tty->winsize = *ws;
30696fd7ce5SGreg Kroah-Hartman pty->winsize = *ws; /* Never used so will go away soon */
30796fd7ce5SGreg Kroah-Hartman done:
308dee4a0beSPeter Hurley mutex_unlock(&tty->winsize_mutex);
30996fd7ce5SGreg Kroah-Hartman return 0;
31096fd7ce5SGreg Kroah-Hartman }
31196fd7ce5SGreg Kroah-Hartman
312d155255aSAlan Cox /**
31301adc807SPeter Hurley * pty_start - start() handler
31401adc807SPeter Hurley * pty_stop - stop() handler
31501adc807SPeter Hurley * @tty: tty being flow-controlled
31601adc807SPeter Hurley *
31701adc807SPeter Hurley * Propagates the TIOCPKT status to the master pty.
31801adc807SPeter Hurley *
31901adc807SPeter Hurley * NB: only the master pty can be in packet mode so only the slave
32001adc807SPeter Hurley * needs start()/stop() handlers
32101adc807SPeter Hurley */
pty_start(struct tty_struct * tty)32201adc807SPeter Hurley static void pty_start(struct tty_struct *tty)
32301adc807SPeter Hurley {
32401adc807SPeter Hurley unsigned long flags;
32501adc807SPeter Hurley
32664d608dbSJiri Slaby if (tty->link && tty->link->ctrl.packet) {
32764d608dbSJiri Slaby spin_lock_irqsave(&tty->ctrl.lock, flags);
32864d608dbSJiri Slaby tty->ctrl.pktstatus &= ~TIOCPKT_STOP;
32964d608dbSJiri Slaby tty->ctrl.pktstatus |= TIOCPKT_START;
33064d608dbSJiri Slaby spin_unlock_irqrestore(&tty->ctrl.lock, flags);
331a9a08845SLinus Torvalds wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
33201adc807SPeter Hurley }
33301adc807SPeter Hurley }
33401adc807SPeter Hurley
pty_stop(struct tty_struct * tty)33501adc807SPeter Hurley static void pty_stop(struct tty_struct *tty)
33601adc807SPeter Hurley {
33701adc807SPeter Hurley unsigned long flags;
33801adc807SPeter Hurley
33964d608dbSJiri Slaby if (tty->link && tty->link->ctrl.packet) {
34064d608dbSJiri Slaby spin_lock_irqsave(&tty->ctrl.lock, flags);
34164d608dbSJiri Slaby tty->ctrl.pktstatus &= ~TIOCPKT_START;
34264d608dbSJiri Slaby tty->ctrl.pktstatus |= TIOCPKT_STOP;
34364d608dbSJiri Slaby spin_unlock_irqrestore(&tty->ctrl.lock, flags);
344a9a08845SLinus Torvalds wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
34501adc807SPeter Hurley }
34601adc807SPeter Hurley }
34701adc807SPeter Hurley
34801adc807SPeter Hurley /**
349d155255aSAlan Cox * pty_common_install - set up the pty pair
350d155255aSAlan Cox * @driver: the pty driver
351d155255aSAlan Cox * @tty: the tty being instantiated
3522c964a2fSRasmus Villemoes * @legacy: true if this is BSD style
353d155255aSAlan Cox *
354d155255aSAlan Cox * Perform the initial set up for the tty/pty pair. Called from the
355d155255aSAlan Cox * tty layer when the port is first opened.
356d155255aSAlan Cox *
357d155255aSAlan Cox * Locking: the caller must hold the tty_mutex
358d155255aSAlan Cox */
pty_common_install(struct tty_driver * driver,struct tty_struct * tty,bool legacy)3595d249bc6SJiri Slaby static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
3605d249bc6SJiri Slaby bool legacy)
36196fd7ce5SGreg Kroah-Hartman {
36296fd7ce5SGreg Kroah-Hartman struct tty_struct *o_tty;
363d03702a2SJiri Slaby struct tty_port *ports[2];
36496fd7ce5SGreg Kroah-Hartman int idx = tty->index;
3655d249bc6SJiri Slaby int retval = -ENOMEM;
36696fd7ce5SGreg Kroah-Hartman
36755199ea3SPeter Hurley /* Opening the slave first has always returned -EIO */
36855199ea3SPeter Hurley if (driver->subtype != PTY_TYPE_MASTER)
36955199ea3SPeter Hurley return -EIO;
37055199ea3SPeter Hurley
371d03702a2SJiri Slaby ports[0] = kmalloc(sizeof **ports, GFP_KERNEL);
372d03702a2SJiri Slaby ports[1] = kmalloc(sizeof **ports, GFP_KERNEL);
3736f9ea7adSJiri Slaby if (!ports[0] || !ports[1])
3742c964a2fSRasmus Villemoes goto err;
37596fd7ce5SGreg Kroah-Hartman if (!try_module_get(driver->other->owner)) {
37696fd7ce5SGreg Kroah-Hartman /* This cannot in fact currently happen */
3772c964a2fSRasmus Villemoes goto err;
37896fd7ce5SGreg Kroah-Hartman }
3792c964a2fSRasmus Villemoes o_tty = alloc_tty_struct(driver->other, idx);
3802c964a2fSRasmus Villemoes if (!o_tty)
3812c964a2fSRasmus Villemoes goto err_put_module;
38296fd7ce5SGreg Kroah-Hartman
3832febdb63SPeter Hurley tty_set_lock_subclass(o_tty);
384d2b6f447SPeter Hurley lockdep_set_subclass(&o_tty->termios_rwsem, TTY_LOCK_SLAVE);
3852febdb63SPeter Hurley
3865d249bc6SJiri Slaby if (legacy) {
38796fd7ce5SGreg Kroah-Hartman /* We always use new tty termios data so we can do this
38896fd7ce5SGreg Kroah-Hartman the easy way .. */
389a3123fd0SPeter Hurley tty_init_termios(tty);
390a3123fd0SPeter Hurley tty_init_termios(o_tty);
39196fd7ce5SGreg Kroah-Hartman
3925d249bc6SJiri Slaby driver->other->ttys[idx] = o_tty;
3935d249bc6SJiri Slaby driver->ttys[idx] = tty;
3945d249bc6SJiri Slaby } else {
395adc8d746SAlan Cox memset(&tty->termios_locked, 0, sizeof(tty->termios_locked));
396adc8d746SAlan Cox tty->termios = driver->init_termios;
397adc8d746SAlan Cox memset(&o_tty->termios_locked, 0, sizeof(tty->termios_locked));
398adc8d746SAlan Cox o_tty->termios = driver->other->init_termios;
3995d249bc6SJiri Slaby }
4005d249bc6SJiri Slaby
40196fd7ce5SGreg Kroah-Hartman /*
40296fd7ce5SGreg Kroah-Hartman * Everything allocated ... set up the o_tty structure.
40396fd7ce5SGreg Kroah-Hartman */
40496fd7ce5SGreg Kroah-Hartman tty_driver_kref_get(driver->other);
40596fd7ce5SGreg Kroah-Hartman /* Establish the links in both directions */
40696fd7ce5SGreg Kroah-Hartman tty->link = o_tty;
40796fd7ce5SGreg Kroah-Hartman o_tty->link = tty;
408d03702a2SJiri Slaby tty_port_init(ports[0]);
409d03702a2SJiri Slaby tty_port_init(ports[1]);
410c81622acSPeter Hurley tty_buffer_set_limit(ports[0], 8192);
411c81622acSPeter Hurley tty_buffer_set_limit(ports[1], 8192);
412d03702a2SJiri Slaby o_tty->port = ports[0];
413d03702a2SJiri Slaby tty->port = ports[1];
414967fab69SJiri Slaby o_tty->port->itty = o_tty;
41596fd7ce5SGreg Kroah-Hartman
4161d1d14daSPeter Hurley tty_buffer_set_lock_subclass(o_tty->port);
4171d1d14daSPeter Hurley
41896fd7ce5SGreg Kroah-Hartman tty_driver_kref_get(driver);
41996fd7ce5SGreg Kroah-Hartman tty->count++;
42055199ea3SPeter Hurley o_tty->count++;
42196fd7ce5SGreg Kroah-Hartman return 0;
422a3123fd0SPeter Hurley
4232c964a2fSRasmus Villemoes err_put_module:
42407584d4aSRasmus Villemoes module_put(driver->other->owner);
4252c964a2fSRasmus Villemoes err:
426d03702a2SJiri Slaby kfree(ports[0]);
427d03702a2SJiri Slaby kfree(ports[1]);
4288a1b8d70SJiri Slaby return retval;
42996fd7ce5SGreg Kroah-Hartman }
43096fd7ce5SGreg Kroah-Hartman
pty_cleanup(struct tty_struct * tty)431d03702a2SJiri Slaby static void pty_cleanup(struct tty_struct *tty)
432d03702a2SJiri Slaby {
43381c79838SJiri Slaby tty_port_put(tty->port);
434d03702a2SJiri Slaby }
435d03702a2SJiri Slaby
4365d249bc6SJiri Slaby /* Traditional BSD devices */
4375d249bc6SJiri Slaby #ifdef CONFIG_LEGACY_PTYS
4385d249bc6SJiri Slaby
pty_install(struct tty_driver * driver,struct tty_struct * tty)4395d249bc6SJiri Slaby static int pty_install(struct tty_driver *driver, struct tty_struct *tty)
4405d249bc6SJiri Slaby {
4415d249bc6SJiri Slaby return pty_common_install(driver, tty, true);
4425d249bc6SJiri Slaby }
4435d249bc6SJiri Slaby
pty_remove(struct tty_driver * driver,struct tty_struct * tty)444d155255aSAlan Cox static void pty_remove(struct tty_driver *driver, struct tty_struct *tty)
445d155255aSAlan Cox {
446d155255aSAlan Cox struct tty_struct *pair = tty->link;
447202680c7SXiaofei Tan
448d155255aSAlan Cox driver->ttys[tty->index] = NULL;
449d155255aSAlan Cox if (pair)
450d155255aSAlan Cox pair->driver->ttys[pair->index] = NULL;
451d155255aSAlan Cox }
452d155255aSAlan Cox
pty_bsd_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)4536caa76b7SAlan Cox static int pty_bsd_ioctl(struct tty_struct *tty,
45496fd7ce5SGreg Kroah-Hartman unsigned int cmd, unsigned long arg)
45596fd7ce5SGreg Kroah-Hartman {
45696fd7ce5SGreg Kroah-Hartman switch (cmd) {
45796fd7ce5SGreg Kroah-Hartman case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
45896fd7ce5SGreg Kroah-Hartman return pty_set_lock(tty, (int __user *) arg);
45984fd7bdfSCyrill Gorcunov case TIOCGPTLCK: /* Get PT Lock status */
46084fd7bdfSCyrill Gorcunov return pty_get_lock(tty, (int __user *)arg);
46106026d91SCyrill Gorcunov case TIOCPKT: /* Set PT packet mode */
46206026d91SCyrill Gorcunov return pty_set_pktmode(tty, (int __user *)arg);
46384fd7bdfSCyrill Gorcunov case TIOCGPKT: /* Get PT packet mode */
46484fd7bdfSCyrill Gorcunov return pty_get_pktmode(tty, (int __user *)arg);
46596fd7ce5SGreg Kroah-Hartman case TIOCSIG: /* Send signal to other side of pty */
46696fd7ce5SGreg Kroah-Hartman return pty_signal(tty, (int) arg);
467ded2f295SJiri Slaby case TIOCGPTN: /* TTY returns ENOTTY, but glibc expects EINVAL here */
468ded2f295SJiri Slaby return -EINVAL;
46996fd7ce5SGreg Kroah-Hartman }
47096fd7ce5SGreg Kroah-Hartman return -ENOIOCTLCMD;
47196fd7ce5SGreg Kroah-Hartman }
47296fd7ce5SGreg Kroah-Hartman
47350f45326SAl Viro #ifdef CONFIG_COMPAT
pty_bsd_compat_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)4745f0f187fSAleksa Sarai static long pty_bsd_compat_ioctl(struct tty_struct *tty,
4755f0f187fSAleksa Sarai unsigned int cmd, unsigned long arg)
4765f0f187fSAleksa Sarai {
4775f0f187fSAleksa Sarai /*
4785f0f187fSAleksa Sarai * PTY ioctls don't require any special translation between 32-bit and
4795f0f187fSAleksa Sarai * 64-bit userspace, they are already compatible.
4805f0f187fSAleksa Sarai */
48150f45326SAl Viro return pty_bsd_ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
4825f0f187fSAleksa Sarai }
48350f45326SAl Viro #else
48450f45326SAl Viro #define pty_bsd_compat_ioctl NULL
48550f45326SAl Viro #endif
4865f0f187fSAleksa Sarai
48796fd7ce5SGreg Kroah-Hartman static int legacy_count = CONFIG_LEGACY_PTY_COUNT;
4887154988fSPaul Gortmaker /*
4897154988fSPaul Gortmaker * not really modular, but the easiest way to keep compat with existing
4907154988fSPaul Gortmaker * bootargs behaviour is to continue using module_param here.
4917154988fSPaul Gortmaker */
49296fd7ce5SGreg Kroah-Hartman module_param(legacy_count, int, 0);
49396fd7ce5SGreg Kroah-Hartman
49496fd7ce5SGreg Kroah-Hartman /*
49596fd7ce5SGreg Kroah-Hartman * The master side of a pty can do TIOCSPTLCK and thus
49696fd7ce5SGreg Kroah-Hartman * has pty_bsd_ioctl.
49796fd7ce5SGreg Kroah-Hartman */
49896fd7ce5SGreg Kroah-Hartman static const struct tty_operations master_pty_ops_bsd = {
49996fd7ce5SGreg Kroah-Hartman .install = pty_install,
50096fd7ce5SGreg Kroah-Hartman .open = pty_open,
50196fd7ce5SGreg Kroah-Hartman .close = pty_close,
50296fd7ce5SGreg Kroah-Hartman .write = pty_write,
50396fd7ce5SGreg Kroah-Hartman .write_room = pty_write_room,
50496fd7ce5SGreg Kroah-Hartman .flush_buffer = pty_flush_buffer,
50596fd7ce5SGreg Kroah-Hartman .unthrottle = pty_unthrottle,
50696fd7ce5SGreg Kroah-Hartman .ioctl = pty_bsd_ioctl,
5075f0f187fSAleksa Sarai .compat_ioctl = pty_bsd_compat_ioctl,
508d03702a2SJiri Slaby .cleanup = pty_cleanup,
509d155255aSAlan Cox .resize = pty_resize,
510d155255aSAlan Cox .remove = pty_remove
51196fd7ce5SGreg Kroah-Hartman };
51296fd7ce5SGreg Kroah-Hartman
51396fd7ce5SGreg Kroah-Hartman static const struct tty_operations slave_pty_ops_bsd = {
51496fd7ce5SGreg Kroah-Hartman .install = pty_install,
51596fd7ce5SGreg Kroah-Hartman .open = pty_open,
51696fd7ce5SGreg Kroah-Hartman .close = pty_close,
51796fd7ce5SGreg Kroah-Hartman .write = pty_write,
51896fd7ce5SGreg Kroah-Hartman .write_room = pty_write_room,
51996fd7ce5SGreg Kroah-Hartman .flush_buffer = pty_flush_buffer,
52096fd7ce5SGreg Kroah-Hartman .unthrottle = pty_unthrottle,
52196fd7ce5SGreg Kroah-Hartman .set_termios = pty_set_termios,
522d03702a2SJiri Slaby .cleanup = pty_cleanup,
523d155255aSAlan Cox .resize = pty_resize,
52401adc807SPeter Hurley .start = pty_start,
52501adc807SPeter Hurley .stop = pty_stop,
526d155255aSAlan Cox .remove = pty_remove
52796fd7ce5SGreg Kroah-Hartman };
52896fd7ce5SGreg Kroah-Hartman
legacy_pty_init(void)52996fd7ce5SGreg Kroah-Hartman static void __init legacy_pty_init(void)
53096fd7ce5SGreg Kroah-Hartman {
53196fd7ce5SGreg Kroah-Hartman struct tty_driver *pty_driver, *pty_slave_driver;
53296fd7ce5SGreg Kroah-Hartman
53396fd7ce5SGreg Kroah-Hartman if (legacy_count <= 0)
53496fd7ce5SGreg Kroah-Hartman return;
53596fd7ce5SGreg Kroah-Hartman
53621aca2faSJiri Slaby pty_driver = tty_alloc_driver(legacy_count,
53721aca2faSJiri Slaby TTY_DRIVER_RESET_TERMIOS |
53821aca2faSJiri Slaby TTY_DRIVER_REAL_RAW |
53921aca2faSJiri Slaby TTY_DRIVER_DYNAMIC_ALLOC);
540c3a6344aSDan Carpenter if (IS_ERR(pty_driver))
54196fd7ce5SGreg Kroah-Hartman panic("Couldn't allocate pty driver");
54296fd7ce5SGreg Kroah-Hartman
54321aca2faSJiri Slaby pty_slave_driver = tty_alloc_driver(legacy_count,
54421aca2faSJiri Slaby TTY_DRIVER_RESET_TERMIOS |
54521aca2faSJiri Slaby TTY_DRIVER_REAL_RAW |
54621aca2faSJiri Slaby TTY_DRIVER_DYNAMIC_ALLOC);
547c3a6344aSDan Carpenter if (IS_ERR(pty_slave_driver))
54896fd7ce5SGreg Kroah-Hartman panic("Couldn't allocate pty slave driver");
54996fd7ce5SGreg Kroah-Hartman
55096fd7ce5SGreg Kroah-Hartman pty_driver->driver_name = "pty_master";
55196fd7ce5SGreg Kroah-Hartman pty_driver->name = "pty";
55296fd7ce5SGreg Kroah-Hartman pty_driver->major = PTY_MASTER_MAJOR;
55396fd7ce5SGreg Kroah-Hartman pty_driver->minor_start = 0;
55496fd7ce5SGreg Kroah-Hartman pty_driver->type = TTY_DRIVER_TYPE_PTY;
55596fd7ce5SGreg Kroah-Hartman pty_driver->subtype = PTY_TYPE_MASTER;
55696fd7ce5SGreg Kroah-Hartman pty_driver->init_termios = tty_std_termios;
55796fd7ce5SGreg Kroah-Hartman pty_driver->init_termios.c_iflag = 0;
55896fd7ce5SGreg Kroah-Hartman pty_driver->init_termios.c_oflag = 0;
55996fd7ce5SGreg Kroah-Hartman pty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
56096fd7ce5SGreg Kroah-Hartman pty_driver->init_termios.c_lflag = 0;
56196fd7ce5SGreg Kroah-Hartman pty_driver->init_termios.c_ispeed = 38400;
56296fd7ce5SGreg Kroah-Hartman pty_driver->init_termios.c_ospeed = 38400;
56396fd7ce5SGreg Kroah-Hartman pty_driver->other = pty_slave_driver;
56496fd7ce5SGreg Kroah-Hartman tty_set_operations(pty_driver, &master_pty_ops_bsd);
56596fd7ce5SGreg Kroah-Hartman
56696fd7ce5SGreg Kroah-Hartman pty_slave_driver->driver_name = "pty_slave";
56796fd7ce5SGreg Kroah-Hartman pty_slave_driver->name = "ttyp";
56896fd7ce5SGreg Kroah-Hartman pty_slave_driver->major = PTY_SLAVE_MAJOR;
56996fd7ce5SGreg Kroah-Hartman pty_slave_driver->minor_start = 0;
57096fd7ce5SGreg Kroah-Hartman pty_slave_driver->type = TTY_DRIVER_TYPE_PTY;
57196fd7ce5SGreg Kroah-Hartman pty_slave_driver->subtype = PTY_TYPE_SLAVE;
57296fd7ce5SGreg Kroah-Hartman pty_slave_driver->init_termios = tty_std_termios;
57396fd7ce5SGreg Kroah-Hartman pty_slave_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
57496fd7ce5SGreg Kroah-Hartman pty_slave_driver->init_termios.c_ispeed = 38400;
57596fd7ce5SGreg Kroah-Hartman pty_slave_driver->init_termios.c_ospeed = 38400;
57696fd7ce5SGreg Kroah-Hartman pty_slave_driver->other = pty_driver;
57796fd7ce5SGreg Kroah-Hartman tty_set_operations(pty_slave_driver, &slave_pty_ops_bsd);
57896fd7ce5SGreg Kroah-Hartman
57996fd7ce5SGreg Kroah-Hartman if (tty_register_driver(pty_driver))
58096fd7ce5SGreg Kroah-Hartman panic("Couldn't register pty driver");
58196fd7ce5SGreg Kroah-Hartman if (tty_register_driver(pty_slave_driver))
58296fd7ce5SGreg Kroah-Hartman panic("Couldn't register pty slave driver");
58396fd7ce5SGreg Kroah-Hartman }
58496fd7ce5SGreg Kroah-Hartman #else
legacy_pty_init(void)58596fd7ce5SGreg Kroah-Hartman static inline void legacy_pty_init(void) { }
58696fd7ce5SGreg Kroah-Hartman #endif
58796fd7ce5SGreg Kroah-Hartman
58896fd7ce5SGreg Kroah-Hartman /* Unix98 devices */
58996fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_UNIX98_PTYS
59096fd7ce5SGreg Kroah-Hartman static struct cdev ptmx_cdev;
59196fd7ce5SGreg Kroah-Hartman
5926509f309SArnd Bergmann /**
593311fc65cSEric W. Biederman * ptm_open_peer - open the peer of a pty
594311fc65cSEric W. Biederman * @master: the open struct file of the ptmx device node
595311fc65cSEric W. Biederman * @tty: the master of the pty being opened
596311fc65cSEric W. Biederman * @flags: the flags for open
5976509f309SArnd Bergmann *
598311fc65cSEric W. Biederman * Provide a race free way for userspace to open the slave end of a pty
599311fc65cSEric W. Biederman * (where they have the master fd and cannot access or trust the mount
600311fc65cSEric W. Biederman * namespace /dev/pts was mounted inside).
6016509f309SArnd Bergmann */
ptm_open_peer(struct file * master,struct tty_struct * tty,int flags)602311fc65cSEric W. Biederman int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags)
6036509f309SArnd Bergmann {
6044503b1c2SColin Ian King int fd;
605311fc65cSEric W. Biederman struct file *filp;
6066509f309SArnd Bergmann int retval = -EINVAL;
607311fc65cSEric W. Biederman struct path path;
608311fc65cSEric W. Biederman
609311fc65cSEric W. Biederman if (tty->driver != ptm_driver)
610311fc65cSEric W. Biederman return -EIO;
6116509f309SArnd Bergmann
61236ecc148SMatthijs van Duin fd = get_unused_fd_flags(flags);
6136509f309SArnd Bergmann if (fd < 0) {
6146509f309SArnd Bergmann retval = fd;
6156509f309SArnd Bergmann goto err;
6166509f309SArnd Bergmann }
6176509f309SArnd Bergmann
618311fc65cSEric W. Biederman /* Compute the slave's path */
619311fc65cSEric W. Biederman path.mnt = devpts_mntget(master, tty->driver_data);
620311fc65cSEric W. Biederman if (IS_ERR(path.mnt)) {
621311fc65cSEric W. Biederman retval = PTR_ERR(path.mnt);
622311fc65cSEric W. Biederman goto err_put;
623311fc65cSEric W. Biederman }
624311fc65cSEric W. Biederman path.dentry = tty->link->driver_data;
625311fc65cSEric W. Biederman
626311fc65cSEric W. Biederman filp = dentry_open(&path, flags, current_cred());
627311fc65cSEric W. Biederman mntput(path.mnt);
6286509f309SArnd Bergmann if (IS_ERR(filp)) {
6296509f309SArnd Bergmann retval = PTR_ERR(filp);
6306509f309SArnd Bergmann goto err_put;
6316509f309SArnd Bergmann }
6326509f309SArnd Bergmann
6336509f309SArnd Bergmann fd_install(fd, filp);
6346509f309SArnd Bergmann return fd;
6356509f309SArnd Bergmann
6366509f309SArnd Bergmann err_put:
6376509f309SArnd Bergmann put_unused_fd(fd);
6386509f309SArnd Bergmann err:
6396509f309SArnd Bergmann return retval;
6406509f309SArnd Bergmann }
6416509f309SArnd Bergmann
pty_unix98_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)6426caa76b7SAlan Cox static int pty_unix98_ioctl(struct tty_struct *tty,
64396fd7ce5SGreg Kroah-Hartman unsigned int cmd, unsigned long arg)
64496fd7ce5SGreg Kroah-Hartman {
64596fd7ce5SGreg Kroah-Hartman switch (cmd) {
64696fd7ce5SGreg Kroah-Hartman case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
64796fd7ce5SGreg Kroah-Hartman return pty_set_lock(tty, (int __user *)arg);
64884fd7bdfSCyrill Gorcunov case TIOCGPTLCK: /* Get PT Lock status */
64984fd7bdfSCyrill Gorcunov return pty_get_lock(tty, (int __user *)arg);
65006026d91SCyrill Gorcunov case TIOCPKT: /* Set PT packet mode */
65106026d91SCyrill Gorcunov return pty_set_pktmode(tty, (int __user *)arg);
65284fd7bdfSCyrill Gorcunov case TIOCGPKT: /* Get PT packet mode */
65384fd7bdfSCyrill Gorcunov return pty_get_pktmode(tty, (int __user *)arg);
65496fd7ce5SGreg Kroah-Hartman case TIOCGPTN: /* Get PT Number */
65596fd7ce5SGreg Kroah-Hartman return put_user(tty->index, (unsigned int __user *)arg);
65696fd7ce5SGreg Kroah-Hartman case TIOCSIG: /* Send signal to other side of pty */
65796fd7ce5SGreg Kroah-Hartman return pty_signal(tty, (int) arg);
65896fd7ce5SGreg Kroah-Hartman }
65996fd7ce5SGreg Kroah-Hartman
66096fd7ce5SGreg Kroah-Hartman return -ENOIOCTLCMD;
66196fd7ce5SGreg Kroah-Hartman }
66296fd7ce5SGreg Kroah-Hartman
66350f45326SAl Viro #ifdef CONFIG_COMPAT
pty_unix98_compat_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)6645f0f187fSAleksa Sarai static long pty_unix98_compat_ioctl(struct tty_struct *tty,
6655f0f187fSAleksa Sarai unsigned int cmd, unsigned long arg)
6665f0f187fSAleksa Sarai {
6675f0f187fSAleksa Sarai /*
6685f0f187fSAleksa Sarai * PTY ioctls don't require any special translation between 32-bit and
6695f0f187fSAleksa Sarai * 64-bit userspace, they are already compatible.
6705f0f187fSAleksa Sarai */
67150f45326SAl Viro return pty_unix98_ioctl(tty, cmd,
67250f45326SAl Viro cmd == TIOCSIG ? arg : (unsigned long)compat_ptr(arg));
6735f0f187fSAleksa Sarai }
67450f45326SAl Viro #else
67550f45326SAl Viro #define pty_unix98_compat_ioctl NULL
67650f45326SAl Viro #endif
6775f0f187fSAleksa Sarai
67896fd7ce5SGreg Kroah-Hartman /**
67996fd7ce5SGreg Kroah-Hartman * ptm_unix98_lookup - find a pty master
68096fd7ce5SGreg Kroah-Hartman * @driver: ptm driver
6810b0a66a5SLee Jones * @file: unused
68296fd7ce5SGreg Kroah-Hartman * @idx: tty index
68396fd7ce5SGreg Kroah-Hartman *
68496fd7ce5SGreg Kroah-Hartman * Look up a pty master device. Called under the tty_mutex for now.
68596fd7ce5SGreg Kroah-Hartman * This provides our locking.
68696fd7ce5SGreg Kroah-Hartman */
68796fd7ce5SGreg Kroah-Hartman
ptm_unix98_lookup(struct tty_driver * driver,struct file * file,int idx)68896fd7ce5SGreg Kroah-Hartman static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver,
6898ead9dd5SLinus Torvalds struct file *file, int idx)
69096fd7ce5SGreg Kroah-Hartman {
691593a27c4SKonstantin Khlebnikov /* Master must be open via /dev/ptmx */
692593a27c4SKonstantin Khlebnikov return ERR_PTR(-EIO);
69396fd7ce5SGreg Kroah-Hartman }
69496fd7ce5SGreg Kroah-Hartman
69596fd7ce5SGreg Kroah-Hartman /**
69696fd7ce5SGreg Kroah-Hartman * pts_unix98_lookup - find a pty slave
69796fd7ce5SGreg Kroah-Hartman * @driver: pts driver
6980b0a66a5SLee Jones * @file: file pointer to tty
69996fd7ce5SGreg Kroah-Hartman * @idx: tty index
70096fd7ce5SGreg Kroah-Hartman *
70196fd7ce5SGreg Kroah-Hartman * Look up a pty master device. Called under the tty_mutex for now.
702d739e65bSAlan Cox * This provides our locking for the tty pointer.
70396fd7ce5SGreg Kroah-Hartman */
70496fd7ce5SGreg Kroah-Hartman
pts_unix98_lookup(struct tty_driver * driver,struct file * file,int idx)70596fd7ce5SGreg Kroah-Hartman static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
7068ead9dd5SLinus Torvalds struct file *file, int idx)
70796fd7ce5SGreg Kroah-Hartman {
708d739e65bSAlan Cox struct tty_struct *tty;
709d739e65bSAlan Cox
710d739e65bSAlan Cox mutex_lock(&devpts_mutex);
7118ead9dd5SLinus Torvalds tty = devpts_get_priv(file->f_path.dentry);
712d739e65bSAlan Cox mutex_unlock(&devpts_mutex);
71396fd7ce5SGreg Kroah-Hartman /* Master must be open before slave */
71496fd7ce5SGreg Kroah-Hartman if (!tty)
71596fd7ce5SGreg Kroah-Hartman return ERR_PTR(-EIO);
71696fd7ce5SGreg Kroah-Hartman return tty;
71796fd7ce5SGreg Kroah-Hartman }
71896fd7ce5SGreg Kroah-Hartman
pty_unix98_install(struct tty_driver * driver,struct tty_struct * tty)71996fd7ce5SGreg Kroah-Hartman static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
72096fd7ce5SGreg Kroah-Hartman {
7215d249bc6SJiri Slaby return pty_common_install(driver, tty, false);
72296fd7ce5SGreg Kroah-Hartman }
72396fd7ce5SGreg Kroah-Hartman
724f4b208ebSJiri Slaby /* this is called once with whichever end is closed last */
pty_unix98_remove(struct tty_driver * driver,struct tty_struct * tty)725c1e33af1SPeter Hurley static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
726f4b208ebSJiri Slaby {
72767245ff3SLinus Torvalds struct pts_fs_info *fsi;
7282831c89fSHerton R. Krzesinski
7292831c89fSHerton R. Krzesinski if (tty->driver->subtype == PTY_TYPE_MASTER)
73067245ff3SLinus Torvalds fsi = tty->driver_data;
7312831c89fSHerton R. Krzesinski else
73267245ff3SLinus Torvalds fsi = tty->link->driver_data;
7335353ed8dSColin Ian King
7345353ed8dSColin Ian King if (fsi) {
73567245ff3SLinus Torvalds devpts_kill_index(fsi, tty->index);
736eedf265aSEric W. Biederman devpts_release(fsi);
737f4b208ebSJiri Slaby }
7385353ed8dSColin Ian King }
739f4b208ebSJiri Slaby
pty_show_fdinfo(struct tty_struct * tty,struct seq_file * m)740d01c3289SMasatake YAMATO static void pty_show_fdinfo(struct tty_struct *tty, struct seq_file *m)
741d01c3289SMasatake YAMATO {
742d01c3289SMasatake YAMATO seq_printf(m, "tty-index:\t%d\n", tty->index);
743d01c3289SMasatake YAMATO }
744d01c3289SMasatake YAMATO
74596fd7ce5SGreg Kroah-Hartman static const struct tty_operations ptm_unix98_ops = {
74696fd7ce5SGreg Kroah-Hartman .lookup = ptm_unix98_lookup,
74796fd7ce5SGreg Kroah-Hartman .install = pty_unix98_install,
7487171604aSJiri Slaby .remove = pty_unix98_remove,
74996fd7ce5SGreg Kroah-Hartman .open = pty_open,
75096fd7ce5SGreg Kroah-Hartman .close = pty_close,
75196fd7ce5SGreg Kroah-Hartman .write = pty_write,
75296fd7ce5SGreg Kroah-Hartman .write_room = pty_write_room,
75396fd7ce5SGreg Kroah-Hartman .flush_buffer = pty_flush_buffer,
75496fd7ce5SGreg Kroah-Hartman .unthrottle = pty_unthrottle,
75596fd7ce5SGreg Kroah-Hartman .ioctl = pty_unix98_ioctl,
7565f0f187fSAleksa Sarai .compat_ioctl = pty_unix98_compat_ioctl,
75736b3c070SAlan Cox .resize = pty_resize,
758d01c3289SMasatake YAMATO .cleanup = pty_cleanup,
759d01c3289SMasatake YAMATO .show_fdinfo = pty_show_fdinfo,
76096fd7ce5SGreg Kroah-Hartman };
76196fd7ce5SGreg Kroah-Hartman
76296fd7ce5SGreg Kroah-Hartman static const struct tty_operations pty_unix98_ops = {
76396fd7ce5SGreg Kroah-Hartman .lookup = pts_unix98_lookup,
76496fd7ce5SGreg Kroah-Hartman .install = pty_unix98_install,
7657171604aSJiri Slaby .remove = pty_unix98_remove,
76696fd7ce5SGreg Kroah-Hartman .open = pty_open,
76796fd7ce5SGreg Kroah-Hartman .close = pty_close,
76896fd7ce5SGreg Kroah-Hartman .write = pty_write,
76996fd7ce5SGreg Kroah-Hartman .write_room = pty_write_room,
77096fd7ce5SGreg Kroah-Hartman .flush_buffer = pty_flush_buffer,
77196fd7ce5SGreg Kroah-Hartman .unthrottle = pty_unthrottle,
77296fd7ce5SGreg Kroah-Hartman .set_termios = pty_set_termios,
77301adc807SPeter Hurley .start = pty_start,
77401adc807SPeter Hurley .stop = pty_stop,
775d03702a2SJiri Slaby .cleanup = pty_cleanup,
77696fd7ce5SGreg Kroah-Hartman };
77796fd7ce5SGreg Kroah-Hartman
77896fd7ce5SGreg Kroah-Hartman /**
77996fd7ce5SGreg Kroah-Hartman * ptmx_open - open a unix 98 pty master
78096fd7ce5SGreg Kroah-Hartman * @inode: inode of device file
78196fd7ce5SGreg Kroah-Hartman * @filp: file pointer to tty
78296fd7ce5SGreg Kroah-Hartman *
78396fd7ce5SGreg Kroah-Hartman * Allocate a unix98 pty master device from the ptmx driver.
78496fd7ce5SGreg Kroah-Hartman *
78596fd7ce5SGreg Kroah-Hartman * Locking: tty_mutex protects the init_dev work. tty->count should
78696fd7ce5SGreg Kroah-Hartman * protect the rest.
78796fd7ce5SGreg Kroah-Hartman * allocated_ptys_lock handles the list of free pty numbers
78896fd7ce5SGreg Kroah-Hartman */
78996fd7ce5SGreg Kroah-Hartman
ptmx_open(struct inode * inode,struct file * filp)79096fd7ce5SGreg Kroah-Hartman static int ptmx_open(struct inode *inode, struct file *filp)
79196fd7ce5SGreg Kroah-Hartman {
79267245ff3SLinus Torvalds struct pts_fs_info *fsi;
79396fd7ce5SGreg Kroah-Hartman struct tty_struct *tty;
7948ead9dd5SLinus Torvalds struct dentry *dentry;
79596fd7ce5SGreg Kroah-Hartman int retval;
79696fd7ce5SGreg Kroah-Hartman int index;
79796fd7ce5SGreg Kroah-Hartman
79896fd7ce5SGreg Kroah-Hartman nonseekable_open(inode, filp);
79996fd7ce5SGreg Kroah-Hartman
800b0b88565SLinus Torvalds /* We refuse fsnotify events on ptmx, since it's a shared resource */
801b0b88565SLinus Torvalds filp->f_mode |= FMODE_NONOTIFY;
802b0b88565SLinus Torvalds
803fa90e1c9SJiri Slaby retval = tty_alloc_file(filp);
804fa90e1c9SJiri Slaby if (retval)
805fa90e1c9SJiri Slaby return retval;
806fa90e1c9SJiri Slaby
807143c97ccSLinus Torvalds fsi = devpts_acquire(filp);
808eedf265aSEric W. Biederman if (IS_ERR(fsi)) {
809eedf265aSEric W. Biederman retval = PTR_ERR(fsi);
81067245ff3SLinus Torvalds goto out_free_file;
811eedf265aSEric W. Biederman }
81267245ff3SLinus Torvalds
81396fd7ce5SGreg Kroah-Hartman /* find a device that is not in use. */
81489c8d91eSAlan Cox mutex_lock(&devpts_mutex);
81567245ff3SLinus Torvalds index = devpts_new_index(fsi);
81605fb79e4SAlan Cox mutex_unlock(&devpts_mutex);
81796fd7ce5SGreg Kroah-Hartman
81867245ff3SLinus Torvalds retval = index;
81967245ff3SLinus Torvalds if (index < 0)
820eedf265aSEric W. Biederman goto out_put_fsi;
82167245ff3SLinus Torvalds
82289c8d91eSAlan Cox
82389c8d91eSAlan Cox mutex_lock(&tty_mutex);
82489c8d91eSAlan Cox tty = tty_init_dev(ptm_driver, index);
82589c8d91eSAlan Cox /* The tty returned here is locked so we can safely
82689c8d91eSAlan Cox drop the mutex */
82789c8d91eSAlan Cox mutex_unlock(&tty_mutex);
82889c8d91eSAlan Cox
82967245ff3SLinus Torvalds retval = PTR_ERR(tty);
83067245ff3SLinus Torvalds if (IS_ERR(tty))
83167245ff3SLinus Torvalds goto out;
83296fd7ce5SGreg Kroah-Hartman
8332831c89fSHerton R. Krzesinski /*
83467245ff3SLinus Torvalds * From here on out, the tty is "live", and the index and
83567245ff3SLinus Torvalds * fsi will be killed/put by the tty_release()
8362831c89fSHerton R. Krzesinski */
83767245ff3SLinus Torvalds set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
83867245ff3SLinus Torvalds tty->driver_data = fsi;
8392831c89fSHerton R. Krzesinski
840fa90e1c9SJiri Slaby tty_add_file(tty, filp);
84196fd7ce5SGreg Kroah-Hartman
8428ead9dd5SLinus Torvalds dentry = devpts_pty_new(fsi, index, tty->link);
8438ead9dd5SLinus Torvalds if (IS_ERR(dentry)) {
8448ead9dd5SLinus Torvalds retval = PTR_ERR(dentry);
8451177c0efSJiri Slaby goto err_release;
846162b97cfSJiri Slaby }
847311fc65cSEric W. Biederman tty->link->driver_data = dentry;
84896fd7ce5SGreg Kroah-Hartman
84996fd7ce5SGreg Kroah-Hartman retval = ptm_driver->ops->open(tty, filp);
85096fd7ce5SGreg Kroah-Hartman if (retval)
851311fc65cSEric W. Biederman goto err_release;
8521177c0efSJiri Slaby
853d435cefeSPeter Hurley tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
854d6847793SPeter Hurley
85589c8d91eSAlan Cox tty_unlock(tty);
8561177c0efSJiri Slaby return 0;
8571177c0efSJiri Slaby err_release:
85889c8d91eSAlan Cox tty_unlock(tty);
85967245ff3SLinus Torvalds // This will also put-ref the fsi
86096fd7ce5SGreg Kroah-Hartman tty_release(inode, filp);
86196fd7ce5SGreg Kroah-Hartman return retval;
86296fd7ce5SGreg Kroah-Hartman out:
86367245ff3SLinus Torvalds devpts_kill_index(fsi, index);
864eedf265aSEric W. Biederman out_put_fsi:
865eedf265aSEric W. Biederman devpts_release(fsi);
86667245ff3SLinus Torvalds out_free_file:
867fa90e1c9SJiri Slaby tty_free_file(filp);
86896fd7ce5SGreg Kroah-Hartman return retval;
86996fd7ce5SGreg Kroah-Hartman }
87096fd7ce5SGreg Kroah-Hartman
871d2ec3f77SKees Cook static struct file_operations ptmx_fops __ro_after_init;
87296fd7ce5SGreg Kroah-Hartman
unix98_pty_init(void)87396fd7ce5SGreg Kroah-Hartman static void __init unix98_pty_init(void)
87496fd7ce5SGreg Kroah-Hartman {
87521aca2faSJiri Slaby ptm_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX,
87621aca2faSJiri Slaby TTY_DRIVER_RESET_TERMIOS |
87721aca2faSJiri Slaby TTY_DRIVER_REAL_RAW |
87821aca2faSJiri Slaby TTY_DRIVER_DYNAMIC_DEV |
87921aca2faSJiri Slaby TTY_DRIVER_DEVPTS_MEM |
88021aca2faSJiri Slaby TTY_DRIVER_DYNAMIC_ALLOC);
881c3a6344aSDan Carpenter if (IS_ERR(ptm_driver))
88296fd7ce5SGreg Kroah-Hartman panic("Couldn't allocate Unix98 ptm driver");
88321aca2faSJiri Slaby pts_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX,
88421aca2faSJiri Slaby TTY_DRIVER_RESET_TERMIOS |
88521aca2faSJiri Slaby TTY_DRIVER_REAL_RAW |
88621aca2faSJiri Slaby TTY_DRIVER_DYNAMIC_DEV |
88721aca2faSJiri Slaby TTY_DRIVER_DEVPTS_MEM |
88821aca2faSJiri Slaby TTY_DRIVER_DYNAMIC_ALLOC);
889c3a6344aSDan Carpenter if (IS_ERR(pts_driver))
89096fd7ce5SGreg Kroah-Hartman panic("Couldn't allocate Unix98 pts driver");
89196fd7ce5SGreg Kroah-Hartman
89296fd7ce5SGreg Kroah-Hartman ptm_driver->driver_name = "pty_master";
89396fd7ce5SGreg Kroah-Hartman ptm_driver->name = "ptm";
89496fd7ce5SGreg Kroah-Hartman ptm_driver->major = UNIX98_PTY_MASTER_MAJOR;
89596fd7ce5SGreg Kroah-Hartman ptm_driver->minor_start = 0;
89696fd7ce5SGreg Kroah-Hartman ptm_driver->type = TTY_DRIVER_TYPE_PTY;
89796fd7ce5SGreg Kroah-Hartman ptm_driver->subtype = PTY_TYPE_MASTER;
89896fd7ce5SGreg Kroah-Hartman ptm_driver->init_termios = tty_std_termios;
89996fd7ce5SGreg Kroah-Hartman ptm_driver->init_termios.c_iflag = 0;
90096fd7ce5SGreg Kroah-Hartman ptm_driver->init_termios.c_oflag = 0;
90196fd7ce5SGreg Kroah-Hartman ptm_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
90296fd7ce5SGreg Kroah-Hartman ptm_driver->init_termios.c_lflag = 0;
90396fd7ce5SGreg Kroah-Hartman ptm_driver->init_termios.c_ispeed = 38400;
90496fd7ce5SGreg Kroah-Hartman ptm_driver->init_termios.c_ospeed = 38400;
90596fd7ce5SGreg Kroah-Hartman ptm_driver->other = pts_driver;
90696fd7ce5SGreg Kroah-Hartman tty_set_operations(ptm_driver, &ptm_unix98_ops);
90796fd7ce5SGreg Kroah-Hartman
90896fd7ce5SGreg Kroah-Hartman pts_driver->driver_name = "pty_slave";
90996fd7ce5SGreg Kroah-Hartman pts_driver->name = "pts";
91096fd7ce5SGreg Kroah-Hartman pts_driver->major = UNIX98_PTY_SLAVE_MAJOR;
91196fd7ce5SGreg Kroah-Hartman pts_driver->minor_start = 0;
91296fd7ce5SGreg Kroah-Hartman pts_driver->type = TTY_DRIVER_TYPE_PTY;
91396fd7ce5SGreg Kroah-Hartman pts_driver->subtype = PTY_TYPE_SLAVE;
91496fd7ce5SGreg Kroah-Hartman pts_driver->init_termios = tty_std_termios;
91596fd7ce5SGreg Kroah-Hartman pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
91696fd7ce5SGreg Kroah-Hartman pts_driver->init_termios.c_ispeed = 38400;
91796fd7ce5SGreg Kroah-Hartman pts_driver->init_termios.c_ospeed = 38400;
91896fd7ce5SGreg Kroah-Hartman pts_driver->other = ptm_driver;
91996fd7ce5SGreg Kroah-Hartman tty_set_operations(pts_driver, &pty_unix98_ops);
92096fd7ce5SGreg Kroah-Hartman
92196fd7ce5SGreg Kroah-Hartman if (tty_register_driver(ptm_driver))
92296fd7ce5SGreg Kroah-Hartman panic("Couldn't register Unix98 ptm driver");
92396fd7ce5SGreg Kroah-Hartman if (tty_register_driver(pts_driver))
92496fd7ce5SGreg Kroah-Hartman panic("Couldn't register Unix98 pts driver");
92596fd7ce5SGreg Kroah-Hartman
92696fd7ce5SGreg Kroah-Hartman /* Now create the /dev/ptmx special device */
92796fd7ce5SGreg Kroah-Hartman tty_default_fops(&ptmx_fops);
92896fd7ce5SGreg Kroah-Hartman ptmx_fops.open = ptmx_open;
92996fd7ce5SGreg Kroah-Hartman
93096fd7ce5SGreg Kroah-Hartman cdev_init(&ptmx_cdev, &ptmx_fops);
93196fd7ce5SGreg Kroah-Hartman if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
93296fd7ce5SGreg Kroah-Hartman register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
93382f8c35fSCong Ding panic("Couldn't register /dev/ptmx driver");
934862d8312SGreg Kroah-Hartman device_create(&tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
93596fd7ce5SGreg Kroah-Hartman }
93696fd7ce5SGreg Kroah-Hartman
93796fd7ce5SGreg Kroah-Hartman #else
unix98_pty_init(void)93896fd7ce5SGreg Kroah-Hartman static inline void unix98_pty_init(void) { }
93996fd7ce5SGreg Kroah-Hartman #endif
94096fd7ce5SGreg Kroah-Hartman
pty_init(void)94196fd7ce5SGreg Kroah-Hartman static int __init pty_init(void)
94296fd7ce5SGreg Kroah-Hartman {
94396fd7ce5SGreg Kroah-Hartman legacy_pty_init();
94496fd7ce5SGreg Kroah-Hartman unix98_pty_init();
94596fd7ce5SGreg Kroah-Hartman return 0;
94696fd7ce5SGreg Kroah-Hartman }
9477154988fSPaul Gortmaker device_initcall(pty_init);
948