diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index b8334e71..23f29d4a 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -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) diff --git a/apps/launch.cpp b/apps/launch.cpp new file mode 100644 index 00000000..d6b171a0 --- /dev/null +++ b/apps/launch.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 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 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 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> clients; + Vector 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 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(); + } + } + } +} diff --git a/apps/startui.cpp b/apps/startui.cpp index 13f60fb4..13b4011e 100644 --- a/apps/startui.cpp +++ b/apps/startui.cpp @@ -119,6 +119,9 @@ Result 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(launch_command, 1), pw->pw_uid, pw->pw_gid, groups.slice())); + StringView taskbar_command[] = { "/usr/bin/taskbar" }; TRY(spawn_process_as_user(Slice(taskbar_command, 1), pw->pw_uid, pw->pw_gid, system_groups.slice())); diff --git a/apps/taskbar.cpp b/apps/taskbar.cpp index e99f4dea..ee22b85d 100644 --- a/apps/taskbar.cpp +++ b/apps/taskbar.cpp @@ -1,5 +1,8 @@ #include +#include +#include #include +#include #include #include #include @@ -10,12 +13,14 @@ static constexpr ui::Color TASKBAR_COLOR = ui::Color::from_rgb(83, 83, 83); +static OwnedPtr launcher_client; + void sigchld_handler(int) { wait(nullptr); } -Result create_widget_group_for_app(ui::HorizontalLayout& layout, Slice args, StringView icon) +Result 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 create_widget_group_for_app(ui::HorizontalLayout& layout, Sliceset_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 create_widget_group_for_app(ui::HorizontalLayout& layout, Slice 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 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(); } diff --git a/libos/include/os/ipc/Launcher.h b/libos/include/os/ipc/Launcher.h new file mode 100644 index 00000000..9c21a56e --- /dev/null +++ b/libos/include/os/ipc/Launcher.h @@ -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 + +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); + }; + } +}