system: unexport Op implementations
All checks were successful
Test / Create distribution (push) Successful in 52s
Test / Sandbox (push) Successful in 3m30s
Test / Hakurei (push) Successful in 5m40s
Test / Sandbox (race detector) (push) Successful in 6m30s
Test / Hpkg (push) Successful in 7m21s
Test / Hakurei (race detector) (push) Successful in 3m22s
Test / Flake checks (push) Successful in 2m2s

None of these are valid with their zero value, and the implementations assume they are created by the builder methods. They are by all means an implementation detail and exporting them makes no sense.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-09-06 16:16:03 +09:00
parent ac81cfbedc
commit e68db7fbfc
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
12 changed files with 157 additions and 157 deletions

View File

@ -9,33 +9,33 @@ import (
"hakurei.app/system/acl" "hakurei.app/system/acl"
) )
// UpdatePerm appends [ACLUpdateOp] to [I] with the [Process] criteria. // UpdatePerm calls UpdatePermType with the [Process] criteria.
func (sys *I) UpdatePerm(path string, perms ...acl.Perm) *I { func (sys *I) UpdatePerm(path string, perms ...acl.Perm) *I {
sys.UpdatePermType(Process, path, perms...) sys.UpdatePermType(Process, path, perms...)
return sys return sys
} }
// UpdatePermType appends [ACLUpdateOp] to [I]. // UpdatePermType maintains [acl.Perms] on a file until its [Enablement] is no longer satisfied.
func (sys *I) UpdatePermType(et Enablement, path string, perms ...acl.Perm) *I { func (sys *I) UpdatePermType(et Enablement, path string, perms ...acl.Perm) *I {
sys.ops = append(sys.ops, &ACLUpdateOp{et, path, perms}) sys.ops = append(sys.ops, &aclUpdateOp{et, path, perms})
return sys return sys
} }
// ACLUpdateOp maintains [acl.Perms] on a file until its [Enablement] is no longer satisfied. // aclUpdateOp implements [I.UpdatePermType].
type ACLUpdateOp struct { type aclUpdateOp struct {
et Enablement et Enablement
path string path string
perms acl.Perms perms acl.Perms
} }
func (a *ACLUpdateOp) Type() Enablement { return a.et } func (a *aclUpdateOp) Type() Enablement { return a.et }
func (a *ACLUpdateOp) apply(sys *I) error { func (a *aclUpdateOp) apply(sys *I) error {
sys.verbose("applying ACL", a) sys.verbose("applying ACL", a)
return newOpError("acl", sys.aclUpdate(a.path, sys.uid, a.perms...), false) return newOpError("acl", sys.aclUpdate(a.path, sys.uid, a.perms...), false)
} }
func (a *ACLUpdateOp) revert(sys *I, ec *Criteria) error { func (a *aclUpdateOp) revert(sys *I, ec *Criteria) error {
if ec.hasType(a.Type()) { if ec.hasType(a.Type()) {
sys.verbose("stripping ACL", a) sys.verbose("stripping ACL", a)
err := sys.aclUpdate(a.path, sys.uid) err := sys.aclUpdate(a.path, sys.uid)
@ -51,17 +51,17 @@ func (a *ACLUpdateOp) revert(sys *I, ec *Criteria) error {
} }
} }
func (a *ACLUpdateOp) Is(o Op) bool { func (a *aclUpdateOp) Is(o Op) bool {
target, ok := o.(*ACLUpdateOp) target, ok := o.(*aclUpdateOp)
return ok && a != nil && target != nil && return ok && a != nil && target != nil &&
a.et == target.et && a.et == target.et &&
a.path == target.path && a.path == target.path &&
slices.Equal(a.perms, target.perms) slices.Equal(a.perms, target.perms)
} }
func (a *ACLUpdateOp) Path() string { return a.path } func (a *aclUpdateOp) Path() string { return a.path }
func (a *ACLUpdateOp) String() string { func (a *aclUpdateOp) String() string {
return fmt.Sprintf("%s type: %s path: %q", return fmt.Sprintf("%s type: %s path: %q",
a.perms, TypeString(a.et), a.path) a.perms, TypeString(a.et), a.path)
} }

View File

@ -12,44 +12,44 @@ import (
func TestACLUpdateOp(t *testing.T) { func TestACLUpdateOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"apply aclUpdate", 0xdeadbeef, 0xff, {"apply aclUpdate", 0xdeadbeef, 0xff,
&ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, []stub.Call{ &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"applying ACL", &ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil), call("verbose", stub.ExpectArgs{[]any{"applying ACL", &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil),
call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, stub.UniqueError(1)), call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, stub.UniqueError(1)),
}, &OpError{Op: "acl", Err: stub.UniqueError(1)}, nil, nil}, }, &OpError{Op: "acl", Err: stub.UniqueError(1)}, nil, nil},
{"revert aclUpdate", 0xdeadbeef, 0xff, {"revert aclUpdate", 0xdeadbeef, 0xff,
&ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, []stub.Call{ &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"applying ACL", &ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil), call("verbose", stub.ExpectArgs{[]any{"applying ACL", &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil),
call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil), call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"stripping ACL", &ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil), call("verbose", stub.ExpectArgs{[]any{"stripping ACL", &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil),
call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, ([]acl.Perm)(nil)}, nil, stub.UniqueError(0)), call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, ([]acl.Perm)(nil)}, nil, stub.UniqueError(0)),
}, &OpError{Op: "acl", Err: stub.UniqueError(0), Revert: true}}, }, &OpError{Op: "acl", Err: stub.UniqueError(0), Revert: true}},
{"success revert skip", 0xdeadbeef, Process, {"success revert skip", 0xdeadbeef, Process,
&ACLUpdateOp{User, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, []stub.Call{ &aclUpdateOp{User, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"applying ACL", &ACLUpdateOp{User, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil), call("verbose", stub.ExpectArgs{[]any{"applying ACL", &aclUpdateOp{User, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil),
call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil), call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"skipping ACL", &ACLUpdateOp{User, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil), call("verbose", stub.ExpectArgs{[]any{"skipping ACL", &aclUpdateOp{User, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil),
}, nil}, }, nil},
{"success revert aclUpdate ENOENT", 0xdeadbeef, 0xff, {"success revert aclUpdate ENOENT", 0xdeadbeef, 0xff,
&ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, []stub.Call{ &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"applying ACL", &ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil), call("verbose", stub.ExpectArgs{[]any{"applying ACL", &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil),
call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil), call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"stripping ACL", &ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil), call("verbose", stub.ExpectArgs{[]any{"stripping ACL", &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil),
call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, ([]acl.Perm)(nil)}, nil, &os.PathError{Op: "acl_get_file", Path: "/proc/nonexistent", Err: syscall.ENOENT}), call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, ([]acl.Perm)(nil)}, nil, &os.PathError{Op: "acl_get_file", Path: "/proc/nonexistent", Err: syscall.ENOENT}),
call("verbosef", stub.ExpectArgs{"target of ACL %s no longer exists", []any{&ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil), call("verbosef", stub.ExpectArgs{"target of ACL %s no longer exists", []any{&aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil),
}, nil}, }, nil},
{"success", 0xdeadbeef, 0xff, {"success", 0xdeadbeef, 0xff,
&ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, []stub.Call{ &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"applying ACL", &ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil), call("verbose", stub.ExpectArgs{[]any{"applying ACL", &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil),
call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil), call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, []acl.Perm{acl.Read, acl.Write, acl.Execute}}, nil, nil),
}, nil, []stub.Call{ }, nil, []stub.Call{
call("verbose", stub.ExpectArgs{[]any{"stripping ACL", &ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil), call("verbose", stub.ExpectArgs{[]any{"stripping ACL", &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{acl.Read, acl.Write, acl.Execute}}}}, nil, nil),
call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, ([]acl.Perm)(nil)}, nil, nil), call("aclUpdate", stub.ExpectArgs{"/proc/nonexistent", 0xdeadbeef, ([]acl.Perm)(nil)}, nil, nil),
}, nil}, }, nil},
}) })
@ -62,27 +62,27 @@ func TestACLUpdateOp(t *testing.T) {
UpdatePerm("/run/user/1971/hakurei", acl.Execute). UpdatePerm("/run/user/1971/hakurei", acl.Execute).
UpdatePerm("/tmp/hakurei.0/tmpdir/150", acl.Read, acl.Write, acl.Execute) UpdatePerm("/tmp/hakurei.0/tmpdir/150", acl.Read, acl.Write, acl.Execute)
}, []Op{ }, []Op{
&ACLUpdateOp{Process, "/run/user/1971/hakurei", []acl.Perm{acl.Execute}}, &aclUpdateOp{Process, "/run/user/1971/hakurei", []acl.Perm{acl.Execute}},
&ACLUpdateOp{Process, "/tmp/hakurei.0/tmpdir/150", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, &aclUpdateOp{Process, "/tmp/hakurei.0/tmpdir/150", []acl.Perm{acl.Read, acl.Write, acl.Execute}},
}, stub.Expect{}}, }, stub.Expect{}},
}) })
checkOpsBuilder(t, "UpdatePermType", []opsBuilderTestCase{ checkOpsBuilder(t, "UpdatePermType", []opsBuilderTestCase{
{"tmpdirp", 0xdeadbeef, func(_ *testing.T, sys *I) { {"tmpdirp", 0xdeadbeef, func(_ *testing.T, sys *I) {
sys.UpdatePermType(User, "/tmp/hakurei.0/tmpdir", acl.Execute) sys.UpdatePermType(User, "/tmp/hakurei.0/tmpdir", acl.Execute)
}, []Op{ }, []Op{
&ACLUpdateOp{User, "/tmp/hakurei.0/tmpdir", []acl.Perm{acl.Execute}}, &aclUpdateOp{User, "/tmp/hakurei.0/tmpdir", []acl.Perm{acl.Execute}},
}, stub.Expect{}}, }, stub.Expect{}},
{"tmpdir", 0xdeadbeef, func(_ *testing.T, sys *I) { {"tmpdir", 0xdeadbeef, func(_ *testing.T, sys *I) {
sys.UpdatePermType(User, "/tmp/hakurei.0/tmpdir/150", acl.Read, acl.Write, acl.Execute) sys.UpdatePermType(User, "/tmp/hakurei.0/tmpdir/150", acl.Read, acl.Write, acl.Execute)
}, []Op{ }, []Op{
&ACLUpdateOp{User, "/tmp/hakurei.0/tmpdir/150", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, &aclUpdateOp{User, "/tmp/hakurei.0/tmpdir/150", []acl.Perm{acl.Read, acl.Write, acl.Execute}},
}, stub.Expect{}}, }, stub.Expect{}},
{"share", 0xdeadbeef, func(_ *testing.T, sys *I) { {"share", 0xdeadbeef, func(_ *testing.T, sys *I) {
sys.UpdatePermType(Process, "/run/user/1971/hakurei/fcb8a12f7c482d183ade8288c3de78b5", acl.Execute) sys.UpdatePermType(Process, "/run/user/1971/hakurei/fcb8a12f7c482d183ade8288c3de78b5", acl.Execute)
}, []Op{ }, []Op{
&ACLUpdateOp{Process, "/run/user/1971/hakurei/fcb8a12f7c482d183ade8288c3de78b5", []acl.Perm{acl.Execute}}, &aclUpdateOp{Process, "/run/user/1971/hakurei/fcb8a12f7c482d183ade8288c3de78b5", []acl.Perm{acl.Execute}},
}, stub.Expect{}}, }, stub.Expect{}},
{"passwd", 0xdeadbeef, func(_ *testing.T, sys *I) { {"passwd", 0xdeadbeef, func(_ *testing.T, sys *I) {
@ -90,50 +90,50 @@ func TestACLUpdateOp(t *testing.T) {
UpdatePermType(Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/passwd", acl.Read). UpdatePermType(Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/passwd", acl.Read).
UpdatePermType(Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/group", acl.Read) UpdatePermType(Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/group", acl.Read)
}, []Op{ }, []Op{
&ACLUpdateOp{Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/passwd", []acl.Perm{acl.Read}}, &aclUpdateOp{Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/passwd", []acl.Perm{acl.Read}},
&ACLUpdateOp{Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/group", []acl.Perm{acl.Read}}, &aclUpdateOp{Process, "/tmp/hakurei.0/fcb8a12f7c482d183ade8288c3de78b5/group", []acl.Perm{acl.Read}},
}, stub.Expect{}}, }, stub.Expect{}},
{"wayland", 0xdeadbeef, func(_ *testing.T, sys *I) { {"wayland", 0xdeadbeef, func(_ *testing.T, sys *I) {
sys.UpdatePermType(EWayland, "/run/user/1971/wayland-0", acl.Read, acl.Write, acl.Execute) sys.UpdatePermType(EWayland, "/run/user/1971/wayland-0", acl.Read, acl.Write, acl.Execute)
}, []Op{ }, []Op{
&ACLUpdateOp{EWayland, "/run/user/1971/wayland-0", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, &aclUpdateOp{EWayland, "/run/user/1971/wayland-0", []acl.Perm{acl.Read, acl.Write, acl.Execute}},
}, stub.Expect{}}, }, stub.Expect{}},
}) })
checkOpIs(t, []opIsTestCase{ checkOpIs(t, []opIsTestCase{
{"nil", (*ACLUpdateOp)(nil), (*ACLUpdateOp)(nil), false}, {"nil", (*aclUpdateOp)(nil), (*aclUpdateOp)(nil), false},
{"zero", new(ACLUpdateOp), new(ACLUpdateOp), true}, {"zero", new(aclUpdateOp), new(aclUpdateOp), true},
{"et differs", {"et differs",
&ACLUpdateOp{ &aclUpdateOp{
EWayland, "/run/user/1971/wayland-0", EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute}, []acl.Perm{acl.Read, acl.Write, acl.Execute},
}, &ACLUpdateOp{ }, &aclUpdateOp{
EX11, "/run/user/1971/wayland-0", EX11, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute}, []acl.Perm{acl.Read, acl.Write, acl.Execute},
}, false}, }, false},
{"path differs", &ACLUpdateOp{ {"path differs", &aclUpdateOp{
EWayland, "/run/user/1971/wayland-0", EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute}, []acl.Perm{acl.Read, acl.Write, acl.Execute},
}, &ACLUpdateOp{ }, &aclUpdateOp{
EWayland, "/run/user/1971/wayland-1", EWayland, "/run/user/1971/wayland-1",
[]acl.Perm{acl.Read, acl.Write, acl.Execute}, []acl.Perm{acl.Read, acl.Write, acl.Execute},
}, false}, }, false},
{"perms differs", &ACLUpdateOp{ {"perms differs", &aclUpdateOp{
EWayland, "/run/user/1971/wayland-0", EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute}, []acl.Perm{acl.Read, acl.Write, acl.Execute},
}, &ACLUpdateOp{ }, &aclUpdateOp{
EWayland, "/run/user/1971/wayland-0", EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write}, []acl.Perm{acl.Read, acl.Write},
}, false}, }, false},
{"equals", &ACLUpdateOp{ {"equals", &aclUpdateOp{
EWayland, "/run/user/1971/wayland-0", EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute}, []acl.Perm{acl.Read, acl.Write, acl.Execute},
}, &ACLUpdateOp{ }, &aclUpdateOp{
EWayland, "/run/user/1971/wayland-0", EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute}, []acl.Perm{acl.Read, acl.Write, acl.Execute},
}, true}, }, true},
@ -141,42 +141,42 @@ func TestACLUpdateOp(t *testing.T) {
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"clear", {"clear",
&ACLUpdateOp{Process, "/proc/nonexistent", []acl.Perm{}}, &aclUpdateOp{Process, "/proc/nonexistent", []acl.Perm{}},
Process, "/proc/nonexistent", Process, "/proc/nonexistent",
`--- type: process path: "/proc/nonexistent"`}, `--- type: process path: "/proc/nonexistent"`},
{"read", {"read",
&ACLUpdateOp{User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/0", []acl.Perm{acl.Read}}, &aclUpdateOp{User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/0", []acl.Perm{acl.Read}},
User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/0", User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/0",
`r-- type: user path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/0"`}, `r-- type: user path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/0"`},
{"write", {"write",
&ACLUpdateOp{User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/1", []acl.Perm{acl.Write}}, &aclUpdateOp{User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/1", []acl.Perm{acl.Write}},
User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/1", User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/1",
`-w- type: user path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/1"`}, `-w- type: user path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/1"`},
{"execute", {"execute",
&ACLUpdateOp{User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/2", []acl.Perm{acl.Execute}}, &aclUpdateOp{User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/2", []acl.Perm{acl.Execute}},
User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/2", User, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/2",
`--x type: user path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/2"`}, `--x type: user path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/2"`},
{"wayland", {"wayland",
&ACLUpdateOp{EWayland, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/wayland", []acl.Perm{acl.Read, acl.Write}}, &aclUpdateOp{EWayland, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/wayland", []acl.Perm{acl.Read, acl.Write}},
EWayland, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/wayland", EWayland, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/wayland",
`rw- type: wayland path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/wayland"`}, `rw- type: wayland path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/wayland"`},
{"x11", {"x11",
&ACLUpdateOp{EX11, "/tmp/.X11-unix/X0", []acl.Perm{acl.Read, acl.Execute}}, &aclUpdateOp{EX11, "/tmp/.X11-unix/X0", []acl.Perm{acl.Read, acl.Execute}},
EX11, "/tmp/.X11-unix/X0", EX11, "/tmp/.X11-unix/X0",
`r-x type: x11 path: "/tmp/.X11-unix/X0"`}, `r-x type: x11 path: "/tmp/.X11-unix/X0"`},
{"dbus", {"dbus",
&ACLUpdateOp{EDBus, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/bus", []acl.Perm{acl.Write, acl.Execute}}, &aclUpdateOp{EDBus, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/bus", []acl.Perm{acl.Write, acl.Execute}},
EDBus, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/bus", EDBus, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/bus",
`-wx type: dbus path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/bus"`}, `-wx type: dbus path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/bus"`},
{"pulseaudio", {"pulseaudio",
&ACLUpdateOp{EPulse, "/run/user/1971/hakurei/27d81d567f8fae7f33278eec45da9446/pulse", []acl.Perm{acl.Read, acl.Write, acl.Execute}}, &aclUpdateOp{EPulse, "/run/user/1971/hakurei/27d81d567f8fae7f33278eec45da9446/pulse", []acl.Perm{acl.Read, acl.Write, acl.Execute}},
EPulse, "/run/user/1971/hakurei/27d81d567f8fae7f33278eec45da9446/pulse", EPulse, "/run/user/1971/hakurei/27d81d567f8fae7f33278eec45da9446/pulse",
`rwx type: pulseaudio path: "/run/user/1971/hakurei/27d81d567f8fae7f33278eec45da9446/pulse"`}, `rwx type: pulseaudio path: "/run/user/1971/hakurei/27d81d567f8fae7f33278eec45da9446/pulse"`},
}) })

