162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 1991, 1992  Linus Torvalds
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/types.h>
762306a36Sopenharmony_ci#include <linux/errno.h>
862306a36Sopenharmony_ci#include <linux/signal.h>
962306a36Sopenharmony_ci#include <linux/sched/signal.h>
1062306a36Sopenharmony_ci#include <linux/sched/task.h>
1162306a36Sopenharmony_ci#include <linux/tty.h>
1262306a36Sopenharmony_ci#include <linux/fcntl.h>
1362306a36Sopenharmony_ci#include <linux/uaccess.h>
1462306a36Sopenharmony_ci#include "tty.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic int is_ignored(int sig)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	return (sigismember(&current->blocked, sig) ||
1962306a36Sopenharmony_ci		current->sighand->action[sig-1].sa.sa_handler == SIG_IGN);
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/**
2362306a36Sopenharmony_ci *	__tty_check_change	-	check for POSIX terminal changes
2462306a36Sopenharmony_ci *	@tty: tty to check
2562306a36Sopenharmony_ci *	@sig: signal to send
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci *	If we try to write to, or set the state of, a terminal and we're
2862306a36Sopenharmony_ci *	not in the foreground, send a SIGTTOU.  If the signal is blocked or
2962306a36Sopenharmony_ci *	ignored, go ahead and perform the operation.  (POSIX 7.2)
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci *	Locking: ctrl.lock
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ciint __tty_check_change(struct tty_struct *tty, int sig)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	unsigned long flags;
3662306a36Sopenharmony_ci	struct pid *pgrp, *tty_pgrp;
3762306a36Sopenharmony_ci	int ret = 0;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (current->signal->tty != tty)
4062306a36Sopenharmony_ci		return 0;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	rcu_read_lock();
4362306a36Sopenharmony_ci	pgrp = task_pgrp(current);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	spin_lock_irqsave(&tty->ctrl.lock, flags);
4662306a36Sopenharmony_ci	tty_pgrp = tty->ctrl.pgrp;
4762306a36Sopenharmony_ci	spin_unlock_irqrestore(&tty->ctrl.lock, flags);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (tty_pgrp && pgrp != tty_pgrp) {
5062306a36Sopenharmony_ci		if (is_ignored(sig)) {
5162306a36Sopenharmony_ci			if (sig == SIGTTIN)
5262306a36Sopenharmony_ci				ret = -EIO;
5362306a36Sopenharmony_ci		} else if (is_current_pgrp_orphaned())
5462306a36Sopenharmony_ci			ret = -EIO;
5562306a36Sopenharmony_ci		else {
5662306a36Sopenharmony_ci			kill_pgrp(pgrp, sig, 1);
5762306a36Sopenharmony_ci			set_thread_flag(TIF_SIGPENDING);
5862306a36Sopenharmony_ci			ret = -ERESTARTSYS;
5962306a36Sopenharmony_ci		}
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci	rcu_read_unlock();
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	if (!tty_pgrp)
6462306a36Sopenharmony_ci		tty_warn(tty, "sig=%d, tty->pgrp == NULL!\n", sig);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return ret;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ciint tty_check_change(struct tty_struct *tty)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	return __tty_check_change(tty, SIGTTOU);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ciEXPORT_SYMBOL(tty_check_change);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_civoid proc_clear_tty(struct task_struct *p)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	unsigned long flags;
7862306a36Sopenharmony_ci	struct tty_struct *tty;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	spin_lock_irqsave(&p->sighand->siglock, flags);
8162306a36Sopenharmony_ci	tty = p->signal->tty;
8262306a36Sopenharmony_ci	p->signal->tty = NULL;
8362306a36Sopenharmony_ci	spin_unlock_irqrestore(&p->sighand->siglock, flags);
8462306a36Sopenharmony_ci	tty_kref_put(tty);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/**
8862306a36Sopenharmony_ci * __proc_set_tty -  set the controlling terminal
8962306a36Sopenharmony_ci *	@tty: tty structure
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci * Only callable by the session leader and only if it does not already have
9262306a36Sopenharmony_ci * a controlling terminal.
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci * Caller must hold:  tty_lock()
9562306a36Sopenharmony_ci *		      a readlock on tasklist_lock
9662306a36Sopenharmony_ci *		      sighand lock
9762306a36Sopenharmony_ci */
9862306a36Sopenharmony_cistatic void __proc_set_tty(struct tty_struct *tty)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	unsigned long flags;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	spin_lock_irqsave(&tty->ctrl.lock, flags);
10362306a36Sopenharmony_ci	/*
10462306a36Sopenharmony_ci	 * The session and fg pgrp references will be non-NULL if
10562306a36Sopenharmony_ci	 * tiocsctty() is stealing the controlling tty
10662306a36Sopenharmony_ci	 */
10762306a36Sopenharmony_ci	put_pid(tty->ctrl.session);
10862306a36Sopenharmony_ci	put_pid(tty->ctrl.pgrp);
10962306a36Sopenharmony_ci	tty->ctrl.pgrp = get_pid(task_pgrp(current));
11062306a36Sopenharmony_ci	tty->ctrl.session = get_pid(task_session(current));
11162306a36Sopenharmony_ci	spin_unlock_irqrestore(&tty->ctrl.lock, flags);
11262306a36Sopenharmony_ci	if (current->signal->tty) {
11362306a36Sopenharmony_ci		tty_debug(tty, "current tty %s not NULL!!\n",
11462306a36Sopenharmony_ci			  current->signal->tty->name);
11562306a36Sopenharmony_ci		tty_kref_put(current->signal->tty);
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci	put_pid(current->signal->tty_old_pgrp);
11862306a36Sopenharmony_ci	current->signal->tty = tty_kref_get(tty);
11962306a36Sopenharmony_ci	current->signal->tty_old_pgrp = NULL;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void proc_set_tty(struct tty_struct *tty)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	spin_lock_irq(&current->sighand->siglock);
12562306a36Sopenharmony_ci	__proc_set_tty(tty);
12662306a36Sopenharmony_ci	spin_unlock_irq(&current->sighand->siglock);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * Called by tty_open() to set the controlling tty if applicable.
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_civoid tty_open_proc_set_tty(struct file *filp, struct tty_struct *tty)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	read_lock(&tasklist_lock);
13562306a36Sopenharmony_ci	spin_lock_irq(&current->sighand->siglock);
13662306a36Sopenharmony_ci	if (current->signal->leader &&
13762306a36Sopenharmony_ci	    !current->signal->tty &&
13862306a36Sopenharmony_ci	    tty->ctrl.session == NULL) {
13962306a36Sopenharmony_ci		/*
14062306a36Sopenharmony_ci		 * Don't let a process that only has write access to the tty
14162306a36Sopenharmony_ci		 * obtain the privileges associated with having a tty as
14262306a36Sopenharmony_ci		 * controlling terminal (being able to reopen it with full
14362306a36Sopenharmony_ci		 * access through /dev/tty, being able to perform pushback).
14462306a36Sopenharmony_ci		 * Many distributions set the group of all ttys to "tty" and
14562306a36Sopenharmony_ci		 * grant write-only access to all terminals for setgid tty
14662306a36Sopenharmony_ci		 * binaries, which should not imply full privileges on all ttys.
14762306a36Sopenharmony_ci		 *
14862306a36Sopenharmony_ci		 * This could theoretically break old code that performs open()
14962306a36Sopenharmony_ci		 * on a write-only file descriptor. In that case, it might be
15062306a36Sopenharmony_ci		 * necessary to also permit this if
15162306a36Sopenharmony_ci		 * inode_permission(inode, MAY_READ) == 0.
15262306a36Sopenharmony_ci		 */
15362306a36Sopenharmony_ci		if (filp->f_mode & FMODE_READ)
15462306a36Sopenharmony_ci			__proc_set_tty(tty);
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci	spin_unlock_irq(&current->sighand->siglock);
15762306a36Sopenharmony_ci	read_unlock(&tasklist_lock);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistruct tty_struct *get_current_tty(void)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct tty_struct *tty;
16362306a36Sopenharmony_ci	unsigned long flags;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	spin_lock_irqsave(&current->sighand->siglock, flags);
16662306a36Sopenharmony_ci	tty = tty_kref_get(current->signal->tty);
16762306a36Sopenharmony_ci	spin_unlock_irqrestore(&current->sighand->siglock, flags);
16862306a36Sopenharmony_ci	return tty;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(get_current_tty);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/*
17362306a36Sopenharmony_ci * Called from tty_release().
17462306a36Sopenharmony_ci */
17562306a36Sopenharmony_civoid session_clear_tty(struct pid *session)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct task_struct *p;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	do_each_pid_task(session, PIDTYPE_SID, p) {
18062306a36Sopenharmony_ci		proc_clear_tty(p);
18162306a36Sopenharmony_ci	} while_each_pid_task(session, PIDTYPE_SID, p);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci/**
18562306a36Sopenharmony_ci *	tty_signal_session_leader	- sends SIGHUP to session leader
18662306a36Sopenharmony_ci *	@tty: controlling tty
18762306a36Sopenharmony_ci *	@exit_session: if non-zero, signal all foreground group processes
18862306a36Sopenharmony_ci *
18962306a36Sopenharmony_ci *	Send SIGHUP and SIGCONT to the session leader and its process group.
19062306a36Sopenharmony_ci *	Optionally, signal all processes in the foreground process group.
19162306a36Sopenharmony_ci *
19262306a36Sopenharmony_ci *	Returns the number of processes in the session with this tty
19362306a36Sopenharmony_ci *	as their controlling terminal. This value is used to drop
19462306a36Sopenharmony_ci *	tty references for those processes.
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_ciint tty_signal_session_leader(struct tty_struct *tty, int exit_session)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct task_struct *p;
19962306a36Sopenharmony_ci	int refs = 0;
20062306a36Sopenharmony_ci	struct pid *tty_pgrp = NULL;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	read_lock(&tasklist_lock);
20362306a36Sopenharmony_ci	if (tty->ctrl.session) {
20462306a36Sopenharmony_ci		do_each_pid_task(tty->ctrl.session, PIDTYPE_SID, p) {
20562306a36Sopenharmony_ci			spin_lock_irq(&p->sighand->siglock);
20662306a36Sopenharmony_ci			if (p->signal->tty == tty) {
20762306a36Sopenharmony_ci				p->signal->tty = NULL;
20862306a36Sopenharmony_ci				/*
20962306a36Sopenharmony_ci				 * We defer the dereferences outside of
21062306a36Sopenharmony_ci				 * the tasklist lock.
21162306a36Sopenharmony_ci				 */
21262306a36Sopenharmony_ci				refs++;
21362306a36Sopenharmony_ci			}
21462306a36Sopenharmony_ci			if (!p->signal->leader) {
21562306a36Sopenharmony_ci				spin_unlock_irq(&p->sighand->siglock);
21662306a36Sopenharmony_ci				continue;
21762306a36Sopenharmony_ci			}
21862306a36Sopenharmony_ci			send_signal_locked(SIGHUP, SEND_SIG_PRIV, p, PIDTYPE_TGID);
21962306a36Sopenharmony_ci			send_signal_locked(SIGCONT, SEND_SIG_PRIV, p, PIDTYPE_TGID);
22062306a36Sopenharmony_ci			put_pid(p->signal->tty_old_pgrp);  /* A noop */
22162306a36Sopenharmony_ci			spin_lock(&tty->ctrl.lock);
22262306a36Sopenharmony_ci			tty_pgrp = get_pid(tty->ctrl.pgrp);
22362306a36Sopenharmony_ci			if (tty->ctrl.pgrp)
22462306a36Sopenharmony_ci				p->signal->tty_old_pgrp =
22562306a36Sopenharmony_ci					get_pid(tty->ctrl.pgrp);
22662306a36Sopenharmony_ci			spin_unlock(&tty->ctrl.lock);
22762306a36Sopenharmony_ci			spin_unlock_irq(&p->sighand->siglock);
22862306a36Sopenharmony_ci		} while_each_pid_task(tty->ctrl.session, PIDTYPE_SID, p);
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci	read_unlock(&tasklist_lock);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (tty_pgrp) {
23362306a36Sopenharmony_ci		if (exit_session)
23462306a36Sopenharmony_ci			kill_pgrp(tty_pgrp, SIGHUP, exit_session);
23562306a36Sopenharmony_ci		put_pid(tty_pgrp);
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return refs;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/**
24262306a36Sopenharmony_ci *	disassociate_ctty	-	disconnect controlling tty
24362306a36Sopenharmony_ci *	@on_exit: true if exiting so need to "hang up" the session
24462306a36Sopenharmony_ci *
24562306a36Sopenharmony_ci *	This function is typically called only by the session leader, when
24662306a36Sopenharmony_ci *	it wants to disassociate itself from its controlling tty.
24762306a36Sopenharmony_ci *
24862306a36Sopenharmony_ci *	It performs the following functions:
24962306a36Sopenharmony_ci *	(1)  Sends a SIGHUP and SIGCONT to the foreground process group
25062306a36Sopenharmony_ci *	(2)  Clears the tty from being controlling the session
25162306a36Sopenharmony_ci *	(3)  Clears the controlling tty for all processes in the
25262306a36Sopenharmony_ci *		session group.
25362306a36Sopenharmony_ci *
25462306a36Sopenharmony_ci *	The argument on_exit is set to 1 if called when a process is
25562306a36Sopenharmony_ci *	exiting; it is 0 if called by the ioctl TIOCNOTTY.
25662306a36Sopenharmony_ci *
25762306a36Sopenharmony_ci *	Locking:
25862306a36Sopenharmony_ci *		BTM is taken for hysterical raisons, and held when
25962306a36Sopenharmony_ci *		  called from no_tty().
26062306a36Sopenharmony_ci *		  tty_mutex is taken to protect tty
26162306a36Sopenharmony_ci *		  ->siglock is taken to protect ->signal/->sighand
26262306a36Sopenharmony_ci *		  tasklist_lock is taken to walk process list for sessions
26362306a36Sopenharmony_ci *		    ->siglock is taken to protect ->signal/->sighand
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_civoid disassociate_ctty(int on_exit)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct tty_struct *tty;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (!current->signal->leader)
27062306a36Sopenharmony_ci		return;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	tty = get_current_tty();
27362306a36Sopenharmony_ci	if (tty) {
27462306a36Sopenharmony_ci		if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) {
27562306a36Sopenharmony_ci			tty_vhangup_session(tty);
27662306a36Sopenharmony_ci		} else {
27762306a36Sopenharmony_ci			struct pid *tty_pgrp = tty_get_pgrp(tty);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci			if (tty_pgrp) {
28062306a36Sopenharmony_ci				kill_pgrp(tty_pgrp, SIGHUP, on_exit);
28162306a36Sopenharmony_ci				if (!on_exit)
28262306a36Sopenharmony_ci					kill_pgrp(tty_pgrp, SIGCONT, on_exit);
28362306a36Sopenharmony_ci				put_pid(tty_pgrp);
28462306a36Sopenharmony_ci			}
28562306a36Sopenharmony_ci		}
28662306a36Sopenharmony_ci		tty_kref_put(tty);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	} else if (on_exit) {
28962306a36Sopenharmony_ci		struct pid *old_pgrp;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		spin_lock_irq(&current->sighand->siglock);
29262306a36Sopenharmony_ci		old_pgrp = current->signal->tty_old_pgrp;
29362306a36Sopenharmony_ci		current->signal->tty_old_pgrp = NULL;
29462306a36Sopenharmony_ci		spin_unlock_irq(&current->sighand->siglock);
29562306a36Sopenharmony_ci		if (old_pgrp) {
29662306a36Sopenharmony_ci			kill_pgrp(old_pgrp, SIGHUP, on_exit);
29762306a36Sopenharmony_ci			kill_pgrp(old_pgrp, SIGCONT, on_exit);
29862306a36Sopenharmony_ci			put_pid(old_pgrp);
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci		return;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	tty = get_current_tty();
30462306a36Sopenharmony_ci	if (tty) {
30562306a36Sopenharmony_ci		unsigned long flags;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		tty_lock(tty);
30862306a36Sopenharmony_ci		spin_lock_irqsave(&tty->ctrl.lock, flags);
30962306a36Sopenharmony_ci		put_pid(tty->ctrl.session);
31062306a36Sopenharmony_ci		put_pid(tty->ctrl.pgrp);
31162306a36Sopenharmony_ci		tty->ctrl.session = NULL;
31262306a36Sopenharmony_ci		tty->ctrl.pgrp = NULL;
31362306a36Sopenharmony_ci		spin_unlock_irqrestore(&tty->ctrl.lock, flags);
31462306a36Sopenharmony_ci		tty_unlock(tty);
31562306a36Sopenharmony_ci		tty_kref_put(tty);
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/* If tty->ctrl.pgrp is not NULL, it may be assigned to
31962306a36Sopenharmony_ci	 * current->signal->tty_old_pgrp in a race condition, and
32062306a36Sopenharmony_ci	 * cause pid memleak. Release current->signal->tty_old_pgrp
32162306a36Sopenharmony_ci	 * after tty->ctrl.pgrp set to NULL.
32262306a36Sopenharmony_ci	 */
32362306a36Sopenharmony_ci	spin_lock_irq(&current->sighand->siglock);
32462306a36Sopenharmony_ci	put_pid(current->signal->tty_old_pgrp);
32562306a36Sopenharmony_ci	current->signal->tty_old_pgrp = NULL;
32662306a36Sopenharmony_ci	spin_unlock_irq(&current->sighand->siglock);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* Now clear signal->tty under the lock */
32962306a36Sopenharmony_ci	read_lock(&tasklist_lock);
33062306a36Sopenharmony_ci	session_clear_tty(task_session(current));
33162306a36Sopenharmony_ci	read_unlock(&tasklist_lock);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/*
33562306a36Sopenharmony_ci *
33662306a36Sopenharmony_ci *	no_tty	- Ensure the current process does not have a controlling tty
33762306a36Sopenharmony_ci */
33862306a36Sopenharmony_civoid no_tty(void)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	/*
34162306a36Sopenharmony_ci	 * FIXME: Review locking here. The tty_lock never covered any race
34262306a36Sopenharmony_ci	 * between a new association and proc_clear_tty but possibly we need
34362306a36Sopenharmony_ci	 * to protect against this anyway.
34462306a36Sopenharmony_ci	 */
34562306a36Sopenharmony_ci	struct task_struct *tsk = current;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	disassociate_ctty(0);
34862306a36Sopenharmony_ci	proc_clear_tty(tsk);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci/**
35262306a36Sopenharmony_ci *	tiocsctty	-	set controlling tty
35362306a36Sopenharmony_ci *	@tty: tty structure
35462306a36Sopenharmony_ci *	@file: file structure used to check permissions
35562306a36Sopenharmony_ci *	@arg: user argument
35662306a36Sopenharmony_ci *
35762306a36Sopenharmony_ci *	This ioctl is used to manage job control. It permits a session
35862306a36Sopenharmony_ci *	leader to set this tty as the controlling tty for the session.
35962306a36Sopenharmony_ci *
36062306a36Sopenharmony_ci *	Locking:
36162306a36Sopenharmony_ci *		Takes tty_lock() to serialize proc_set_tty() for this tty
36262306a36Sopenharmony_ci *		Takes tasklist_lock internally to walk sessions
36362306a36Sopenharmony_ci *		Takes ->siglock() when updating signal->tty
36462306a36Sopenharmony_ci */
36562306a36Sopenharmony_cistatic int tiocsctty(struct tty_struct *tty, struct file *file, int arg)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	int ret = 0;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	tty_lock(tty);
37062306a36Sopenharmony_ci	read_lock(&tasklist_lock);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (current->signal->leader &&
37362306a36Sopenharmony_ci			task_session(current) == tty->ctrl.session)
37462306a36Sopenharmony_ci		goto unlock;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/*
37762306a36Sopenharmony_ci	 * The process must be a session leader and
37862306a36Sopenharmony_ci	 * not have a controlling tty already.
37962306a36Sopenharmony_ci	 */
38062306a36Sopenharmony_ci	if (!current->signal->leader || current->signal->tty) {
38162306a36Sopenharmony_ci		ret = -EPERM;
38262306a36Sopenharmony_ci		goto unlock;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (tty->ctrl.session) {
38662306a36Sopenharmony_ci		/*
38762306a36Sopenharmony_ci		 * This tty is already the controlling
38862306a36Sopenharmony_ci		 * tty for another session group!
38962306a36Sopenharmony_ci		 */
39062306a36Sopenharmony_ci		if (arg == 1 && capable(CAP_SYS_ADMIN)) {
39162306a36Sopenharmony_ci			/*
39262306a36Sopenharmony_ci			 * Steal it away
39362306a36Sopenharmony_ci			 */
39462306a36Sopenharmony_ci			session_clear_tty(tty->ctrl.session);
39562306a36Sopenharmony_ci		} else {
39662306a36Sopenharmony_ci			ret = -EPERM;
39762306a36Sopenharmony_ci			goto unlock;
39862306a36Sopenharmony_ci		}
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* See the comment in tty_open_proc_set_tty(). */
40262306a36Sopenharmony_ci	if ((file->f_mode & FMODE_READ) == 0 && !capable(CAP_SYS_ADMIN)) {
40362306a36Sopenharmony_ci		ret = -EPERM;
40462306a36Sopenharmony_ci		goto unlock;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	proc_set_tty(tty);
40862306a36Sopenharmony_ciunlock:
40962306a36Sopenharmony_ci	read_unlock(&tasklist_lock);
41062306a36Sopenharmony_ci	tty_unlock(tty);
41162306a36Sopenharmony_ci	return ret;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/**
41562306a36Sopenharmony_ci *	tty_get_pgrp	-	return a ref counted pgrp pid
41662306a36Sopenharmony_ci *	@tty: tty to read
41762306a36Sopenharmony_ci *
41862306a36Sopenharmony_ci *	Returns a refcounted instance of the pid struct for the process
41962306a36Sopenharmony_ci *	group controlling the tty.
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_cistruct pid *tty_get_pgrp(struct tty_struct *tty)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	unsigned long flags;
42462306a36Sopenharmony_ci	struct pid *pgrp;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	spin_lock_irqsave(&tty->ctrl.lock, flags);
42762306a36Sopenharmony_ci	pgrp = get_pid(tty->ctrl.pgrp);
42862306a36Sopenharmony_ci	spin_unlock_irqrestore(&tty->ctrl.lock, flags);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return pgrp;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_get_pgrp);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/*
43562306a36Sopenharmony_ci * This checks not only the pgrp, but falls back on the pid if no
43662306a36Sopenharmony_ci * satisfactory pgrp is found. I dunno - gdb doesn't work correctly
43762306a36Sopenharmony_ci * without this...
43862306a36Sopenharmony_ci *
43962306a36Sopenharmony_ci * The caller must hold rcu lock or the tasklist lock.
44062306a36Sopenharmony_ci */
44162306a36Sopenharmony_cistatic struct pid *session_of_pgrp(struct pid *pgrp)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	struct task_struct *p;
44462306a36Sopenharmony_ci	struct pid *sid = NULL;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	p = pid_task(pgrp, PIDTYPE_PGID);
44762306a36Sopenharmony_ci	if (p == NULL)
44862306a36Sopenharmony_ci		p = pid_task(pgrp, PIDTYPE_PID);
44962306a36Sopenharmony_ci	if (p != NULL)
45062306a36Sopenharmony_ci		sid = task_session(p);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return sid;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci/**
45662306a36Sopenharmony_ci *	tiocgpgrp		-	get process group
45762306a36Sopenharmony_ci *	@tty: tty passed by user
45862306a36Sopenharmony_ci *	@real_tty: tty side of the tty passed by the user if a pty else the tty
45962306a36Sopenharmony_ci *	@p: returned pid
46062306a36Sopenharmony_ci *
46162306a36Sopenharmony_ci *	Obtain the process group of the tty. If there is no process group
46262306a36Sopenharmony_ci *	return an error.
46362306a36Sopenharmony_ci *
46462306a36Sopenharmony_ci *	Locking: none. Reference to current->signal->tty is safe.
46562306a36Sopenharmony_ci */
46662306a36Sopenharmony_cistatic int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	struct pid *pid;
46962306a36Sopenharmony_ci	int ret;
47062306a36Sopenharmony_ci	/*
47162306a36Sopenharmony_ci	 * (tty == real_tty) is a cheap way of
47262306a36Sopenharmony_ci	 * testing if the tty is NOT a master pty.
47362306a36Sopenharmony_ci	 */
47462306a36Sopenharmony_ci	if (tty == real_tty && current->signal->tty != real_tty)
47562306a36Sopenharmony_ci		return -ENOTTY;
47662306a36Sopenharmony_ci	pid = tty_get_pgrp(real_tty);
47762306a36Sopenharmony_ci	ret =  put_user(pid_vnr(pid), p);
47862306a36Sopenharmony_ci	put_pid(pid);
47962306a36Sopenharmony_ci	return ret;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci/**
48362306a36Sopenharmony_ci *	tiocspgrp		-	attempt to set process group
48462306a36Sopenharmony_ci *	@tty: tty passed by user
48562306a36Sopenharmony_ci *	@real_tty: tty side device matching tty passed by user
48662306a36Sopenharmony_ci *	@p: pid pointer
48762306a36Sopenharmony_ci *
48862306a36Sopenharmony_ci *	Set the process group of the tty to the session passed. Only
48962306a36Sopenharmony_ci *	permitted where the tty session is our session.
49062306a36Sopenharmony_ci *
49162306a36Sopenharmony_ci *	Locking: RCU, ctrl lock
49262306a36Sopenharmony_ci */
49362306a36Sopenharmony_cistatic int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct pid *pgrp;
49662306a36Sopenharmony_ci	pid_t pgrp_nr;
49762306a36Sopenharmony_ci	int retval = tty_check_change(real_tty);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (retval == -EIO)
50062306a36Sopenharmony_ci		return -ENOTTY;
50162306a36Sopenharmony_ci	if (retval)
50262306a36Sopenharmony_ci		return retval;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (get_user(pgrp_nr, p))
50562306a36Sopenharmony_ci		return -EFAULT;
50662306a36Sopenharmony_ci	if (pgrp_nr < 0)
50762306a36Sopenharmony_ci		return -EINVAL;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	spin_lock_irq(&real_tty->ctrl.lock);
51062306a36Sopenharmony_ci	if (!current->signal->tty ||
51162306a36Sopenharmony_ci	    (current->signal->tty != real_tty) ||
51262306a36Sopenharmony_ci	    (real_tty->ctrl.session != task_session(current))) {
51362306a36Sopenharmony_ci		retval = -ENOTTY;
51462306a36Sopenharmony_ci		goto out_unlock_ctrl;
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci	rcu_read_lock();
51762306a36Sopenharmony_ci	pgrp = find_vpid(pgrp_nr);
51862306a36Sopenharmony_ci	retval = -ESRCH;
51962306a36Sopenharmony_ci	if (!pgrp)
52062306a36Sopenharmony_ci		goto out_unlock;
52162306a36Sopenharmony_ci	retval = -EPERM;
52262306a36Sopenharmony_ci	if (session_of_pgrp(pgrp) != task_session(current))
52362306a36Sopenharmony_ci		goto out_unlock;
52462306a36Sopenharmony_ci	retval = 0;
52562306a36Sopenharmony_ci	put_pid(real_tty->ctrl.pgrp);
52662306a36Sopenharmony_ci	real_tty->ctrl.pgrp = get_pid(pgrp);
52762306a36Sopenharmony_ciout_unlock:
52862306a36Sopenharmony_ci	rcu_read_unlock();
52962306a36Sopenharmony_ciout_unlock_ctrl:
53062306a36Sopenharmony_ci	spin_unlock_irq(&real_tty->ctrl.lock);
53162306a36Sopenharmony_ci	return retval;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci/**
53562306a36Sopenharmony_ci *	tiocgsid		-	get session id
53662306a36Sopenharmony_ci *	@tty: tty passed by user
53762306a36Sopenharmony_ci *	@real_tty: tty side of the tty passed by the user if a pty else the tty
53862306a36Sopenharmony_ci *	@p: pointer to returned session id
53962306a36Sopenharmony_ci *
54062306a36Sopenharmony_ci *	Obtain the session id of the tty. If there is no session
54162306a36Sopenharmony_ci *	return an error.
54262306a36Sopenharmony_ci */
54362306a36Sopenharmony_cistatic int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	unsigned long flags;
54662306a36Sopenharmony_ci	pid_t sid;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/*
54962306a36Sopenharmony_ci	 * (tty == real_tty) is a cheap way of
55062306a36Sopenharmony_ci	 * testing if the tty is NOT a master pty.
55162306a36Sopenharmony_ci	 */
55262306a36Sopenharmony_ci	if (tty == real_tty && current->signal->tty != real_tty)
55362306a36Sopenharmony_ci		return -ENOTTY;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	spin_lock_irqsave(&real_tty->ctrl.lock, flags);
55662306a36Sopenharmony_ci	if (!real_tty->ctrl.session)
55762306a36Sopenharmony_ci		goto err;
55862306a36Sopenharmony_ci	sid = pid_vnr(real_tty->ctrl.session);
55962306a36Sopenharmony_ci	spin_unlock_irqrestore(&real_tty->ctrl.lock, flags);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	return put_user(sid, p);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cierr:
56462306a36Sopenharmony_ci	spin_unlock_irqrestore(&real_tty->ctrl.lock, flags);
56562306a36Sopenharmony_ci	return -ENOTTY;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci/*
56962306a36Sopenharmony_ci * Called from tty_ioctl(). If tty is a pty then real_tty is the slave side,
57062306a36Sopenharmony_ci * if not then tty == real_tty.
57162306a36Sopenharmony_ci */
57262306a36Sopenharmony_cilong tty_jobctrl_ioctl(struct tty_struct *tty, struct tty_struct *real_tty,
57362306a36Sopenharmony_ci		       struct file *file, unsigned int cmd, unsigned long arg)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	void __user *p = (void __user *)arg;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	switch (cmd) {
57862306a36Sopenharmony_ci	case TIOCNOTTY:
57962306a36Sopenharmony_ci		if (current->signal->tty != tty)
58062306a36Sopenharmony_ci			return -ENOTTY;
58162306a36Sopenharmony_ci		no_tty();
58262306a36Sopenharmony_ci		return 0;
58362306a36Sopenharmony_ci	case TIOCSCTTY:
58462306a36Sopenharmony_ci		return tiocsctty(real_tty, file, arg);
58562306a36Sopenharmony_ci	case TIOCGPGRP:
58662306a36Sopenharmony_ci		return tiocgpgrp(tty, real_tty, p);
58762306a36Sopenharmony_ci	case TIOCSPGRP:
58862306a36Sopenharmony_ci		return tiocspgrp(tty, real_tty, p);
58962306a36Sopenharmony_ci	case TIOCGSID:
59062306a36Sopenharmony_ci		return tiocgsid(tty, real_tty, p);
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci	return -ENOIOCTLCMD;
59362306a36Sopenharmony_ci}
594