aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--cmd/hakurei/command.go11
-rw-r--r--hst/enablement.go77
-rw-r--r--hst/enablement_test.go49
-rw-r--r--hst/hst.go3
-rw-r--r--internal/app/app_test.go6
-rw-r--r--internal/app/finalise.go14
-rw-r--r--internal/app/process.go5
-rw-r--r--system/acl.go7
-rw-r--r--system/acl_test.go37
-rw-r--r--system/dbus.go3
-rw-r--r--system/dispatcher_test.go5
-rw-r--r--system/enablement.go47
-rw-r--r--system/enablement_test.go43
-rw-r--r--system/link.go8
-rw-r--r--system/link_test.go17
-rw-r--r--system/mkdir.go8
-rw-r--r--system/system.go13
-rw-r--r--system/system_test.go23
-rw-r--r--system/tmpfiles.go4
-rw-r--r--system/wayland.go3
-rw-r--r--system/xhost.go3
-rw-r--r--system/xhost_test.go9
22 files changed, 198 insertions, 197 deletions
diff --git a/cmd/hakurei/command.go b/cmd/hakurei/command.go
index 58e8ace1..5e123f0b 100644
--- a/cmd/hakurei/command.go
+++ b/cmd/hakurei/command.go
@@ -17,7 +17,6 @@ import (
"hakurei.app/internal"
"hakurei.app/internal/app"
"hakurei.app/internal/app/state"
- "hakurei.app/system"
"hakurei.app/system/dbus"
)
@@ -128,18 +127,18 @@ func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningE
config.Home = a
}
- var e system.Enablement
+ var e hst.Enablement
if flagWayland {
- e |= system.EWayland
+ e |= hst.EWayland
}
if flagX11 {
- e |= system.EX11
+ e |= hst.EX11
}
if flagDBus {
- e |= system.EDBus
+ e |= hst.EDBus
}
if flagPulse {
- e |= system.EPulse
+ e |= hst.EPulse
}
config.Enablements = hst.NewEnablements(e)
diff --git a/hst/enablement.go b/hst/enablement.go
index 021288b3..deaacb9d 100644
--- a/hst/enablement.go
+++ b/hst/enablement.go
@@ -2,15 +2,56 @@ package hst
import (
"encoding/json"
+ "fmt"
+ "strings"
"syscall"
+)
+
+// Enablement represents an optional host service to export to the target user.
+type Enablement byte
- "hakurei.app/system"
+const (
+ EWayland Enablement = 1 << iota
+ EX11
+ EDBus
+ EPulse
+
+ EM
)
-// NewEnablements returns the address of [system.Enablement] as [Enablements].
-func NewEnablements(e system.Enablement) *Enablements { return (*Enablements)(&e) }
+func (e Enablement) String() string {
+ switch e {
+ case 0:
+ return "(no enablements)"
+ case EWayland:
+ return "wayland"
+ case EX11:
+ return "x11"
+ case EDBus:
+ return "dbus"
+ case EPulse:
+ return "pulseaudio"
+ default:
+ buf := new(strings.Builder)
+ buf.Grow(32)
+
+ for i := Enablement(1); i < EM; i <<= 1 {
+ if e&i != 0 {
+ buf.WriteString(", " + i.String())
+ }
+ }
+
+ if buf.Len() == 0 {
+ return fmt.Sprintf("e%x", byte(e))
+ }
+ return strings.TrimPrefix(buf.String(), ", ")
+ }
+}
+
+// NewEnablements returns the address of [Enablement] as [Enablements].
+func NewEnablements(e Enablement) *Enablements { return (*Enablements)(&e) }
-// enablementsJSON is the [json] representation of the [system.Enablement] bit field.
+// enablementsJSON is the [json] representation of the [Enablement] bit field.
type enablementsJSON struct {
Wayland bool `json:"wayland,omitempty"`
X11 bool `json:"x11,omitempty"`
@@ -18,15 +59,15 @@ type enablementsJSON struct {
Pulse bool `json:"pulse,omitempty"`
}
-// Enablements is the [json] adapter for [system.Enablement].
-type Enablements system.Enablement
+// Enablements is the [json] adapter for [Enablement].
+type Enablements Enablement
-// Unwrap returns the underlying [system.Enablement].
-func (e *Enablements) Unwrap() system.Enablement {
+// Unwrap returns the underlying [Enablement].
+func (e *Enablements) Unwrap() Enablement {
if e == nil {
return 0
}
- return system.Enablement(*e)
+ return Enablement(*e)
}
func (e *Enablements) MarshalJSON() ([]byte, error) {
@@ -34,10 +75,10 @@ func (e *Enablements) MarshalJSON() ([]byte, error) {
return nil, syscall.EINVAL
}
return json.Marshal(&enablementsJSON{
- Wayland: system.Enablement(*e)&system.EWayland != 0,
- X11: system.Enablement(*e)&system.EX11 != 0,
- DBus: system.Enablement(*e)&system.EDBus != 0,
- Pulse: system.Enablement(*e)&system.EPulse != 0,
+ Wayland: Enablement(*e)&EWayland != 0,
+ X11: Enablement(*e)&EX11 != 0,
+ DBus: Enablement(*e)&EDBus != 0,
+ Pulse: Enablement(*e)&EPulse != 0,
})
}
@@ -51,18 +92,18 @@ func (e *Enablements) UnmarshalJSON(data []byte) error {
return err
}
- var ve system.Enablement
+ var ve Enablement
if v.Wayland {
- ve |= system.EWayland
+ ve |= EWayland
}
if v.X11 {
- ve |= system.EX11
+ ve |= EX11
}
if v.DBus {
- ve |= system.EDBus
+ ve |= EDBus
}
if v.Pulse {
- ve |= system.EPulse
+ ve |= EPulse
}
*e = Enablements(ve)
return nil
diff --git a/hst/enablement_test.go b/hst/enablement_test.go
index c132dd37..82059aa9 100644
--- a/hst/enablement_test.go
+++ b/hst/enablement_test.go
@@ -7,9 +7,44 @@ import (
"testing"
"hakurei.app/hst"
- "hakurei.app/system"
)
+func TestEnablementString(t *testing.T) {
+ testCases := []struct {
+ flags hst.Enablement
+ want string
+ }{
+ {0, "(no enablements)"},
+ {hst.EWayland, "wayland"},
+ {hst.EX11, "x11"},
+ {hst.EDBus, "dbus"},
+ {hst.EPulse, "pulseaudio"},
+ {hst.EWayland | hst.EX11, "wayland, x11"},
+ {hst.EWayland | hst.EDBus, "wayland, dbus"},
+ {hst.EWayland | hst.EPulse, "wayland, pulseaudio"},
+ {hst.EX11 | hst.EDBus, "x11, dbus"},
+ {hst.EX11 | hst.EPulse, "x11, pulseaudio"},
+ {hst.EDBus | hst.EPulse, "dbus, pulseaudio"},
+ {hst.EWayland | hst.EX11 | hst.EDBus, "wayland, x11, dbus"},
+ {hst.EWayland | hst.EX11 | hst.EPulse, "wayland, x11, pulseaudio"},
+ {hst.EWayland | hst.EDBus | hst.EPulse, "wayland, dbus, pulseaudio"},
+ {hst.EX11 | hst.EDBus | hst.EPulse, "x11, dbus, pulseaudio"},
+ {hst.EWayland | hst.EX11 | hst.EDBus | hst.EPulse, "wayland, x11, dbus, pulseaudio"},
+
+ {1 << 5, "e20"},
+ {1 << 6, "e40"},
+ {1 << 7, "e80"},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.want, func(t *testing.T) {
+ if got := tc.flags.String(); got != tc.want {
+ t.Errorf("String: %q, want %q", got, tc.want)
+ }
+ })
+ }
+}
+
func TestEnablements(t *testing.T) {
testCases := []struct {
name string
@@ -19,11 +54,11 @@ func TestEnablements(t *testing.T) {
}{
{"nil", nil, "null", `{"value":null,"magic":3236757504}`},
{"zero", hst.NewEnablements(0), `{}`, `{"value":{},"magic":3236757504}`},
- {"wayland", hst.NewEnablements(system.EWayland), `{"wayland":true}`, `{"value":{"wayland":true},"magic":3236757504}`},
- {"x11", hst.NewEnablements(system.EX11), `{"x11":true}`, `{"value":{"x11":true},"magic":3236757504}`},
- {"dbus", hst.NewEnablements(system.EDBus), `{"dbus":true}`, `{"value":{"dbus":true},"magic":3236757504}`},
- {"pulse", hst.NewEnablements(system.EPulse), `{"pulse":true}`, `{"value":{"pulse":true},"magic":3236757504}`},
- {"all", hst.NewEnablements(system.EWayland | system.EX11 | system.EDBus | system.EPulse), `{"wayland":true,"x11":true,"dbus":true,"pulse":true}`, `{"value":{"wayland":true,"x11":true,"dbus":true,"pulse":true},"magic":3236757504}`},
+ {"wayland", hst.NewEnablements(hst.EWayland), `{"wayland":true}`, `{"value":{"wayland":true},"magic":3236757504}`},
+ {"x11", hst.NewEnablements(hst.EX11), `{"x11":true}`, `{"value":{"x11":true},"magic":3236757504}`},
+ {"dbus", hst.NewEnablements(hst.EDBus), `{"dbus":true}`, `{"value":{"dbus":true},"magic":3236757504}`},
+ {"pulse", hst.NewEnablements(hst.EPulse), `{"pulse":true}`, `{"value":{"pulse":true},"magic":3236757504}`},
+ {"all", hst.NewEnablements(hst.EWayland | hst.EX11 | hst.EDBus | hst.EPulse), `{"wayland":true,"x11":true,"dbus":true,"pulse":true}`, `{"value":{"wayland":true,"x11":true,"dbus":true,"pulse":true},"magic":3236757504}`},
}
for _, tc := range testCases {
@@ -88,7 +123,7 @@ func TestEnablements(t *testing.T) {
})
t.Run("val", func(t *testing.T) {
- if got := hst.NewEnablements(system.EWayland | system.EPulse).Unwrap(); got != system.EWayland|system.EPulse {
+ if got := hst.NewEnablements(hst.EWayland | hst.EPulse).Unwrap(); got != hst.EWayland|hst.EPulse {
t.Errorf("Unwrap: %v", got)
}
})
diff --git a/hst/hst.go b/hst/hst.go
index c9a23d7a..5399cba0 100644
--- a/hst/hst.go
+++ b/hst/hst.go
@@ -8,7 +8,6 @@ import (
"hakurei.app/container"
"hakurei.app/container/seccomp"
- "hakurei.app/system"
"hakurei.app/system/dbus"
)
@@ -71,7 +70,7 @@ func Template() *Config {
"--ozone-platform=wayland",
},
- Enablements: NewEnablements(system.EWayland | system.EDBus | system.EPulse),
+ Enablements: NewEnablements(EWayland | EDBus | EPulse),
SessionBus: &dbus.Config{
See: nil,
diff --git a/internal/app/app_test.go b/internal/app/app_test.go
index fa85c461..585f95b2 100644
--- a/internal/app/app_test.go
+++ b/internal/app/app_test.go
@@ -121,7 +121,7 @@ func TestApp(t *testing.T) {
},
Filter: true,
},
- Enablements: hst.NewEnablements(system.EWayland | system.EDBus | system.EPulse),
+ Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse),
},
state.ID{
0xeb, 0xf0, 0x83, 0xd1,
@@ -229,7 +229,7 @@ func TestApp(t *testing.T) {
&hst.Config{
ID: "org.chromium.Chromium",
Path: m("/nix/store/yqivzpzzn7z5x0lq9hmbzygh45d8rhqd-chromium-start"),
- Enablements: hst.NewEnablements(system.EWayland | system.EDBus | system.EPulse),
+ Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse),
Shell: m("/run/current-system/sw/bin/zsh"),
Container: &hst.ContainerConfig{
@@ -288,7 +288,7 @@ func TestApp(t *testing.T) {
Ensure("/tmp/hakurei.0/tmpdir/1", 01700).UpdatePermType(system.User, "/tmp/hakurei.0/tmpdir/1", acl.Read, acl.Write, acl.Execute).
Ensure("/run/user/1971/hakurei", 0700).UpdatePermType(system.User, "/run/user/1971/hakurei", acl.Execute).
Ensure("/run/user/1971", 0700).UpdatePermType(system.User, "/run/user/1971", acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset
- UpdatePermType(system.EWayland, "/run/user/1971/wayland-0", acl.Read, acl.Write, acl.Execute).
+ UpdatePermType(hst.EWayland, "/run/user/1971/wayland-0", acl.Read, acl.Write, acl.Execute).
Ephemeral(system.Process, "/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1", 0700).UpdatePermType(system.Process, "/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1", acl.Execute).
Link("/run/user/1971/pulse/native", "/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse").
CopyFile(nil, "/home/ophestra/xdg/config/pulse/cookie", 256, 256).
diff --git a/internal/app/finalise.go b/internal/app/finalise.go
index d2c685f4..08fab978 100644
--- a/internal/app/finalise.go
+++ b/internal/app/finalise.go
@@ -241,7 +241,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C
}
// bind GPU stuff
- if config.Enablements.Unwrap()&(system.EX11|system.EWayland) != 0 {
+ if config.Enablements.Unwrap()&(hst.EX11|hst.EWayland) != 0 {
conf.Filesystem = append(conf.Filesystem, hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{Source: container.AbsFHSDev.Append("dri"), Device: true, Optional: true}})
}
// opportunistically bind kvm
@@ -353,7 +353,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C
k.env[term] = t
}
- if config.Enablements.Unwrap()&system.EWayland != 0 {
+ if config.Enablements.Unwrap()&hst.EWayland != 0 {
// outer wayland socket (usually `/run/user/%d/wayland-%d`)
var socketPath *container.Absolute
if name, ok := k.lookupEnv(wayland.WaylandDisplay); !ok {
@@ -382,11 +382,11 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C
msg.Verbose("direct wayland access, PROCEED WITH CAUTION")
share.ensureRuntimeDir()
k.container.Bind(socketPath, innerPath, 0)
- k.sys.UpdatePermType(system.EWayland, socketPath.String(), acl.Read, acl.Write, acl.Execute)
+ k.sys.UpdatePermType(hst.EWayland, socketPath.String(), acl.Read, acl.Write, acl.Execute)
}
}
- if config.Enablements.Unwrap()&system.EX11 != 0 {
+ if config.Enablements.Unwrap()&hst.EX11 != 0 {
if d, ok := k.lookupEnv(display); !ok {
return newWithMessage("DISPLAY is not set")
} else {
@@ -410,7 +410,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C
return &hst.AppError{Step: fmt.Sprintf("access X11 socket %q", socketPath), Err: err}
}
} else {
- k.sys.UpdatePermType(system.EX11, socketPath.String(), acl.Read, acl.Write, acl.Execute)
+ k.sys.UpdatePermType(hst.EX11, socketPath.String(), acl.Read, acl.Write, acl.Execute)
if !config.Container.HostAbstract {
d = "unix:" + socketPath.String()
}
@@ -423,7 +423,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C
}
}
- if config.Enablements.Unwrap()&system.EPulse != 0 {
+ if config.Enablements.Unwrap()&hst.EPulse != 0 {
// PulseAudio runtime directory (usually `/run/user/%d/pulse`)
pulseRuntimeDir := share.sc.RuntimePath.Append("pulse")
// PulseAudio socket (usually `/run/user/%d/pulse/native`)
@@ -527,7 +527,7 @@ func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.C
}
}
- if config.Enablements.Unwrap()&system.EDBus != 0 {
+ if config.Enablements.Unwrap()&hst.EDBus != 0 {
// ensure dbus session bus defaults
if config.SessionBus == nil {
config.SessionBus = dbus.NewConfig(config.ID, true, true)
diff --git a/internal/app/process.go b/internal/app/process.go
index 1b9c037b..1687c868 100644
--- a/internal/app/process.go
+++ b/internal/app/process.go
@@ -13,6 +13,7 @@ import (
"time"
"hakurei.app/container"
+ "hakurei.app/hst"
"hakurei.app/internal"
"hakurei.app/internal/app/state"
"hakurei.app/system"
@@ -146,7 +147,7 @@ func (ms mainState) beforeExit(isFault bool) {
}
}
- var rt system.Enablement
+ var rt hst.Enablement
if states, err := c.Load(); err != nil {
// it is impossible to continue from this point;
// revert per-process state here to limit damage
@@ -182,7 +183,7 @@ func (ms mainState) beforeExit(isFault bool) {
}
}
- ec |= rt ^ (system.EWayland | system.EX11 | system.EDBus | system.EPulse)
+ ec |= rt ^ (hst.EWayland | hst.EX11 | hst.EDBus | hst.EPulse)
if ms.IsVerbose() {
if ec > 0 {
ms.Verbose("reverting operations scope", system.TypeString(ec))
diff --git a/system/acl.go b/system/acl.go
index f65ba916..eb0fe8ab 100644
--- a/system/acl.go
+++ b/system/acl.go
@@ -6,6 +6,7 @@ import (
"os"
"slices"
+ "hakurei.app/hst"
"hakurei.app/system/acl"
)
@@ -16,19 +17,19 @@ func (sys *I) UpdatePerm(path string, perms ...acl.Perm) *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 hst.Enablement, path string, perms ...acl.Perm) *I {
sys.ops = append(sys.ops, &aclUpdateOp{et, path, perms})
return sys
}
// aclUpdateOp implements [I.UpdatePermType].
type aclUpdateOp struct {
- et Enablement
+ et hst.Enablement
path string
perms acl.Perms
}
-func (a *aclUpdateOp) Type() Enablement { return a.et }
+func (a *aclUpdateOp) Type() hst.Enablement { return a.et }
func (a *aclUpdateOp) apply(sys *I) error {
sys.msg.Verbose("applying ACL", a)
diff --git a/system/acl_test.go b/system/acl_test.go
index 5228b902..b288bd4a 100644
--- a/system/acl_test.go
+++ b/system/acl_test.go
@@ -6,6 +6,7 @@ import (
"testing"
"hakurei.app/container/stub"
+ "hakurei.app/hst"
"hakurei.app/system/acl"
)
@@ -94,9 +95,9 @@ func TestACLUpdateOp(t *testing.T) {
}, stub.Expect{}},
{"wayland", 0xdeadbeef, func(_ *testing.T, sys *I) {
- sys.UpdatePermType(EWayland, "/run/user/1971/wayland-0", acl.Read, acl.Write, acl.Execute)
+ sys.UpdatePermType(hst.EWayland, "/run/user/1971/wayland-0", acl.Read, acl.Write, acl.Execute)
}, []Op{
- &aclUpdateOp{EWayland, "/run/user/1971/wayland-0", []acl.Perm{acl.Read, acl.Write, acl.Execute}},
+ &aclUpdateOp{hst.EWayland, "/run/user/1971/wayland-0", []acl.Perm{acl.Read, acl.Write, acl.Execute}},
}, stub.Expect{}},
})
@@ -106,34 +107,34 @@ func TestACLUpdateOp(t *testing.T) {
{"et differs",
&aclUpdateOp{
- EWayland, "/run/user/1971/wayland-0",
+ hst.EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute},
}, &aclUpdateOp{
- EX11, "/run/user/1971/wayland-0",
+ hst.EX11, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute},
}, false},
{"path differs", &aclUpdateOp{
- EWayland, "/run/user/1971/wayland-0",
+ hst.EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute},
}, &aclUpdateOp{
- EWayland, "/run/user/1971/wayland-1",
+ hst.EWayland, "/run/user/1971/wayland-1",
[]acl.Perm{acl.Read, acl.Write, acl.Execute},
}, false},
{"perms differs", &aclUpdateOp{
- EWayland, "/run/user/1971/wayland-0",
+ hst.EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute},
}, &aclUpdateOp{
- EWayland, "/run/user/1971/wayland-0",
+ hst.EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write},
}, false},
{"equals", &aclUpdateOp{
- EWayland, "/run/user/1971/wayland-0",
+ hst.EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute},
}, &aclUpdateOp{
- EWayland, "/run/user/1971/wayland-0",
+ hst.EWayland, "/run/user/1971/wayland-0",
[]acl.Perm{acl.Read, acl.Write, acl.Execute},
}, true},
})
@@ -160,23 +161,23 @@ func TestACLUpdateOp(t *testing.T) {
`--x type: user path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/2"`},
{"wayland",
- &aclUpdateOp{EWayland, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/wayland", []acl.Perm{acl.Read, acl.Write}},
- EWayland, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/wayland",
+ &aclUpdateOp{hst.EWayland, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/wayland", []acl.Perm{acl.Read, acl.Write}},
+ hst.EWayland, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/wayland",
`rw- type: wayland path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/wayland"`},
{"x11",
- &aclUpdateOp{EX11, "/tmp/.X11-unix/X0", []acl.Perm{acl.Read, acl.Execute}},
- EX11, "/tmp/.X11-unix/X0",
+ &aclUpdateOp{hst.EX11, "/tmp/.X11-unix/X0", []acl.Perm{acl.Read, acl.Execute}},
+ hst.EX11, "/tmp/.X11-unix/X0",
`r-x type: x11 path: "/tmp/.X11-unix/X0"`},
{"dbus",
- &aclUpdateOp{EDBus, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/bus", []acl.Perm{acl.Write, acl.Execute}},
- EDBus, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/bus",
+ &aclUpdateOp{hst.EDBus, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/bus", []acl.Perm{acl.Write, acl.Execute}},
+ hst.EDBus, "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/bus",
`-wx type: dbus path: "/tmp/hakurei.0/27d81d567f8fae7f33278eec45da9446/bus"`},
{"pulseaudio",
- &aclUpdateOp{EPulse, "/run/user/1971/hakurei/27d81d567f8fae7f33278eec45da9446/pulse", []acl.Perm{acl.Read, acl.Write, acl.Execute}},
- EPulse, "/run/user/1971/hakurei/27d81d567f8fae7f33278eec45da9446/pulse",
+ &aclUpdateOp{hst.EPulse, "/run/user/1971/hakurei/27d81d567f8fae7f33278eec45da9446/pulse", []acl.Perm{acl.Read, acl.Write, acl.Execute}},
+ hst.EPulse, "/run/user/1971/hakurei/27d81d567f8fae7f33278eec45da9446/pulse",
`rwx type: pulseaudio path: "/run/user/1971/hakurei/27d81d567f8fae7f33278eec45da9446/pulse"`},
})
}
diff --git a/system/dbus.go b/system/dbus.go
index 97c567a8..2f2ac357 100644
--- a/system/dbus.go
+++ b/system/dbus.go
@@ -12,6 +12,7 @@ import (
"syscall"
"hakurei.app/container"
+ "hakurei.app/hst"
"hakurei.app/system/dbus"
)
@@ -81,7 +82,7 @@ type dbusProxyOp struct {
system bool
}
-func (d *dbusProxyOp) Type() Enablement { return Process }
+func (d *dbusProxyOp) Type() hst.Enablement { return Process }
func (d *dbusProxyOp) apply(sys *I) error {
sys.msg.Verbosef("session bus proxy on %q for upstream %q", d.final.Session[1], d.final.Session[0])
diff --git a/system/dispatcher_test.go b/system/dispatcher_test.go
index d83b46b4..eb93d16f 100644
--- a/system/dispatcher_test.go
+++ b/system/dispatcher_test.go
@@ -12,6 +12,7 @@ import (
"unsafe"
"hakurei.app/container/stub"
+ "hakurei.app/hst"
"hakurei.app/system/acl"
"hakurei.app/system/dbus"
"hakurei.app/system/internal/xcb"
@@ -26,7 +27,7 @@ func call(name string, args stub.ExpectArgs, ret any, err error) stub.Call {
type opBehaviourTestCase struct {
name string
uid int
- ec Enablement
+ ec hst.Enablement
op Op
apply []stub.Call
@@ -142,7 +143,7 @@ type opMetaTestCase struct {
name string
op Op
- wantType Enablement
+ wantType hst.Enablement
wantPath string
wantString string
}
diff --git a/system/enablement.go b/system/enablement.go
deleted file mode 100644
index 9c1342db..00000000
--- a/system/enablement.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package system
-
-import (
- "fmt"
- "strings"
-)
-
-// Enablement represents an optional host service to export to the target user.
-type Enablement byte
-
-const (
- EWayland Enablement = 1 << iota
- EX11
- EDBus
- EPulse
-
- EM
-)
-
-func (e Enablement) String() string {
- switch e {
- case 0:
- return "(no enablements)"
- case EWayland:
- return "wayland"
- case EX11:
- return "x11"
- case EDBus:
- return "dbus"
- case EPulse:
- return "pulseaudio"
- default:
- buf := new(strings.Builder)
- buf.Grow(32)
-
- for i := Enablement(1); i < EM; i <<= 1 {
- if e&i != 0 {
- buf.WriteString(", " + i.String())
- }
- }
-
- if buf.Len() == 0 {
- return fmt.Sprintf("e%x", byte(e))
- }
- return strings.TrimPrefix(buf.String(), ", ")
- }
-}
diff --git a/system/enablement_test.go b/system/enablement_test.go
deleted file mode 100644
index ba81656b..00000000
--- a/system/enablement_test.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package system_test
-
-import (
- "testing"
-
- "hakurei.app/system"
-)
-
-func TestEnablementString(t *testing.T) {
- testCases := []struct {
- flags system.Enablement
- want string
- }{
- {0, "(no enablements)"},
- {system.EWayland, "wayland"},
- {system.EX11, "x11"},
- {system.EDBus, "dbus"},
- {system.EPulse, "pulseaudio"},
- {system.EWayland | system.EX11, "wayland, x11"},
- {system.EWayland | system.EDBus, "wayland, dbus"},
- {system.EWayland | system.EPulse, "wayland, pulseaudio"},
- {system.EX11 | system.EDBus, "x11, dbus"},
- {system.EX11 | system.EPulse, "x11, pulseaudio"},
- {system.EDBus | system.EPulse, "dbus, pulseaudio"},
- {system.EWayland | system.EX11 | system.EDBus, "wayland, x11, dbus"},
- {system.EWayland | system.EX11 | system.EPulse, "wayland, x11, pulseaudio"},
- {system.EWayland | system.EDBus | system.EPulse, "wayland, dbus, pulseaudio"},
- {system.EX11 | system.EDBus | system.EPulse, "x11, dbus, pulseaudio"},
- {system.EWayland | system.EX11 | system.EDBus | system.EPulse, "wayland, x11, dbus, pulseaudio"},
-
- {1 << 5, "e20"},
- {1 << 6, "e40"},
- {1 << 7, "e80"},
- }
-
- for _, tc := range testCases {
- t.Run(tc.want, func(t *testing.T) {
- if got := tc.flags.String(); got != tc.want {
- t.Errorf("String: %q, want %q", got, tc.want)
- }
- })
- }
-}
diff --git a/system/link.go b/system/link.go
index d463b623..50a05348 100644
--- a/system/link.go
+++ b/system/link.go
@@ -2,24 +2,26 @@ package system
import (
"fmt"
+
+ "hakurei.app/hst"
)
// Link calls LinkFileType with the [Process] criteria.
func (sys *I) Link(oldname, newname string) *I { return sys.LinkFileType(Process, oldname, newname) }
// 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 hst.Enablement, oldname, newname string) *I {
sys.ops = append(sys.ops, &hardlinkOp{et, newname, oldname})
return sys
}
// hardlinkOp implements [I.LinkFileType].
type hardlinkOp struct {
- et Enablement
+ et hst.Enablement
dst, src string
}
-func (l *hardlinkOp) Type() Enablement { return l.et }
+func (l *hardlinkOp) Type() hst.Enablement { return l.et }
func (l *hardlinkOp) apply(sys *I) error {
sys.msg.Verbose("linking", l)
diff --git a/system/link_test.go b/system/link_test.go
index b962db1a..02a59c07 100644
--- a/system/link_test.go
+++ b/system/link_test.go
@@ -4,32 +4,33 @@ import (
"testing"
"hakurei.app/container/stub"
+ "hakurei.app/hst"
)
func TestHardlinkOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{
- {"link", 0xdeadbeef, 0xff, &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{
- call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil),
+ {"link", 0xdeadbeef, 0xff, &hardlinkOp{hst.EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{
+ call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{hst.EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil),
call("link", stub.ExpectArgs{"/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}, nil, stub.UniqueError(1)),
}, &OpError{Op: "hardlink", Err: stub.UniqueError(1)}, nil, nil},
- {"remove", 0xdeadbeef, 0xff, &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{
- call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil),
+ {"remove", 0xdeadbeef, 0xff, &hardlinkOp{hst.EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{
+ call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{hst.EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil),
call("link", stub.ExpectArgs{"/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}, nil, nil),
}, nil, []stub.Call{
call("verbosef", stub.ExpectArgs{"removing hard link %q", []any{"/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}}, nil, nil),
call("remove", stub.ExpectArgs{"/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}, nil, stub.UniqueError(0)),
}, &OpError{Op: "hardlink", Err: stub.UniqueError(0), Revert: true}},
- {"success skip", 0xdeadbeef, EWayland | EX11, &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{
- call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil),
+ {"success skip", 0xdeadbeef, hst.EWayland | hst.EX11, &hardlinkOp{hst.EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{
+ call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{hst.EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil),
call("link", stub.ExpectArgs{"/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}, nil, nil),
}, nil, []stub.Call{
call("verbosef", stub.ExpectArgs{"skipping hard link %q", []any{"/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}}, nil, nil),
}, nil},
- {"success", 0xdeadbeef, 0xff, &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{
- call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil),
+ {"success", 0xdeadbeef, 0xff, &hardlinkOp{hst.EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}, []stub.Call{
+ call("verbose", stub.ExpectArgs{[]any{"linking", &hardlinkOp{hst.EPulse, "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse", "/run/user/1000/pulse/native"}}}, nil, nil),
call("link", stub.ExpectArgs{"/run/user/1000/pulse/native", "/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}, nil, nil),
}, nil, []stub.Call{
call("verbosef", stub.ExpectArgs{"removing hard link %q", []any{"/run/user/1000/hakurei/9663730666a44cfc2a81610379e02ed6/pulse"}}, nil, nil),
diff --git a/system/mkdir.go b/system/mkdir.go
index c4e61638..23c42104 100644
--- a/system/mkdir.go
+++ b/system/mkdir.go
@@ -4,6 +4,8 @@ import (
"errors"
"fmt"
"os"
+
+ "hakurei.app/hst"
)
// Ensure ensures the existence of a directory.
@@ -13,20 +15,20 @@ func (sys *I) Ensure(name string, perm os.FileMode) *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 hst.Enablement, name string, perm os.FileMode) *I {
sys.ops = append(sys.ops, &mkdirOp{et, name, perm, true})
return sys
}
// mkdirOp implements [I.Ensure] and [I.Ephemeral].
type mkdirOp struct {
- et Enablement
+ et hst.Enablement
path string
perm os.FileMode
ephemeral bool
}
-func (m *mkdirOp) Type() Enablement { return m.et }
+func (m *mkdirOp) Type() hst.Enablement { return m.et }
func (m *mkdirOp) apply(sys *I) error {
sys.msg.Verbose("ensuring directory", m)
diff --git a/system/system.go b/system/system.go
index 0d16a14a..5599218e 100644
--- a/system/system.go
+++ b/system/system.go
@@ -7,11 +7,12 @@ import (
"strings"
"hakurei.app/container"
+ "hakurei.app/hst"
)
const (
// User type is reverted at final instance exit.
- User = EM << iota
+ User = hst.EM << iota
// Process type is unconditionally reverted on exit.
Process
@@ -19,21 +20,21 @@ const (
)
// Criteria specifies types of Op to revert.
-type Criteria Enablement
+type Criteria hst.Enablement
-func (ec *Criteria) hasType(t Enablement) bool {
+func (ec *Criteria) hasType(t hst.Enablement) bool {
// nil criteria: revert everything except User
if ec == nil {
return t != User
}
- return Enablement(*ec)&t != 0
+ return hst.Enablement(*ec)&t != 0
}
// Op is a reversible system operation.
type Op interface {
// Type returns [Op]'s enablement type, for matching a revert criteria.
- Type() Enablement
+ Type() hst.Enablement
apply(sys *I) error
revert(sys *I, ec *Criteria) error
@@ -44,7 +45,7 @@ type Op interface {
}
// TypeString extends [Enablement.String] to support [User] and [Process].
-func TypeString(e Enablement) string {
+func TypeString(e hst.Enablement) string {
switch e {
case User:
return "user"
diff --git a/system/system_test.go b/system/system_test.go
index 73b70fce..0f5e2d68 100644
--- a/system/system_test.go
+++ b/system/system_test.go
@@ -10,18 +10,19 @@ import (
"hakurei.app/container"
"hakurei.app/container/stub"
+ "hakurei.app/hst"
"hakurei.app/system/internal/xcb"
)
func TestCriteria(t *testing.T) {
testCases := []struct {
name string
- ec, t Enablement
+ ec, t hst.Enablement
want bool
}{
- {"nil", 0xff, EWayland, true},
+ {"nil", 0xff, hst.EWayland, true},
{"nil user", 0xff, User, false},
- {"all", EWayland | EX11 | EDBus | EPulse | User | Process, Process, true},
+ {"all", hst.EWayland | hst.EX11 | hst.EDBus | hst.EPulse | User | Process, Process, true},
}
for _, tc := range testCases {
@@ -40,18 +41,18 @@ func TestCriteria(t *testing.T) {
func TestTypeString(t *testing.T) {
testCases := []struct {
- e Enablement
+ e hst.Enablement
want string
}{
- {EWayland, EWayland.String()},
- {EX11, EX11.String()},
- {EDBus, EDBus.String()},
- {EPulse, EPulse.String()},
+ {hst.EWayland, hst.EWayland.String()},
+ {hst.EX11, hst.EX11.String()},
+ {hst.EDBus, hst.EDBus.String()},
+ {hst.EPulse, hst.EPulse.String()},
{User, "user"},
{Process, "process"},
{User | Process, "user, process"},
- {EWayland | User | Process, "wayland, user, process"},
- {EX11 | Process, "x11, process"},
+ {hst.EWayland | User | Process, "wayland, user, process"},
+ {hst.EX11 | Process, "x11, process"},
}
for _, tc := range testCases {
@@ -176,7 +177,7 @@ func TestCommitRevert(t *testing.T) {
testCases := []struct {
name string
f func(sys *I)
- ec Enablement
+ ec hst.Enablement
commit []stub.Call
wantErrCommit error
diff --git a/system/tmpfiles.go b/system/tmpfiles.go
index 845b43ab..434a3aea 100644
--- a/system/tmpfiles.go
+++ b/system/tmpfiles.go
@@ -7,6 +7,8 @@ import (
"io"
"os"
"syscall"
+
+ "hakurei.app/hst"
)
// CopyFile reads up to n bytes from src and writes the resulting byte slice to payloadP.
@@ -26,7 +28,7 @@ type tmpfileOp struct {
buf *bytes.Buffer
}
-func (t *tmpfileOp) Type() Enablement { return Process }
+func (t *tmpfileOp) Type() hst.Enablement { return Process }
func (t *tmpfileOp) apply(sys *I) error {
if t.payload == nil {
diff --git a/system/wayland.go b/system/wayland.go
index 5a87ab96..77cd61f0 100644
--- a/system/wayland.go
+++ b/system/wayland.go
@@ -5,6 +5,7 @@ import (
"fmt"
"os"
+ "hakurei.app/hst"
"hakurei.app/system/acl"
"hakurei.app/system/wayland"
)
@@ -32,7 +33,7 @@ type waylandOp struct {
conn waylandConn
}
-func (w *waylandOp) Type() Enablement { return Process }
+func (w *waylandOp) Type() hst.Enablement { return Process }
func (w *waylandOp) apply(sys *I) error {
if w.sync == nil {
diff --git a/system/xhost.go b/system/xhost.go
index e5ea2580..9a547f43 100644
--- a/system/xhost.go
+++ b/system/xhost.go
@@ -1,6 +1,7 @@
package system
import (
+ "hakurei.app/hst"
"hakurei.app/system/internal/xcb"
)
@@ -13,7 +14,7 @@ func (sys *I) ChangeHosts(username string) *I {
// xhostOp implements [I.ChangeHosts].
type xhostOp string
-func (x xhostOp) Type() Enablement { return EX11 }
+func (x xhostOp) Type() hst.Enablement { return hst.EX11 }
func (x xhostOp) apply(sys *I) error {
sys.msg.Verbosef("inserting entry %s to X11", x)
diff --git a/system/xhost_test.go b/system/xhost_test.go
index 68601e86..ad3ba89d 100644
--- a/system/xhost_test.go
+++ b/system/xhost_test.go
@@ -4,17 +4,18 @@ import (
"testing"
"hakurei.app/container/stub"
+ "hakurei.app/hst"
"hakurei.app/system/internal/xcb"
)
func TestXHostOp(t *testing.T) {
checkOpBehaviour(t, []opBehaviourTestCase{
- {"xcbChangeHosts revert", 0xbeef, EX11, xhostOp("chronos"), []stub.Call{
+ {"xcbChangeHosts revert", 0xbeef, hst.EX11, xhostOp("chronos"), []stub.Call{
call("verbosef", stub.ExpectArgs{"inserting entry %s to X11", []any{xhostOp("chronos")}}, nil, nil),
call("xcbChangeHosts", stub.ExpectArgs{xcb.HostMode(xcb.HostModeInsert), xcb.Family(xcb.FamilyServerInterpreted), "localuser\x00chronos"}, nil, stub.UniqueError(1)),
}, &OpError{Op: "xhost", Err: stub.UniqueError(1)}, nil, nil},
- {"xcbChangeHosts revert", 0xbeef, EX11, xhostOp("chronos"), []stub.Call{
+ {"xcbChangeHosts revert", 0xbeef, hst.EX11, xhostOp("chronos"), []stub.Call{
call("verbosef", stub.ExpectArgs{"inserting entry %s to X11", []any{xhostOp("chronos")}}, nil, nil),
call("xcbChangeHosts", stub.ExpectArgs{xcb.HostMode(xcb.HostModeInsert), xcb.Family(xcb.FamilyServerInterpreted), "localuser\x00chronos"}, nil, nil),
}, nil, []stub.Call{
@@ -29,7 +30,7 @@ func TestXHostOp(t *testing.T) {
call("verbosef", stub.ExpectArgs{"skipping entry %s in X11", []any{xhostOp("chronos")}}, nil, nil),
}, nil},
- {"success", 0xbeef, EX11, xhostOp("chronos"), []stub.Call{
+ {"success", 0xbeef, hst.EX11, xhostOp("chronos"), []stub.Call{
call("verbosef", stub.ExpectArgs{"inserting entry %s to X11", []any{xhostOp("chronos")}}, nil, nil),
call("xcbChangeHosts", stub.ExpectArgs{xcb.HostMode(xcb.HostModeInsert), xcb.Family(xcb.FamilyServerInterpreted), "localuser\x00chronos"}, nil, nil),
}, nil, []stub.Call{
@@ -52,6 +53,6 @@ func TestXHostOp(t *testing.T) {
})
checkOpMeta(t, []opMetaTestCase{
- {"xhost", xhostOp("chronos"), EX11, "/tmp/.X11-unix", "SI:localuser:chronos"},
+ {"xhost", xhostOp("chronos"), hst.EX11, "/tmp/.X11-unix", "SI:localuser:chronos"},
})
}