apps: Add a background launcher service

This service is used only by taskbar, for now, to launch apps with regular privileges instead of inheriting the special group 'wsys'.
This commit is contained in:
apio 2024-02-01 21:58:44 +01:00
parent a7ff298852
commit 75d0d12b71
Signed by: apio
GPG Key ID: B8A7D06E42258954
5 changed files with 205 additions and 14 deletions

View File

@ -50,3 +50,4 @@ target_link_libraries(2048 PUBLIC ui)
luna_app(clock.cpp clock)
target_link_libraries(clock PUBLIC ui)
luna_app(startui.cpp startui)
luna_app(launch.cpp launch)

153
apps/launch.cpp Normal file
View File

@ -0,0 +1,153 @@
/**
* @file launch.cpp
* @author apio (cloudapio.eu)
* @brief Background process that handles detached launching of apps.
*
* @copyright Copyright (c) 2024, the Luna authors.
*
*/
#include <errno.h>
#include <os/ArgumentParser.h>
#include <os/File.h>
#include <os/LocalServer.h>
#include <os/Process.h>
#include <os/Security.h>
#include <os/ipc/Launcher.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
struct Client
{
os::LocalServer::Client conn;
u8 rpc_id { 0 };
bool rpc_in_progress { false };
Client(os::LocalServer::Client&& client) : conn(move(client)) {};
};
#define READ_MESSAGE(request) \
do { \
auto rc = client.conn.recv_typed(request); \
if (rc.has_error()) \
{ \
if (rc.error() == EAGAIN) \
{ \
client.rpc_in_progress = true; \
client.rpc_id = decltype(request)::ID; \
return {}; \
} \
if (rc.error() == EINTR) \
{ \
client.rpc_in_progress = true; \
client.rpc_id = decltype(request)::ID; \
return {}; \
} \
else \
return rc.release_error(); \
} \
} while (0)
Result<void> handle_launch_detached_message(Client& client)
{
os::Launcher::LaunchDetachedRequest request;
READ_MESSAGE(request);
auto path = COPY_IPC_STRING(request.command);
StringView args[] = { path.view() };
auto result = os::Process::spawn(args[0], { args, 1 }, false);
if (result.has_error()) os::IPC::send_error(client.conn, result.error());
return {};
}
Result<void> handle_ipc_message(Client& client, u8 id)
{
client.rpc_in_progress = false;
switch (id)
{
case os::Launcher::LAUNCH_DETACHED_ID: return handle_launch_detached_message(client);
default: os::eprintln("launch: Invalid IPC message from client!"); return err(EINVAL);
}
}
static Result<void> handle_ipc(Client& client)
{
if (client.rpc_in_progress) return handle_ipc_message(client, client.rpc_id);
u8 id;
auto rc = client.conn.recv_typed(id);
if (rc.has_error())
{
if (rc.error() == EAGAIN) { return {}; }
if (rc.error() == EINTR) { return {}; }
else
return rc.release_error();
}
return handle_ipc_message(client, id);
}
void sigchld_handler(int)
{
os::Process::wait(os::Process::ANY_CHILD, nullptr);
}
Result<int> luna_main(int argc, char** argv)
{
TRY(os::Security::pledge("stdio wpath cpath unix proc exec", NULL));
StringView socket_path = "/tmp/launch.sock";
os::ArgumentParser parser;
parser.add_description("Background process that handles detached launching of apps."_sv);
parser.add_system_program_info("launch"_sv);
parser.parse(argc, argv);
signal(SIGCHLD, sigchld_handler);
auto server = TRY(os::LocalServer::create(socket_path, false));
TRY(server->listen(20));
Vector<OwnedPtr<Client>> clients;
Vector<struct pollfd> fds;
TRY(fds.try_append({ .fd = server->fd(), .events = POLLIN, .revents = 0 }));
TRY(os::Security::pledge("stdio unix proc exec", NULL));
while (1)
{
for (auto& pfd : fds) { pfd.revents = 0; }
int rc = poll(fds.data(), fds.size(), 1000);
if (!rc) continue;
if (rc < 0 && errno != EINTR) { os::println("poll: error: %s", strerror(errno)); }
if (fds[0].revents & POLLIN)
{
auto client = TRY(server->accept());
os::println("launch: New client connected!");
TRY(fds.try_append({ .fd = client.fd(), .events = POLLIN, .revents = 0 }));
OwnedPtr<Client> c = TRY(adopt_owned_if_nonnull(new Client(move(client))));
TRY(clients.try_append(move(c)));
}
for (usize i = 0; i < clients.size(); i++)
{
if (fds[i + 1].revents & POLLIN) handle_ipc(*clients[i]);
if (fds[i + 1].revents & POLLHUP)
{
os::println("launch: Client %d disconnected", i);
fds.remove_at(i + 1);
auto client = clients.remove_at(i);
client->conn.disconnect();
}
}
}
}

