diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-11-15 13:47:09 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-11-15 13:58:34 +0900 |
| commit | a91920310d20e1fc472dea044fce8fd9438835df (patch) | |
| tree | 378cba47d8586e798130a2855f91a685e05244a3 /internal/system/dbus | |
| parent | 16e674782abbda8c85eac2e7d0c5c22e8cfbf5de (diff) | |
internal: relocate packages
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/system/dbus')
| -rw-r--r-- | internal/system/dbus/address.go | 193 | ||||
| -rw-r--r-- | internal/system/dbus/address_escape_test.go | 59 | ||||
| -rw-r--r-- | internal/system/dbus/address_test.go | 123 | ||||
| -rw-r--r-- | internal/system/dbus/config.go | 71 | ||||
| -rw-r--r-- | internal/system/dbus/config_test.go | 124 | ||||
| -rw-r--r-- | internal/system/dbus/dbus.go | 69 | ||||
| -rw-r--r-- | internal/system/dbus/dbus_test.go | 170 | ||||
| -rw-r--r-- | internal/system/dbus/export_test.go | 15 | ||||
| -rw-r--r-- | internal/system/dbus/proc.go | 188 | ||||
| -rw-r--r-- | internal/system/dbus/proc_test.go | 11 | ||||
| -rw-r--r-- | internal/system/dbus/proxy.go | 105 | ||||
| -rw-r--r-- | internal/system/dbus/samples_test.go | 214 | ||||
| -rw-r--r-- | internal/system/dbus/testdata/dev.vencord.Vesktop.json | 18 | ||||
| -rw-r--r-- | internal/system/dbus/testdata/org.chromium.Chromium+.json | 9 | ||||
| -rw-r--r-- | internal/system/dbus/testdata/org.chromium.Chromium.json | 24 | ||||
| -rw-r--r-- | internal/system/dbus/testdata/uk.gensokyo.CrashTestDummy.json | 21 |
16 files changed, 0 insertions, 1414 deletions
diff --git a/internal/system/dbus/address.go b/internal/system/dbus/address.go deleted file mode 100644 index edee7e20..00000000 --- a/internal/system/dbus/address.go +++ /dev/null @@ -1,193 +0,0 @@ -package dbus - -import ( - "bytes" - "encoding/hex" - "errors" - "fmt" - "slices" -) - -type AddrEntry struct { - Method string `json:"method"` - Values [][2]string `json:"values"` -} - -// EqualAddrEntries returns whether two slices of [AddrEntry] are equal. -func EqualAddrEntries(entries, target []AddrEntry) bool { - return slices.EqualFunc(entries, target, func(a AddrEntry, b AddrEntry) bool { - return a.Method == b.Method && slices.Equal(a.Values, b.Values) - }) -} - -// Parse parses D-Bus address according to -// https://dbus.freedesktop.org/doc/dbus-specification.html#addresses -func Parse(addr []byte) ([]AddrEntry, error) { - // Look for a semicolon - address := bytes.Split(bytes.TrimSuffix(addr, []byte{';'}), []byte{';'}) - - // Allocate for entries - v := make([]AddrEntry, len(address)) - - for i, s := range address { - var pairs [][]byte - - // Look for the colon : - if method, list, ok := bytes.Cut(s, []byte{':'}); !ok { - return v, &BadAddressError{ErrNoColon, i, s, -1, nil} - } else { - pairs = bytes.Split(list, []byte{','}) - v[i].Method = string(method) - v[i].Values = make([][2]string, len(pairs)) - } - - for j, pair := range pairs { - key, value, ok := bytes.Cut(pair, []byte{'='}) - if !ok { - return v, &BadAddressError{ErrBadPairSep, i, s, j, pair} - } - if len(key) == 0 { - return v, &BadAddressError{ErrBadPairKey, i, s, j, pair} - } - if len(value) == 0 { - return v, &BadAddressError{ErrBadPairVal, i, s, j, pair} - } - v[i].Values[j][0] = string(key) - - if val, errno := unescapeValue(value); errno != errSuccess { - return v, &BadAddressError{errno, i, s, j, pair} - } else { - v[i].Values[j][1] = string(val) - } - } - } - - return v, nil -} - -func unescapeValue(v []byte) (val []byte, errno ParseError) { - if l := len(v) - (bytes.Count(v, []byte{'%'}) * 2); l < 0 { - errno = ErrBadValLength - return - } else { - val = make([]byte, l) - } - - var i, skip int - for iu, b := range v { - if skip > 0 { - skip-- - continue - } - - if ib := bytes.IndexByte([]byte("-_/.\\*"), b); ib != -1 { // - // _/.\* - goto opt - } else if b >= '0' && b <= '9' { // 0-9 - goto opt - } else if b >= 'A' && b <= 'Z' { // A-Z - goto opt - } else if b >= 'a' && b <= 'z' { // a-z - goto opt - } - - if b != '%' { - errno = ErrBadValByte - break - } - - skip += 2 - if iu+2 >= len(v) { - errno = ErrBadValHexLength - break - } - if c, err := hex.Decode(val[i:i+1], v[iu+1:iu+3]); err != nil { - if errors.As(err, new(hex.InvalidByteError)) { - errno = ErrBadValHexByte - break - } - // unreachable - panic(err.Error()) - } else if c != 1 { - // unreachable - panic(fmt.Sprintf("invalid decode length %d", c)) - } - i++ - continue - - opt: - val[i] = b - i++ - } - - return -} - -type ParseError uint8 - -func (e ParseError) Error() string { - switch e { - case errSuccess: - panic("attempted to return success as error") - case ErrNoColon: - return "address does not contain a colon" - case ErrBadPairSep: - return "'=' character not found" - case ErrBadPairKey: - return "'=' character has no key preceding it" - case ErrBadPairVal: - return "'=' character has no value following it" - case ErrBadValLength: - return "unescaped value has impossible length" - case ErrBadValByte: - return "in D-Bus address, characters other than [-0-9A-Za-z_/.\\*] should have been escaped" - case ErrBadValHexLength: - return "in D-Bus address, percent character was not followed by two hex digits" - case ErrBadValHexByte: - return "in D-Bus address, percent character was followed by characters other than hex digits" - - default: - return fmt.Sprintf("parse error %d", e) - } -} - -const ( - errSuccess ParseError = iota - ErrNoColon - ErrBadPairSep - ErrBadPairKey - ErrBadPairVal - ErrBadValLength - ErrBadValByte - ErrBadValHexLength - ErrBadValHexByte -) - -type BadAddressError struct { - // error type - Type ParseError - - // bad entry position - EntryPos int - // bad entry value - EntryVal []byte - - // bad pair position - PairPos int - // bad pair value - PairVal []byte -} - -func (a *BadAddressError) Is(err error) bool { - var b *BadAddressError - return errors.As(err, &b) && a.Type == b.Type && - a.EntryPos == b.EntryPos && slices.Equal(a.EntryVal, b.EntryVal) && - a.PairPos == b.PairPos && slices.Equal(a.PairVal, b.PairVal) -} - -func (a *BadAddressError) Error() string { - return a.Type.Error() -} - -func (a *BadAddressError) Unwrap() error { - return a.Type -} diff --git a/internal/system/dbus/address_escape_test.go b/internal/system/dbus/address_escape_test.go deleted file mode 100644 index 4ef48f8b..00000000 --- a/internal/system/dbus/address_escape_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package dbus - -import ( - "testing" -) - -func TestUnescapeValue(t *testing.T) { - t.Parallel() - - testCases := []struct { - value string - want string - wantErr ParseError - }{ - // upstream test cases - {value: "abcde", want: "abcde"}, - {value: "", want: ""}, - {value: "%20%20", want: " "}, - {value: "%24", want: "$"}, - {value: "%25", want: "%"}, - {value: "abc%24", want: "abc$"}, - {value: "%24abc", want: "$abc"}, - {value: "abc%24abc", want: "abc$abc"}, - {value: "/", want: "/"}, - {value: "-", want: "-"}, - {value: "_", want: "_"}, - {value: "A", want: "A"}, - {value: "I", want: "I"}, - {value: "Z", want: "Z"}, - {value: "a", want: "a"}, - {value: "i", want: "i"}, - {value: "z", want: "z"}, - /* Bug: https://bugs.freedesktop.org/show_bug.cgi?id=53499 */ - {value: "%c3%b6", want: "\xc3\xb6"}, - - {value: "%a", wantErr: ErrBadValHexLength}, - {value: "%q", wantErr: ErrBadValHexLength}, - {value: "%az", wantErr: ErrBadValHexByte}, - {value: "%%", wantErr: ErrBadValLength}, - {value: "%$$", wantErr: ErrBadValHexByte}, - {value: "abc%a", wantErr: ErrBadValHexLength}, - {value: "%axyz", wantErr: ErrBadValHexByte}, - {value: "%", wantErr: ErrBadValLength}, - {value: "$", wantErr: ErrBadValByte}, - {value: " ", wantErr: ErrBadValByte}, - } - - for _, tc := range testCases { - t.Run("unescape "+tc.value, func(t *testing.T) { - t.Parallel() - - if got, errno := unescapeValue([]byte(tc.value)); errno != tc.wantErr { - t.Errorf("unescapeValue() errno = %v, wantErr %v", errno, tc.wantErr) - } else if tc.wantErr == errSuccess && string(got) != tc.want { - t.Errorf("unescapeValue() = %q, want %q", got, tc.want) - } - }) - } -} diff --git a/internal/system/dbus/address_test.go b/internal/system/dbus/address_test.go deleted file mode 100644 index 9ed48214..00000000 --- a/internal/system/dbus/address_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package dbus_test - -import ( - "errors" - "reflect" - "testing" - - "hakurei.app/internal/system/dbus" -) - -func TestParse(t *testing.T) { - t.Parallel() - - testCases := []struct { - name string - addr string - want []dbus.AddrEntry - wantErr error - }{ - { - name: "simple session unix", - addr: "unix:path=/run/user/1971/bus", - want: []dbus.AddrEntry{{ - Method: "unix", - Values: [][2]string{{"path", "/run/user/1971/bus"}}, - }}, - }, - { - name: "simple upper escape", - addr: "debug:name=Test,cat=cute,escaped=%c3%b6", - want: []dbus.AddrEntry{{ - Method: "debug", - Values: [][2]string{ - {"name", "Test"}, - {"cat", "cute"}, - {"escaped", "\xc3\xb6"}, - }, - }}, - }, - { - name: "simple bad escape", - addr: "debug:name=%", - wantErr: &dbus.BadAddressError{Type: dbus.ErrBadValLength, - EntryPos: 0, EntryVal: []byte("debug:name=%"), PairPos: 0, PairVal: []byte("name=%")}, - }, - - // upstream test cases - { - name: "full address success", - addr: "unix:path=/tmp/foo;debug:name=test,sliff=sloff;", - want: []dbus.AddrEntry{ - {Method: "unix", Values: [][2]string{{"path", "/tmp/foo"}}}, - {Method: "debug", Values: [][2]string{{"name", "test"}, {"sliff", "sloff"}}}, - }, - }, - { - name: "empty address", - addr: "", - wantErr: &dbus.BadAddressError{Type: dbus.ErrNoColon, - EntryVal: []byte{}, PairPos: -1}, - }, - { - name: "no body", - addr: "foo", - wantErr: &dbus.BadAddressError{Type: dbus.ErrNoColon, - EntryPos: 0, EntryVal: []byte("foo"), PairPos: -1}, - }, - { - name: "no pair separator", - addr: "foo:bar", - wantErr: &dbus.BadAddressError{Type: dbus.ErrBadPairSep, - EntryPos: 0, EntryVal: []byte("foo:bar"), PairPos: 0, PairVal: []byte("bar")}, - }, - { - name: "no pair separator multi pair", - addr: "foo:bar,baz", - wantErr: &dbus.BadAddressError{Type: dbus.ErrBadPairSep, - EntryPos: 0, EntryVal: []byte("foo:bar,baz"), PairPos: 0, PairVal: []byte("bar")}, - }, - { - name: "no pair separator single valid pair", - addr: "foo:bar=foo,baz", - wantErr: &dbus.BadAddressError{Type: dbus.ErrBadPairSep, - EntryPos: 0, EntryVal: []byte("foo:bar=foo,baz"), PairPos: 1, PairVal: []byte("baz")}, - }, - { - name: "no body single valid address", - addr: "foo:bar=foo;baz", - wantErr: &dbus.BadAddressError{Type: dbus.ErrNoColon, - EntryPos: 1, EntryVal: []byte("baz"), PairPos: -1}, - }, - { - name: "no key", - addr: "foo:=foo", - wantErr: &dbus.BadAddressError{Type: dbus.ErrBadPairKey, - EntryPos: 0, EntryVal: []byte("foo:=foo"), PairPos: 0, PairVal: []byte("=foo")}, - }, - { - name: "no value", - addr: "foo:foo=", - wantErr: &dbus.BadAddressError{Type: dbus.ErrBadPairVal, - EntryPos: 0, EntryVal: []byte("foo:foo="), PairPos: 0, PairVal: []byte("foo=")}, - }, - { - name: "no pair separator single valid pair trailing", - addr: "foo:foo,bar=baz", - wantErr: &dbus.BadAddressError{Type: dbus.ErrBadPairSep, - EntryPos: 0, EntryVal: []byte("foo:foo,bar=baz"), PairPos: 0, PairVal: []byte("foo")}, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - t.Parallel() - - if got, err := dbus.Parse([]byte(tc.addr)); !errors.Is(err, tc.wantErr) { - t.Errorf("Parse() error = %v, wantErr %v", err, tc.wantErr) - } else if tc.wantErr == nil && !reflect.DeepEqual(got, tc.want) { - t.Errorf("Parse() = %#v, want %#v", got, tc.want) - } - }) - } -} diff --git a/internal/system/dbus/config.go b/internal/system/dbus/config.go deleted file mode 100644 index 2a09ee90..00000000 --- a/internal/system/dbus/config.go +++ /dev/null @@ -1,71 +0,0 @@ -package dbus - -import ( - "hakurei.app/hst" -) - -// ProxyPair is an upstream dbus address and a downstream socket path. -type ProxyPair [2]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++ - } - if c.Filter { - argc++ - } - - args = make([]string, 0, argc) - args = append(args, bus[0], bus[1]) - if c.Filter { - args = append(args, "--filter") - } - for _, name := range c.See { - args = append(args, "--see="+name) - } - for _, name := range c.Talk { - args = append(args, "--talk="+name) - } - for _, name := range c.Own { - args = append(args, "--own="+name) - } - for name, rule := range c.Call { - args = append(args, "--call="+name+"="+rule) - } - for name, rule := range c.Broadcast { - args = append(args, "--broadcast="+name+"="+rule) - } - if c.Log { - args = append(args, "--log") - } - - return -} - -// 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), - - Filter: true, - } - - if defaults { - c.Talk = []string{"org.freedesktop.DBus", "org.freedesktop.Notifications"} - - c.Call["org.freedesktop.portal.*"] = "*" - c.Broadcast["org.freedesktop.portal.*"] = "@/org/freedesktop/portal/*" - - if id != "" { - c.Own = []string{id + ".*"} - if mpris { - c.Own = append(c.Own, "org.mpris.MediaPlayer2."+id+".*") - } - } - } - - return &c -} diff --git a/internal/system/dbus/config_test.go b/internal/system/dbus/config_test.go deleted file mode 100644 index dec4cb16..00000000 --- a/internal/system/dbus/config_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package dbus_test - -import ( - "reflect" - "slices" - "strings" - "testing" - - "hakurei.app/hst" - "hakurei.app/internal/system/dbus" -) - -func TestConfigArgs(t *testing.T) { - t.Parallel() - - 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) { - t.Parallel() - - if got := dbus.Args(tc.c, tc.bus); !slices.Equal(got, tc.want) { - t.Errorf("Args: %v, want %v", got, tc.want) - } - }) - } -} - -func TestNewConfig(t *testing.T) { - t.Parallel() - ids := [...]string{"org.chromium.Chromium", "dev.vencord.Vesktop"} - - type newTestCase struct { - id string - args [2]bool - 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}, &hst.BusConfig{ - Call: make(map[string]string), - Broadcast: make(map[string]string), - Filter: true, - }}, - newTestCase{"", [2]bool{false, true}, &hst.BusConfig{ - Call: make(map[string]string), - Broadcast: make(map[string]string), - Filter: true, - }}, - 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}, &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, - }}, - ) - for _, id := range ids { - tcs = append(tcs, - 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}, &hst.BusConfig{ - Call: make(map[string]string), - Broadcast: make(map[string]string), - Filter: true, - }}, - 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}, &hst.BusConfig{ - Talk: []string{"org.freedesktop.DBus", "org.freedesktop.Notifications"}, - Own: []string{id + ".*", "org.mpris.MediaPlayer2." + id + ".*"}, - Call: map[string]string{"org.freedesktop.portal.*": "*"}, - Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"}, - Filter: true, - }}, - ) - } - - for _, tc := range tcs { - name := new(strings.Builder) - name.WriteString("create new configuration struct") - - if tc.args[0] { - name.WriteString(" with builtin defaults") - if tc.args[1] { - name.WriteString(" (mpris)") - } - } - - if tc.id != "" { - name.WriteString(" for application ID ") - name.WriteString(tc.id) - } - - t.Run(name.String(), func(t *testing.T) { - t.Parallel() - - if gotC := dbus.NewConfig(tc.id, tc.args[0], tc.args[1]); !reflect.DeepEqual(gotC, tc.want) { - t.Errorf("NewConfig(%q, %t, %t) = %v, want %v", - tc.id, tc.args[0], tc.args[1], - gotC, tc.want) - } - }) - } -} diff --git a/internal/system/dbus/dbus.go b/internal/system/dbus/dbus.go deleted file mode 100644 index 9ea89f51..00000000 --- a/internal/system/dbus/dbus.go +++ /dev/null @@ -1,69 +0,0 @@ -// Package dbus wraps xdg-dbus-proxy and implements configuration and sandboxing of the underlying helper process. -package dbus - -import ( - "fmt" - "os" - "sync" -) - -const ( - /* - SessionBusAddress is the name of the environment variable where the address of the login session message bus is given in. - - If that variable is not set, applications may also try to read the address from the X Window System root window property _DBUS_SESSION_BUS_ADDRESS. - The root window property must have type STRING. The environment variable should have precedence over the root window property. - - The address of the login session message bus is given in the DBUS_SESSION_BUS_ADDRESS environment variable. - If DBUS_SESSION_BUS_ADDRESS is not set, or if it's set to the string "autolaunch:", - the system should use platform-specific methods of locating a running D-Bus session server, - or starting one if a running instance cannot be found. - Note that this mechanism is not recommended for attempting to determine if a daemon is running. - It is inherently racy to attempt to make this determination, since the bus daemon may be started just before or just after the determination is made. - Therefore, it is recommended that applications do not try to make this determination for their functionality purposes, and instead they should attempt to start the server. - - This package diverges from the specification, as the caller is unlikely to be an X client, or be in a position to autolaunch a dbus server. - So a fallback address with a socket located in the well-known default XDG_RUNTIME_DIR formatting is used. - */ - SessionBusAddress = "DBUS_SESSION_BUS_ADDRESS" - - /* - SystemBusAddress is the name of the environment variable where the address of the system message bus is given in. - - If that variable is not set, applications should try to connect to the well-known address unix:path=/var/run/dbus/system_bus_socket. - Implementations of the well-known system bus should listen on an address that will result in that connection being successful. - */ - SystemBusAddress = "DBUS_SYSTEM_BUS_ADDRESS" - - // FallbackSystemBusAddress is used when [SystemBusAddress] is not set. - FallbackSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" -) - -var ( - address [2]string - addressOnce sync.Once -) - -// Address returns the session and system bus addresses copied from environment, -// or appropriate fallback values if they are not set. -func Address() (session, system string) { - addressOnce.Do(func() { - // resolve upstream session bus address - if addr, ok := os.LookupEnv(SessionBusAddress); !ok { - // fall back to default format - address[0] = fmt.Sprintf("unix:path=/run/user/%d/bus", os.Getuid()) - } else { - address[0] = addr - } - - // resolve upstream system bus address - if addr, ok := os.LookupEnv(SystemBusAddress); !ok { - // fall back to default hardcoded value - address[1] = FallbackSystemBusAddress - } else { - address[1] = addr - } - }) - - return address[0], address[1] -} diff --git a/internal/system/dbus/dbus_test.go b/internal/system/dbus/dbus_test.go deleted file mode 100644 index 9fc2b539..00000000 --- a/internal/system/dbus/dbus_test.go +++ /dev/null @@ -1,170 +0,0 @@ -package dbus_test - -import ( - "context" - "errors" - "fmt" - "io" - "os" - "strings" - "syscall" - "testing" - "time" - - "hakurei.app/internal/helper" - "hakurei.app/internal/system/dbus" - "hakurei.app/message" -) - -func TestFinalise(t *testing.T) { - if _, err := dbus.Finalise(dbus.ProxyPair{}, dbus.ProxyPair{}, nil, nil); !errors.Is(err, syscall.EBADE) { - t.Errorf("Finalise: error = %v, want %v", - err, syscall.EBADE) - } - - 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 { - t.Errorf("Finalise: error = %v, wantErr %v", - err, tc[0].wantErr) - return - } else { - wt = v - } - - // rest of the tests happen for sealed instances - if tc[0].wantErr { - return - } - - // build null-terminated string from wanted args - want := new(strings.Builder) - args := append(tc[0].want, tc[1].want...) - for _, arg := range args { - want.WriteString(arg) - want.WriteByte(0) - } - - got := new(strings.Builder) - if _, err := wt.WriteTo(got); err != nil { - t.Errorf("WriteTo: error = %v", err) - } - - if want.String() != got.String() { - t.Errorf("Seal: %q, want %q", - got.String(), want.String()) - } - }) - } -} - -func TestProxyStartWaitCloseString(t *testing.T) { - t.Run("sandbox", func(t *testing.T) { testProxyFinaliseStartWaitCloseString(t, true) }) - t.Run("direct", func(t *testing.T) { testProxyFinaliseStartWaitCloseString(t, false) }) -} - -const ( - stubProxyTimeout = 5 * time.Second -) - -func testProxyFinaliseStartWaitCloseString(t *testing.T, useSandbox bool) { - { - oldWaitDelay := helper.WaitDelay - helper.WaitDelay = 16 * time.Second - t.Cleanup(func() { helper.WaitDelay = oldWaitDelay }) - } - - { - proxyName := dbus.ProxyName - dbus.ProxyName = os.Args[0] - t.Cleanup(func() { dbus.ProxyName = proxyName }) - } - - var p *dbus.Proxy - - t.Run("string for nil proxy", func(t *testing.T) { - want := "(invalid dbus proxy)" - if got := p.String(); got != want { - t.Errorf("String: %q, want %q", - got, want) - } - }) - - t.Run("invalid start", func(t *testing.T) { - if !useSandbox { - p = dbus.NewDirect(t.Context(), message.New(nil), nil, nil) - } else { - p = dbus.New(t.Context(), message.New(nil), nil, nil) - } - - if err := p.Start(); !errors.Is(err, syscall.ENOTRECOVERABLE) { - t.Errorf("Start: error = %q, wantErr %q", err, syscall.ENOTRECOVERABLE) - return - } - }) - - for id, tc := range testCasePairs { - // this test does not test errors - if tc[0].wantErr { - continue - } - - t.Run("proxy for "+id, func(t *testing.T) { - var final *dbus.Final - t.Run("finalise", func(t *testing.T) { - if v, err := dbus.Finalise(tc[0].bus, tc[1].bus, tc[0].c, tc[1].c); err != nil { - t.Errorf("Finalise: error = %v, wantErr %v", err, tc[0].wantErr) - return - } else { - final = v - } - }) - - ctx, cancel := context.WithTimeout(t.Context(), stubProxyTimeout) - defer cancel() - output := new(strings.Builder) - if !useSandbox { - p = dbus.NewDirect(ctx, message.New(nil), final, output) - } else { - p = dbus.New(ctx, message.New(nil), final, output) - } - - { // check invalid wait behaviour - wantErr := "dbus: not started" - if err := p.Wait(); err == nil || err.Error() != wantErr { - t.Errorf("Wait: error = %v, wantErr %v", err, wantErr) - } - } - - { // check string behaviour - want := "(unused dbus proxy)" - if got := p.String(); got != want { - t.Errorf("String: %q, want %q", got, want) - return - } - } - - if err := p.Start(); err != nil { - t.Fatalf("Start: error = %v", err) - } - - { // check running string behaviour - wantSubstr := fmt.Sprintf("%s --args=3 --fd=4", os.Args[0]) - if useSandbox { - wantSubstr = `argv: ["xdg-dbus-proxy" "--args=3" "--fd=4"], filter: true, rules: 0, flags: 0x1, presets: 0xf` - } - if got := p.String(); !strings.Contains(got, wantSubstr) { - t.Errorf("String: %q, want %q", - got, wantSubstr) - return - } - } - - p.Close() - if err := p.Wait(); err != nil { - t.Errorf("Wait: error = %v\noutput: %s", err, output.String()) - } - }) - } -} diff --git a/internal/system/dbus/export_test.go b/internal/system/dbus/export_test.go deleted file mode 100644 index bc24f3f3..00000000 --- a/internal/system/dbus/export_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package dbus - -import ( - "context" - "io" - - "hakurei.app/message" -) - -// NewDirect returns a new instance of [Proxy] with its sandbox disabled. -func NewDirect(ctx context.Context, msg message.Msg, final *Final, output io.Writer) *Proxy { - p := New(ctx, msg, final, output) - p.useSandbox = false - return p -} diff --git a/internal/system/dbus/proc.go b/internal/system/dbus/proc.go deleted file mode 100644 index c934e89e..00000000 --- a/internal/system/dbus/proc.go +++ /dev/null @@ -1,188 +0,0 @@ -package dbus - -import ( - "context" - "errors" - "os" - "os/exec" - "strconv" - "syscall" - - "hakurei.app/container" - "hakurei.app/container/check" - "hakurei.app/container/seccomp" - "hakurei.app/container/std" - "hakurei.app/internal/helper" - "hakurei.app/ldd" -) - -// Start starts and configures a D-Bus proxy process. -func (p *Proxy) Start() error { - if p.final == nil || p.final.WriterTo == nil { - return syscall.ENOTRECOVERABLE - } - - p.mu.Lock() - defer p.mu.Unlock() - p.pmu.Lock() - defer p.pmu.Unlock() - - if p.cancel != nil || p.cause != nil { - return errors.New("dbus: already started") - } - - ctx, cancel := context.WithCancelCause(p.ctx) - - if !p.useSandbox { - p.helper = helper.NewDirect(ctx, p.name, p.final, true, argF, func(cmd *exec.Cmd) { - if p.output != nil { - cmd.Stdout, cmd.Stderr = p.output, p.output - } - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - cmd.Env = make([]string, 0) - }, nil) - } else { - var toolPath *check.Absolute - if a, err := check.NewAbs(p.name); err != nil { - if p.name, err = exec.LookPath(p.name); err != nil { - return err - } else if toolPath, err = check.NewAbs(p.name); err != nil { - return err - } - } else { - toolPath = a - } - - var libPaths []*check.Absolute - if entries, err := ldd.Resolve(ctx, p.msg, toolPath); err != nil { - return err - } else { - libPaths = ldd.Path(entries) - } - - p.helper = helper.New( - ctx, p.msg, toolPath, "xdg-dbus-proxy", - p.final, true, - argF, func(z *container.Container) { - z.SeccompFlags |= seccomp.AllowMultiarch - z.SeccompPresets |= std.PresetStrict - z.Hostname = "hakurei-dbus" - if p.output != nil { - z.Stdout, z.Stderr = p.output, p.output - } - - // these lib paths are unpredictable, so mount them first so they cannot cover anything - for _, name := range libPaths { - z.Bind(name, name, 0) - } - - // upstream bus directories - upstreamPaths := make([]*check.Absolute, 0, 2) - for _, addr := range [][]AddrEntry{p.final.SessionUpstream, p.final.SystemUpstream} { - for _, ent := range addr { - if ent.Method != "unix" { - continue - } - for _, pair := range ent.Values { - if pair[0] != "path" { - continue - } - if a, err := check.NewAbs(pair[1]); err != nil { - continue - } else { - upstreamPaths = append(upstreamPaths, a.Dir()) - } - } - } - } - check.SortAbs(upstreamPaths) - upstreamPaths = check.CompactAbs(upstreamPaths) - for _, name := range upstreamPaths { - z.Bind(name, name, 0) - } - z.HostNet = len(upstreamPaths) == 0 - z.HostAbstract = z.HostNet - - // parent directories of bind paths - sockDirPaths := make([]*check.Absolute, 0, 2) - if a, err := check.NewAbs(p.final.Session[1]); err == nil { - sockDirPaths = append(sockDirPaths, a.Dir()) - } - if a, err := check.NewAbs(p.final.System[1]); err == nil { - sockDirPaths = append(sockDirPaths, a.Dir()) - } - check.SortAbs(sockDirPaths) - sockDirPaths = check.CompactAbs(sockDirPaths) - for _, name := range sockDirPaths { - z.Bind(name, name, std.BindWritable) - } - - // xdg-dbus-proxy bin path - binPath := toolPath.Dir() - z.Bind(binPath, binPath, 0) - }, nil) - } - - if err := p.helper.Start(); err != nil { - cancel(err) - p.helper = nil - return err - } - - p.cancel, p.cause = cancel, func() error { return context.Cause(ctx) } - return nil -} - -var proxyClosed = errors.New("proxy closed") - -// Wait blocks until xdg-dbus-proxy exits and releases resources. -func (p *Proxy) Wait() error { - p.mu.RLock() - defer p.mu.RUnlock() - - p.pmu.RLock() - if p.helper == nil || p.cancel == nil || p.cause == nil { - p.pmu.RUnlock() - return errors.New("dbus: not started") - } - - var errs [3]error - - errs[0] = p.helper.Wait() - if errors.Is(errs[0], context.Canceled) && - errors.Is(p.cause(), proxyClosed) { - errs[0] = nil - } - p.pmu.RUnlock() - - // ensure socket removal so ephemeral directory is empty at revert - if err := os.Remove(p.final.Session[1]); err != nil && !errors.Is(err, os.ErrNotExist) { - errs[1] = err - } - if p.final.System[1] != "" { - if err := os.Remove(p.final.System[1]); err != nil && !errors.Is(err, os.ErrNotExist) { - errs[2] = err - } - } - - return errors.Join(errs[:]...) -} - -// Close cancels the context passed to the helper instance attached to xdg-dbus-proxy. -func (p *Proxy) Close() { - p.pmu.Lock() - defer p.pmu.Unlock() - - if p.cancel == nil { - panic("dbus: not started") - } - p.cancel(proxyClosed) -} - -func argF(argsFd, statFd int) []string { - if statFd == -1 { - return []string{"--args=" + strconv.Itoa(argsFd)} - } else { - return []string{"--args=" + strconv.Itoa(argsFd), "--fd=" + strconv.Itoa(statFd)} - } -} diff --git a/internal/system/dbus/proc_test.go b/internal/system/dbus/proc_test.go deleted file mode 100644 index 544856ed..00000000 --- a/internal/system/dbus/proc_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package dbus_test - -import ( - "os" - "testing" - - "hakurei.app/container" - "hakurei.app/internal/helper" -) - -func TestMain(m *testing.M) { container.TryArgv0(nil); helper.InternalHelperStub(); os.Exit(m.Run()) } diff --git a/internal/system/dbus/proxy.go b/internal/system/dbus/proxy.go deleted file mode 100644 index 9de49c41..00000000 --- a/internal/system/dbus/proxy.go +++ /dev/null @@ -1,105 +0,0 @@ -package dbus - -import ( - "context" - "io" - "sync" - "syscall" - - "hakurei.app/hst" - "hakurei.app/internal/helper" - "hakurei.app/message" -) - -// ProxyName is the file name or path to the proxy program. -// Overriding ProxyName will only affect Proxy instance created after the change. -var ProxyName = "xdg-dbus-proxy" - -// Proxy holds the state of a xdg-dbus-proxy process, and should never be copied. -type Proxy struct { - helper helper.Helper - ctx context.Context - msg message.Msg - - cancel context.CancelCauseFunc - cause func() error - - final *Final - output io.Writer - useSandbox bool - - name string - - mu, pmu sync.RWMutex -} - -func (p *Proxy) String() string { - if p == nil { - return "(invalid dbus proxy)" - } - - p.mu.RLock() - defer p.mu.RUnlock() - - if p.helper != nil { - return p.helper.String() - } - - return "(unused dbus proxy)" -} - -// Final describes the outcome of a proxy configuration. -type Final struct { - Session, System ProxyPair - // parsed upstream address - SessionUpstream, SystemUpstream []AddrEntry - io.WriterTo -} - -// Finalise creates a checked argument writer for [Proxy]. -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 { - return - } - args = append(args, Args(session, sessionBus)...) - } - if system != nil { - if err = system.CheckInterfaces("system"); err != nil { - return - } - args = append(args, Args(system, systemBus)...) - } - - final = &Final{Session: sessionBus, System: systemBus} - - final.WriterTo, err = helper.NewCheckedArgs(args...) - if err != nil { - return - } - - if session != nil { - final.SessionUpstream, err = Parse([]byte(final.Session[0])) - if err != nil { - return - } - } - if system != nil { - final.SystemUpstream, err = Parse([]byte(final.System[0])) - if err != nil { - return - } - } - - return -} - -// New returns a new instance of [Proxy]. -func New(ctx context.Context, msg message.Msg, final *Final, output io.Writer) *Proxy { - return &Proxy{name: ProxyName, ctx: ctx, msg: msg, final: final, output: output, useSandbox: true} -} diff --git a/internal/system/dbus/samples_test.go b/internal/system/dbus/samples_test.go deleted file mode 100644 index 7db3d6b9..00000000 --- a/internal/system/dbus/samples_test.go +++ /dev/null @@ -1,214 +0,0 @@ -package dbus_test - -import ( - "hakurei.app/hst" -) - -const ( - sampleHostPath = "/tmp/bus" - sampleHostAddr = "unix:path=" + sampleHostPath - sampleBindPath = "/tmp/proxied_bus" -) - -var samples = []dbusTestCase{ - { - "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"}, - Own: []string{"org.chromium.Chromium.*", "org.mpris.MediaPlayer2.org.chromium.Chromium.*", - "org.mpris.MediaPlayer2.chromium.*"}, - Call: map[string]string{"org.freedesktop.portal.*": "*"}, - Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"}, - Log: false, - Filter: true, - }, false, false, - [2]string{sampleHostAddr, sampleBindPath}, - []string{ - sampleHostAddr, - sampleBindPath, - "--filter", - "--talk=org.freedesktop.Notifications", - "--talk=org.freedesktop.FileManager1", - "--talk=org.freedesktop.ScreenSaver", - "--talk=org.freedesktop.secrets", - "--talk=org.kde.kwalletd5", - "--talk=org.kde.kwalletd6", - "--talk=org.gnome.SessionManager", - "--own=org.chromium.Chromium.*", - "--own=org.mpris.MediaPlayer2.org.chromium.Chromium.*", - "--own=org.mpris.MediaPlayer2.chromium.*", - "--call=org.freedesktop.portal.*=*", - "--broadcast=org.freedesktop.portal.*=@/org/freedesktop/portal/*", - }, - }, - { - "org.chromium.Chromium+", &hst.BusConfig{ - See: nil, - Talk: []string{"org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"}, - Own: nil, - Call: nil, - Broadcast: nil, - Log: false, - Filter: true, - }, false, false, - [2]string{sampleHostAddr, sampleBindPath}, - []string{ - sampleHostAddr, - sampleBindPath, - "--filter", - "--talk=org.bluez", - "--talk=org.freedesktop.Avahi", - "--talk=org.freedesktop.UPower", - }, - }, - - { - "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.*"}, - Call: map[string]string{"org.freedesktop.portal.*": "*"}, - Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"}, - Log: false, - Filter: true, - }, false, false, - [2]string{sampleHostAddr, sampleBindPath}, - []string{ - sampleHostAddr, - sampleBindPath, - "--filter", - "--talk=org.freedesktop.Notifications", - "--talk=org.kde.StatusNotifierWatcher", - "--own=dev.vencord.Vesktop.*", - "--own=org.mpris.MediaPlayer2.dev.vencord.Vesktop.*", - "--call=org.freedesktop.portal.*=*", - "--broadcast=org.freedesktop.portal.*=@/org/freedesktop/portal/*"}, - }, - - { - "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.*"}, - Call: map[string]string{"org.freedesktop.portal.*": "*"}, - Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"}, - Log: true, - Filter: true, - }, false, false, - [2]string{sampleHostAddr, sampleBindPath}, - []string{ - sampleHostAddr, - sampleBindPath, - "--filter", - "--see=uk.gensokyo.CrashTestDummy1", - "--talk=org.freedesktop.Notifications", - "--own=uk.gensokyo.CrashTestDummy.*", - "--own=org.mpris.MediaPlayer2.uk.gensokyo.CrashTestDummy.*", - "--call=org.freedesktop.portal.*=*", - "--broadcast=org.freedesktop.portal.*=@/org/freedesktop/portal/*", - "--log"}, - }, - { - "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.*"}, - Call: map[string]string{"org.freedesktop.portal.*": "*"}, - Broadcast: map[string]string{"org.freedesktop.portal.*": "@/org/freedesktop/portal/*"}, - Log: true, - Filter: true, - }, false, true, - [2]string{sampleHostAddr, sampleBindPath}, - []string{ - sampleHostAddr, - sampleBindPath, - "--filter", - "--see=uk.gensokyo.CrashTestDummy", - "--talk=org.freedesktop.Notifications", - "--own=uk.gensokyo.CrashTestDummy1.*", - "--own=org.mpris.MediaPlayer2.uk.gensokyo.CrashTestDummy1.*", - "--call=org.freedesktop.portal.*=*", - "--broadcast=org.freedesktop.portal.*=@/org/freedesktop/portal/*", - "--log"}, - }, -} - -type dbusTestCase struct { - id string - c *hst.BusConfig - wantErr bool - wantErrF bool - bus [2]string - want []string -} - -var ( - testCasesExt = func() []dbusTestCase { - testCases := make([]dbusTestCase, len(samples)*2) - for i := range samples { - testCases[i] = samples[i] - - fi := &testCases[len(samples)+i] - *fi = samples[i] - - // 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 - } - - fi.c = new(hst.BusConfig) - *fi.c = *samples[i].c - injectNulls(&fi.c.See) - injectNulls(&fi.c.Talk) - injectNulls(&fi.c.Own) - } - return testCases - }() - - 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 - } - - // 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 - } - return pairs - }() -) diff --git a/internal/system/dbus/testdata/dev.vencord.Vesktop.json b/internal/system/dbus/testdata/dev.vencord.Vesktop.json deleted file mode 100644 index 05259774..00000000 --- a/internal/system/dbus/testdata/dev.vencord.Vesktop.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "talk":[ - "org.freedesktop.Notifications", - "org.kde.StatusNotifierWatcher" - ], - "own":[ - "dev.vencord.Vesktop.*", - "org.mpris.MediaPlayer2.dev.vencord.Vesktop.*" - ], - "call":{ - "org.freedesktop.portal.*":"*" - }, - "broadcast":{ - "org.freedesktop.portal.*":"@/org/freedesktop/portal/*" - }, - - "filter":true -} diff --git a/internal/system/dbus/testdata/org.chromium.Chromium+.json b/internal/system/dbus/testdata/org.chromium.Chromium+.json deleted file mode 100644 index 0a27d719..00000000 --- a/internal/system/dbus/testdata/org.chromium.Chromium+.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "talk":[ - "org.bluez", - "org.freedesktop.Avahi", - "org.freedesktop.UPower" - ], - - "filter":true -}
\ No newline at end of file diff --git a/internal/system/dbus/testdata/org.chromium.Chromium.json b/internal/system/dbus/testdata/org.chromium.Chromium.json deleted file mode 100644 index be027f39..00000000 --- a/internal/system/dbus/testdata/org.chromium.Chromium.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "talk":[ - "org.freedesktop.Notifications", - "org.freedesktop.FileManager1", - "org.freedesktop.ScreenSaver", - "org.freedesktop.secrets", - "org.kde.kwalletd5", - "org.kde.kwalletd6", - "org.gnome.SessionManager" - ], - "own":[ - "org.chromium.Chromium.*", - "org.mpris.MediaPlayer2.org.chromium.Chromium.*", - "org.mpris.MediaPlayer2.chromium.*" - ], - "call":{ - "org.freedesktop.portal.*":"*" - }, - "broadcast":{ - "org.freedesktop.portal.*":"@/org/freedesktop/portal/*" - }, - - "filter":true -} diff --git a/internal/system/dbus/testdata/uk.gensokyo.CrashTestDummy.json b/internal/system/dbus/testdata/uk.gensokyo.CrashTestDummy.json deleted file mode 100644 index 43272f67..00000000 --- a/internal/system/dbus/testdata/uk.gensokyo.CrashTestDummy.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "see": [ - "uk.gensokyo.CrashTestDummy1" - ], - "talk":[ - "org.freedesktop.Notifications" - ], - "own":[ - "uk.gensokyo.CrashTestDummy.*", - "org.mpris.MediaPlayer2.uk.gensokyo.CrashTestDummy.*" - ], - "call":{ - "org.freedesktop.portal.*":"*" - }, - "broadcast":{ - "org.freedesktop.portal.*":"@/org/freedesktop/portal/*" - }, - - "log": true, - "filter":true -} |
