Files
hmmmm/src/proto/msg.c
root 589bc8d620 feat: migrate config management from TOML to FlatBuffers
Config is now loaded at runtime via WebSocket (ConfigCtrlMessage)
instead of from hardcoded TOML files. The emulator starts with no
devices and waits for clients to send configuration.

- Define FlatBuffers schemas: EmulationConfig, ComposeDeviceConfig,
  BaseDeviceConfig with recursive DeviceConfig union
- Rename MemSegment.start → addr (flatcc builder/reader name collision)
- Add ConfigCtrlMessage handler: validates STILL state, walks the
  device tree depth-first, assigns numeric IDs, responds with
  DeviceIdMappingNotif or ConfigLoadError
- Add fb_build_config_device_id_mapping() and fb_build_config_error()
  FlatBuffer builders
- Remove hardcoded device loading from main.c; iterate dynamically
  loaded devices in the exec loop
- Fix double-free: freeConf() already frees the struct itself, remove
  redundant free() calls in config.c and base_device.c
- Fix heap-buffer-overflow in device parseSpecsFromConfig: malloc
  for segment name was missing +1 for the null terminator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 14:02:41 +00:00

353 lines
11 KiB
C

#include "proto/msg.h"
#include "panic.h"
#include "flatcc/flatcc_builder.h"
#include "proto_builder.h"
#include "control_builder.h"
#include "exec_notif_builder.h"
#include "auth_builder.h"
#include "stream_builder.h"
#include "stream_data_builder.h"
uint8_t* fb_build_auth_response(uint64_t nonce, uint64_t seat_id, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
hmmmm_auth_AuthResponse_ref_t ar =
hmmmm_auth_AuthResponse_create(&B, seat_id);
hmmmm_ServerPayload_union_ref_t payload =
hmmmm_ServerPayload_as_AuthResponse(ar);
hmmmm_ServerMessage_create_as_root(&B, nonce, payload);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_exec_notify(uint64_t nonce, uint64_t tclk, uint8_t state, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
hmmmm_ctrl_exec_notif_ExecNotifyMessage_ref_t notif =
hmmmm_ctrl_exec_notif_ExecNotifyMessage_create(
&B, tclk, (hmmmm_ctrl_exec_notif_ExecState_enum_t)(int8_t)state);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
hmmmm_ctrl_CtrlServerPayload_as_ExecNotifyMessage(notif);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
hmmmm_ServerPayload_union_ref_t payload =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, payload);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_stream_data_push(
flatcc_builder_t *B,
uint64_t nonce, uint32_t stream_id, uint64_t tclk,
const uint8_t* data, size_t data_len,
size_t* len_out)
{
flatcc_builder_reset(B);
flatbuffers_uint8_vec_ref_t data_vec =
flatbuffers_uint8_vec_create(B, data, data_len);
hmmmm_stream_StreamDataPush_ref_t push =
hmmmm_stream_StreamDataPush_create(B, stream_id, tclk, data_vec);
hmmmm_stream_StreamServerPayload_union_ref_t stream_payload =
hmmmm_stream_StreamServerPayload_as_StreamDataPush(push);
hmmmm_stream_StreamServerMessage_ref_t stream_msg =
hmmmm_stream_StreamServerMessage_create(B, stream_payload);
hmmmm_ServerPayload_union_ref_t payload =
hmmmm_ServerPayload_as_StreamServerMessage(stream_msg);
hmmmm_ServerMessage_create_as_root(B, nonce, payload);
uint8_t* buf = flatcc_builder_finalize_buffer(B, len_out);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_setup_buf(uint64_t nonce, uint32_t lost_buf_size,
uint64_t client_lifetime_ticks, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
hmmmm_ctrl_setup_buf_SetupBuf_ref_t sb =
hmmmm_ctrl_setup_buf_SetupBuf_create(&B, lost_buf_size, client_lifetime_ticks);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
hmmmm_ctrl_CtrlServerPayload_as_SetupBuf(sb);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
hmmmm_ServerPayload_union_ref_t setup_outer =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, setup_outer);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_orphaned_response(uint64_t nonce, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
// Use start/end directly: passing 0 to _create causes entries_add to return -1
hmmmm_ctrl_orphaned_OrphanedResponse_start(&B);
hmmmm_ctrl_orphaned_OrphanedResponse_ref_t resp =
hmmmm_ctrl_orphaned_OrphanedResponse_end(&B);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
hmmmm_ctrl_CtrlServerPayload_as_OrphanedResponse(resp);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
hmmmm_ServerPayload_union_ref_t orphan_outer =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, orphan_outer);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_lost_messages_response(uint64_t nonce, uint64_t seat_id, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
// Use start/end directly: passing 0 for messages vec causes messages_add to return -1
hmmmm_ctrl_lost_LostMessagesResponse_start(&B);
hmmmm_ctrl_lost_LostMessagesResponse_seat_id_add(&B, seat_id);
hmmmm_ctrl_lost_LostMessagesResponse_ref_t resp =
hmmmm_ctrl_lost_LostMessagesResponse_end(&B);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
hmmmm_ctrl_CtrlServerPayload_as_LostMessagesResponse(resp);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
hmmmm_ServerPayload_union_ref_t lost_outer =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, lost_outer);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_mem_read_response(
uint64_t nonce, uint64_t tclk,
uint32_t device_id, uint32_t seg_id, uint32_t offset,
const uint8_t* data, size_t data_len,
size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
flatbuffers_uint8_vec_ref_t data_vec =
flatbuffers_uint8_vec_create(&B, data, data_len);
hmmmm_mem_MemReadResponse_ref_t resp =
hmmmm_mem_MemReadResponse_create(&B, tclk, device_id, seg_id, offset, data_vec);
hmmmm_mem_MemServerPayload_union_ref_t mem_payload =
hmmmm_mem_MemServerPayload_as_MemReadResponse(resp);
hmmmm_mem_MemServerMessage_ref_t mem_msg =
hmmmm_mem_MemServerMessage_create(&B, mem_payload);
hmmmm_ServerPayload_union_ref_t mem_outer =
hmmmm_ServerPayload_as_MemServerMessage(mem_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, mem_outer);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_stream_reg_confirm(
uint64_t nonce, uint32_t stream_id,
uint32_t device_id, uint32_t seg_id, uint32_t offset, uint32_t length,
uint8_t mode, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
hmmmm_stream_StreamRegConfirm_ref_t confirm =
hmmmm_stream_StreamRegConfirm_create(
&B, stream_id, device_id, seg_id, offset, length,
(hmmmm_stream_StreamMode_enum_t)(int8_t)mode);
hmmmm_stream_StreamServerPayload_union_ref_t sreg_payload =
hmmmm_stream_StreamServerPayload_as_StreamRegConfirm(confirm);
hmmmm_stream_StreamServerMessage_ref_t sreg_msg =
hmmmm_stream_StreamServerMessage_create(&B, sreg_payload);
hmmmm_ServerPayload_union_ref_t sreg_outer =
hmmmm_ServerPayload_as_StreamServerMessage(sreg_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, sreg_outer);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_config_device_id_mapping(
uint64_t nonce,
char** paths[], size_t path_lens[],
size_t device_count, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
// Pre-create DeviceIdEntry refs (bottom-up: strings first, then tables)
hmmmm_ctrl_config_notif_DeviceIdEntry_ref_t entry_refs[device_count];
for (size_t i = 0; i < device_count; i++) {
// Build string vector for path
flatbuffers_string_vec_start(&B);
for (size_t j = 0; j < path_lens[i]; j++) {
flatbuffers_string_ref_t sref = flatbuffers_string_create_str(&B, paths[i][j]);
flatbuffers_string_vec_push(&B, sref);
}
flatbuffers_string_vec_ref_t path_vec = flatbuffers_string_vec_end(&B);
// Empty seg_ids vector
hmmmm_ctrl_config_notif_SegIdEntry_vec_start(&B);
hmmmm_ctrl_config_notif_SegIdEntry_vec_ref_t seg_ids_vec =
hmmmm_ctrl_config_notif_SegIdEntry_vec_end(&B);
entry_refs[i] = hmmmm_ctrl_config_notif_DeviceIdEntry_create(
&B, path_vec, (uint32_t)i, seg_ids_vec);
}
// Build entries vector
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_start(&B);
for (size_t i = 0; i < device_count; i++) {
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_push(&B, entry_refs[i]);
}
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_ref_t entries_vec =
hmmmm_ctrl_config_notif_DeviceIdEntry_vec_end(&B);
hmmmm_ctrl_config_notif_DeviceIdMappingNotif_ref_t mapping =
hmmmm_ctrl_config_notif_DeviceIdMappingNotif_create(&B, entries_vec);
hmmmm_ctrl_config_notif_ConfigNotifPayload_union_ref_t notif_payload =
hmmmm_ctrl_config_notif_ConfigNotifPayload_as_DeviceIdMappingNotif(mapping);
hmmmm_ctrl_config_notif_ConfigNotifMessage_ref_t notif_msg =
hmmmm_ctrl_config_notif_ConfigNotifMessage_create(&B, 0, notif_payload);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
hmmmm_ctrl_CtrlServerPayload_as_ConfigNotifMessage(notif_msg);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
hmmmm_ServerPayload_union_ref_t payload =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, payload);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}
uint8_t* fb_build_config_error(uint64_t nonce, const char* message, size_t* len_out)
{
flatcc_builder_t B;
if (flatcc_builder_init(&B)) {
panic("flatcc_builder_init failed\n");
}
flatbuffers_string_ref_t msg_str = flatbuffers_string_create_str(&B, message);
hmmmm_ctrl_config_notif_ConfigLoadError_ref_t err =
hmmmm_ctrl_config_notif_ConfigLoadError_create(&B, msg_str);
hmmmm_ctrl_config_notif_ConfigNotifPayload_union_ref_t notif_payload =
hmmmm_ctrl_config_notif_ConfigNotifPayload_as_ConfigLoadError(err);
hmmmm_ctrl_config_notif_ConfigNotifMessage_ref_t notif_msg =
hmmmm_ctrl_config_notif_ConfigNotifMessage_create(&B, 0, notif_payload);
hmmmm_ctrl_CtrlServerPayload_union_ref_t ctrl_payload =
hmmmm_ctrl_CtrlServerPayload_as_ConfigNotifMessage(notif_msg);
hmmmm_ctrl_CtrlServerMessage_ref_t ctrl_msg =
hmmmm_ctrl_CtrlServerMessage_create(&B, ctrl_payload);
hmmmm_ServerPayload_union_ref_t payload =
hmmmm_ServerPayload_as_CtrlServerMessage(ctrl_msg);
hmmmm_ServerMessage_create_as_root(&B, nonce, payload);
uint8_t* buf = flatcc_builder_finalize_buffer(&B, len_out);
flatcc_builder_clear(&B);
NULL_GUARD(buf);
return buf;
}