dbus: hold node-message-bus, and never restart the bus live (hq ADR 0215)
A live restart of the system bus during an upgrade hung every login on a workstation until a reboot. The module owns the bus's packages, declares the bus running with no restart or reload trigger, publishes only curated events (health, services, denials; never traffic) and serves tools to look at both buses.
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
)
|
||||
|
||||
var busArg = map[string]any{"type": "string", "enum": Buses,
|
||||
"description": "system (the default) or session (this account's login session bus, where one exists)"}
|
||||
|
||||
func busOf(args map[string]any) string {
|
||||
b, _ := args["bus"].(string)
|
||||
return b
|
||||
}
|
||||
|
||||
// Tools are the module's tools over MCP (novox/hq ADR 0215): the names on either bus, what an object
|
||||
// offers, a bounded watch of message headers, the module's check, and the bus's health.
|
||||
func Tools(m *Machine, w *Watcher) []stdio.Tool {
|
||||
return []stdio.Tool{
|
||||
{
|
||||
Name: "dbus_names",
|
||||
Description: "Every well-known name on the system or session bus: its owner's pid, process, user and " +
|
||||
"systemd unit, the activatable names that are not running, and how many connections the bus has. " +
|
||||
"Read as the operator's account.",
|
||||
Input: map[string]any{"type": "object", "properties": map[string]any{"bus": busArg}},
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
return m.Names(context.Background(), busOf(args))
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "dbus_introspect",
|
||||
Description: "What one object on a bus offers: its interfaces with their methods (arguments in and out), " +
|
||||
"properties (type and access, never their values) and signals, and its child objects. A service " +
|
||||
"that is not running is not started for it.",
|
||||
Input: map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"bus": busArg,
|
||||
"service": map[string]any{"type": "string", "description": "the bus name, e.g. org.freedesktop.login1"},
|
||||
"path": map[string]any{"type": "string", "description": "the object path (default /)"},
|
||||
},
|
||||
"required": []string{"service"},
|
||||
},
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
service, _ := args["service"].(string)
|
||||
path, _ := args["path"].(string)
|
||||
return m.Introspect(context.Background(), busOf(args), service, path)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "dbus_monitor",
|
||||
Description: fmt.Sprintf("Watch a bus for a few seconds (at most %d) and answer the headers of the "+
|
||||
"messages that passed: type, sender, destination, path, interface, member. Never a message's body, "+
|
||||
"which carries secrets, notification text and the clipboard. Optional match rule and names to "+
|
||||
"narrow it. The system bus is watched as root through sudo -n.", MaxMonitorSeconds),
|
||||
Input: map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"bus": busArg,
|
||||
"seconds": map[string]any{"type": "integer", "description": fmt.Sprintf("how long (default 5, at most %d)", MaxMonitorSeconds)},
|
||||
"match": map[string]any{"type": "string", "description": "a D-Bus match rule, e.g. type='signal',interface='org.freedesktop.login1.Manager'"},
|
||||
"names": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "description": "only messages to or from these bus names"},
|
||||
},
|
||||
},
|
||||
Run: func(args map[string]any) (any, error) {
|
||||
s := 5
|
||||
if v, ok := args["seconds"].(float64); ok {
|
||||
s = int(v)
|
||||
}
|
||||
match, _ := args["match"].(string)
|
||||
var names []string
|
||||
if list, ok := args["names"].([]any); ok {
|
||||
for _, n := range list {
|
||||
if str, ok := n.(string); ok {
|
||||
names = append(names, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
return m.Monitor(context.Background(), busOf(args), s, match, names)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "dbus_check",
|
||||
Description: "Whether this machine's message bus is as the mesh expects: the bus's packages installed, " +
|
||||
"dbus-broker running behind dbus.service, whether the running bus is older than its installed " +
|
||||
"package (then a reboot is due: the bus is never restarted live), activatable services whose " +
|
||||
"unit does not exist, policy denials in the last hour, and the watcher's state.",
|
||||
Run: func(map[string]any) (any, error) { return m.Check(context.Background(), w), nil },
|
||||
},
|
||||
{
|
||||
Name: "dbus_health",
|
||||
Description: "The system bus's health now: a ping's round trip, how many connections it has, its unit, " +
|
||||
"pid and uptime, and what the watcher last saw.",
|
||||
Run: func(map[string]any) (any, error) { return m.Health(context.Background(), w), nil },
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user