View File

@ -28,9 +28,10 @@ func (sys *I) MustProxyDBus(sessionPath string, session *dbus.Config, systemPath
} }
} }
// ProxyDBus finalises configuration and appends [DBusProxyOp] to [I]. // 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 string) (func(), error) { func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath string) (func(), error) {
d := new(DBusProxyOp) d := new(dbusProxyOp)
// session bus is required as otherwise this is effectively a very expensive noop // session bus is required as otherwise this is effectively a very expensive noop
if session == nil { if session == nil {
@ -70,9 +71,8 @@ func (sys *I) ProxyDBus(session, system *dbus.Config, sessionPath, systemPath st
return d.out.Dump, nil return d.out.Dump, nil
} }
// DBusProxyOp starts xdg-dbus-proxy via [dbus] and terminates it on revert. // dbusProxyOp implements [I.ProxyDBus].
// This [Op] is always [Process] scoped. type dbusProxyOp struct {
type DBusProxyOp struct {
proxy *dbus.Proxy // populated during apply proxy *dbus.Proxy // populated during apply
final *dbus.Final final *dbus.Final
@ -81,9 +81,9 @@ type DBusProxyOp struct {
system bool system bool
} }
func (d *DBusProxyOp) Type() Enablement { return Process } func (d *dbusProxyOp) Type() Enablement { return Process }
func (d *DBusProxyOp) apply(sys *I) error { func (d *dbusProxyOp) apply(sys *I) error {
sys.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 { if d.system {
sys.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])
@ -99,7 +99,7 @@ func (d *DBusProxyOp) apply(sys *I) error {
return nil return nil
} }
func (d *DBusProxyOp) revert(sys *I, _ *Criteria) error { func (d *dbusProxyOp) revert(sys *I, _ *Criteria) error {
// criteria ignored here since dbus is always process-scoped // criteria ignored here since dbus is always process-scoped
sys.verbose("terminating message bus proxy") sys.verbose("terminating message bus proxy")
sys.dbusProxyClose(d.proxy) sys.dbusProxyClose(d.proxy)
@ -116,8 +116,8 @@ func (d *DBusProxyOp) revert(sys *I, _ *Criteria) error {
fmt.Sprintf("message bus proxy error: %v", err), true) fmt.Sprintf("message bus proxy error: %v", err), true)
} }
func (d *DBusProxyOp) Is(o Op) bool { func (d *dbusProxyOp) Is(o Op) bool {
target, ok := o.(*DBusProxyOp) target, ok := o.(*dbusProxyOp)
return ok && d != nil && target != nil && return ok && d != nil && target != nil &&
d.system == target.system && d.system == target.system &&
d.final != nil && target.final != nil && d.final != nil && target.final != nil &&
@ -128,8 +128,8 @@ func (d *DBusProxyOp) Is(o Op) bool {
reflect.DeepEqual(d.final.WriterTo, target.final.WriterTo) reflect.DeepEqual(d.final.WriterTo, target.final.WriterTo)
} }
func (d *DBusProxyOp) Path() string { return container.Nonexistent } func (d *dbusProxyOp) Path() string { return container.Nonexistent }
func (d *DBusProxyOp) String() string { return d.proxy.String() } func (d *dbusProxyOp) String() string { return d.proxy.String() }
const ( const (
// lpwSizeThreshold is the threshold of bytes written to linePrefixWriter which, // lpwSizeThreshold is the threshold of bytes written to linePrefixWriter which,

View File

@ -16,7 +16,7 @@ import (
func TestDBusProxyOp(t *testing.T) { func TestDBusProxyOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{ checkOpBehaviour(t, []opBehaviourTestCase{
{"dbusProxyStart", 0xdeadbeef, 0xff, &DBusProxyOp{ {"dbusProxyStart", 0xdeadbeef, 0xff, &dbusProxyOp{
final: dbusNewFinalSample(4), final: dbusNewFinalSample(4),
out: new(linePrefixWriter), // panics on write out: new(linePrefixWriter), // panics on write
system: true, system: true,
@ -29,7 +29,7 @@ func TestDBusProxyOp(t *testing.T) {
Msg: "cannot start message bus proxy: unique error 2 injected by the test suite", Msg: "cannot start message bus proxy: unique error 2 injected by the test suite",
}, nil, nil}, }, nil, nil},
{"dbusProxyWait", 0xdeadbeef, 0xff, &DBusProxyOp{ {"dbusProxyWait", 0xdeadbeef, 0xff, &dbusProxyOp{
final: dbusNewFinalSample(3), final: dbusNewFinalSample(3),
}, []stub.Call{ }, []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{"session bus proxy on %q for upstream %q", []any{"/tmp/hakurei.0/99dd71ee2146369514e0d10783368f8f/bus", "unix:path=/run/user/1000/bus"}}, nil, nil),
@ -45,7 +45,7 @@ func TestDBusProxyOp(t *testing.T) {
Msg: "message bus proxy error: unique error 1 injected by the test suite", Msg: "message bus proxy error: unique error 1 injected by the test suite",
}}, }},
{"success dbusProxyWait cancel", 0xdeadbeef, 0xff, &DBusProxyOp{ {"success dbusProxyWait cancel", 0xdeadbeef, 0xff, &dbusProxyOp{
final: dbusNewFinalSample(2), final: dbusNewFinalSample(2),
system: true, system: true,
}, []stub.Call{ }, []stub.Call{
@ -60,7 +60,7 @@ func TestDBusProxyOp(t *testing.T) {
call("verbose", stub.ExpectArgs{[]any{"message bus proxy canceled upstream"}}, nil, nil), call("verbose", stub.ExpectArgs{[]any{"message bus proxy canceled upstream"}}, nil, nil),
}, nil}, }, nil},
{"success", 0xdeadbeef, 0xff, &DBusProxyOp{ {"success", 0xdeadbeef, 0xff, &dbusProxyOp{
final: dbusNewFinalSample(1), final: dbusNewFinalSample(1),
system: true, system: true,
}, []stub.Call{ }, []stub.Call{
@ -154,7 +154,7 @@ func TestDBusProxyOp(t *testing.T) {
Talk: []string{"system\x00"}, Filter: true, Talk: []string{"system\x00"}, Filter: true,
}) })
}, []Op{ }, []Op{
&DBusProxyOp{ &dbusProxyOp{
final: dbusNewFinalSample(0), final: dbusNewFinalSample(0),
system: true, system: true,
}, },
@ -174,10 +174,10 @@ func TestDBusProxyOp(t *testing.T) {
}) })
checkOpIs(t, []opIsTestCase{ checkOpIs(t, []opIsTestCase{
{"nil", (*DBusProxyOp)(nil), (*DBusProxyOp)(nil), false}, {"nil", (*dbusProxyOp)(nil), (*dbusProxyOp)(nil), false},
{"zero", new(DBusProxyOp), new(DBusProxyOp), false}, {"zero", new(dbusProxyOp), new(dbusProxyOp), false},
{"system differs", &DBusProxyOp{final: &dbus.Final{ {"system differs", &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -189,7 +189,7 @@ func TestDBusProxyOp(t *testing.T) {
"--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket", "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
}), }),
}, system: false, }, system: false,
}, &DBusProxyOp{final: &dbus.Final{ }, &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -203,7 +203,7 @@ func TestDBusProxyOp(t *testing.T) {
}, system: true, }, system: true,
}, false}, }, false},
{"wt differs", &DBusProxyOp{final: &dbus.Final{ {"wt differs", &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -215,7 +215,7 @@ func TestDBusProxyOp(t *testing.T) {
"--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket", "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
}), }),
}, system: true, }, system: true,
}, &DBusProxyOp{final: &dbus.Final{ }, &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -229,7 +229,7 @@ func TestDBusProxyOp(t *testing.T) {
}, system: true, }, system: true,
}, false}, }, false},
{"final system upstream differs", &DBusProxyOp{final: &dbus.Final{ {"final system upstream differs", &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -241,7 +241,7 @@ func TestDBusProxyOp(t *testing.T) {
"--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket", "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
}), }),
}, system: true, }, system: true,
}, &DBusProxyOp{final: &dbus.Final{ }, &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -255,7 +255,7 @@ func TestDBusProxyOp(t *testing.T) {
}, system: true, }, system: true,
}, false}, }, false},
{"final session upstream differs", &DBusProxyOp{final: &dbus.Final{ {"final session upstream differs", &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -267,7 +267,7 @@ func TestDBusProxyOp(t *testing.T) {
"--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket", "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
}), }),
}, system: true, }, system: true,
}, &DBusProxyOp{final: &dbus.Final{ }, &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -281,7 +281,7 @@ func TestDBusProxyOp(t *testing.T) {
}, system: true, }, system: true,
}, false}, }, false},
{"final system differs", &DBusProxyOp{final: &dbus.Final{ {"final system differs", &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.1/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -293,7 +293,7 @@ func TestDBusProxyOp(t *testing.T) {
"--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket", "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
}), }),
}, system: true, }, system: true,
}, &DBusProxyOp{final: &dbus.Final{ }, &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -307,7 +307,7 @@ func TestDBusProxyOp(t *testing.T) {
}, system: true, }, system: true,
}, false}, }, false},
{"final session differs", &DBusProxyOp{final: &dbus.Final{ {"final session differs", &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1001/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -319,7 +319,7 @@ func TestDBusProxyOp(t *testing.T) {
"--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket", "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
}), }),
}, system: true, }, system: true,
}, &DBusProxyOp{final: &dbus.Final{ }, &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -333,7 +333,7 @@ func TestDBusProxyOp(t *testing.T) {
}, system: true, }, system: true,
}, false}, }, false},
{"equals", &DBusProxyOp{final: &dbus.Final{ {"equals", &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -345,7 +345,7 @@ func TestDBusProxyOp(t *testing.T) {
"--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket", "--filter", "unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket",
}), }),
}, system: true, }, system: true,
}, &DBusProxyOp{final: &dbus.Final{ }, &dbusProxyOp{final: &dbus.Final{
Session: dbus.ProxyPair{"unix:path=/run/user/1000/bus", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/bus"}, 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"}, System: dbus.ProxyPair{"unix:path=/run/dbus/system_bus_socket", "/tmp/hakurei.0/b186c281d9e83a39afdc66d964ef99c6/system_bus_socket"},
@ -361,7 +361,7 @@ func TestDBusProxyOp(t *testing.T) {
}) })
checkOpMeta(t, []opMetaTestCase{ checkOpMeta(t, []opMetaTestCase{
{"dbus", new(DBusProxyOp), {"dbus", new(dbusProxyOp),
Process, "/proc/nonexistent", Process, "/proc/nonexistent",
"(invalid dbus proxy)"}, "(invalid dbus proxy)"},
}) })

