diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 3d330b8f..3e10f980 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -86,6 +86,7 @@ set(KERNEL_SOURCES kernel/Storage/StorageDevice.cpp kernel/Syscall.cpp kernel/Terminal/FramebufferTerminal.cpp + kernel/Terminal/PseudoTerminal.cpp kernel/Terminal/Serial.cpp kernel/Terminal/TTY.cpp kernel/Terminal/VirtualTTY.cpp diff --git a/kernel/include/kernel/Device/DeviceNumbers.h b/kernel/include/kernel/Device/DeviceNumbers.h index 0f019e13..cb57f9d2 100644 --- a/kernel/include/kernel/Device/DeviceNumbers.h +++ b/kernel/include/kernel/Device/DeviceNumbers.h @@ -9,7 +9,7 @@ namespace Kernel { Framebuffer = 1, TTY, - Serial, + PTSMaster, Null, Zero, Debug, diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 029bc5ce..a2b03488 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -174,6 +174,8 @@ namespace Kernel BAN::ErrorOr sys_ttyname(int fildes, char* storage); BAN::ErrorOr sys_isatty(int fildes); + BAN::ErrorOr sys_posix_openpt(int flags); + BAN::ErrorOr sys_ptsname(int fildes, char* buffer, size_t buffer_len); BAN::ErrorOr sys_tty_ctrl(int fildes, int command, int flags); diff --git a/kernel/include/kernel/Terminal/PseudoTerminal.h b/kernel/include/kernel/Terminal/PseudoTerminal.h new file mode 100644 index 00000000..e3f0d5c9 --- /dev/null +++ b/kernel/include/kernel/Terminal/PseudoTerminal.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include + +namespace Kernel +{ + + class PseudoTerminalSlave; + + class PseudoTerminalMaster final : public CharacterDevice, public BAN::Weakable + { + public: + static BAN::ErrorOr> create(mode_t, uid_t, gid_t); + + dev_t rdev() const override { return m_rdev; } + BAN::StringView name() const override { return ""_sv; } + + BAN::ErrorOr> slave(); + + BAN::ErrorOr ptsname(); + + void putchar(uint8_t ch); + + protected: + BAN::ErrorOr read_impl(off_t, BAN::ByteSpan) override; + BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan) override; + + bool can_read_impl() const override { SpinLockGuard _(m_buffer_lock); return m_buffer_size > 0; } + bool can_write_impl() const override { SpinLockGuard _(m_buffer_lock); return m_buffer_size < m_buffer->size(); } + bool has_error_impl() const override { return false; } + + private: + PseudoTerminalMaster(BAN::UniqPtr, mode_t, uid_t, gid_t); + ~PseudoTerminalMaster(); + + private: + BAN::WeakPtr m_slave; + + mutable SpinLock m_buffer_lock; + ThreadBlocker m_buffer_blocker; + BAN::UniqPtr m_buffer; + size_t m_buffer_tail { 0 }; + size_t m_buffer_size { 0 }; + + const dev_t m_rdev; + + friend class BAN::RefPtr; + }; + + class PseudoTerminalSlave final : public TTY, public BAN::Weakable + { + public: + static BAN::ErrorOr> create(BAN::String&& name, mode_t, uid_t, gid_t); + + BAN::StringView name() const override { return m_name; } + + uint32_t height() const override { return m_height; } + uint32_t width() const override { return m_width; } + + void clear() override; + + protected: + void putchar_impl(uint8_t ch) override; + + private: + PseudoTerminalSlave(BAN::UniqPtr, BAN::String&& name, mode_t, uid_t, gid_t); + + private: + BAN::String m_name; + + BAN::WeakPtr m_master; + BAN::UniqPtr m_buffer; + size_t m_buffer_tail { 0 }; + size_t m_buffer_size { 0 }; + + uint32_t m_width { 0 }; + uint32_t m_height { 0 }; + + friend class PseudoTerminalMaster; + friend class BAN::RefPtr; + }; + +} diff --git a/kernel/include/kernel/Terminal/Serial.h b/kernel/include/kernel/Terminal/Serial.h index 69bc1c42..5bca55bc 100644 --- a/kernel/include/kernel/Terminal/Serial.h +++ b/kernel/include/kernel/Terminal/Serial.h @@ -63,11 +63,6 @@ namespace Kernel Serial m_serial; BAN::CircularQueue m_input; SpinLock m_input_lock; - - public: - virtual dev_t rdev() const override { return m_rdev; } - private: - const dev_t m_rdev; }; } diff --git a/kernel/include/kernel/Terminal/TTY.h b/kernel/include/kernel/Terminal/TTY.h index f7e96ebe..6dad0a74 100644 --- a/kernel/include/kernel/Terminal/TTY.h +++ b/kernel/include/kernel/Terminal/TTY.h @@ -42,6 +42,8 @@ namespace Kernel virtual uint32_t width() const = 0; void putchar(uint8_t ch); + virtual dev_t rdev() const final override { return m_rdev; } + virtual void clear() = 0; virtual BAN::ErrorOr chmod_impl(mode_t) override; @@ -54,17 +56,7 @@ namespace Kernel virtual bool has_error_impl() const override { return false; } protected: - TTY(mode_t mode, uid_t uid, gid_t gid) - : CharacterDevice(mode, uid, gid) - { - // FIXME: add correct baud and flags - m_termios.c_iflag = 0; - m_termios.c_oflag = 0; - m_termios.c_cflag = CS8; - m_termios.c_lflag = ECHO | ICANON; - m_termios.c_ospeed = B38400; - m_termios.c_ispeed = B38400; - } + TTY(mode_t mode, uid_t uid, gid_t gid); virtual void putchar_impl(uint8_t ch) = 0; virtual BAN::ErrorOr read_impl(off_t, BAN::ByteSpan) override; @@ -79,6 +71,8 @@ namespace Kernel termios m_termios; private: + const dev_t m_rdev; + pid_t m_foreground_pgrp { 0 }; struct tty_ctrl_t diff --git a/kernel/include/kernel/Terminal/VirtualTTY.h b/kernel/include/kernel/Terminal/VirtualTTY.h index a01147e8..a2ac4e2e 100644 --- a/kernel/include/kernel/Terminal/VirtualTTY.h +++ b/kernel/include/kernel/Terminal/VirtualTTY.h @@ -83,11 +83,6 @@ namespace Kernel bool m_show_cursor { true }; TerminalDriver* m_terminal_driver { nullptr }; - - public: - virtual dev_t rdev() const override { return m_rdev; } - private: - const dev_t m_rdev; }; } diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 8689ef80..51904452 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -1734,6 +1735,50 @@ namespace Kernel return 0; } + BAN::ErrorOr Process::sys_posix_openpt(int flags) + { + if (flags & ~(O_RDWR | O_NOCTTY)) + return BAN::Error::from_errno(EINVAL); + + mode_t mode = 0440; + if (flags & O_WRONLY) + mode = 0660; + + auto pts_master = TRY(PseudoTerminalMaster::create(mode, m_credentials.ruid(), m_credentials.rgid())); + auto pts_slave = TRY(pts_master->slave()); + + VirtualFileSystem::File file; + file.inode = pts_master; + TRY(file.canonical_path.append(pts_master->name())); + + LockGuard _(m_process_lock); + + int pts_master_fd = TRY(m_open_file_descriptors.open(file, flags)); + + if (!(flags & O_NOCTTY) && is_session_leader() && !m_controlling_terminal) + m_controlling_terminal = (TTY*)pts_slave.ptr(); + + return pts_master_fd; + } + + BAN::ErrorOr Process::sys_ptsname(int fildes, char* buffer, size_t buffer_len) + { + LockGuard _(m_process_lock); + TRY(validate_pointer_access(buffer, buffer_len)); + + auto inode = TRY(m_open_file_descriptors.inode_of(fildes)); + if (TRY(m_open_file_descriptors.path_of(fildes)) != ""_sv) + return BAN::Error::from_errno(ENOTTY); + + auto ptsname = TRY(static_cast(inode.ptr())->ptsname()); + + const size_t to_copy = BAN::Math::min(ptsname.size() + 1, buffer_len); + memcpy(buffer, ptsname.data(), to_copy); + buffer[to_copy] = '\0'; + + return 0; + } + BAN::ErrorOr Process::sys_tty_ctrl(int fildes, int command, int flags) { LockGuard _(m_process_lock); @@ -1936,10 +1981,11 @@ namespace Kernel if (!inode->is_tty()) return BAN::Error::from_errno(ENOTTY); - if ((TTY*)inode.ptr() != m_controlling_terminal.ptr()) + auto* tty = static_cast(inode.ptr()); + if (tty != m_controlling_terminal.ptr()) return BAN::Error::from_errno(ENOTTY); - ((TTY*)inode.ptr())->set_foreground_pgrp(pgrp); + tty->set_foreground_pgrp(pgrp); return 0; } diff --git a/kernel/kernel/Terminal/PseudoTerminal.cpp b/kernel/kernel/Terminal/PseudoTerminal.cpp new file mode 100644 index 00000000..503f1534 --- /dev/null +++ b/kernel/kernel/Terminal/PseudoTerminal.cpp @@ -0,0 +1,142 @@ +#include +#include +#include + +#include + +namespace Kernel +{ + + BAN::Atomic s_pts_master_minor = 0; + BAN::Atomic s_pts_slave_number = 0; + + BAN::ErrorOr> PseudoTerminalMaster::create(mode_t mode, uid_t uid, gid_t gid) + { + auto pts_master_buffer = TRY(VirtualRange::create_to_vaddr_range( + PageTable::kernel(), + KERNEL_OFFSET, static_cast(-1), + 16 * PAGE_SIZE, + PageTable::Flags::ReadWrite | PageTable::Flags::Present, true + )); + auto pts_master = TRY(BAN::RefPtr::create(BAN::move(pts_master_buffer), mode, uid, gid)); + + auto pts_slave_buffer = TRY(VirtualRange::create_to_vaddr_range( + PageTable::kernel(), + KERNEL_OFFSET, static_cast(-1), + 16 * PAGE_SIZE, + PageTable::Flags::ReadWrite | PageTable::Flags::Present, true + )); + auto pts_slave_name = TRY(BAN::String::formatted("pts{}", s_pts_slave_number++)); + auto pts_slave = TRY(BAN::RefPtr::create(BAN::move(pts_slave_buffer), BAN::move(pts_slave_name), 0610, uid, gid)); + + pts_master->m_slave = TRY(pts_slave->get_weak_ptr()); + pts_slave->m_master = TRY(pts_master->get_weak_ptr()); + + DevFileSystem::get().add_device(pts_slave); + + return pts_master; + } + + PseudoTerminalMaster::PseudoTerminalMaster(BAN::UniqPtr buffer, mode_t mode, uid_t uid, gid_t gid) + : CharacterDevice(mode, uid, gid) + , m_buffer(BAN::move(buffer)) + , m_rdev(makedev(DeviceNumber::PTSMaster, s_pts_master_minor++)) + { } + + PseudoTerminalMaster::~PseudoTerminalMaster() + { + if (auto slave = m_slave.lock()) + DevFileSystem::get().remove_device(slave); + } + + BAN::ErrorOr> PseudoTerminalMaster::slave() + { + if (auto slave = m_slave.lock()) + return slave; + return BAN::Error::from_errno(ENODEV); + } + + BAN::ErrorOr PseudoTerminalMaster::ptsname() + { + if (auto slave = m_slave.lock()) + return TRY(BAN::String::formatted("/dev/{}", slave->name())); + return BAN::Error::from_errno(ENODEV); + } + + void PseudoTerminalMaster::putchar(uint8_t ch) + { + SpinLockGuard _(m_buffer_lock); + + if (m_buffer_size == m_buffer->size()) + { + dwarnln("PseudoTerminalMaster buffer full"); + m_buffer_tail = (m_buffer_tail + 1) % m_buffer->size(); + m_buffer_size--; + } + + *reinterpret_cast(m_buffer->vaddr() + (m_buffer_tail + m_buffer_size) % m_buffer->size()) = ch; + m_buffer_size++; + } + + BAN::ErrorOr PseudoTerminalMaster::read_impl(off_t, BAN::ByteSpan buffer) + { + auto state = m_buffer_lock.lock(); + + while (m_buffer_size == 0) + { + m_buffer_lock.unlock(state); + TRY(Thread::current().block_or_eintr_indefinite(m_buffer_blocker)); + m_buffer_lock.lock(); + } + + const size_t to_copy = BAN::Math::min(buffer.size(), m_buffer_size); + + if (m_buffer_tail + to_copy < m_buffer->size()) + memcpy(buffer.data(), reinterpret_cast(m_buffer->vaddr() + m_buffer_tail), to_copy); + else + { + const size_t before_wrap = m_buffer_size - m_buffer_tail; + const size_t after_wrap = to_copy - before_wrap; + + memcpy(buffer.data(), reinterpret_cast(m_buffer->vaddr() + m_buffer_tail), before_wrap); + memcpy(buffer.data() + before_wrap, reinterpret_cast(m_buffer->vaddr()), after_wrap); + } + + m_buffer_size -= to_copy; + m_buffer_tail = (m_buffer_tail + to_copy) % m_buffer->size(); + + m_buffer_lock.unlock(state); + + return to_copy; + } + + BAN::ErrorOr PseudoTerminalMaster::write_impl(off_t, BAN::ConstByteSpan buffer) + { + auto slave = m_slave.lock(); + if (!slave) + return BAN::Error::from_errno(ENODEV); + + for (size_t i = 0; i < buffer.size(); i++) + slave->handle_input_byte(buffer[i]); + return buffer.size(); + } + + PseudoTerminalSlave::PseudoTerminalSlave(BAN::UniqPtr buffer, BAN::String&& name, mode_t mode, uid_t uid, gid_t gid) + : TTY(mode, uid, gid) + , m_name(BAN::move(name)) + , m_buffer(BAN::move(buffer)) + {} + + void PseudoTerminalSlave::clear() + { + const char message[] { '\e', '[', '2', 'J' }; + (void)write_impl(0, BAN::ConstByteSpan::from(message)); + } + + void PseudoTerminalSlave::putchar_impl(uint8_t ch) + { + if (auto master = m_master.lock()) + master->putchar(ch); + } + +} diff --git a/kernel/kernel/Terminal/Serial.cpp b/kernel/kernel/Terminal/Serial.cpp index 2c9c41d3..5bfdc2fc 100644 --- a/kernel/kernel/Terminal/Serial.cpp +++ b/kernel/kernel/Terminal/Serial.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -7,7 +6,6 @@ #include #include -#include #define MAX_BAUD 115200 @@ -33,6 +31,8 @@ namespace Kernel { + static BAN::Atomic s_next_tty_number = 0; + static constexpr uint16_t s_serial_ports[] = { 0x3F8, 0x2F8, 0x3E8, 0x2E8, 0x5F8, 0x4F8, 0x5E8, 0x4E8 }; static BAN::Array s_serial_drivers; static bool s_has_devices { false }; @@ -40,12 +40,6 @@ namespace Kernel static BAN::RefPtr s_com1; static BAN::RefPtr s_com2; - static dev_t next_rdev() - { - static dev_t minor = 0; - return makedev(DeviceNumber::Serial, minor++); - } - void Serial::initialize() { int count = 0; @@ -179,9 +173,8 @@ namespace Kernel SerialTTY::SerialTTY(Serial serial) : TTY(0600, 0, 0) - , m_name(MUST(BAN::String::formatted("ttyS{}", minor(rdev())))) + , m_name(MUST(BAN::String::formatted("ttyS{}", s_next_tty_number++))) , m_serial(serial) - , m_rdev(next_rdev()) {} BAN::ErrorOr> SerialTTY::create(Serial serial) diff --git a/kernel/kernel/Terminal/TTY.cpp b/kernel/kernel/Terminal/TTY.cpp index fcce60d6..2f5b3d69 100644 --- a/kernel/kernel/Terminal/TTY.cpp +++ b/kernel/kernel/Terminal/TTY.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,25 @@ namespace Kernel static BAN::RefPtr s_tty; + static dev_t next_tty_rdev() + { + static BAN::Atomic s_minor = 0; + return makedev(DeviceNumber::TTY, s_minor++); + } + + TTY::TTY(mode_t mode, uid_t uid, gid_t gid) + : CharacterDevice(mode, uid, gid) + , m_rdev(next_tty_rdev()) + { + // FIXME: add correct baud and flags + m_termios.c_iflag = 0; + m_termios.c_oflag = 0; + m_termios.c_cflag = CS8; + m_termios.c_lflag = ECHO | ICANON; + m_termios.c_ospeed = B38400; + m_termios.c_ispeed = B38400; + } + BAN::RefPtr TTY::current() { ASSERT(s_tty); diff --git a/kernel/kernel/Terminal/VirtualTTY.cpp b/kernel/kernel/Terminal/VirtualTTY.cpp index b4279c44..7f37e289 100644 --- a/kernel/kernel/Terminal/VirtualTTY.cpp +++ b/kernel/kernel/Terminal/VirtualTTY.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -10,7 +9,6 @@ #include #include -#include #define BEL 0x07 #define BS 0x08 @@ -25,11 +23,7 @@ namespace Kernel { - static dev_t next_rdev() - { - static dev_t minor = 0; - return makedev(DeviceNumber::TTY, minor++); - } + static BAN::Atomic s_next_tty_number = 0; BAN::ErrorOr> VirtualTTY::create(TerminalDriver* driver) { @@ -43,9 +37,8 @@ namespace Kernel VirtualTTY::VirtualTTY(TerminalDriver* driver) : TTY(0600, 0, 0) - , m_name(MUST(BAN::String::formatted("tty{}", minor(rdev())))) + , m_name(MUST(BAN::String::formatted("tty{}", s_next_tty_number++))) , m_terminal_driver(driver) - , m_rdev(next_rdev()) { m_width = m_terminal_driver->width(); m_height = m_terminal_driver->height(); diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index 1d947e7a..073a3f1e 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -88,6 +88,8 @@ __BEGIN_DECLS O(SYS_SIGPENDING, sigpending) \ O(SYS_SIGPROCMASK, sigprocmask) \ O(SYS_SETITIMER, setitimer) \ + O(SYS_POSIX_OPENPT, posix_openpt) \ + O(SYS_PTSNAME, ptsname) \ enum Syscall { diff --git a/userspace/libraries/LibC/stdlib.cpp b/userspace/libraries/LibC/stdlib.cpp index e96785f9..bcede8f7 100644 --- a/userspace/libraries/LibC/stdlib.cpp +++ b/userspace/libraries/LibC/stdlib.cpp @@ -519,6 +519,31 @@ char* mktemp(char*) ASSERT_NOT_REACHED(); } +int posix_openpt(int oflag) +{ + return syscall(SYS_POSIX_OPENPT, oflag); +} + +int grantpt(int) +{ + // currently posix_openpt() does this + return 0; +} + +int unlockpt(int) +{ + // currently posix_openpt() does this + return 0; +} + +char* ptsname(int fildes) +{ + static char buffer[PATH_MAX]; + if (syscall(SYS_PTSNAME, fildes, buffer, sizeof(buffer)) == -1) + return nullptr; + return buffer; +} + size_t mbstowcs(wchar_t* __restrict pwcs, const char* __restrict s, size_t n) { auto* us = reinterpret_cast(s);