package main import ( "context" "encoding/json" "errors" "reflect" "strings" "testing" "time" ) // Recorded answers, trimmed, from a workstation of 2026-10-05. const busctlList = `[{"name":":1.0","pid":456,"process":"systemd-timesyn","user":"systemd-timesync","connection":":1.0","unit":"systemd-timesyncd.service","session":null,"description":null},` + `{"name":"fi.w1.wpa_supplicant1","pid":1442,"process":"wpa_supplicant","user":"root","connection":":1.23","unit":"wpa_supplicant.service","session":null,"description":null},` + `{"name":"org.blueman.Mechanism","pid":null,"process":null,"user":null,"connection":"(activatable)","unit":null,"session":null,"description":null},` + `{"name":":1.11","pid":975,"process":"polkitd","user":"polkitd","connection":":1.11","unit":"polkit.service","session":null,"description":null},` + `{"name":"org.freedesktop.DBus","pid":807,"process":"dbus-broker-lau","user":"root","connection":"org.freedesktop.DBus","unit":"dbus-broker.service","session":null,"description":null}]` func TestNamesAreSplitIntoRunningActivatableAndConnections(t *testing.T) { n, err := ParseNames("system", busctlList) if err != nil { t.Fatal(err) } if n.Connections != 2 || len(n.Running) != 1 || n.Running[0].Name != "fi.w1.wpa_supplicant1" || n.Running[0].Unit != "wpa_supplicant.service" || n.Running[0].PID != 1442 { t.Fatalf("%+v", n) } if !reflect.DeepEqual(n.ActivatableNotRunning, []string{"org.blueman.Mechanism"}) { t.Fatalf("%v", n.ActivatableNotRunning) } } const introspection = `{"type":"s","data":["\n\n \n \n \n \n \n \n \n \n \n \n \n \n\n"]}` func TestAnIntrospectionIsShapedWithoutValues(t *testing.T) { node, err := ParseIntrospection(introspection) if err != nil { t.Fatal(err) } o := Shape("system", "org.freedesktop.login1", "/org/freedesktop/login1", node) if len(o.Interfaces) != 1 || !reflect.DeepEqual(o.Children, []string{"session", "seat"}) { t.Fatalf("%+v", o) } i := o.Interfaces[0] if !reflect.DeepEqual(i.Methods[0], Member{Name: "Inhibit", In: []string{"what s"}, Out: []string{"pipe_fd h"}}) || !reflect.DeepEqual(i.Properties[0], Property{Name: "IdleHint", Type: "b", Access: "read"}) || !reflect.DeepEqual(i.Signals[0], Member{Name: "PrepareForSleep", Args: []string{"start b"}}) { t.Fatalf("%+v", i) } } func TestIntrospectRefusesWhatIsNotANameOrAPathAndNeverStartsAService(t *testing.T) { var asked []string m := testMachine(t, nil) m.Run = func(_ context.Context, name string, args ...string) (string, error) { asked = append([]string{name}, args...) return introspection, nil } if _, err := m.Introspect(context.Background(), "system", "--address=x", "/"); err == nil { t.Fatal("an option was taken for a bus name") } if _, err := m.Introspect(context.Background(), "system", "org.freedesktop.login1", "relative"); err == nil { t.Fatal("a relative path was taken") } if _, err := m.Introspect(context.Background(), "system", "org.freedesktop.login1", ""); err != nil { t.Fatal(err) } if !strings.Contains(strings.Join(asked, " "), "--auto-start=no") || asked[len(asked)-3] != "/" { t.Fatalf("%v", asked) } } const monitorLines = `{"type":"method_call","endian":"l","flags":0,"version":1,"cookie":186058,"timestamp-realtime":1791193058632202,"sender":":1.797","destination":"org.freedesktop.UPower","path":"/org/freedesktop/UPower","interface":"org.freedesktop.UPower","member":"GetDisplayDevice","payload":{"type":"","data":[]}} {"type":"signal","endian":"l","flags":1,"version":1,"cookie":7,"timestamp-realtime":1791193058632725,"sender":":1.40","path":"/org/freedesktop/Notifications","interface":"org.freedesktop.Notifications","member":"Notify","payload":{"type":"susssasa{sv}i","data":["app",0,"","the secret notification text","the clipboard's password",[],{},5000]}} not json at all {"type":"error","endian":"l","flags":1,"version":1,"cookie":9,"sender":":1.9","destination":":1.797","error_name":"org.freedesktop.DBus.Error.AccessDenied","payload":{"type":"s","data":["the reason, with a token"]}}` func TestAMonitoredMessageIsItsHeaderOnly(t *testing.T) { var asked []string m := testMachine(t, nil) m.Stream = func(ctx context.Context, line func(string) bool, name string, args ...string) error { asked = append([]string{name}, args...) if _, ok := ctx.Deadline(); !ok { t.Error("the watch has no deadline") } for _, l := range strings.Split(monitorLines, "\n") { if !line(l) { break } } return nil } w, err := m.Monitor(context.Background(), "system", 3, "type='signal'", []string{"org.freedesktop.Notifications"}) if err != nil { t.Fatal(err) } raw, _ := json.Marshal(w) for _, secret := range []string{"secret notification", "password", "token", "payload", "data"} { if strings.Contains(string(raw), secret) { t.Errorf("the answer carries %q: %s", secret, raw) } } if w.Total != 3 || w.Messages[1].Member != "Notify" || w.Messages[2].ErrorName != "org.freedesktop.DBus.Error.AccessDenied" { t.Fatalf("%+v", w) } cmd := strings.Join(asked, " ") if !strings.HasPrefix(cmd, "sudo -n timeout 3s busctl --system") || !strings.Contains(cmd, "--match=type='signal'") || !strings.HasSuffix(cmd, "org.freedesktop.Notifications") { t.Fatalf("%s", cmd) } } func TestAMonitorIsBoundedAndTheSessionBusIsTheAccounts(t *testing.T) { m := testMachine(t, map[string]string{"/run/user/1000/bus": ""}) var asked string m.Stream = func(_ context.Context, _ func(string) bool, name string, args ...string) error { asked = name + " " + strings.Join(args, " ") return nil } for _, s := range []int{0, MaxMonitorSeconds + 1} { if _, err := m.Monitor(context.Background(), "system", s, "", nil); err == nil { t.Errorf("%d seconds were accepted", s) } } if _, err := m.Monitor(context.Background(), "session", 2, "", []string{"-x"}); err == nil { t.Error("an option was taken for a name") } if _, err := m.Monitor(context.Background(), "session", 2, "", nil); err != nil { t.Fatal(err) } if strings.HasPrefix(asked, "sudo") || !strings.Contains(asked, "--address=unix:path=/run/user/1000/bus") { t.Fatalf("%s", asked) } } func TestASessionBusIsFoundOrSaidMissing(t *testing.T) { m := testMachine(t, nil) if _, err := m.SessionAddress(); !errors.Is(err, ErrNoSession) { t.Fatalf("a server without a session said %v", err) } m = testMachine(t, map[string]string{"/run/user/1000/bus": ""}) if a, err := m.SessionAddress(); err != nil || a != "unix:path=/run/user/1000/bus" { t.Fatalf("%q %v", a, err) } m.Env = func(k string) string { if k == "DBUS_SESSION_BUS_ADDRESS" { return "unix:path=/run/user/1000/bus,guid=abc" } return "" } if a, _ := m.SessionAddress(); a != "unix:path=/run/user/1000/bus,guid=abc" { t.Fatalf("%q", a) } } const journalLines = `{"__CURSOR":"s=1;i=1","__REALTIME_TIMESTAMP":"1791192960341045","MESSAGE":"Ready","_PID":"807"} {"__CURSOR":"s=1;i=2","__REALTIME_TIMESTAMP":"1791192960341045","MESSAGE":"A security policy denied :1.1199 to send method call /org/freedesktop/DBus:org.freedesktop.DBus.Debug.Stats.GetStats to org.freedesktop.DBus.","DBUS_BROKER_TRANSMIT_ACTION":"send","DBUS_BROKER_MESSAGE_TYPE":"method_call","DBUS_BROKER_SENDER_UNIQUE_NAME":":1.1199","DBUS_BROKER_MESSAGE_DESTINATION":"org.freedesktop.DBus","DBUS_BROKER_MESSAGE_PATH":"/org/freedesktop/DBus","DBUS_BROKER_MESSAGE_INTERFACE":"org.freedesktop.DBus.Debug.Stats","DBUS_BROKER_MESSAGE_MEMBER":"GetStats","DBUS_BROKER_POLICY_TYPE":"internal"} {"__CURSOR":"s=1;i=3","MESSAGE":[65,66]}` func TestDenialsAreReadFromTheJournalByTheirFields(t *testing.T) { d, cursor := ParseDenials(journalLines) if cursor != "s=1;i=3" || len(d) != 1 { t.Fatalf("%q %+v", cursor, d) } want := Denial{At: "2026-10-05T09:36:00Z", Action: "send", Type: "method_call", Sender: ":1.1199", Destination: "org.freedesktop.DBus", Path: "/org/freedesktop/DBus", Interface: "org.freedesktop.DBus.Debug.Stats", Member: "GetStats", Policy: "internal"} if d[0] != want { t.Fatalf("%+v", d[0]) } } func TestTheJournalIsReadFromTheCursorOn(t *testing.T) { m := testMachine(t, nil) var asked []string m.Run = func(_ context.Context, name string, args ...string) (string, error) { asked = append([]string{name}, args...) return journalLines, nil } _, next, _ := m.Denials(context.Background(), "", time.Unix(100, 0)) if !strings.Contains(strings.Join(asked, " "), "--since @100") { t.Fatalf("%v", asked) } m.Denials(context.Background(), next, time.Time{}) if !strings.Contains(strings.Join(asked, " "), "--after-cursor s=1;i=3") || !strings.Contains(strings.Join(asked, " "), "-u dbus-broker.service") { t.Fatalf("%v", asked) } } func TestAUnitIsReadFromACgroup(t *testing.T) { for in, want := range map[string]string{ "0::/system.slice/bluetooth.service\n": "bluetooth.service", "0::/user.slice/user-1000.slice/user@1000.service/app.slice/dunst.service": "dunst.service", "0::/user.slice/user-1000.slice/session-2.scope": "session-2.scope", "0::/init.scope": "init.scope", "": "", } { if got := UnitFromCgroup(in); got != want { t.Errorf("%q: %q, want %q", in, got, want) } } } func TestShowBlocksAreReadInOrder(t *testing.T) { b := ParseShow("Id=dbus-org.freedesktop.resolve1.service\nLoadState=not-found\n\nId=systemd-hostnamed.service\nLoadState=loaded\n") if len(b) != 2 || b[0]["LoadState"] != "not-found" || b[1]["Id"] != "systemd-hostnamed.service" { t.Fatalf("%v", b) } } func TestARunningBusOlderThanItsPackageIsSaid(t *testing.T) { p := ParseDesc("%NAME%\ndbus-broker\n\n%VERSION%\n37-3\n\n%INSTALLDATE%\n1772097880\n") if p.Name != "dbus-broker" || p.Version != "37-3" || p.Installed.Unix() != 1772097880 { t.Fatalf("%+v", p) } if due, _ := RebootDue(time.Unix(1791123478, 0), []Package{p}); due { t.Fatal("a bus started after its package was said to be older") } due, detail := RebootDue(time.Unix(1772000000, 0), []Package{p}) if !due || !strings.Contains(detail, "reboot is due") || !strings.Contains(detail, "dbus-broker 37-3") { t.Fatalf("%v %s", due, detail) } } func TestConnectionsAreCountedFromStatsOrNames(t *testing.T) { stats := `{"type":"a{sv}","data":[{"org.bus1.DBus.Debug.Stats.PeerAccounting":{"type":"a(sa{sv}a{su})","data":[[":1.0",{},{}],[":1.1",{},{}],[":1.7",{},{}]]}}]}` if n, ok := CountConnections(stats); !ok || n != 3 { t.Fatalf("%d %v", n, ok) } if n, ok := CountConnections(`{"type":"a{sv}","data":[{"ActiveConnections":{"type":"u","data":12}}]}`); !ok || n != 12 { t.Fatalf("%d %v", n, ok) } if n, ok := CountUniqueNames(`{"type":"as","data":[["org.freedesktop.DBus",":1.0","org.bluez",":1.5"]]}`); !ok || n != 2 { t.Fatalf("%d %v", n, ok) } } func TestStatsAreNeverAskedAsTheAccount(t *testing.T) { m := testMachine(t, nil) var calls []string m.Run = func(_ context.Context, name string, args ...string) (string, error) { line := name + " " + strings.Join(args, " ") calls = append(calls, line) if name == "sudo" { return "", errors.New("sudo: a password is required") } if strings.Contains(line, "ListNames") { return `{"type":"as","data":[[":1.0",":1.1"]]}`, nil } return "Id=dbus-broker.service\nActiveState=active\nMainPID=807\nActiveEnterTimestamp=@1791123478\n", nil } h := m.Health(context.Background(), nil) for _, c := range calls { if strings.Contains(c, "GetStats") && !strings.HasPrefix(c, "sudo -n ") { t.Fatalf("Debug.Stats asked as the account, which the bus logs as a denial: %s", c) } } if h["connections"] != 2 || h["unit"] != "dbus-broker.service" { t.Fatalf("%v", h) } } func TestTheCheckReadsPackagesUnitsServiceFilesAndDenials(t *testing.T) { m := testMachine(t, map[string]string{ "/var/lib/pacman/local/dbus-1.16.2-1/desc": "%NAME%\ndbus\n\n%VERSION%\n1.16.2-1\n\n%INSTALLDATE%\n1741340000\n", "/var/lib/pacman/local/dbus-broker-37-3/desc": "%NAME%\ndbus-broker\n\n%VERSION%\n37-3\n\n%INSTALLDATE%\n1800000000\n", "/var/lib/pacman/local/dbus-broker-units-37-3/desc": "%NAME%\ndbus-broker-units\n\n%VERSION%\n37-3\n\n%INSTALLDATE%\n1772097880\n", "/usr/share/dbus-1/system-services/org.bluez.service": "[D-BUS Service]\nName=org.bluez\nExec=/bin/false\nUser=root\nSystemdService=dbus-org.bluez.service\n", "/usr/share/dbus-1/system-services/org.example.service": "[D-BUS Service]\nName=org.example\nSystemdService=example.service\n", "/usr/share/dbus-1/system-services/org.freedesktop.systemd1.service": "[D-BUS Service]\nName=org.freedesktop.systemd1\nExec=/bin/false\n", }) m.Run = func(_ context.Context, name string, args ...string) (string, error) { line := strings.Join(args, " ") switch { case name == "journalctl": return journalLines, nil case strings.Contains(line, "Id,LoadState"): return "Id=dbus-org.bluez.service\nLoadState=not-found\n\nId=example.service\nLoadState=not-found\n", nil default: return "Id=dbus-broker.service\nActiveState=active\nMainPID=807\nActiveEnterTimestamp=@1791123478\n", nil } } got := m.Check(context.Background(), nil) byName := map[string]Check{} for _, c := range got["checks"].([]Check) { byName[c.Name] = c } if !byName["package dbus-broker-units"].OK || !byName["system bus"].OK { t.Fatalf("%+v", got) } if c := byName["running bus is the installed one"]; c.OK || !strings.Contains(c.Detail, "dbus-broker 37-3") { t.Fatalf("an upgraded broker was not said: %+v", c) } if c := byName["activatable services"]; c.OK || !strings.Contains(c.Detail, "org.example → example.service") || strings.Contains(c.Detail, "bluez") { t.Fatalf("%+v", c) } if c := byName["policy denials in the last hour"]; c.OK || !strings.Contains(c.Detail, "GetStats") { t.Fatalf("%+v", c) } if !strings.Contains(strings.Join(got["notes"].([]string), " "), "org.bluez → dbus-org.bluez.service") { t.Fatalf("%v", got["notes"]) } }