#include "fs/devices/MasterPTY.h" #include "Pledge.h" #include "fs/devices/DeviceRegistry.h" #include "fs/devices/PTYMultiplexer.h" #include "fs/devpts/FileSystem.h" #include "memory/MemoryManager.h" #include "thread/Scheduler.h" #include Result> MasterPTY::create_pair(int index) { auto master = TRY(make_shared()); auto slave = TRY(make_shared()); auto name = TRY(String::format("%d"_sv, index)); for (auto& fs : g_devpts_instances) { fs->root_inode()->add_entry(slave, name.chars()); } slave->m_name = move(name); master->m_metadata.mode = 0666; master->m_index = index; master->m_slave = slave; master->m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, 0); master->m_settings.c_lflag = ECHO | ECHOE | ECHOCTL | ISIG | ICANON; master->m_settings.c_cc[VEOF] = '\4'; master->m_settings.c_cc[VERASE] = '\b'; master->m_settings.c_cc[VINTR] = '\3'; master->m_settings.c_cc[VQUIT] = '\x1c'; master->m_window.ws_col = 80; master->m_window.ws_row = 25; slave->m_master = master.ptr(); slave->m_metadata.devid = luna_dev_makedev(DeviceRegistry::Terminal, index + 2); slave->m_metadata.uid = Scheduler::current()->auth.euid; slave->m_metadata.gid = Scheduler::current()->auth.egid; slave->m_metadata.mode = 0620; return (SharedPtr)master; } Result MasterPTY::handle_background_process_group(bool can_succeed, int signo) const { if (!m_foreground_process_group.has_value()) return {}; auto foreground_pgrp = m_foreground_process_group.value(); auto* current = Scheduler::current(); if (current->pgid == foreground_pgrp) return {}; if ((current->signal_mask.get(signo - 1)) || (current->signal_handlers[signo - 1].sa_handler == SIG_IGN)) { if (can_succeed) return {}; return err(EIO); } current->send_signal(signo); if (can_succeed) return err(EINTR); return err(EIO); } bool MasterPTY::is_canonical() { return m_settings.c_lflag & ICANON; } Result MasterPTY::handle_input(u8 key) { bool is_special_character { false }; if (is_canonical()) { if (key == m_settings.c_cc[VERASE]) { auto maybe_char = m_current_line_buffer.try_pop(); if (maybe_char.has_value() && maybe_char.value()) { if ((m_settings.c_lflag & ECHO) && (m_settings.c_lflag & ECHOE)) { u8 backspace = (u8)'\b'; m_buffer.append_data(&backspace, 1); if (_iscntrl(maybe_char.value())) m_buffer.append_data(&backspace, 1); if (maybe_char.value() == '\t') { m_buffer.append_data(&backspace, 1); m_buffer.append_data(&backspace, 1); } return {}; } return {}; } if ((m_settings.c_lflag & ECHOE)) return {}; else is_special_character = true; } if (key == m_settings.c_cc[VEOF]) { const auto size = m_current_line_buffer.size(); auto buffer = Buffer { m_current_line_buffer.release_data(), size }; TRY(m_lines.try_append(move(buffer))); is_special_character = true; } if (m_settings.c_lflag & ISIG) { if (key == m_settings.c_cc[VINTR]) { if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear(); if (m_foreground_process_group.has_value()) Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Thread* thread) { thread->send_signal(SIGINT); return true; }); is_special_character = true; } if (key == m_settings.c_cc[VQUIT]) { if (!(m_settings.c_lflag & NOFLSH)) m_current_line_buffer.clear(); if (m_foreground_process_group.has_value()) Scheduler::for_each_in_process_group(*m_foreground_process_group, [](Thread* thread) { thread->send_signal(SIGQUIT); return true; }); is_special_character = true; } } } if (!is_special_character) { if (is_canonical()) { const usize max_line_size = key == '\n' ? 4096 : 4095; if (m_current_line_buffer.size() < max_line_size) TRY(m_current_line_buffer.try_append(key)); if (key == '\n') { const auto size = m_current_line_buffer.size(); auto buffer = Buffer { m_current_line_buffer.release_data(), size }; TRY(m_lines.try_append(move(buffer))); } } else TRY(m_slave->m_buffer.append_data(&key, 1)); } if (!(m_settings.c_lflag & ECHO)) return {}; if (_iscntrl(key)) { if (m_settings.c_lflag & ECHOCTL) { bool should_echo = true; if (key == '\n' || key == '\t' || key == '\0' || key == m_settings.c_cc[VEOF]) should_echo = false; if (should_echo) { u8 caret_notation[2] = { '^', '\0', }; if (key == 0x7f) caret_notation[1] = '?'; else caret_notation[1] = key + 0x40; m_buffer.append_data(caret_notation, 2); } else m_buffer.append_data(&key, 1); } } else m_buffer.append_data(&key, 1); return {}; } Result MasterPTY::read(u8* buf, usize, usize length) const { length = m_buffer.dequeue_data(buf, length); return length; } Result MasterPTY::write(const u8* buf, usize, usize length) { for (usize i = 0; i < length; i++) { TRY(handle_input(buf[i])); } return length; } Result MasterPTY::ioctl(int request, void* arg) { auto* current = Scheduler::current(); TRY(check_pledge(current, Promise::p_tty)); switch (request) { case TIOCGPTN: { if (!MemoryManager::copy_to_user_typed((int*)arg, &m_index)) return err(EFAULT); return 0; } default: return err(EINVAL); } } MasterPTY::~MasterPTY() { m_slave->m_master = nullptr; for (auto& fs : g_devpts_instances) { fs->root_inode()->remove_entry(m_slave->m_name.chars()); } PTYMultiplexer::did_remove_pty(m_index); if (m_session.has_value()) { auto leader = Scheduler::find_by_pid(*m_session); if (leader.has_value()) leader.value()->send_signal(SIGHUP); } }