154 lines
6.0 KiB
C++
154 lines
6.0 KiB
C++
|
/**
|
||
|
* @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();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|