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(¤t->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(¤t->sighand->siglock); 12562306a36Sopenharmony_ci __proc_set_tty(tty); 12662306a36Sopenharmony_ci spin_unlock_irq(¤t->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(¤t->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(¤t->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(¤t->sighand->siglock, flags); 16662306a36Sopenharmony_ci tty = tty_kref_get(current->signal->tty); 16762306a36Sopenharmony_ci spin_unlock_irqrestore(¤t->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(¤t->sighand->siglock); 29262306a36Sopenharmony_ci old_pgrp = current->signal->tty_old_pgrp; 29362306a36Sopenharmony_ci current->signal->tty_old_pgrp = NULL; 29462306a36Sopenharmony_ci spin_unlock_irq(¤t->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(¤t->sighand->siglock); 32462306a36Sopenharmony_ci put_pid(current->signal->tty_old_pgrp); 32562306a36Sopenharmony_ci current->signal->tty_old_pgrp = NULL; 32662306a36Sopenharmony_ci spin_unlock_irq(¤t->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