mirror of
https://dev.lirent.ru/Vatrog/vm-automation-signaling.git
synced 2026-06-26 04:36:37 +03:00
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:
@@ -0,0 +1,156 @@
|
||||
/* vmsigd.c — the vmsig management daemon.
|
||||
*
|
||||
* Owns the /dev/shm/vmsig discovery namespace and serves a unix-socket control plane over the
|
||||
* signaling layer for the VMs found there. It wires nothing VM-specific: discovery hot-plugs
|
||||
* each VM's adapter trio and publishes the roster; the daemon only supplies the loop, the
|
||||
* discovery roots, the control socket, and a coarse per-uid admission policy.
|
||||
*
|
||||
* Real input/memctx actuation needs an armed library build (memctx -> vmie). A stub build
|
||||
* still runs (socket/admission/discovery machinery), but memctx will not bootstrap.
|
||||
*
|
||||
* Usage: vmsigd [--config PATH] [--socket S] [--watch DIR] [--pve-conf DIR] [--qmp-dir DIR]
|
||||
* [--slots PATH] [--foreground]
|
||||
* precedence: argv > environment (VMSIGD_*) > config file > built-in defaults. */
|
||||
#define _GNU_SOURCE
|
||||
#include "vmsig.h"
|
||||
#include "vmsig_socket.h"
|
||||
#include "discovery.h"
|
||||
#include "core_internal.h" /* core_add_source (in-repo daemon, intimate with the core) */
|
||||
#include "vmsigd.h"
|
||||
#include "vmsigd_admission.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
static vmsig_core* g_core;
|
||||
static vmsigd_config g_cfg;
|
||||
static char g_cfg_path[VMSIGD_PATH_MAX];
|
||||
|
||||
/* Audit trace: admissions/denials, lease and memctx grants — on the loop thread, to stderr
|
||||
* (systemd routes stderr to the journal). */
|
||||
static void on_audit(void* ud, const vmsig_audit* a) {
|
||||
(void)ud;
|
||||
static const char* k[] = {
|
||||
"ADMIT", "REJECT", "DOWN_DENIED", "LEASE_GRANTED", "LEASE_DENIED",
|
||||
"LEASE_REVOKED", "LEASE_RECLAIMED", "MEMCTX_GRANTED"
|
||||
};
|
||||
const char* name = (a->kind <= VMSIG_AUDIT_MEMCTX_GRANTED) ? k[a->kind] : "?";
|
||||
fprintf(stderr, "vmsigd: audit %-14s principal=%u ep=%u cmd=%u detail=%u\n",
|
||||
name, a->principal, a->endpoint, a->cmd, a->detail);
|
||||
}
|
||||
|
||||
/* Signals arrive as fd readiness (signalfd) on the loop thread — no async-handler hazards.
|
||||
* TERM/INT => graceful stop; HUP => reload ONLY the admission table from the config file
|
||||
* (paths/socket/adapters are untouched; already-connected grants are not retroactively
|
||||
* changed — a peer reconnects to pick up a changed entitlement). */
|
||||
static void on_signal(void* user, uint32_t events) {
|
||||
(void)events;
|
||||
int sfd = *(int*)user;
|
||||
struct signalfd_siginfo si;
|
||||
while (read(sfd, &si, sizeof si) == (ssize_t)sizeof si) {
|
||||
if (si.ssi_signo == SIGINT || si.ssi_signo == SIGTERM) {
|
||||
vmsig_core_stop(g_core);
|
||||
} else if (si.ssi_signo == SIGHUP) {
|
||||
vmsigd_config fresh;
|
||||
vmsigd_config_defaults(&fresh);
|
||||
if (g_cfg_path[0] && vmsigd_config_parse_file(&fresh, g_cfg_path) == 0) {
|
||||
memcpy(g_cfg.grants, fresh.grants, sizeof g_cfg.grants);
|
||||
g_cfg.ngrants = fresh.ngrants; /* swap admission table only */
|
||||
fprintf(stderr, "vmsigd: reloaded %d grant rule(s)\n", g_cfg.ngrants);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char* arg_val(int argc, char** argv, int* i) {
|
||||
char* a = argv[*i];
|
||||
char* eq = strchr(a, '=');
|
||||
if (eq) return eq + 1;
|
||||
if (*i + 1 < argc) { (*i)++; return argv[*i]; }
|
||||
return "";
|
||||
}
|
||||
|
||||
static void apply_env(vmsigd_config* c) {
|
||||
const char* v;
|
||||
if ((v = getenv("VMSIGD_SOCKET"))) snprintf(c->socket, sizeof c->socket, "%s", v);
|
||||
if ((v = getenv("VMSIGD_WATCH"))) snprintf(c->watch, sizeof c->watch, "%s", v);
|
||||
if ((v = getenv("VMSIGD_PVE_CONF"))) snprintf(c->pve_conf, sizeof c->pve_conf, "%s", v);
|
||||
if ((v = getenv("VMSIGD_QMP_DIR"))) snprintf(c->qmp_dir, sizeof c->qmp_dir, "%s", v);
|
||||
if ((v = getenv("VMSIGD_SLOTS"))) snprintf(c->slots, sizeof c->slots, "%s", v);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
/* config path: argv --config > env > default. */
|
||||
const char* cfg_path = getenv("VMSIGD_CONFIG");
|
||||
if (!cfg_path) cfg_path = "/etc/vmsig/vmsigd.conf";
|
||||
for (int i = 1; i < argc; i++)
|
||||
if (!strncmp(argv[i], "--config", 8)) { cfg_path = arg_val(argc, argv, &i); }
|
||||
|
||||
vmsigd_config_defaults(&g_cfg);
|
||||
vmsigd_config_parse_file(&g_cfg, cfg_path); /* missing file => defaults (not fatal) */
|
||||
snprintf(g_cfg_path, sizeof g_cfg_path, "%s", cfg_path);
|
||||
apply_env(&g_cfg);
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
char* a = argv[i];
|
||||
if (!strncmp(a, "--config", 8)) { (void)arg_val(argc, argv, &i); }
|
||||
else if (!strncmp(a, "--socket", 8)) snprintf(g_cfg.socket, sizeof g_cfg.socket, "%s", arg_val(argc, argv, &i));
|
||||
else if (!strncmp(a, "--watch", 7)) snprintf(g_cfg.watch, sizeof g_cfg.watch, "%s", arg_val(argc, argv, &i));
|
||||
else if (!strncmp(a, "--pve-conf", 10)) snprintf(g_cfg.pve_conf, sizeof g_cfg.pve_conf, "%s", arg_val(argc, argv, &i));
|
||||
else if (!strncmp(a, "--qmp-dir", 9)) snprintf(g_cfg.qmp_dir, sizeof g_cfg.qmp_dir, "%s", arg_val(argc, argv, &i));
|
||||
else if (!strncmp(a, "--slots", 7)) snprintf(g_cfg.slots, sizeof g_cfg.slots, "%s", arg_val(argc, argv, &i));
|
||||
else if (!strcmp(a, "--foreground")) { /* default; systemd Type=simple */ }
|
||||
else if (!strcmp(a, "-h") || !strcmp(a, "--help")) {
|
||||
fprintf(stderr, "usage: %s [--config P][--socket S][--watch D][--pve-conf D]"
|
||||
"[--qmp-dir D][--slots P][--foreground]\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Signals via signalfd, serviced on the loop thread. SIGPIPE ignored (dead-peer writes). */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGHUP);
|
||||
sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
int sfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
if (sfd < 0) { perror("vmsigd: signalfd"); return 1; }
|
||||
|
||||
vmsig_ctx* ctx = vmsig_ctx_new();
|
||||
if (!ctx) { fprintf(stderr, "vmsigd: ctx_new failed\n"); close(sfd); return 1; }
|
||||
g_core = vmsig_core_new(ctx);
|
||||
if (!g_core) { fprintf(stderr, "vmsigd: core_new failed\n"); vmsig_ctx_free(ctx); close(sfd); return 1; }
|
||||
vmsig_core_set_audit(g_core, on_audit, NULL);
|
||||
|
||||
if (core_add_source(g_core, sfd, on_signal, &sfd, NULL) != 0) {
|
||||
fprintf(stderr, "vmsigd: signal source registration failed\n");
|
||||
vmsig_core_free(g_core); vmsig_ctx_free(ctx); close(sfd); return 1;
|
||||
}
|
||||
|
||||
vmsig_discovery* disc = vmsig_discovery_new(
|
||||
g_core, g_cfg.watch, g_cfg.pve_conf, g_cfg.qmp_dir,
|
||||
g_cfg.slots[0] ? g_cfg.slots : NULL, NULL, NULL);
|
||||
if (!disc) {
|
||||
fprintf(stderr, "vmsigd: discovery_new(%s) failed\n", g_cfg.watch);
|
||||
vmsig_core_free(g_core); vmsig_ctx_free(ctx); close(sfd); return 1;
|
||||
}
|
||||
|
||||
vmsigd_admission adm = { &g_cfg, disc };
|
||||
if (vmsig_socket_attach(g_core, g_cfg.socket, vmsigd_policy, &adm) != 0) {
|
||||
fprintf(stderr, "vmsigd: socket_attach(%s) failed\n", g_cfg.socket);
|
||||
vmsig_core_free(g_core); vmsig_ctx_free(ctx); close(sfd); return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "vmsigd: serving %s (watch=%s pve=%s qmp=%s) %d grant rule(s)\n",
|
||||
g_cfg.socket, g_cfg.watch, g_cfg.pve_conf, g_cfg.qmp_dir, g_cfg.ngrants);
|
||||
int rc = vmsig_core_run(g_core);
|
||||
fprintf(stderr, "vmsigd: loop exit rc=%d\n", rc);
|
||||
|
||||
vmsig_core_free(g_core); /* reaps discovery (source on_free) + closes the socket listener */
|
||||
vmsig_ctx_free(ctx);
|
||||
close(sfd);
|
||||
return rc;
|
||||
}
|
||||
Reference in New Issue
Block a user