2#include "TerminalControl.h"
3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/process/Process.h"
5#include "pedigree/kernel/process/Scheduler.h"
6#include "pedigree/kernel/process/TerminationDeferral.h"
7#include "pedigree/kernel/processor/Processor.h"
8#include "pedigree/kernel/processor/ProcessorInformation.h"
9#include "pedigree/kernel/syscallError.h"
13#include "PosixProcess.h"
14#include "PosixSubsystem.h"
22 : m_Console(console), m_Control(control) {}
23 ~TerminalBinding()
override {
24 m_Console->releaseVfsReference();
26 File* file()
const override {
27 return static_cast<TerminalControl*
>(m_Control.get())->active() ? m_Console :
nullptr;
37 return process->getType() == Process::Posix ?
static_cast<PosixProcess*
>(process) : nullptr;
41 auto context = process.acquireCttyContext();
42 return context && context->file() == &console;
46TerminalControl::TerminalControl(
size_t session,
size_t foreground)
47 : m_Active(true), m_Session(session), m_Foreground(foreground) {}
49bool TerminalControl::active()
const {
50 return __atomic_load_n(&m_Active, __ATOMIC_ACQUIRE);
53void TerminalControl::controlCharacter(Character character) {
56 const size_t foreground = __atomic_load_n(&m_Foreground, __ATOMIC_ACQUIRE);
57 const int signal = character == Character::Interrupt ? SIGINT
58 : character == Character::Quit ? SIGQUIT
63 recipient->getType() != Process::Posix)
65 auto* process =
static_cast<PosixProcess*
>(recipient.get());
69 if (process->getSessionId() != m_Session || !process->getProcessGroupId(group) ||
74 auto* subsystem =
static_cast<PosixSubsystem*
>(process->getSubsystem());
76 subsystem->
sendSignal(thread.get(), signal,
false,
true);
80void TerminalControl::invalidate() {
81 __atomic_store_n(&m_Active,
false, __ATOMIC_RELEASE);
84Mutex& TerminalControl::lock() {
85 return terminalPolicy;
88int TerminalControl::attach(
ConsoleFile& console,
bool steal,
bool automatic,
92 auto epoch = opened ? opened : console.captureOpenEpoch();
94 SYSCALL_ERROR(OutOfMemory);
98 if (epoch->revoked()) {
99 SYSCALL_ERROR(IoError);
103 if (!process || console.isMaster()) {
104 SYSCALL_ERROR(NotAConsole);
105 return automatic ? 0 : -1;
107 auto context = process->acquireCttyContext();
108 File* existing = context ? context->file() :
nullptr;
109 const size_t session = process->getSessionId();
110 if (session != process->
getUserspaceId() || (existing && existing != &console)) {
113 NOTICE(
"TIOCSCTTY EPERM - session doesn't match");
114 SYSCALL_ERROR(NotEnoughPermissions);
117 auto previous = console.controlState();
119 if (old && old->active() && old->m_Session == session && existing == &console)
121 if (old && old->active() && old->m_Session != session &&
122 (!steal || process->getEffectiveUserId() != 0)) {
125 NOTICE(
"TIOCSCTTY EPERM - you are not root and a previous session is active");
126 SYSCALL_ERROR(NotEnoughPermissions);
130 if (!process->getProcessGroupId(group)) {
131 SYSCALL_ERROR(NoSuchProcess);
136 SYSCALL_ERROR(OutOfMemory);
139 auto* rawBinding =
new TerminalBinding(&console, control);
142 SYSCALL_ERROR(OutOfMemory);
147 SYSCALL_ERROR(OutOfMemory);
152 console.setControlState(control);
153 process->setCttyContext(binding);
157int TerminalControl::setForeground(
ConsoleFile& console,
int group,
161 if (opened && opened->revoked()) {
162 SYSCALL_ERROR(NotAConsole);
166 auto slot = console.controlState();
168 if (!process || !control || !control->active() || !currentTerminal(*process, console) ||
169 control->m_Session != process->getSessionId()) {
170 SYSCALL_ERROR(NotAConsole);
174 SYSCALL_ERROR(InvalidArgument);
180 if (!target || target->sessionId != control->m_Session) {
181 SYSCALL_ERROR(NotEnoughPermissions);
185 __atomic_store_n(&control->m_Foreground,
static_cast<size_t>(group), __ATOMIC_RELEASE);
191 if (opened && opened->revoked()) {
192 SYSCALL_ERROR(IoError);
196 auto slot = console.controlState();
198 if (!process || !control || !control->active() || !currentTerminal(*process, console) ||
199 control->m_Session != process->getSessionId()) {
200 SYSCALL_ERROR(NotAConsole);
203 return static_cast<int>(__atomic_load_n(&control->m_Foreground, __ATOMIC_ACQUIRE));
206int TerminalControl::hangup() {
209 if (caller->getEffectiveUserId() != 0) {
210 SYSCALL_ERROR(NotEnoughPermissions);
219 context = caller->acquireCttyContext();
220 File* file = context ? context->file() :
nullptr;
223 if (!ConsoleManager::instance().isConsole(file)) {
224 SYSCALL_ERROR(NotAConsole);
230 SYSCALL_ERROR(OutOfMemory);
233 auto slot = console->controlState();
235 if (!console->beginRevocation(retired)) {
236 SYSCALL_ERROR(DeviceBusy);
239 if (control && control->active()) {
241 (leader->getType() != Process::Posix ||
242 static_cast<PosixProcess*
>(leader.get())->getSessionId() != control->m_Session))
244 control->invalidate();
249 console->finishRevocation(retired, replacement);
252 auto* subsystem =
static_cast<PosixSubsystem*
>(leader->getSubsystem());
254 subsystem->
sendSignal(thread.get(), SIGHUP,
false,
true);
255 subsystem->sendSignal(thread.get(), SIGCONT,
false,
true);
261void TerminalControl::processTerminated(
PosixProcess& process) {
263 size_t session = 0, foreground = 0;
269 context = process.acquireCttyContext();
270 File* file = context ? context->file() :
nullptr;
271 if (file && ConsoleManager::instance().isConsole(file)) {
273 auto slot = console->controlState();
276 const bool preservePtyData = console->isPtySlave();
277 if (control && control->active() && control->m_Session == process.
getUserspaceId() &&
278 (preservePtyData || console->beginRevocation(retired))) {
279 session = control->m_Session;
280 foreground = __atomic_load_n(&control->m_Foreground, __ATOMIC_ACQUIRE);
281 control->invalidate();
283 if (!preservePtyData)
292 if (!session || !foreground)
297 recipient->getType() != Process::Posix || recipient.get() == &process)
299 auto* target =
static_cast<PosixProcess*
>(recipient.get());
301 if (target->getSessionId() != session || !target->getProcessGroupId(group) ||
305 auto* subsystem =
static_cast<PosixSubsystem*
>(target->getSubsystem());
306 if (subsystem && target->acquireProcessSignalThread(thread)) {
307 subsystem->
sendSignal(thread.get(), SIGHUP,
false,
true);
308 subsystem->sendSignal(thread.get(), SIGCONT,
false,
true);
virtual bool retainVfsReference()
virtual void releaseVfsReference()
virtual void sendSignal(Thread *pThread, int signal, bool yield=true, bool processDirected=false)
ProcessGroup * findGroup(size_t gid) const
size_t getUserspaceId() const
MUST_USE_RESULT bool acquireProcessSignalThread(ThreadLease &lease)
static ProcessorInformation & information()
static Scheduler & instance()
MUST_USE_RESULT bool acquireProcess(ProcessLease &lease, size_t n)
static SharedPointer< T > tryAdopt(T *ptr)
static SharedPointer< T > tryAllocate(Args...)