/* test_roster.c — VM roster inventory coherence (WS2): VMSIG_EV_ROSTER publish, CAP_ROSTER * gating, endpoint_mask scoping, retained-replay to a late subscriber, and DETACH clearing * the retained datum. Publish/replay are synchronous (no fd), so the loop is not run: the * inproc deliver fires the subscriber callback inline. */ #define _GNU_SOURCE #include "vmsig.h" #include "core_internal.h" /* core_roster_publish */ #include #include #include static int g_fail = 0; #define CHECK(cond, msg) do { \ if (!(cond)) { printf(" FAIL: %s\n", (msg)); g_fail = 1; } \ } while (0) typedef struct { int count; uint32_t ep, vmid, state, action; char name[VMSIG_ROSTER_NAME_MAX]; } robs; static int rob_on_ev(void* u, const vmsig_event* ev) { robs* r = u; if (ev->kind != VMSIG_EV_ROSTER) return 0; const vmsig_roster* e = (const vmsig_roster*)ev->inln; r->count++; r->ep = ev->endpoint; r->vmid = e->vmid; r->state = e->state; r->action = e->action; memcpy(r->name, e->name, sizeof r->name); return 0; } static int add_robs(vmsig_core* core, robs* r, uint32_t cap, uint64_t epmask) { vmsig_inproc_cfg cfg; memset(&cfg, 0, sizeof cfg); cfg.on_event = rob_on_ev; cfg.user = r; void* ctl = vmsig_inproc_control_new(&cfg); vmsig_grant g; memset(&g, 0, sizeof g); g.principal = 9; g.endpoint_mask = epmask; g.source_mask = 0xFFFFFFFFu; g.cap_mask = cap; return vmsig_core_add_control(core, vmsig_inproc_control_ops(), ctl, &g); } static void publish(vmsig_core* core, uint32_t ep, uint32_t vmid, uint32_t state, uint32_t action, const char* name) { vmsig_roster e; memset(&e, 0, sizeof e); e.vmid = vmid; e.state = state; e.action = action; snprintf(e.name, sizeof e.name, "%s", name); core_roster_publish(core, ep, &e); } static void test_roster(void) { printf("test_roster\n"); vmsig_ctx* ctx = vmsig_ctx_new(); vmsig_core* core = vmsig_core_new(ctx); robs a, b, cc; memset(&a,0,sizeof a); memset(&b,0,sizeof b); memset(&cc,0,sizeof cc); add_robs(core, &a, VMSIG_CAP_ROSTER, ~0ull); /* all endpoints, can see roster */ add_robs(core, &b, VMSIG_CAP_OBSERVE, ~0ull); /* no CAP_ROSTER -> denied */ add_robs(core, &cc, VMSIG_CAP_ROSTER, 1ull << 0); /* scoped to ep0 only */ /* ATTACH ep0 */ publish(core, 0, 1001, VMSIG_VM_RUNNING, VMSIG_ROSTER_ATTACH, "win-1001"); CHECK(a.count == 1 && a.ep == 0 && a.vmid == 1001 && a.action == VMSIG_ROSTER_ATTACH, "A (CAP_ROSTER) received ATTACH ep0"); CHECK(strcmp(a.name, "win-1001") == 0, "A: name carried inline"); CHECK(b.count == 0, "B without CAP_ROSTER does NOT receive roster"); CHECK(cc.count == 1, "C scoped to ep0 received ep0 ATTACH"); /* ATTACH ep1 */ publish(core, 1, 1002, VMSIG_VM_RUNNING, VMSIG_ROSTER_ATTACH, "win-1002"); CHECK(a.count == 2 && a.ep == 1 && a.vmid == 1002, "A received ATTACH ep1"); CHECK(cc.count == 1, "C scoped to ep0 does NOT receive ep1 (endpoint_mask filter)"); /* late subscriber D: replay of the retained roster (ep0 + ep1) on add_control */ robs d; memset(&d, 0, sizeof d); add_robs(core, &d, VMSIG_CAP_ROSTER, ~0ull); CHECK(d.count == 2, "late subscriber D replayed BOTH retained roster entries"); /* DETACH ep0: current subscribers see it; the retained datum is cleared */ publish(core, 0, 1001, VMSIG_VM_SHUTDOWN, VMSIG_ROSTER_DETACH, "win-1001"); CHECK(a.count == 3 && a.ep == 0 && a.action == VMSIG_ROSTER_DETACH, "A received DETACH ep0"); /* late subscriber E after DETACH: replay yields ONLY ep1 (ep0 cleared) */ robs e; memset(&e, 0, sizeof e); add_robs(core, &e, VMSIG_CAP_ROSTER, ~0ull); CHECK(e.count == 1 && e.ep == 1 && e.vmid == 1002, "late subscriber E replayed only the live ep1 (detached ep0 not retained)"); vmsig_core_free(core); vmsig_ctx_free(ctx); } int main(void) { test_roster(); printf("roster tests: %s\n", g_fail ? "FAIL" : "PASS"); return g_fail ? 1 : 0; }