mirror of
https://dev.lirent.ru/Vatrog/vm-automation-signaling.git
synced 2026-06-25 20:36:36 +03:00
e9aee057c7
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.
94 lines
3.6 KiB
C
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;
|
|
}
|