Some checks failed
Build and test / build (push) Has been cancelled
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...
111 lines
3.4 KiB
C++
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);
|
|
}
|
|
}
|