/* 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 #include #include #include 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; }