View File

@ -5,29 +5,29 @@ import (
"os" "os"
) )
// Link appends [HardlinkOp] to [I] the [Process] criteria. // Link calls LinkFileType with the [Process] criteria.
func (sys *I) Link(oldname, newname string) *I { return sys.LinkFileType(Process, oldname, newname) } func (sys *I) Link(oldname, newname string) *I { return sys.LinkFileType(Process, oldname, newname) }
// LinkFileType appends [HardlinkOp] to [I]. // LinkFileType maintains a hardlink until its [Enablement] is no longer satisfied.
func (sys *I) LinkFileType(et Enablement, oldname, newname string) *I { func (sys *I) LinkFileType(et Enablement, oldname, newname string) *I {
sys.ops = append(sys.ops, &HardlinkOp{et, newname, oldname}) sys.ops = append(sys.ops, &hardlinkOp{et, newname, oldname})
return sys return sys
} }
// HardlinkOp maintains a hardlink until its [Enablement] is no longer satisfied. // hardlinkOp implements [I.LinkFileType].
type HardlinkOp struct { type hardlinkOp struct {
et Enablement et Enablement
dst, src string dst, src string
} }
func (l *HardlinkOp) Type() Enablement { return l.et } func (l *hardlinkOp) Type() Enablement { return l.et }
func (l *HardlinkOp) apply(*I) error { func (l *hardlinkOp) apply(*I) error {
msg.Verbose("linking", l) msg.Verbose("linking", l)
return newOpError("hardlink", os.Link(l.src, l.dst), false) return newOpError("hardlink", os.Link(l.src, l.dst), false)
} }
func (l *HardlinkOp) revert(_ *I, ec *Criteria) error { func (l *hardlinkOp) revert(_ *I, ec *Criteria) error {
if ec.hasType(l.Type()) { if ec.hasType(l.Type()) {
msg.Verbosef("removing hard link %q", l.dst) msg.Verbosef("removing hard link %q", l.dst)
return newOpError("hardlink", os.Remove(l.dst), true) return newOpError("hardlink", os.Remove(l.dst), true)
@ -37,10 +37,10 @@ func (l *HardlinkOp) revert(_ *I, ec *Criteria) error {
} }
} }
func (l *HardlinkOp) Is(o Op) bool { func (l *hardlinkOp) Is(o Op) bool {
target, ok := o.(*HardlinkOp) target, ok := o.(*hardlinkOp)
return ok && l != nil && target != nil && *l == *target return ok && l != nil && target != nil && *l == *target
} }
func (l *HardlinkOp) Path() string { return l.src } func (l *hardlinkOp) Path() string { return l.src }
func (l *HardlinkOp) String() string { return fmt.Sprintf("%q from %q", l.dst, l.src) } func (l *hardlinkOp) String() string { return fmt.Sprintf("%q from %q", l.dst, l.src) }

View File

@ -6,30 +6,29 @@ import (
"os" "os"
) )
// Ensure appends [MkdirOp] to [I] with its [Enablement] ignored. // Ensure ensures the existence of a directory.
func (sys *I) Ensure(name string, perm os.FileMode) *I { func (sys *I) Ensure(name string, perm os.FileMode) *I {
sys.ops = append(sys.ops, &MkdirOp{User, name, perm, false}) sys.ops = append(sys.ops, &mkdirOp{User, name, perm, false})
return sys return sys
} }
// Ephemeral appends an ephemeral [MkdirOp] to [I]. // Ephemeral ensures the existence of a directory until its [Enablement] is no longer satisfied.
func (sys *I) Ephemeral(et Enablement, name string, perm os.FileMode) *I { func (sys *I) Ephemeral(et Enablement, name string, perm os.FileMode) *I {
sys.ops = append(sys.ops, &MkdirOp{et, name, perm, true}) sys.ops = append(sys.ops, &mkdirOp{et, name, perm, true})
return sys return sys
} }
// MkdirOp ensures the existence of a directory. // mkdirOp implements [I.Ensure] and [I.Ephemeral].
// For ephemeral, the directory is destroyed once [Enablement] is no longer satisfied. type mkdirOp struct {
type MkdirOp struct {
et Enablement et Enablement
path string path string
perm os.FileMode perm os.FileMode
ephemeral bool ephemeral bool
} }
func (m *MkdirOp) Type() Enablement { return m.et } func (m *mkdirOp) Type() Enablement { return m.et }
func (m *MkdirOp) apply(*I) error { func (m *mkdirOp) apply(*I) error {
msg.Verbose("ensuring directory", m) msg.Verbose("ensuring directory", m)
// create directory // create directory
@ -44,7 +43,7 @@ func (m *MkdirOp) apply(*I) error {
} }
} }
func (m *MkdirOp) revert(_ *I, ec *Criteria) error { func (m *mkdirOp) revert(_ *I, ec *Criteria) error {
if !m.ephemeral { if !m.ephemeral {
// skip non-ephemeral dir and do not log anything // skip non-ephemeral dir and do not log anything
return nil return nil
@ -59,14 +58,14 @@ func (m *MkdirOp) revert(_ *I, ec *Criteria) error {
} }
} }
func (m *MkdirOp) Is(o Op) bool { func (m *mkdirOp) Is(o Op) bool {
target, ok := o.(*MkdirOp) target, ok := o.(*mkdirOp)
return ok && m != nil && target != nil && *m == *target return ok && m != nil && target != nil && *m == *target
} }
func (m *MkdirOp) Path() string { return m.path } func (m *mkdirOp) Path() string { return m.path }
func (m *MkdirOp) String() string { func (m *mkdirOp) String() string {
t := "ensure" t := "ensure"
if m.ephemeral { if m.ephemeral {
t = TypeString(m.Type()) t = TypeString(m.Type())

View File

@ -21,7 +21,7 @@ func TestEnsure(t *testing.T) {
t.Run(tc.name+"_"+tc.perm.String(), func(t *testing.T) { t.Run(tc.name+"_"+tc.perm.String(), func(t *testing.T) {
sys := New(t.Context(), 150) sys := New(t.Context(), 150)
sys.Ensure(tc.name, tc.perm) sys.Ensure(tc.name, tc.perm)
(&tcOp{User, tc.name}).test(t, sys.ops, []Op{&MkdirOp{User, tc.name, tc.perm, false}}, "Ensure") (&tcOp{User, tc.name}).test(t, sys.ops, []Op{&mkdirOp{User, tc.name, tc.perm, false}}, "Ensure")
}) })
} }
} }
@ -38,7 +38,7 @@ func TestEphemeral(t *testing.T) {
t.Run(tc.path+"_"+tc.perm.String()+"_"+TypeString(tc.et), func(t *testing.T) { t.Run(tc.path+"_"+tc.perm.String()+"_"+TypeString(tc.et), func(t *testing.T) {
sys := New(t.Context(), 150) sys := New(t.Context(), 150)
sys.Ephemeral(tc.et, tc.path, tc.perm) sys.Ephemeral(tc.et, tc.path, tc.perm)
tc.test(t, sys.ops, []Op{&MkdirOp{tc.et, tc.path, tc.perm, true}}, "Ephemeral") tc.test(t, sys.ops, []Op{&mkdirOp{tc.et, tc.path, tc.perm, true}}, "Ephemeral")
}) })
} }
} }
@ -60,7 +60,7 @@ func TestMkdirString(t *testing.T) {
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.want, func(t *testing.T) { t.Run(tc.want, func(t *testing.T) {
m := &MkdirOp{ m := &mkdirOp{
et: tc.et, et: tc.et,
path: container.Nonexistent, path: container.Nonexistent,
perm: 0701, perm: 0701,

View File

@ -9,16 +9,16 @@ import (
"syscall" "syscall"
) )
// CopyFile appends [TmpfileOp] to [I]. // CopyFile reads up to n bytes from src and writes the resulting byte slice to payloadP.
func (sys *I) CopyFile(payload *[]byte, src string, cap int, n int64) *I { func (sys *I) CopyFile(payloadP *[]byte, src string, cap int, n int64) *I {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
buf.Grow(cap) buf.Grow(cap)
sys.ops = append(sys.ops, &TmpfileOp{payload, src, n, buf}) sys.ops = append(sys.ops, &tmpfileOp{payloadP, src, n, buf})
return sys return sys
} }
// TmpfileOp reads up to n bytes from src and writes the resulting byte slice to payload. // tmpfileOp implements [I.CopyFile].
type TmpfileOp struct { type tmpfileOp struct {
payload *[]byte payload *[]byte
src string src string
@ -26,8 +26,9 @@ type TmpfileOp struct {
buf *bytes.Buffer buf *bytes.Buffer
} }
func (t *TmpfileOp) Type() Enablement { return Process } func (t *tmpfileOp) Type() Enablement { return Process }
func (t *TmpfileOp) apply(*I) error {
func (t *tmpfileOp) apply(*I) error {
msg.Verbose("copying", t) msg.Verbose("copying", t)
if t.payload == nil { if t.payload == nil {
@ -55,12 +56,12 @@ func (t *TmpfileOp) apply(*I) error {
*t.payload = t.buf.Bytes() *t.payload = t.buf.Bytes()
return nil return nil
} }
func (t *TmpfileOp) revert(*I, *Criteria) error { t.buf.Reset(); return nil } func (t *tmpfileOp) revert(*I, *Criteria) error { t.buf.Reset(); return nil }
func (t *TmpfileOp) Is(o Op) bool { func (t *tmpfileOp) Is(o Op) bool {
target, ok := o.(*TmpfileOp) target, ok := o.(*tmpfileOp)
return ok && t != nil && target != nil && return ok && t != nil && target != nil &&
t.src == target.src && t.n == target.n t.src == target.src && t.n == target.n
} }
func (t *TmpfileOp) Path() string { return t.src } func (t *tmpfileOp) Path() string { return t.src }
func (t *TmpfileOp) String() string { return fmt.Sprintf("up to %d bytes from %q", t.n, t.src) } func (t *tmpfileOp) String() string { return fmt.Sprintf("up to %d bytes from %q", t.n, t.src) }

View File

@ -18,7 +18,7 @@ func TestCopyFile(t *testing.T) {
sys := New(t.Context(), 150) sys := New(t.Context(), 150)
sys.CopyFile(new([]byte), tc.path, tc.cap, tc.n) sys.CopyFile(new([]byte), tc.path, tc.cap, tc.n)
tc.test(t, sys.ops, []Op{ tc.test(t, sys.ops, []Op{
&TmpfileOp{nil, tc.path, tc.n, nil}, &tmpfileOp{nil, tc.path, tc.n, nil},
}, "CopyFile") }, "CopyFile")
}) })
} }
@ -36,7 +36,7 @@ func TestLink(t *testing.T) {
sys := New(t.Context(), 150) sys := New(t.Context(), 150)
sys.Link(tc.src, tc.dst) sys.Link(tc.src, tc.dst)
(&tcOp{Process, tc.src}).test(t, sys.ops, []Op{ (&tcOp{Process, tc.src}).test(t, sys.ops, []Op{
&HardlinkOp{Process, tc.dst, tc.src}, &hardlinkOp{Process, tc.dst, tc.src},
}, "Link") }, "Link")
}) })
} }
@ -55,7 +55,7 @@ func TestLinkFileType(t *testing.T) {
sys := New(t.Context(), 150) sys := New(t.Context(), 150)
sys.LinkFileType(tc.et, tc.path, tc.dst) sys.LinkFileType(tc.et, tc.path, tc.dst)
tc.test(t, sys.ops, []Op{ tc.test(t, sys.ops, []Op{
&HardlinkOp{tc.et, tc.dst, tc.path}, &hardlinkOp{tc.et, tc.dst, tc.path},
}, "LinkFileType") }, "LinkFileType")
}) })
} }
@ -73,7 +73,7 @@ func TestTmpfile_String(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.want, func(t *testing.T) { t.Run(tc.want, func(t *testing.T) {
if got := (&TmpfileOp{src: tc.src, n: tc.n}).String(); got != tc.want { if got := (&tmpfileOp{src: tc.src, n: tc.n}).String(); got != tc.want {
t.Errorf("String() = %v, want %v", got, tc.want) t.Errorf("String() = %v, want %v", got, tc.want)
} }
}) })

