From 81e1fdf81e6e2ca3a7045d2dab1884a69715dda9 Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 12 Jul 2023 13:49:37 +0200 Subject: [PATCH] kernel+libc+login+sh+su: Implement foreground and background process groups in the default console Also, the console sends SIGINT to the foreground process group when ^C is pressed! --- apps/login.cpp | 11 ++++ apps/sh.cpp | 23 +++++++- apps/su.cpp | 26 +++++++++ kernel/src/fs/devices/ConsoleDevice.cpp | 77 +++++++++++++++++++++++-- libc/include/bits/termios.h | 2 + 5 files changed, 134 insertions(+), 5 deletions(-) diff --git a/apps/login.cpp b/apps/login.cpp index 055e6532..e44535a2 100644 --- a/apps/login.cpp +++ b/apps/login.cpp @@ -1,7 +1,10 @@ +#include #include #include #include +#include #include +#include #include Result luna_main(int argc, char** argv) @@ -26,6 +29,14 @@ Result luna_main(int argc, char** argv) if (username.is_empty()) { + signal(SIGTTOU, SIG_IGN); + + if (isatty(STDIN_FILENO)) + { + pid_t pgid = getpgid(0); + ioctl(STDIN_FILENO, TIOCSPGRP, &pgid); + } + auto input = os::File::standard_input(); os::print("Username: "); diff --git a/apps/sh.cpp b/apps/sh.cpp index 9d5bb3a2..4e388980 100644 --- a/apps/sh.cpp +++ b/apps/sh.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -11,6 +12,7 @@ #include #include #include +#include #include #include @@ -80,7 +82,11 @@ Result luna_main(int argc, char** argv) else { username = getenv("USER"); } endpwent(); + signal(SIGTTOU, SIG_IGN); signal(SIGINT, sigint_handler); + + pid_t pgid = getpgid(0); + ioctl(STDIN_FILENO, TIOCSPGRP, &pgid); } while (1) @@ -127,11 +133,26 @@ Result luna_main(int argc, char** argv) pid_t child = TRY(os::Process::fork()); - if (child == 0) { TRY(execute_command(cmd.view())); } + if (child == 0) + { + if (interactive) + { + setpgid(0, 0); + pid_t pgid = getpgid(0); + ioctl(STDIN_FILENO, TIOCSPGRP, &pgid); + } + TRY(execute_command(cmd.view())); + } int status; TRY(os::Process::wait(child, &status)); + if (interactive) + { + pid_t pgid = getpgid(0); + ioctl(STDIN_FILENO, TIOCSPGRP, &pgid); + } + if (WIFSIGNALED(status)) { int sig = WTERMSIG(status); diff --git a/apps/su.cpp b/apps/su.cpp index ef7d3514..a0c95f6c 100644 --- a/apps/su.cpp +++ b/apps/su.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -13,8 +14,24 @@ void restore_terminal() ioctl(fileno(stdin), TCSETS, &orig); } +void sigint_handler(int) +{ + restore_terminal(); + raise(SIGINT); +} + char* getpass() { + if (!isatty(STDIN_FILENO)) + { + // FIXME: Just read from /dev/tty (the controlling terminal). Problem: that doesn't exist yet. + fprintf(stderr, "error: password must be read from a terminal!"); + return nullptr; + } + + pid_t pgid = getpgid(0); + ioctl(STDIN_FILENO, TIOCSPGRP, &pgid); + fputs("Password: ", stdout); if (ioctl(fileno(stdin), TCGETS, &orig) < 0) @@ -23,6 +40,12 @@ char* getpass() return nullptr; } + struct sigaction sa; + sa.sa_handler = sigint_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESETHAND; + sigaction(SIGINT, &sa, NULL); + atexit(restore_terminal); struct termios tc = orig; @@ -82,6 +105,8 @@ Result luna_main(int argc, char** argv) if ((prompt_password || getuid() != geteuid()) && *entry->pw_passwd) { + signal(SIGTTOU, SIG_IGN); + char* pass = getpass(); if (!pass) return 1; @@ -102,6 +127,7 @@ Result luna_main(int argc, char** argv) chdir(entry->pw_dir); clearenv(); setenv("PATH", "/bin:/sbin", 1); + setpgid(0, 0); } if (login || entry->pw_uid != 0) setenv("USER", entry->pw_name, 1); diff --git a/kernel/src/fs/devices/ConsoleDevice.cpp b/kernel/src/fs/devices/ConsoleDevice.cpp index 2038ec50..be2b0f0f 100644 --- a/kernel/src/fs/devices/ConsoleDevice.cpp +++ b/kernel/src/fs/devices/ConsoleDevice.cpp @@ -15,15 +15,42 @@ static Buffer g_console_input; static bool g_eof { false }; static bool g_echo { true }; +static bool g_stop_background_output { false }; + +static Option g_foreground_process_group; Result ConsoleDevice::create() { auto device = (SharedPtr)TRY(make_shared()); + g_foreground_process_group = {}; return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device); } +static Result handle_background_process_group(bool can_succeed, int signo) +{ + if (!g_foreground_process_group.has_value()) return {}; + + auto foreground_pgrp = g_foreground_process_group.value(); + + auto* current = Scheduler::current(); + if ((pid_t)current->pgid == foreground_pgrp) return {}; + + if ((current->signal_mask & (1 << (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); +} + Result ConsoleDevice::read(u8* buf, usize, usize length) const { + TRY(handle_background_process_group(false, SIGTTIN)); + if (length > g_console_input.size()) length = g_console_input.size(); memcpy(buf, g_console_input.data(), length); @@ -39,6 +66,8 @@ Result ConsoleDevice::read(u8* buf, usize, usize length) const Result ConsoleDevice::write(const u8* buf, usize, usize length) { + if (g_stop_background_output) TRY(handle_background_process_group(true, SIGTTOU)); + TextConsole::write((const char*)buf, length); return length; } @@ -63,19 +92,36 @@ void ConsoleDevice::did_press_key(char key) } // Ctrl+D - if (key == 'd' && (Keyboard::modifiers() & Keyboard::LeftControl)) + if (key == 'd' && (Keyboard::modifiers() == Keyboard::LeftControl)) { if (g_temp_input.size() == 0) g_eof = true; return; } - if (key == 'e' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl))) + if (key == 'c' && (Keyboard::modifiers() == Keyboard::LeftControl)) + { + g_temp_input.clear(); + + if (g_echo) TextConsole::wprintln(L"^C"); + + if (g_foreground_process_group.has_value()) + { + Scheduler::for_each_in_process_group(g_foreground_process_group.value(), [](Thread* thread) { + thread->send_signal(SIGINT); + return true; + }); + } + + return; + } + + if (key == 'e' && (Keyboard::modifiers() == (Keyboard::LeftAlt | Keyboard::LeftControl))) { Scheduler::dump_state(); return; } - if (key == 'm' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl))) + if (key == 'm' && (Keyboard::modifiers() == (Keyboard::LeftAlt | Keyboard::LeftControl))) { kinfoln("Total memory: %s", to_dynamic_unit(MemoryManager::total()).release_value().chars()); kinfoln("Free memory: %s", to_dynamic_unit(MemoryManager::free()).release_value().chars()); @@ -84,7 +130,7 @@ void ConsoleDevice::did_press_key(char key) return; } - if (key == 'h' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl))) + if (key == 'h' && (Keyboard::modifiers() == (Keyboard::LeftAlt | Keyboard::LeftControl))) { dump_heap_usage(); return; @@ -113,15 +159,38 @@ Result ConsoleDevice::ioctl(int request, void* arg) }; if (g_echo) tc.c_lflag |= ECHO; + if (g_stop_background_output) tc.c_lflag |= TOSTOP; return MemoryManager::copy_to_user_typed((struct termios*)arg, &tc) ? 0 : err(EFAULT); } case TCSETS: { + TRY(handle_background_process_group(true, SIGTTOU)); + struct termios tc; if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &tc)) return err(EFAULT); if (tc.c_lflag & ECHO) g_echo = true; else g_echo = false; + if (tc.c_lflag & TOSTOP) g_stop_background_output = true; + else + g_stop_background_output = false; + + return 0; + } + case TIOCSPGRP: { + TRY(handle_background_process_group(true, SIGTTOU)); + + 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); + + g_foreground_process_group = pgid; return 0; } default: return err(EINVAL); diff --git a/libc/include/bits/termios.h b/libc/include/bits/termios.h index e4049293..3c0572c5 100644 --- a/libc/include/bits/termios.h +++ b/libc/include/bits/termios.h @@ -13,9 +13,11 @@ struct termios // Values for c_lflag. #define ECHO 1 +#define TOSTOP 2 // termios ioctl() requests. #define TCGETS 0 #define TCSETS 1 +#define TIOCSPGRP 2 #endif