apps: Add startui
This service starts a complete UI session (wind, components, and init --user), all with their respective privileges. This lets us move that responsibility away from wind and let it be only a window manager.
This commit is contained in:
parent
ea14dab7d7
commit
443d8957f3
@ -49,3 +49,4 @@ luna_app(2048.cpp 2048)
|
|||||||
target_link_libraries(2048 PUBLIC ui)
|
target_link_libraries(2048 PUBLIC ui)
|
||||||
luna_app(clock.cpp clock)
|
luna_app(clock.cpp clock)
|
||||||
target_link_libraries(clock PUBLIC ui)
|
target_link_libraries(clock PUBLIC ui)
|
||||||
|
luna_app(startui.cpp startui)
|
||||||
|
132
apps/startui.cpp
Normal file
132
apps/startui.cpp
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/**
|
||||||
|
* @file startui.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Servic to start and manage a UI session.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <os/File.h>
|
||||||
|
#include <os/Main.h>
|
||||||
|
#include <os/Process.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// Timeout should be provided in milliseconds.
|
||||||
|
Result<void> wait_until_file_created(StringView path, int timeout)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
while (stat(path.chars(), &st) < 0)
|
||||||
|
{
|
||||||
|
if (timeout <= 0) return err(ETIMEDOUT);
|
||||||
|
usleep(100000); // FIXME: Implement something like inotify or use signals to avoid polling.
|
||||||
|
timeout -= 100;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Vector<gid_t>> read_supplementary_groups(const char* name)
|
||||||
|
{
|
||||||
|
Vector<gid_t> extra_groups;
|
||||||
|
|
||||||
|
setgrent();
|
||||||
|
group* grp;
|
||||||
|
while ((grp = getgrent()))
|
||||||
|
{
|
||||||
|
for (char** user = grp->gr_mem; *user; user++)
|
||||||
|
{
|
||||||
|
if (!strcmp(*user, name))
|
||||||
|
{
|
||||||
|
TRY(extra_groups.try_append(grp->gr_gid));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endgrent();
|
||||||
|
|
||||||
|
return extra_groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> spawn_process_as_user(Slice<StringView> arguments, uid_t user, gid_t group,
|
||||||
|
Slice<gid_t> supplementary_groups)
|
||||||
|
{
|
||||||
|
pid_t child = TRY(os::Process::fork());
|
||||||
|
if (child == 0)
|
||||||
|
{
|
||||||
|
if (setgroups(static_cast<int>(supplementary_groups.size()), supplementary_groups.data()) < 0)
|
||||||
|
return err(errno);
|
||||||
|
setgid(group);
|
||||||
|
setuid(user);
|
||||||
|
TRY(os::Process::exec(arguments[0], arguments, false));
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<int> luna_main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (geteuid() != 0)
|
||||||
|
{
|
||||||
|
os::eprintln("error: %s can only be started as root.", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringView username;
|
||||||
|
|
||||||
|
os::ArgumentParser parser;
|
||||||
|
parser.add_description("Service to start and manage a UI session.");
|
||||||
|
parser.add_system_program_info("startui"_sv);
|
||||||
|
parser.add_value_argument(username, 'u', "user", "the user to start the UI session as");
|
||||||
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
if (username.is_empty())
|
||||||
|
{
|
||||||
|
os::eprintln("error: startui needs a --username parameter to run.");
|
||||||
|
parser.short_usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct passwd* pw = getpwnam(username.chars());
|
||||||
|
if (!pw)
|
||||||
|
{
|
||||||
|
os::eprintln("error: user %s not found.", username.chars());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto groups = TRY(read_supplementary_groups(username.chars()));
|
||||||
|
|
||||||
|
setsid();
|
||||||
|
|
||||||
|
// First of all, start the display server.
|
||||||
|
StringView wind_command[] = { "/usr/bin/wind" };
|
||||||
|
TRY(os::Process::spawn(wind_command[0], Slice<StringView>(wind_command, 1)));
|
||||||
|
|
||||||
|
TRY(wait_until_file_created("/tmp/wind.sock", 10000));
|
||||||
|
|
||||||
|
clearenv();
|
||||||
|
chdir(pw->pw_dir);
|
||||||
|
setenv("PATH", "/usr/bin:/usr/local/bin", 1);
|
||||||
|
setenv("USER", pw->pw_name, 1);
|
||||||
|
setenv("HOME", pw->pw_dir, 1);
|
||||||
|
setenv("SHELL", pw->pw_shell, 1);
|
||||||
|
|
||||||
|
// Next, start the required UI components.
|
||||||
|
StringView taskbar_command[] = { "/usr/bin/taskbar" };
|
||||||
|
TRY(spawn_process_as_user(Slice<StringView>(taskbar_command, 1), pw->pw_uid, pw->pw_gid, groups.slice()));
|
||||||
|
|
||||||
|
// Finally, start init in user mode to manage all configured optional services.
|
||||||
|
StringView init_command[] = { "/usr/bin/init", "--user" };
|
||||||
|
TRY(spawn_process_as_user(Slice<StringView>(init_command, 2), pw->pw_uid, pw->pw_gid, groups.slice()));
|
||||||
|
|
||||||
|
// If any process exits, it's either the display server, one of the required UI components, or init --user. We care
|
||||||
|
// about all of these, so let's just crash the entire UI session if they exit. In the future we might restart some
|
||||||
|
// of them and continue, but for now we'll just exit on all of them.
|
||||||
|
os::Process::wait(os::Process::ANY_CHILD, nullptr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
Name=login
|
Name=login
|
||||||
Description=Start the display server.
|
Description=Start the display server.
|
||||||
Command=/usr/bin/wind --user=selene
|
Command=/usr/bin/startui --user=selene
|
||||||
StandardOutput=/dev/uart0
|
StandardOutput=/dev/uart0
|
||||||
StandardError=/dev/uart0
|
StandardError=/dev/uart0
|
||||||
Restart=true
|
Restart=true
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
Name=taskbar
|
|
||||||
Description=Start the taskbar.
|
|
||||||
Command=/usr/bin/taskbar
|
|
||||||
WorkingDirectory=/home/selene
|
|
||||||
Restart=true
|
|
@ -1,4 +1,3 @@
|
|||||||
Name=terminal
|
Name=terminal
|
||||||
Description=Start the terminal.
|
Description=Start the terminal.
|
||||||
WorkingDirectory=/home/selene
|
|
||||||
Command=/usr/bin/terminal
|
Command=/usr/bin/terminal
|
@ -56,31 +56,6 @@ static void debug(const Vector<OwnedPtr<Client>>& clients)
|
|||||||
os::println("--- wind: END DEBUG OUTPUT ---");
|
os::println("--- wind: END DEBUG OUTPUT ---");
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<void> set_supplementary_groups(const char* name)
|
|
||||||
{
|
|
||||||
Vector<gid_t> extra_groups;
|
|
||||||
|
|
||||||
setgrent();
|
|
||||||
group* grp;
|
|
||||||
while ((grp = getgrent()))
|
|
||||||
{
|
|
||||||
for (char** user = grp->gr_mem; *user; user++)
|
|
||||||
{
|
|
||||||
if (!strcmp(*user, name))
|
|
||||||
{
|
|
||||||
os::println("Adding supplementary group: %d", grp->gr_gid);
|
|
||||||
TRY(extra_groups.try_append(grp->gr_gid));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
endgrent();
|
|
||||||
|
|
||||||
if (setgroups(static_cast<int>(extra_groups.size()), extra_groups.data()) < 0) return err(errno);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
srand((unsigned)time(NULL));
|
srand((unsigned)time(NULL));
|
||||||
@ -88,13 +63,11 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
TRY(os::Security::pledge("stdio rpath wpath cpath unix proc exec tty id", NULL));
|
TRY(os::Security::pledge("stdio rpath wpath cpath unix proc exec tty id", NULL));
|
||||||
|
|
||||||
StringView socket_path = "/tmp/wind.sock";
|
StringView socket_path = "/tmp/wind.sock";
|
||||||
StringView user;
|
|
||||||
|
|
||||||
os::ArgumentParser parser;
|
os::ArgumentParser parser;
|
||||||
parser.add_description("The display server for Luna's graphical user interface."_sv);
|
parser.add_description("The display server for Luna's graphical user interface."_sv);
|
||||||
parser.add_system_program_info("wind"_sv);
|
parser.add_system_program_info("wind"_sv);
|
||||||
parser.add_value_argument(socket_path, 's', "socket"_sv, "the path for the local IPC socket"_sv);
|
parser.add_value_argument(socket_path, 's', "socket"_sv, "the path for the local IPC socket"_sv);
|
||||||
parser.add_value_argument(user, 'u', "user"_sv, "the user to run as"_sv);
|
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
if (geteuid() != 0)
|
if (geteuid() != 0)
|
||||||
@ -117,6 +90,10 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
Mouse mouse_pointer { screen.canvas() };
|
Mouse mouse_pointer { screen.canvas() };
|
||||||
|
|
||||||
|
// Opened all necessary files as root, drop privileges now.
|
||||||
|
setgid(WIND_GROUP_ID);
|
||||||
|
setuid(WIND_USER_ID);
|
||||||
|
|
||||||
int fd = open("/dev/null", O_RDONLY);
|
int fd = open("/dev/null", O_RDONLY);
|
||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
{
|
{
|
||||||
@ -124,45 +101,11 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
setegid(WIND_GROUP_ID);
|
umask(0002);
|
||||||
seteuid(WIND_USER_ID);
|
|
||||||
|
|
||||||
if (setsid() < 0) perror("setsid");
|
|
||||||
|
|
||||||
mode_t mask = umask(0002);
|
|
||||||
|
|
||||||
auto server = TRY(os::LocalServer::create(socket_path, false));
|
auto server = TRY(os::LocalServer::create(socket_path, false));
|
||||||
TRY(server->listen(20));
|
TRY(server->listen(20));
|
||||||
|
|
||||||
umask(mask);
|
|
||||||
|
|
||||||
seteuid(0);
|
|
||||||
|
|
||||||
clearenv();
|
|
||||||
|
|
||||||
pid_t child = TRY(os::Process::fork());
|
|
||||||
if (!child)
|
|
||||||
{
|
|
||||||
if (!user.is_empty())
|
|
||||||
{
|
|
||||||
auto* pwd = getpwnam(user.chars());
|
|
||||||
if (pwd)
|
|
||||||
{
|
|
||||||
TRY(set_supplementary_groups(user.chars()));
|
|
||||||
setgid(pwd->pw_gid);
|
|
||||||
setuid(pwd->pw_uid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StringView args[] = { "/usr/bin/init"_sv, "--user"_sv };
|
|
||||||
TRY(os::Process::exec("/usr/bin/init"_sv, Slice<StringView> { args, 2 }, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
umask(0002);
|
|
||||||
|
|
||||||
setgid(WIND_GROUP_ID);
|
|
||||||
setuid(WIND_USER_ID);
|
|
||||||
|
|
||||||
ui::Color background = ui::Color::from_rgb(0x10, 0x10, 0x10);
|
ui::Color background = ui::Color::from_rgb(0x10, 0x10, 0x10);
|
||||||
|
|
||||||
Vector<OwnedPtr<Client>> clients;
|
Vector<OwnedPtr<Client>> clients;
|
||||||
|
Loading…
Reference in New Issue
Block a user