From 87781c765844409226804157546753b84ef3c6b4 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Mon, 8 Dec 2025 08:03:50 +0900 Subject: [PATCH] treewide: include PipeWire op and enforce PulseAudio check This fully replaces PulseAudio with PipeWire and enforces the PulseAudio check and error message. The pipewire-pulse daemon is handled in the NixOS module. Closes #26. Signed-off-by: Ophestra --- cmd/hakurei/command.go | 12 +++++-- cmd/hakurei/print_test.go | 13 +++---- cmd/hpkg/build.nix | 1 - cmd/hpkg/test/test.py | 8 ++--- hst/config.go | 6 ++-- hst/config_test.go | 2 +- hst/hst.go | 2 +- hst/hst_test.go | 3 +- internal/outcome/outcome.go | 1 + internal/outcome/run_test.go | 56 ++++++++++-------------------- internal/outcome/shim_test.go | 2 +- internal/outcome/sppulse.go | 2 +- internal/outcome/sppulse_test.go | 42 +++++++++++++++------- internal/outcome/spruntime.go | 5 ++- internal/outcome/spruntime_test.go | 8 ++--- internal/store/data_test.go | 4 +-- nixos.nix | 9 +++++ options.nix | 14 ++++---- test/configuration.nix | 6 ---- test/interactive/hakurei.nix | 2 +- test/sandbox/case/device.nix | 12 ++++--- test/sandbox/case/mapuid.nix | 12 ++++--- test/sandbox/case/pd.nix | 2 +- test/sandbox/case/pdlike.nix | 12 ++++--- test/sandbox/case/preset.nix | 12 ++++--- test/sandbox/case/tty.nix | 12 ++++--- test/sandbox/test.py | 2 +- test/test.py | 18 ++++++---- 28 files changed, 154 insertions(+), 126 deletions(-) diff --git a/cmd/hakurei/command.go b/cmd/hakurei/command.go index 63467e5..8f20c8e 100644 --- a/cmd/hakurei/command.go +++ b/cmd/hakurei/command.go @@ -14,6 +14,7 @@ import ( _ "unsafe" // for go:linkname "hakurei.app/command" + "hakurei.app/container" "hakurei.app/container/check" "hakurei.app/container/fhs" "hakurei.app/hst" @@ -149,9 +150,6 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr if flagPipeWire || flagPulse { et |= hst.EPipeWire } - if flagPulse { - et |= hst.EPulse - } config := &hst.Config{ ID: flagID, @@ -189,6 +187,14 @@ func buildCommand(ctx context.Context, msg message.Msg, early *earlyHardeningErr }}) } + // start pipewire-pulse: this most likely exists on host if PipeWire is available + if flagPulse { + config.Container.Filesystem = append(config.Container.Filesystem, hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSDaemon{ + Target: fhs.AbsRunUser.Append(strconv.Itoa(container.OverflowUid(msg)), "pulse/native"), + Exec: shell, Args: []string{"-lc", "pipewire-pulse"}, + }}) + } + config.Container.Filesystem = append(config.Container.Filesystem, // opportunistically bind kvm hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{ diff --git a/cmd/hakurei/print_test.go b/cmd/hakurei/print_test.go index d0c12d1..d2e5ea3 100644 --- a/cmd/hakurei/print_test.go +++ b/cmd/hakurei/print_test.go @@ -62,7 +62,7 @@ func TestPrintShowInstance(t *testing.T) { {"nil", nil, nil, false, false, "Error: invalid configuration!\n\n", false}, {"config", nil, hst.Template(), false, false, `App Identity: 9 (org.chromium.Chromium) - Enablements: wayland, dbus, pipewire, pulseaudio + Enablements: wayland, dbus, pipewire Groups: video, dialout, plugdev Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir Home: /data/data/org.chromium.Chromium @@ -159,7 +159,7 @@ Session bus App Identity: 9 (org.chromium.Chromium) - Enablements: wayland, dbus, pipewire, pulseaudio + Enablements: wayland, dbus, pipewire Groups: video, dialout, plugdev Flags: multiarch, compat, devel, userns, net, abstract, tty, mapuid, device, runtime, tmpdir Home: /data/data/org.chromium.Chromium @@ -215,8 +215,7 @@ App "enablements": { "wayland": true, "dbus": true, - "pipewire": true, - "pulse": true + "pipewire": true }, "session_bus": { "see": null, @@ -367,8 +366,7 @@ App "enablements": { "wayland": true, "dbus": true, - "pipewire": true, - "pulse": true + "pipewire": true }, "session_bus": { "see": null, @@ -566,8 +564,7 @@ func TestPrintPs(t *testing.T) { "enablements": { "wayland": true, "dbus": true, - "pipewire": true, - "pulse": true + "pipewire": true }, "session_bus": { "see": null, diff --git a/cmd/hpkg/build.nix b/cmd/hpkg/build.nix index 5e2f55a..d994fc2 100644 --- a/cmd/hpkg/build.nix +++ b/cmd/hpkg/build.nix @@ -176,7 +176,6 @@ let x11 = allow_x11; dbus = allow_dbus; pipewire = allow_audio; - pulse = allow_audio; }; mesa = if gpu then mesaWrappers else null; diff --git a/cmd/hpkg/test/test.py b/cmd/hpkg/test/test.py index ef6a626..81e0281 100644 --- a/cmd/hpkg/test/test.py +++ b/cmd/hpkg/test/test.py @@ -90,13 +90,13 @@ wait_for_window("hakurei@machine-foot") machine.send_chars("clear; wayland-info && touch /tmp/success-client\n") machine.wait_for_file("/tmp/hakurei.0/tmpdir/2/success-client") collect_state_ui("app_wayland") -check_state("foot", {"wayland": True, "dbus": True, "pipewire": True, "pulse": True}) +check_state("foot", {"wayland": True, "dbus": True, "pipewire": True}) # Verify acl on XDG_RUNTIME_DIR: -print(machine.succeed("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 10002")) +print(machine.succeed("getfacl --absolute-names --omit-header --numeric /tmp/hakurei.0/runtime | grep 10002")) machine.send_chars("exit\n") machine.wait_until_fails("pgrep foot") # Verify acl cleanup on XDG_RUNTIME_DIR: -machine.wait_until_fails("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 10002") +machine.wait_until_fails("getfacl --absolute-names --omit-header --numeric /tmp/hakurei.0/runtime | grep 10002") # Exit Sway and verify process exit status 0: swaymsg("exit", succeed=False) @@ -107,4 +107,4 @@ print(machine.succeed("find /tmp/hakurei.0 " + "-path '/tmp/hakurei.0/runtime/*/*' -prune -o " + "-path '/tmp/hakurei.0/tmpdir/*/*' -prune -o " + "-print")) -print(machine.succeed("find /run/user/1000/hakurei")) +print(machine.fail("ls /run/user/1000/hakurei")) diff --git a/hst/config.go b/hst/config.go index 23c21cc..5032471 100644 --- a/hst/config.go +++ b/hst/config.go @@ -109,11 +109,9 @@ func (config *Config) Validate() error { } } - // EPulse without EPipeWire is insecure - if et := config.Enablements.Unwrap(); !config.DirectPulse && - et&EPipeWire == 0 && et&EPulse != 0 { + if et := config.Enablements.Unwrap(); !config.DirectPulse && et&EPulse != 0 { return &AppError{Step: "validate configuration", Err: ErrInsecure, - Msg: "enablement PulseAudio requires PipeWire, which is not set"} + Msg: "enablement PulseAudio is insecure and no longer supported"} } return nil diff --git a/hst/config_test.go b/hst/config_test.go index 731a131..776c21e 100644 --- a/hst/config_test.go +++ b/hst/config_test.go @@ -58,7 +58,7 @@ func TestConfigValidate(t *testing.T) { Shell: fhs.AbsTmp, Path: fhs.AbsTmp, }}, &hst.AppError{Step: "validate configuration", Err: hst.ErrInsecure, - Msg: "enablement PulseAudio requires PipeWire, which is not set"}}, + Msg: "enablement PulseAudio is insecure and no longer supported"}}, {"valid", &hst.Config{Container: &hst.ContainerConfig{ Home: fhs.AbsTmp, Shell: fhs.AbsTmp, diff --git a/hst/hst.go b/hst/hst.go index 94bcd09..439f4b2 100644 --- a/hst/hst.go +++ b/hst/hst.go @@ -70,7 +70,7 @@ func Template() *Config { return &Config{ ID: "org.chromium.Chromium", - Enablements: NewEnablements(EWayland | EDBus | EPipeWire | EPulse), + Enablements: NewEnablements(EWayland | EDBus | EPipeWire), SessionBus: &BusConfig{ See: nil, diff --git a/hst/hst_test.go b/hst/hst_test.go index 01d229f..e3b9861 100644 --- a/hst/hst_test.go +++ b/hst/hst_test.go @@ -105,8 +105,7 @@ func TestTemplate(t *testing.T) { "enablements": { "wayland": true, "dbus": true, - "pipewire": true, - "pulse": true + "pipewire": true }, "session_bus": { "see": null, diff --git a/internal/outcome/outcome.go b/internal/outcome/outcome.go index 3252db9..ea01099 100644 --- a/internal/outcome/outcome.go +++ b/internal/outcome/outcome.go @@ -295,6 +295,7 @@ func (state *outcomeStateSys) toSystem() error { // optional via enablements &spWaylandOp{}, &spX11Op{}, + spPipeWireOp{}, &spPulseOp{}, &spDBusOp{}, diff --git a/internal/outcome/run_test.go b/internal/outcome/run_test.go index 6908cd2..f2aa163 100644 --- a/internal/outcome/run_test.go +++ b/internal/outcome/run_test.go @@ -27,7 +27,7 @@ import ( "hakurei.app/message" ) -func TestOutcomeMain(t *testing.T) { +func TestOutcomeRun(t *testing.T) { t.Parallel() msg := message.New(nil) msg.SwapVerbose(testing.Verbose()) @@ -67,18 +67,8 @@ func TestOutcomeMain(t *testing.T) { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ). - // ensureRuntimeDir - Ensure(m("/run/user/1971"), 0700). - UpdatePermType(system.User, m("/run/user/1971"), acl.Execute). - Ensure(m("/run/user/1971/hakurei"), 0700). - UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute). - - // runtime - Ephemeral(system.Process, m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), 0700). - UpdatePerm(m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), acl.Execute). - - // spPulseOp - Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pulse")). + // spPipeWireOp + PipeWire(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pipewire")). // spDBusOp MustProxyDBus( @@ -106,8 +96,7 @@ func TestOutcomeMain(t *testing.T) { "GOOGLE_DEFAULT_CLIENT_ID=77185425430.apps.googleusercontent.com", "GOOGLE_DEFAULT_CLIENT_SECRET=OTJgUOQcT7lO7GsGZq2G4IlT", "HOME=/data/data/org.chromium.Chromium", - "PULSE_COOKIE=/.hakurei/pulse-cookie", - "PULSE_SERVER=unix:/run/user/1971/pulse/native", + "PIPEWIRE_REMOTE=/run/user/1971/pipewire-0", "SHELL=/run/current-system/sw/bin/zsh", "TERM=xterm-256color", "USER=chronos", @@ -144,7 +133,7 @@ func TestOutcomeMain(t *testing.T) { Tmpfs(fhs.AbsDevShm, 0, 01777). // spRuntimeOp - Tmpfs(fhs.AbsRunUser, 1<<12, 0755). + Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755). Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/1971"), std.BindWritable). // spTmpdirOp @@ -157,9 +146,8 @@ func TestOutcomeMain(t *testing.T) { // spWaylandOp Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1971/wayland-0"), 0). - // spPulseOp - Bind(m("/run/user/1971/hakurei/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pulse"), m("/run/user/1971/pulse/native"), 0). - Place(m("/.hakurei/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)). + // spPipeWireOp + Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pipewire"), m("/run/user/1971/pipewire-0"), 0). // spDBusOp Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bus"), m("/run/user/1971/bus"), 0). @@ -244,7 +232,7 @@ func TestOutcomeMain(t *testing.T) { Tmpfs(hst.AbsPrivateTmp, 4096, 0755). DevWritable(m("/dev/"), true). Tmpfs(m("/dev/shm/"), 0, 01777). - Tmpfs(m("/run/user/"), 4096, 0755). + Tmpfs(m("/run/user/"), xdgRuntimeDirSize, 0755). Bind(m("/tmp/hakurei.0/runtime/0"), m("/run/user/65534"), std.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/0"), m("/tmp/"), std.BindWritable). Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")). @@ -298,7 +286,7 @@ func TestOutcomeMain(t *testing.T) { }, Filter: true, }, - Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse), + Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPipeWire | hst.EPulse), Container: &hst.ContainerConfig{ Filesystem: []hst.FilesystemConfigJSON{ @@ -347,10 +335,7 @@ func TestOutcomeMain(t *testing.T) { Ensure(m("/tmp/hakurei.0/tmpdir/9"), 01700).UpdatePermType(system.User, m("/tmp/hakurei.0/tmpdir/9"), acl.Read, acl.Write, acl.Execute). Ephemeral(system.Process, m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c"), 0711). Wayland(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c"). - Ensure(m("/run/user/1971"), 0700).UpdatePermType(system.User, m("/run/user/1971"), acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset - Ensure(m("/run/user/1971/hakurei"), 0700).UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute). - Ephemeral(system.Process, m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c"), 0700).UpdatePermType(system.Process, m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c"), acl.Execute). - Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c/pulse")). + PipeWire(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/pipewire")). MustProxyDBus(&hst.BusConfig{ Talk: []string{ "org.freedesktop.Notifications", @@ -397,8 +382,7 @@ func TestOutcomeMain(t *testing.T) { "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus", "DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket", "HOME=/home/chronos", - "PULSE_COOKIE=" + hst.PrivateTmp + "/pulse-cookie", - "PULSE_SERVER=unix:/run/user/65534/pulse/native", + "PIPEWIRE_REMOTE=/run/user/65534/pipewire-0", "SHELL=/run/current-system/sw/bin/zsh", "TERM=xterm-256color", "USER=chronos", @@ -413,14 +397,13 @@ func TestOutcomeMain(t *testing.T) { Tmpfs(hst.AbsPrivateTmp, 4096, 0755). DevWritable(m("/dev/"), true). Tmpfs(m("/dev/shm/"), 0, 01777). - Tmpfs(m("/run/user/"), 4096, 0755). + Tmpfs(m("/run/user/"), xdgRuntimeDirSize, 0755). Bind(m("/tmp/hakurei.0/runtime/9"), m("/run/user/65534"), std.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/9"), m("/tmp/"), std.BindWritable). Place(m("/etc/passwd"), []byte("chronos:x:65534:65534:Hakurei:/home/chronos:/run/current-system/sw/bin/zsh\n")). Place(m("/etc/group"), []byte("hakurei:x:65534:\n")). Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/65534/wayland-0"), 0). - Bind(m("/run/user/1971/hakurei/ebf083d1b175911782d413369b64ce7c/pulse"), m("/run/user/65534/pulse/native"), 0). - Place(m(hst.PrivateTmp+"/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)). + Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/pipewire"), m("/run/user/65534/pipewire-0"), 0). Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/bus"), m("/run/user/65534/bus"), 0). Bind(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0). Bind(m("/dev/dri"), m("/dev/dri"), std.BindWritable|std.BindDevice|std.BindOptional). @@ -440,7 +423,7 @@ func TestOutcomeMain(t *testing.T) { {"nixos chromium direct wayland", new(stubNixOS), &hst.Config{ ID: "org.chromium.Chromium", - Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPulse), + Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPipeWire | hst.EPulse), Container: &hst.ContainerConfig{ Env: nil, Filesystem: []hst.FilesystemConfigJSON{ @@ -502,9 +485,8 @@ func TestOutcomeMain(t *testing.T) { Ensure(m("/run/user/1971"), 0700).UpdatePermType(system.User, m("/run/user/1971"), acl.Execute). // this is ordered as is because the previous Ensure only calls mkdir if XDG_RUNTIME_DIR is unset Ensure(m("/run/user/1971/hakurei"), 0700).UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute). UpdatePermType(hst.EWayland, m("/run/user/1971/wayland-0"), acl.Read, acl.Write, acl.Execute). - Ephemeral(system.Process, m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1"), 0700).UpdatePermType(system.Process, m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1"), acl.Execute). - Link(m("/run/user/1971/pulse/native"), m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse")). Ephemeral(system.Process, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1"), 0711). + PipeWire(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/pipewire")). MustProxyDBus(&hst.BusConfig{ Talk: []string{ "org.freedesktop.FileManager1", "org.freedesktop.Notifications", @@ -544,8 +526,7 @@ func TestOutcomeMain(t *testing.T) { "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1971/bus", "DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket", "HOME=/var/lib/persist/module/hakurei/0/1", - "PULSE_COOKIE=" + hst.PrivateTmp + "/pulse-cookie", - "PULSE_SERVER=unix:/run/user/1971/pulse/native", + "PIPEWIRE_REMOTE=/run/user/1971/pipewire-0", "SHELL=/run/current-system/sw/bin/zsh", "TERM=xterm-256color", "USER=u0_a1", @@ -559,14 +540,13 @@ func TestOutcomeMain(t *testing.T) { Tmpfs(hst.AbsPrivateTmp, 4096, 0755). DevWritable(m("/dev/"), true). Tmpfs(m("/dev/shm/"), 0, 01777). - Tmpfs(m("/run/user/"), 4096, 0755). + Tmpfs(m("/run/user/"), xdgRuntimeDirSize, 0755). Bind(m("/tmp/hakurei.0/runtime/1"), m("/run/user/1971"), std.BindWritable). Bind(m("/tmp/hakurei.0/tmpdir/1"), m("/tmp/"), std.BindWritable). Place(m("/etc/passwd"), []byte("u0_a1:x:1971:100:Hakurei:/var/lib/persist/module/hakurei/0/1:/run/current-system/sw/bin/zsh\n")). Place(m("/etc/group"), []byte("hakurei:x:100:\n")). Bind(m("/run/user/1971/wayland-0"), m("/run/user/1971/wayland-0"), 0). - Bind(m("/run/user/1971/hakurei/8e2c76b066dabe574cf073bdb46eb5c1/pulse"), m("/run/user/1971/pulse/native"), 0). - Place(m(hst.PrivateTmp+"/pulse-cookie"), bytes.Repeat([]byte{0}, pulseCookieSizeMax)). + Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/pipewire"), m("/run/user/1971/pipewire-0"), 0). Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/bus"), m("/run/user/1971/bus"), 0). Bind(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0). Bind(m("/bin"), m("/bin"), 0). diff --git a/internal/outcome/shim_test.go b/internal/outcome/shim_test.go index 147607f..532a2c7 100644 --- a/internal/outcome/shim_test.go +++ b/internal/outcome/shim_test.go @@ -69,7 +69,7 @@ func TestShimEntrypoint(t *testing.T) { Tmpfs(fhs.AbsDevShm, 0, 01777). // spRuntimeOp - Tmpfs(fhs.AbsRunUser, 1<<12, 0755). + Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755). Bind(m("/tmp/hakurei.10/runtime/9999"), m("/run/user/1000"), std.BindWritable). // spTmpdirOp diff --git a/internal/outcome/sppulse.go b/internal/outcome/sppulse.go index a2dc240..e86fb3f 100644 --- a/internal/outcome/sppulse.go +++ b/internal/outcome/sppulse.go @@ -29,7 +29,7 @@ type spPulseOp struct { } func (s *spPulseOp) toSystem(state *outcomeStateSys) error { - if state.et&hst.EPulse == 0 { + if !state.directPulse || state.et&hst.EPulse == 0 { return errNotEnabled } diff --git a/internal/outcome/sppulse_test.go b/internal/outcome/sppulse_test.go index 4ec445d..70fae9e 100644 --- a/internal/outcome/sppulse_test.go +++ b/internal/outcome/sppulse_test.go @@ -18,24 +18,40 @@ import ( func TestSpPulseOp(t *testing.T) { t.Parallel() - config := hst.Template() + newConfig := func() *hst.Config { + config := hst.Template() + config.DirectPulse = true + config.Enablements = hst.NewEnablements(hst.EPulse) + return config + } + + config := newConfig() sampleCookie := bytes.Repeat([]byte{0xfc}, pulseCookieSizeMax) checkOpBehaviour(t, []opBehaviourTestCase{ {"not enabled", func(bool, bool) outcomeOp { return new(spPulseOp) }, func() *hst.Config { - c := hst.Template() + c := newConfig() + c.DirectPulse = true *c.Enablements = 0 return c }, nil, nil, nil, nil, errNotEnabled, nil, nil, nil, nil, nil}, + {"not enabled direct", func(bool, bool) outcomeOp { + return new(spPulseOp) + }, func() *hst.Config { + c := newConfig() + c.DirectPulse = false + return c + }, nil, nil, nil, nil, errNotEnabled, nil, nil, nil, nil, nil}, + {"socketDir stat", func(isShim, _ bool) outcomeOp { if !isShim { return new(spPulseOp) } return &spPulseOp{Cookie: (*[256]byte)(sampleCookie)} - }, hst.Template, nil, []stub.Call{ + }, newConfig, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), stub.UniqueError(2)), }, nil, nil, &hst.AppError{ Step: `access PulseAudio directory "/proc/nonexistent/xdg_runtime_dir/pulse"`, @@ -44,7 +60,7 @@ func TestSpPulseOp(t *testing.T) { {"socketDir nonexistent", func(bool, bool) outcomeOp { return new(spPulseOp) - }, hst.Template, nil, []stub.Call{ + }, newConfig, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), os.ErrNotExist), }, nil, nil, &hst.AppError{ Step: "finalise", @@ -54,7 +70,7 @@ func TestSpPulseOp(t *testing.T) { {"socket stat", func(bool, bool) outcomeOp { return new(spPulseOp) - }, hst.Template, nil, []stub.Call{ + }, newConfig, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, (*stubFi)(nil), stub.UniqueError(1)), }, nil, nil, &hst.AppError{ @@ -64,7 +80,7 @@ func TestSpPulseOp(t *testing.T) { {"socket nonexistent", func(bool, bool) outcomeOp { return new(spPulseOp) - }, hst.Template, nil, []stub.Call{ + }, newConfig, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, (*stubFi)(nil), os.ErrNotExist), }, nil, nil, &hst.AppError{ @@ -75,7 +91,7 @@ func TestSpPulseOp(t *testing.T) { {"socket mode", func(bool, bool) outcomeOp { return new(spPulseOp) - }, hst.Template, nil, []stub.Call{ + }, newConfig, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, &stubFi{mode: 0660}, nil), }, nil, nil, &hst.AppError{ @@ -86,7 +102,7 @@ func TestSpPulseOp(t *testing.T) { {"cookie notAbs", func(bool, bool) outcomeOp { return new(spPulseOp) - }, hst.Template, nil, []stub.Call{ + }, newConfig, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, &stubFi{mode: 0666}, nil), call("lookupEnv", stub.ExpectArgs{"PULSE_COOKIE"}, "proc/nonexistent/cookie", nil), @@ -97,7 +113,7 @@ func TestSpPulseOp(t *testing.T) { {"cookie loadFile", func(bool, bool) outcomeOp { return new(spPulseOp) - }, hst.Template, nil, []stub.Call{ + }, newConfig, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, &stubFi{mode: 0666}, nil), call("lookupEnv", stub.ExpectArgs{"PULSE_COOKIE"}, "/proc/nonexistent/cookie", nil), @@ -118,7 +134,7 @@ func TestSpPulseOp(t *testing.T) { op.CookieSize += +0xfd } return op - }, hst.Template, nil, []stub.Call{ + }, newConfig, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, &stubFi{mode: 0666}, nil), call("lookupEnv", stub.ExpectArgs{"PULSE_COOKIE"}, "/proc/nonexistent/cookie", nil), @@ -150,7 +166,7 @@ func TestSpPulseOp(t *testing.T) { sampleCookieTrunc := make([]byte, pulseCookieSizeMax) copy(sampleCookieTrunc, sampleCookie[:len(sampleCookie)-0xe]) return &spPulseOp{Cookie: (*[pulseCookieSizeMax]byte)(sampleCookieTrunc), CookieSize: pulseCookieSizeMax - 0xe} - }, hst.Template, nil, []stub.Call{ + }, newConfig, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, &stubFi{mode: 0666}, nil), call("lookupEnv", stub.ExpectArgs{"PULSE_COOKIE"}, "/proc/nonexistent/cookie", nil), @@ -183,7 +199,7 @@ func TestSpPulseOp(t *testing.T) { return new(spPulseOp) } return &spPulseOp{Cookie: (*[pulseCookieSizeMax]byte)(sampleCookie), CookieSize: pulseCookieSizeMax} - }, hst.Template, nil, []stub.Call{ + }, newConfig, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, &stubFi{mode: 0666}, nil), call("lookupEnv", stub.ExpectArgs{"PULSE_COOKIE"}, "/proc/nonexistent/cookie", nil), @@ -213,7 +229,7 @@ func TestSpPulseOp(t *testing.T) { {"success", func(bool, bool) outcomeOp { return new(spPulseOp) - }, hst.Template, nil, []stub.Call{ + }, newConfig, nil, []stub.Call{ call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse"}, (*stubFi)(nil), nil), call("stat", stub.ExpectArgs{wantRuntimePath + "/pulse/native"}, &stubFi{mode: 0666}, nil), call("lookupEnv", stub.ExpectArgs{"PULSE_COOKIE"}, nil, nil), diff --git a/internal/outcome/spruntime.go b/internal/outcome/spruntime.go index 1e066bd..6ec532b 100644 --- a/internal/outcome/spruntime.go +++ b/internal/outcome/spruntime.go @@ -91,6 +91,9 @@ func (s *spRuntimeOp) toSystem(state *outcomeStateSys) error { return nil } +// xdgRuntimeDirSize is the size of the filesystem mounted on inner XDG_RUNTIME_DIR. +const xdgRuntimeDirSize = 1 << 24 + func (s *spRuntimeOp) toContainer(state *outcomeStateParams) error { state.runtimeDir = fhs.AbsRunUser.Append(state.mapuid.String()) state.env[envXDGRuntimeDir] = state.runtimeDir.String() @@ -108,7 +111,7 @@ func (s *spRuntimeOp) toContainer(state *outcomeStateParams) error { } - state.params.Tmpfs(fhs.AbsRunUser, 1<<12, 0755) + state.params.Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755) if state.Container.Flags&hst.FShareRuntime != 0 { _, runtimeDirInst := s.commonPaths(state.outcomeState) state.params.Bind(runtimeDirInst, state.runtimeDir, std.BindWritable) diff --git a/internal/outcome/spruntime_test.go b/internal/outcome/spruntime_test.go index 18f3ce9..58cf90d 100644 --- a/internal/outcome/spruntime_test.go +++ b/internal/outcome/spruntime_test.go @@ -40,7 +40,7 @@ func TestSpRuntimeOp(t *testing.T) { // this op configures the container state and does not make calls during toContainer }, &container.Params{ Ops: new(container.Ops). - Tmpfs(fhs.AbsRunUser, 1<<12, 0755). + Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755). Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), std.BindWritable), }, paramsWantEnv(config, map[string]string{ "XDG_RUNTIME_DIR": "/run/user/1000", @@ -67,7 +67,7 @@ func TestSpRuntimeOp(t *testing.T) { // this op configures the container state and does not make calls during toContainer }, &container.Params{ Ops: new(container.Ops). - Tmpfs(fhs.AbsRunUser, 1<<12, 0755). + Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755). Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), std.BindWritable), }, paramsWantEnv(config, map[string]string{ "XDG_RUNTIME_DIR": "/run/user/1000", @@ -94,7 +94,7 @@ func TestSpRuntimeOp(t *testing.T) { // this op configures the container state and does not make calls during toContainer }, &container.Params{ Ops: new(container.Ops). - Tmpfs(fhs.AbsRunUser, 1<<12, 0755). + Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755). Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), std.BindWritable), }, paramsWantEnv(config, map[string]string{ "XDG_RUNTIME_DIR": "/run/user/1000", @@ -117,7 +117,7 @@ func TestSpRuntimeOp(t *testing.T) { // this op configures the container state and does not make calls during toContainer }, &container.Params{ Ops: new(container.Ops). - Tmpfs(fhs.AbsRunUser, 1<<12, 0755). + Tmpfs(fhs.AbsRunUser, xdgRuntimeDirSize, 0755). Bind(m("/proc/nonexistent/tmp/hakurei.0/runtime/9"), m("/run/user/1000"), std.BindWritable), }, paramsWantEnv(config, map[string]string{ "XDG_RUNTIME_DIR": "/run/user/1000", diff --git a/internal/store/data_test.go b/internal/store/data_test.go index a2eb92c..fe0e212 100644 --- a/internal/store/data_test.go +++ b/internal/store/data_test.go @@ -47,9 +47,9 @@ func TestEntryData(t *testing.T) { {"inconsistent enablement", "\x00\xff\xca\xfe\x00\x00\xff\x00" + templateStateGob, NewTemplateState(), &hst.AppError{ Step: "validate state enablement", Err: os.ErrInvalid, - Msg: "state entry aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa has unexpected enablement byte 0x1d, 0xff"}}, + Msg: "state entry aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa has unexpected enablement byte 0xd, 0xff"}}, - {"template", "\x00\xff\xca\xfe\x00\x00\x1d\xe2" + templateStateGob, NewTemplateState(), nil}, + {"template", "\x00\xff\xca\xfe\x00\x00\x0d\xf2" + templateStateGob, NewTemplateState(), nil}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/nixos.nix b/nixos.nix index efa659c..2f238ee 100644 --- a/nixos.nix +++ b/nixos.nix @@ -196,6 +196,15 @@ in } ] ) + ++ optional (app.enablements.pipewire && app.pulse) { + type = "daemon"; + dst = if app.mapRealUid then "/run/user/${toString config.users.users.${username}.uid}/pulse/native" else "/run/user/65534/pulse/native"; + path = cfg.shell; + args = [ + "-lc" + "pipewire-pulse" + ]; + } ++ [ { type = "bind"; diff --git a/options.nix b/options.nix index effa655..8be79b5 100644 --- a/options.nix +++ b/options.nix @@ -245,14 +245,14 @@ in Whether to share the PipeWire server via SecurityContext. ''; }; + }; - pulse = mkOption { - type = nullOr bool; - default = true; - description = '' - Whether to run the PulseAudio compatibility daemon. - ''; - }; + pulse = mkOption { + type = nullOr bool; + default = true; + description = '' + Whether to run the PulseAudio compatibility daemon. + ''; }; share = mkOption { diff --git a/test/configuration.nix b/test/configuration.nix index 5bc50e7..58fdf5a 100644 --- a/test/configuration.nix +++ b/test/configuration.nix @@ -134,7 +134,6 @@ enablements = { wayland = false; pipewire = false; - pulse = false; }; }; @@ -154,7 +153,6 @@ enablements = { dbus = false; pipewire = false; - pulse = false; }; }; @@ -170,7 +168,6 @@ enablements = { dbus = false; pipewire = false; - pulse = false; }; }; @@ -203,7 +200,6 @@ x11 = true; dbus = false; pipewire = false; - pulse = false; }; }; @@ -223,7 +219,6 @@ enablements = { dbus = false; pipewire = false; - pulse = false; }; }; @@ -238,7 +233,6 @@ x11 = false; dbus = false; pipewire = false; - pulse = false; }; }; }; diff --git a/test/interactive/hakurei.nix b/test/interactive/hakurei.nix index 887ed3e..9b37bc7 100644 --- a/test/interactive/hakurei.nix +++ b/test/interactive/hakurei.nix @@ -15,7 +15,7 @@ command = "foot"; enablements = { dbus = false; - pulse = false; + pipewire = false; }; }; }; diff --git a/test/sandbox/case/device.nix b/test/sandbox/case/device.nix index b567834..da5c6fd 100644 --- a/test/sandbox/case/device.nix +++ b/test/sandbox/case/device.nix @@ -41,7 +41,7 @@ in "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus" "DISPLAY=unix:/tmp/.X11-unix/X0" "HOME=/var/lib/hakurei/u0/a4" - "PULSE_SERVER=unix:/run/user/65534/pulse/native" + "PIPEWIRE_REMOTE=/run/user/65534/pipewire-0" "SHELL=/run/current-system/sw/bin/bash" "TERM=linux" "USER=u0_a4" @@ -137,8 +137,12 @@ in user = fs "800001ed" { "65534" = fs "800001c0" { bus = fs "10001fd" null null; - pulse = fs "800001c0" { native = fs "10001b6" null null; } null; + pulse = fs "800001c0" { + native = fs "10001ff" null null; + pid = fs "1a4" null null; + } null; wayland-0 = fs "1000038" null null; + pipewire-0 = fs "1000038" null null; } null; } null; } null; @@ -220,13 +224,13 @@ in (ent "/" ignore ignore ignore ignore ignore) (ent "/" ignore ignore ignore ignore ignore) (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10004,gid=10004") - (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10004,gid=10004") + (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10004,gid=10004") (ent "/tmp/hakurei.0/tmpdir/4" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10004,gid=10004") (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10004,gid=10004") (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/.X11-unix" "/tmp/.X11-unix" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") - (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore) + (ent ignore "/run/user/65534/pipewire-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") diff --git a/test/sandbox/case/mapuid.nix b/test/sandbox/case/mapuid.nix index f5e33a5..4201aac 100644 --- a/test/sandbox/case/mapuid.nix +++ b/test/sandbox/case/mapuid.nix @@ -49,7 +49,7 @@ in env = [ "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus" "HOME=/var/lib/hakurei/u0/a3" - "PULSE_SERVER=unix:/run/user/1000/pulse/native" + "PIPEWIRE_REMOTE=/run/user/1000/pipewire-0" "SHELL=/run/current-system/sw/bin/bash" "TERM=linux" "USER=u0_a3" @@ -162,8 +162,12 @@ in user = fs "800001ed" { "1000" = fs "800001f8" { bus = fs "10001fd" null null; - pulse = fs "800001c0" { native = fs "10001b6" null null; } null; + pulse = fs "800001c0" { + native = fs "10001ff" null null; + pid = fs "1a4" null null; + } null; wayland-0 = fs "1000038" null null; + pipewire-0 = fs "1000038" null null; } null; } null; } null; @@ -247,13 +251,13 @@ in (ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666") (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw") (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10003,gid=10003") - (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10003,gid=10003") + (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10003,gid=10003") (ent "/tmp/hakurei.0/runtime/3" "/run/user/1000" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.0/tmpdir/3" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10003,gid=10003") (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10003,gid=10003") (ent ignore "/run/user/1000/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") - (ent ignore "/run/user/1000/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore) + (ent ignore "/run/user/1000/pipewire-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/1000/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") diff --git a/test/sandbox/case/pd.nix b/test/sandbox/case/pd.nix index 98627fe..4d7d082 100644 --- a/test/sandbox/case/pd.nix +++ b/test/sandbox/case/pd.nix @@ -181,7 +181,7 @@ (ent ignore "/dev/console" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666") (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw") (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10000,gid=10000") - (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10000,gid=10000") + (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10000,gid=10000") (ent "/tmp/hakurei.0/runtime/0" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.0/tmpdir/0" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10000,gid=10000") diff --git a/test/sandbox/case/pdlike.nix b/test/sandbox/case/pdlike.nix index d8de1ab..d76c0cc 100644 --- a/test/sandbox/case/pdlike.nix +++ b/test/sandbox/case/pdlike.nix @@ -49,7 +49,7 @@ in env = [ "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus" "HOME=/var/lib/hakurei/u0/a5" - "PULSE_SERVER=unix:/run/user/65534/pulse/native" + "PIPEWIRE_REMOTE=/run/user/65534/pipewire-0" "SHELL=/run/current-system/sw/bin/bash" "TERM=linux" "USER=u0_a5" @@ -160,8 +160,12 @@ in user = fs "800001ed" { "65534" = fs "800001f8" { bus = fs "10001fd" null null; - pulse = fs "800001c0" { native = fs "10001b6" null null; } null; + pulse = fs "800001c0" { + native = fs "10001ff" null null; + pid = fs "1a4" null null; + } null; wayland-0 = fs "1000038" null null; + pipewire-0 = fs "1000038" null null; } null; } null; } null; @@ -245,13 +249,13 @@ in (ent ignore "/dev/console" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666") (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw") (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10005,gid=10005") - (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10005,gid=10005") + (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10005,gid=10005") (ent "/tmp/hakurei.0/runtime/5" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/hakurei.0/tmpdir/5" "/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10005,gid=10005") (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10005,gid=10005") (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") - (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore) + (ent ignore "/run/user/65534/pipewire-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") diff --git a/test/sandbox/case/preset.nix b/test/sandbox/case/preset.nix index aabe2b0..070446b 100644 --- a/test/sandbox/case/preset.nix +++ b/test/sandbox/case/preset.nix @@ -49,7 +49,7 @@ in env = [ "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus" "HOME=/var/lib/hakurei/u0/a1" - "PULSE_SERVER=unix:/run/user/65534/pulse/native" + "PIPEWIRE_REMOTE=/run/user/65534/pipewire-0" "SHELL=/run/current-system/sw/bin/bash" "TERM=linux" "USER=u0_a1" @@ -159,8 +159,12 @@ in user = fs "800001ed" { "65534" = fs "800001c0" { bus = fs "10001fd" null null; - pulse = fs "800001c0" { native = fs "10001b6" null null; } null; + pulse = fs "800001c0" { + native = fs "10001ff" null null; + pid = fs "1a4" null null; + } null; wayland-0 = fs "1000038" null null; + pipewire-0 = fs "1000038" null null; } null; } null; } null; @@ -243,12 +247,12 @@ in (ent "/" "/dev/pts" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,mode=620,ptmxmode=666") (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw") (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10001,gid=10001") - (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10001,gid=10001") + (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10001,gid=10001") (ent "/" "/tmp" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10001,gid=10001") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10001,gid=10001") (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10001,gid=10001") (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") - (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore) + (ent ignore "/run/user/65534/pipewire-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") diff --git a/test/sandbox/case/tty.nix b/test/sandbox/case/tty.nix index 8dbff16..0058332 100644 --- a/test/sandbox/case/tty.nix +++ b/test/sandbox/case/tty.nix @@ -50,7 +50,7 @@ in "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus" "DISPLAY=:0" "HOME=/var/lib/hakurei/u0/a2" - "PULSE_SERVER=unix:/run/user/65534/pulse/native" + "PIPEWIRE_REMOTE=/run/user/65534/pipewire-0" "SHELL=/run/current-system/sw/bin/bash" "TERM=linux" "USER=u0_a2" @@ -164,8 +164,12 @@ in user = fs "800001ed" { "65534" = fs "800001f8" { bus = fs "10001fd" null null; - pulse = fs "800001c0" { native = fs "10001b6" null null; } null; + pulse = fs "800001c0" { + native = fs "10001ff" null null; + pid = fs "1a4" null null; + } null; wayland-0 = fs "1000038" null null; + pipewire-0 = fs "1000038" null null; } null; } null; } null; @@ -252,14 +256,14 @@ in (ent ignore "/dev/console" "rw,nosuid,noexec,relatime" "devpts" "devpts" "rw,gid=3,mode=620,ptmxmode=666") (ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw") (ent "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10002,gid=10002") - (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10002,gid=10002") + (ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,mode=755,uid=10002,gid=10002") (ent "/tmp/hakurei.0/runtime/2" "/run/user/65534" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/" "/tmp" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10002,gid=10002") (ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10002,gid=10002") (ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10002,gid=10002") (ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/tmp/.X11-unix" "/tmp/.X11-unix" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") - (ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore) + (ent ignore "/run/user/65534/pipewire-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") (ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw") diff --git a/test/sandbox/test.py b/test/sandbox/test.py index ad38e73..6231134 100644 --- a/test/sandbox/test.py +++ b/test/sandbox/test.py @@ -83,4 +83,4 @@ swaymsg("exit", succeed=False) machine.wait_for_file("/tmp/sway-exit-ok") # Print hakurei runDir contents: -print(machine.succeed("find /run/user/1000/hakurei")) +print(machine.fail("ls /run/user/1000/hakurei")) diff --git a/test/test.py b/test/test.py index 305614f..d1edca4 100644 --- a/test/test.py +++ b/test/test.py @@ -160,17 +160,17 @@ machine.succeed("pkill -9 mako") # Check revert type selection: hakurei("-v run --wayland -X --dbus --pulse -u p0 foot && touch /tmp/p0-exit-ok") wait_for_window("p0@machine") -print(machine.succeed("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 10000")) +print(machine.succeed("getfacl --absolute-names --omit-header --numeric /tmp/hakurei.0/runtime | grep 10000")) hakurei("-v run --wayland -X --dbus --pulse -u p1 foot && touch /tmp/p1-exit-ok") wait_for_window("p1@machine") -print(machine.succeed("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 10000")) +print(machine.succeed("getfacl --absolute-names --omit-header --numeric /tmp/hakurei.0/runtime | grep 10000")) machine.send_chars("exit\n") machine.wait_for_file("/tmp/p1-exit-ok", timeout=15) # Verify acl is kept alive: -print(machine.succeed("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 10000")) +print(machine.succeed("getfacl --absolute-names --omit-header --numeric /tmp/hakurei.0/runtime | grep 10000")) machine.send_chars("exit\n") machine.wait_for_file("/tmp/p0-exit-ok", timeout=15) -machine.fail("getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep 10000") +machine.fail("getfacl --absolute-names --omit-header --numeric /tmp/hakurei.0/runtime | grep 10000") # Check invalid identifier fd behaviour: machine.fail('echo \'{"container":{"shell":"/proc/nonexistent","home":"/proc/nonexistent","path":"/proc/nonexistent"}}\' | sudo -u alice -i hakurei -v app --identifier-fd 32767 - 2>&1 | tee > /tmp/invalid-identifier-fd') @@ -219,15 +219,21 @@ machine.send_chars("exit\n") machine.wait_until_fails("pgrep foot", timeout=5) machine.fail(f"getfacl --absolute-names --omit-header --numeric /run/user/1000 | grep {hakurei_identity(0) + 10000}", timeout=5) -# Test PulseAudio (hakurei does not support PipeWire yet): +# Test pipewire-pulse: swaymsg("exec pa-foot") wait_for_window(f"u0_a{hakurei_identity(1)}@machine") machine.send_chars("clear; pactl info && touch /var/tmp/pulse-ok\n") machine.wait_for_file("/var/tmp/pulse-ok", timeout=15) collect_state_ui("pulse_wayland") -check_state("pa-foot", {"wayland": True, "pipewire": True, "pulse": True}) +check_state("pa-foot", {"wayland": True, "pipewire": True}) +# Test PipeWire: +machine.send_chars("clear; pw-cli i 0 && touch /var/tmp/pw-ok\n") +machine.wait_for_file("/var/tmp/pw-ok", timeout=15) +collect_state_ui("pipewire_wayland") machine.send_chars("exit\n") machine.wait_until_fails("pgrep foot", timeout=5) +# Test PipeWire SecurityContext: +machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v run --pulse pactl set-sink-mute @DEFAULT_SINK@ toggle") # Test XWayland (foot does not support X): swaymsg("exec x11-alacritty")