aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--system/dbus.go35
-rw-r--r--system/dbus_test.go371
-rw-r--r--system/dispatcher.go30
-rw-r--r--system/dispatcher_test.go81
4 files changed, 499 insertions, 18 deletions
diff --git a/system/dbus.go b/system/dbus.go
index 12841e6a..819f2495 100644
--- a/system/dbus.go
+++ b/system/dbus.go
@@ -43,10 +43,10 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st
d.system = system != nil
var sessionBus, systemBus dbus.ProxyPair
- sessionBus[0], systemBus[0] = dbus.Address()
+ sessionBus[0], systemBus[0] = sys.dbusAddress()
sessionBus[1], systemBus[1] = sessionPath, systemPath
d.out = &linePrefixWriter{println: log.Println, prefix: "(dbus) ", buf: new(strings.Builder)}
- if final, err := dbus.Finalise(sessionBus, systemBus, session, system); err != nil {
+ if final, err := sys.dbusFinalise(sessionBus, systemBus, session, system); err != nil {
if errors.Is(err, syscall.EINVAL) {
return nil, newOpErrorMessage("dbus", err,
"message bus proxy configuration contains NUL byte", false)
@@ -54,14 +54,14 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st
return nil, newOpErrorMessage("dbus", err,
fmt.Sprintf("cannot finalise message bus proxy: %v", err), false)
} else {
- if msg.IsVerbose() {
- msg.Verbose("session bus proxy:", session.Args(sessionBus))
+ if sys.isVerbose() {
+ sys.verbose("session bus proxy:", session.Args(sessionBus))
if system != nil {
- msg.Verbose("system bus proxy:", system.Args(systemBus))
+ sys.verbose("system bus proxy:", system.Args(systemBus))
}
// this calls the argsWt String method
- msg.Verbose("message bus proxy final args:", final.WriterTo)
+ sys.verbose("message bus proxy final args:", final.WriterTo)
}
d.final = final
@@ -85,29 +85,32 @@ type DBusProxyOp struct {
func (d *DBusProxyOp) Type() Enablement { return Process }
func (d *DBusProxyOp) apply(sys *I) error {
- msg.Verbosef("session bus proxy on %q for upstream %q", d.final.Session[1], d.final.Session[0])
+ sys.verbosef("session bus proxy on %q for upstream %q", d.final.Session[1], d.final.Session[0])
if d.system {
- msg.Verbosef("system bus proxy on %q for upstream %q", d.final.System[1], d.final.System[0])
+ sys.verbosef("system bus proxy on %q for upstream %q", d.final.System[1], d.final.System[0])
}
d.proxy = dbus.New(sys.ctx, d.final, d.out)
- if err := d.proxy.Start(); err != nil {
+ if err := sys.dbusProxyStart(d.proxy); err != nil {
d.out.Dump()
return newOpErrorMessage("dbus", err,
fmt.Sprintf("cannot start message bus proxy: %v", err), false)
}
- msg.Verbose("starting message bus proxy", d.proxy)
+ sys.verbose("starting message bus proxy", d.proxy)
return nil
}
-func (d *DBusProxyOp) revert(*I, *Criteria) error {
+func (d *DBusProxyOp) revert(sys *I, _ *Criteria) error {
// criteria ignored here since dbus is always process-scoped
- msg.Verbose("terminating message bus proxy")
- d.proxy.Close()
- defer msg.Verbose("message bus proxy exit")
- err := d.proxy.Wait()
+ sys.verbose("terminating message bus proxy")
+ sys.dbusProxyClose(d.proxy)
+
+ exitMessage := "message bus proxy exit"
+ defer func() { sys.verbose(exitMessage) }()
+
+ err := sys.dbusProxyWait(d.proxy)
if errors.Is(err, context.Canceled) {
- msg.Verbose("message bus proxy canceled upstream")
+ exitMessage = "message bus proxy canceled upstream"
err = nil
}
return newOpErrorMessage("dbus", err,
diff --git a/system/dbus_test.go b/system/dbus_test.go
index d5d053b7..e3ace3d5 100644
--- a/system/dbus_test.go
+++ b/system/dbus_test.go
@@ -1,13 +1,384 @@
package system
import (
+ "context"
"reflect"
"slices"
+ "strconv"
"strings"
"syscall"
"testing"
+
+ "hakurei.app/container/stub"
+ "hakurei.app/helper"
+ "hakurei.app/system/dbus"
)
+func TestDBusProxyOp(t *testing.T) {
+ checkOpBehaviour(t, []opBehaviourTestCase{
+ {"dbusProxyStart", 0xdeadbeef, 0xff, &DBusProxyOp{
+ final: dbusNewFinalSample(4),
+ out: new(linePrefixWriter), // panics on write
+ system: true,
+ }, []stub.Call{
+ call("verbosef", stub.ExpectArgs{"session bus proxy on %q for upstream %q", []any{"/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus", "unix:path=/run/user/1000/bus"}}, nil, nil),
+ call("verbosef", stub.ExpectArgs{"system bus proxy on %q for upstream %q", []any{"/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket", "unix:path=/run/dbus/system_bus_socket"}}, nil, nil),
+ call("dbusProxyStart", stub.ExpectArgs{dbusNewFinalSample(4)}, nil, stub.UniqueError(2)),
+ }, &OpError{
+ Op: "dbus", Err: stub.UniqueError(2),
+ Msg: "cannot start message bus proxy: unique error 2 injected by the test suite",
+ }, nil, nil},
+
+ {"dbusProxyWait", 0xdeadbeef, 0xff, &DBusProxyOp{
+ final: dbusNewFinalSample(3),
+ }, []stub.Call{
+ call("verbosef", stub.ExpectArgs{"session bus proxy on %q for upstream %q", []any{"/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus", "unix:path=/run/user/1000/bus"}}, nil, nil),
+ call("dbusProxyStart", stub.ExpectArgs{dbusNewFinalSample(3)}, nil, nil),
+ call("verbose", stub.ExpectArgs{[]any{"starting message bus proxy", ignoreValue{}}}, nil, nil),
+ }, nil, []stub.Call{
+ call("verbose", stub.ExpectArgs{[]any{"terminating message bus proxy"}}, nil, nil),
+ call("dbusProxyClose", stub.ExpectArgs{dbusNewFinalSample(3)}, nil, nil),
+ call("dbusProxyWait", stub.ExpectArgs{dbusNewFinalSample(3)}, nil, stub.UniqueError(1)),
+ call("verbose", stub.ExpectArgs{[]any{"message bus proxy exit"}}, nil, nil),
+ }, &OpError{
+ Op: "dbus", Err: stub.UniqueError(1), Revert: true,
+ Msg: "message bus proxy error: unique error 1 injected by the test suite",
+ }},
+
+ {"success dbusProxyWait cancel", 0xdeadbeef, 0xff, &DBusProxyOp{
+ final: dbusNewFinalSample(2),
+ system: true,
+ }, []stub.Call{
+ call("verbosef", stub.ExpectArgs{"session bus proxy on %q for upstream %q", []any{"/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus", "unix:path=/run/user/1000/bus"}}, nil, nil),
+ call("verbosef", stub.ExpectArgs{"system bus proxy on %q for upstream %q", []any{"/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket", "unix:path=/run/dbus/system_bus_socket"}}, nil, nil),
+ call("dbusProxyStart", stub.ExpectArgs{dbusNewFinalSample(2)}, nil, nil),
+ call("verbose", stub.ExpectArgs{[]any{"starting message bus proxy", ignoreValue{}}}, nil, nil),
+ }, nil, []stub.Call{
+ call("verbose", stub.ExpectArgs{[]any{"terminating message bus proxy"}}, nil, nil),
+ call("dbusProxyClose", stub.ExpectArgs{dbusNewFinalSample(2)}, nil, nil),
+ call("dbusProxyWait", stub.ExpectArgs{dbusNewFinalSample(2)}, nil, context.Canceled),
+ call("verbose", stub.ExpectArgs{[]any{"message bus proxy canceled upstream"}}, nil, nil),
+ }, nil},
+
+ {"success", 0xdeadbeef, 0xff, &DBusProxyOp{
+ final: dbusNewFinalSample(1),
+ system: true,
+ }, []stub.Call{
+ call("verbosef", stub.ExpectArgs{"session bus proxy on %q for upstream %q", []any{"/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus", "unix:path=/run/user/1000/bus"}}, nil, nil),
+ call("verbosef", stub.ExpectArgs{"system bus proxy on %q for upstream %q", []any{"/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket", "unix:path=/run/dbus/system_bus_socket"}}, nil, nil),
+ call("dbusProxyStart", stub.ExpectArgs{dbusNewFinalSample(1)}, nil, nil),
+ call("verbose", stub.ExpectArgs{[]any{"starting message bus proxy", ignoreValue{}}}, nil, nil),
+ }, nil, []stub.Call{
+ call("verbose", stub.ExpectArgs{[]any{"terminating message bus proxy"}}, nil, nil),
+ call("dbusProxyClose", stub.ExpectArgs{dbusNewFinalSample(1)}, nil, nil),
+ call("dbusProxyWait", stub.ExpectArgs{dbusNewFinalSample(1)}, nil, nil),
+ call("verbose", stub.ExpectArgs{[]any{"message bus proxy exit"}}, nil, nil),
+ }, nil},
+ })
+
+ checkOpsBuilder(t, "ProxyDBus", []opsBuilderTestCase{
+ {"nil session", 0xcafebabe, func(t *testing.T, sys *I) {
+ wantErr := &OpError{
+ Op: "dbus", Err: ErrDBusConfig,
+ Msg: "attempted to create message bus proxy args without session bus config",
+ }
+ if f, err := sys.ProxyDBus(nil, new(dbus.Config), "", ""); !reflect.DeepEqual(err, wantErr) {
+ t.Errorf("ProxyDBus: error = %v, want %v", err, wantErr)
+ } else if f != nil {
+ t.Errorf("ProxyDBus: f = %p", f)
+ }
+ }, nil, stub.Expect{}},
+
+ {"dbusFinalise NUL", 0xcafebabe, func(_ *testing.T, sys *I) {
+ defer func() {
+ want := "message bus proxy configuration contains NUL byte"
+ if r := recover(); r != want {
+ t.Errorf("MustProxyDBus: panic = %v, want %v", r, want)
+ }
+ }()
+
+ sys.MustProxyDBus(
+ "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus", &dbus.Config{
+ // use impossible value here as an implicit assert that it goes through the stub
+ Talk: []string{"session\x00"}, Filter: true,
+ }, "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket", &dbus.Config{
+ // use impossible value here as an implicit assert that it goes through the stub
+ Talk: []string{"system\x00"}, Filter: true,
+ })
+ }, nil, stub.Expect{Calls: []stub.Call{
+ call("dbusAddress", stub.ExpectArgs{}, [2]string{"unix:path=/run/user/1000/bus", "unix:path=/run/dbus/system_bus_socket"}, nil),
+ 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},
+ }, (*dbus.Final)(nil), syscall.EINVAL),
+ }}},
+
+ {"dbusFinalise", 0xcafebabe, func(_ *testing.T, sys *I) {
+ wantErr := &OpError{
+ Op: "dbus", Err: stub.UniqueError(0),
+ Msg: "cannot finalise message bus proxy: unique error 0 injected by the test suite",
+ }
+ if f, err := sys.ProxyDBus(
+ &dbus.Config{
+ // use impossible value here as an implicit assert that it goes through the stub
+ Talk: []string{"session\x00"}, Filter: true,
+ }, &dbus.Config{
+ // use impossible value here as an implicit assert that it goes through the stub
+ Talk: []string{"system\x00"}, Filter: true,
+ },
+ "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus",
+ "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"); !reflect.DeepEqual(err, wantErr) {
+ t.Errorf("ProxyDBus: error = %v", err)
+ } else if f != nil {
+ t.Errorf("ProxyDBus: f = %p", f)
+ }
+ }, nil, stub.Expect{Calls: []stub.Call{
+ call("dbusAddress", stub.ExpectArgs{}, [2]string{"unix:path=/run/user/1000/bus", "unix:path=/run/dbus/system_bus_socket"}, nil),
+ 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},
+ }, (*dbus.Final)(nil), stub.UniqueError(0)),
+ }}},
+
+ {"full", 0xcafebabe, func(_ *testing.T, sys *I) {
+ sys.MustProxyDBus(
+ "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus", &dbus.Config{
+ // use impossible value here as an implicit assert that it goes through the stub
+ Talk: []string{"session\x00"}, Filter: true,
+ }, "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket", &dbus.Config{
+ // use impossible value here as an implicit assert that it goes through the stub
+ Talk: []string{"system\x00"}, Filter: true,
+ })
+ }, []Op{
+ &DBusProxyOp{
+ final: dbusNewFinalSample(0),
+ system: true,
+ },
+ }, stub.Expect{Calls: []stub.Call{
+ call("dbusAddress", stub.ExpectArgs{}, [2]string{"unix:path=/run/user/1000/bus", "unix:path=/run/dbus/system_bus_socket"}, nil),
+ 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},
+ }, 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),
+ call("verbose", stub.ExpectArgs{[]any{"system bus proxy:", []string{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket", "--filter", "--talk=system\x00"}}}, nil, nil),
+ call("verbose", stub.ExpectArgs{[]any{"message bus proxy final args:", helper.MustNewCheckedArgs([]string{"unique", "value", "0", "injected", "by", "the", "test", "suite"})}}, nil, nil),
+ }}},
+ })
+
+ checkOpIs(t, []opIsTestCase{
+ {"nil", (*DBusProxyOp)(nil), (*DBusProxyOp)(nil), false},
+ {"zero", new(DBusProxyOp), new(DBusProxyOp), false},
+
+ {"system differs", &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: false,
+ }, &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, false},
+
+ {"wt differs", &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1001/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, false},
+
+ {"final system upstream differs", &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket\x00"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, false},
+
+ {"final session upstream differs", &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1001/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, false},
+
+ {"final system differs", &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.1/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, false},
+
+ {"final session differs", &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1001/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, false},
+
+ {"equals", &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, &DBusProxyOp{final: &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"unix", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{
+ "--filter", "unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus",
+ "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
+ }),
+ }, system: true,
+ }, true},
+ })
+
+ checkOpMeta(t, []opMetaTestCase{
+ {"dbus", new(DBusProxyOp),
+ Process, "/proc/nonexistent",
+ "(invalid dbus proxy)"},
+ })
+}
+
+func dbusNewFinalSample(v int) *dbus.Final {
+ return &dbus.Final{
+ Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus"},
+ System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/system_bus_socket"},
+
+ SessionUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/user/1000/bus"}}}},
+ SystemUpstream: []dbus.AddrEntry{{Method: "unix", Values: [][2]string{{"path", "/run/dbus/system_bus_socket"}}}},
+
+ WriterTo: helper.MustNewCheckedArgs([]string{"unique", "value", strconv.Itoa(v), "injected", "by", "the", "test", "suite"}),
+ }
+}
+
func TestLinePrefixWriter(t *testing.T) {
testCases := []struct {
name string
diff --git a/system/dispatcher.go b/system/dispatcher.go
index c483c818..c896de57 100644
--- a/system/dispatcher.go
+++ b/system/dispatcher.go
@@ -1,6 +1,9 @@
package system
-import "hakurei.app/system/acl"
+import (
+ "hakurei.app/system/acl"
+ "hakurei.app/system/dbus"
+)
// syscallDispatcher provides methods that make state-dependent system calls as part of their behaviour.
// syscallDispatcher is embedded in [I], so all methods must be unexported.
@@ -13,6 +16,18 @@ type syscallDispatcher interface {
// aclUpdate provides [acl.Update].
aclUpdate(name string, uid int, perms ...acl.Perm) error
+ // 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)
+ // dbusProxyStart provides the Start method of [dbus.Proxy].
+ dbusProxyStart(proxy *dbus.Proxy) error
+ // dbusProxyClose provides the Close method of [dbus.Proxy].
+ dbusProxyClose(proxy *dbus.Proxy)
+ // dbusProxyWait provides the Wait method of [dbus.Proxy].
+ dbusProxyWait(proxy *dbus.Proxy) error
+
+ isVerbose() bool
verbose(v ...any)
verbosef(format string, v ...any)
}
@@ -26,5 +41,18 @@ func (k direct) aclUpdate(name string, uid int, perms ...acl.Perm) error {
return acl.Update(name, uid, perms...)
}
+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) {
+ return dbus.Finalise(sessionBus, systemBus, session, system)
+}
+
+func (k direct) dbusProxyStart(proxy *dbus.Proxy) error { return proxy.Start() }
+func (k direct) dbusProxyClose(proxy *dbus.Proxy) { proxy.Close() }
+func (k direct) dbusProxyWait(proxy *dbus.Proxy) error { return proxy.Wait() }
+
+func (k direct) isVerbose() bool { return msg.IsVerbose() }
func (direct) verbose(v ...any) { msg.Verbose(v...) }
func (direct) verbosef(format string, v ...any) { msg.Verbosef(format, v...) }
diff --git a/system/dispatcher_test.go b/system/dispatcher_test.go
index 7c7c10c9..c23c6c58 100644
--- a/system/dispatcher_test.go
+++ b/system/dispatcher_test.go
@@ -1,12 +1,15 @@
package system
import (
+ "os"
"reflect"
"slices"
"testing"
+ "unsafe"
"hakurei.app/container/stub"
"hakurei.app/system/acl"
+ "hakurei.app/system/dbus"
)
// call initialises a [stub.Call].
@@ -197,9 +200,85 @@ func (k *kstub) aclUpdate(name string, uid int, perms ...acl.Perm) error {
stub.CheckArgReflect(k.Stub, "perms", perms, 2))
}
+func (k *kstub) dbusAddress() (session, system string) {
+ k.Helper()
+ ret := k.Expects("dbusAddress").Ret.([2]string)
+ return ret[0], ret[1]
+}
+
+func (k *kstub) dbusFinalise(sessionBus, systemBus dbus.ProxyPair, session, system *dbus.Config) (final *dbus.Final, err error) {
+ k.Helper()
+ expect := k.Expects("dbusFinalise")
+
+ final = expect.Ret.(*dbus.Final)
+ err = expect.Error(
+ stub.CheckArg(k.Stub, "sessionBus", sessionBus, 0),
+ stub.CheckArg(k.Stub, "systemBus", systemBus, 1),
+ stub.CheckArgReflect(k.Stub, "session", session, 2),
+ stub.CheckArgReflect(k.Stub, "system", system, 3))
+ if err != nil {
+ final = nil
+ }
+ return
+}
+
+func (k *kstub) dbusProxyStart(proxy *dbus.Proxy) error {
+ k.Helper()
+ return k.dbusProxySCW(k.Expects("dbusProxyStart"), proxy)
+}
+func (k *kstub) dbusProxyClose(proxy *dbus.Proxy) {
+ k.Helper()
+ if k.dbusProxySCW(k.Expects("dbusProxyClose"), proxy) != nil {
+ k.Fail()
+ }
+}
+func (k *kstub) dbusProxyWait(proxy *dbus.Proxy) error {
+ k.Helper()
+ return k.dbusProxySCW(k.Expects("dbusProxyWait"), proxy)
+}
+func (k *kstub) dbusProxySCW(expect *stub.Call, proxy *dbus.Proxy) error {
+ k.Helper()
+ v := reflect.ValueOf(proxy).Elem()
+
+ if ctxV := v.FieldByName("ctx"); ctxV.IsNil() {
+ k.Errorf("proxy: ctx = %s", ctxV.String())
+ return os.ErrInvalid
+ }
+
+ finalV := v.FieldByName("final")
+ if gotFinal := reflect.NewAt(finalV.Type(), unsafe.Pointer(finalV.UnsafeAddr())).Elem().Interface().(*dbus.Final); !reflect.DeepEqual(gotFinal, expect.Args[0]) {
+ k.Errorf("proxy: final = %#v, want %#v", gotFinal, expect.Args[0])
+ return os.ErrInvalid
+ }
+
+ outputV := v.FieldByName("output")
+ if _, ok := reflect.NewAt(outputV.Type(), unsafe.Pointer(outputV.UnsafeAddr())).Elem().Interface().(*linePrefixWriter); !ok {
+ k.Errorf("proxy: output = %s", outputV.String())
+ return os.ErrInvalid
+ }
+
+ return expect.Err
+}
+
+func (k *kstub) isVerbose() bool { k.Helper(); return k.Expects("isVerbose").Ret.(bool) }
+
+// ignoreValue marks a value to be ignored by the test suite.
+type ignoreValue struct{}
+
func (k *kstub) verbose(v ...any) {
k.Helper()
- if k.Expects("verbose").Error(
+ expect := k.Expects("verbose")
+
+ // translate ignores in v
+ if want, ok := expect.Args[0].([]any); ok && len(v) == len(want) {
+ for i, a := range want {
+ if _, ok = a.(ignoreValue); ok {
+ v[i] = ignoreValue{}
+ }
+ }
+ }
+
+ if expect.Error(
stub.CheckArgReflect(k.Stub, "v", v, 0)) != nil {
k.FailNow()
}