Luna/kernel/src/fs/devices/SlavePTY.cpp
apio dc766e1da7
Some checks failed
Build and test / build (push) Has been cancelled
kernel: Rework VFS access checking + add processes
VFS functions now accept a single Process* pointer instead of credentials and groups.
There is now a distinction between processes and threads
Now to fix all the bugs... waitpid crashes the process with an NX error...
2024-12-06 21:35:59 +01:00

111 lines
3.4 KiB
C++

#include "fs/devices/SlavePTY.h"
#include "Pledge.h"
#include "fs/devices/MasterPTY.h"
#include "memory/MemoryManager.h"
#include "thread/Scheduler.h"
Result<usize> SlavePTY::read(u8* buf, usize, usize length) const
{
if (!m_master) return err(EIO);
TRY(m_master->handle_background_process_group(false, SIGTTIN));
if (!m_master->is_canonical()) length = m_buffer.dequeue_data(buf, length);
else
{
if (m_master->m_lines.size() == 0) { return 0; }
auto& line = m_master->m_lines[0];
length = line.dequeue_data(buf, length);
if (line.size() == 0) m_master->m_lines.remove_at(0);
}
return length;
}
Result<usize> SlavePTY::write(const u8* buf, usize, usize length)
{
if (!m_master) return err(EIO);
if (m_master->m_settings.c_lflag & TOSTOP) TRY(m_master->handle_background_process_group(true, SIGTTOU));
TRY(m_master->m_buffer.append_data(buf, length));
m_metadata.update_mtime();
return length;
}
bool SlavePTY::will_block_if_read() const
{
if (!m_master) return false;
if (!m_master->is_canonical()) return m_buffer.is_empty();
else
return !m_master->m_lines.size();
}
Result<u64> SlavePTY::ioctl(int request, void* arg)
{
auto* current = Process::current();
TRY(check_pledge(current, Promise::p_tty));
if (!m_master) return err(EIO);
switch (request)
{
case TCGETS: {
return MemoryManager::copy_to_user_typed((struct termios*)arg, &m_master->m_settings) ? 0 : err(EFAULT);
}
case TCSETS: {
if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &m_master->m_settings)) return err(EFAULT);
return 0;
}
case TIOCSPGRP: {
pid_t pgid;
if (!MemoryManager::copy_from_user_typed((const pid_t*)arg, &pgid)) return err(EFAULT);
if (current->controlling_terminal != SharedPtr<VFS::Inode> { this }) return err(ENOTTY);
bool pgid_exists = false;
pid_t sid;
Scheduler::for_each_in_process_group(pgid, [&pgid_exists, &sid](Process* p) {
pgid_exists = true;
sid = p->sid.load(); // should be the same for all threads in the process group
return false;
});
if (!pgid_exists) return err(EPERM);
if (sid != current->sid) return err(EPERM);
m_master->m_foreground_process_group = pgid;
return 0;
}
case TIOCGPGRP: {
if (current->controlling_terminal != SharedPtr<VFS::Inode> { this }) return err(ENOTTY);
pid_t pgid = m_master->m_foreground_process_group.value_or((pid_t)next_thread_id());
if (!MemoryManager::copy_to_user_typed((pid_t*)arg, &pgid)) return err(EFAULT);
return 0;
}
case TIOCGWINSZ: {
if (!MemoryManager::copy_to_user_typed((struct winsize*)arg, &m_master->m_window)) return err(EFAULT);
return 0;
}
case TIOCSCTTY: {
if (current->controlling_terminal) return err(EPERM);
if (this->m_master->m_session.has_value()) return err(EPERM);
if (!current->is_session_leader()) return err(EPERM);
Scheduler::for_each_in_session(current->sid, [this](Process* p) {
p->controlling_terminal = this;
return true;
});
m_master->m_session = current->sid.load();
m_master->m_foreground_process_group = current->pgid.load();
return 0;
}
default: return err(EINVAL);
}
}