#include "fs/devices/SlavePTY.h" #include "Pledge.h" #include "fs/devices/MasterPTY.h" #include "memory/MemoryManager.h" #include "thread/Scheduler.h" Result 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 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 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 { 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 { 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); } }