kernel+libc+login+sh+su: Implement foreground and background process groups in the default console
All checks were successful
continuous-integration/drone/push Build is passing

Also, the console sends SIGINT to the foreground process group when ^C is pressed!
This commit is contained in:
apio 2023-07-12 13:49:37 +02:00
parent 9f45026cc2
commit 81e1fdf81e
Signed by: apio
GPG Key ID: B8A7D06E42258954
5 changed files with 134 additions and 5 deletions

View File

@ -1,7 +1,10 @@
#include <bits/termios.h>
#include <luna/String.h> #include <luna/String.h>
#include <os/ArgumentParser.h> #include <os/ArgumentParser.h>
#include <os/File.h> #include <os/File.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
Result<int> luna_main(int argc, char** argv) Result<int> luna_main(int argc, char** argv)
@ -26,6 +29,14 @@ Result<int> luna_main(int argc, char** argv)
if (username.is_empty()) 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(); auto input = os::File::standard_input();
os::print("Username: "); os::print("Username: ");

View File

@ -1,3 +1,4 @@
#include <bits/termios.h>
#include <errno.h> #include <errno.h>
#include <luna/String.h> #include <luna/String.h>
#include <luna/Vector.h> #include <luna/Vector.h>
@ -11,6 +12,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/ioctl.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <unistd.h> #include <unistd.h>
@ -80,7 +82,11 @@ Result<int> luna_main(int argc, char** argv)
else { username = getenv("USER"); } else { username = getenv("USER"); }
endpwent(); endpwent();
signal(SIGTTOU, SIG_IGN);
signal(SIGINT, sigint_handler); signal(SIGINT, sigint_handler);
pid_t pgid = getpgid(0);
ioctl(STDIN_FILENO, TIOCSPGRP, &pgid);
} }
while (1) while (1)
@ -127,11 +133,26 @@ Result<int> luna_main(int argc, char** argv)
pid_t child = TRY(os::Process::fork()); 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; int status;
TRY(os::Process::wait(child, &status)); TRY(os::Process::wait(child, &status));
if (interactive)
{
pid_t pgid = getpgid(0);
ioctl(STDIN_FILENO, TIOCSPGRP, &pgid);
}
if (WIFSIGNALED(status)) if (WIFSIGNALED(status))
{ {
int sig = WTERMSIG(status); int sig = WTERMSIG(status);

View File

@ -1,6 +1,7 @@
#include <bits/termios.h> #include <bits/termios.h>
#include <os/ArgumentParser.h> #include <os/ArgumentParser.h>
#include <pwd.h> #include <pwd.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
@ -13,8 +14,24 @@ void restore_terminal()
ioctl(fileno(stdin), TCSETS, &orig); ioctl(fileno(stdin), TCSETS, &orig);
} }
void sigint_handler(int)
{
restore_terminal();
raise(SIGINT);
}
char* getpass() 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); fputs("Password: ", stdout);
if (ioctl(fileno(stdin), TCGETS, &orig) < 0) if (ioctl(fileno(stdin), TCGETS, &orig) < 0)
@ -23,6 +40,12 @@ char* getpass()
return nullptr; 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); atexit(restore_terminal);
struct termios tc = orig; struct termios tc = orig;
@ -82,6 +105,8 @@ Result<int> luna_main(int argc, char** argv)
if ((prompt_password || getuid() != geteuid()) && *entry->pw_passwd) if ((prompt_password || getuid() != geteuid()) && *entry->pw_passwd)
{ {
signal(SIGTTOU, SIG_IGN);
char* pass = getpass(); char* pass = getpass();
if (!pass) return 1; if (!pass) return 1;
@ -102,6 +127,7 @@ Result<int> luna_main(int argc, char** argv)
chdir(entry->pw_dir); chdir(entry->pw_dir);
clearenv(); clearenv();
setenv("PATH", "/bin:/sbin", 1); setenv("PATH", "/bin:/sbin", 1);
setpgid(0, 0);
} }
if (login || entry->pw_uid != 0) setenv("USER", entry->pw_name, 1); if (login || entry->pw_uid != 0) setenv("USER", entry->pw_name, 1);

View File

@ -15,15 +15,42 @@
static Buffer g_console_input; static Buffer g_console_input;
static bool g_eof { false }; static bool g_eof { false };
static bool g_echo { true }; static bool g_echo { true };
static bool g_stop_background_output { false };
static Option<pid_t> g_foreground_process_group;
Result<void> ConsoleDevice::create() Result<void> ConsoleDevice::create()
{ {
auto device = (SharedPtr<Device>)TRY(make_shared<ConsoleDevice>()); auto device = (SharedPtr<Device>)TRY(make_shared<ConsoleDevice>());
g_foreground_process_group = {};
return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device); return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device);
} }
static Result<void> 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<usize> ConsoleDevice::read(u8* buf, usize, usize length) const Result<usize> 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(); if (length > g_console_input.size()) length = g_console_input.size();
memcpy(buf, g_console_input.data(), length); memcpy(buf, g_console_input.data(), length);
@ -39,6 +66,8 @@ Result<usize> ConsoleDevice::read(u8* buf, usize, usize length) const
Result<usize> ConsoleDevice::write(const u8* buf, usize, usize length) Result<usize> 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); TextConsole::write((const char*)buf, length);
return length; return length;
} }
@ -63,19 +92,36 @@ void ConsoleDevice::did_press_key(char key)
} }
// Ctrl+D // 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; if (g_temp_input.size() == 0) g_eof = true;
return; 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(); Scheduler::dump_state();
return; 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("Total memory: %s", to_dynamic_unit(MemoryManager::total()).release_value().chars());
kinfoln("Free memory: %s", to_dynamic_unit(MemoryManager::free()).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; return;
} }
if (key == 'h' && (Keyboard::modifiers() & (Keyboard::LeftAlt | Keyboard::LeftControl))) if (key == 'h' && (Keyboard::modifiers() == (Keyboard::LeftAlt | Keyboard::LeftControl)))
{ {
dump_heap_usage(); dump_heap_usage();
return; return;
@ -113,15 +159,38 @@ Result<u64> ConsoleDevice::ioctl(int request, void* arg)
}; };
if (g_echo) tc.c_lflag |= ECHO; 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); return MemoryManager::copy_to_user_typed((struct termios*)arg, &tc) ? 0 : err(EFAULT);
} }
case TCSETS: { case TCSETS: {
TRY(handle_background_process_group(true, SIGTTOU));
struct termios tc; struct termios tc;
if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &tc)) return err(EFAULT); if (!MemoryManager::copy_from_user_typed((const struct termios*)arg, &tc)) return err(EFAULT);
if (tc.c_lflag & ECHO) g_echo = true; if (tc.c_lflag & ECHO) g_echo = true;
else else
g_echo = false; 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; return 0;
} }
default: return err(EINVAL); default: return err(EINVAL);

View File

@ -13,9 +13,11 @@ struct termios
// Values for c_lflag. // Values for c_lflag.
#define ECHO 1 #define ECHO 1
#define TOSTOP 2
// termios ioctl() requests. // termios ioctl() requests.
#define TCGETS 0 #define TCGETS 0
#define TCSETS 1 #define TCSETS 1
#define TIOCSPGRP 2
#endif #endif