From 0f81276efc87cc4fcd04dfb1979106cd3189129d Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Fri, 14 Aug 2026 10:33:20 +0200 Subject: [PATCH] feat: add inherited sessions and desktop session teardown --- kernel/src/Api/Process.hpp | 8 +++ kernel/src/Api/Syscall.cpp | 4 ++ kernel/src/Api/Syscall.hpp | 2 + kernel/src/Sched/Scheduler.cpp | 70 ++++++++++++++++++++++ kernel/src/Sched/Scheduler.hpp | 7 +++ programs/include/Api/Syscall.hpp | 2 + programs/include/libc/montauk.h | 10 ++++ programs/include/montauk/syscall.h | 6 ++ programs/man/syscalls.2 | 10 ++++ programs/src/desktop/main.cpp | 5 ++ programs/src/login/login.hpp | 2 + programs/src/login/login_input.cpp | 18 ++++++ programs/src/login/main.cpp | 1 + template/docs/syscalls.md | 2 + template/sysroot/include/Api/Syscall.hpp | 2 + template/sysroot/include/libc/montauk.h | 10 ++++ template/sysroot/include/montauk/syscall.h | 6 ++ 17 files changed, 165 insertions(+) diff --git a/kernel/src/Api/Process.hpp b/kernel/src/Api/Process.hpp index c52f30a..0f6eecc 100644 --- a/kernel/src/Api/Process.hpp +++ b/kernel/src/Api/Process.hpp @@ -159,6 +159,14 @@ namespace montauk::abi { return Sched::KillProcess(pid); } + static int Sys_SetSession() { + return Sched::CreateSession(); + } + + static int Sys_KillSession(int sessionId) { + return Sched::KillSession(sessionId); + } + static int Sys_SetUser(int pid, const char* name) { if (name == nullptr) return -1; auto* target = Sched::GetProcessByPid(pid); diff --git a/kernel/src/Api/Syscall.cpp b/kernel/src/Api/Syscall.cpp index 8aa3417..f221795 100644 --- a/kernel/src/Api/Syscall.cpp +++ b/kernel/src/Api/Syscall.cpp @@ -359,6 +359,10 @@ namespace montauk::abi { return (int64_t)Sys_ProcList((ProcInfo*)frame->arg1, (int)frame->arg2); case SYS_KILL: return (int64_t)Sys_Kill((int)frame->arg1); + case SYS_SETSESSION: + return (int64_t)Sys_SetSession(); + case SYS_KILLSESSION: + return (int64_t)Sys_KillSession((int)frame->arg1); case SYS_DEVLIST: if ((int64_t)frame->arg2 < 0) return -1; if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(DevInfo), true)) return -1; diff --git a/kernel/src/Api/Syscall.hpp b/kernel/src/Api/Syscall.hpp index 9bc6cef..8179dd1 100644 --- a/kernel/src/Api/Syscall.hpp +++ b/kernel/src/Api/Syscall.hpp @@ -194,6 +194,8 @@ namespace montauk::abi { /* Process.hpp */ static constexpr uint64_t SYS_SETUSER = 92; static constexpr uint64_t SYS_GETUSER = 93; + static constexpr uint64_t SYS_SETSESSION = 174; + static constexpr uint64_t SYS_KILLSESSION = 175; /* Filesystem.hpp */ static constexpr uint64_t SYS_FRENAME = 94; diff --git a/kernel/src/Sched/Scheduler.cpp b/kernel/src/Sched/Scheduler.cpp index 9806a91..9b2e6bd 100644 --- a/kernel/src/Sched/Scheduler.cpp +++ b/kernel/src/Sched/Scheduler.cpp @@ -297,6 +297,7 @@ namespace Sched { processTable[i].environment[0] = '\0'; processTable[i].environmentLength = 1; processTable[i].user[0] = '\0'; + processTable[i].sessionId = -1; processTable[i].cwd[0] = '\0'; processTable[i].runningOnCpu = -1; processTable[i].killPending = false; @@ -560,6 +561,13 @@ namespace Sched { } } + // Process sessions are explicit groups used by supervisors such as + // login.elf. A session leader opts in with CreateSession(); all of its + // subsequently spawned descendants inherit the same stable ID. + proc.sessionId = (parentPrimarySlot >= 0) + ? processTable[parentPrimarySlot].sessionId + : -1; + { if (parentSlot >= 0 && processTable[parentSlot].cwd[0]) { int i = 0; @@ -1747,6 +1755,68 @@ namespace Sched { return 0; } + int CreateSession() { + schedLock.Acquire(); + Process* proc = GetCurrentProcessPtr(); + if (proc == nullptr) { + schedLock.Release(); + return -1; + } + proc->sessionId = proc->pid; + int sessionId = proc->sessionId; + schedLock.Release(); + return sessionId; + } + + int KillSession(int sessionId) { + if (sessionId < 0) return -1; + + int callerPid = GetCurrentPid(); + int killed = 0; + + // Mark the complete group in one scheduler-lock transaction. New + // children inherit sessionId, so a supervisor can repeat this call + // until zero is returned to close the small spawn/exit race cleanly. + schedLock.Acquire(); + for (int i = 0; i < MaxProcesses; i++) { + Process& primary = processTable[i]; + if (primary.primarySlot != i || primary.pid == callerPid || + primary.sessionId != sessionId) { + continue; + } + auto state = primary.state; + if (state != ProcessState::Ready && state != ProcessState::Running && + state != ProcessState::Blocked) { + continue; + } + + primary.exitCode = 256 + 9; /* killed (SIGKILL) */ + primary.killPending = true; + if (primary.state == ProcessState::Blocked) { + primary.state = ProcessState::Ready; + readyCount++; + primary.waitingForPid = -1; + primary.waitingOnObject = nullptr; + primary.sleepUntilTick = 0; + } + + for (int j = 0; j < MaxProcesses; j++) { + if (j == i || processTable[j].primarySlot != i) continue; + if (processTable[j].state == ProcessState::Running) { + processTable[j].killPending = true; + } + } + killed++; + } + schedLock.Release(); + + if (killed > 0) { + KickOneIdleCpu(Smp::GetCurrentCpuData() + ? Smp::GetCurrentCpuData()->cpuIndex : -1); + } + return killed; + } + int LookupExitCode(int pid) { schedLock.Acquire(); int code = 0; diff --git a/kernel/src/Sched/Scheduler.hpp b/kernel/src/Sched/Scheduler.hpp index 95e3b45..d2701e8 100644 --- a/kernel/src/Sched/Scheduler.hpp +++ b/kernel/src/Sched/Scheduler.hpp @@ -86,6 +86,7 @@ namespace Sched { char environment[EnvironmentBytes]; // NUL-separated NAME=VALUE entries uint32_t environmentLength; char user[32]; // Owner user name (inherited from parent on spawn) + int sessionId; // Process-session leader PID (inherited on spawn) char cwd[256]; // Absolute current working directory // Thread-local storage. fsBase is loaded into IA32_FS_BASE when @@ -252,6 +253,12 @@ namespace Sched { // Returns 0 on success, -1 on failure. int KillProcess(int pid); + // Start a new process session for the caller, or terminate every process + // belonging to a session. KillSession returns the number of live members + // it signalled; callers can repeat until it returns zero. + int CreateSession(); + int KillSession(int sessionId); + // Find a process by PID (returns nullptr if not found or not alive) Process* GetProcessByPid(int pid); diff --git a/programs/include/Api/Syscall.hpp b/programs/include/Api/Syscall.hpp index ab9836d..b2ed837 100644 --- a/programs/include/Api/Syscall.hpp +++ b/programs/include/Api/Syscall.hpp @@ -125,6 +125,8 @@ namespace montauk::abi { // User management static constexpr uint64_t SYS_SETUSER = 92; static constexpr uint64_t SYS_GETUSER = 93; + static constexpr uint64_t SYS_SETSESSION = 174; + static constexpr uint64_t SYS_KILLSESSION = 175; static constexpr uint64_t SYS_FRENAME = 94; static constexpr uint64_t SYS_GETCWD = 95; static constexpr uint64_t SYS_CHDIR = 96; diff --git a/programs/include/libc/montauk.h b/programs/include/libc/montauk.h index 4b9ce7c..98d9aca 100644 --- a/programs/include/libc/montauk.h +++ b/programs/include/libc/montauk.h @@ -76,6 +76,8 @@ extern "C" { #define MTK_SYS_GETENVIRON 171 #define MTK_SYS_SETENVIRON 172 #define MTK_SYS_SPAWN_ENV 173 +#define MTK_SYS_SETSESSION 174 +#define MTK_SYS_KILLSESSION 175 #define MTK_SYS_CHILDIO_READ 50 #define MTK_SYS_CHILDIO_WRITE 51 #define MTK_SYS_CHILDIO_WRITEKEY 52 @@ -428,6 +430,14 @@ static inline int mtk_kill(int pid) { return (int)_mtk_syscall1(MTK_SYS_KILL, (long)pid); } +static inline int mtk_setsession(void) { + return (int)_mtk_syscall0(MTK_SYS_SETSESSION); +} + +static inline int mtk_killsession(int session_id) { + return (int)_mtk_syscall1(MTK_SYS_KILLSESSION, (long)session_id); +} + static inline int mtk_proclist(mtk_procinfo *buf, int max) { return (int)_mtk_syscall2(MTK_SYS_PROCLIST, (long)buf, (long)max); } diff --git a/programs/include/montauk/syscall.h b/programs/include/montauk/syscall.h index 74c744b..7031953 100644 --- a/programs/include/montauk/syscall.h +++ b/programs/include/montauk/syscall.h @@ -458,6 +458,12 @@ namespace montauk { inline int kill(int pid) { return (int)syscall1(montauk::abi::SYS_KILL, (uint64_t)pid); } + inline int setsession() { + return (int)syscall0(montauk::abi::SYS_SETSESSION); + } + inline int killsession(int sessionId) { + return (int)syscall1(montauk::abi::SYS_KILLSESSION, (uint64_t)sessionId); + } inline int devlist(montauk::abi::DevInfo* buf, int max) { return (int)syscall2(montauk::abi::SYS_DEVLIST, (uint64_t)buf, (uint64_t)max); } diff --git a/programs/man/syscalls.2 b/programs/man/syscalls.2 index ac74158..a8de620 100644 --- a/programs/man/syscalls.2 +++ b/programs/man/syscalls.2 @@ -65,6 +65,16 @@ Terminate another process by PID. int montauk::kill(int pid); +.B SYS_SETSESSION (174) + Make the calling process the leader of a new process session. Processes + spawned afterward inherit the session identifier. + int montauk::setsession(); + +.B SYS_KILLSESSION (175) + Terminate all live processes in a process session. Returns the number of + members signalled; repeat until zero to wait for complete teardown. + int montauk::killsession(int sessionId); + .B SYS_CHDIR (96) Change the calling process's current working directory. int montauk::chdir(const char* path); diff --git a/programs/src/desktop/main.cpp b/programs/src/desktop/main.cpp index 95c6275..d27053b 100644 --- a/programs/src/desktop/main.cpp +++ b/programs/src/desktop/main.cpp @@ -895,6 +895,11 @@ static bool existing_desktop_running() { } extern "C" void _start() { + // External applications and all of their descendants inherit this stable + // desktop-session ID. login.elf uses it to tear down the complete session + // after Log Out, Shutdown, or Reboot returns control to the supervisor. + montauk::setsession(); + if (existing_desktop_running()) { gui::dialogs::message_box( "Desktop Already Running", diff --git a/programs/src/login/login.hpp b/programs/src/login/login.hpp index 73eb527..283da32 100644 --- a/programs/src/login/login.hpp +++ b/programs/src/login/login.hpp @@ -40,6 +40,8 @@ enum SessionMode { SESSION_CONSOLE, }; +void terminate_desktop_session(int session_id); + struct LoginState { gui::Framebuffer fb; int screen_w, screen_h; diff --git a/programs/src/login/login_input.cpp b/programs/src/login/login_input.cpp index f9c5167..fed0def 100644 --- a/programs/src/login/login_input.cpp +++ b/programs/src/login/login_input.cpp @@ -63,6 +63,9 @@ void launch_session(LoginState* ls) { if (pid >= 0) { montauk::setuser(pid, ls->username); montauk::waitpid(pid); + if (ls->session_mode == SESSION_DESKTOP) { + terminate_desktop_session(pid); + } } // The desktop hands off a graceful power-off by posting a request and @@ -99,6 +102,21 @@ gui::Rect login_field_rect(const LoginLayout& lo, LoginState* ls, int idx) { } // namespace +void terminate_desktop_session(int session_id) { + if (session_id < 0) return; + + // Killing is asynchronous because a member may currently be executing on + // another CPU or inside a syscall. Repeat until the kernel observes the + // session empty; no member can spawn another process after that point. + // + + const int timeout_ms = 2000; + for (int waited = 0; waited < timeout_ms; waited++) { + if (montauk::killsession(session_id) <= 0) return; + montauk::sleep_ms(1); + } +} + LoginField get_field(LoginState* ls, int field) { if (ls->mode == MODE_FIRST_BOOT) { switch (field) { diff --git a/programs/src/login/main.cpp b/programs/src/login/main.cpp index 944d9d4..3ffcd3a 100644 --- a/programs/src/login/main.cpp +++ b/programs/src/login/main.cpp @@ -29,6 +29,7 @@ static void maybe_run_setup_session() { if (pid >= 0) { montauk::setuser(pid, user); montauk::waitpid(pid); + terminate_desktop_session(pid); } montauk::user::clear_session(); diff --git a/template/docs/syscalls.md b/template/docs/syscalls.md index b1e5496..22b0932 100644 --- a/template/docs/syscalls.md +++ b/template/docs/syscalls.md @@ -36,6 +36,8 @@ int spawn(const char* path, const char* args); // Spawn child, inheriting int waitpid(int pid); // Wait for process to exit int kill(int pid); // Kill a process int proclist(ProcInfo* buf, int max); // List all processes (returns count) +int setsession(); // Start a new inherited process session +int killsession(int sessionId); // Kill live members; repeat until zero ``` **`ProcInfo` struct:** diff --git a/template/sysroot/include/Api/Syscall.hpp b/template/sysroot/include/Api/Syscall.hpp index 07b4cf4..dd20270 100644 --- a/template/sysroot/include/Api/Syscall.hpp +++ b/template/sysroot/include/Api/Syscall.hpp @@ -125,6 +125,8 @@ namespace montauk::abi { // User management static constexpr uint64_t SYS_SETUSER = 92; static constexpr uint64_t SYS_GETUSER = 93; + static constexpr uint64_t SYS_SETSESSION = 174; + static constexpr uint64_t SYS_KILLSESSION = 175; static constexpr uint64_t SYS_FRENAME = 94; static constexpr uint64_t SYS_GETCWD = 95; static constexpr uint64_t SYS_CHDIR = 96; diff --git a/template/sysroot/include/libc/montauk.h b/template/sysroot/include/libc/montauk.h index 7a81b01..2ac8269 100644 --- a/template/sysroot/include/libc/montauk.h +++ b/template/sysroot/include/libc/montauk.h @@ -76,6 +76,8 @@ extern "C" { #define MTK_SYS_GETENVIRON 171 #define MTK_SYS_SETENVIRON 172 #define MTK_SYS_SPAWN_ENV 173 +#define MTK_SYS_SETSESSION 174 +#define MTK_SYS_KILLSESSION 175 #define MTK_SYS_CHILDIO_READ 50 #define MTK_SYS_CHILDIO_WRITE 51 #define MTK_SYS_CHILDIO_WRITEKEY 52 @@ -423,6 +425,14 @@ static inline int mtk_kill(int pid) { return (int)_mtk_syscall1(MTK_SYS_KILL, (long)pid); } +static inline int mtk_setsession(void) { + return (int)_mtk_syscall0(MTK_SYS_SETSESSION); +} + +static inline int mtk_killsession(int session_id) { + return (int)_mtk_syscall1(MTK_SYS_KILLSESSION, (long)session_id); +} + static inline int mtk_proclist(mtk_procinfo *buf, int max) { return (int)_mtk_syscall2(MTK_SYS_PROCLIST, (long)buf, (long)max); } diff --git a/template/sysroot/include/montauk/syscall.h b/template/sysroot/include/montauk/syscall.h index 93efccc..4472e8c 100644 --- a/template/sysroot/include/montauk/syscall.h +++ b/template/sysroot/include/montauk/syscall.h @@ -456,6 +456,12 @@ namespace montauk { inline int kill(int pid) { return (int)syscall1(montauk::abi::SYS_KILL, (uint64_t)pid); } + inline int setsession() { + return (int)syscall0(montauk::abi::SYS_SETSESSION); + } + inline int killsession(int sessionId) { + return (int)syscall1(montauk::abi::SYS_KILLSESSION, (uint64_t)sessionId); + } inline int devlist(montauk::abi::DevInfo* buf, int max) { return (int)syscall2(montauk::abi::SYS_DEVLIST, (uint64_t)buf, (uint64_t)max); }