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"
15#include "modules/system/vfs/VFS.h"
24 : m_Console(console), m_Control(control), m_Storage(pedigree_std::move(storage)) {}
25 ~TerminalBinding()
override {
26 m_Console->releaseVfsReference();
28 File* file()
const override {
29 return static_cast<TerminalControl*
>(m_Control.get())->active() ? m_Console :
nullptr;
40 return process->getType() == Process::Posix ?
static_cast<PosixProcess*
>(process) : nullptr;
44 auto context = process.acquireCttyContext();
45 return context && context->file() == &console;
49TerminalControl::TerminalControl(
size_t session,
size_t foreground)
50 : m_Active(true), m_Session(session), m_Foreground(foreground) {}
52bool TerminalControl::active()
const {
53 return __atomic_load_n(&m_Active, __ATOMIC_ACQUIRE);
56void TerminalControl::controlCharacter(Character character) {
59 const size_t foreground = __atomic_load_n(&m_Foreground, __ATOMIC_ACQUIRE);
60 const int signal = character == Character::Interrupt ? SIGINT
61 : character == Character::Quit ? SIGQUIT
66 recipient->getType() != Process::Posix)
68 auto* process =
static_cast<PosixProcess*
>(recipient.get());
72 if (process->getSessionId() != m_Session || !process->getProcessGroupId(group) ||
77 auto* subsystem =
static_cast<PosixSubsystem*
>(process->getSubsystem());
79 subsystem->
sendSignal(thread.get(), signal,
false,
true);
83void TerminalControl::invalidate() {
84 __atomic_store_n(&m_Active,
false, __ATOMIC_RELEASE);
87Mutex& TerminalControl::lock() {
88 return terminalPolicy;
91int TerminalControl::attach(
ConsoleFile& console,
bool steal,
bool automatic,
95 auto epoch = opened ? opened : console.captureOpenEpoch();
97 SYSCALL_ERROR(OutOfMemory);
101 if (epoch->revoked()) {
102 SYSCALL_ERROR(IoError);
106 if (!process || console.isMaster()) {
107 SYSCALL_ERROR(NotAConsole);
108 return automatic ? 0 : -1;
110 auto context = process->acquireCttyContext();
111 File* existing = context ? context->file() :
nullptr;
112 const size_t session = process->getSessionId();
113 if (session != process->
getUserspaceId(Process::rootPidNamespace().get()) ||
114 (existing && existing != &console)) {
117 NOTICE(
"TIOCSCTTY EPERM - session doesn't match");
118 SYSCALL_ERROR(NotEnoughPermissions);
121 auto previous = console.controlState();
123 if (old && old->active() && old->m_Session == session && existing == &console)
125 if (old && old->active() && old->m_Session != session &&
126 (!steal || process->getEffectiveUserId() != 0)) {
129 NOTICE(
"TIOCSCTTY EPERM - you are not root and a previous session is active");
130 SYSCALL_ERROR(NotEnoughPermissions);
134 if (!process->getProcessGroupId(group)) {
135 SYSCALL_ERROR(NoSuchProcess);
140 if (!control || !
VFS::instance().pinFilesystem(console.getFilesystem(), storage) ||
142 SYSCALL_ERROR(OutOfMemory);
145 auto* rawBinding =
new TerminalBinding(&console, control, pedigree_std::move(storage));
148 SYSCALL_ERROR(OutOfMemory);
153 SYSCALL_ERROR(OutOfMemory);
158 console.setControlState(control);
159 process->setCttyContext(binding);
163int TerminalControl::setForeground(
ConsoleFile& console,
int group,
167 if (opened && opened->revoked()) {
168 SYSCALL_ERROR(NotAConsole);
172 auto slot = console.controlState();
174 if (!process || !control || !control->active() || !currentTerminal(*process, console) ||
175 control->m_Session != process->getSessionId()) {
176 SYSCALL_ERROR(NotAConsole);
180 SYSCALL_ERROR(InvalidArgument);
186 ProcessGroupManager::instance().
findGroup(group, process->pidNamespace().
get());
187 if (!target || target->sessionId != control->m_Session) {
188 SYSCALL_ERROR(NotEnoughPermissions);
191 group = target->processGroupId;
193 __atomic_store_n(&control->m_Foreground,
static_cast<size_t>(group), __ATOMIC_RELEASE);
199 if (opened && opened->revoked()) {
200 SYSCALL_ERROR(IoError);
204 auto slot = console.controlState();
206 if (!process || !control || !control->active() || !currentTerminal(*process, console) ||
207 control->m_Session != process->getSessionId()) {
208 SYSCALL_ERROR(NotAConsole);
212 auto* group = ProcessGroupManager::instance().
findGroup(
213 __atomic_load_n(&control->m_Foreground, __ATOMIC_ACQUIRE));
214 return group && group->identity ? group->identity->id(process->pidNamespace().
get()) : 0;
219 if (opened && opened->revoked()) {
220 SYSCALL_ERROR(IoError);
223 auto slot = console.controlState();
225 if (!control || !control->active()) {
226 SYSCALL_ERROR(NotAConsole);
230 if (!console.isMaster()) {
232 if (!process || !currentTerminal(*process, console) ||
233 control->m_Session != process->getSessionId()) {
234 SYSCALL_ERROR(NotAConsole);
238 auto* process = currentProcess();
241 leader, control->m_Session, Process::rootPidNamespace().get())) {
247int TerminalControl::hangup() {
250 if (caller->getEffectiveUserId() != 0) {
251 SYSCALL_ERROR(NotEnoughPermissions);
260 context = caller->acquireCttyContext();
261 File* file = context ? context->file() :
nullptr;
264 if (!ConsoleManager::instance().isConsole(file)) {
265 SYSCALL_ERROR(NotAConsole);
271 SYSCALL_ERROR(OutOfMemory);
274 auto slot = console->controlState();
276 if (!console->beginRevocation(retired)) {
277 SYSCALL_ERROR(DeviceBusy);
280 if (control && control->active()) {
282 Process::rootPidNamespace().get()) &&
283 (leader->getType() != Process::Posix ||
284 static_cast<PosixProcess*
>(leader.get())->getSessionId() != control->m_Session)) {
287 control->invalidate();
292 console->finishRevocation(retired, replacement);
295 auto* subsystem =
static_cast<PosixSubsystem*
>(leader->getSubsystem());
297 subsystem->
sendSignal(thread.get(), SIGHUP,
false,
true);
298 subsystem->sendSignal(thread.get(), SIGCONT,
false,
true);
304void TerminalControl::processTerminated(
PosixProcess& process) {
306 size_t session = 0, foreground = 0;
312 context = process.acquireCttyContext();
313 File* file = context ? context->file() :
nullptr;
314 if (file && ConsoleManager::instance().isConsole(file)) {
316 auto slot = console->controlState();
319 const bool preservePtyData = console->isPtySlave();
320 if (control && control->active() &&
321 control->m_Session == process.
getUserspaceId(Process::rootPidNamespace().get()) &&
322 (preservePtyData || console->beginRevocation(retired))) {
323 session = control->m_Session;
324 foreground = __atomic_load_n(&control->m_Foreground, __ATOMIC_ACQUIRE);
325 control->invalidate();
327 if (!preservePtyData)
336 if (!session || !foreground)
341 recipient->getType() != Process::Posix || recipient.get() == &process)
343 auto* target =
static_cast<PosixProcess*
>(recipient.get());
345 if (target->getSessionId() != session || !target->getProcessGroupId(group) ||
349 auto* subsystem =
static_cast<PosixSubsystem*
>(target->getSubsystem());
350 if (subsystem && target->acquireProcessSignalThread(thread)) {
351 subsystem->
sendSignal(thread.get(), SIGHUP,
false,
true);
352 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...)