Files
vatrog-vm-signaling/src/test/test_inputobs.c
T
lirent e9aee057c7 vmsig: a neutral signaling layer between sensors/input and controls
An epoll-driven, neutral transfer-event bus that connects sensors and input
actuators to one or more controls, bidirectionally. It owns the transfer context
and events — delivery order, priority, protocol-level timing, and an
interrupt-driven event model over fd sources (eventfd/timerfd/sockets) — and
stays agnostic to both the sensor/input drivers and the control.

What lives here:
- memctx: a coherent address-space context per endpoint — the guest address-space
  root paired with a pre-opened read-only RAM-region fd, with per-endpoint epoch
  invalidation and retained replay to late subscribers. Perception lives in
  out-of-tree sensor libraries that consume this datum read-only.
- exclusive-ownership leases for destructive resource classes (input, power,
  memory-write).
- write-signaled memory writes (MEMWRITE): an atomic write to guest memory routed
  through the seam under an exclusive lease, never a writable mapping.
- a host-management seam for VM lifecycle/status and a neutral input-injection
  command path.
- multi-VM endpoints; capability-gated, audited control authorization over an
  in-process or unix-socket transport.

Builds against headers only by default (a stub mode that exercises the seam
without a VM); armed builds link the real sensor/input libraries behind flags.
2026-06-20 21:21:20 +03:00

94 lines
3.6 KiB
C

/* test_inputobs.c — input observation:
* held-query: a control with CAP_INPUT, on CMD_QUERY_INPUT, receives INPUT_HELD from the
* vmctl record (stub without vmctl => count=0); without CAP_INPUT — DOWN_DENIED.
* (The cursor sensor moved out of signaling with the FRAME adapter: CURSOR_STATE is now
* emitted by the out-of-repo vgpu-perception shell-as-control, not by a signaling adapter.)
* In-proc, under ASAN. Links against libvmsig. */
#define _GNU_SOURCE
#include "vmsig.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
static int g_fail = 0;
#define CHECK(c, m) do { if (!(c)) { printf(" FAIL: %s\n", (m)); g_fail = 1; } } while (0)
#define EP 0u
typedef struct {
vmsig_core* core;
void* ctl;
int held; /* INPUT_HELD count */
int last_held_count;
int stop_held; /* stop after N held replies (0=no) */
} obs;
static int on_ev(void* u, const vmsig_event* ev) {
obs* o = u;
if (ev->kind == VMSIG_EV_INPUT_HELD) {
const vmsig_input_held* h = (const vmsig_input_held*)ev->inln;
o->held++; o->last_held_count = (int)h->count;
if (o->stop_held && o->held >= o->stop_held) vmsig_core_stop(o->core);
}
return 0;
}
static void add_ctl(vmsig_core* core, obs* o, uint32_t cap, uint32_t src_mask) {
vmsig_inproc_cfg cfg; memset(&cfg, 0, sizeof cfg);
cfg.on_event = on_ev; cfg.user = o;
cfg.sub.source_mask = src_mask; cfg.sub.prio_min = VMSIG_PRIO_BULK;
void* ctl = vmsig_inproc_control_new(&cfg);
o->ctl = ctl;
vmsig_grant g; memset(&g, 0, sizeof g);
g.endpoint_mask = 1ull << EP; g.source_mask = src_mask; g.cap_mask = cap;
vmsig_core_add_control(core, vmsig_inproc_control_ops(), ctl, &g);
}
static void send_query_input(void* ctl) {
vmsig_event d; memset(&d, 0, sizeof d);
d.kind = VMSIG_EV_CMD_QUERY_INPUT; d.source = VMSIG_SRC_INPUT; d.dir = VMSIG_DIR_DOWN;
d.endpoint = EP; d.prio = VMSIG_PRIO_HIGH;
vmsig_inproc_send(ctl, &d);
}
static int g_down_denied = 0;
static void audit_cb(void* ud, const vmsig_audit* a) {
(void)ud; if (a->kind == VMSIG_AUDIT_DOWN_DENIED) g_down_denied++;
}
/* ---- held-query: CAP_INPUT -> INPUT_HELD (stub count=0); without cap -> DOWN_DENIED ---- */
static void test_held_query(void) {
printf("test_held_query\n");
g_down_denied = 0;
vmsig_ctx* ctx = vmsig_ctx_new();
vmsig_core* core = vmsig_core_new(ctx);
vmsig_core_set_audit(core, audit_cb, NULL);
vmsig_core_add_adapter(core, vmsig_input_ops(), NULL, EP); /* stub input (no vmctl) */
obs a; memset(&a, 0, sizeof a); a.core = core; a.stop_held = 1;
add_ctl(core, &a, VMSIG_CAP_INPUT, 0xFFFFFFFFu);
send_query_input(a.ctl);
vmsig_core_run(core); /* pump_down -> INPUT_HELD -> pump_up */
CHECK(a.held == 1, "held: CAP_INPUT receives INPUT_HELD");
CHECK(a.last_held_count == 0, "held: stub without vmctl -> count=0");
/* without CAP_INPUT (OBSERVE only): CMD_QUERY_INPUT rejected BEFORE ctx (synchronously). */
obs b; memset(&b, 0, sizeof b); b.core = core;
add_ctl(core, &b, VMSIG_CAP_OBSERVE, 0xFFFFFFFFu);
int before = g_down_denied;
send_query_input(b.ctl);
CHECK(b.held == 0, "held: without CAP_INPUT -> no INPUT_HELD");
CHECK(g_down_denied == before + 1, "held: without CAP_INPUT -> DOWN_DENIED");
vmsig_core_free(core);
vmsig_ctx_free(ctx);
}
int main(void) {
printf("test_inputobs\n");
test_held_query();
printf("inputobs tests: %s\n", g_fail ? "FAIL" : "PASS");
return g_fail ? 1 : 0;
}