Files
mesh-host/internal/profile/detectors.go
jschoubben 1ea4c330df A seat is hardware; a graphical session is state
Two questions that had been answered by one capability. graphical-session asks
whether a session is running now; seat asks whether one could ever run here.
Assignment needs the second -- a headless server can never have a display
server, a workstation with nothing installed yet can, and until now those
looked identical. The mesh would have assigned xorg to the server and found out
at apply time.

"seat" rather than "display", and the distinction matters most on a phone. An
Android device plainly has a screen and has no seat: nothing there is going to
take a DRM device and present an X or Wayland session on it. A capability
called display would answer yes and be useless. This one answers no, which is
true and useful.

Read from what the kernel reports about its connectors, which distinguishes the
two ways of not having one: a machine with a graphics card and nothing plugged
in is a different thing from a machine with no graphics at all, and somebody
deciding where a desktop goes wants to know which they are looking at.

Verified against this workstation: it reports card1-DP-1 and card1-DP-2, which
are the two monitors actually connected, and ignores the DisplayPort and HDMI
that are not -- and the writeback connector reporting "unknown", which counting
would have handed a seat to machines that have none.
2026-08-29 21:12:22 +02:00

211 lines
7.5 KiB
Go

package profile
import (
"context"
"fmt"
"os"
"strings"
)
// Named capabilities. Constants rather than strings at the call site, because a capability
// nothing declares is a capability nothing can require, and a typo would produce exactly that.
const (
CapContainerRuntime = "container-runtime"
CapPackageManager = "package-manager"
CapServiceManager = "service-manager"
CapFirewall = "firewall"
CapOverlay = "overlay"
CapGraphicalSession = "graphical-session"
// CapSeat is hardware: somewhere a display server COULD run. CapGraphicalSession above is
// state: whether one IS running. Assignment needs the first.
CapSeat = "seat"
CapPrivileged = "privileged"
)
// commandCapability is the shape most detectors take: run something, and treat a working
// invocation as evidence.
//
// It runs a command that only succeeds if the thing is FUNCTIONING, never `--version` alone.
// A version string proves a binary is on disk, which is the assumption 04-ISSUES/007 records
// as false: the package was installed and the daemon was not running.
type commandCapability struct {
name string
command string
args []string
// why describes what a success actually proves, and is reported as the detector's `How`.
why string
// interpret decides the verdict from what the command said and how it exited.
//
// Exists because "exit zero" is not a universal answer. A degraded service manager reports
// its state on stdout and exits non-zero — it is running, and reading only the exit code
// declared no service manager on a machine whose init it was. That is 04-ISSUES/007 in the
// mirror: 007 is installed-but-broken reported present; this is working-but-imperfect
// reported absent. Both place work wrongly, and this one was only visible by running
// against a real machine.
//
// nil means the ordinary rule: success is exit zero.
interpret func(stdout string, err error) (present bool, detail string)
runner Runner
}
func (c commandCapability) Name() string { return c.name }
func (c commandCapability) Detect(ctx context.Context) Verdict {
out, err := c.runner(ctx, c.command, c.args...)
interpret := c.interpret
if interpret == nil {
interpret = exitZero
}
present, detail := interpret(out, err)
if strings.TrimSpace(detail) == "" {
// A verdict with no reason is the fault in a new place: something nobody can act on.
// Reached when a command fails silently, which systemctl does.
if present {
detail = "responded"
} else {
detail = fmt.Sprintf("%s gave no reason", c.command)
}
}
return Verdict{Name: c.name, Present: present, Detail: firstLine(detail), How: c.why}
}
// exitZero is the ordinary rule: the command worked, so the capability is there.
func exitZero(stdout string, err error) (bool, string) {
if err != nil {
return false, err.Error()
}
return true, stdout
}
// systemRunning reads what an init system says about itself rather than how it exited.
//
// `is-system-running` exits non-zero for every state except `running` — including `degraded`,
// which means units failed and the init is emphatically present. Treating that as absent made
// a machine running systemd report no service manager.
func systemRunning(stdout string, err error) (bool, string) {
state := strings.TrimSpace(firstLine(stdout))
switch state {
case "running", "degraded", "starting", "maintenance", "stopping":
return true, state
case "":
if err != nil {
return false, err.Error()
}
return false, "said nothing"
default:
// `offline` and `unknown` mean it is not managing this machine.
return false, state
}
}
func firstLine(s string) string {
s = strings.TrimSpace(s)
if i := strings.IndexByte(s, '\n'); i >= 0 {
s = s[:i]
}
if len(s) > 200 {
s = s[:200] + "…"
}
return s
}
// privileged reports whether the host can change this machine at all.
//
// Reported as a capability rather than checked at startup on purpose: a host that cannot act
// is still a host that can report, and novox/hq ADR 0004 says what varies between nodes lives
// here rather than in the definition of a node.
type privileged struct{}
func (privileged) Name() string { return CapPrivileged }
func (privileged) Detect(context.Context) Verdict {
uid := os.Geteuid()
if uid == 0 {
return Verdict{
Name: CapPrivileged, Present: true,
Detail: "effective uid 0",
How: "effective uid — the host changes a machine, which needs root",
}
}
return Verdict{
Name: CapPrivileged, Present: false,
Detail: fmt.Sprintf("effective uid %d, not 0", uid),
How: "effective uid — the host changes a machine, which needs root",
}
}
// graphicalSession reports whether anything could display a window here.
//
// Environment rather than a probe, because a display server is reachable through a socket a
// detector would have to guess at, and the variables are what an application would use anyway.
// Stated so the limit is visible: this detects that a session is ADVERTISED, which is weaker
// than the other detectors here.
type graphicalSession struct{}
func (graphicalSession) Name() string { return CapGraphicalSession }
func (graphicalSession) Detect(context.Context) Verdict {
const how = "DISPLAY / WAYLAND_DISPLAY — weaker than the other checks: advertised, not probed"
if d := os.Getenv("WAYLAND_DISPLAY"); d != "" {
return Verdict{Name: CapGraphicalSession, Present: true, Detail: "wayland: " + d, How: how}
}
if d := os.Getenv("DISPLAY"); d != "" {
return Verdict{Name: CapGraphicalSession, Present: true, Detail: "x11: " + d, How: how}
}
return Verdict{
Name: CapGraphicalSession, Present: false,
Detail: "neither DISPLAY nor WAYLAND_DISPLAY is set",
How: how,
}
}
// Default returns the detectors the host runs when nobody says otherwise.
//
// Each command is chosen to prove the thing WORKS rather than exists:
// - the container runtime is asked for server-side information, which fails when the daemon
// is down even though the client is installed — the exact shape of 04-ISSUES/007;
// - the service manager is asked whether it is the running init, not whether it is present;
// - the firewall is asked to list a ruleset, which needs both the tool and the permission.
func Default(runner Runner) []Detector {
if runner == nil {
runner = ExecRunner
}
return []Detector{
privileged{},
graphicalSession{},
seat{},
commandCapability{
name: CapContainerRuntime, command: "docker", args: []string{"info", "--format", "{{.ServerVersion}}"},
why: "asks the daemon for its version — a running daemon, not an installed client",
runner: runner,
},
commandCapability{
name: CapPackageManager, command: "pacman", args: []string{"-Q", "pacman"},
why: "queries the package database — a working database, not a binary on disk",
runner: runner,
},
commandCapability{
name: CapServiceManager, command: "systemctl", args: []string{"is-system-running"},
why: "reads the init's own account of its state — degraded is still running",
interpret: systemRunning,
runner: runner,
},
commandCapability{
name: CapFirewall, command: "nft", args: []string{"list", "ruleset"},
why: "lists the ruleset — needs the tool AND the privilege to use it",
runner: runner,
},
commandCapability{
name: CapOverlay, command: "wg", args: []string{"show", "interfaces"},
why: "asks the kernel for interfaces — needs the module, not just the tool",
runner: runner,
},
}
}
// isRoot is the same question `privileged` answers, exposed for tests that must check the
// detector against something other than itself.
func isRoot() bool { return os.Geteuid() == 0 }