View File

@ -119,6 +119,9 @@ Result<int> luna_main(int argc, char** argv)
setenv("SHELL", pw->pw_shell, 1);
// Next, start the required UI components.
StringView launch_command[] = { "/usr/bin/launch" };
TRY(spawn_process_as_user(Slice<StringView>(launch_command, 1), pw->pw_uid, pw->pw_gid, groups.slice()));
StringView taskbar_command[] = { "/usr/bin/taskbar" };
TRY(spawn_process_as_user(Slice<StringView>(taskbar_command, 1), pw->pw_uid, pw->pw_gid, system_groups.slice()));

View File

@ -1,5 +1,8 @@
#include <os/File.h>
#include <os/IPC.h>
#include <os/LocalClient.h>
#include <os/Process.h>
#include <os/ipc/Launcher.h>
#include <signal.h>
#include <sys/wait.h>
#include <ui/App.h>
@ -10,12 +13,14 @@
static constexpr ui::Color TASKBAR_COLOR = ui::Color::from_rgb(83, 83, 83);
static OwnedPtr<os::LocalClient> launcher_client;
void sigchld_handler(int)
{
wait(nullptr);
}
Result<void> create_widget_group_for_app(ui::HorizontalLayout& layout, Slice<StringView> args, StringView icon)
Result<void> create_widget_group_for_app(ui::HorizontalLayout& layout, StringView path, StringView icon)
{
auto* button = new (std::nothrow) ui::Button({ 0, 0, 50, 50 });
if (!button) return err(ENOMEM);
@ -25,7 +30,11 @@ Result<void> create_widget_group_for_app(ui::HorizontalLayout& layout, Slice<Str
ui::Container({ 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center);
if (!container) return err(ENOMEM);
button->set_widget(*container);
button->set_action([=] { os::Process::spawn(args[0], args, false); });
button->set_action([=] {
os::Launcher::LaunchDetachedRequest request;
SET_IPC_STRING(request.command, path.chars());
os::IPC::send_async(*launcher_client, request);
});
auto image = TRY(ui::ImageWidget::load(icon));
container->set_widget(*image);
@ -38,10 +47,12 @@ Result<void> create_widget_group_for_app(ui::HorizontalLayout& layout, Slice<Str
Result<int> luna_main(int, char**)
{
ui::App app;
TRY(app.init());
TRY(app.init("/tmp/wsys.sock"));
TRY(os::EventLoop::the().register_signal_handler(SIGCHLD, sigchld_handler));
launcher_client = TRY(os::LocalClient::connect("/tmp/launch.sock", false));
ui::Rect screen = app.screen_rect();
ui::Rect bar = ui::Rect { ui::Point { 0, screen.height - 50 }, screen.width, 50 };
@ -54,17 +65,10 @@ Result<int> luna_main(int, char**)
ui::HorizontalLayout layout(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No);
window->set_main_widget(layout);
StringView terminal_command[] = { "/usr/bin/terminal" };
TRY(create_widget_group_for_app(layout, { terminal_command, 1 }, "/usr/share/icons/32x32/app-terminal.tga"));
StringView about_command[] = { "/usr/bin/about" };
TRY(create_widget_group_for_app(layout, { about_command, 1 }, "/usr/share/icons/32x32/app-about.tga"));
StringView gol_command[] = { "/usr/bin/gol" };
TRY(create_widget_group_for_app(layout, { gol_command, 1 }, "/usr/share/icons/32x32/app-gol.tga"));
StringView clock_command[] = { "/usr/bin/clock" };
TRY(create_widget_group_for_app(layout, { clock_command, 1 }, "/usr/share/icons/32x32/app-clock.tga"));
TRY(create_widget_group_for_app(layout, "/usr/bin/terminal", "/usr/share/icons/32x32/app-terminal.tga"));
TRY(create_widget_group_for_app(layout, "/usr/bin/about", "/usr/share/icons/32x32/app-about.tga"));
TRY(create_widget_group_for_app(layout, "/usr/bin/gol", "/usr/share/icons/32x32/app-gol.tga"));
TRY(create_widget_group_for_app(layout, "/usr/bin/clock", "/usr/share/icons/32x32/app-clock.tga"));
return app.run();
}

View File

@ -0,0 +1,30 @@
/**
* @file ipc/Launcher.h
* @author apio (cloudapio.eu)
* @brief IPC message definitions for UI messages sent to the launch server.
*
* @copyright Copyright (c) 2024, the Luna authors.
*
*/
#pragma once
#include <os/IPC.h>
namespace os
{
namespace Launcher
{
enum ServerMessages : u8
{
IPC_ENUM_SERVER(launch),
LAUNCH_DETACHED_ID,
};
struct LaunchDetachedRequest
{
static constexpr u8 ID = LAUNCH_DETACHED_ID;
IPC_STRING(command);
};
}
}