All checks were successful
continuous-integration/drone/push Build is passing
Fixes #42.
226 lines
6.7 KiB
C++
226 lines
6.7 KiB
C++
#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 <luna/CType.h>
|
|
|
|
Result<SharedPtr<VFS::Inode>> MasterPTY::create_pair(int index)
|
|
{
|
|
auto master = TRY(make_shared<MasterPTY>());
|
|
auto slave = TRY(make_shared<SlavePTY>());
|
|
|
|
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<VFS::Inode>)master;
|
|
}
|
|
|
|
Result<void> 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<void> 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<usize> MasterPTY::read(u8* buf, usize, usize length) const
|
|
{
|
|
length = m_buffer.dequeue_data(buf, length);
|
|
|
|
return length;
|
|
}
|
|
|
|
Result<usize> MasterPTY::write(const u8* buf, usize, usize length)
|
|
{
|
|
for (usize i = 0; i < length; i++) { TRY(handle_input(buf[i])); }
|
|
|
|
return length;
|
|
}
|
|
|
|
Result<u64> 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);
|
|
}
|
|
}
|