vmsig: management daemon, runtime endpoint lifecycle, roster, discovery, in-tree drivers, packaging

- core: runtime attach/detach of a per-endpoint adapter trio (runtime-safe add_adapter + vmsig_core_detach_endpoint, deferred reap)
- roster: VMSIG_EV_ROSTER + CAP_ROSTER, retained per-endpoint and replayed to late subscribers
- discovery: inotify trigger dir, vmid/endpoint slot allocator, host probe; vmsigd daemon with config + per-uid admission
- input driver and vgpu perception built in-tree; vgpu perception as a separate library
- memctx: own the supplied ro_fd (closed at detach)
- deb packaging: install rules, systemd unit, tmpfiles, default config
This commit is contained in:
2026-06-22 17:25:06 +03:00
parent 0d387a4249
commit 9bde398b6c
55 changed files with 4703 additions and 61 deletions
+121
View File
@@ -0,0 +1,121 @@
/* test_daemoncfg.c — vmsigd config parser + admission policy (WS4). Config parse is pure;
* admission is exercised against a live discovery (fake probe + recording sink) so the
* vmid->endpoint resolution at connect time is verified end-to-end without armed adapters. */
#define _GNU_SOURCE
#include "vmsig.h"
#include "discovery.h"
#include "vmsigd.h"
#include "vmsigd_admission.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
static int g_fail = 0;
#define CHECK(cond, msg) do { if (!(cond)) { printf(" FAIL: %s\n", (msg)); g_fail = 1; } } while (0)
static void test_config(void) {
printf("test_config\n");
const char* sample =
"# vmsigd config\n"
"socket = /run/foo.sock\n"
"watch = /dev/shm/vmsig\n"
"pve_conf = /etc/pve/qemu-server\n"
"\n"
"[grant uid=0]\n"
"vmids = *\n"
"caps = observe,input,memctx,roster\n"
"arb_prio = 100\n"
"[grant uid=1000]\n"
"vmids = 101, 102\n"
"caps = observe\n"
"arb_prio = 50\n";
vmsigd_config c; vmsigd_config_defaults(&c);
CHECK(vmsigd_config_parse_buf(&c, sample) == 0, "parse ok");
CHECK(strcmp(c.socket, "/run/foo.sock") == 0, "global socket override");
CHECK(strcmp(c.qmp_dir, "/var/run/qemu-server") == 0, "default qmp_dir retained");
CHECK(c.ngrants == 2, "two grant stanzas");
CHECK(c.grants[0].uid == 0 && c.grants[0].all_vms, "grant0 uid=0 vmids=*");
CHECK(c.grants[0].cap_mask ==
(VMSIG_CAP_OBSERVE | VMSIG_CAP_INPUT | VMSIG_CAP_MEMCTX | VMSIG_CAP_ROSTER),
"grant0 caps parsed");
CHECK(c.grants[0].arb_prio == 100, "grant0 arb_prio");
CHECK(c.grants[1].uid == 1000 && !c.grants[1].all_vms && c.grants[1].nvmids == 2 &&
c.grants[1].vmids[0] == 101 && c.grants[1].vmids[1] == 102, "grant1 vmid list");
CHECK(c.grants[1].cap_mask == VMSIG_CAP_OBSERVE, "grant1 caps");
CHECK(c.grants[1].arb_prio == 50, "grant1 arb_prio");
}
/* ---- fake probe + recording sink (attach vmids to slots without armed adapters) ---- */
typedef struct { int dummy; } fakeprobe;
static int fp_config(const vmsig_host_probe* p, uint32_t vmid, vmsig_host_facts* out) {
(void)p; memset(out, 0, sizeof *out); out->vmid = vmid; out->share_on = 1; out->ok = 1;
snprintf(out->name, sizeof out->name, "win-%u", vmid);
return 0;
}
static int fp_live(const vmsig_host_probe* p, vmsig_host_facts* io) {
(void)p; io->retry = 0; io->ok = 1; io->vm_state = VMSIG_VM_RUNNING; io->low = 0x80000000ull;
return 0;
}
static int rs_attach(void* ud, vmsig_core* core, uint32_t vmid, uint32_t ep,
const vmsig_host_facts* f) { (void)ud;(void)core;(void)vmid;(void)ep;(void)f; return 0; }
static void rs_detach(void* ud, vmsig_core* core, uint32_t vmid, uint32_t ep) {
(void)ud;(void)core;(void)vmid;(void)ep;
}
static void test_admission(void) {
printf("test_admission\n");
vmsig_ctx* ctx = vmsig_ctx_new();
vmsig_core* core = vmsig_core_new(ctx);
fakeprobe fpd;
vmsig_host_probe probe = { fp_config, fp_live, &fpd };
vmsig_discovery_sink sink = { rs_attach, rs_detach, NULL };
char dir[] = "/tmp/vmsig_adm.XXXXXX"; CHECK(mkdtemp(dir) != NULL, "temp dir");
vmsig_discovery* disc = vmsig_discovery_new(core, dir, NULL, NULL, NULL, &probe, &sink);
CHECK(disc != NULL, "discovery created");
vmsig_discovery_feed(disc, 101, 1); /* -> ep0 */
vmsig_discovery_feed(disc, 102, 1); /* -> ep1 */
vmsigd_config c; vmsigd_config_defaults(&c);
vmsigd_config_parse_buf(&c,
"[grant uid=0]\nvmids=*\ncaps=observe,input,memctx,roster\narb_prio=100\n"
"[grant uid=1000]\nvmids=101,102\ncaps=observe\narb_prio=50\n"
"[grant uid=1001]\nvmids=999\ncaps=observe\narb_prio=10\n");
vmsigd_admission adm = { &c, disc };
/* uid 0: all_vms => full mask */
vmsig_grant g0 = vmsigd_policy(0, 0, &adm);
CHECK(g0.endpoint_mask == ~0ull, "uid0 (vmids=*) covers all endpoints");
CHECK(g0.cap_mask == (VMSIG_CAP_OBSERVE | VMSIG_CAP_INPUT | VMSIG_CAP_MEMCTX | VMSIG_CAP_ROSTER),
"uid0 caps");
CHECK(g0.arb_prio == 100 && g0.principal == 0, "uid0 prio/principal");
/* uid 1000: vmids 101,102 attached at ep0,ep1 => bits 0,1 */
vmsig_grant g1 = vmsigd_policy(1000, 0, &adm);
CHECK(g1.endpoint_mask == ((1ull << 0) | (1ull << 1)), "uid1000 resolved to ep0,ep1 bits");
CHECK(g1.cap_mask == VMSIG_CAP_OBSERVE && g1.arb_prio == 50, "uid1000 caps/prio");
/* uid 1001: vmid 999 not attached => no bits (peer learns via roster / reconnect) */
vmsig_grant g2 = vmsigd_policy(1001, 0, &adm);
CHECK(g2.endpoint_mask == 0, "uid1001 unbound vmid => no endpoint bit yet");
/* unknown uid: empty grant => reject */
vmsig_grant g3 = vmsigd_policy(4242, 0, &adm);
CHECK(g3.cap_mask == 0 && g3.endpoint_mask == 0, "unknown uid => empty grant (reject)");
vmsig_core_free(core);
vmsig_ctx_free(ctx);
rmdir(dir);
}
int main(void) {
test_config();
test_admission();
printf("daemoncfg tests: %s\n", g_fail ? "FAIL" : "PASS");
return g_fail ? 1 : 0;
}