Compare commits
1 Commits
v0.3.3
...
42700ee3be
| Author | SHA1 | Date | |
|---|---|---|---|
|
42700ee3be
|
@@ -14,6 +14,7 @@ import (
|
|||||||
_ "unsafe" // for go:linkname
|
_ "unsafe" // for go:linkname
|
||||||
|
|
||||||
"hakurei.app/command"
|
"hakurei.app/command"
|
||||||
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/fhs"
|
"hakurei.app/container/fhs"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
@@ -186,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", "exec pipewire-pulse"},
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
config.Container.Filesystem = append(config.Container.Filesystem,
|
config.Container.Filesystem = append(config.Container.Filesystem,
|
||||||
// opportunistically bind kvm
|
// opportunistically bind kvm
|
||||||
hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{
|
hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
testers,
|
nixosTest,
|
||||||
callPackage,
|
callPackage,
|
||||||
|
|
||||||
system,
|
system,
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
let
|
let
|
||||||
buildPackage = self.buildPackage.${system};
|
buildPackage = self.buildPackage.${system};
|
||||||
in
|
in
|
||||||
testers.nixosTest {
|
nixosTest {
|
||||||
name = "hpkg";
|
name = "hpkg";
|
||||||
nodes.machine = {
|
nodes.machine = {
|
||||||
environment.etc = {
|
environment.etc = {
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ func TestIsAutoRootBindable(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
var msg message.Msg
|
var msg message.Msg
|
||||||
if tc.log {
|
if tc.log {
|
||||||
msg = &kstub{nil, nil, stub.New(t, func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { panic("unreachable") }, stub.Expect{Calls: []stub.Call{
|
msg = &kstub{nil, stub.New(t, func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { panic("unreachable") }, stub.Expect{Calls: []stub.Call{
|
||||||
call("verbose", stub.ExpectArgs{[]any{"got unexpected root entry"}}, nil, nil),
|
call("verbose", stub.ExpectArgs{[]any{"got unexpected root entry"}}, nil, nil),
|
||||||
}})}
|
}})}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,8 +162,7 @@ func checkSimple(t *testing.T, fname string, testCases []simpleTestCase) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
wait4signal := make(chan struct{})
|
wait4signal := make(chan struct{})
|
||||||
lockNotify := make(chan struct{})
|
k := &kstub{wait4signal, stub.New(t, func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { return &kstub{wait4signal, s} }, tc.want)}
|
||||||
k := &kstub{wait4signal, lockNotify, stub.New(t, func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { return &kstub{wait4signal, lockNotify, s} }, tc.want)}
|
|
||||||
defer stub.HandleExit(t)
|
defer stub.HandleExit(t)
|
||||||
if err := tc.f(k); !reflect.DeepEqual(err, tc.wantErr) {
|
if err := tc.f(k); !reflect.DeepEqual(err, tc.wantErr) {
|
||||||
t.Errorf("%s: error = %v, want %v", fname, err, tc.wantErr)
|
t.Errorf("%s: error = %v, want %v", fname, err, tc.wantErr)
|
||||||
@@ -201,8 +200,8 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
k := &kstub{nil, nil, stub.New(t,
|
k := &kstub{nil, stub.New(t,
|
||||||
func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { return &kstub{nil, nil, s} },
|
func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { return &kstub{nil, s} },
|
||||||
stub.Expect{Calls: slices.Concat(tc.early, []stub.Call{{Name: stub.CallSeparator}}, tc.apply)},
|
stub.Expect{Calls: slices.Concat(tc.early, []stub.Call{{Name: stub.CallSeparator}}, tc.apply)},
|
||||||
)}
|
)}
|
||||||
state := &setupState{Params: tc.params, Msg: k}
|
state := &setupState{Params: tc.params, Msg: k}
|
||||||
@@ -323,19 +322,12 @@ const (
|
|||||||
|
|
||||||
type kstub struct {
|
type kstub struct {
|
||||||
wait4signal chan struct{}
|
wait4signal chan struct{}
|
||||||
lockNotify chan struct{}
|
|
||||||
*stub.Stub[syscallDispatcher]
|
*stub.Stub[syscallDispatcher]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *kstub) new(f func(k syscallDispatcher)) { k.Helper(); k.New(f) }
|
func (k *kstub) new(f func(k syscallDispatcher)) { k.Helper(); k.New(f) }
|
||||||
|
|
||||||
func (k *kstub) lockOSThread() {
|
func (k *kstub) lockOSThread() { k.Helper(); k.Expects("lockOSThread") }
|
||||||
k.Helper()
|
|
||||||
expect := k.Expects("lockOSThread")
|
|
||||||
if k.lockNotify != nil && expect.Ret == magicWait4Signal {
|
|
||||||
<-k.lockNotify
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *kstub) setPtracer(pid uintptr) error {
|
func (k *kstub) setPtracer(pid uintptr) error {
|
||||||
k.Helper()
|
k.Helper()
|
||||||
@@ -480,10 +472,6 @@ func (k *kstub) notify(c chan<- os.Signal, sig ...os.Signal) {
|
|||||||
k.FailNow()
|
k.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if k.lockNotify != nil && expect.Ret == magicWait4Signal {
|
|
||||||
defer close(k.lockNotify)
|
|
||||||
}
|
|
||||||
|
|
||||||
// export channel for external instrumentation
|
// export channel for external instrumentation
|
||||||
if chanf, ok := expect.Args[0].(func(c chan<- os.Signal)); ok && chanf != nil {
|
if chanf, ok := expect.Args[0].(func(c chan<- os.Signal)); ok && chanf != nil {
|
||||||
chanf(c)
|
chanf(c)
|
||||||
|
|||||||
@@ -380,9 +380,6 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
|||||||
err error
|
err error
|
||||||
wpid = -2
|
wpid = -2
|
||||||
wstatus WaitStatus
|
wstatus WaitStatus
|
||||||
|
|
||||||
// whether initial process has started
|
|
||||||
started bool
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// keep going until no child process is left
|
// keep going until no child process is left
|
||||||
@@ -409,10 +406,6 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !started {
|
|
||||||
started = initialProcessStarted.Load()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = EINTR
|
err = EINTR
|
||||||
for errors.Is(err, EINTR) {
|
for errors.Is(err, EINTR) {
|
||||||
wpid, err = k.wait4(-1, &wstatus, 0, nil)
|
wpid, err = k.wait4(-1, &wstatus, 0, nil)
|
||||||
@@ -421,7 +414,7 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
|||||||
|
|
||||||
if !errors.Is(err, ECHILD) {
|
if !errors.Is(err, ECHILD) {
|
||||||
k.printf(msg, "unexpected wait4 response: %v", err)
|
k.printf(msg, "unexpected wait4 response: %v", err)
|
||||||
} else if !started {
|
} else if !initialProcessStarted.Load() {
|
||||||
// initial process has not yet been reached and all daemons
|
// initial process has not yet been reached and all daemons
|
||||||
// terminated or none were started in the first place
|
// terminated or none were started in the first place
|
||||||
time.Sleep(500 * time.Microsecond)
|
time.Sleep(500 * time.Microsecond)
|
||||||
|
|||||||
@@ -1992,7 +1992,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
|
|
||||||
/* wait4 */
|
/* wait4 */
|
||||||
Tracks: []stub.Expect{{Calls: []stub.Call{
|
Tracks: []stub.Expect{{Calls: []stub.Call{
|
||||||
call("lockOSThread", stub.ExpectArgs{}, magicWait4Signal, nil),
|
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
|
||||||
|
|
||||||
// this terminates the goroutine at the call, preventing it from leaking while preserving behaviour
|
// this terminates the goroutine at the call, preventing it from leaking while preserving behaviour
|
||||||
call("wait4", stub.ExpectArgs{-1, nil, 0, nil, stub.PanicExit}, 0, syscall.ECHILD),
|
call("wait4", stub.ExpectArgs{-1, nil, 0, nil, stub.PanicExit}, 0, syscall.ECHILD),
|
||||||
@@ -2075,7 +2075,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(10)}}, nil, nil),
|
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(10)}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
||||||
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
||||||
call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- CancelSignal }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, magicWait4Signal, nil),
|
call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- CancelSignal }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
|
||||||
call("verbose", stub.ExpectArgs{[]any{"forwarding context cancellation"}}, nil, nil),
|
call("verbose", stub.ExpectArgs{[]any{"forwarding context cancellation"}}, nil, nil),
|
||||||
// magicWait4Signal as ret causes wait4 stub to unblock
|
// magicWait4Signal as ret causes wait4 stub to unblock
|
||||||
call("signal", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent", os.Interrupt}, magicWait4Signal, stub.UniqueError(9)),
|
call("signal", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent", os.Interrupt}, magicWait4Signal, stub.UniqueError(9)),
|
||||||
@@ -2090,7 +2090,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
|
|
||||||
/* wait4 */
|
/* wait4 */
|
||||||
Tracks: []stub.Expect{{Calls: []stub.Call{
|
Tracks: []stub.Expect{{Calls: []stub.Call{
|
||||||
call("lockOSThread", stub.ExpectArgs{}, magicWait4Signal, nil),
|
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
|
||||||
|
|
||||||
// magicWait4Signal as args[4] causes this to block until simulated signal is delivered
|
// magicWait4Signal as args[4] causes this to block until simulated signal is delivered
|
||||||
call("wait4", stub.ExpectArgs{-1, syscall.WaitStatus(0xfade01ce), 0, nil, magicWait4Signal}, 0xbad, nil),
|
call("wait4", stub.ExpectArgs{-1, syscall.WaitStatus(0xfade01ce), 0, nil, magicWait4Signal}, 0xbad, nil),
|
||||||
@@ -2175,7 +2175,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(7)}}, nil, nil),
|
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(7)}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
||||||
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
||||||
call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- syscall.SIGQUIT }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, magicWait4Signal, nil),
|
call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- syscall.SIGQUIT }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"got %s, forwarding to initial process", []any{"quit"}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"got %s, forwarding to initial process", []any{"quit"}}, nil, nil),
|
||||||
// magicWait4Signal as ret causes wait4 stub to unblock
|
// magicWait4Signal as ret causes wait4 stub to unblock
|
||||||
call("signal", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent", syscall.SIGQUIT}, magicWait4Signal, stub.UniqueError(0xfe)),
|
call("signal", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent", syscall.SIGQUIT}, magicWait4Signal, stub.UniqueError(0xfe)),
|
||||||
@@ -2190,7 +2190,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
|
|
||||||
/* wait4 */
|
/* wait4 */
|
||||||
Tracks: []stub.Expect{{Calls: []stub.Call{
|
Tracks: []stub.Expect{{Calls: []stub.Call{
|
||||||
call("lockOSThread", stub.ExpectArgs{}, magicWait4Signal, nil),
|
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
|
||||||
|
|
||||||
// magicWait4Signal as args[4] causes this to block until simulated signal is delivered
|
// magicWait4Signal as args[4] causes this to block until simulated signal is delivered
|
||||||
call("wait4", stub.ExpectArgs{-1, syscall.WaitStatus(0xfade01ce), 0, nil, magicWait4Signal}, 0xbad, nil),
|
call("wait4", stub.ExpectArgs{-1, syscall.WaitStatus(0xfade01ce), 0, nil, magicWait4Signal}, 0xbad, nil),
|
||||||
@@ -2275,7 +2275,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(7)}}, nil, nil),
|
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(7)}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
||||||
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
||||||
call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- os.Interrupt }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, magicWait4Signal, nil),
|
call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- os.Interrupt }, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"got %s", []any{"interrupt"}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"got %s", []any{"interrupt"}}, nil, nil),
|
||||||
call("beforeExit", stub.ExpectArgs{}, nil, nil),
|
call("beforeExit", stub.ExpectArgs{}, nil, nil),
|
||||||
call("exit", stub.ExpectArgs{0}, nil, nil),
|
call("exit", stub.ExpectArgs{0}, nil, nil),
|
||||||
@@ -2283,7 +2283,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
|
|
||||||
/* wait4 */
|
/* wait4 */
|
||||||
Tracks: []stub.Expect{{Calls: []stub.Call{
|
Tracks: []stub.Expect{{Calls: []stub.Call{
|
||||||
call("lockOSThread", stub.ExpectArgs{}, magicWait4Signal, nil),
|
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
|
||||||
|
|
||||||
// this terminates the goroutine at the call, preventing it from leaking while preserving behaviour
|
// this terminates the goroutine at the call, preventing it from leaking while preserving behaviour
|
||||||
call("wait4", stub.ExpectArgs{-1, nil, 0, nil, stub.PanicExit}, 0, syscall.ECHILD),
|
call("wait4", stub.ExpectArgs{-1, nil, 0, nil, stub.PanicExit}, 0, syscall.ECHILD),
|
||||||
@@ -2366,7 +2366,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(5)}}, nil, nil),
|
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(5)}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
||||||
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
||||||
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, magicWait4Signal, nil),
|
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
|
||||||
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
||||||
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil),
|
||||||
@@ -2377,7 +2377,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
|
|
||||||
/* wait4 */
|
/* wait4 */
|
||||||
Tracks: []stub.Expect{{Calls: []stub.Call{
|
Tracks: []stub.Expect{{Calls: []stub.Call{
|
||||||
call("lockOSThread", stub.ExpectArgs{}, magicWait4Signal, nil),
|
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
|
||||||
|
|
||||||
call("wait4", stub.ExpectArgs{-1, syscall.WaitStatus(0xfade01ce), 0, nil}, 0xbad, nil),
|
call("wait4", stub.ExpectArgs{-1, syscall.WaitStatus(0xfade01ce), 0, nil}, 0xbad, nil),
|
||||||
// this terminates the goroutine at the call, preventing it from leaking while preserving behaviour
|
// this terminates the goroutine at the call, preventing it from leaking while preserving behaviour
|
||||||
@@ -2461,7 +2461,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(3)}}, nil, nil),
|
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(3)}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
||||||
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
||||||
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, magicWait4Signal, nil),
|
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
|
||||||
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
||||||
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"initial process exited with signal %s", []any{syscall.Signal(0x4e)}}, nil, nil),
|
||||||
@@ -2471,7 +2471,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
|
|
||||||
/* wait4 */
|
/* wait4 */
|
||||||
Tracks: []stub.Expect{{Calls: []stub.Call{
|
Tracks: []stub.Expect{{Calls: []stub.Call{
|
||||||
call("lockOSThread", stub.ExpectArgs{}, magicWait4Signal, nil),
|
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
|
||||||
|
|
||||||
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
||||||
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
||||||
@@ -2599,7 +2599,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(1)}}, nil, nil),
|
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(1)}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
|
||||||
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
|
||||||
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, magicWait4Signal, nil),
|
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
|
||||||
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
||||||
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"initial process exited with code %d", []any{1}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"initial process exited with code %d", []any{1}}, nil, nil),
|
||||||
@@ -2609,7 +2609,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
|
|
||||||
/* wait4 */
|
/* wait4 */
|
||||||
Tracks: []stub.Expect{{Calls: []stub.Call{
|
Tracks: []stub.Expect{{Calls: []stub.Call{
|
||||||
call("lockOSThread", stub.ExpectArgs{}, magicWait4Signal, nil),
|
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
|
||||||
|
|
||||||
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
||||||
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
||||||
@@ -2741,7 +2741,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(0)}}, nil, nil),
|
call("fatalf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(0)}}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/bin/zsh")}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{check.MustAbs("/bin/zsh")}}, nil, nil),
|
||||||
call("start", stub.ExpectArgs{"/bin/zsh", []string{"zsh", "-c", "exec vim"}, []string{"DISPLAY=:0"}, "/.hakurei"}, &os.Process{Pid: 0xcafe}, nil),
|
call("start", stub.ExpectArgs{"/bin/zsh", []string{"zsh", "-c", "exec vim"}, []string{"DISPLAY=:0"}, "/.hakurei"}, &os.Process{Pid: 0xcafe}, nil),
|
||||||
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, magicWait4Signal, nil),
|
call("notify", stub.ExpectArgs{nil, []os.Signal{CancelSignal, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT}}, nil, nil),
|
||||||
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
||||||
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
||||||
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
call("verbose", stub.ExpectArgs{[]any{os.ErrInvalid.Error()}}, nil, nil),
|
||||||
@@ -2752,7 +2752,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
|
|
||||||
/* wait4 */
|
/* wait4 */
|
||||||
Tracks: []stub.Expect{{Calls: []stub.Call{
|
Tracks: []stub.Expect{{Calls: []stub.Call{
|
||||||
call("lockOSThread", stub.ExpectArgs{}, magicWait4Signal, nil),
|
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
|
||||||
|
|
||||||
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
||||||
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
call("wait4", stub.ExpectArgs{-1, nil, 0, nil}, 0, syscall.EINTR),
|
||||||
|
|||||||
16
flake.lock
generated
16
flake.lock
generated
@@ -7,32 +7,32 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1765384171,
|
"lastModified": 1756679287,
|
||||||
"narHash": "sha256-FuFtkJrW1Z7u+3lhzPRau69E0CNjADku1mLQQflUORo=",
|
"narHash": "sha256-Xd1vOeY9ccDf5VtVK12yM0FS6qqvfUop8UQlxEB+gTQ=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "44777152652bc9eacf8876976fa72cc77ca8b9d8",
|
"rev": "07fc025fe10487dd80f2ec694f1cd790e752d0e8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"ref": "release-25.11",
|
"ref": "release-25.05",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1765311797,
|
"lastModified": 1757020766,
|
||||||
"narHash": "sha256-mSD5Ob7a+T2RNjvPvOA1dkJHGVrNVl8ZOrAwBjKBDQo=",
|
"narHash": "sha256-PLoSjHRa2bUbi1x9HoXgTx2AiuzNXs54c8omhadyvp0=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "09eb77e94fa25202af8f3e81ddc7353d9970ac1b",
|
"rev": "fe83bbdde2ccdc2cb9573aa846abe8363f79a97a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-25.11",
|
"ref": "nixos-25.05",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
description = "hakurei container tool and nixos module";
|
description = "hakurei container tool and nixos module";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||||
|
|
||||||
home-manager = {
|
home-manager = {
|
||||||
url = "github:nix-community/home-manager/release-25.11";
|
url = "github:nix-community/home-manager/release-25.05";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -185,13 +185,13 @@
|
|||||||
hakurei =
|
hakurei =
|
||||||
let
|
let
|
||||||
# this is used for interactive vm testing during development, where tests might be broken
|
# this is used for interactive vm testing during development, where tests might be broken
|
||||||
package = self.packages.${pkgs.stdenv.hostPlatform.system}.hakurei.override {
|
package = self.packages.${pkgs.system}.hakurei.override {
|
||||||
buildGoModule = previousArgs: pkgs.pkgsStatic.buildGoModule (previousArgs // { doCheck = false; });
|
buildGoModule = previousArgs: pkgs.pkgsStatic.buildGoModule (previousArgs // { doCheck = false; });
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
inherit package;
|
inherit package;
|
||||||
hsuPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.hsu.override { hakurei = package; };
|
hsuPackage = self.packages.${pkgs.system}.hsu.override { hakurei = package; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,46 +30,12 @@ type Config struct {
|
|||||||
// This option is unsupported and most likely enables full control over the Wayland
|
// This option is unsupported and most likely enables full control over the Wayland
|
||||||
// session. Do not set this to true unless you are sure you know what you are doing.
|
// session. Do not set this to true unless you are sure you know what you are doing.
|
||||||
DirectWayland bool `json:"direct_wayland,omitempty"`
|
DirectWayland bool `json:"direct_wayland,omitempty"`
|
||||||
// Direct access to the PipeWire socket established via SecurityContext::Create, no
|
|
||||||
// attempt is made to start the pipewire-pulse server.
|
|
||||||
//
|
|
||||||
// The SecurityContext machinery is fatally flawed, it blindly sets read and execute
|
|
||||||
// bits on all objects for clients with the lowest achievable privilege level (by
|
|
||||||
// setting PW_KEY_ACCESS to "restricted"). This enables them to call any method
|
|
||||||
// targeting any object, and since Registry::Destroy checks for the read and execute bit,
|
|
||||||
// allows the destruction of any object other than PW_ID_CORE as well. This behaviour
|
|
||||||
// is implemented separately in media-session and wireplumber, with the wireplumber
|
|
||||||
// implementation in Lua via an embedded Lua vm. In all known setups, wireplumber is
|
|
||||||
// in use, and there is no known way to change its behaviour and set permissions
|
|
||||||
// differently without replacing the Lua script. Also, since PipeWire relies on these
|
|
||||||
// permissions to work, reducing them is not possible.
|
|
||||||
//
|
|
||||||
// Currently, the only other sandboxed use case is flatpak, which is not aware of
|
|
||||||
// PipeWire and blindly exposes the bare PulseAudio socket to the container (behaves
|
|
||||||
// like DirectPulse). This socket is backed by the pipewire-pulse compatibility daemon,
|
|
||||||
// which obtains client pid via the SO_PEERCRED option. The PipeWire daemon, pipewire-pulse
|
|
||||||
// daemon and the session manager daemon then separately performs the /.flatpak-info hack
|
|
||||||
// described in https://git.gensokyo.uk/security/hakurei/issues/21. Under such use case,
|
|
||||||
// since the client has no direct access to PipeWire, insecure parts of the protocol are
|
|
||||||
// obscured by pipewire-pulse simply not implementing them, and thus hiding the flaws
|
|
||||||
// described above.
|
|
||||||
//
|
|
||||||
// Hakurei does not rely on the /.flatpak-info hack. Instead, a socket is sets up via
|
|
||||||
// SecurityContext. A pipewire-pulse server connected through it achieves the same
|
|
||||||
// permissions as flatpak does via the /.flatpak-info hack and is maintained for the
|
|
||||||
// life of the container.
|
|
||||||
//
|
|
||||||
// This option is unsupported and enables a denial-of-service attack as the sandboxed
|
|
||||||
// client is able to destroy any client object and thus disconnecting them from PipeWire,
|
|
||||||
// or destroy the SecurityContext object preventing any further container creation.
|
|
||||||
// Do not set this to true, it is insecure under any configuration.
|
|
||||||
DirectPipeWire bool `json:"direct_pipewire,omitempty"`
|
|
||||||
// Direct access to PulseAudio socket, no attempt is made to establish pipewire-pulse
|
// Direct access to PulseAudio socket, no attempt is made to establish pipewire-pulse
|
||||||
// server via a PipeWire socket with a SecurityContext attached and the bare socket
|
// server via a PipeWire socket with a SecurityContext attached and the bare socket
|
||||||
// is made available to the container.
|
// is made available to the container.
|
||||||
//
|
//
|
||||||
// This option is unsupported and enables arbitrary code execution as the PulseAudio
|
// This option is unsupported and enables arbitrary code execution as the PulseAudio
|
||||||
// server. Do not set this to true, it is insecure under any configuration.
|
// server. Do not set this to true, this is insecure under any configuration.
|
||||||
DirectPulse bool `json:"direct_pulse,omitempty"`
|
DirectPulse bool `json:"direct_pulse,omitempty"`
|
||||||
|
|
||||||
// Extra acl updates to perform before setuid.
|
// Extra acl updates to perform before setuid.
|
||||||
|
|||||||
@@ -53,10 +53,6 @@ type syscallDispatcher interface {
|
|||||||
readdir(name string) ([]os.DirEntry, error)
|
readdir(name string) ([]os.DirEntry, error)
|
||||||
// tempdir provides [os.TempDir].
|
// tempdir provides [os.TempDir].
|
||||||
tempdir() string
|
tempdir() string
|
||||||
// mkdir provides [os.Mkdir].
|
|
||||||
mkdir(name string, perm os.FileMode) error
|
|
||||||
// removeAll provides [os.RemoveAll].
|
|
||||||
removeAll(path string) error
|
|
||||||
// exit provides [os.Exit].
|
// exit provides [os.Exit].
|
||||||
exit(code int)
|
exit(code int)
|
||||||
|
|
||||||
@@ -66,8 +62,6 @@ type syscallDispatcher interface {
|
|||||||
// lookupGroupId calls [user.LookupGroup] and returns the Gid field of the resulting [user.Group] struct.
|
// lookupGroupId calls [user.LookupGroup] and returns the Gid field of the resulting [user.Group] struct.
|
||||||
lookupGroupId(name string) (string, error)
|
lookupGroupId(name string) (string, error)
|
||||||
|
|
||||||
// lookPath provides exec.LookPath.
|
|
||||||
lookPath(file string) (string, error)
|
|
||||||
// cmdOutput provides the Output method of [exec.Cmd].
|
// cmdOutput provides the Output method of [exec.Cmd].
|
||||||
cmdOutput(cmd *exec.Cmd) ([]byte, error)
|
cmdOutput(cmd *exec.Cmd) ([]byte, error)
|
||||||
|
|
||||||
@@ -127,8 +121,6 @@ func (direct) stat(name string) (os.FileInfo, error) { return os.Stat(name)
|
|||||||
func (direct) open(name string) (osFile, error) { return os.Open(name) }
|
func (direct) open(name string) (osFile, error) { return os.Open(name) }
|
||||||
func (direct) readdir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) }
|
func (direct) readdir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) }
|
||||||
func (direct) tempdir() string { return os.TempDir() }
|
func (direct) tempdir() string { return os.TempDir() }
|
||||||
func (direct) mkdir(name string, perm os.FileMode) error { return os.Mkdir(name, perm) }
|
|
||||||
func (direct) removeAll(path string) error { return os.RemoveAll(path) }
|
|
||||||
func (direct) exit(code int) { os.Exit(code) }
|
func (direct) exit(code int) { os.Exit(code) }
|
||||||
|
|
||||||
func (direct) evalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) }
|
func (direct) evalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) }
|
||||||
@@ -142,7 +134,6 @@ func (direct) lookupGroupId(name string) (gid string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (direct) lookPath(file string) (string, error) { return exec.LookPath(file) }
|
|
||||||
func (direct) cmdOutput(cmd *exec.Cmd) ([]byte, error) { return cmd.Output() }
|
func (direct) cmdOutput(cmd *exec.Cmd) ([]byte, error) { return cmd.Output() }
|
||||||
|
|
||||||
func (direct) notifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) {
|
func (direct) notifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) {
|
||||||
|
|||||||
@@ -701,13 +701,10 @@ func (panicDispatcher) stat(string) (os.FileInfo, error) { pa
|
|||||||
func (panicDispatcher) open(string) (osFile, error) { panic("unreachable") }
|
func (panicDispatcher) open(string) (osFile, error) { panic("unreachable") }
|
||||||
func (panicDispatcher) readdir(string) ([]os.DirEntry, error) { panic("unreachable") }
|
func (panicDispatcher) readdir(string) ([]os.DirEntry, error) { panic("unreachable") }
|
||||||
func (panicDispatcher) tempdir() string { panic("unreachable") }
|
func (panicDispatcher) tempdir() string { panic("unreachable") }
|
||||||
func (panicDispatcher) mkdir(string, os.FileMode) error { panic("unreachable") }
|
|
||||||
func (panicDispatcher) removeAll(string) error { panic("unreachable") }
|
|
||||||
func (panicDispatcher) exit(int) { panic("unreachable") }
|
func (panicDispatcher) exit(int) { panic("unreachable") }
|
||||||
func (panicDispatcher) evalSymlinks(string) (string, error) { panic("unreachable") }
|
func (panicDispatcher) evalSymlinks(string) (string, error) { panic("unreachable") }
|
||||||
func (panicDispatcher) prctl(uintptr, uintptr, uintptr) error { panic("unreachable") }
|
func (panicDispatcher) prctl(uintptr, uintptr, uintptr) error { panic("unreachable") }
|
||||||
func (panicDispatcher) lookupGroupId(string) (string, error) { panic("unreachable") }
|
func (panicDispatcher) lookupGroupId(string) (string, error) { panic("unreachable") }
|
||||||
func (panicDispatcher) lookPath(string) (string, error) { panic("unreachable") }
|
|
||||||
func (panicDispatcher) cmdOutput(*exec.Cmd) ([]byte, error) { panic("unreachable") }
|
func (panicDispatcher) cmdOutput(*exec.Cmd) ([]byte, error) { panic("unreachable") }
|
||||||
func (panicDispatcher) overflowUid(message.Msg) int { panic("unreachable") }
|
func (panicDispatcher) overflowUid(message.Msg) int { panic("unreachable") }
|
||||||
func (panicDispatcher) overflowGid(message.Msg) int { panic("unreachable") }
|
func (panicDispatcher) overflowGid(message.Msg) int { panic("unreachable") }
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ type outcomeState struct {
|
|||||||
// Copied from their respective exported values.
|
// Copied from their respective exported values.
|
||||||
mapuid, mapgid *stringPair[int]
|
mapuid, mapgid *stringPair[int]
|
||||||
|
|
||||||
// Copied from [env.Paths] per-process.
|
// Copied from [EnvPaths] per-process.
|
||||||
sc hst.Paths
|
sc hst.Paths
|
||||||
*env.Paths
|
*env.Paths
|
||||||
|
|
||||||
@@ -172,8 +172,6 @@ type outcomeStateSys struct {
|
|||||||
|
|
||||||
// Copied from [hst.Config]. Safe for read by spWaylandOp.toSystem only.
|
// Copied from [hst.Config]. Safe for read by spWaylandOp.toSystem only.
|
||||||
directWayland bool
|
directWayland bool
|
||||||
// Copied from [hst.Config]. Safe for read by spPipeWireOp.toSystem only.
|
|
||||||
directPipeWire bool
|
|
||||||
// Copied from [hst.Config]. Safe for read by spPulseOp.toSystem only.
|
// Copied from [hst.Config]. Safe for read by spPulseOp.toSystem only.
|
||||||
directPulse bool
|
directPulse bool
|
||||||
// Copied header from [hst.Config]. Safe for read by spFilesystemOp.toSystem only.
|
// Copied header from [hst.Config]. Safe for read by spFilesystemOp.toSystem only.
|
||||||
@@ -189,8 +187,9 @@ type outcomeStateSys struct {
|
|||||||
func (s *outcomeState) newSys(config *hst.Config, sys *system.I) *outcomeStateSys {
|
func (s *outcomeState) newSys(config *hst.Config, sys *system.I) *outcomeStateSys {
|
||||||
return &outcomeStateSys{
|
return &outcomeStateSys{
|
||||||
appId: config.ID, et: config.Enablements.Unwrap(),
|
appId: config.ID, et: config.Enablements.Unwrap(),
|
||||||
directWayland: config.DirectWayland, directPipeWire: config.DirectPipeWire, directPulse: config.DirectPulse,
|
directWayland: config.DirectWayland, directPulse: config.DirectPulse,
|
||||||
extraPerms: config.ExtraPerms, sessionBus: config.SessionBus, systemBus: config.SystemBus,
|
extraPerms: config.ExtraPerms,
|
||||||
|
sessionBus: config.SessionBus, systemBus: config.SystemBus,
|
||||||
sys: sys, outcomeState: s,
|
sys: sys, outcomeState: s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,10 +256,6 @@ type outcomeStateParams struct {
|
|||||||
// Populated by spRuntimeOp.
|
// Populated by spRuntimeOp.
|
||||||
runtimeDir *check.Absolute
|
runtimeDir *check.Absolute
|
||||||
|
|
||||||
// Path to pipewire-pulse server.
|
|
||||||
// Populated by spPipeWireOp if DirectPipeWire is false.
|
|
||||||
pipewirePulsePath *check.Absolute
|
|
||||||
|
|
||||||
as hst.ApplyState
|
as hst.ApplyState
|
||||||
*outcomeState
|
*outcomeState
|
||||||
}
|
}
|
||||||
@@ -300,7 +295,7 @@ func (state *outcomeStateSys) toSystem() error {
|
|||||||
// optional via enablements
|
// optional via enablements
|
||||||
&spWaylandOp{},
|
&spWaylandOp{},
|
||||||
&spX11Op{},
|
&spX11Op{},
|
||||||
&spPipeWireOp{},
|
spPipeWireOp{},
|
||||||
&spPulseOp{},
|
&spPulseOp{},
|
||||||
&spDBusOp{},
|
&spDBusOp{},
|
||||||
|
|
||||||
|
|||||||
@@ -68,11 +68,7 @@ func TestOutcomeRun(t *testing.T) {
|
|||||||
).
|
).
|
||||||
|
|
||||||
// spPipeWireOp
|
// spPipeWireOp
|
||||||
PipeWire(
|
PipeWire(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pipewire")).
|
||||||
m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pipewire"),
|
|
||||||
"org.chromium.Chromium",
|
|
||||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
||||||
).
|
|
||||||
|
|
||||||
// spDBusOp
|
// spDBusOp
|
||||||
MustProxyDBus(
|
MustProxyDBus(
|
||||||
@@ -100,6 +96,7 @@ func TestOutcomeRun(t *testing.T) {
|
|||||||
"GOOGLE_DEFAULT_CLIENT_ID=77185425430.apps.googleusercontent.com",
|
"GOOGLE_DEFAULT_CLIENT_ID=77185425430.apps.googleusercontent.com",
|
||||||
"GOOGLE_DEFAULT_CLIENT_SECRET=OTJgUOQcT7lO7GsGZq2G4IlT",
|
"GOOGLE_DEFAULT_CLIENT_SECRET=OTJgUOQcT7lO7GsGZq2G4IlT",
|
||||||
"HOME=/data/data/org.chromium.Chromium",
|
"HOME=/data/data/org.chromium.Chromium",
|
||||||
|
"PIPEWIRE_REMOTE=/run/user/1971/pipewire-0",
|
||||||
"SHELL=/run/current-system/sw/bin/zsh",
|
"SHELL=/run/current-system/sw/bin/zsh",
|
||||||
"TERM=xterm-256color",
|
"TERM=xterm-256color",
|
||||||
"USER=chronos",
|
"USER=chronos",
|
||||||
@@ -149,6 +146,9 @@ func TestOutcomeRun(t *testing.T) {
|
|||||||
// spWaylandOp
|
// spWaylandOp
|
||||||
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1971/wayland-0"), 0).
|
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/wayland"), m("/run/user/1971/wayland-0"), 0).
|
||||||
|
|
||||||
|
// spPipeWireOp
|
||||||
|
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/pipewire"), m("/run/user/1971/pipewire-0"), 0).
|
||||||
|
|
||||||
// spDBusOp
|
// spDBusOp
|
||||||
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bus"), m("/run/user/1971/bus"), 0).
|
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bus"), m("/run/user/1971/bus"), 0).
|
||||||
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0).
|
Bind(m("/tmp/hakurei.0/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/system_bus_socket"), m("/var/run/dbus/system_bus_socket"), 0).
|
||||||
@@ -170,7 +170,7 @@ func TestOutcomeRun(t *testing.T) {
|
|||||||
Remount(fhs.AbsRoot, syscall.MS_RDONLY),
|
Remount(fhs.AbsRoot, syscall.MS_RDONLY),
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{"nixos permissive defaults no enablements", new(stubNixOS), &hst.Config{DirectPipeWire: true, Container: &hst.ContainerConfig{
|
{"nixos permissive defaults no enablements", new(stubNixOS), &hst.Config{Container: &hst.ContainerConfig{
|
||||||
Filesystem: []hst.FilesystemConfigJSON{
|
Filesystem: []hst.FilesystemConfigJSON{
|
||||||
{FilesystemConfig: &hst.FSBind{
|
{FilesystemConfig: &hst.FSBind{
|
||||||
Target: fhs.AbsRoot,
|
Target: fhs.AbsRoot,
|
||||||
@@ -252,8 +252,6 @@ func TestOutcomeRun(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
|
|
||||||
{"nixos permissive defaults chromium", new(stubNixOS), &hst.Config{
|
{"nixos permissive defaults chromium", new(stubNixOS), &hst.Config{
|
||||||
DirectPipeWire: true,
|
|
||||||
|
|
||||||
ID: "org.chromium.Chromium",
|
ID: "org.chromium.Chromium",
|
||||||
Identity: 9,
|
Identity: 9,
|
||||||
Groups: []string{"video"},
|
Groups: []string{"video"},
|
||||||
@@ -337,7 +335,7 @@ func TestOutcomeRun(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).
|
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).
|
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").
|
Wayland(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/wayland"), m("/run/user/1971/wayland-0"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c").
|
||||||
PipeWire(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/pipewire"), "org.chromium.Chromium", "ebf083d1b175911782d413369b64ce7c").
|
PipeWire(m("/tmp/hakurei.0/ebf083d1b175911782d413369b64ce7c/pipewire")).
|
||||||
MustProxyDBus(&hst.BusConfig{
|
MustProxyDBus(&hst.BusConfig{
|
||||||
Talk: []string{
|
Talk: []string{
|
||||||
"org.freedesktop.Notifications",
|
"org.freedesktop.Notifications",
|
||||||
@@ -424,8 +422,6 @@ func TestOutcomeRun(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
|
|
||||||
{"nixos chromium direct wayland", new(stubNixOS), &hst.Config{
|
{"nixos chromium direct wayland", new(stubNixOS), &hst.Config{
|
||||||
DirectPipeWire: true,
|
|
||||||
|
|
||||||
ID: "org.chromium.Chromium",
|
ID: "org.chromium.Chromium",
|
||||||
Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPipeWire | hst.EPulse),
|
Enablements: hst.NewEnablements(hst.EWayland | hst.EDBus | hst.EPipeWire | hst.EPulse),
|
||||||
Container: &hst.ContainerConfig{
|
Container: &hst.ContainerConfig{
|
||||||
@@ -490,7 +486,7 @@ func TestOutcomeRun(t *testing.T) {
|
|||||||
Ensure(m("/run/user/1971/hakurei"), 0700).UpdatePermType(system.User, m("/run/user/1971/hakurei"), acl.Execute).
|
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).
|
UpdatePermType(hst.EWayland, m("/run/user/1971/wayland-0"), acl.Read, acl.Write, acl.Execute).
|
||||||
Ephemeral(system.Process, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1"), 0711).
|
Ephemeral(system.Process, m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1"), 0711).
|
||||||
PipeWire(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/pipewire"), "org.chromium.Chromium", "8e2c76b066dabe574cf073bdb46eb5c1").
|
PipeWire(m("/tmp/hakurei.0/8e2c76b066dabe574cf073bdb46eb5c1/pipewire")).
|
||||||
MustProxyDBus(&hst.BusConfig{
|
MustProxyDBus(&hst.BusConfig{
|
||||||
Talk: []string{
|
Talk: []string{
|
||||||
"org.freedesktop.FileManager1", "org.freedesktop.Notifications",
|
"org.freedesktop.FileManager1", "org.freedesktop.Notifications",
|
||||||
@@ -883,16 +879,6 @@ func (k *stubNixOS) lookupGroupId(name string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *stubNixOS) lookPath(file string) (string, error) {
|
|
||||||
switch file {
|
|
||||||
case "pipewire-pulse":
|
|
||||||
return "/run/current-system/sw/bin/pipewire-pulse", nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("unexpected file %q", file))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *stubNixOS) cmdOutput(cmd *exec.Cmd) ([]byte, error) {
|
func (k *stubNixOS) cmdOutput(cmd *exec.Cmd) ([]byte, error) {
|
||||||
switch cmd.Path {
|
switch cmd.Path {
|
||||||
case "/proc/nonexistent/hsu":
|
case "/proc/nonexistent/hsu":
|
||||||
|
|||||||
@@ -14,12 +14,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/container"
|
"hakurei.app/container"
|
||||||
"hakurei.app/container/check"
|
|
||||||
"hakurei.app/container/fhs"
|
|
||||||
"hakurei.app/container/seccomp"
|
"hakurei.app/container/seccomp"
|
||||||
"hakurei.app/container/std"
|
"hakurei.app/container/std"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/internal/pipewire"
|
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,55 +83,6 @@ func Shim(msg message.Msg) {
|
|||||||
shimEntrypoint(direct{msg})
|
shimEntrypoint(direct{msg})
|
||||||
}
|
}
|
||||||
|
|
||||||
// A shimPrivate holds state of the private work directory owned by shim.
|
|
||||||
type shimPrivate struct {
|
|
||||||
// Path to directory if created.
|
|
||||||
pathname *check.Absolute
|
|
||||||
|
|
||||||
k syscallDispatcher
|
|
||||||
id *stringPair[hst.ID]
|
|
||||||
}
|
|
||||||
|
|
||||||
// unwrap returns the underlying pathname.
|
|
||||||
func (sp *shimPrivate) unwrap() *check.Absolute {
|
|
||||||
if sp.pathname == nil {
|
|
||||||
if a, err := check.NewAbs(sp.k.tempdir()); err != nil {
|
|
||||||
sp.k.fatal(err)
|
|
||||||
panic("unreachable")
|
|
||||||
} else {
|
|
||||||
pathname := a.Append(".hakurei-shim-" + sp.id.String())
|
|
||||||
sp.k.getMsg().Verbosef("creating private work directory %q", pathname)
|
|
||||||
if err = sp.k.mkdir(pathname.String(), 0700); err != nil {
|
|
||||||
sp.k.fatal(err)
|
|
||||||
panic("unreachable")
|
|
||||||
}
|
|
||||||
sp.pathname = pathname
|
|
||||||
return sp.unwrap()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return sp.pathname
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the absolute pathname to the directory held by shimPrivate.
|
|
||||||
func (sp *shimPrivate) String() string { return sp.unwrap().String() }
|
|
||||||
|
|
||||||
// destroy removes the directory held by shimPrivate.
|
|
||||||
func (sp *shimPrivate) destroy() {
|
|
||||||
defer func() { sp.pathname = nil }()
|
|
||||||
if sp.pathname != nil {
|
|
||||||
sp.k.getMsg().Verbosef("destroying private work directory %q", sp.pathname)
|
|
||||||
if err := sp.k.removeAll(sp.pathname.String()); err != nil {
|
|
||||||
sp.k.getMsg().GetLogger().Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// shimPipeWireTimeout is the duration pipewire-pulse is allowed to run before its socket becomes available.
|
|
||||||
shimPipeWireTimeout = 5 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
func shimEntrypoint(k syscallDispatcher) {
|
func shimEntrypoint(k syscallDispatcher) {
|
||||||
msg := k.getMsg()
|
msg := k.getMsg()
|
||||||
if msg == nil {
|
if msg == nil {
|
||||||
@@ -260,7 +208,6 @@ func shimEntrypoint(k syscallDispatcher) {
|
|||||||
|
|
||||||
ctx, stop := k.notifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
ctx, stop := k.notifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||||
cancelContainer.Store(&stop)
|
cancelContainer.Store(&stop)
|
||||||
sp := shimPrivate{k: k, id: state.id}
|
|
||||||
z := container.New(ctx, msg)
|
z := container.New(ctx, msg)
|
||||||
z.Params = *stateParams.params
|
z.Params = *stateParams.params
|
||||||
z.Stdin, z.Stdout, z.Stderr = os.Stdin, os.Stdout, os.Stderr
|
z.Stdin, z.Stdout, z.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||||
@@ -268,79 +215,6 @@ func shimEntrypoint(k syscallDispatcher) {
|
|||||||
// bounds and default enforced in finalise.go
|
// bounds and default enforced in finalise.go
|
||||||
z.WaitDelay = state.Shim.WaitDelay
|
z.WaitDelay = state.Shim.WaitDelay
|
||||||
|
|
||||||
if stateParams.pipewirePulsePath != nil {
|
|
||||||
zpw := container.NewCommand(ctx, msg, stateParams.pipewirePulsePath, pipewirePulseName)
|
|
||||||
zpw.Hostname = "hakurei-" + pipewirePulseName
|
|
||||||
zpw.SeccompFlags |= seccomp.AllowMultiarch
|
|
||||||
zpw.SeccompPresets |= std.PresetStrict
|
|
||||||
zpw.Env = []string{
|
|
||||||
// pipewire SecurityContext socket path
|
|
||||||
pipewire.Remote + "=" + stateParams.instancePath().Append("pipewire").String(),
|
|
||||||
// pipewire-pulse socket directory path
|
|
||||||
envXDGRuntimeDir + "=" + sp.String(),
|
|
||||||
}
|
|
||||||
if msg.IsVerbose() {
|
|
||||||
zpw.Stdin, zpw.Stdout, zpw.Stderr = os.Stdin, os.Stdout, os.Stderr
|
|
||||||
}
|
|
||||||
zpw.
|
|
||||||
Bind(fhs.AbsRoot, fhs.AbsRoot, 0).
|
|
||||||
Bind(sp.unwrap(), sp.unwrap(), std.BindWritable).
|
|
||||||
Proc(fhs.AbsProc).Dev(fhs.AbsDev, true)
|
|
||||||
socketPath := sp.unwrap().Append("pulse", "native")
|
|
||||||
innerSocketPath := stateParams.runtimeDir.Append("pulse", "native")
|
|
||||||
|
|
||||||
if err := k.containerStart(zpw); err != nil {
|
|
||||||
sp.destroy()
|
|
||||||
printMessageError(func(v ...any) { k.fatal(fmt.Sprintln(v...)) },
|
|
||||||
"cannot start "+pipewirePulseName+" container:", err)
|
|
||||||
}
|
|
||||||
if err := k.containerServe(zpw); err != nil {
|
|
||||||
sp.destroy()
|
|
||||||
printMessageError(func(v ...any) { k.fatal(fmt.Sprintln(v...)) },
|
|
||||||
"cannot configure "+pipewirePulseName+" container:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
done := make(chan error, 1)
|
|
||||||
k.new(func(k syscallDispatcher, msg message.Msg) { done <- k.containerWait(zpw) })
|
|
||||||
|
|
||||||
socketTimer := time.NewTimer(shimPipeWireTimeout)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-socketTimer.C:
|
|
||||||
sp.destroy()
|
|
||||||
k.fatal(pipewirePulseName + " exceeded deadline before socket appeared")
|
|
||||||
break
|
|
||||||
|
|
||||||
case err := <-done:
|
|
||||||
var exitError *exec.ExitError
|
|
||||||
if !errors.As(err, &exitError) {
|
|
||||||
msg.Verbosef("cannot wait: %v", err)
|
|
||||||
k.exit(127)
|
|
||||||
}
|
|
||||||
sp.destroy()
|
|
||||||
k.fatal(pipewirePulseName + " " + exitError.ProcessState.String())
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
if _, err := k.stat(socketPath.String()); err != nil {
|
|
||||||
if !errors.Is(err, os.ErrNotExist) {
|
|
||||||
sp.destroy()
|
|
||||||
k.fatal(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(500 * time.Microsecond)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
z.Bind(socketPath, innerSocketPath, 0)
|
|
||||||
z.Env = append(z.Env, "PULSE_SERVER=unix:"+innerSocketPath.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := k.containerStart(z); err != nil {
|
if err := k.containerStart(z); err != nil {
|
||||||
var f func(v ...any)
|
var f func(v ...any)
|
||||||
if logger := msg.GetLogger(); logger != nil {
|
if logger := msg.GetLogger(); logger != nil {
|
||||||
@@ -351,11 +225,9 @@ func shimEntrypoint(k syscallDispatcher) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
printMessageError(f, "cannot start container:", err)
|
printMessageError(f, "cannot start container:", err)
|
||||||
sp.destroy()
|
|
||||||
k.exit(hst.ExitFailure)
|
k.exit(hst.ExitFailure)
|
||||||
}
|
}
|
||||||
if err := k.containerServe(z); err != nil {
|
if err := k.containerServe(z); err != nil {
|
||||||
sp.destroy()
|
|
||||||
printMessageError(func(v ...any) { k.fatal(fmt.Sprintln(v...)) },
|
printMessageError(func(v ...any) { k.fatal(fmt.Sprintln(v...)) },
|
||||||
"cannot configure container:", err)
|
"cannot configure container:", err)
|
||||||
}
|
}
|
||||||
@@ -364,13 +236,10 @@ func shimEntrypoint(k syscallDispatcher) {
|
|||||||
seccomp.Preset(std.PresetStrict, seccomp.AllowMultiarch),
|
seccomp.Preset(std.PresetStrict, seccomp.AllowMultiarch),
|
||||||
seccomp.AllowMultiarch,
|
seccomp.AllowMultiarch,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
sp.destroy()
|
|
||||||
k.fatalf("cannot load syscall filter: %v", err)
|
k.fatalf("cannot load syscall filter: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := k.containerWait(z); err != nil {
|
if err := k.containerWait(z); err != nil {
|
||||||
sp.destroy()
|
|
||||||
|
|
||||||
var exitError *exec.ExitError
|
var exitError *exec.ExitError
|
||||||
if !errors.As(err, &exitError) {
|
if !errors.As(err, &exitError) {
|
||||||
if errors.Is(err, context.Canceled) {
|
if errors.Is(err, context.Canceled) {
|
||||||
@@ -381,5 +250,4 @@ func shimEntrypoint(k syscallDispatcher) {
|
|||||||
}
|
}
|
||||||
k.exit(exitError.ExitCode())
|
k.exit(exitError.ExitCode())
|
||||||
}
|
}
|
||||||
sp.destroy()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,51 +3,29 @@ package outcome
|
|||||||
import (
|
import (
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
|
|
||||||
"hakurei.app/container/check"
|
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
"hakurei.app/internal/pipewire"
|
"hakurei.app/internal/pipewire"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pipewirePulseName = "pipewire-pulse"
|
func init() { gob.Register(spPipeWireOp{}) }
|
||||||
|
|
||||||
func init() { gob.Register(new(spPipeWireOp)) }
|
|
||||||
|
|
||||||
// spPipeWireOp exports the PipeWire server to the container via SecurityContext.
|
// spPipeWireOp exports the PipeWire server to the container via SecurityContext.
|
||||||
// Runs after spRuntimeOp.
|
// Runs after spRuntimeOp.
|
||||||
type spPipeWireOp struct {
|
type spPipeWireOp struct{}
|
||||||
// Path to pipewire-pulse server. Populated during toSystem if DirectPipeWire is false.
|
|
||||||
CompatServerPath *check.Absolute
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *spPipeWireOp) toSystem(state *outcomeStateSys) error {
|
func (s spPipeWireOp) toSystem(state *outcomeStateSys) error {
|
||||||
if state.et&hst.EPipeWire == 0 {
|
if state.et&hst.EPipeWire == 0 {
|
||||||
return errNotEnabled
|
return errNotEnabled
|
||||||
}
|
}
|
||||||
if !state.directPipeWire {
|
|
||||||
if n, err := state.k.lookPath(pipewirePulseName); err != nil {
|
|
||||||
return &hst.AppError{Step: "look up " + pipewirePulseName, Err: err}
|
|
||||||
} else if s.CompatServerPath, err = check.NewAbs(n); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
appId := state.appId
|
state.sys.PipeWire(state.instance().Append("pipewire"))
|
||||||
if appId == "" {
|
|
||||||
// use instance ID in case app id is not set
|
|
||||||
appId = "app.hakurei." + state.id.String()
|
|
||||||
}
|
|
||||||
state.sys.PipeWire(state.instance().Append("pipewire"), appId, state.id.String())
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *spPipeWireOp) toContainer(state *outcomeStateParams) error {
|
func (s spPipeWireOp) toContainer(state *outcomeStateParams) error {
|
||||||
if s.CompatServerPath == nil {
|
innerPath := state.runtimeDir.Append(pipewire.PW_DEFAULT_REMOTE)
|
||||||
innerPath := state.runtimeDir.Append(pipewire.PW_DEFAULT_REMOTE)
|
state.env[pipewire.Remote] = innerPath.String()
|
||||||
state.env[pipewire.Remote] = innerPath.String()
|
state.params.Bind(state.instancePath().Append("pipewire"), innerPath, 0)
|
||||||
state.params.Bind(state.instancePath().Append("pipewire"), innerPath, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// pipewire-pulse behaviour implemented in shim.go
|
|
||||||
state.pipewirePulsePath = s.CompatServerPath
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func TestSpPipeWireOp(t *testing.T) {
|
|||||||
|
|
||||||
checkOpBehaviour(t, []opBehaviourTestCase{
|
checkOpBehaviour(t, []opBehaviourTestCase{
|
||||||
{"not enabled", func(bool, bool) outcomeOp {
|
{"not enabled", func(bool, bool) outcomeOp {
|
||||||
return new(spPipeWireOp)
|
return spPipeWireOp{}
|
||||||
}, func() *hst.Config {
|
}, func() *hst.Config {
|
||||||
c := hst.Template()
|
c := hst.Template()
|
||||||
*c.Enablements = 0
|
*c.Enablements = 0
|
||||||
@@ -24,19 +24,13 @@ func TestSpPipeWireOp(t *testing.T) {
|
|||||||
}, nil, nil, nil, nil, errNotEnabled, nil, nil, nil, nil, nil},
|
}, nil, nil, nil, nil, errNotEnabled, nil, nil, nil, nil, nil},
|
||||||
|
|
||||||
{"success", func(bool, bool) outcomeOp {
|
{"success", func(bool, bool) outcomeOp {
|
||||||
return new(spPipeWireOp)
|
return spPipeWireOp{}
|
||||||
}, func() *hst.Config {
|
}, hst.Template, nil, []stub.Call{}, newI().
|
||||||
c := hst.Template()
|
|
||||||
c.DirectPipeWire = true
|
|
||||||
return c
|
|
||||||
}, nil, []stub.Call{}, newI().
|
|
||||||
// state.instance
|
// state.instance
|
||||||
Ephemeral(system.Process, m(wantInstancePrefix), 0711).
|
Ephemeral(system.Process, m(wantInstancePrefix), 0711).
|
||||||
// toSystem
|
// toSystem
|
||||||
PipeWire(
|
PipeWire(
|
||||||
m(wantInstancePrefix+"/pipewire"),
|
m(wantInstancePrefix + "/pipewire"),
|
||||||
"org.chromium.Chromium",
|
|
||||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
||||||
), sysUsesInstance(nil), nil, insertsOps(afterSpRuntimeOp(nil)), []stub.Call{
|
), sysUsesInstance(nil), nil, insertsOps(afterSpRuntimeOp(nil)), []stub.Call{
|
||||||
// this op configures the container state and does not make calls during toContainer
|
// this op configures the container state and does not make calls during toContainer
|
||||||
}, &container.Params{
|
}, &container.Params{
|
||||||
|
|||||||
@@ -103,8 +103,6 @@ type Client struct {
|
|||||||
|
|
||||||
// Populated by [CoreBoundProps] events targeting [Client].
|
// Populated by [CoreBoundProps] events targeting [Client].
|
||||||
Properties SPADict `json:"props"`
|
Properties SPADict `json:"props"`
|
||||||
|
|
||||||
noRemove
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) consume(opcode byte, files []int, unmarshal func(v any)) error {
|
func (client *Client) consume(opcode byte, files []int, unmarshal func(v any)) error {
|
||||||
@@ -115,7 +113,7 @@ func (client *Client) consume(opcode byte, files []int, unmarshal func(v any)) e
|
|||||||
return nil
|
return nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(&UnsupportedOpcodeError{opcode, client.String()})
|
return &UnsupportedOpcodeError{opcode, client.String()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,14 +40,13 @@ const (
|
|||||||
PW_CORE_EVENT_ADD_MEM
|
PW_CORE_EVENT_ADD_MEM
|
||||||
PW_CORE_EVENT_REMOVE_MEM
|
PW_CORE_EVENT_REMOVE_MEM
|
||||||
PW_CORE_EVENT_BOUND_PROPS
|
PW_CORE_EVENT_BOUND_PROPS
|
||||||
|
|
||||||
PW_CORE_EVENT_NUM
|
PW_CORE_EVENT_NUM
|
||||||
|
|
||||||
PW_VERSION_CORE_EVENTS = 1
|
PW_VERSION_CORE_EVENTS = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PW_CORE_METHOD_ADD_LISTENER = iota
|
PW_CORE_METHOD_ADD_LISTENER = iota
|
||||||
|
|
||||||
PW_CORE_METHOD_HELLO
|
PW_CORE_METHOD_HELLO
|
||||||
PW_CORE_METHOD_SYNC
|
PW_CORE_METHOD_SYNC
|
||||||
PW_CORE_METHOD_PONG
|
PW_CORE_METHOD_PONG
|
||||||
@@ -55,26 +54,25 @@ const (
|
|||||||
PW_CORE_METHOD_GET_REGISTRY
|
PW_CORE_METHOD_GET_REGISTRY
|
||||||
PW_CORE_METHOD_CREATE_OBJECT
|
PW_CORE_METHOD_CREATE_OBJECT
|
||||||
PW_CORE_METHOD_DESTROY
|
PW_CORE_METHOD_DESTROY
|
||||||
|
|
||||||
PW_CORE_METHOD_NUM
|
PW_CORE_METHOD_NUM
|
||||||
|
|
||||||
PW_VERSION_CORE_METHODS = 0
|
PW_VERSION_CORE_METHODS = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PW_REGISTRY_EVENT_GLOBAL = iota
|
PW_REGISTRY_EVENT_GLOBAL = iota
|
||||||
PW_REGISTRY_EVENT_GLOBAL_REMOVE
|
PW_REGISTRY_EVENT_GLOBAL_REMOVE
|
||||||
|
|
||||||
PW_REGISTRY_EVENT_NUM
|
PW_REGISTRY_EVENT_NUM
|
||||||
|
|
||||||
PW_VERSION_REGISTRY_EVENTS = 0
|
PW_VERSION_REGISTRY_EVENTS = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PW_REGISTRY_METHOD_ADD_LISTENER = iota
|
PW_REGISTRY_METHOD_ADD_LISTENER = iota
|
||||||
|
|
||||||
PW_REGISTRY_METHOD_BIND
|
PW_REGISTRY_METHOD_BIND
|
||||||
PW_REGISTRY_METHOD_DESTROY
|
PW_REGISTRY_METHOD_DESTROY
|
||||||
|
|
||||||
PW_REGISTRY_METHOD_NUM
|
PW_REGISTRY_METHOD_NUM
|
||||||
|
|
||||||
PW_VERSION_REGISTRY_METHODS = 0
|
PW_VERSION_REGISTRY_METHODS = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -268,31 +266,6 @@ type CoreErrorEvent struct{ CoreError }
|
|||||||
// Opcode satisfies [Message] with a constant value.
|
// Opcode satisfies [Message] with a constant value.
|
||||||
func (c *CoreErrorEvent) Opcode() byte { return PW_CORE_EVENT_ERROR }
|
func (c *CoreErrorEvent) Opcode() byte { return PW_CORE_EVENT_ERROR }
|
||||||
|
|
||||||
// The CoreRemoveId event is used internally by the object ID management logic.
|
|
||||||
//
|
|
||||||
// When a client deletes an object, the server will send this event to acknowledge
|
|
||||||
// that it has seen the delete request. When the client receives this event, it
|
|
||||||
// will know that it can safely reuse the object ID.
|
|
||||||
type CoreRemoveId struct {
|
|
||||||
// A proxy id that was removed.
|
|
||||||
ID Int `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opcode satisfies [Message] with a constant value.
|
|
||||||
func (c *CoreRemoveId) Opcode() byte { return PW_CORE_EVENT_REMOVE_ID }
|
|
||||||
|
|
||||||
// FileCount satisfies [Message] with a constant value.
|
|
||||||
func (c *CoreRemoveId) FileCount() Int { return 0 }
|
|
||||||
|
|
||||||
// Size satisfies [KnownSize] with a constant value.
|
|
||||||
func (c *CoreRemoveId) Size() Word { return SizePrefix + Size(SizeInt) }
|
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
|
||||||
func (c *CoreRemoveId) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
|
||||||
|
|
||||||
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
|
|
||||||
func (c *CoreRemoveId) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
|
|
||||||
|
|
||||||
// The CoreBoundProps event is emitted when a local object ID is bound to a global ID.
|
// The CoreBoundProps event is emitted when a local object ID is bound to a global ID.
|
||||||
// It is emitted before the global becomes visible in the registry.
|
// It is emitted before the global becomes visible in the registry.
|
||||||
type CoreBoundProps struct {
|
type CoreBoundProps struct {
|
||||||
@@ -326,96 +299,13 @@ func (c *CoreBoundProps) UnmarshalBinary(data []byte) error { return Unmarshal(d
|
|||||||
|
|
||||||
// ErrBadBoundProps is returned when a [CoreBoundProps] event targeting a proxy
|
// ErrBadBoundProps is returned when a [CoreBoundProps] event targeting a proxy
|
||||||
// that should never be targeted is received and processed.
|
// that should never be targeted is received and processed.
|
||||||
var ErrBadBoundProps = errors.New("attempting to store bound props on a proxy that should never be targeted")
|
var ErrBadBoundProps = errors.New("attempted to store bound props on proxy that should never be targeted")
|
||||||
|
|
||||||
// noAck is embedded by proxies that are never targeted by [CoreBoundProps].
|
// noAck is embedded by proxies that are never targeted by [CoreBoundProps].
|
||||||
type noAck struct{}
|
type noAck struct{}
|
||||||
|
|
||||||
// setBoundProps should never be called as this proxy should never be targeted by [CoreBoundProps].
|
// setBoundProps should never be called as this proxy should never be targeted by [CoreBoundProps].
|
||||||
func (noAck) setBoundProps(*CoreBoundProps) error { panic(ErrBadBoundProps) }
|
func (noAck) setBoundProps(*CoreBoundProps) error { return ErrBadBoundProps }
|
||||||
|
|
||||||
// ErrBadRemove is returned when a [CoreRemoveId] event targeting a proxy
|
|
||||||
// that should never be targeted is received and processed.
|
|
||||||
var ErrBadRemove = errors.New("attempting to remove a proxy that should never be targeted")
|
|
||||||
|
|
||||||
// noRemove is embedded by proxies that are never targeted by [CoreRemoveId].
|
|
||||||
type noRemove struct{}
|
|
||||||
|
|
||||||
// remove should never be called as this proxy should never be targeted by [CoreRemoveId].
|
|
||||||
func (noRemove) remove() error { panic(ErrBadRemove) }
|
|
||||||
|
|
||||||
// ErrInvalidRemove is returned when a proxy is somehow removed twice. This is only reached for
|
|
||||||
// an implementation error as the proxy struct should no longer be reachable after the first call.
|
|
||||||
var ErrInvalidRemove = errors.New("attempting to remove an already freed proxy")
|
|
||||||
|
|
||||||
// removable is embedded by proxies that can be targeted by [CoreRemoveId] and requires no cleanup.
|
|
||||||
type removable bool
|
|
||||||
|
|
||||||
// remove checks against removal of a freed proxy and marks the proxy as removed.
|
|
||||||
func (s *removable) remove() error {
|
|
||||||
if *s {
|
|
||||||
panic(ErrInvalidRemove)
|
|
||||||
}
|
|
||||||
*s = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrProxyDestroyed is returned when attempting to use a proxy method when the underlying
|
|
||||||
// proxy has already been targeted by a [CoreRemoveId] event.
|
|
||||||
var ErrProxyDestroyed = errors.New("underlying proxy has been removed")
|
|
||||||
|
|
||||||
// checkDestroy returns [ErrProxyDestroyed] if the current proxy has been destroyed.
|
|
||||||
// Must be called at the beginning of any exported method of a proxy embedding removable.
|
|
||||||
func (s *removable) checkDestroy() error {
|
|
||||||
if *s {
|
|
||||||
// not fatal: the caller is allowed to recover from this and allocate a new proxy
|
|
||||||
return ErrProxyDestroyed
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// mustCheckDestroy calls checkDestroy and panics if a non-nil error is returned.
|
|
||||||
// This is useful for non-exported methods as they should become unreachable.
|
|
||||||
func (s *removable) mustCheckDestroy() {
|
|
||||||
if err := s.checkDestroy(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// destructible is embedded by proxies that can be targeted by the [CoreRemoveId] event and the
|
|
||||||
// [CoreDestroy] method and requires no cleanup. destructible purposefully does not override
|
|
||||||
// removable.mustCheckDestroy because it is used by unexported methods called during event handling
|
|
||||||
// and are exempt from the destruction check.
|
|
||||||
type destructible struct {
|
|
||||||
destroyed bool
|
|
||||||
|
|
||||||
removable
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkDestroy overrides removable.checkDestroy to also check the destroyed field.
|
|
||||||
func (s *destructible) checkDestroy() error {
|
|
||||||
if s.destroyed {
|
|
||||||
return ErrProxyDestroyed
|
|
||||||
}
|
|
||||||
if err := s.removable.checkDestroy(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// destroy calls removable.checkDestroy then queues a [CoreDestroy] event if it succeeds.
|
|
||||||
func (s *destructible) destroy(ctx *Context, id Int) error {
|
|
||||||
if err := s.checkDestroy(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
l := len(ctx.pendingDestruction)
|
|
||||||
ctx.pendingDestruction[id] = struct{}{}
|
|
||||||
if len(ctx.pendingDestruction) != l+1 {
|
|
||||||
return ErrProxyDestroyed
|
|
||||||
}
|
|
||||||
s.destroyed = true
|
|
||||||
return ctx.GetCore().destroy(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// An InconsistentIdError describes an inconsistent state where the server claims an impossible
|
// An InconsistentIdError describes an inconsistent state where the server claims an impossible
|
||||||
// proxy or global id. This is only generated by the [CoreBoundProps] event.
|
// proxy or global id. This is only generated by the [CoreBoundProps] event.
|
||||||
@@ -459,10 +349,10 @@ func (c *CoreHello) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
|||||||
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
|
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
|
||||||
func (c *CoreHello) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
|
func (c *CoreHello) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
|
||||||
|
|
||||||
// hello queues a [CoreHello] message for the PipeWire server.
|
// coreHello queues a [CoreHello] message for the PipeWire server.
|
||||||
// This method should not be called directly, the [New] function queues this message.
|
// This method should not be called directly, the New function queues this message.
|
||||||
func (core *Core) hello() error {
|
func (ctx *Context) coreHello() error {
|
||||||
return core.ctx.writeMessage(
|
return ctx.writeMessage(
|
||||||
PW_ID_CORE,
|
PW_ID_CORE,
|
||||||
&CoreHello{PW_VERSION_CORE},
|
&CoreHello{PW_VERSION_CORE},
|
||||||
)
|
)
|
||||||
@@ -498,12 +388,12 @@ func (c *CoreSync) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
|||||||
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
|
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
|
||||||
func (c *CoreSync) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
|
func (c *CoreSync) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
|
||||||
|
|
||||||
// sync queues a [CoreSync] message for the PipeWire server.
|
// coreSync queues a [CoreSync] message for the PipeWire server.
|
||||||
// This is not safe to use directly, callers should use Sync instead.
|
// This is not safe to use directly, callers should use Sync instead.
|
||||||
func (core *Core) sync(id Int) error {
|
func (ctx *Context) coreSync(id Int) error {
|
||||||
return core.ctx.writeMessage(
|
return ctx.writeMessage(
|
||||||
PW_ID_CORE,
|
PW_ID_CORE,
|
||||||
&CoreSync{id, CoreSyncSequenceOffset + Int(core.ctx.sequence)},
|
&CoreSync{id, CoreSyncSequenceOffset + Int(ctx.sequence)},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -520,7 +410,7 @@ const (
|
|||||||
// Sync queues a [CoreSync] message for the PipeWire server and initiates a Roundtrip.
|
// Sync queues a [CoreSync] message for the PipeWire server and initiates a Roundtrip.
|
||||||
func (core *Core) Sync() error {
|
func (core *Core) Sync() error {
|
||||||
core.done = false
|
core.done = false
|
||||||
if err := core.sync(roundtripSyncID); err != nil {
|
if err := core.ctx.coreSync(roundtripSyncID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
deadline := time.Now().Add(syncTimeout)
|
deadline := time.Now().Add(syncTimeout)
|
||||||
@@ -539,10 +429,6 @@ func (core *Core) Sync() error {
|
|||||||
core.ctx.closeReceivedFiles()
|
core.ctx.closeReceivedFiles()
|
||||||
return &ProxyFatalError{Err: UnacknowledgedProxyError(slices.Collect(maps.Keys(core.ctx.pendingIds))), ProxyErrs: core.ctx.cloneAsProxyErrors()}
|
return &ProxyFatalError{Err: UnacknowledgedProxyError(slices.Collect(maps.Keys(core.ctx.pendingIds))), ProxyErrs: core.ctx.cloneAsProxyErrors()}
|
||||||
}
|
}
|
||||||
if len(core.ctx.pendingDestruction) != 0 {
|
|
||||||
core.ctx.closeReceivedFiles()
|
|
||||||
return &ProxyFatalError{Err: UnacknowledgedProxyDestructionError(slices.Collect(maps.Keys(core.ctx.pendingDestruction))), ProxyErrs: core.ctx.cloneAsProxyErrors()}
|
|
||||||
}
|
|
||||||
return core.ctx.doSyncComplete()
|
return core.ctx.doSyncComplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,89 +497,6 @@ func (ctx *Context) GetRegistry() (*Registry, error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CoreCreateObject is sent when the client requests to create a
|
|
||||||
// new object from a factory of a certain type.
|
|
||||||
//
|
|
||||||
// The client allocates a new_id for the proxy. The server will
|
|
||||||
// allocate a new resource with the same new_id and from then on,
|
|
||||||
// Methods and Events will be exchanged between the new object of
|
|
||||||
// the given type.
|
|
||||||
type CoreCreateObject struct {
|
|
||||||
// The name of a server factory object to use.
|
|
||||||
FactoryName String `json:"factory_name"`
|
|
||||||
// The type of the object to create, this is also the type of
|
|
||||||
// the interface of the new_id proxy.
|
|
||||||
Type String `json:"type"`
|
|
||||||
// Undocumented, assumed to be the local version of the proxy.
|
|
||||||
Version Int `json:"version"`
|
|
||||||
// Extra properties to create the object.
|
|
||||||
Properties *SPADict `json:"props"`
|
|
||||||
// The proxy id of the new object.
|
|
||||||
NewID Int `json:"new_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opcode satisfies [Message] with a constant value.
|
|
||||||
func (c *CoreCreateObject) Opcode() byte { return PW_CORE_METHOD_CREATE_OBJECT }
|
|
||||||
|
|
||||||
// FileCount satisfies [Message] with a constant value.
|
|
||||||
func (c *CoreCreateObject) FileCount() Int { return 0 }
|
|
||||||
|
|
||||||
// Size satisfies [KnownSize] with a value computed at runtime.
|
|
||||||
func (c *CoreCreateObject) Size() Word {
|
|
||||||
return SizePrefix +
|
|
||||||
SizeString[Word](c.FactoryName) +
|
|
||||||
SizeString[Word](c.Type) +
|
|
||||||
Size(SizeInt) +
|
|
||||||
c.Properties.Size() +
|
|
||||||
Size(SizeInt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
|
||||||
func (c *CoreCreateObject) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
|
||||||
|
|
||||||
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
|
|
||||||
func (c *CoreCreateObject) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
|
|
||||||
|
|
||||||
// createObject queues a [CoreCreateObject] message for the PipeWire server.
|
|
||||||
// This is not safe to use directly, callers should use typed wrapper methods on [Registry] instead.
|
|
||||||
func (core *Core) createObject(factoryName, typeName String, version Int, props SPADict, newId Int) error {
|
|
||||||
return core.ctx.writeMessage(
|
|
||||||
PW_ID_CORE,
|
|
||||||
&CoreCreateObject{factoryName, typeName, version, &props, newId},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CoreDestroy is sent when the client requests to destroy an object.
|
|
||||||
type CoreDestroy struct {
|
|
||||||
// The proxy id of the object to destroy.
|
|
||||||
ID Int `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opcode satisfies [Message] with a constant value.
|
|
||||||
func (c *CoreDestroy) Opcode() byte { return PW_CORE_METHOD_DESTROY }
|
|
||||||
|
|
||||||
// FileCount satisfies [Message] with a constant value.
|
|
||||||
func (c *CoreDestroy) FileCount() Int { return 0 }
|
|
||||||
|
|
||||||
// Size satisfies [KnownSize] with a constant value.
|
|
||||||
func (c *CoreDestroy) Size() Word { return SizePrefix + Size(SizeInt) }
|
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
|
||||||
func (c *CoreDestroy) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
|
||||||
|
|
||||||
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
|
|
||||||
func (c *CoreDestroy) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
|
|
||||||
|
|
||||||
// destroy queues a [CoreDestroy] message for the PipeWire server.
|
|
||||||
// This is not safe to use directly, callers should use the exported method
|
|
||||||
// on the proxy implementation instead.
|
|
||||||
func (core *Core) destroy(id Int) error {
|
|
||||||
return core.ctx.writeMessage(
|
|
||||||
PW_ID_CORE,
|
|
||||||
&CoreDestroy{id},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A RegistryGlobal event is emitted to notify a client about a new global object.
|
// A RegistryGlobal event is emitted to notify a client about a new global object.
|
||||||
type RegistryGlobal struct {
|
type RegistryGlobal struct {
|
||||||
// The global id.
|
// The global id.
|
||||||
@@ -730,30 +533,6 @@ func (c *RegistryGlobal) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
|||||||
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
|
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
|
||||||
func (c *RegistryGlobal) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
|
func (c *RegistryGlobal) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
|
||||||
|
|
||||||
// A RegistryGlobalRemove event is emitted when a global with id was removed.
|
|
||||||
type RegistryGlobalRemove struct {
|
|
||||||
// The global id that was removed.
|
|
||||||
ID Int `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opcode satisfies [Message] with a constant value.
|
|
||||||
func (c *RegistryGlobalRemove) Opcode() byte { return PW_REGISTRY_EVENT_GLOBAL_REMOVE }
|
|
||||||
|
|
||||||
// FileCount satisfies [Message] with a constant value.
|
|
||||||
func (c *RegistryGlobalRemove) FileCount() Int { return 0 }
|
|
||||||
|
|
||||||
// Size satisfies [KnownSize] with a constant value.
|
|
||||||
func (c *RegistryGlobalRemove) Size() Word {
|
|
||||||
return SizePrefix +
|
|
||||||
Size(SizeInt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
|
||||||
func (c *RegistryGlobalRemove) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
|
||||||
|
|
||||||
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
|
|
||||||
func (c *RegistryGlobalRemove) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
|
|
||||||
|
|
||||||
// RegistryBind is sent when the client requests to bind to the
|
// RegistryBind is sent when the client requests to bind to the
|
||||||
// global object with id and use the client proxy with new_id as
|
// global object with id and use the client proxy with new_id as
|
||||||
// the proxy. After this call, methods can be sent to the remote
|
// the proxy. After this call, methods can be sent to the remote
|
||||||
@@ -805,70 +584,10 @@ func (registry *Registry) bind(proxy eventProxy, id, version Int) (Int, error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegistryDestroy is sent to try to destroy the global object with id.
|
|
||||||
// This might fail when the client does not have permission.
|
|
||||||
type RegistryDestroy struct {
|
|
||||||
// The global id to destroy.
|
|
||||||
ID Int `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opcode satisfies [Message] with a constant value.
|
|
||||||
func (c *RegistryDestroy) Opcode() byte { return PW_REGISTRY_METHOD_DESTROY }
|
|
||||||
|
|
||||||
// FileCount satisfies [Message] with a constant value.
|
|
||||||
func (c *RegistryDestroy) FileCount() Int { return 0 }
|
|
||||||
|
|
||||||
// Size satisfies [KnownSize] with a constant value.
|
|
||||||
func (c *RegistryDestroy) Size() Word {
|
|
||||||
return SizePrefix +
|
|
||||||
Size(SizeInt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalBinary satisfies [encoding.BinaryMarshaler] via [Marshal].
|
|
||||||
func (c *RegistryDestroy) MarshalBinary() ([]byte, error) { return Marshal(c) }
|
|
||||||
|
|
||||||
// UnmarshalBinary satisfies [encoding.BinaryUnmarshaler] via [Unmarshal].
|
|
||||||
func (c *RegistryDestroy) UnmarshalBinary(data []byte) error { return Unmarshal(data, c) }
|
|
||||||
|
|
||||||
// destroy queues a [RegistryDestroy] message for the PipeWire server.
|
|
||||||
func (registry *Registry) destroy(id Int) error {
|
|
||||||
return registry.ctx.writeMessage(
|
|
||||||
registry.ID,
|
|
||||||
&RegistryDestroy{id},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy tries to destroy the global object with id.
|
|
||||||
func (registry *Registry) Destroy(id Int) (err error) {
|
|
||||||
asCoreError := registry.ctx.expectsCoreError(registry.ID, &err)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = registry.destroy(id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = registry.ctx.GetCore().Sync(); err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if coreError := asCoreError(); coreError == nil {
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
switch syscall.Errno(-coreError.Result) {
|
|
||||||
case syscall.EPERM:
|
|
||||||
return &PermissionError{registry.ID, coreError.Message}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return coreError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// An UnsupportedObjectTypeError is the name of a type not known by the server [Registry].
|
// An UnsupportedObjectTypeError is the name of a type not known by the server [Registry].
|
||||||
type UnsupportedObjectTypeError string
|
type UnsupportedObjectTypeError string
|
||||||
|
|
||||||
func (e UnsupportedObjectTypeError) Error() string { return "unsupported object type " + string(e) }
|
func (e UnsupportedObjectTypeError) Error() string { return "unsupported object type " + string(e) }
|
||||||
func (e UnsupportedObjectTypeError) Message() string { return e.Error() }
|
|
||||||
|
|
||||||
// Core holds state of [PW_TYPE_INTERFACE_Core].
|
// Core holds state of [PW_TYPE_INTERFACE_Core].
|
||||||
type Core struct {
|
type Core struct {
|
||||||
@@ -879,24 +598,22 @@ type Core struct {
|
|||||||
done bool
|
done bool
|
||||||
|
|
||||||
ctx *Context
|
ctx *Context
|
||||||
|
|
||||||
noAck
|
noAck
|
||||||
noRemove
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUnexpectedDone is a [CoreDone] event with unexpected values.
|
// ErrUnexpectedDone is a [CoreDone] event with unexpected values.
|
||||||
var ErrUnexpectedDone = errors.New("multiple Core::Done events targeting Core::Sync")
|
var ErrUnexpectedDone = errors.New("multiple Core::Done events targeting Core::Sync")
|
||||||
|
|
||||||
// An UnknownProxyIdError describes an event targeting a proxy id that was never allocated.
|
// An UnknownBoundIdError describes the server claiming to have bound a proxy id that was never allocated.
|
||||||
type UnknownProxyIdError[E any] struct {
|
type UnknownBoundIdError[E any] struct {
|
||||||
// Offending id decoded from Data.
|
// Offending id decoded from Data.
|
||||||
Id Int
|
Id Int
|
||||||
// Event received from the server.
|
// Event received from the server.
|
||||||
Event E
|
Event E
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *UnknownProxyIdError[E]) Error() string {
|
func (e *UnknownBoundIdError[E]) Error() string {
|
||||||
return "unknown proxy id " + strconv.Itoa(int(e.Id))
|
return "unknown bound proxy id " + strconv.Itoa(int(e.Id))
|
||||||
}
|
}
|
||||||
|
|
||||||
// An InvalidPingError is a [CorePing] event targeting a proxy id that was never allocated.
|
// An InvalidPingError is a [CorePing] event targeting a proxy id that was never allocated.
|
||||||
@@ -951,19 +668,6 @@ func (core *Core) consume(opcode byte, files []int, unmarshal func(v any)) error
|
|||||||
unmarshal(&coreError)
|
unmarshal(&coreError)
|
||||||
return &coreError
|
return &coreError
|
||||||
|
|
||||||
case PW_CORE_EVENT_REMOVE_ID:
|
|
||||||
var coreRemoveId CoreRemoveId
|
|
||||||
unmarshal(&coreRemoveId)
|
|
||||||
if proxy, ok := core.ctx.proxy[coreRemoveId.ID]; !ok {
|
|
||||||
// this should never happen so is non-recoverable if it does
|
|
||||||
panic(&UnknownProxyIdError[*CoreRemoveId]{Id: coreRemoveId.ID, Event: &coreRemoveId})
|
|
||||||
} else {
|
|
||||||
delete(core.ctx.proxy, coreRemoveId.ID)
|
|
||||||
// not always populated so this is not checked
|
|
||||||
delete(core.ctx.pendingDestruction, coreRemoveId.ID)
|
|
||||||
return proxy.remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
case PW_CORE_EVENT_BOUND_PROPS:
|
case PW_CORE_EVENT_BOUND_PROPS:
|
||||||
var boundProps CoreBoundProps
|
var boundProps CoreBoundProps
|
||||||
unmarshal(&boundProps)
|
unmarshal(&boundProps)
|
||||||
@@ -971,12 +675,12 @@ func (core *Core) consume(opcode byte, files []int, unmarshal func(v any)) error
|
|||||||
delete(core.ctx.pendingIds, boundProps.ID)
|
delete(core.ctx.pendingIds, boundProps.ID)
|
||||||
proxy, ok := core.ctx.proxy[boundProps.ID]
|
proxy, ok := core.ctx.proxy[boundProps.ID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return &UnknownProxyIdError[*CoreBoundProps]{Id: boundProps.ID, Event: &boundProps}
|
return &UnknownBoundIdError[*CoreBoundProps]{Id: boundProps.ID, Event: &boundProps}
|
||||||
}
|
}
|
||||||
return proxy.setBoundProps(&boundProps)
|
return proxy.setBoundProps(&boundProps)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(&UnsupportedOpcodeError{opcode, core.String()})
|
return &UnsupportedOpcodeError{opcode, core.String()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -994,9 +698,7 @@ type Registry struct {
|
|||||||
Objects map[Int]RegistryGlobal `json:"objects"`
|
Objects map[Int]RegistryGlobal `json:"objects"`
|
||||||
|
|
||||||
ctx *Context
|
ctx *Context
|
||||||
|
|
||||||
noAck
|
noAck
|
||||||
noRemove
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A GlobalIDCollisionError describes a [RegistryGlobal] event stepping on a previous instance of itself.
|
// A GlobalIDCollisionError describes a [RegistryGlobal] event stepping on a previous instance of itself.
|
||||||
@@ -1012,14 +714,6 @@ func (e *GlobalIDCollisionError) Error() string {
|
|||||||
" stepping on previous id " + strconv.Itoa(int(e.ID)) + " for " + e.Previous.Type
|
" stepping on previous id " + strconv.Itoa(int(e.ID)) + " for " + e.Previous.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
// An UnknownGlobalIDRemoveError describes a [RegistryGlobalRemove] event announcing the removal of
|
|
||||||
// a global id that is not yet known to [Registry] or was already deleted.
|
|
||||||
type UnknownGlobalIDRemoveError Int
|
|
||||||
|
|
||||||
func (e UnknownGlobalIDRemoveError) Error() string {
|
|
||||||
return "Registry::GlobalRemove event targets unknown id " + strconv.Itoa(int(e))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (registry *Registry) consume(opcode byte, files []int, unmarshal func(v any)) error {
|
func (registry *Registry) consume(opcode byte, files []int, unmarshal func(v any)) error {
|
||||||
closeReceivedFiles(files...)
|
closeReceivedFiles(files...)
|
||||||
switch opcode {
|
switch opcode {
|
||||||
@@ -1033,21 +727,8 @@ func (registry *Registry) consume(opcode byte, files []int, unmarshal func(v any
|
|||||||
registry.Objects[global.ID] = global
|
registry.Objects[global.ID] = global
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
case PW_REGISTRY_EVENT_GLOBAL_REMOVE:
|
|
||||||
var globalRemove RegistryGlobalRemove
|
|
||||||
unmarshal(&globalRemove)
|
|
||||||
// server emits PW_CORE_EVENT_REMOVE_ID events targeting
|
|
||||||
// affected proxies so they do not need to be handled here
|
|
||||||
l := len(registry.Objects)
|
|
||||||
delete(registry.Objects, globalRemove.ID)
|
|
||||||
if len(registry.Objects) != l-1 {
|
|
||||||
// this should never happen so is non-recoverable if it does
|
|
||||||
panic(UnknownGlobalIDRemoveError(globalRemove.ID))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(&UnsupportedOpcodeError{opcode, registry.String()})
|
return &UnsupportedOpcodeError{opcode, registry.String()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ func TestCoreError(t *testing.T) {
|
|||||||
/* padding */ 0, 0, 0, 0,
|
/* padding */ 0, 0, 0, 0,
|
||||||
|
|
||||||
/* size: 0x1b bytes */ 0x1b, 0, 0, 0,
|
/* size: 0x1b bytes */ 0x1b, 0, 0, 0,
|
||||||
/* type: String */ 8, 0, 0, 0,
|
/*type: String*/ 8, 0, 0, 0,
|
||||||
|
|
||||||
// value: "no permission to destroy 0\x00"
|
// value: "no permission to destroy 0\x00"
|
||||||
0x6e, 0x6f, 0x20, 0x70,
|
0x6e, 0x6f, 0x20, 0x70,
|
||||||
@@ -192,24 +192,6 @@ func TestCoreError(t *testing.T) {
|
|||||||
}.run(t)
|
}.run(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCoreRemoveId(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
encodingTestCases[pipewire.CoreRemoveId, *pipewire.CoreRemoveId]{
|
|
||||||
{"sample", []byte{
|
|
||||||
/* size: rest of data */ 0x10, 0, 0, 0,
|
|
||||||
/* type: Struct */ 0xe, 0, 0, 0,
|
|
||||||
|
|
||||||
/* size: 4 bytes */ 4, 0, 0, 0,
|
|
||||||
/* type: Int */ 4, 0, 0, 0,
|
|
||||||
/* value: 3 */ 3, 0, 0, 0,
|
|
||||||
/* padding */ 0, 0, 0, 0,
|
|
||||||
}, pipewire.CoreRemoveId{
|
|
||||||
ID: 3,
|
|
||||||
}, nil},
|
|
||||||
}.run(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCoreBoundProps(t *testing.T) {
|
func TestCoreBoundProps(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -306,83 +288,6 @@ func TestCoreGetRegistry(t *testing.T) {
|
|||||||
}.run(t)
|
}.run(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCoreCreateObject(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
encodingTestCases[pipewire.CoreCreateObject, *pipewire.CoreCreateObject]{
|
|
||||||
{"sample", []byte{
|
|
||||||
/* size: rest of data */ 0x80, 0, 0, 0,
|
|
||||||
/* type: Struct */ 0xe, 0, 0, 0,
|
|
||||||
|
|
||||||
/* size: 0x13 bytes */ 0x13, 0, 0, 0,
|
|
||||||
/* type: String */ 8, 0, 0, 0,
|
|
||||||
|
|
||||||
// value: "spa-device-factory\x00"
|
|
||||||
0x73, 0x70, 0x61, 0x2d,
|
|
||||||
0x64, 0x65, 0x76, 0x69,
|
|
||||||
0x63, 0x65, 0x2d, 0x66,
|
|
||||||
0x61, 0x63, 0x74, 0x6f,
|
|
||||||
0x72, 0x79, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
|
|
||||||
/* size: 0x1a bytes */ 0x1a, 0, 0, 0,
|
|
||||||
/* type: String */ 8, 0, 0, 0,
|
|
||||||
|
|
||||||
// value: "PipeWire:Interface:Device\x00"
|
|
||||||
0x50, 0x69, 0x70, 0x65,
|
|
||||||
0x57, 0x69, 0x72, 0x65,
|
|
||||||
0x3a, 0x49, 0x6e, 0x74,
|
|
||||||
0x65, 0x72, 0x66, 0x61,
|
|
||||||
0x63, 0x65, 0x3a, 0x44,
|
|
||||||
0x65, 0x76, 0x69, 0x63,
|
|
||||||
0x65, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
|
|
||||||
/* size: 4 bytes */ 4, 0, 0, 0,
|
|
||||||
/* type: Int */ 4, 0, 0, 0,
|
|
||||||
/* value: 3 */ 3, 0, 0, 0,
|
|
||||||
/* padding */ 0, 0, 0, 0,
|
|
||||||
|
|
||||||
/* size */ 0x10, 0, 0, 0,
|
|
||||||
/* type: Struct */ 0xe, 0, 0, 0,
|
|
||||||
|
|
||||||
/* size: 4 bytes */ 4, 0, 0, 0,
|
|
||||||
/* type: Int */ 4, 0, 0, 0,
|
|
||||||
/* value: 0 */ 0, 0, 0, 0,
|
|
||||||
/* padding */ 0, 0, 0, 0,
|
|
||||||
|
|
||||||
/* size: 4 bytes */ 4, 0, 0, 0,
|
|
||||||
/* type: Int */ 4, 0, 0, 0,
|
|
||||||
/* value: 0xbad */ 0xad, 0xb, 0, 0,
|
|
||||||
/* padding */ 0, 0, 0, 0,
|
|
||||||
}, pipewire.CoreCreateObject{
|
|
||||||
FactoryName: "spa-device-factory",
|
|
||||||
Type: pipewire.PW_TYPE_INTERFACE_Device,
|
|
||||||
Version: pipewire.PW_VERSION_FACTORY,
|
|
||||||
Properties: &pipewire.SPADict{},
|
|
||||||
NewID: 0xbad,
|
|
||||||
}, nil},
|
|
||||||
}.run(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCoreDestroy(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
encodingTestCases[pipewire.CoreDestroy, *pipewire.CoreDestroy]{
|
|
||||||
{"sample", []byte{
|
|
||||||
/* size: rest of data */ 0x10, 0, 0, 0,
|
|
||||||
/* type: Struct */ 0xe, 0, 0, 0,
|
|
||||||
|
|
||||||
/* size: 4 bytes */ 4, 0, 0, 0,
|
|
||||||
/* type: Int */ 4, 0, 0, 0,
|
|
||||||
/* value: 3 */ 3, 0, 0, 0,
|
|
||||||
/* padding */ 0, 0, 0, 0,
|
|
||||||
}, pipewire.CoreDestroy{
|
|
||||||
ID: 3,
|
|
||||||
}, nil},
|
|
||||||
}.run(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRegistryGlobal(t *testing.T) {
|
func TestRegistryGlobal(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -840,23 +745,6 @@ func TestRegistryGlobal(t *testing.T) {
|
|||||||
}.run(t)
|
}.run(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRegistryGlobalRemove(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
encodingTestCases[pipewire.RegistryGlobalRemove, *pipewire.RegistryGlobalRemove]{
|
|
||||||
{"sample", []byte{
|
|
||||||
/* size: rest of data*/ 0x10, 0, 0, 0,
|
|
||||||
/* type: Struct */ 0xe, 0, 0, 0,
|
|
||||||
/* size: 4 bytes */ 4, 0, 0, 0,
|
|
||||||
/* type: Int */ 4, 0, 0, 0,
|
|
||||||
/* value: 0xbad */ 0xad, 0xb, 0, 0,
|
|
||||||
/* padding */ 0, 0, 0, 0,
|
|
||||||
}, pipewire.RegistryGlobalRemove{
|
|
||||||
ID: 0xbad,
|
|
||||||
}, nil},
|
|
||||||
}.run(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRegistryBind(t *testing.T) {
|
func TestRegistryBind(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -869,20 +757,3 @@ func TestRegistryBind(t *testing.T) {
|
|||||||
}, nil},
|
}, nil},
|
||||||
}.run(t)
|
}.run(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRegistryDestroy(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
encodingTestCases[pipewire.RegistryDestroy, *pipewire.RegistryDestroy]{
|
|
||||||
{"sample", []byte{
|
|
||||||
/* size: rest of data*/ 0x10, 0, 0, 0,
|
|
||||||
/* type: Struct */ 0xe, 0, 0, 0,
|
|
||||||
/* size: 4 bytes */ 4, 0, 0, 0,
|
|
||||||
/* type: Int */ 4, 0, 0, 0,
|
|
||||||
/* value: 0xbad */ 0xad, 0xb, 0, 0,
|
|
||||||
/* padding */ 0, 0, 0, 0,
|
|
||||||
}, pipewire.RegistryDestroy{
|
|
||||||
ID: 0xbad,
|
|
||||||
}, nil},
|
|
||||||
}.run(t)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ package pipewire
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@@ -52,27 +51,20 @@ type Context struct {
|
|||||||
buf []byte
|
buf []byte
|
||||||
// Current [Header.Sequence] value, incremented every write.
|
// Current [Header.Sequence] value, incremented every write.
|
||||||
sequence Int
|
sequence Int
|
||||||
// Pending file descriptors to be sent with the next message.
|
// Current server-side [Header.Sequence] value, incremented on every event processed.
|
||||||
pendingFiles []int
|
remoteSequence Int
|
||||||
// File count already kept track of in [Header].
|
|
||||||
headerFiles int
|
|
||||||
|
|
||||||
// Proxy id associations.
|
// Proxy id associations.
|
||||||
proxy map[Int]eventProxy
|
proxy map[Int]eventProxy
|
||||||
// Newly allocated proxies pending acknowledgement from the server.
|
// Newly allocated proxies pending acknowledgement from the server.
|
||||||
pendingIds map[Int]struct{}
|
pendingIds map[Int]struct{}
|
||||||
// Smallest available Id for the next proxy.
|
// Smallest available Id for the next proxy.
|
||||||
nextId Int
|
nextId Int
|
||||||
// Proxies targeted by the [CoreDestroy] event pending until next [CoreSync].
|
// Server side registry generation number.
|
||||||
pendingDestruction map[Int]struct{}
|
generation Long
|
||||||
|
// Pending file descriptors to be sent with the next message.
|
||||||
// Proxy for built-in core events.
|
pendingFiles []int
|
||||||
core Core
|
// File count kept track of in [Header].
|
||||||
// Proxy for built-in client events.
|
headerFiles int
|
||||||
client Client
|
|
||||||
|
|
||||||
// Current server-side [Header.Sequence] value, incremented on every event processed.
|
|
||||||
remoteSequence Int
|
|
||||||
// Files from the server. This is discarded on every Roundtrip so eventProxy
|
// Files from the server. This is discarded on every Roundtrip so eventProxy
|
||||||
// implementations must make sure to close them to avoid leaking fds.
|
// implementations must make sure to close them to avoid leaking fds.
|
||||||
//
|
//
|
||||||
@@ -88,11 +80,13 @@ type Context struct {
|
|||||||
// Pending footer value deferred to the next round trip,
|
// Pending footer value deferred to the next round trip,
|
||||||
// sent if pendingFooter is nil. This is for emulating upstream behaviour
|
// sent if pendingFooter is nil. This is for emulating upstream behaviour
|
||||||
deferredPendingFooter KnownSize
|
deferredPendingFooter KnownSize
|
||||||
// Server side registry generation number.
|
|
||||||
generation Long
|
|
||||||
// Deferred operations ran after a [Core.Sync] completes or Close is called. Errors
|
// Deferred operations ran after a [Core.Sync] completes or Close is called. Errors
|
||||||
//are reported as part of [ProxyConsumeError] and is not considered fatal unless panicked.
|
//are reported as part of [ProxyConsumeError] and is not considered fatal unless panicked.
|
||||||
syncComplete []func() error
|
syncComplete []func() error
|
||||||
|
// Proxy for built-in core events.
|
||||||
|
core Core
|
||||||
|
// Proxy for built-in client events.
|
||||||
|
client Client
|
||||||
|
|
||||||
// Passed to [Conn.Recvmsg]. Not copied if sufficient for all received messages.
|
// Passed to [Conn.Recvmsg]. Not copied if sufficient for all received messages.
|
||||||
iovecBuf [1 << 15]byte
|
iovecBuf [1 << 15]byte
|
||||||
@@ -126,9 +120,8 @@ func New(conn Conn, props SPADict) (*Context, error) {
|
|||||||
PW_ID_CLIENT: {},
|
PW_ID_CLIENT: {},
|
||||||
}
|
}
|
||||||
ctx.nextId = Int(len(ctx.proxy))
|
ctx.nextId = Int(len(ctx.proxy))
|
||||||
ctx.pendingDestruction = make(map[Int]struct{})
|
|
||||||
|
|
||||||
if err := ctx.core.hello(); err != nil {
|
if err := ctx.coreHello(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := ctx.clientUpdateProperties(props); err != nil {
|
if err := ctx.clientUpdateProperties(props); err != nil {
|
||||||
@@ -278,20 +271,16 @@ func (ctx *Context) recvmsg(remaining []byte) (payload []byte, err error) {
|
|||||||
n, oobn, recvflags, err = ctx.conn.Recvmsg(ctx.iovecBuf[len(remaining):], ctx.oobBuf[:], recvmsgFlags)
|
n, oobn, recvflags, err = ctx.conn.Recvmsg(ctx.iovecBuf[len(remaining):], ctx.oobBuf[:], recvmsgFlags)
|
||||||
|
|
||||||
if oob := ctx.oobBuf[:oobn]; len(oob) > 0 {
|
if oob := ctx.oobBuf[:oobn]; len(oob) > 0 {
|
||||||
var oobErr error
|
|
||||||
|
|
||||||
var scm []syscall.SocketControlMessage
|
var scm []syscall.SocketControlMessage
|
||||||
if scm, oobErr = syscall.ParseSocketControlMessage(oob); oobErr != nil {
|
if scm, err = syscall.ParseSocketControlMessage(oob); err != nil {
|
||||||
ctx.closeReceivedFiles()
|
ctx.closeReceivedFiles()
|
||||||
err = oobErr
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var fds []int
|
var fds []int
|
||||||
for i := range scm {
|
for i := range scm {
|
||||||
if fds, oobErr = syscall.ParseUnixRights(&scm[i]); oobErr != nil {
|
if fds, err = syscall.ParseUnixRights(&scm[i]); err != nil {
|
||||||
ctx.closeReceivedFiles()
|
ctx.closeReceivedFiles()
|
||||||
err = oobErr
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.receivedFiles = append(ctx.receivedFiles, fds...)
|
ctx.receivedFiles = append(ctx.receivedFiles, fds...)
|
||||||
@@ -321,7 +310,7 @@ func (ctx *Context) recvmsg(remaining []byte) (payload []byte, err error) {
|
|||||||
err = syscall.EPIPE // not wrapped as it did not come from the syscall
|
err = syscall.EPIPE // not wrapped as it did not come from the syscall
|
||||||
}
|
}
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
payload = ctx.iovecBuf[:len(remaining)+n]
|
payload = ctx.iovecBuf[:n]
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -426,9 +415,6 @@ type eventProxy interface {
|
|||||||
consume(opcode byte, files []int, unmarshal func(v any)) error
|
consume(opcode byte, files []int, unmarshal func(v any)) error
|
||||||
// setBoundProps stores a [CoreBoundProps] event received from the server.
|
// setBoundProps stores a [CoreBoundProps] event received from the server.
|
||||||
setBoundProps(event *CoreBoundProps) error
|
setBoundProps(event *CoreBoundProps) error
|
||||||
// remove is called when the proxy is removed for any reason, usually from
|
|
||||||
// being targeted by a [PW_CORE_EVENT_REMOVE_ID] event.
|
|
||||||
remove() error
|
|
||||||
|
|
||||||
// Stringer returns the PipeWire interface name.
|
// Stringer returns the PipeWire interface name.
|
||||||
fmt.Stringer
|
fmt.Stringer
|
||||||
@@ -509,21 +495,13 @@ func (e DanglingFilesError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// An UnacknowledgedProxyError holds newly allocated proxy ids that the server failed
|
// An UnacknowledgedProxyError holds newly allocated proxy ids that the server failed
|
||||||
// to acknowledge after an otherwise successful [Core.Sync].
|
// to acknowledge after an otherwise successful [Context.Roundtrip].
|
||||||
type UnacknowledgedProxyError []Int
|
type UnacknowledgedProxyError []Int
|
||||||
|
|
||||||
func (e UnacknowledgedProxyError) Error() string {
|
func (e UnacknowledgedProxyError) Error() string {
|
||||||
return "server did not acknowledge " + strconv.Itoa(len(e)) + " proxies"
|
return "server did not acknowledge " + strconv.Itoa(len(e)) + " proxies"
|
||||||
}
|
}
|
||||||
|
|
||||||
// An UnacknowledgedProxyDestructionError holds destroyed proxy ids that the server failed
|
|
||||||
// to acknowledge after an otherwise successful [Core.Sync].
|
|
||||||
type UnacknowledgedProxyDestructionError []Int
|
|
||||||
|
|
||||||
func (e UnacknowledgedProxyDestructionError) Error() string {
|
|
||||||
return "server did not acknowledge " + strconv.Itoa(len(e)) + " proxy destructions"
|
|
||||||
}
|
|
||||||
|
|
||||||
// A ProxyFatalError describes an error that terminates event handling during a
|
// A ProxyFatalError describes an error that terminates event handling during a
|
||||||
// [Context.Roundtrip] and makes further event processing no longer possible.
|
// [Context.Roundtrip] and makes further event processing no longer possible.
|
||||||
type ProxyFatalError struct {
|
type ProxyFatalError struct {
|
||||||
@@ -603,9 +581,6 @@ func (ctx *Context) roundtrip() (err error) {
|
|||||||
if err = ctx.sendmsg(ctx.buf, ctx.pendingFiles...); err != nil {
|
if err = ctx.sendmsg(ctx.buf, ctx.pendingFiles...); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.buf = ctx.buf[:0]
|
|
||||||
ctx.pendingFiles = ctx.pendingFiles[:0]
|
|
||||||
ctx.headerFiles = 0
|
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
var danglingFiles DanglingFilesError
|
var danglingFiles DanglingFilesError
|
||||||
@@ -654,7 +629,7 @@ func (ctx *Context) roundtrip() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// currentSeq returns the current sequence number.
|
// currentSeq returns the current sequence number.
|
||||||
// This must only be called immediately after queueing a message.
|
// This must only be called from eventProxy.consume.
|
||||||
func (ctx *Context) currentSeq() Int { return ctx.sequence - 1 }
|
func (ctx *Context) currentSeq() Int { return ctx.sequence - 1 }
|
||||||
|
|
||||||
// currentRemoteSeq returns the current remote sequence number.
|
// currentRemoteSeq returns the current remote sequence number.
|
||||||
@@ -682,6 +657,10 @@ func (ctx *Context) consume(receiveRemaining []byte) (remaining []byte, err erro
|
|||||||
return
|
return
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
ctx.buf = ctx.buf[:0]
|
||||||
|
ctx.pendingFiles = ctx.pendingFiles[:0]
|
||||||
|
ctx.headerFiles = 0
|
||||||
|
|
||||||
if remaining, err = ctx.recvmsg(receiveRemaining); err != nil {
|
if remaining, err = ctx.recvmsg(receiveRemaining); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -695,14 +674,20 @@ func (ctx *Context) consume(receiveRemaining []byte) (remaining []byte, err erro
|
|||||||
if err = header.UnmarshalBinary(remaining[:SizeHeader]); err != nil {
|
if err = header.UnmarshalBinary(remaining[:SizeHeader]); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remote sequence sometimes do not start with 0
|
||||||
|
if ctx.remoteSequence == 0 {
|
||||||
|
ctx.remoteSequence = header.Sequence
|
||||||
|
}
|
||||||
|
|
||||||
if header.Sequence != ctx.remoteSequence {
|
if header.Sequence != ctx.remoteSequence {
|
||||||
return remaining, UnexpectedSequenceError(header.Sequence)
|
return remaining, UnexpectedSequenceError(header.Sequence)
|
||||||
}
|
}
|
||||||
|
ctx.remoteSequence++
|
||||||
|
|
||||||
if len(remaining) < int(SizeHeader+header.Size) {
|
if len(remaining) < int(SizeHeader+header.Size) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.remoteSequence++
|
|
||||||
|
|
||||||
proxy, ok := ctx.proxy[header.ID]
|
proxy, ok := ctx.proxy[header.ID]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -804,52 +789,6 @@ func (ctx *Context) Close() (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// expectsCoreError returns a function that inspects an error value and
|
|
||||||
// returns the address of a [CoreError] if it is the only error present
|
|
||||||
// and targets the specified proxy and sequence.
|
|
||||||
//
|
|
||||||
// The behaviour of expectsCoreError is only correct for an empty buf
|
|
||||||
// prior to calling. If buf is not empty, [Core.Sync] is called, with
|
|
||||||
// its return value stored to the value pointed to by errP if not nil,
|
|
||||||
// and the function is not populated.
|
|
||||||
//
|
|
||||||
// The caller must queue a message and call [Core.Sync] immediately
|
|
||||||
// after calling expectsCoreError.
|
|
||||||
func (ctx *Context) expectsCoreError(id Int, errP *error) (asCoreError func() (coreError *CoreError)) {
|
|
||||||
if len(ctx.buf) > 0 {
|
|
||||||
if err := ctx.GetCore().Sync(); err != nil {
|
|
||||||
*errP = err
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sequence := ctx.sequence
|
|
||||||
return func() (coreError *CoreError) {
|
|
||||||
if proxyErrors, ok := (*errP).(ProxyConsumeError); !ok ||
|
|
||||||
len(proxyErrors) != 1 ||
|
|
||||||
!errors.As(proxyErrors[0], &coreError) ||
|
|
||||||
coreError == nil ||
|
|
||||||
coreError.ID != id ||
|
|
||||||
coreError.Sequence != sequence {
|
|
||||||
// do not return a non-matching CoreError
|
|
||||||
coreError = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A PermissionError describes an error emitted by the server when trying to
|
|
||||||
// perform an operation that the client has no permission for.
|
|
||||||
type PermissionError struct {
|
|
||||||
// The id of the resource (proxy if emitted by the client) that is in error.
|
|
||||||
ID Int `json:"id"`
|
|
||||||
// An error message.
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*PermissionError) Unwrap() error { return syscall.EPERM }
|
|
||||||
func (e *PermissionError) Error() string { return e.Message }
|
|
||||||
|
|
||||||
// Remote is the environment (sic) with the remote name.
|
// Remote is the environment (sic) with the remote name.
|
||||||
const Remote = "PIPEWIRE_REMOTE"
|
const Remote = "PIPEWIRE_REMOTE"
|
||||||
|
|
||||||
@@ -884,7 +823,8 @@ func connectName(name string, manager bool) (conn *net.UnixConn, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ConnectName connects to a PipeWire remote by name.
|
// ConnectName connects to a PipeWire remote by name.
|
||||||
func ConnectName(name string, manager bool, props SPADict) (ctx *Context, err error) {
|
func ConnectName(name string, manager bool) (ctx *Context, err error) {
|
||||||
|
var props SPADict
|
||||||
if manager {
|
if manager {
|
||||||
props = append(props, SPADictItem{Key: PW_KEY_REMOTE_INTENTION, Value: "manager"})
|
props = append(props, SPADictItem{Key: PW_KEY_REMOTE_INTENTION, Value: "manager"})
|
||||||
}
|
}
|
||||||
@@ -910,6 +850,4 @@ func ConnectName(name string, manager bool, props SPADict) (ctx *Context, err er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Connect connects to the PipeWire remote.
|
// Connect connects to the PipeWire remote.
|
||||||
func Connect(manager bool, props SPADict) (ctx *Context, err error) {
|
func Connect(manager bool) (ctx *Context, err error) { return ConnectName("", manager) }
|
||||||
return ConnectName("", manager, props)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -680,6 +680,9 @@ func TestContext(t *testing.T) {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
t.Fatalf("SecurityContext.Create: error = %v", err)
|
t.Fatalf("SecurityContext.Create: error = %v", err)
|
||||||
}
|
}
|
||||||
|
if err := ctx.GetCore().Sync(); err != nil {
|
||||||
|
t.Fatalf("Sync: error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// none of these should change
|
// none of these should change
|
||||||
if coreInfo := ctx.GetCore().Info; !reflect.DeepEqual(coreInfo, &wantCoreInfo0) {
|
if coreInfo := ctx.GetCore().Info; !reflect.DeepEqual(coreInfo, &wantCoreInfo0) {
|
||||||
@@ -833,7 +836,6 @@ func TestContextErrors(t *testing.T) {
|
|||||||
|
|
||||||
{"UnexpectedFileCountError", &pipewire.UnexpectedFileCountError{0, -1}, "received -1 files instead of the expected 0"},
|
{"UnexpectedFileCountError", &pipewire.UnexpectedFileCountError{0, -1}, "received -1 files instead of the expected 0"},
|
||||||
{"UnacknowledgedProxyError", make(pipewire.UnacknowledgedProxyError, 1<<4), "server did not acknowledge 16 proxies"},
|
{"UnacknowledgedProxyError", make(pipewire.UnacknowledgedProxyError, 1<<4), "server did not acknowledge 16 proxies"},
|
||||||
{"UnacknowledgedProxyDestructionError", make(pipewire.UnacknowledgedProxyDestructionError, 1<<4), "server did not acknowledge 16 proxy destructions"},
|
|
||||||
{"DanglingFilesError", make(pipewire.DanglingFilesError, 1<<4), "received 16 dangling files"},
|
{"DanglingFilesError", make(pipewire.DanglingFilesError, 1<<4), "received 16 dangling files"},
|
||||||
{"UnexpectedFilesError", pipewire.UnexpectedFilesError(1 << 4), "server message headers claim to have sent more files than actually received"},
|
{"UnexpectedFilesError", pipewire.UnexpectedFilesError(1 << 4), "server message headers claim to have sent more files than actually received"},
|
||||||
{"UnexpectedSequenceError", pipewire.UnexpectedSequenceError(1 << 4), "unexpected seq 16"},
|
{"UnexpectedSequenceError", pipewire.UnexpectedSequenceError(1 << 4), "unexpected seq 16"},
|
||||||
@@ -860,19 +862,6 @@ func TestContextErrors(t *testing.T) {
|
|||||||
ID: 0xbad,
|
ID: 0xbad,
|
||||||
Sequence: 0xcafe,
|
Sequence: 0xcafe,
|
||||||
}, "received Core::Ping seq 51966 targeting unknown proxy id 2989"},
|
}, "received Core::Ping seq 51966 targeting unknown proxy id 2989"},
|
||||||
|
|
||||||
{"GlobalIDCollisionError", &pipewire.GlobalIDCollisionError{
|
|
||||||
ID: 0xbad,
|
|
||||||
Previous: &pipewire.RegistryGlobal{Type: "PipeWire:Interface:Invalid"},
|
|
||||||
Current: &pipewire.RegistryGlobal{Type: "PipeWire:Interface:NewInvalid"},
|
|
||||||
}, "new Registry::Global event for PipeWire:Interface:NewInvalid stepping on previous id 2989 for PipeWire:Interface:Invalid"},
|
|
||||||
|
|
||||||
{"UnknownGlobalIDRemoveError", pipewire.UnknownGlobalIDRemoveError(0xbad), "Registry::GlobalRemove event targets unknown id 2989"},
|
|
||||||
|
|
||||||
{"PermissionError", &pipewire.PermissionError{
|
|
||||||
ID: 2,
|
|
||||||
Message: "no permission to destroy 0",
|
|
||||||
}, "no permission to destroy 0"},
|
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -384,15 +384,18 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
|
|||||||
return nil
|
return nil
|
||||||
|
|
||||||
case reflect.Pointer:
|
case reflect.Pointer:
|
||||||
if ok, err := unmarshalHandleNone(&data, wireSizeP); err != nil {
|
if len(data) < SizePrefix {
|
||||||
return err
|
return ErrEOFPrefix
|
||||||
} else if ok {
|
}
|
||||||
|
switch SPAKind(binary.NativeEndian.Uint32(data[SizeSPrefix:])) {
|
||||||
|
case SPA_TYPE_None:
|
||||||
v.SetZero()
|
v.SetZero()
|
||||||
return nil
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
v.Set(reflect.New(v.Type().Elem()))
|
default:
|
||||||
return unmarshalValue(data, v.Elem(), wireSizeP)
|
v.Set(reflect.New(v.Type().Elem()))
|
||||||
|
return unmarshalValue(data, v.Elem(), wireSizeP)
|
||||||
|
}
|
||||||
|
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
*wireSizeP = 0
|
*wireSizeP = 0
|
||||||
@@ -419,29 +422,6 @@ func unmarshalValue(data []byte, v reflect.Value, wireSizeP *Word) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unmarshalHandleNone establishes prefix bounds and, for a value of type [SPA_TYPE_None],
|
|
||||||
// validates its size and skips the header. This is for unmarshalling values that can be nil.
|
|
||||||
func unmarshalHandleNone(data *[]byte, wireSizeP *Word) (bool, error) {
|
|
||||||
if len(*data) < SizePrefix {
|
|
||||||
return false, ErrEOFPrefix
|
|
||||||
}
|
|
||||||
|
|
||||||
if SPAKind(binary.NativeEndian.Uint32((*data)[SizeSPrefix:])) != SPA_TYPE_None {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
*wireSizeP = 0
|
|
||||||
if err := unmarshalCheckTypeBounds(data, SPA_TYPE_None, wireSizeP); err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(*data) != 0 {
|
|
||||||
return true, TrailingGarbageError(*data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// An InconsistentSizeError describes an inconsistent size prefix encountered
|
// An InconsistentSizeError describes an inconsistent size prefix encountered
|
||||||
// in data passed to [Unmarshal].
|
// in data passed to [Unmarshal].
|
||||||
type InconsistentSizeError struct{ Prefix, Expect Word }
|
type InconsistentSizeError struct{ Prefix, Expect Word }
|
||||||
|
|||||||
@@ -26,10 +26,9 @@ const (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
PW_SECURITY_CONTEXT_METHOD_ADD_LISTENER = iota
|
PW_SECURITY_CONTEXT_METHOD_ADD_LISTENER = iota
|
||||||
|
|
||||||
PW_SECURITY_CONTEXT_METHOD_CREATE
|
PW_SECURITY_CONTEXT_METHOD_CREATE
|
||||||
|
|
||||||
PW_SECURITY_CONTEXT_METHOD_NUM
|
PW_SECURITY_CONTEXT_METHOD_NUM
|
||||||
|
|
||||||
PW_VERSION_SECURITY_CONTEXT_METHODS = 0
|
PW_VERSION_SECURITY_CONTEXT_METHODS = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -91,8 +90,6 @@ type SecurityContext struct {
|
|||||||
GlobalID Int `json:"id"`
|
GlobalID Int `json:"id"`
|
||||||
|
|
||||||
ctx *Context
|
ctx *Context
|
||||||
|
|
||||||
destructible
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSecurityContext queues a [RegistryBind] message for the PipeWire server
|
// GetSecurityContext queues a [RegistryBind] message for the PipeWire server
|
||||||
@@ -111,39 +108,13 @@ func (registry *Registry) GetSecurityContext() (securityContext *SecurityContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create queues a [SecurityContextCreate] message for the PipeWire server.
|
// Create queues a [SecurityContextCreate] message for the PipeWire server.
|
||||||
func (securityContext *SecurityContext) Create(listenFd, closeFd int, props SPADict) (err error) {
|
func (securityContext *SecurityContext) Create(listenFd, closeFd int, props SPADict) error {
|
||||||
if err = securityContext.checkDestroy(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
asCoreError := securityContext.ctx.expectsCoreError(securityContext.ID, &err)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// queued in reverse based on upstream behaviour, unsure why
|
// queued in reverse based on upstream behaviour, unsure why
|
||||||
offset := securityContext.ctx.queueFiles(closeFd, listenFd)
|
offset := securityContext.ctx.queueFiles(closeFd, listenFd)
|
||||||
if err = securityContext.ctx.writeMessage(
|
return securityContext.ctx.writeMessage(
|
||||||
securityContext.ID,
|
securityContext.ID,
|
||||||
&SecurityContextCreate{ListenFd: offset + 1, CloseFd: offset + 0, Properties: &props},
|
&SecurityContextCreate{ListenFd: offset + 1, CloseFd: offset + 0, Properties: &props},
|
||||||
); err != nil {
|
)
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = securityContext.ctx.GetCore().Sync(); err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if coreError := asCoreError(); coreError == nil {
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
switch syscall.Errno(-coreError.Result) {
|
|
||||||
case syscall.EPERM:
|
|
||||||
return &PermissionError{securityContext.ID, coreError.Message}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return coreError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// securityContextCloser holds onto resources associated to the security context.
|
// securityContextCloser holds onto resources associated to the security context.
|
||||||
@@ -173,9 +144,6 @@ func (scc *securityContextCloser) Close() (err error) {
|
|||||||
// BindAndCreate binds a new socket to the specified pathname and pass it to Create.
|
// BindAndCreate binds a new socket to the specified pathname and pass it to Create.
|
||||||
// It returns an [io.Closer] corresponding to [SecurityContextCreate.CloseFd].
|
// It returns an [io.Closer] corresponding to [SecurityContextCreate.CloseFd].
|
||||||
func (securityContext *SecurityContext) BindAndCreate(pathname string, props SPADict) (io.Closer, error) {
|
func (securityContext *SecurityContext) BindAndCreate(pathname string, props SPADict) (io.Closer, error) {
|
||||||
if err := securityContext.checkDestroy(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var scc securityContextCloser
|
var scc securityContextCloser
|
||||||
|
|
||||||
// ensure pathname is available
|
// ensure pathname is available
|
||||||
@@ -217,19 +185,17 @@ func (securityContext *SecurityContext) BindAndCreate(pathname string, props SPA
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (securityContext *SecurityContext) consume(opcode byte, files []int, _ func(v any)) error {
|
func (securityContext *SecurityContext) consume(opcode byte, files []int, _ func(v any)) error {
|
||||||
securityContext.mustCheckDestroy()
|
|
||||||
closeReceivedFiles(files...)
|
closeReceivedFiles(files...)
|
||||||
switch opcode {
|
switch opcode {
|
||||||
// SecurityContext does not receive any events
|
// SecurityContext does not receive any events
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(&UnsupportedOpcodeError{opcode, securityContext.String()})
|
return &UnsupportedOpcodeError{opcode, securityContext.String()}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (securityContext *SecurityContext) setBoundProps(event *CoreBoundProps) error {
|
func (securityContext *SecurityContext) setBoundProps(event *CoreBoundProps) error {
|
||||||
securityContext.mustCheckDestroy()
|
|
||||||
if securityContext.ID != event.ID {
|
if securityContext.ID != event.ID {
|
||||||
return &InconsistentIdError{Proxy: securityContext, ID: securityContext.ID, ServerID: event.ID}
|
return &InconsistentIdError{Proxy: securityContext, ID: securityContext.ID, ServerID: event.ID}
|
||||||
}
|
}
|
||||||
@@ -239,9 +205,4 @@ func (securityContext *SecurityContext) setBoundProps(event *CoreBoundProps) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy destroys this [SecurityContext] proxy.
|
|
||||||
func (securityContext *SecurityContext) Destroy() error {
|
|
||||||
return securityContext.destroy(securityContext.ctx, securityContext.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (securityContext *SecurityContext) String() string { return PW_TYPE_INTERFACE_SecurityContext }
|
func (securityContext *SecurityContext) String() string { return PW_TYPE_INTERFACE_SecurityContext }
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func (k direct) waylandNew(displayPath, bindPath *check.Absolute, appID, instanc
|
|||||||
return wayland.New(displayPath, bindPath, appID, instanceID)
|
return wayland.New(displayPath, bindPath, appID, instanceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k direct) pipewireConnect() (*pipewire.Context, error) { return pipewire.Connect(true, nil) }
|
func (k direct) pipewireConnect() (*pipewire.Context, error) { return pipewire.Connect(true) }
|
||||||
|
|
||||||
func (k direct) xcbChangeHosts(mode xcb.HostMode, family xcb.Family, address string) error {
|
func (k direct) xcbChangeHosts(mode xcb.HostMode, family xcb.Family, address string) error {
|
||||||
return xcb.ChangeHosts(mode, family, address)
|
return xcb.ChangeHosts(mode, family, address)
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import (
|
|||||||
// PipeWire maintains a pipewire socket with SecurityContext attached via [pipewire].
|
// PipeWire maintains a pipewire socket with SecurityContext attached via [pipewire].
|
||||||
// The socket stops accepting connections once the pipe referred to by sync is closed.
|
// The socket stops accepting connections once the pipe referred to by sync is closed.
|
||||||
// The socket is pathname only and is destroyed on revert.
|
// The socket is pathname only and is destroyed on revert.
|
||||||
func (sys *I) PipeWire(dst *check.Absolute, appID, instanceID string) *I {
|
func (sys *I) PipeWire(dst *check.Absolute) *I {
|
||||||
sys.ops = append(sys.ops, &pipewireOp{nil, dst, appID, instanceID})
|
sys.ops = append(sys.ops, &pipewireOp{nil, dst})
|
||||||
return sys
|
return sys
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,8 +23,6 @@ func (sys *I) PipeWire(dst *check.Absolute, appID, instanceID string) *I {
|
|||||||
type pipewireOp struct {
|
type pipewireOp struct {
|
||||||
scc io.Closer
|
scc io.Closer
|
||||||
dst *check.Absolute
|
dst *check.Absolute
|
||||||
|
|
||||||
appID, instanceID string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pipewireOp) Type() hst.Enablement { return Process }
|
func (p *pipewireOp) Type() hst.Enablement { return Process }
|
||||||
@@ -58,11 +56,12 @@ func (p *pipewireOp) apply(sys *I) (err error) {
|
|||||||
|
|
||||||
if p.scc, err = securityContext.BindAndCreate(p.dst.String(), pipewire.SPADict{
|
if p.scc, err = securityContext.BindAndCreate(p.dst.String(), pipewire.SPADict{
|
||||||
{Key: pipewire.PW_KEY_SEC_ENGINE, Value: "app.hakurei"},
|
{Key: pipewire.PW_KEY_SEC_ENGINE, Value: "app.hakurei"},
|
||||||
{Key: pipewire.PW_KEY_SEC_APP_ID, Value: p.appID},
|
|
||||||
{Key: pipewire.PW_KEY_SEC_INSTANCE_ID, Value: p.instanceID},
|
|
||||||
{Key: pipewire.PW_KEY_ACCESS, Value: "restricted"},
|
{Key: pipewire.PW_KEY_ACCESS, Value: "restricted"},
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return newOpError("pipewire", err, false)
|
return newOpError("pipewire", err, false)
|
||||||
|
} else if err = ctx.GetCore().Sync(); err != nil {
|
||||||
|
_ = p.scc.Close()
|
||||||
|
return newOpError("pipewire", err, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = sys.chmod(p.dst.String(), 0); err != nil {
|
if err = sys.chmod(p.dst.String(), 0); err != nil {
|
||||||
@@ -93,9 +92,7 @@ func (p *pipewireOp) revert(sys *I, _ *Criteria) error {
|
|||||||
func (p *pipewireOp) Is(o Op) bool {
|
func (p *pipewireOp) Is(o Op) bool {
|
||||||
target, ok := o.(*pipewireOp)
|
target, ok := o.(*pipewireOp)
|
||||||
return ok && p != nil && target != nil &&
|
return ok && p != nil && target != nil &&
|
||||||
p.dst.Is(target.dst) &&
|
p.dst.Is(target.dst)
|
||||||
p.appID == target.appID &&
|
|
||||||
p.instanceID == target.instanceID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pipewireOp) Path() string { return p.dst.String() }
|
func (p *pipewireOp) Path() string { return p.dst.String() }
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ func TestPipeWireOp(t *testing.T) {
|
|||||||
checkOpBehaviour(t, checkNoParallel, []opBehaviourTestCase{
|
checkOpBehaviour(t, checkNoParallel, []opBehaviourTestCase{
|
||||||
{"success", 0xbeef, 0xff, &pipewireOp{nil,
|
{"success", 0xbeef, 0xff, &pipewireOp{nil,
|
||||||
m(path.Join(t.TempDir(), "pipewire")),
|
m(path.Join(t.TempDir(), "pipewire")),
|
||||||
"org.chromium.Chromium",
|
|
||||||
"ebf083d1b175911782d413369b64ce7c",
|
|
||||||
}, []stub.Call{
|
}, []stub.Call{
|
||||||
call("pipewireConnect", stub.ExpectArgs{}, func() *pipewire.Context {
|
call("pipewireConnect", stub.ExpectArgs{}, func() *pipewire.Context {
|
||||||
if ctx, err := pipewire.New(&stubPipeWireConn{sendmsg: []string{
|
if ctx, err := pipewire.New(&stubPipeWireConn{sendmsg: []string{
|
||||||
@@ -156,11 +154,11 @@ func TestPipeWireOp(t *testing.T) {
|
|||||||
string([]byte{
|
string([]byte{
|
||||||
// header: SecurityContext::Create
|
// header: SecurityContext::Create
|
||||||
3, 0, 0, 0,
|
3, 0, 0, 0,
|
||||||
0x40, 1, 0, 1,
|
0xa8, 0, 0, 1,
|
||||||
5, 0, 0, 0,
|
5, 0, 0, 0,
|
||||||
2, 0, 0, 0,
|
2, 0, 0, 0,
|
||||||
// Struct
|
// Struct
|
||||||
0x38, 1, 0, 0,
|
0xa0, 0, 0, 0,
|
||||||
0xe, 0, 0, 0,
|
0xe, 0, 0, 0,
|
||||||
// Fd: listen_fd = 1
|
// Fd: listen_fd = 1
|
||||||
8, 0, 0, 0,
|
8, 0, 0, 0,
|
||||||
@@ -173,12 +171,12 @@ func TestPipeWireOp(t *testing.T) {
|
|||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
// Struct: spa_dict
|
// Struct: spa_dict
|
||||||
0x10, 1, 0, 0,
|
0x78, 0, 0, 0,
|
||||||
0xe, 0, 0, 0,
|
0xe, 0, 0, 0,
|
||||||
// Int: n_items = 4
|
// Int: n_items = 2
|
||||||
4, 0, 0, 0,
|
|
||||||
4, 0, 0, 0,
|
4, 0, 0, 0,
|
||||||
4, 0, 0, 0,
|
4, 0, 0, 0,
|
||||||
|
2, 0, 0, 0,
|
||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
// String: key = "pipewire.sec.engine"
|
// String: key = "pipewire.sec.engine"
|
||||||
0x14, 0, 0, 0,
|
0x14, 0, 0, 0,
|
||||||
@@ -196,48 +194,6 @@ func TestPipeWireOp(t *testing.T) {
|
|||||||
0x68, 0x61, 0x6b, 0x75,
|
0x68, 0x61, 0x6b, 0x75,
|
||||||
0x72, 0x65, 0x69, 0,
|
0x72, 0x65, 0x69, 0,
|
||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
// String: key = "pipewire.sec.app-id"
|
|
||||||
0x14, 0, 0, 0,
|
|
||||||
8, 0, 0, 0,
|
|
||||||
0x70, 0x69, 0x70, 0x65,
|
|
||||||
0x77, 0x69, 0x72, 0x65,
|
|
||||||
0x2e, 0x73, 0x65, 0x63,
|
|
||||||
0x2e, 0x61, 0x70, 0x70,
|
|
||||||
0x2d, 0x69, 0x64, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
// String: value = "org.chromium.Chromium"
|
|
||||||
0x16, 0, 0, 0,
|
|
||||||
8, 0, 0, 0,
|
|
||||||
0x6f, 0x72, 0x67, 0x2e,
|
|
||||||
0x63, 0x68, 0x72, 0x6f,
|
|
||||||
0x6d, 0x69, 0x75, 0x6d,
|
|
||||||
0x2e, 0x43, 0x68, 0x72,
|
|
||||||
0x6f, 0x6d, 0x69, 0x75,
|
|
||||||
// String: key = "pipewire.sec.instance-id"
|
|
||||||
0x6d, 0, 0, 0,
|
|
||||||
0x19, 0, 0, 0,
|
|
||||||
8, 0, 0, 0,
|
|
||||||
0x70, 0x69, 0x70, 0x65,
|
|
||||||
0x77, 0x69, 0x72, 0x65,
|
|
||||||
0x2e, 0x73, 0x65, 0x63,
|
|
||||||
0x2e, 0x69, 0x6e, 0x73,
|
|
||||||
0x74, 0x61, 0x6e, 0x63,
|
|
||||||
0x65, 0x2d, 0x69, 0x64,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
// String: value = "ebf083d1b175911782d413369b64ce7c"
|
|
||||||
0x21, 0, 0, 0,
|
|
||||||
8, 0, 0, 0,
|
|
||||||
0x65, 0x62, 0x66, 0x30,
|
|
||||||
0x38, 0x33, 0x64, 0x31,
|
|
||||||
0x62, 0x31, 0x37, 0x35,
|
|
||||||
0x39, 0x31, 0x31, 0x37,
|
|
||||||
0x38, 0x32, 0x64, 0x34,
|
|
||||||
0x31, 0x33, 0x33, 0x36,
|
|
||||||
0x39, 0x62, 0x36, 0x34,
|
|
||||||
0x63, 0x65, 0x37, 0x63,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
// String: key = "pipewire.access"
|
// String: key = "pipewire.access"
|
||||||
0x10, 0, 0, 0,
|
0x10, 0, 0, 0,
|
||||||
8, 0, 0, 0,
|
8, 0, 0, 0,
|
||||||
@@ -430,43 +386,29 @@ func TestPipeWireOp(t *testing.T) {
|
|||||||
|
|
||||||
checkOpsBuilder(t, "PipeWire", []opsBuilderTestCase{
|
checkOpsBuilder(t, "PipeWire", []opsBuilderTestCase{
|
||||||
{"sample", 0xcafe, func(_ *testing.T, sys *I) {
|
{"sample", 0xcafe, func(_ *testing.T, sys *I) {
|
||||||
sys.PipeWire(m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
sys.PipeWire(m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"))
|
||||||
"org.chromium.Chromium",
|
|
||||||
"ebf083d1b175911782d413369b64ce7c")
|
|
||||||
}, []Op{&pipewireOp{nil,
|
}, []Op{&pipewireOp{nil,
|
||||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
||||||
"org.chromium.Chromium",
|
|
||||||
"ebf083d1b175911782d413369b64ce7c",
|
|
||||||
}}, stub.Expect{}},
|
}}, stub.Expect{}},
|
||||||
})
|
})
|
||||||
|
|
||||||
checkOpIs(t, []opIsTestCase{
|
checkOpIs(t, []opIsTestCase{
|
||||||
{"dst differs", &pipewireOp{nil,
|
{"dst differs", &pipewireOp{nil,
|
||||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7d/pipewire"),
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7d/pipewire"),
|
||||||
"org.chromium.Chromium",
|
|
||||||
"ebf083d1b175911782d413369b64ce7c",
|
|
||||||
}, &pipewireOp{nil,
|
}, &pipewireOp{nil,
|
||||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
||||||
"org.chromium.Chromium",
|
|
||||||
"ebf083d1b175911782d413369b64ce7c",
|
|
||||||
}, false},
|
}, false},
|
||||||
|
|
||||||
{"equals", &pipewireOp{nil,
|
{"equals", &pipewireOp{nil,
|
||||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
||||||
"org.chromium.Chromium",
|
|
||||||
"ebf083d1b175911782d413369b64ce7c",
|
|
||||||
}, &pipewireOp{nil,
|
}, &pipewireOp{nil,
|
||||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
||||||
"org.chromium.Chromium",
|
|
||||||
"ebf083d1b175911782d413369b64ce7c",
|
|
||||||
}, true},
|
}, true},
|
||||||
})
|
})
|
||||||
|
|
||||||
checkOpMeta(t, []opMetaTestCase{
|
checkOpMeta(t, []opMetaTestCase{
|
||||||
{"sample", &pipewireOp{nil,
|
{"sample", &pipewireOp{nil,
|
||||||
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
m("/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"),
|
||||||
"org.chromium.Chromium",
|
|
||||||
"ebf083d1b175911782d413369b64ce7c",
|
|
||||||
}, Process, "/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire",
|
}, Process, "/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire",
|
||||||
`pipewire socket at "/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"`},
|
`pipewire socket at "/tmp/hakurei.1971/ebf083d1b175911782d413369b64ce7c/pipewire"`},
|
||||||
})
|
})
|
||||||
|
|||||||
11
nixos.nix
11
nixos.nix
@@ -68,7 +68,7 @@ in
|
|||||||
|
|
||||||
home-manager =
|
home-manager =
|
||||||
let
|
let
|
||||||
privPackages = mapAttrs (_: userid: {
|
privPackages = mapAttrs (username: userid: {
|
||||||
home.packages = foldlAttrs (
|
home.packages = foldlAttrs (
|
||||||
acc: id: app:
|
acc: id: app:
|
||||||
[
|
[
|
||||||
@@ -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"
|
||||||
|
"exec pipewire-pulse"
|
||||||
|
];
|
||||||
|
}
|
||||||
++ [
|
++ [
|
||||||
{
|
{
|
||||||
type = "bind";
|
type = "bind";
|
||||||
|
|||||||
10
options.md
10
options.md
@@ -35,7 +35,7 @@ package
|
|||||||
|
|
||||||
|
|
||||||
*Default:*
|
*Default:*
|
||||||
` <derivation hakurei-static-x86_64-unknown-linux-musl-0.3.3> `
|
` <derivation hakurei-static-x86_64-unknown-linux-musl-0.3.1> `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -73,11 +73,11 @@ null or boolean
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## environment\.hakurei\.apps\.\<name>\.enablements\.pipewire
|
## environment\.hakurei\.apps\.\<name>\.enablements\.pulse
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Whether to share the PipeWire server via pipewire-pulse on a SecurityContext socket\.
|
Whether to share the PulseAudio socket and cookie\.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ null or boolean
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Whether to share the Wayland server via security-context-v1\.
|
Whether to share the Wayland socket\.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -805,7 +805,7 @@ package
|
|||||||
|
|
||||||
|
|
||||||
*Default:*
|
*Default:*
|
||||||
` <derivation hakurei-hsu-0.3.3> `
|
` <derivation hakurei-hsu-0.3.1> `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
14
options.nix
14
options.nix
@@ -12,13 +12,13 @@ in
|
|||||||
|
|
||||||
package = mkOption {
|
package = mkOption {
|
||||||
type = types.package;
|
type = types.package;
|
||||||
default = packages.${pkgs.stdenv.hostPlatform.system}.hakurei;
|
default = packages.${pkgs.system}.hakurei;
|
||||||
description = "The hakurei package to use.";
|
description = "The hakurei package to use.";
|
||||||
};
|
};
|
||||||
|
|
||||||
hsuPackage = mkOption {
|
hsuPackage = mkOption {
|
||||||
type = types.package;
|
type = types.package;
|
||||||
default = packages.${pkgs.stdenv.hostPlatform.system}.hsu;
|
default = packages.${pkgs.system}.hsu;
|
||||||
description = "The hsu package to use.";
|
description = "The hsu package to use.";
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -242,11 +242,19 @@ in
|
|||||||
type = nullOr bool;
|
type = nullOr bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = ''
|
description = ''
|
||||||
Whether to share the PipeWire server via pipewire-pulse on a SecurityContext socket.
|
Whether to share the PipeWire server via SecurityContext.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pulse = mkOption {
|
||||||
|
type = nullOr bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether to run the PulseAudio compatibility daemon.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
share = mkOption {
|
share = mkOption {
|
||||||
type = nullOr package;
|
type = nullOr package;
|
||||||
default = null;
|
default = null;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
buildGoModule rec {
|
buildGoModule rec {
|
||||||
pname = "hakurei";
|
pname = "hakurei";
|
||||||
version = "0.3.3";
|
version = "0.3.2";
|
||||||
|
|
||||||
srcFiltered = builtins.path {
|
srcFiltered = builtins.path {
|
||||||
name = "${pname}-src";
|
name = "${pname}-src";
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
|
|
||||||
virtualisation = {
|
virtualisation = {
|
||||||
# Hopefully reduces spurious test failures:
|
# Hopefully reduces spurious test failures:
|
||||||
memorySize = if pkgs.stdenv.hostPlatform.is32bit then 2046 else 8192;
|
memorySize = if pkgs.hostPlatform.is32bit then 2046 else 8192;
|
||||||
|
|
||||||
qemu.options = [
|
qemu.options = [
|
||||||
# Need to switch to a different GPU driver than the default one (-vga std) so that Sway can launch:
|
# Need to switch to a different GPU driver than the default one (-vga std) so that Sway can launch:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
testers,
|
nixosTest,
|
||||||
buildFHSEnv,
|
buildFHSEnv,
|
||||||
writeShellScriptBin,
|
writeShellScriptBin,
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
withRace ? false,
|
withRace ? false,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
testers.nixosTest {
|
nixosTest {
|
||||||
name = "hakurei" + (if withRace then "-race" else "");
|
name = "hakurei" + (if withRace then "-race" else "");
|
||||||
nodes.machine =
|
nodes.machine =
|
||||||
{ options, pkgs, ... }:
|
{ options, pkgs, ... }:
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ in
|
|||||||
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
|
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
|
||||||
"DISPLAY=unix:/tmp/.X11-unix/X0"
|
"DISPLAY=unix:/tmp/.X11-unix/X0"
|
||||||
"HOME=/var/lib/hakurei/u0/a4"
|
"HOME=/var/lib/hakurei/u0/a4"
|
||||||
|
"PIPEWIRE_REMOTE=/run/user/65534/pipewire-0"
|
||||||
"SHELL=/run/current-system/sw/bin/bash"
|
"SHELL=/run/current-system/sw/bin/bash"
|
||||||
"TERM=linux"
|
"TERM=linux"
|
||||||
"USER=u0_a4"
|
"USER=u0_a4"
|
||||||
@@ -48,7 +49,6 @@ in
|
|||||||
"XDG_RUNTIME_DIR=/run/user/65534"
|
"XDG_RUNTIME_DIR=/run/user/65534"
|
||||||
"XDG_SESSION_CLASS=user"
|
"XDG_SESSION_CLASS=user"
|
||||||
"XDG_SESSION_TYPE=wayland"
|
"XDG_SESSION_TYPE=wayland"
|
||||||
"PULSE_SERVER=unix:/run/user/65534/pulse/native"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
fs = fs "dead" {
|
fs = fs "dead" {
|
||||||
@@ -75,7 +75,6 @@ in
|
|||||||
"fstab" = fs "80001ff" null null;
|
"fstab" = fs "80001ff" null null;
|
||||||
"hsurc" = fs "80001ff" null null;
|
"hsurc" = fs "80001ff" null null;
|
||||||
"fuse.conf" = fs "80001ff" null null;
|
"fuse.conf" = fs "80001ff" null null;
|
||||||
"gai.conf" = fs "80001ff" null null;
|
|
||||||
"group" = fs "180" null "hakurei:x:65534:\n";
|
"group" = fs "180" null "hakurei:x:65534:\n";
|
||||||
"host.conf" = fs "80001ff" null null;
|
"host.conf" = fs "80001ff" null null;
|
||||||
"hostname" = fs "80001ff" null null;
|
"hostname" = fs "80001ff" null null;
|
||||||
@@ -138,8 +137,12 @@ in
|
|||||||
user = fs "800001ed" {
|
user = fs "800001ed" {
|
||||||
"65534" = fs "800001c0" {
|
"65534" = fs "800001c0" {
|
||||||
bus = fs "10001fd" null null;
|
bus = fs "10001fd" null null;
|
||||||
pulse = fs "800001c0" { native = fs "10001ff" null null; } null;
|
pulse = fs "800001c0" {
|
||||||
|
native = fs "10001ff" null null;
|
||||||
|
pid = fs "1a4" null null;
|
||||||
|
} null;
|
||||||
wayland-0 = fs "1000038" null null;
|
wayland-0 = fs "1000038" null null;
|
||||||
|
pipewire-0 = fs "1000038" null null;
|
||||||
} null;
|
} null;
|
||||||
} null;
|
} null;
|
||||||
} null;
|
} null;
|
||||||
@@ -190,6 +193,8 @@ in
|
|||||||
home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
|
home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
|
||||||
nix = fs "800001ed" {
|
nix = fs "800001ed" {
|
||||||
profiles = fs "800001ed" {
|
profiles = fs "800001ed" {
|
||||||
|
home-manager = fs "80001ff" null null;
|
||||||
|
home-manager-1-link = fs "80001ff" null null;
|
||||||
profile = fs "80001ff" null null;
|
profile = fs "80001ff" null null;
|
||||||
profile-1-link = fs "80001ff" null null;
|
profile-1-link = fs "80001ff" null null;
|
||||||
} null;
|
} null;
|
||||||
@@ -220,14 +225,15 @@ 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 "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10004,gid=10004")
|
||||||
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,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/vda" "rw")
|
(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/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 "/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/vda" "rw")
|
(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/vda" "rw")
|
(ent "/tmp/.X11-unix" "/tmp/.X11-unix" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent ignore "/run/user/65534/pipewire-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "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")
|
||||||
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
||||||
(ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
@@ -235,13 +241,12 @@ in
|
|||||||
(ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
||||||
(ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/" "/.hakurei/.ro-store" "rw,relatime" "overlay" "overlay" "ro,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,redirect_dir=nofollow,userxattr")
|
(ent "/" "/.hakurei/.ro-store" "rw,relatime" "overlay" "overlay" "ro,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,redirect_dir=nofollow,userxattr")
|
||||||
(ent "/" "/.hakurei/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,upperdir=/host/tmp/.hakurei-store-rw/upper,workdir=/host/tmp/.hakurei-store-rw/work,redirect_dir=nofollow,userxattr")
|
(ent "/" "/.hakurei/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,upperdir=/host/tmp/.hakurei-store-rw/upper,workdir=/host/tmp/.hakurei-store-rw/work,redirect_dir=nofollow,userxattr")
|
||||||
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/var/lib/hakurei/u0/a4" "/var/lib/hakurei/u0/a4" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/lib/hakurei/u0/a4" "/var/lib/hakurei/u0/a4" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
|
||||||
];
|
];
|
||||||
|
|
||||||
seccomp = true;
|
seccomp = true;
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ in
|
|||||||
env = [
|
env = [
|
||||||
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus"
|
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus"
|
||||||
"HOME=/var/lib/hakurei/u0/a3"
|
"HOME=/var/lib/hakurei/u0/a3"
|
||||||
|
"PIPEWIRE_REMOTE=/run/user/1000/pipewire-0"
|
||||||
"SHELL=/run/current-system/sw/bin/bash"
|
"SHELL=/run/current-system/sw/bin/bash"
|
||||||
"TERM=linux"
|
"TERM=linux"
|
||||||
"USER=u0_a3"
|
"USER=u0_a3"
|
||||||
@@ -56,7 +57,6 @@ in
|
|||||||
"XDG_RUNTIME_DIR=/run/user/1000"
|
"XDG_RUNTIME_DIR=/run/user/1000"
|
||||||
"XDG_SESSION_CLASS=user"
|
"XDG_SESSION_CLASS=user"
|
||||||
"XDG_SESSION_TYPE=wayland"
|
"XDG_SESSION_TYPE=wayland"
|
||||||
"PULSE_SERVER=unix:/run/user/1000/pulse/native"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
fs = fs "dead" {
|
fs = fs "dead" {
|
||||||
@@ -100,7 +100,6 @@ in
|
|||||||
"fstab" = fs "80001ff" null null;
|
"fstab" = fs "80001ff" null null;
|
||||||
"hsurc" = fs "80001ff" null null;
|
"hsurc" = fs "80001ff" null null;
|
||||||
"fuse.conf" = fs "80001ff" null null;
|
"fuse.conf" = fs "80001ff" null null;
|
||||||
"gai.conf" = fs "80001ff" null null;
|
|
||||||
"group" = fs "180" null "hakurei:x:100:\n";
|
"group" = fs "180" null "hakurei:x:100:\n";
|
||||||
"host.conf" = fs "80001ff" null null;
|
"host.conf" = fs "80001ff" null null;
|
||||||
"hostname" = fs "80001ff" null null;
|
"hostname" = fs "80001ff" null null;
|
||||||
@@ -163,8 +162,12 @@ in
|
|||||||
user = fs "800001ed" {
|
user = fs "800001ed" {
|
||||||
"1000" = fs "800001f8" {
|
"1000" = fs "800001f8" {
|
||||||
bus = fs "10001fd" null null;
|
bus = fs "10001fd" null null;
|
||||||
pulse = fs "800001c0" { native = fs "10001ff" null null; } null;
|
pulse = fs "800001c0" {
|
||||||
|
native = fs "10001ff" null null;
|
||||||
|
pid = fs "1a4" null null;
|
||||||
|
} null;
|
||||||
wayland-0 = fs "1000038" null null;
|
wayland-0 = fs "1000038" null null;
|
||||||
|
pipewire-0 = fs "1000038" null null;
|
||||||
} null;
|
} null;
|
||||||
} null;
|
} null;
|
||||||
} null;
|
} null;
|
||||||
@@ -213,6 +216,8 @@ in
|
|||||||
home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
|
home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
|
||||||
nix = fs "800001ed" {
|
nix = fs "800001ed" {
|
||||||
profiles = fs "800001ed" {
|
profiles = fs "800001ed" {
|
||||||
|
home-manager = fs "80001ff" null null;
|
||||||
|
home-manager-1-link = fs "80001ff" null null;
|
||||||
profile = fs "80001ff" null null;
|
profile = fs "80001ff" null null;
|
||||||
profile-1-link = fs "80001ff" null null;
|
profile-1-link = fs "80001ff" null null;
|
||||||
} null;
|
} null;
|
||||||
@@ -247,14 +252,15 @@ in
|
|||||||
(ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
|
(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 "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10003,gid=10003")
|
||||||
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,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/vda" "rw")
|
(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/vda" "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/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 "/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/vda" "rw")
|
(ent ignore "/run/user/1000/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent ignore "/run/user/1000/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent ignore "/run/user/1000/pipewire-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent ignore "/run/user/1000/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "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")
|
||||||
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
||||||
(ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
@@ -262,13 +268,12 @@ in
|
|||||||
(ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
||||||
(ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/" "/.hakurei/.ro-store" "rw,relatime" "overlay" "overlay" "ro,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,redirect_dir=nofollow,userxattr")
|
(ent "/" "/.hakurei/.ro-store" "rw,relatime" "overlay" "overlay" "ro,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,redirect_dir=nofollow,userxattr")
|
||||||
(ent "/" "/.hakurei/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,upperdir=/host/tmp/.hakurei-store-rw/upper,workdir=/host/tmp/.hakurei-store-rw/work,redirect_dir=nofollow,userxattr")
|
(ent "/" "/.hakurei/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,upperdir=/host/tmp/.hakurei-store-rw/upper,workdir=/host/tmp/.hakurei-store-rw/work,redirect_dir=nofollow,userxattr")
|
||||||
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/var/lib/hakurei/u0/a3" "/var/lib/hakurei/u0/a3" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/lib/hakurei/u0/a3" "/var/lib/hakurei/u0/a3" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent ignore "/run/user/1000/pulse/native" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
|
||||||
];
|
];
|
||||||
|
|
||||||
seccomp = true;
|
seccomp = true;
|
||||||
|
|||||||
@@ -61,7 +61,6 @@
|
|||||||
"fstab" = fs "80001ff" null null;
|
"fstab" = fs "80001ff" null null;
|
||||||
"hsurc" = fs "80001ff" null null;
|
"hsurc" = fs "80001ff" null null;
|
||||||
"fuse.conf" = fs "80001ff" null null;
|
"fuse.conf" = fs "80001ff" null null;
|
||||||
"gai.conf" = fs "80001ff" null null;
|
|
||||||
"group" = fs "180" null "hakurei:x:65534:\n";
|
"group" = fs "180" null "hakurei:x:65534:\n";
|
||||||
"host.conf" = fs "80001ff" null null;
|
"host.conf" = fs "80001ff" null null;
|
||||||
"hostname" = fs "80001ff" null null;
|
"hostname" = fs "80001ff" null null;
|
||||||
@@ -140,23 +139,23 @@
|
|||||||
|
|
||||||
mount = [
|
mount = [
|
||||||
(ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10000,gid=10000")
|
(ent "/sysroot" "/" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10000,gid=10000")
|
||||||
(ent "/bin" "/bin" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/bin" "/bin" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/home" "/home" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/home" "/home" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/lib64" "/lib64" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/lib64" "/lib64" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/lost+found" "/lost+found" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/lost+found" "/lost+found" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/nix" "/nix" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/nix" "/nix" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/" "/nix/.ro-store" "rw,nosuid,nodev,relatime" "9p" "nix-store" ignore)
|
(ent "/" "/nix/.ro-store" "rw,nosuid,nodev,relatime" "9p" "nix-store" ignore)
|
||||||
(ent "/" "/nix/.rw-store" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,mode=755")
|
(ent "/" "/nix/.rw-store" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" "rw,mode=755")
|
||||||
(ent "/" "/nix/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
(ent "/" "/nix/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
||||||
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
||||||
(ent "/root" "/root" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/root" "/root" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/" "/run" "rw,nosuid,nodev" "tmpfs" "tmpfs" ignore)
|
(ent "/" "/run" "rw,nosuid,nodev" "tmpfs" "tmpfs" ignore)
|
||||||
(ent "/" "/run/keys" "rw,nosuid,nodev,relatime" "ramfs" "ramfs" "rw,mode=750")
|
(ent "/" "/run/keys" "rw,nosuid,nodev,relatime" "ramfs" "ramfs" "rw,mode=750")
|
||||||
(ent "/" "/run/credentials/systemd-journald.service" "ro,nosuid,nodev,noexec,relatime,nosymfollow" "tmpfs" "tmpfs" "rw,size=1024k,nr_inodes=1024,mode=700,noswap")
|
(ent "/" "/run/credentials/systemd-journald.service" "ro,nosuid,nodev,noexec,relatime,nosymfollow" "tmpfs" "tmpfs" "rw,size=1024k,nr_inodes=1024,mode=700,noswap")
|
||||||
(ent "/" "/run/wrappers" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
|
(ent "/" "/run/wrappers" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
|
||||||
(ent "/" "/run/credentials/getty@tty1.service" "ro,nosuid,nodev,noexec,relatime,nosymfollow" "tmpfs" "tmpfs" "rw,size=1024k,nr_inodes=1024,mode=700,noswap")
|
(ent "/" "/run/credentials/getty@tty1.service" "ro,nosuid,nodev,noexec,relatime,nosymfollow" "tmpfs" "tmpfs" "rw,size=1024k,nr_inodes=1024,mode=700,noswap")
|
||||||
(ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
|
(ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "tmpfs" ignore)
|
||||||
(ent "/srv" "/srv" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/srv" "/srv" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/" "/sys" "rw,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/" "/sys" "rw,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/" "/sys/kernel/security" "rw,nosuid,nodev,noexec,relatime" "securityfs" "securityfs" "rw")
|
(ent "/" "/sys/kernel/security" "rw,nosuid,nodev,noexec,relatime" "securityfs" "securityfs" "rw")
|
||||||
(ent "/../../.." "/sys/fs/cgroup" "rw,nosuid,nodev,noexec,relatime" "cgroup2" "cgroup2" "rw,nsdelegate,memory_recursiveprot")
|
(ent "/../../.." "/sys/fs/cgroup" "rw,nosuid,nodev,noexec,relatime" "cgroup2" "cgroup2" "rw,nsdelegate,memory_recursiveprot")
|
||||||
@@ -167,8 +166,8 @@
|
|||||||
(ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
|
(ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
|
||||||
(ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
|
(ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
|
||||||
(ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
|
(ent "/" ignore "rw,nosuid,nodev,noexec,relatime" ignore ignore "rw")
|
||||||
(ent "/usr" "/usr" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/usr" "/usr" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/var" "/var" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var" "/var" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
|
(ent "/" "/proc" "rw,nosuid,nodev,noexec,relatime" "proc" "proc" "rw")
|
||||||
(ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10000,gid=10000")
|
(ent "/" "/.hakurei" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=4k,mode=755,uid=10000,gid=10000")
|
||||||
(ent "/" "/dev" "ro,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=10000,gid=10000")
|
(ent "/" "/dev" "ro,nosuid,nodev,relatime" "tmpfs" "devtmpfs" "rw,mode=755,uid=10000,gid=10000")
|
||||||
@@ -183,12 +182,12 @@
|
|||||||
(ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
|
(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 "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10000,gid=10000")
|
||||||
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,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/vda" "rw")
|
(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/vda" "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")
|
(ent ignore "/etc/passwd" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10000,gid=10000")
|
||||||
(ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10000,gid=10000")
|
(ent ignore "/etc/group" "ro,nosuid,nodev,relatime" "tmpfs" "rootfs" "rw,uid=10000,gid=10000")
|
||||||
(ent "/kvm" "/dev/kvm" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
(ent "/kvm" "/dev/kvm" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
||||||
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=10000,gid=10000")
|
(ent "/" "/run/user/1000" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=10000,gid=10000")
|
||||||
(ent "/" "/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=10000,gid=10000")
|
(ent "/" "/run/nscd" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=10000,gid=10000")
|
||||||
(ent "/" "/run/dbus" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=10000,gid=10000")
|
(ent "/" "/run/dbus" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=8k,mode=755,uid=10000,gid=10000")
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ in
|
|||||||
env = [
|
env = [
|
||||||
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
|
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
|
||||||
"HOME=/var/lib/hakurei/u0/a5"
|
"HOME=/var/lib/hakurei/u0/a5"
|
||||||
|
"PIPEWIRE_REMOTE=/run/user/65534/pipewire-0"
|
||||||
"SHELL=/run/current-system/sw/bin/bash"
|
"SHELL=/run/current-system/sw/bin/bash"
|
||||||
"TERM=linux"
|
"TERM=linux"
|
||||||
"USER=u0_a5"
|
"USER=u0_a5"
|
||||||
@@ -56,7 +57,6 @@ in
|
|||||||
"XDG_RUNTIME_DIR=/run/user/65534"
|
"XDG_RUNTIME_DIR=/run/user/65534"
|
||||||
"XDG_SESSION_CLASS=user"
|
"XDG_SESSION_CLASS=user"
|
||||||
"XDG_SESSION_TYPE=wayland"
|
"XDG_SESSION_TYPE=wayland"
|
||||||
"PULSE_SERVER=unix:/run/user/65534/pulse/native"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
fs = fs "dead" {
|
fs = fs "dead" {
|
||||||
@@ -98,7 +98,6 @@ in
|
|||||||
"fstab" = fs "80001ff" null null;
|
"fstab" = fs "80001ff" null null;
|
||||||
"hsurc" = fs "80001ff" null null;
|
"hsurc" = fs "80001ff" null null;
|
||||||
"fuse.conf" = fs "80001ff" null null;
|
"fuse.conf" = fs "80001ff" null null;
|
||||||
"gai.conf" = fs "80001ff" null null;
|
|
||||||
"group" = fs "180" null "hakurei:x:65534:\n";
|
"group" = fs "180" null "hakurei:x:65534:\n";
|
||||||
"host.conf" = fs "80001ff" null null;
|
"host.conf" = fs "80001ff" null null;
|
||||||
"hostname" = fs "80001ff" null null;
|
"hostname" = fs "80001ff" null null;
|
||||||
@@ -161,8 +160,12 @@ in
|
|||||||
user = fs "800001ed" {
|
user = fs "800001ed" {
|
||||||
"65534" = fs "800001f8" {
|
"65534" = fs "800001f8" {
|
||||||
bus = fs "10001fd" null null;
|
bus = fs "10001fd" null null;
|
||||||
pulse = fs "800001c0" { native = fs "10001ff" null null; } null;
|
pulse = fs "800001c0" {
|
||||||
|
native = fs "10001ff" null null;
|
||||||
|
pid = fs "1a4" null null;
|
||||||
|
} null;
|
||||||
wayland-0 = fs "1000038" null null;
|
wayland-0 = fs "1000038" null null;
|
||||||
|
pipewire-0 = fs "1000038" null null;
|
||||||
} null;
|
} null;
|
||||||
} null;
|
} null;
|
||||||
} null;
|
} null;
|
||||||
@@ -211,6 +214,8 @@ in
|
|||||||
home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
|
home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
|
||||||
nix = fs "800001ed" {
|
nix = fs "800001ed" {
|
||||||
profiles = fs "800001ed" {
|
profiles = fs "800001ed" {
|
||||||
|
home-manager = fs "80001ff" null null;
|
||||||
|
home-manager-1-link = fs "80001ff" null null;
|
||||||
profile = fs "80001ff" null null;
|
profile = fs "80001ff" null null;
|
||||||
profile-1-link = fs "80001ff" null null;
|
profile-1-link = fs "80001ff" null null;
|
||||||
} null;
|
} null;
|
||||||
@@ -245,14 +250,15 @@ in
|
|||||||
(ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
|
(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 "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10005,gid=10005")
|
||||||
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,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/vda" "rw")
|
(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/vda" "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/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 "/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/vda" "rw")
|
(ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent ignore "/run/user/65534/pipewire-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "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")
|
||||||
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
||||||
(ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
@@ -260,10 +266,9 @@ in
|
|||||||
(ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
||||||
(ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/var/lib/hakurei/u0/a5" "/var/lib/hakurei/u0/a5" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/lib/hakurei/u0/a5" "/var/lib/hakurei/u0/a5" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
|
||||||
];
|
];
|
||||||
|
|
||||||
seccomp = true;
|
seccomp = true;
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ in
|
|||||||
env = [
|
env = [
|
||||||
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
|
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
|
||||||
"HOME=/var/lib/hakurei/u0/a1"
|
"HOME=/var/lib/hakurei/u0/a1"
|
||||||
|
"PIPEWIRE_REMOTE=/run/user/65534/pipewire-0"
|
||||||
"SHELL=/run/current-system/sw/bin/bash"
|
"SHELL=/run/current-system/sw/bin/bash"
|
||||||
"TERM=linux"
|
"TERM=linux"
|
||||||
"USER=u0_a1"
|
"USER=u0_a1"
|
||||||
@@ -56,7 +57,6 @@ in
|
|||||||
"XDG_RUNTIME_DIR=/run/user/65534"
|
"XDG_RUNTIME_DIR=/run/user/65534"
|
||||||
"XDG_SESSION_CLASS=user"
|
"XDG_SESSION_CLASS=user"
|
||||||
"XDG_SESSION_TYPE=wayland"
|
"XDG_SESSION_TYPE=wayland"
|
||||||
"PULSE_SERVER=unix:/run/user/65534/pulse/native"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
fs = fs "dead" {
|
fs = fs "dead" {
|
||||||
@@ -97,7 +97,6 @@ in
|
|||||||
"fstab" = fs "80001ff" null null;
|
"fstab" = fs "80001ff" null null;
|
||||||
"hsurc" = fs "80001ff" null null;
|
"hsurc" = fs "80001ff" null null;
|
||||||
"fuse.conf" = fs "80001ff" null null;
|
"fuse.conf" = fs "80001ff" null null;
|
||||||
"gai.conf" = fs "80001ff" null null;
|
|
||||||
"group" = fs "180" null "hakurei:x:65534:\n";
|
"group" = fs "180" null "hakurei:x:65534:\n";
|
||||||
"host.conf" = fs "80001ff" null null;
|
"host.conf" = fs "80001ff" null null;
|
||||||
"hostname" = fs "80001ff" null null;
|
"hostname" = fs "80001ff" null null;
|
||||||
@@ -160,8 +159,12 @@ in
|
|||||||
user = fs "800001ed" {
|
user = fs "800001ed" {
|
||||||
"65534" = fs "800001c0" {
|
"65534" = fs "800001c0" {
|
||||||
bus = fs "10001fd" null null;
|
bus = fs "10001fd" null null;
|
||||||
pulse = fs "800001c0" { native = fs "10001ff" null null; } null;
|
pulse = fs "800001c0" {
|
||||||
|
native = fs "10001ff" null null;
|
||||||
|
pid = fs "1a4" null null;
|
||||||
|
} null;
|
||||||
wayland-0 = fs "1000038" null null;
|
wayland-0 = fs "1000038" null null;
|
||||||
|
pipewire-0 = fs "1000038" null null;
|
||||||
} null;
|
} null;
|
||||||
} null;
|
} null;
|
||||||
} null;
|
} null;
|
||||||
@@ -210,6 +213,8 @@ in
|
|||||||
home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
|
home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
|
||||||
nix = fs "800001ed" {
|
nix = fs "800001ed" {
|
||||||
profiles = fs "800001ed" {
|
profiles = fs "800001ed" {
|
||||||
|
home-manager = fs "80001ff" null null;
|
||||||
|
home-manager-1-link = fs "80001ff" null null;
|
||||||
profile = fs "80001ff" null null;
|
profile = fs "80001ff" null null;
|
||||||
profile-1-link = fs "80001ff" null null;
|
profile-1-link = fs "80001ff" null null;
|
||||||
} null;
|
} null;
|
||||||
@@ -246,10 +251,11 @@ in
|
|||||||
(ent "/" "/tmp" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,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/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 "/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/vda" "rw")
|
(ent ignore "/run/user/65534/wayland-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent ignore "/run/user/65534/pipewire-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "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")
|
||||||
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
||||||
(ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
@@ -257,10 +263,9 @@ in
|
|||||||
(ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
||||||
(ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/var/lib/hakurei/u0/a1" "/var/lib/hakurei/u0/a1" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/lib/hakurei/u0/a1" "/var/lib/hakurei/u0/a1" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
|
||||||
];
|
];
|
||||||
|
|
||||||
seccomp = true;
|
seccomp = true;
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ in
|
|||||||
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
|
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/65534/bus"
|
||||||
"DISPLAY=:0"
|
"DISPLAY=:0"
|
||||||
"HOME=/var/lib/hakurei/u0/a2"
|
"HOME=/var/lib/hakurei/u0/a2"
|
||||||
|
"PIPEWIRE_REMOTE=/run/user/65534/pipewire-0"
|
||||||
"SHELL=/run/current-system/sw/bin/bash"
|
"SHELL=/run/current-system/sw/bin/bash"
|
||||||
"TERM=linux"
|
"TERM=linux"
|
||||||
"USER=u0_a2"
|
"USER=u0_a2"
|
||||||
@@ -57,7 +58,6 @@ in
|
|||||||
"XDG_RUNTIME_DIR=/run/user/65534"
|
"XDG_RUNTIME_DIR=/run/user/65534"
|
||||||
"XDG_SESSION_CLASS=user"
|
"XDG_SESSION_CLASS=user"
|
||||||
"XDG_SESSION_TYPE=wayland"
|
"XDG_SESSION_TYPE=wayland"
|
||||||
"PULSE_SERVER=unix:/run/user/65534/pulse/native"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
fs = fs "dead" {
|
fs = fs "dead" {
|
||||||
@@ -102,7 +102,6 @@ in
|
|||||||
"fstab" = fs "80001ff" null null;
|
"fstab" = fs "80001ff" null null;
|
||||||
"hsurc" = fs "80001ff" null null;
|
"hsurc" = fs "80001ff" null null;
|
||||||
"fuse.conf" = fs "80001ff" null null;
|
"fuse.conf" = fs "80001ff" null null;
|
||||||
"gai.conf" = fs "80001ff" null null;
|
|
||||||
"group" = fs "180" null "hakurei:x:65534:\n";
|
"group" = fs "180" null "hakurei:x:65534:\n";
|
||||||
"host.conf" = fs "80001ff" null null;
|
"host.conf" = fs "80001ff" null null;
|
||||||
"hostname" = fs "80001ff" null null;
|
"hostname" = fs "80001ff" null null;
|
||||||
@@ -165,8 +164,12 @@ in
|
|||||||
user = fs "800001ed" {
|
user = fs "800001ed" {
|
||||||
"65534" = fs "800001f8" {
|
"65534" = fs "800001f8" {
|
||||||
bus = fs "10001fd" null null;
|
bus = fs "10001fd" null null;
|
||||||
pulse = fs "800001c0" { native = fs "10001ff" null null; } null;
|
pulse = fs "800001c0" {
|
||||||
|
native = fs "10001ff" null null;
|
||||||
|
pid = fs "1a4" null null;
|
||||||
|
} null;
|
||||||
wayland-0 = fs "1000038" null null;
|
wayland-0 = fs "1000038" null null;
|
||||||
|
pipewire-0 = fs "1000038" null null;
|
||||||
} null;
|
} null;
|
||||||
} null;
|
} null;
|
||||||
} null;
|
} null;
|
||||||
@@ -217,6 +220,8 @@ in
|
|||||||
home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
|
home-manager = fs "800001ed" { gcroots = fs "800001ed" { current-home = fs "80001ff" null null; } null; } null;
|
||||||
nix = fs "800001ed" {
|
nix = fs "800001ed" {
|
||||||
profiles = fs "800001ed" {
|
profiles = fs "800001ed" {
|
||||||
|
home-manager = fs "80001ff" null null;
|
||||||
|
home-manager-1-link = fs "80001ff" null null;
|
||||||
profile = fs "80001ff" null null;
|
profile = fs "80001ff" null null;
|
||||||
profile-1-link = fs "80001ff" null null;
|
profile-1-link = fs "80001ff" null null;
|
||||||
} null;
|
} null;
|
||||||
@@ -252,15 +257,16 @@ in
|
|||||||
(ent "/" "/dev/mqueue" "rw,nosuid,nodev,noexec,relatime" "mqueue" "mqueue" "rw")
|
(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 "/" "/dev/shm" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,uid=10002,gid=10002")
|
||||||
(ent "/" "/run/user" "rw,nosuid,nodev,relatime" "tmpfs" "ephemeral" "rw,size=16384k,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/vda" "rw")
|
(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 "/" "/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/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 "/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/vda" "rw")
|
(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/vda" "rw")
|
(ent "/tmp/.X11-unix" "/tmp/.X11-unix" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent ignore "/run/user/65534/pipewire-0" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/bin" "/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent ignore "/run/user/65534/bus" "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/usr/bin" "/usr/bin" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "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")
|
||||||
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
(ent "/" "/nix/store" "ro,nosuid,nodev,relatime" "overlay" "overlay" "rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work,uuid=on")
|
||||||
(ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/block" "/sys/block" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/bus" "/sys/bus" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
@@ -268,13 +274,12 @@ in
|
|||||||
(ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/dev" "/sys/dev" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
(ent "/devices" "/sys/devices" "ro,nosuid,nodev,noexec,relatime" "sysfs" "sysfs" "rw")
|
||||||
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
(ent "/dri" "/dev/dri" "rw,nosuid" "devtmpfs" "devtmpfs" ignore)
|
||||||
(ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/tmp" "/var/tmp" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/cache" "/var/cache" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/" "/.hakurei/.ro-store" "rw,relatime" "overlay" "overlay" "ro,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,redirect_dir=nofollow,userxattr")
|
(ent "/" "/.hakurei/.ro-store" "rw,relatime" "overlay" "overlay" "ro,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,redirect_dir=nofollow,userxattr")
|
||||||
(ent "/" "/.hakurei/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,upperdir=/host/tmp/.hakurei-store-rw/upper,workdir=/host/tmp/.hakurei-store-rw/work,redirect_dir=nofollow,uuid=on,userxattr")
|
(ent "/" "/.hakurei/store" "rw,relatime" "overlay" "overlay" "rw,lowerdir=/host/nix/.ro-store:/host/nix/.rw-store/upper,upperdir=/host/tmp/.hakurei-store-rw/upper,workdir=/host/tmp/.hakurei-store-rw/work,redirect_dir=nofollow,uuid=on,userxattr")
|
||||||
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/etc" ignore "ro,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent "/var/lib/hakurei/u0/a2" "/var/lib/hakurei/u0/a2" "rw,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
(ent "/var/lib/hakurei/u0/a2" "/var/lib/hakurei/u0/a2" "rw,nosuid,nodev,relatime" "ext4" "/dev/disk/by-label/nixos" "rw")
|
||||||
(ent ignore "/run/user/65534/pulse/native" "ro,nosuid,nodev,relatime" "ext4" "/dev/vda" "rw")
|
|
||||||
];
|
];
|
||||||
|
|
||||||
seccomp = true;
|
seccomp = true;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
testProgram = pkgs.callPackage ./tool/package.nix { inherit (config.environment.hakurei.package) version; };
|
testProgram = pkgs.callPackage ./tool/package.nix { inherit (config.environment.hakurei.package) version; };
|
||||||
testCases = import ./case pkgs.stdenv.hostPlatform.system lib testProgram;
|
testCases = import ./case pkgs.system lib testProgram;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
users.users = {
|
users.users = {
|
||||||
@@ -33,7 +33,7 @@ in
|
|||||||
hakurei -v run hakurei-test \
|
hakurei -v run hakurei-test \
|
||||||
-p "/var/tmp/.hakurei-check-ok.0" \
|
-p "/var/tmp/.hakurei-check-ok.0" \
|
||||||
-t ${toString (builtins.toFile "hakurei-pd-want.json" (builtins.toJSON testCases.pd.want))} \
|
-t ${toString (builtins.toFile "hakurei-pd-want.json" (builtins.toJSON testCases.pd.want))} \
|
||||||
-s ${testCases.pd.expectedFilter.${pkgs.stdenv.hostPlatform.system}} "$@"
|
-s ${testCases.pd.expectedFilter.${pkgs.system}} "$@"
|
||||||
'')
|
'')
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
testers,
|
nixosTest,
|
||||||
|
|
||||||
self,
|
self,
|
||||||
withRace ? false,
|
withRace ? false,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
testers.nixosTest {
|
nixosTest {
|
||||||
name = "hakurei-sandbox" + (if withRace then "-race" else "");
|
name = "hakurei-sandbox" + (if withRace then "-race" else "");
|
||||||
nodes.machine =
|
nodes.machine =
|
||||||
{ options, pkgs, ... }:
|
{ options, pkgs, ... }:
|
||||||
|
|||||||
@@ -79,10 +79,8 @@ check_sandbox("device")
|
|||||||
check_sandbox("pdlike")
|
check_sandbox("pdlike")
|
||||||
|
|
||||||
# Exit Sway and verify process exit status 0:
|
# Exit Sway and verify process exit status 0:
|
||||||
machine.wait_until_fails("pgrep -x hakurei", timeout=5)
|
|
||||||
swaymsg("exit", succeed=False)
|
swaymsg("exit", succeed=False)
|
||||||
machine.wait_for_file("/tmp/sway-exit-ok")
|
machine.wait_for_file("/tmp/sway-exit-ok")
|
||||||
|
|
||||||
# Print hakurei runDir contents:
|
# Print hakurei runDir contents:
|
||||||
print(machine.fail("ls /run/user/1000/hakurei"))
|
print(machine.fail("ls /run/user/1000/hakurei"))
|
||||||
machine.succeed("find /tmp -maxdepth 1 -type d -name '.hakurei-shim-*' -print -exec false '{}' +")
|
|
||||||
|
|||||||
12
test/test.py
12
test/test.py
@@ -226,17 +226,15 @@ machine.send_chars("clear; pactl info && touch /var/tmp/pulse-ok\n")
|
|||||||
machine.wait_for_file("/var/tmp/pulse-ok", timeout=15)
|
machine.wait_for_file("/var/tmp/pulse-ok", timeout=15)
|
||||||
collect_state_ui("pulse_wayland")
|
collect_state_ui("pulse_wayland")
|
||||||
check_state("pa-foot", {"wayland": True, "pipewire": True})
|
check_state("pa-foot", {"wayland": True, "pipewire": True})
|
||||||
machine.fail("find /tmp -maxdepth 1 -type d -name '.hakurei-shim-*' -print -exec false '{}' +")
|
# 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.send_chars("exit\n")
|
||||||
machine.wait_until_fails("pgrep foot", timeout=5)
|
machine.wait_until_fails("pgrep foot", timeout=5)
|
||||||
machine.wait_until_fails("pgrep -x hakurei", timeout=5)
|
|
||||||
machine.succeed("find /tmp -maxdepth 1 -type d -name '.hakurei-shim-*' -print -exec false '{}' +")
|
|
||||||
# Test PipeWire SecurityContext:
|
# Test PipeWire SecurityContext:
|
||||||
machine.succeed("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v run --pulse pactl info")
|
machine.succeed("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v run --pulse pactl info")
|
||||||
machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v run --pulse pactl set-sink-mute @DEFAULT_SINK@ toggle")
|
machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v run --pulse pactl set-sink-mute @DEFAULT_SINK@ toggle")
|
||||||
# Test PipeWire direct access:
|
|
||||||
machine.succeed("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 pw-dump")
|
|
||||||
machine.fail("sudo -u alice -i XDG_RUNTIME_DIR=/run/user/1000 hakurei -v run --pipewire pw-dump")
|
|
||||||
|
|
||||||
# Test XWayland (foot does not support X):
|
# Test XWayland (foot does not support X):
|
||||||
swaymsg("exec x11-alacritty")
|
swaymsg("exec x11-alacritty")
|
||||||
@@ -282,7 +280,6 @@ machine.send_key("ctrl-c")
|
|||||||
machine.wait_until_fails("pgrep foot", timeout=5)
|
machine.wait_until_fails("pgrep foot", timeout=5)
|
||||||
|
|
||||||
# Exit Sway and verify process exit status 0:
|
# Exit Sway and verify process exit status 0:
|
||||||
machine.wait_until_fails("pgrep -x hakurei", timeout=5)
|
|
||||||
swaymsg("exit", succeed=False)
|
swaymsg("exit", succeed=False)
|
||||||
machine.wait_for_file("/tmp/sway-exit-ok")
|
machine.wait_for_file("/tmp/sway-exit-ok")
|
||||||
|
|
||||||
@@ -292,7 +289,6 @@ print(machine.succeed("find /tmp/hakurei.0 "
|
|||||||
+ "-path '/tmp/hakurei.0/tmpdir/*/*' -prune -o "
|
+ "-path '/tmp/hakurei.0/tmpdir/*/*' -prune -o "
|
||||||
+ "-print"))
|
+ "-print"))
|
||||||
print(machine.succeed("find /run/user/1000/hakurei"))
|
print(machine.succeed("find /run/user/1000/hakurei"))
|
||||||
machine.succeed("find /tmp -maxdepth 1 -type d -name '.hakurei-shim-*' -print -exec false '{}' +")
|
|
||||||
|
|
||||||
# Verify go test status:
|
# Verify go test status:
|
||||||
machine.wait_for_file("/tmp/hakurei-test-done")
|
machine.wait_for_file("/tmp/hakurei-test-done")
|
||||||
|
|||||||
Reference in New Issue
Block a user