/** * @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(); } } } }