ealanos: Rewrote Kuori remote shell component to use Hoitaja's new Shell service. This removes the limitations of the Launcher session that only allowed start nodes to be 640 characters long.

This commit is contained in:
Michael Mueller
2025-06-20 17:34:10 +02:00
parent b5657ab546
commit 3fd592a0b3
3 changed files with 202 additions and 316 deletions

View File

@@ -1,352 +1,216 @@
#include <base/heap.h>
#include <base/attached_rom_dataspace.h>
#include <libc/component.h>
#include "base/attached_ram_dataspace.h"
#include "base/ram_allocator.h"
#include "region_map/region_map.h"
#include "sys/_pthreadtypes.h"
#include "util/xml_node.h"
#include <base/log.h>
#include <lwip/lwip_genode_init.h>
#include <lwip/nic_netif.h>
#include <timer_session/connection.h>
#include <ealanos/laucher/connection.h>
#include <base/attached_rom_dataspace.h>
#include <cstring>
#include <libc/component.h>
#include <internal/thread_create.h>
#include <cstdio>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
namespace Lwip {
extern "C" {
#include <lwip/arch.h>
#include <lwip/opt.h>
#include <lwip/tcp.h>
#include <lwip/dns.h>
#include <lwip/ip_addr.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <ealanos/shell/connection.h>
#include "protocol.h"
namespace Ealan {
namespace Kuori {
class Server;
}
}
namespace Ealan::Kuori {
using namespace Genode;
class Main;
}
class Ealan::Kuori::Main
static void print(Genode::Output &output, sockaddr_in const &addr)
{
private:
Heap &_heap;
Genode::print(output, (ntohl(addr.sin_addr.s_addr) >> 24) & 0xff);
output.out_string(".");
Genode::print(output, (ntohl(addr.sin_addr.s_addr) >> 16) & 0xff);
output.out_string(".");
Genode::print(output, (ntohl(addr.sin_addr.s_addr) >> 8) & 0xff);
output.out_string(".");
Genode::print(output, (ntohl(addr.sin_addr.s_addr) >> 0) & 0xff);
output.out_string(":");
Genode::print(output, ntohs(addr.sin_port));
}
class Ealan::Kuori::Server
{
private:
Ealan::Launcher_connection &_hoitaja;
Libc::Env &_env;
Genode::Attached_rom_dataspace _config_rom {_env, "config"};
Genode::Xml_node _config{_config_rom.xml()};
Ealan::Shell::Connection _shell{_env};
struct Wakeup_scheduler : Lwip::Nic_netif::Wakeup_scheduler
{
Lwip::Nic_netif *nic{nullptr};
void _die(const char *reason){ perror(reason); _env.parent().exit(1); }
void schedule_nic_server_wakeup() override
{
nic->wakeup_nic_server();
}
struct handler {
pthread_t *thread;
Server *server;
int cd;
};
void set_nic(Lwip::Nic_netif *nic)
{
this->nic = nic;
}
char *_config_ptr;
Wakeup_scheduler() = default;
} _wakeup_scheduler;
public:
enum states
{
NONE = 0,
ACCEPTED,
RECEIVED,
CLOSING
};
using Command = Ealan::Kuori::Package_header::Command;
using Header = Ealan::Kuori::Package_header;
using State = Ealan::Kuori::Package_header::State;
struct state
{
uint8_t state;
uint8_t retries;
struct Lwip::tcp_pcb *pcb;
struct Lwip::pbuf *p;
};
Lwip::tcp_pcb *listen_socket;
uint16_t _port{8080};
Server(Libc::Env &env) : _env(env) {}
struct Tcp_connection
{
static Main *kuori;
/**
* @brief Free state and lwip objects
*
* @param s
*/
static void free(struct state *s)
{
if (s != nullptr) {
if (s->p) {
Lwip::pbuf_free(s->p);
}
Lwip::mem_free(s);
}
}
static void *handle(void *args)
{
struct handler *h = reinterpret_cast<struct handler *>(args);
h->server->handle_connection(h->cd);
delete h->thread;
delete h;
static void close(struct Lwip::tcp_pcb *tpcb, struct state *s)
{
Lwip::tcp_arg(tpcb, nullptr);
Lwip::tcp_sent(tpcb, nullptr);
Lwip::tcp_recv(tpcb, nullptr);
Lwip::tcp_err(tpcb, nullptr);
Lwip::tcp_poll(tpcb, nullptr, 0);
return nullptr;
}
free(s);
Lwip::tcp_close(tpcb);
}
void handle_connection(int cd)
{
Header header;
static void send(struct Lwip::tcp_pcb *tpcb, struct state *s)
{
struct Lwip::pbuf *ptr;
Lwip::err_t wr_err = Lwip::ERR_OK;
while (true) {
int ret = read(cd, &header, sizeof(header));
while ((wr_err == Lwip::ERR_OK) && (s->p != nullptr))
{
ptr = s->p;
if (!ret) break;
wr_err = Lwip::tcp_write(tpcb, ptr->payload, ptr->len, 3);
if (wr_err == Lwip::ERR_OK) {
uint16_t plen;
if (ret > 0) {
Request request{header.command, header.length, cd};
Genode::log("size of command: ", sizeof(Command));
Genode::log("CMD=", static_cast<unsigned int>(request.command), " CD=", cd, " LEN=", header.length);
process_request(request);
}
}
plen = ptr->len;
s->p = ptr->next;
}
if (s->p) {
Lwip::pbuf_ref(s->p);
}
Lwip::pbuf_free(ptr);
Lwip::tcp_recved(tpcb, plen);
} else if (wr_err == Lwip::ERR_MEM) {
s->p = ptr;
} else {
Genode::error("Encountered LwIP error code ", static_cast<signed int>(wr_err));
}
}
}
void process_request(Request &request)
{
switch (request.command) {
case Command::BEGIN:
{
static void error(void *arg, Lwip::err_t error)
{
struct state *s;
/* Acknowledge the new transaction */
LWIP_UNUSED_ARG(error);
Header *ack = new Header {Command::ACK, State::ACCEPT, static_cast<std::uint32_t>(::strlen(_config_ptr)) };
write(request.cd, ack, sizeof(Header));
s = static_cast<struct state *>(arg);
/* Send habitat configuration to client */
write(request.cd, _config_ptr, ::strlen(_config_ptr));
break;
}
free(s);
}
case Command::COMMIT:
{
char *buf = new char[request.length];
Genode::log("Commit: Awaiting ", request.length, " bytes");
std::uint32_t bytes = 0;
int rc = 0;
static Lwip::err_t poll(void *arg, struct Lwip::tcp_pcb *tpcb)
{
Lwip::err_t rc;
struct state *s;
while (bytes < request.length) {
rc = read(request.cd, &buf[bytes], request.length);
if (rc == 0)
break;
Genode::log("Read ", rc, " bytes.");
bytes += rc;
}
s = static_cast<struct state *>(arg);
if (s) {
if (s->p) {
send(tpcb, s);
} else {
if (s->state == CLOSING) {
close(tpcb, s);
}
}
rc = Lwip::ERR_OK;
} else {
Lwip::tcp_abort(tpcb);
rc = Lwip::ERR_ABRT;
}
return rc;
}
static Lwip::err_t sent(void *arg, struct Lwip::tcp_pcb *tpcb, [[maybe_unused]] Lwip::u16_t len)
{
struct state *s;
memcpy(_config_ptr, buf, request.length);
_config_ptr[bytes] = '\0';
s = static_cast<struct state *>(arg);
s->retries = 0;
printf("Read new configuration:\n %s\n", _config_ptr);
if (s->p) {
Lwip::tcp_sent(tpcb, sent);
send(tpcb, s);
} else {
if (s->state == CLOSING) {
close(tpcb, s);
}
}
return Lwip::ERR_OK;
}
_shell.commit();
break;
}
default:
{
return;
}
}
}
static Lwip::err_t receive(void *arg, struct Lwip::tcp_pcb *tpcb, struct Lwip::pbuf *p, Lwip::err_t err)
{
struct state *s;
Lwip::err_t rc = Lwip::ERR_OK;
void run()
{
int rc = 0;
s = static_cast<struct state *>(arg);
Genode::log("Kuori v1.0 - Remote Shell for EalánOS");
if (!p) {
s->state = CLOSING;
if (!s->p) {
close(tpcb, s);
} else {
send(tpcb, s);
}
rc = Lwip::ERR_OK;
} else if (err != Lwip::ERR_OK) {
rc = err;
} else if (s->state == ACCEPTED) {
s->state = RECEIVED;
kuori->parse_and_launch(tpcb, p, s);
}
else if (s->state == RECEIVED)
{
if (!s->p) {
s->p = p;
kuori->parse_and_launch(tpcb, p, s);
}
else
{
struct Lwip::pbuf *ptr;
ptr = s->p;
Lwip::pbuf_cat(ptr, p);
}
rc = Lwip::ERR_OK;
}
else
{
Lwip::tcp_recved(tpcb, p->tot_len);
Lwip::pbuf_free(p);
rc = Lwip::ERR_OK;
}
Genode::Ram_dataspace_capability cap = _shell.connect();
Genode::Region_map::Attr attr{};
attr.writeable = true;
return rc;
}
_config_ptr = _env.rm().attach(cap, attr).convert<char *>(
[&](Genode::Region_map::Range r) { return reinterpret_cast<char*>(r.start); }, [&] (Genode::Region_map::Attach_error) { return nullptr; });
static Lwip::err_t accept([[maybe_unused]] void *arg, struct Lwip::tcp_pcb *newpcb, Lwip::err_t err)
{
Lwip::err_t rc;
struct state *s;
if (!_config_ptr) _die("Failed mapping habitat config");
LWIP_UNUSED_ARG(arg);
Genode::log("Mapped habitat configuration");
if ((err != Lwip::ERR_OK) || (newpcb == nullptr)) {
return Lwip::ERR_VAL;
}
printf("%s\n", _config_ptr);
s = static_cast<struct state *>(Lwip::mem_malloc(sizeof(struct state)));
if (s) {
s->state = ACCEPTED;
s->pcb = newpcb;
s->retries = 0;
s->p = nullptr;
int const sd = socket(AF_INET, SOCK_STREAM, 0);
if (sd == -1) { _die("socket"); }
Lwip::tcp_arg(newpcb, s);
Lwip::tcp_recv(newpcb, receive);
Lwip::tcp_err(newpcb, error);
Lwip::tcp_poll(newpcb, poll, 0);
Lwip::tcp_sent(newpcb, sent);
unsigned const port = _config.attribute_value("port", 8080U);
rc = Lwip::ERR_OK;
sockaddr_in const addr{0, AF_INET, htons(port), {INADDR_ANY}, {INADDR_ANY}};
sockaddr const *paddr = reinterpret_cast<sockaddr const *>(&addr);
Genode::log("Accepted new connection from ", const_cast<const char*>(Lwip::ipaddr_ntoa(&newpcb->remote_ip)));
}
else
{
rc = Lwip::ERR_MEM;
}
return rc;
}
int const on = 1;
rc = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
};
if (rc) _die("setsockopt");
void init()
{
listen_socket = Lwip::tcp_new_ip_type(Lwip::IPADDR_TYPE_ANY);
rc = bind(sd, paddr, sizeof(addr));
if (rc == -1) _die("bind");
if (!listen_socket) {
Genode::error("Failed to create server socket.");
}
rc = listen(sd, SOMAXCONN);
if (rc == -1) _die("listen");
Lwip::err_t err;
err = Lwip::tcp_bind(listen_socket, &Lwip::ip_addr_any, _port);
Genode::log("Kuori service up and running.");
if (err == Lwip::ERR_OK) {
listen_socket = Lwip::tcp_listen_with_backlog(listen_socket, 255);
Lwip::tcp_accept(listen_socket, Tcp_connection::accept);
} else {
Genode::error("Failed to bind socket.");
}
while (true) {
sockaddr_in caddr;
socklen_t scaddr = sizeof(caddr);
sockaddr *pcaddr = reinterpret_cast<sockaddr *>(&caddr);
Tcp_connection::kuori = this;
int const cd = accept(sd, pcaddr, &scaddr);
if (cd == -1) _die("accept");
Genode::log("Kuori v0.1 - Remote shell for EalánOS");
}
Genode::log("New connection from ", caddr);
struct handler *handler = new struct handler();
handler->cd = cd;
handler->server = this;
handler->thread = new pthread_t();
Lwip::Nic_netif _netif;
Genode::String<32> const name {"kuori#", caddr};
public:
Main(Env &env, Genode::Heap &heap, Xml_node config, Ealan::Launcher_connection &hoitaja) : _heap(heap), _hoitaja(hoitaja), _wakeup_scheduler(), _netif(env, _heap, config, _wakeup_scheduler) {
_wakeup_scheduler.set_nic(&_netif);
init();
}
void parse_and_launch(Lwip::tcp_pcb *tpcb, Lwip::pbuf *p, state *state)
{
try {
Genode::log("Parsing input of length ", p->len);
Genode::log(static_cast<const char *>(p->payload));
Xml_node start_node(static_cast<char *>(p->payload));
//Genode::log(start_node);
Genode::String<90> name = start_node.attribute_value("name", Genode::String<90>());
char msg[120];
std::sprintf(msg, "Lauching %s\n", name.string());
Genode::String<640> cell_cfg(static_cast<const char*>(p->payload));
Genode::log(cell_cfg);
_hoitaja.launch(cell_cfg);
Lwip::pbuf *response_buffer = Lwip::pbuf_alloc(Lwip::PBUF_TRANSPORT, static_cast<Lwip::u16_t>(Genode::strlen(msg)), Lwip::PBUF_RAM);
Lwip::pbuf_take(response_buffer, msg, static_cast<Lwip::u16_t>(Genode::strlen(msg)));
state->p = response_buffer;
Tcp_connection::send(tpcb, state);
}
catch (Genode::Xml_attribute::Invalid_syntax)
{
Genode::error("Invalid config supplied.");
Genode::String<100> response("Invalid cell configuration.\n");
Lwip::pbuf *response_buffer = Lwip::pbuf_alloc(Lwip::PBUF_TRANSPORT, static_cast<Lwip::u16_t>(response.length()), Lwip::PBUF_RAM);
Lwip::pbuf_take(response_buffer, response.string(), static_cast<Lwip::u16_t>(response.length()));
state->p = response_buffer;
Tcp_connection::send(tpcb, state);
}
}
Libc::pthread_create_from_session(handler->thread, Server::handle, handler, 8*4096, name.string(), &_env.cpu(), _env.cpu().affinity_space().location_of_index(0));
}
}
};
Ealan::Kuori::Main *Ealan::Kuori::Main::Tcp_connection::kuori;
void Libc::Component::construct(Libc::Env &env)
{
Genode::Attached_rom_dataspace _config{env, "config"};
Genode::Xml_node config = _config.xml();
static Ealan::Kuori::Server kuori(env);
static Genode::Heap heap{env.ram(), env.rm()};
static Timer::Connection timer{env};
Lwip::genode_init(heap, timer);
Genode::Thread *myself = Genode::Thread::myself();
Genode::Affinity::Location loc = myself->affinity();
Genode::Affinity affinity(env.cpu().affinity_space(), loc);
static Ealan::Launcher_connection hoitaja{env, affinity};
static Ealan::Kuori::Main kuori(env, heap, config, hoitaja);
Libc::with_libc([&]() {
kuori.run();
});
}

View File

@@ -0,0 +1,36 @@
#ifndef __EALANOS__SRC__APP__KUORI__PROTOCOL_H_
#define __EALANOS__SRC__APP__KUORI__PROTOCOL_H_
#include <cstdint>
namespace Ealan
{
namespace Kuori
{
using Handle = std::uint16_t;
struct Package_header {
enum Command { BEGIN, COMMIT, ACK };
enum State {
ACCEPT,
FAILED,
SUCCESS,
WAIT
};
Command command;
State state;
std::uint32_t length;
};
struct Request {
Package_header::Command command;
std::uint32_t length;
int cd;
};
}
// namespace Kuori
}
#endif

View File

@@ -1,22 +1,8 @@
SRC_CC = main.cc
SRC_CC += vfs.cc printf.cc rand.cc sys_arch.cc
TARGET = kuori
LIBPORTS_DIR = $(REP_DIR)/../libports
VFS_DIR = $(LIBPORTS_DIR)/src/lib/vfs/lwip
vpath %.cc $(VFS_DIR)
LWIP_PORT_DIR := $(call select_from_ports,lwip)
LWIPDIR := $(LWIP_PORT_DIR)/src/lib/lwip/src
INC_DIR += $(LWIP_PORT_DIR)/include/lwip \
$(LWIPDIR)/include \
$(LWIPDIR)/include/ipv4 \
$(LWIPDIR)/include/api \
$(LWIPDIR)/include/netif \
$(LIBPORTS_DIR)/src/lib/lwip/include \
$(LIBPORTS_DIR)/src/lib/vfs \
$(LIBPORTS_DIR)/src/lib/vfs/lwip
INC_DIR += $(call select_from_repositories,src/lib/libc)
INC_DIR += $(call select_from_repositories,src/lib/libc)/spec/x86_64
CC_OPT += -Wno-error=conversion -Wno-error=effc++
LIBS += base libm libc lwip stdcxx