diff --git a/cmd/hakurei/command.go b/cmd/hakurei/command.go index 4575552..0c2cb53 100644 --- a/cmd/hakurei/command.go +++ b/cmd/hakurei/command.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/json" "errors" "fmt" "io" @@ -213,19 +214,19 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE if flagDBusConfigSession == "builtin" { config.SessionBus = dbus.NewConfig(flagID, true, flagDBusMpris) } else { - if conf, err := dbus.NewConfigFromFile(flagDBusConfigSession); err != nil { + if f, err := os.Open(flagDBusConfigSession); err != nil { + log.Fatal(err.Error()) + } else if err = json.NewDecoder(f).Decode(&config.SessionBus); err != nil { log.Fatalf("cannot load session bus proxy config from %q: %s", flagDBusConfigSession, err) - } else { - config.SessionBus = conf } } // system bus proxy is optional if flagDBusConfigSystem != "nil" { - if conf, err := dbus.NewConfigFromFile(flagDBusConfigSystem); err != nil { + if f, err := os.Open(flagDBusConfigSystem); err != nil { + log.Fatal(err.Error()) + } else if err = json.NewDecoder(f).Decode(&config.SystemBus); err != nil { log.Fatalf("cannot load system bus proxy config from %q: %s", flagDBusConfigSystem, err) - } else { - config.SystemBus = conf } } diff --git a/cmd/hakurei/print.go b/cmd/hakurei/print.go index c9084a5..1a97b85 100644 --- a/cmd/hakurei/print.go +++ b/cmd/hakurei/print.go @@ -15,7 +15,6 @@ import ( "hakurei.app/hst" "hakurei.app/internal/app" "hakurei.app/internal/app/state" - "hakurei.app/system/dbus" ) func printShowSystem(output io.Writer, short, flagJSON bool) { @@ -140,7 +139,7 @@ func printShowInstance( } } - printDBus := func(c *dbus.Config) { + printDBus := func(c *hst.BusConfig) { t.Printf(" Filter:\t%v\n", c.Filter) if len(c.See) > 0 { t.Printf(" See:\t%q\n", c.See) diff --git a/cmd/hakurei/print_test.go b/cmd/hakurei/print_test.go index a666d25..dcdc0d0 100644 --- a/cmd/hakurei/print_test.go +++ b/cmd/hakurei/print_test.go @@ -7,7 +7,6 @@ import ( "hakurei.app/hst" "hakurei.app/internal/app/state" - "hakurei.app/system/dbus" ) var ( @@ -101,7 +100,7 @@ Filesystem Extra ACL `, false}, - {"config pd dbus see", nil, &hst.Config{SessionBus: &dbus.Config{See: []string{"org.example.test"}}}, false, false, `Error: configuration missing container state! + {"config pd dbus see", nil, &hst.Config{SessionBus: &hst.BusConfig{See: []string{"org.example.test"}}}, false, false, `Error: configuration missing container state! App Identity: 0 diff --git a/cmd/hpkg/app.go b/cmd/hpkg/app.go index a4910aa..da1fd30 100644 --- a/cmd/hpkg/app.go +++ b/cmd/hpkg/app.go @@ -7,7 +7,6 @@ import ( "hakurei.app/container" "hakurei.app/hst" - "hakurei.app/system/dbus" ) type appInfo struct { @@ -37,9 +36,9 @@ type appInfo struct { // passed through to [hst.Config] DirectWayland bool `json:"direct_wayland,omitempty"` // passed through to [hst.Config] - SystemBus *dbus.Config `json:"system_bus,omitempty"` + SystemBus *hst.BusConfig `json:"system_bus,omitempty"` // passed through to [hst.Config] - SessionBus *dbus.Config `json:"session_bus,omitempty"` + SessionBus *hst.BusConfig `json:"session_bus,omitempty"` // passed through to [hst.Config] Enablements *hst.Enablements `json:"enablements,omitempty"` diff --git a/hst/config.go b/hst/config.go index 4629d3d..e661d2d 100644 --- a/hst/config.go +++ b/hst/config.go @@ -6,7 +6,6 @@ import ( "time" "hakurei.app/container" - "hakurei.app/system/dbus" ) const Tmp = "/.hakurei" @@ -42,10 +41,10 @@ type ( // Session D-Bus proxy configuration. // If set to nil, session bus proxy assume built-in defaults. - SessionBus *dbus.Config `json:"session_bus,omitempty"` + SessionBus *BusConfig `json:"session_bus,omitempty"` // System D-Bus proxy configuration. // If set to nil, system bus proxy is disabled. - SystemBus *dbus.Config `json:"system_bus,omitempty"` + SystemBus *BusConfig `json:"system_bus,omitempty"` // Direct access to wayland socket, no attempt is made to attach security-context-v1 // and the bare socket is made available to the container. DirectWayland bool `json:"direct_wayland,omitempty"` diff --git a/hst/dbus.go b/hst/dbus.go new file mode 100644 index 0000000..7bad231 --- /dev/null +++ b/hst/dbus.go @@ -0,0 +1,19 @@ +package hst + +// BusConfig configures the xdg-dbus-proxy process. +type BusConfig struct { + // See set 'see' policy for NAME (--see=NAME) + See []string `json:"see"` + // Talk set 'talk' policy for NAME (--talk=NAME) + Talk []string `json:"talk"` + // Own set 'own' policy for NAME (--own=NAME) + Own []string `json:"own"` + + // Call set RULE for calls on NAME (--call=NAME=RULE) + Call map[string]string `json:"call"` + // Broadcast set RULE for broadcasts from NAME (--broadcast=NAME=RULE) + Broadcast map[string]string `json:"broadcast"` + + Log bool `json:"log,omitempty"` + Filter bool `json:"filter"` +} diff --git a/hst/hst.go b/hst/hst.go index 14c1845..3e4c7de 100644 --- a/hst/hst.go +++ b/hst/hst.go @@ -7,7 +7,6 @@ import ( "os" "hakurei.app/container" - "hakurei.app/system/dbus" ) // An AppError is returned while starting an app according to [hst.Config]. @@ -62,7 +61,7 @@ func Template() *Config { Enablements: NewEnablements(EWayland | EDBus | EPulse), - SessionBus: &dbus.Config{ + SessionBus: &BusConfig{ See: nil, Talk: []string{"org.freedesktop.Notifications", "org.freedesktop.FileManager1", "org.freedesktop.ScreenSaver", "org.freedesktop.secrets", "org.kde.kwalletd5", "org.kde.kwalletd6", "org.gnome.SessionManager"}, @@ -73,7 +72,7 @@ func Template() *Config { Log: false, Filter: true, }, - SystemBus: &dbus.Config{ + SystemBus: &BusConfig{ See: nil, Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"}, Own: nil, diff --git a/internal/app/app_test.go b/internal/app/app_test.go index bcca558..d966b6c 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -20,7 +20,6 @@ import ( "hakurei.app/internal/app/state" "hakurei.app/system" "hakurei.app/system/acl" - "hakurei.app/system/dbus" ) func TestApp(t *testing.T) { @@ -122,7 +121,7 @@ func TestApp(t *testing.T) { ID: "org.chromium.Chromium", Identity: 9, Groups: []string{"video"}, - SessionBus: &dbus.Config{ + SessionBus: &hst.BusConfig{ Talk: []string{ "org.freedesktop.Notifications", "org.freedesktop.FileManager1", @@ -145,7 +144,7 @@ func TestApp(t *testing.T) { }, Filter: true, }, - SystemBus: &dbus.Config{ + SystemBus: &hst.BusConfig{ Talk: []string{ "org.bluez", "org.freedesktop.Avahi", @@ -208,7 +207,7 @@ func TestApp(t *testing.T) { Ensure(m("/run/user/1971"), 0700).UpdatePermType(system.User, m("/run/user/1971"), acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset Ephemeral(system.Process, m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c"), 0700).UpdatePermType(system.Process, m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c"), acl.Execute). Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c/pulse")). - MustProxyDBus(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), &dbus.Config{ + MustProxyDBus(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), &hst.BusConfig{ Talk: []string{ "org.freedesktop.Notifications", "org.freedesktop.FileManager1", @@ -230,7 +229,7 @@ func TestApp(t *testing.T) { "org.freedesktop.portal.*": "@/org/freedesktop/portal/*", }, Filter: true, - }, m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), &dbus.Config{ + }, m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), &hst.BusConfig{ Talk: []string{ "org.bluez", "org.freedesktop.Avahi", @@ -319,11 +318,11 @@ func TestApp(t *testing.T) { Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"), }, - SystemBus: &dbus.Config{ + SystemBus: &hst.BusConfig{ Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"}, Filter: true, }, - SessionBus: &dbus.Config{ + SessionBus: &hst.BusConfig{ Talk: []string{ "org.freedesktop.FileManager1", "org.freedesktop.Notifications", "org.freedesktop.ScreenSaver", "org.freedesktop.secrets", @@ -359,7 +358,7 @@ func TestApp(t *testing.T) { Ephemeral(system.Process, m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1"), 0700).UpdatePermType(system.Process, m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1"), acl.Execute). Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse")). Ephemeral(system.Process, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1"), 0711). - MustProxyDBus(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), &dbus.Config{ + MustProxyDBus(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), &hst.BusConfig{ Talk: []string{ "org.freedesktop.FileManager1", "org.freedesktop.Notifications", "org.freedesktop.ScreenSaver", "org.freedesktop.secrets", @@ -372,7 +371,7 @@ func TestApp(t *testing.T) { }, Call: map[string]string{}, Broadcast: map[string]string{}, Filter: true, - }, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), &dbus.Config{ + }, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), &hst.BusConfig{ Talk: []string{ "org.bluez", "org.freedesktop.Avahi", diff --git a/system/dbus.go b/system/dbus.go index 81236b4..1a0b099 100644 --- a/system/dbus.go +++ b/system/dbus.go @@ -21,7 +21,7 @@ var ( ) // MustProxyDBus calls ProxyDBus and panics if an error is returned. -func (sys *I) MustProxyDBus(sessionPath *container.Absolute, session *dbus.Config, systemPath *container.Absolute, system *dbus.Config) *I { +func (sys *I) MustProxyDBus(sessionPath *container.Absolute, session *hst.BusConfig, systemPath *container.Absolute, system *hst.BusConfig) *I { if err := sys.ProxyDBus(session, system, sessionPath, systemPath); err != nil { panic(err.Error()) } else { @@ -31,7 +31,7 @@ func (sys *I) MustProxyDBus(sessionPath *container.Absolute, session *dbus.Confi // ProxyDBus finalises configuration ahead of time and starts xdg-dbus-proxy via [dbus] and terminates it on revert. // This [Op] is always [Process] scoped. -func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath *container.Absolute) error { +func (sys *I) ProxyDBus(session, system *hst.BusConfig, sessionPath, systemPath *container.Absolute) error { d := new(dbusProxyOp) // session bus is required as otherwise this is effectively a very expensive noop @@ -56,9 +56,9 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath *c fmt.Sprintf("cannot finalise message bus proxy: %v", err), false) } else { if sys.msg.IsVerbose() { - sys.msg.Verbose("session bus proxy:", session.Args(sessionBus)) + sys.msg.Verbose("session bus proxy:", dbus.Args(session, sessionBus)) if system != nil { - sys.msg.Verbose("system bus proxy:", system.Args(systemBus)) + sys.msg.Verbose("system bus proxy:", dbus.Args(system, systemBus)) } // this calls the argsWt String method diff --git a/system/dbus/config.go b/system/dbus/config.go index 3f66b2b..2aeb3bb 100644 --- a/system/dbus/config.go +++ b/system/dbus/config.go @@ -1,64 +1,50 @@ package dbus import ( - "encoding/json" - "errors" - "io" - "os" "strings" + + "hakurei.app/hst" ) // ProxyPair is an upstream dbus address and a downstream socket path. type ProxyPair [2]string -type Config struct { - // See set 'see' policy for NAME (--see=NAME) - See []string `json:"see"` - // Talk set 'talk' policy for NAME (--talk=NAME) - Talk []string `json:"talk"` - // Own set 'own' policy for NAME (--own=NAME) - Own []string `json:"own"` - - // Call set RULE for calls on NAME (--call=NAME=RULE) - Call map[string]string `json:"call"` - // Broadcast set RULE for broadcasts from NAME (--broadcast=NAME=RULE) - Broadcast map[string]string `json:"broadcast"` - - Log bool `json:"log,omitempty"` - Filter bool `json:"filter"` -} - -func (c *Config) interfaces(yield func(string) bool) { - for _, iface := range c.See { - if !yield(iface) { - return +// interfacesAll returns an iterator over all interfaces specified in c. +func interfacesAll(c *hst.BusConfig) func(yield func(string) bool) { + return func(yield func(string) bool) { + for _, iface := range c.See { + if !yield(iface) { + return + } } - } - for _, iface := range c.Talk { - if !yield(iface) { - return + for _, iface := range c.Talk { + if !yield(iface) { + return + } } - } - for _, iface := range c.Own { - if !yield(iface) { - return + for _, iface := range c.Own { + if !yield(iface) { + return + } } - } - for iface := range c.Call { - if !yield(iface) { - return + for iface := range c.Call { + if !yield(iface) { + return + } } - } - for iface := range c.Broadcast { - if !yield(iface) { - return + for iface := range c.Broadcast { + if !yield(iface) { + return + } } } } -func (c *Config) checkInterfaces(segment string) error { - for iface := range c.interfaces { +// checkInterfaces checks [hst.BusConfig] for invalid interfaces based on an undocumented check in xdg-dbus-error, +// returning [BadInterfaceError] if one is encountered. +func checkInterfaces(c *hst.BusConfig, segment string) error { + for iface := range interfacesAll(c) { /* xdg-dbus-proxy fails without output when this condition is not met: char *dot = strrchr (filter->interface, '.'); @@ -83,7 +69,8 @@ func (c *Config) checkInterfaces(segment string) error { return nil } -func (c *Config) Args(bus ProxyPair) (args []string) { +// Args returns the xdg-dbus-proxy arguments equivalent of [hst.BusConfig]. +func Args(c *hst.BusConfig, bus ProxyPair) (args []string) { argc := 2 + len(c.See) + len(c.Talk) + len(c.Own) + len(c.Call) + len(c.Broadcast) if c.Log { argc++ @@ -119,25 +106,9 @@ func (c *Config) Args(bus ProxyPair) (args []string) { return } -func (c *Config) Load(r io.Reader) error { return json.NewDecoder(r).Decode(&c) } - -// NewConfigFromFile opens the target config file at path and parses its contents into *Config. -func NewConfigFromFile(path string) (*Config, error) { - if f, err := os.Open(path); err != nil { - return nil, err - } else { - c := new(Config) - err1 := c.Load(f) - err = f.Close() - - return c, errors.Join(err1, err) - } -} - -// NewConfig returns a reference to a Config struct with optional defaults. -// If id is an empty string own defaults are omitted. -func NewConfig(id string, defaults, mpris bool) (c *Config) { - c = &Config{ +// NewConfig returns the address of a new [hst.BusConfig] with optional defaults. +func NewConfig(id string, defaults, mpris bool) *hst.BusConfig { + c := hst.BusConfig{ Call: make(map[string]string), Broadcast: make(map[string]string), @@ -158,5 +129,5 @@ func NewConfig(id string, defaults, mpris bool) (c *Config) { } } - return + return &c } diff --git a/system/dbus/config_test.go b/system/dbus/config_test.go index 0fe9330..9a0e570 100644 --- a/system/dbus/config_test.go +++ b/system/dbus/config_test.go @@ -1,26 +1,24 @@ package dbus_test import ( - "errors" - "os" - "path" "reflect" "slices" "strings" "testing" + "hakurei.app/hst" "hakurei.app/system/dbus" ) func TestConfig_Args(t *testing.T) { - for _, tc := range makeTestCases() { + for _, tc := range testCasesExt { if tc.wantErr { // args does not check for nulls continue } t.Run("build arguments for "+tc.id, func(t *testing.T) { - if got := tc.c.Args(tc.bus); !slices.Equal(got, tc.want) { + if got := dbus.Args(tc.c, tc.bus); !slices.Equal(got, tc.want) { t.Errorf("Args(%q) = %v, want %v", tc.bus, got, tc.want) @@ -29,74 +27,36 @@ func TestConfig_Args(t *testing.T) { } } -func TestNewConfigFromFile(t *testing.T) { - for _, tc := range makeTestCases() { - name := new(strings.Builder) - name.WriteString("parse configuration file for application ") - name.WriteString(tc.id) - if tc.wantErr { - name.WriteString(" with unexpected results") - } - - samplePath := path.Join("testdata", tc.id+".json") - - t.Run(name.String(), func(t *testing.T) { - got, err := dbus.NewConfigFromFile(samplePath) - if errors.Is(err, os.ErrNotExist) != tc.wantErrF { - t.Errorf("NewConfigFromFile(%q) error = %v, wantErrF %v", - samplePath, - err, tc.wantErrF) - return - } - - if tc.wantErrF { - return - } - - if !tc.wantErr && !reflect.DeepEqual(got, tc.c) { - t.Errorf("NewConfigFromFile(%q) got = %v, want %v", - samplePath, - got, tc.c) - } - if tc.wantErr && reflect.DeepEqual(got, tc.c) { - t.Errorf("NewConfigFromFile(%q) got = %v, wantErr %v", - samplePath, - got, tc.wantErr) - } - }) - } -} - func TestNewConfig(t *testing.T) { ids := [...]string{"org.chromium.Chromium", "dev.vencord.Vesktop"} type newTestCase struct { id string args [2]bool - want *dbus.Config + want *hst.BusConfig } // populate tests from IDs in generic tests tcs := make([]newTestCase, 0, (len(ids)+1)*4) // tests for defaults without id tcs = append(tcs, - newTestCase{"", [2]bool{false, false}, &dbus.Config{ + newTestCase{"", [2]bool{false, false}, &hst.BusConfig{ Call: make(map[string]string), Broadcast: make(map[string]string), Filter: true, }}, - newTestCase{"", [2]bool{false, true}, &dbus.Config{ + newTestCase{"", [2]bool{false, true}, &hst.BusConfig{ Call: make(map[string]string), Broadcast: make(map[string]string), Filter: true, }}, - newTestCase{"", [2]bool{true, false}, &dbus.Config{ + newTestCase{"", [2]bool{true, false}, &hst.BusConfig{ Talk: []string{"org.freedesktop.DBus", "org.freedesktop.Notifications"}, Call: map[string]string{"org.freedesktop.portal.*": "*"}, Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"}, Filter: true, }}, - newTestCase{"", [2]bool{true, true}, &dbus.Config{ + newTestCase{"", [2]bool{true, true}, &hst.BusConfig{ Talk: []string{"org.freedesktop.DBus", "org.freedesktop.Notifications"}, Call: map[string]string{"org.freedesktop.portal.*": "*"}, Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"}, @@ -105,24 +65,24 @@ func TestNewConfig(t *testing.T) { ) for _, id := range ids { tcs = append(tcs, - newTestCase{id, [2]bool{false, false}, &dbus.Config{ + newTestCase{id, [2]bool{false, false}, &hst.BusConfig{ Call: make(map[string]string), Broadcast: make(map[string]string), Filter: true, }}, - newTestCase{id, [2]bool{false, true}, &dbus.Config{ + newTestCase{id, [2]bool{false, true}, &hst.BusConfig{ Call: make(map[string]string), Broadcast: make(map[string]string), Filter: true, }}, - newTestCase{id, [2]bool{true, false}, &dbus.Config{ + newTestCase{id, [2]bool{true, false}, &hst.BusConfig{ Talk: []string{"org.freedesktop.DBus", "org.freedesktop.Notifications"}, Own: []string{id + ".*"}, Call: map[string]string{"org.freedesktop.portal.*": "*"}, Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"}, Filter: true, }}, - newTestCase{id, [2]bool{true, true}, &dbus.Config{ + newTestCase{id, [2]bool{true, true}, &hst.BusConfig{ Talk: []string{"org.freedesktop.DBus", "org.freedesktop.Notifications"}, Own: []string{id + ".*", "org.mpris.MediaPlayer2." + id + ".*"}, Call: map[string]string{"org.freedesktop.portal.*": "*"}, diff --git a/system/dbus/dbus_test.go b/system/dbus/dbus_test.go index 2599ef8..978b55e 100644 --- a/system/dbus/dbus_test.go +++ b/system/dbus/dbus_test.go @@ -22,7 +22,7 @@ func TestFinalise(t *testing.T) { err, syscall.EBADE) } - for id, tc := range testCasePairs() { + for id, tc := range testCasePairs { t.Run("create final for "+id, func(t *testing.T) { var wt io.WriterTo if v, err := dbus.Finalise(tc[0].bus, tc[1].bus, tc[0].c, tc[1].c); (errors.Is(err, syscall.EINVAL)) != tc[0].wantErr { @@ -105,7 +105,7 @@ func testProxyFinaliseStartWaitCloseString(t *testing.T, useSandbox bool) { } }) - for id, tc := range testCasePairs() { + for id, tc := range testCasePairs { // this test does not test errors if tc[0].wantErr { continue diff --git a/system/dbus/proxy.go b/system/dbus/proxy.go index a83bc9c..59a1d7d 100644 --- a/system/dbus/proxy.go +++ b/system/dbus/proxy.go @@ -9,6 +9,7 @@ import ( "hakurei.app/container" "hakurei.app/helper" + "hakurei.app/hst" ) // ProxyName is the file name or path to the proxy program. @@ -66,23 +67,23 @@ type Final struct { } // Finalise creates a checked argument writer for [Proxy]. -func Finalise(sessionBus, systemBus ProxyPair, session, system *Config) (final *Final, err error) { +func Finalise(sessionBus, systemBus ProxyPair, session, system *hst.BusConfig) (final *Final, err error) { if session == nil && system == nil { return nil, syscall.EBADE } var args []string if session != nil { - if err = session.checkInterfaces("session"); err != nil { + if err = checkInterfaces(session, "session"); err != nil { return } - args = append(args, session.Args(sessionBus)...) + args = append(args, Args(session, sessionBus)...) } if system != nil { - if err = system.checkInterfaces("system"); err != nil { + if err = checkInterfaces(system, "system"); err != nil { return } - args = append(args, system.Args(systemBus)...) + args = append(args, Args(system, systemBus)...) } final = &Final{Session: sessionBus, System: systemBus} diff --git a/system/dbus/samples_test.go b/system/dbus/samples_test.go index 43dda76..7db3d6b 100644 --- a/system/dbus/samples_test.go +++ b/system/dbus/samples_test.go @@ -1,9 +1,7 @@ package dbus_test import ( - "sync" - - "hakurei.app/system/dbus" + "hakurei.app/hst" ) const ( @@ -14,7 +12,7 @@ const ( var samples = []dbusTestCase{ { - "org.chromium.Chromium", &dbus.Config{ + "org.chromium.Chromium", &hst.BusConfig{ See: nil, Talk: []string{"org.freedesktop.Notifications", "org.freedesktop.FileManager1", "org.freedesktop.ScreenSaver", "org.freedesktop.secrets", "org.kde.kwalletd5", "org.kde.kwalletd6", "org.gnome.SessionManager"}, @@ -45,7 +43,7 @@ var samples = []dbusTestCase{ }, }, { - "org.chromium.Chromium+", &dbus.Config{ + "org.chromium.Chromium+", &hst.BusConfig{ See: nil, Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"}, Own: nil, @@ -66,7 +64,7 @@ var samples = []dbusTestCase{ }, { - "dev.vencord.Vesktop", &dbus.Config{ + "dev.vencord.Vesktop", &hst.BusConfig{ See: nil, Talk: []string{"org.freedesktop.Notifications", "org.kde.StatusNotifierWatcher"}, Own: []string{"dev.vencord.Vesktop.*", "org.mpris.MediaPlayer2.dev.vencord.Vesktop.*"}, @@ -89,7 +87,7 @@ var samples = []dbusTestCase{ }, { - "uk.gensokyo.CrashTestDummy", &dbus.Config{ + "uk.gensokyo.CrashTestDummy", &hst.BusConfig{ See: []string{"uk.gensokyo.CrashTestDummy1"}, Talk: []string{"org.freedesktop.Notifications"}, Own: []string{"uk.gensokyo.CrashTestDummy.*", "org.mpris.MediaPlayer2.uk.gensokyo.CrashTestDummy.*"}, @@ -112,7 +110,7 @@ var samples = []dbusTestCase{ "--log"}, }, { - "uk.gensokyo.CrashTestDummy1", &dbus.Config{ + "uk.gensokyo.CrashTestDummy1", &hst.BusConfig{ See: []string{"uk.gensokyo.CrashTestDummy"}, Talk: []string{"org.freedesktop.Notifications"}, Own: []string{"uk.gensokyo.CrashTestDummy1.*", "org.mpris.MediaPlayer2.uk.gensokyo.CrashTestDummy1.*"}, @@ -138,7 +136,7 @@ var samples = []dbusTestCase{ type dbusTestCase struct { id string - c *dbus.Config + c *hst.BusConfig wantErr bool wantErrF bool bus [2]string @@ -146,83 +144,71 @@ type dbusTestCase struct { } var ( - testCasesV []dbusTestCase - testCasePairsV map[string][2]dbusTestCase + testCasesExt = func() []dbusTestCase { + testCases := make([]dbusTestCase, len(samples)*2) + for i := range samples { + testCases[i] = samples[i] - testCaseOnce sync.Once -) + fi := &testCases[len(samples)+i] + *fi = samples[i] -func makeTestCases() []dbusTestCase { - testCaseOnce.Do(testCaseGenerate) - return testCasesV -} + // create null-injected test cases + fi.wantErr = true + injectNulls := func(t *[]string) { + f := make([]string, len(*t)) + for i := range f { + f[i] = "\x00" + (*t)[i] + "\x00" + } + *t = f + } -func testCasePairs() map[string][2]dbusTestCase { - testCaseOnce.Do(testCaseGenerate) - return testCasePairsV -} - -func injectNulls(t *[]string) { - f := make([]string, len(*t)) - for i := range f { - f[i] = "\x00" + (*t)[i] + "\x00" - } - *t = f -} - -func testCaseGenerate() { - // create null-injected test cases - testCasesV = make([]dbusTestCase, len(samples)*2) - for i := range samples { - testCasesV[i] = samples[i] - testCasesV[len(samples)+i] = samples[i] - testCasesV[len(samples)+i].c = new(dbus.Config) - *testCasesV[len(samples)+i].c = *samples[i].c - - // inject nulls - fi := &testCasesV[len(samples)+i] - fi.wantErr = true - - injectNulls(&fi.c.See) - injectNulls(&fi.c.Talk) - injectNulls(&fi.c.Own) - } - - // enumerate test case pairs - var pc int - for _, tc := range samples { - if tc.id != "" { - pc++ - } - } - testCasePairsV = make(map[string][2]dbusTestCase, pc) - for i, tc := range testCasesV { - if tc.id == "" { - continue + fi.c = new(hst.BusConfig) + *fi.c = *samples[i].c + injectNulls(&fi.c.See) + injectNulls(&fi.c.Talk) + injectNulls(&fi.c.Own) } + return testCases + }() - // skip already enumerated system bus test - if tc.id[len(tc.id)-1] == '+' { - continue - } - - ftp := [2]dbusTestCase{tc} - - // system proxy tests always place directly after its user counterpart with id ending in + - if i+1 < len(testCasesV) && testCasesV[i+1].id[len(testCasesV[i+1].id)-1] == '+' { - // attach system bus config - ftp[1] = testCasesV[i+1] - - // check for misplaced/mismatching tests - if ftp[0].wantErr != ftp[1].wantErr || ftp[0].id+"+" != ftp[1].id { - panic("mismatching session/system pairing") + testCasePairs = func() map[string][2]dbusTestCase { + // enumerate test case pairs + var pc int + for _, tc := range samples { + if tc.id != "" { + pc++ } } + pairs := make(map[string][2]dbusTestCase, pc) + for i, tc := range testCasesExt { + if tc.id == "" { + continue + } - k := tc.id - if tc.wantErr { - k = "malformed_" + k + // skip already enumerated system bus test + if tc.id[len(tc.id)-1] == '+' { + continue + } + + ftp := [2]dbusTestCase{tc} + + // system proxy tests always place directly after its user counterpart with id ending in + + if i+1 < len(testCasesExt) && testCasesExt[i+1].id[len(testCasesExt[i+1].id)-1] == '+' { + // attach system bus config + ftp[1] = testCasesExt[i+1] + + // check for misplaced/mismatching tests + if ftp[0].wantErr != ftp[1].wantErr || ftp[0].id+"+" != ftp[1].id { + panic("mismatching session/system pairing") + } + } + + k := tc.id + if tc.wantErr { + k = "malformed_" + k + } + pairs[k] = ftp } - testCasePairsV[k] = ftp - } -} + return pairs + }() +) diff --git a/system/dbus_test.go b/system/dbus_test.go index 0f3033a..0bc7b7f 100644 --- a/system/dbus_test.go +++ b/system/dbus_test.go @@ -11,6 +11,7 @@ import ( "hakurei.app/container/stub" "hakurei.app/helper" + "hakurei.app/hst" "hakurei.app/system/dbus" ) @@ -82,7 +83,7 @@ func TestDBusProxyOp(t *testing.T) { Op: "dbus", Err: ErrDBusConfig, Msg: "attempted to create message bus proxy args without session bus config", } - if err := sys.ProxyDBus(nil, new(dbus.Config), nil, nil); !reflect.DeepEqual(err, wantErr) { + if err := sys.ProxyDBus(nil, new(hst.BusConfig), nil, nil); !reflect.DeepEqual(err, wantErr) { t.Errorf("ProxyDBus: error = %v, want %v", err, wantErr) } }, nil, stub.Expect{}}, @@ -96,10 +97,10 @@ func TestDBusProxyOp(t *testing.T) { }() sys.MustProxyDBus( - m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus"), &dbus.Config{ + m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus"), &hst.BusConfig{ // use impossible value here as an implicit assert that it goes through the stub Talk: []string{"session\x00"}, Filter: true, - }, m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"), &dbus.Config{ + }, m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"), &hst.BusConfig{ // use impossible value here as an implicit assert that it goes through the stub Talk: []string{"system\x00"}, Filter: true, }) @@ -108,8 +109,8 @@ func TestDBusProxyOp(t *testing.T) { call("dbusFinalise", stub.ExpectArgs{ dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus"}, dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"}, - &dbus.Config{Talk: []string{"session\x00"}, Filter: true}, - &dbus.Config{Talk: []string{"system\x00"}, Filter: true}, + &hst.BusConfig{Talk: []string{"session\x00"}, Filter: true}, + &hst.BusConfig{Talk: []string{"system\x00"}, Filter: true}, }, (*dbus.Final)(nil), syscall.EINVAL), }}}, @@ -119,10 +120,10 @@ func TestDBusProxyOp(t *testing.T) { Msg: "cannot finalise message bus proxy: unique error 0 injected by the test suite", } if err := sys.ProxyDBus( - &dbus.Config{ + &hst.BusConfig{ // use impossible value here as an implicit assert that it goes through the stub Talk: []string{"session\x00"}, Filter: true, - }, &dbus.Config{ + }, &hst.BusConfig{ // use impossible value here as an implicit assert that it goes through the stub Talk: []string{"system\x00"}, Filter: true, }, @@ -135,17 +136,17 @@ func TestDBusProxyOp(t *testing.T) { call("dbusFinalise", stub.ExpectArgs{ dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus"}, dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"}, - &dbus.Config{Talk: []string{"session\x00"}, Filter: true}, - &dbus.Config{Talk: []string{"system\x00"}, Filter: true}, + &hst.BusConfig{Talk: []string{"session\x00"}, Filter: true}, + &hst.BusConfig{Talk: []string{"system\x00"}, Filter: true}, }, (*dbus.Final)(nil), stub.UniqueError(0)), }}}, {"full", 0xcafebabe, func(_ *testing.T, sys *I) { sys.MustProxyDBus( - m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus"), &dbus.Config{ + m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus"), &hst.BusConfig{ // use impossible value here as an implicit assert that it goes through the stub Talk: []string{"session\x00"}, Filter: true, - }, m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"), &dbus.Config{ + }, m("/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"), &hst.BusConfig{ // use impossible value here as an implicit assert that it goes through the stub Talk: []string{"system\x00"}, Filter: true, }) @@ -159,8 +160,8 @@ func TestDBusProxyOp(t *testing.T) { call("dbusFinalise", stub.ExpectArgs{ dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus"}, dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"}, - &dbus.Config{Talk: []string{"session\x00"}, Filter: true}, - &dbus.Config{Talk: []string{"system\x00"}, Filter: true}, + &hst.BusConfig{Talk: []string{"session\x00"}, Filter: true}, + &hst.BusConfig{Talk: []string{"system\x00"}, Filter: true}, }, dbusNewFinalSample(0), nil), call("isVerbose", stub.ExpectArgs{}, true, nil), call("verbose", stub.ExpectArgs{[]any{"session bus proxy:", []string{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus", "--filter", "--talk=session\x00"}}}, nil, nil), diff --git a/system/dispatcher.go b/system/dispatcher.go index 38aab88..3f17dc1 100644 --- a/system/dispatcher.go +++ b/system/dispatcher.go @@ -6,6 +6,7 @@ import ( "log" "os" + "hakurei.app/hst" "hakurei.app/system/acl" "hakurei.app/system/dbus" "hakurei.app/system/internal/xcb" @@ -50,7 +51,7 @@ type syscallDispatcher interface { // dbusAddress provides [dbus.Address]. dbusAddress() (session, system string) // dbusFinalise provides [dbus.Finalise]. - dbusFinalise(sessionBus, systemBus dbus.ProxyPair, session, system *dbus.Config) (final *dbus.Final, err error) + dbusFinalise(sessionBus, systemBus dbus.ProxyPair, session, system *hst.BusConfig) (final *dbus.Final, err error) // dbusProxyStart provides the Start method of [dbus.Proxy]. dbusProxyStart(proxy *dbus.Proxy) error // dbusProxyClose provides the Close method of [dbus.Proxy]. @@ -85,7 +86,7 @@ func (k direct) dbusAddress() (session, system string) { return dbus.Address() } -func (k direct) dbusFinalise(sessionBus, systemBus dbus.ProxyPair, session, system *dbus.Config) (final *dbus.Final, err error) { +func (k direct) dbusFinalise(sessionBus, systemBus dbus.ProxyPair, session, system *hst.BusConfig) (final *dbus.Final, err error) { return dbus.Finalise(sessionBus, systemBus, session, system) } diff --git a/system/dispatcher_test.go b/system/dispatcher_test.go index eb93d16..9a1a0d9 100644 --- a/system/dispatcher_test.go +++ b/system/dispatcher_test.go @@ -305,7 +305,7 @@ func (k *kstub) dbusAddress() (session, system string) { return ret[0], ret[1] } -func (k *kstub) dbusFinalise(sessionBus, systemBus dbus.ProxyPair, session, system *dbus.Config) (final *dbus.Final, err error) { +func (k *kstub) dbusFinalise(sessionBus, systemBus dbus.ProxyPair, session, system *hst.BusConfig) (final *dbus.Final, err error) { k.Helper() expect := k.Expects("dbusFinalise")