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,103 @@
|
||||
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() }
|
||||
Reference in New Issue
Block a user