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
+5 -17
View File
@@ -3,19 +3,17 @@
* Mechanism (recommended): vmctl is a blocking QMP round-trip; we run it on a
* worker thread, completion ack via a completion-eventfd. The uinput path is a
* local instantaneous write; when armed it would be done inline (see comment in submit).
* Real actuation is under VMSIG_WITH_VMCTL; otherwise the stub acks (spine without a VM). */
* Real actuation when cfg.stub==0 (vmctl opened); otherwise the stub acks (spine without a VM).
* vmctl is the in-tree input driver (src/si/input/, absorbed); cfg.stub gates opening it. */
#include "vmsig_adapter.h"
#include "adapter_util.h"
#include "input.h"
#include "vmctl.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/epoll.h>
#ifdef VMSIG_WITH_VMCTL
#include "vmctl.h"
#endif
/* POD request/result of the worker. */
typedef struct {
int cmd; /* 0 = input event, 1 = lifecycle */
@@ -40,9 +38,7 @@ struct vmsig_adapter {
vmsig_emit emit;
vmsig_worker* worker;
const char* qmp_path; /* borrowed from cfg (valid through attach); SERVICE power/lifecycle */
#ifdef VMSIG_WITH_VMCTL
vmctl_t* vmctl;
#endif
vmctl_t* vmctl; /* NULL in stub mode (cfg.stub) — no actuator opened */
};
static int input_job(void* user, const void* reqp, void* resp) {
@@ -53,7 +49,6 @@ static int input_job(void* user, const void* reqp, void* resp) {
rs->corr = rq->corr;
rs->origin = rq->origin;
rs->noack = rq->noack;
#ifdef VMSIG_WITH_VMCTL
if (a->vmctl) {
int r = -1;
if (rq->cmd == 0) {
@@ -87,9 +82,8 @@ static int input_job(void* user, const void* reqp, void* resp) {
rs->ok = (r == 0);
return r;
}
#endif
(void)a;
rs->ok = 1; /* stub: ack without actuation */
rs->ok = 1; /* stub: ack without actuation (vmctl not opened) */
return 0;
}
@@ -109,7 +103,6 @@ static int in_attach(vmsig_adapter* a, const vmsig_emit* emit, vmsig_fd_reg* reg
a->worker = vmsig_worker_new(input_job, a, 1, 64); /* QMP is a serial channel, cap 64 */
if (!a->worker) return -1;
#ifdef VMSIG_WITH_VMCTL
if (!a->stub) {
/* armed: open the actuator. Injection is ALWAYS uinput (orphaned host uinput + external
* QEMU input-linux). PTR_BOTH gives both pointer forms a device (A=abs tablet, B=rel
@@ -125,7 +118,6 @@ static int in_attach(vmsig_adapter* a, const vmsig_emit* emit, vmsig_fd_reg* reg
a->vmctl = vmctl_open(&vcfg);
if (!a->vmctl) { vmsig_worker_free(a->worker); a->worker = NULL; return -1; }
}
#endif
reg[0].fd = vmsig_worker_evfd(a->worker);
reg[0].epoll_events = EPOLLIN;
@@ -166,7 +158,6 @@ static int in_submit(vmsig_adapter* a, const vmsig_event* ev) {
* actuate — nothing to hold). */
vmsig_input_held h;
memset(&h, 0, sizeof h);
#ifdef VMSIG_WITH_VMCTL
if (a->vmctl) {
const uint32_t capn = (uint32_t)(sizeof h.ent / sizeof h.ent[0]);
unsigned char bits[VMCTL_KEYS_SNAPSHOT_BYTES];
@@ -184,7 +175,6 @@ static int in_submit(vmsig_adapter* a, const vmsig_event* ev) {
else h.flags |= VMSIG_INPUT_HELD_TRUNC;
}
}
#endif
vmsig_event up;
memset(&up, 0, sizeof up);
up.kind = VMSIG_EV_INPUT_HELD; up.source = VMSIG_SRC_INPUT; up.dir = VMSIG_DIR_UP;
@@ -223,9 +213,7 @@ static int in_submit(vmsig_adapter* a, const vmsig_event* ev) {
static void in_close(vmsig_adapter* a) {
if (!a) return;
vmsig_worker_free(a->worker);
#ifdef VMSIG_WITH_VMCTL
if (a->vmctl) vmctl_close(a->vmctl);
#endif
free(a);
}