From c323a812a5dd24e710a7ab8dce5ef649878f72f3 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 14 Oct 2023 20:41:34 +0200 Subject: [PATCH] kernel+libc+terminal+wind: Add support for POSIX sessions Fixes #42. --- kernel/src/api/Syscall.h | 3 ++- kernel/src/fs/devices/MasterPTY.cpp | 37 +++++------------------------ kernel/src/fs/devices/MasterPTY.h | 1 + kernel/src/fs/devices/SlavePTY.cpp | 21 +++++++++++++++- kernel/src/sys/exec.cpp | 3 +++ kernel/src/sys/id.cpp | 34 ++++++++++++++++++++++++-- kernel/src/thread/Scheduler.h | 13 ++++++++++ kernel/src/thread/Thread.cpp | 20 +++++++++++++++- kernel/src/thread/Thread.h | 6 +++++ libc/include/bits/termios.h | 1 + libc/include/unistd.h | 6 +++++ libc/src/unistd.cpp | 12 ++++++++++ terminal/TerminalWidget.cpp | 6 ++++- wind/main.cpp | 4 ++-- 14 files changed, 128 insertions(+), 39 deletions(-) diff --git a/kernel/src/api/Syscall.h b/kernel/src/api/Syscall.h index 55003ab4..d85c6914 100644 --- a/kernel/src/api/Syscall.h +++ b/kernel/src/api/Syscall.h @@ -8,7 +8,8 @@ _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) _e(umask) _e(linkat) _e(faccessat) \ _e(pivot_root) _e(sigreturn) _e(sigaction) _e(kill) _e(sigprocmask) _e(setpgid) _e(isatty) \ _e(getpgid) _e(socket) _e(bind) _e(connect) _e(listen) _e(accept) _e(poll) _e(msync) \ - _e(truncate) _e(ftruncate) _e(utimensat) _e(alarm) _e(pledge) _e(memstat) + _e(truncate) _e(ftruncate) _e(utimensat) _e(alarm) _e(pledge) _e(memstat) _e(setsid) \ + _e(getsid) enum Syscalls { diff --git a/kernel/src/fs/devices/MasterPTY.cpp b/kernel/src/fs/devices/MasterPTY.cpp index 150e1090..9cceb7f2 100644 --- a/kernel/src/fs/devices/MasterPTY.cpp +++ b/kernel/src/fs/devices/MasterPTY.cpp @@ -203,37 +203,6 @@ Result MasterPTY::ioctl(int request, void* arg) switch (request) { - case TCGETS: { - return MemoryManager::copy_to_user_typed((struct termios*)arg, &m_settings) ? 0 : err(EFAULT); - } - case TCSETS: { - if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &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); - - bool pgid_exists = false; - Scheduler::for_each_in_process_group(pgid, [&pgid_exists](Thread*) { - pgid_exists = true; - return false; - }); - if (!pgid_exists) return err(EPERM); - - m_foreground_process_group = pgid; - return 0; - } - case TIOCGPGRP: { - pid_t pgid = 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_window)) return err(EFAULT); - return 0; - } case TIOCGPTN: { if (!MemoryManager::copy_to_user_typed((int*)arg, &m_index)) return err(EFAULT); return 0; @@ -247,4 +216,10 @@ 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); + } } diff --git a/kernel/src/fs/devices/MasterPTY.h b/kernel/src/fs/devices/MasterPTY.h index 2c7c0098..1a5d2f6f 100644 --- a/kernel/src/fs/devices/MasterPTY.h +++ b/kernel/src/fs/devices/MasterPTY.h @@ -65,6 +65,7 @@ class MasterPTY : public VFS::DeviceInode mutable Buffer m_buffer; SharedPtr m_slave; mutable Option m_foreground_process_group; + mutable Option m_session; struct winsize m_window; typedef Vector Line; diff --git a/kernel/src/fs/devices/SlavePTY.cpp b/kernel/src/fs/devices/SlavePTY.cpp index 91fd333a..31057ccc 100644 --- a/kernel/src/fs/devices/SlavePTY.cpp +++ b/kernel/src/fs/devices/SlavePTY.cpp @@ -63,18 +63,23 @@ Result SlavePTY::ioctl(int request, void* arg) 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; - Scheduler::for_each_in_process_group(pgid, [&pgid_exists](Thread*) { + pid_t sid; + Scheduler::for_each_in_process_group(pgid, [&pgid_exists, &sid](Thread* thread) { pgid_exists = true; + sid = thread->sid; // 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; @@ -83,6 +88,20 @@ Result SlavePTY::ioctl(int request, void* arg) 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](Thread* thread) { + thread->controlling_terminal = this; + return true; + }); + + m_master->m_session = current->sid; + + return 0; + } default: return err(EINVAL); } } diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index f9dfeb95..a86700f7 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -169,6 +169,9 @@ Result sys_fork(Registers* regs, SyscallArgs) thread->parent = current; thread->promises = current->promises; thread->execpromises = current->execpromises; + thread->controlling_terminal = current->controlling_terminal; + thread->pgid = current->pgid; + thread->sid = current->sid; for (int i = 0; i < FD_MAX; i++) { thread->fd_table[i] = current->fd_table[i]; } diff --git a/kernel/src/sys/id.cpp b/kernel/src/sys/id.cpp index 383745a0..7b7bb7c1 100644 --- a/kernel/src/sys/id.cpp +++ b/kernel/src/sys/id.cpp @@ -130,18 +130,21 @@ Result sys_setpgid(Registers*, SyscallArgs args) auto* thread = TRY(Result::from_option(Scheduler::find_by_pid(pid), ESRCH)); if (thread != current && thread->parent != current) return err(ESRCH); - // FIXME: Weird session stuff, we don't have that currently. + if (thread->is_session_leader() || thread->sid != current->sid) return err(EPERM); if (thread->has_called_exec) return err(EPERM); if (pgid != current->id) { bool pgid_exists = false; - Scheduler::for_each_in_process_group(pgid, [&pgid_exists](Thread*) { + pid_t sid; + Scheduler::for_each_in_process_group(pgid, [&pgid_exists, &sid](Thread* t) { pgid_exists = true; + sid = t->sid; // this should be the same for all threads in the process group return false; }); if (!pgid_exists) return err(EPERM); + if (sid != thread->sid) return err(EPERM); } thread->pgid = (u64)pgid; @@ -164,6 +167,33 @@ Result sys_getpgid(Registers*, SyscallArgs args) return (u64)thread->pgid; } +Result sys_setsid(Registers*, SyscallArgs) +{ + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_proc)); + + if (current->pgid == current->id) return err(EPERM); + + current->sid = current->pgid = current->id; + current->controlling_terminal = {}; + + return current->sid; +} + +Result sys_getsid(Registers*, SyscallArgs args) +{ + pid_t pid = (pid_t)args[0]; + + auto* current = Scheduler::current(); + TRY(check_pledge(current, Promise::p_stdio)); + + if (pid == 0) pid = current->id; + + auto* thread = TRY(Result::from_option(Scheduler::find_by_pid(pid), ESRCH)); + + return thread->sid; +} + Result sys_fchmodat(Registers*, SyscallArgs args) { int dirfd = (int)args[0]; diff --git a/kernel/src/thread/Scheduler.h b/kernel/src/thread/Scheduler.h index 5c9ec66d..1f9e4024 100644 --- a/kernel/src/thread/Scheduler.h +++ b/kernel/src/thread/Scheduler.h @@ -61,6 +61,19 @@ namespace Scheduler } } + template void for_each_in_session(pid_t sid, Callback callback) + { + for (Thread* current = g_threads.first().value_or(nullptr); current; + current = g_threads.next(current).value_or(nullptr)) + { + if (current->sid == sid) + { + bool should_continue = callback(current); + if (!should_continue) return; + } + } + } + void dump_state(); bool has_children(Thread* thread); diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index 3b030fa6..d9a6eade 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -93,6 +93,20 @@ Result> Thread::resolve_atfile(int dirfd, const String& pa return true; }); + if (is_session_leader()) + { + kinfoln("thread %d is exiting as a session leader, sending signals to session", id); + // FIXME: Send SIGHUP only to the foreground process group if the session has a controlling terminal. + Scheduler::for_each_in_session(sid, [this](Thread* thread) { + if (thread == this) return true; + thread->sid = 0; + thread->controlling_terminal = {}; + thread->send_signal(SIGHUP); + kinfoln("reparenting and sending SIGHUP to %d", thread->id); + return true; + }); + } + if (parent) { if (parent->state == ThreadState::Waiting) @@ -104,7 +118,11 @@ Result> Thread::resolve_atfile(int dirfd, const String& pa parent->wake_up(); } } - else { parent->send_signal(SIGCHLD); } + else + { + while (parent->pending_signals.get(SIGCHLD - 1)) kernel_yield(); + parent->send_signal(SIGCHLD); + } } state = ThreadState::Exited; diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index bcb35697..584700d2 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -78,6 +78,7 @@ struct Thread : public LinkedListNode pid_t id; pid_t pgid { 0 }; + pid_t sid { 0 }; Credentials auth; @@ -152,6 +153,11 @@ struct Thread : public LinkedListNode state = ThreadState::Runnable; } + bool is_session_leader() + { + return id == sid; + } + void init_regs_kernel(); void init_regs_user(); diff --git a/libc/include/bits/termios.h b/libc/include/bits/termios.h index 5273c236..dcb49588 100644 --- a/libc/include/bits/termios.h +++ b/libc/include/bits/termios.h @@ -46,5 +46,6 @@ struct winsize #define TIOCGPGRP 3 #define TIOCGWINSZ 4 #define TIOCGPTN 5 +#define TIOCSCTTY 6 #endif diff --git a/libc/include/unistd.h b/libc/include/unistd.h index aa7f5938..ef7cdca4 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -52,6 +52,9 @@ extern "C" /* Return a process' process group ID. */ pid_t getpgid(pid_t pid); + /* Return a process' session ID. */ + pid_t getsid(pid_t pid); + /* Set the current process' user IDs. */ int setuid(uid_t uid); @@ -67,6 +70,9 @@ extern "C" /* Set the current process or a child process's process group. */ int setpgid(pid_t pid, pid_t pgid); + /* Create a new session and make this process the leader of it. */ + pid_t setsid(void); + /* Change the owner and group of a file. */ int chown(const char* path, uid_t uid, gid_t gid); diff --git a/libc/src/unistd.cpp b/libc/src/unistd.cpp index 04be50ca..c856635c 100644 --- a/libc/src/unistd.cpp +++ b/libc/src/unistd.cpp @@ -527,4 +527,16 @@ extern "C" long rc = syscall(SYS_pledge, promises, execpromises); __errno_return(rc, int); } + + pid_t getsid(pid_t pid) + { + long rc = syscall(SYS_getsid, pid); + __errno_return(rc, pid_t); + } + + pid_t setsid() + { + long rc = syscall(SYS_setsid); + __errno_return(rc, pid_t); + } } diff --git a/terminal/TerminalWidget.cpp b/terminal/TerminalWidget.cpp index 76e93262..b998f5ce 100644 --- a/terminal/TerminalWidget.cpp +++ b/terminal/TerminalWidget.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -61,13 +62,16 @@ Result TerminalWidget::init(char* const* args) pid_t child = TRY(os::Process::fork()); if (child == 0) { + setsid(); + close(master); + ioctl(slave, TIOCSCTTY); + dup2(slave, STDIN_FILENO); dup2(slave, STDOUT_FILENO); dup2(slave, STDERR_FILENO); - setpgid(0, 0); tcsetpgrp(slave, getpid()); close(slave); diff --git a/wind/main.cpp b/wind/main.cpp index 6b0a8e32..0b4718d3 100644 --- a/wind/main.cpp +++ b/wind/main.cpp @@ -88,8 +88,6 @@ Result luna_main(int argc, char** argv) Mouse mouse_pointer { screen.canvas() }; - setpgid(0, 0); - int fd = open("/dev/null", O_RDONLY); if (fd >= 0) { @@ -109,6 +107,8 @@ Result luna_main(int argc, char** argv) } } + if (setsid() < 0) perror("setsid"); + auto server = TRY(os::LocalServer::create(socket_path, false)); TRY(server->listen(20));