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
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:
parent
9f45026cc2
commit
81e1fdf81e
@ -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: ");
|
||||||
|
23
apps/sh.cpp
23
apps/sh.cpp
@ -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);
|
||||||
|
26
apps/su.cpp
26
apps/su.cpp
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user