23#include "lwip/sockets.h"
37const char*
TAG =
"NET";
39constexpr uint8_t ACCEPT_RING = 4;
42 void* plugin =
nullptr;
45 uint32_t action_id = 0;
47 int ring[ACCEPT_RING];
48 uint8_t head = 0, tail = 0;
53 return p && p->manifest().capabilities.net_listen;
57int open_listen(uint16_t port) {
58 int fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
59 if (fd < 0)
return -1;
61 ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one,
sizeof(one));
63 struct sockaddr_in addr{};
64 addr.sin_family = AF_INET;
65 addr.sin_addr.s_addr = htonl(INADDR_ANY);
66 addr.sin_port = htons(port);
67 if (::bind(fd,
reinterpret_cast<struct sockaddr*
>(&addr),
sizeof(addr)) != 0 ||
68 ::listen(fd, 4) != 0) {
72 int flags = ::fcntl(fd, F_GETFL, 0);
73 if (
flags >= 0) ::fcntl(fd, F_SETFL,
flags | O_NONBLOCK);
77void close_listener() {
78 if (net_state.listen_fd >= 0) { ::close(net_state.listen_fd); net_state.listen_fd = -1; }
79 while (net_state.tail != net_state.head) {
80 ::close(net_state.ring[net_state.tail]);
81 net_state.tail =
static_cast<uint8_t
>((net_state.tail + 1) % ACCEPT_RING);
93 if (net_state.active) {
94 if (net_state.plugin == plugin && net_state.port == port) {
95 net_state.action_id = action_id;
101 net_state.plugin = plugin;
102 net_state.port = port;
103 net_state.action_id = action_id;
104 net_state.listen_fd = open_listen(port);
105 net_state.active =
true;
107 if (net_state.listen_fd < 0) {
108 LOG_W(
TAG,
"bind port %u pending (retrying)",
static_cast<unsigned>(port));
110 LOG_I(
TAG,
"listening on port %u",
static_cast<unsigned>(port));
120 int fd = net_state.ring[net_state.tail];
121 net_state.tail =
static_cast<uint8_t
>((net_state.tail + 1) % ACCEPT_RING);
123 if (handle < 0) { ::close(fd);
return handle; }
130 (port != 0 && net_state.port != port)) {
135 net_state.listen_fd = -1;
142 if (!net_state.active)
return;
143 if (net_state.listen_fd < 0) {
144 net_state.listen_fd = open_listen(net_state.port);
145 if (net_state.listen_fd < 0)
return;
146 LOG_I(
TAG,
"listening on port %u",
static_cast<unsigned>(net_state.port));
149 int c = ::accept(net_state.listen_fd,
nullptr,
nullptr);
151 if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
153 ::close(net_state.listen_fd);
154 net_state.listen_fd = -1;
157 uint8_t next =
static_cast<uint8_t
>((net_state.head + 1) % ACCEPT_RING);
158 if (next == net_state.tail) { ::close(c);
break; }
159 net_state.ring[net_state.head] = c;
160 net_state.head = next;
162 if (net_state.tail != net_state.head && net_state.action_id && net_state.plugin) {
164 static_cast<pm::Plugin*
>(net_state.plugin), net_state.action_id, 0, 0);
169 if (net_state.active && net_state.plugin == plugin) {
172 net_state.listen_fd = -1;
Discovers, loads, runs and unloads WASM plugins on the badge.
Owned WAMR module instance + per-plugin state.
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
#define LOG_I(tag, fmt,...)
static PluginManager & instance() noexcept
void dispatchActionTo(Plugin *plugin, uint32_t action_id, uint32_t idx, uint32_t user_data)
int host_net_accept(void)
Take the next accepted connection as a socket handle for host_socket_read/write/close.
int host_net_listen(uint16_t port, uint32_t action_id)
Start a TCP listener on port; fires action_id while clients wait.
int host_net_close(uint16_t port)
Stop the listener (pass 0 or the listening port). Closes pending un-accepted connections.
CDC Badge OS plugin host API - canonical C ABI contract.
#define HOST_ERR_NO_CAPABILITY
#define HOST_ERR_INVALID_ARG
#define HOST_ERR_NOT_FOUND
void * plg_get_active_plugin(void)
void * plg_get_active_plugin(void)
int plg_socket_adopt(int fd, void *owner)
void plg_net_on_unload(void *plugin)