hst/dbus: move dbus config struct
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m11s
Test / Hakurei (push) Successful in 3m12s
Test / Hpkg (push) Successful in 4m0s
Test / Hakurei (race detector) (push) Successful in 5m20s
Test / Sandbox (race detector) (push) Successful in 2m11s
Test / Flake checks (push) Successful in 1m31s

This allows holding a xdg-dbus-proxy configuration without importing system/dbus.

It also makes more sense in the project structure since the config struct is part of the hst API however the rest of the implementation is not.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-10-07 19:01:18 +09:00
parent 3ce63e95d7
commit d23b4dc9e6
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
17 changed files with 185 additions and 251 deletions

View File

@ -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
}
}

View File

@ -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)

View File

@ -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

View File

@ -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"`

View File

@ -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"`

19
hst/dbus.go Normal file
View File

@ -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"`
}

View File

@ -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,

View File

@ -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",

View File

@ -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

View File

@ -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
}

View File

@ -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.*": "*"},

View File

@ -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

View File

@ -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}

View File

@ -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
}()
)

View File

@ -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),

View File

@ -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)
}

View File

@ -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")