View File

@ -9,16 +9,16 @@ import (
"hakurei.app/system/wayland" "hakurei.app/system/wayland"
) )
// Wayland appends [WaylandOp] to [I]. // Wayland maintains a wayland socket with security-context-v1 attached via [wayland].
// The socket stops accepting connections once the pipe referred to by sync is closed.
// The socket is pathname only and is destroyed on revert.
func (sys *I) Wayland(syncFd **os.File, dst, src, appID, instanceID string) *I { func (sys *I) Wayland(syncFd **os.File, dst, src, appID, instanceID string) *I {
sys.ops = append(sys.ops, &WaylandOp{syncFd, dst, src, appID, instanceID, wayland.Conn{}}) sys.ops = append(sys.ops, &waylandOp{syncFd, dst, src, appID, instanceID, wayland.Conn{}})
return sys return sys
} }
// WaylandOp maintains a wayland socket with security-context-v1 attached via [wayland]. // waylandOp implements [I.Wayland].
// The socket stops accepting connections once the pipe referred to by sync is closed. type waylandOp struct {
// The socket is pathname only and is destroyed on revert.
type WaylandOp struct {
sync **os.File sync **os.File
dst, src string dst, src string
appID, instanceID string appID, instanceID string
@ -26,9 +26,9 @@ type WaylandOp struct {
conn wayland.Conn conn wayland.Conn
} }
func (w *WaylandOp) Type() Enablement { return Process } func (w *waylandOp) Type() Enablement { return Process }
func (w *WaylandOp) apply(sys *I) error { func (w *waylandOp) apply(sys *I) error {
if w.sync == nil { if w.sync == nil {
// this is a misuse of the API; do not return a wrapped error // this is a misuse of the API; do not return a wrapped error
return errors.New("invalid sync") return errors.New("invalid sync")
@ -58,7 +58,7 @@ func (w *WaylandOp) apply(sys *I) error {
} }
} }
func (w *WaylandOp) revert(_ *I, ec *Criteria) error { func (w *waylandOp) revert(_ *I, ec *Criteria) error {
if ec.hasType(w.Type()) { if ec.hasType(w.Type()) {
msg.Verbosef("removing wayland socket on %q", w.dst) msg.Verbosef("removing wayland socket on %q", w.dst)
if err := os.Remove(w.dst); err != nil && !errors.Is(err, os.ErrNotExist) { if err := os.Remove(w.dst); err != nil && !errors.Is(err, os.ErrNotExist) {
@ -73,12 +73,12 @@ func (w *WaylandOp) revert(_ *I, ec *Criteria) error {
} }
} }
func (w *WaylandOp) Is(o Op) bool { func (w *waylandOp) Is(o Op) bool {
target, ok := o.(*WaylandOp) target, ok := o.(*waylandOp)
return ok && w != nil && target != nil && return ok && w != nil && target != nil &&
w.dst == target.dst && w.src == target.src && w.dst == target.dst && w.src == target.src &&
w.appID == target.appID && w.instanceID == target.instanceID w.appID == target.appID && w.instanceID == target.instanceID
} }
func (w *WaylandOp) Path() string { return w.dst } func (w *waylandOp) Path() string { return w.dst }
func (w *WaylandOp) String() string { return fmt.Sprintf("wayland socket at %q", w.dst) } func (w *waylandOp) String() string { return fmt.Sprintf("wayland socket at %q", w.dst) }

View File

@ -4,24 +4,24 @@ import (
"hakurei.app/system/internal/xcb" "hakurei.app/system/internal/xcb"
) )
// ChangeHosts appends [XHostOp] to [I]. // ChangeHosts inserts the target user into X11 hosts and deletes it once its [Enablement] is no longer satisfied.
func (sys *I) ChangeHosts(username string) *I { func (sys *I) ChangeHosts(username string) *I {
sys.ops = append(sys.ops, XHostOp(username)) sys.ops = append(sys.ops, xhostOp(username))
return sys return sys
} }
// XHostOp inserts the target user into X11 hosts and deletes it once its [Enablement] is no longer satisfied. // xhostOp implements [I.ChangeHosts].
type XHostOp string type xhostOp string
func (x XHostOp) Type() Enablement { return EX11 } func (x xhostOp) Type() Enablement { return EX11 }
func (x XHostOp) apply(*I) error { func (x xhostOp) apply(*I) error {
msg.Verbosef("inserting entry %s to X11", x) msg.Verbosef("inserting entry %s to X11", x)
return newOpError("xhost", return newOpError("xhost",
xcb.ChangeHosts(xcb.HostModeInsert, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)), false) xcb.ChangeHosts(xcb.HostModeInsert, xcb.FamilyServerInterpreted, "localuser\x00"+string(x)), false)
} }
func (x XHostOp) revert(_ *I, ec *Criteria) error { func (x xhostOp) revert(_ *I, ec *Criteria) error {
if ec.hasType(x.Type()) { if ec.hasType(x.Type()) {
msg.Verbosef("deleting entry %s from X11", x) msg.Verbosef("deleting entry %s from X11", x)
return newOpError("xhost", return newOpError("xhost",
@ -32,6 +32,6 @@ func (x XHostOp) revert(_ *I, ec *Criteria) error {
} }
} }
func (x XHostOp) Is(o Op) bool { target, ok := o.(XHostOp); return ok && x == target } func (x xhostOp) Is(o Op) bool { target, ok := o.(xhostOp); return ok && x == target }
func (x XHostOp) Path() string { return string(x) } func (x xhostOp) Path() string { return string(x) }
func (x XHostOp) String() string { return string("SI:localuser:" + x) } func (x xhostOp) String() string { return string("SI:localuser:" + x) }

View File

@ -11,7 +11,7 @@ func TestChangeHosts(t *testing.T) {
sys := New(t.Context(), 150) sys := New(t.Context(), 150)
sys.ChangeHosts(tc) sys.ChangeHosts(tc)
(&tcOp{EX11, tc}).test(t, sys.ops, []Op{ (&tcOp{EX11, tc}).test(t, sys.ops, []Op{
XHostOp(tc), xhostOp(tc),
}, "ChangeHosts") }, "ChangeHosts")
}) })
} }
@ -26,7 +26,7 @@ func TestXHost_String(t *testing.T) {
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.want, func(t *testing.T) { t.Run(tc.want, func(t *testing.T) {
if got := XHostOp(tc.username).String(); got != tc.want { if got := xhostOp(tc.username).String(); got != tc.want {
t.Errorf("String() = %v, want %v", got, tc.want) t.Errorf("String() = %v, want %v", got, tc.want)
} }
}) })