Files
mesh-catalog/modules/dbus/cmd/dbus-tools/bus.go
T
jochen 7b5e1d3362 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.
2026-10-05 11:50:52 +02:00

104 lines
2.9 KiB
Go

package main
import (
"context"
"github.com/godbus/dbus/v5"
)
const (
busName = "org.freedesktop.DBus"
busPath = "/org/freedesktop/DBus"
)
// systemBus is the machine's system bus as the watcher uses it, over its own private connection: the
// bus driver's answers and its NameOwnerChanged signal, never another peer's messages.
type systemBus struct {
conn *dbus.Conn
out chan NameChange
}
// DialSystemBus connects to the system bus as this account and listens for names changing owner.
func DialSystemBus() (Bus, error) {
conn, err := dbus.SystemBusPrivate()
if err != nil {
return nil, err
}
if err := conn.Auth(nil); err != nil {
conn.Close()
return nil, err
}
if err := conn.Hello(); err != nil {
conn.Close()
return nil, err
}
if err := conn.AddMatchSignal(dbus.WithMatchSender(busName), dbus.WithMatchInterface(busName),
dbus.WithMatchMember("NameOwnerChanged")); err != nil {
conn.Close()
return nil, err
}
raw := make(chan *dbus.Signal, 256)
conn.Signal(raw)
b := &systemBus{conn: conn, out: make(chan NameChange, 256)}
go func() {
// The signal channel closes when the connection is lost; so does this one, which is how the
// watcher learns the bus went away.
defer close(b.out)
for {
select {
case s, open := <-raw:
if !open {
return
}
if s.Name != busName+".NameOwnerChanged" || len(s.Body) != 3 {
continue
}
name, _ := s.Body[0].(string)
old, _ := s.Body[1].(string)
nw, _ := s.Body[2].(string)
b.out <- NameChange{Name: name, Old: old, New: nw}
case <-conn.Context().Done():
return
}
}
}()
return b, nil
}
func (b *systemBus) driver() dbus.BusObject { return b.conn.Object(busName, busPath) }
// Ping asks the bus driver its id. Not org.freedesktop.DBus.Peer.Ping: dbus-broker's system policy
// refuses that to an account that is not root, and logs the refusal as a denial.
func (b *systemBus) Ping(ctx context.Context) error {
var id string
return b.driver().CallWithContext(ctx, busName+".GetId", 0).Store(&id)
}
func (b *systemBus) ID(ctx context.Context) (string, error) {
var id string
err := b.driver().CallWithContext(ctx, busName+".GetId", 0).Store(&id)
return id, err
}
func (b *systemBus) PID(ctx context.Context, name string) (uint32, error) {
var pid uint32
err := b.driver().CallWithContext(ctx, busName+".GetConnectionUnixProcessID", 0, name).Store(&pid)
return pid, err
}
func (b *systemBus) Names(ctx context.Context) ([]string, error) {
var names []string
err := b.driver().CallWithContext(ctx, busName+".ListNames", 0).Store(&names)
return names, err
}
func (b *systemBus) Activatable(ctx context.Context) ([]string, error) {
var names []string
err := b.driver().CallWithContext(ctx, busName+".ListActivatableNames", 0).Store(&names)
return names, err
}
func (b *systemBus) Changes() <-chan NameChange { return b.out }
func (b *systemBus) Close() { b.conn.Close() }