All checks were successful
Test / Create distribution (push) Successful in 29s
Test / Sandbox (race detector) (push) Successful in 42s
Test / Sandbox (push) Successful in 43s
Test / Hakurei (push) Successful in 47s
Test / Hakurei (race detector) (push) Successful in 46s
Test / Hpkg (push) Successful in 43s
Test / Flake checks (push) Successful in 1m32s
Tests for this Op happens to be the best out of everything due to the robust infrastructure offered by internal/pipewire. This is now ready to use in internal/outcome for implementing #26. Signed-off-by: Ophestra <cat@gensokyo.uk>
100 lines
2.8 KiB
Go
100 lines
2.8 KiB
Go
package system
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"hakurei.app/container/check"
|
|
"hakurei.app/hst"
|
|
"hakurei.app/internal/acl"
|
|
"hakurei.app/internal/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 is pathname only and is destroyed on revert.
|
|
func (sys *I) PipeWire(dst *check.Absolute) *I {
|
|
sys.ops = append(sys.ops, &pipewireOp{nil, dst})
|
|
return sys
|
|
}
|
|
|
|
// pipewireOp implements [I.PipeWire].
|
|
type pipewireOp struct {
|
|
scc io.Closer
|
|
dst *check.Absolute
|
|
}
|
|
|
|
func (p *pipewireOp) Type() hst.Enablement { return Process }
|
|
|
|
func (p *pipewireOp) apply(sys *I) (err error) {
|
|
var ctx *pipewire.Context
|
|
if ctx, err = sys.pipewireConnect(); err != nil {
|
|
return newOpError("pipewire", err, false)
|
|
}
|
|
defer func() {
|
|
if closeErr := ctx.Close(); closeErr != nil && err == nil {
|
|
err = newOpError("pipewire", closeErr, false)
|
|
}
|
|
}()
|
|
|
|
sys.msg.Verbosef("pipewire pathname socket on %q", p.dst)
|
|
|
|
var registry *pipewire.Registry
|
|
if registry, err = ctx.GetRegistry(); err != nil {
|
|
return newOpError("pipewire", err, false)
|
|
} else if err = ctx.GetCore().Sync(); err != nil {
|
|
return newOpError("pipewire", err, false)
|
|
}
|
|
|
|
var securityContext *pipewire.SecurityContext
|
|
if securityContext, err = registry.GetSecurityContext(); err != nil {
|
|
return newOpError("pipewire", err, false)
|
|
} else if err = ctx.Roundtrip(); err != nil {
|
|
return newOpError("pipewire", err, false)
|
|
}
|
|
|
|
if p.scc, err = securityContext.BindAndCreate(p.dst.String(), pipewire.SPADict{
|
|
{Key: pipewire.PW_KEY_SEC_ENGINE, Value: "app.hakurei"},
|
|
{Key: pipewire.PW_KEY_ACCESS, Value: "restricted"},
|
|
}); err != nil {
|
|
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 closeErr := p.scc.Close(); closeErr != nil {
|
|
return newOpError("pipewire", errors.Join(err, closeErr), false)
|
|
}
|
|
return newOpError("pipewire", err, false)
|
|
}
|
|
|
|
if err = sys.aclUpdate(p.dst.String(), sys.uid, acl.Read, acl.Write, acl.Execute); err != nil {
|
|
if closeErr := p.scc.Close(); closeErr != nil {
|
|
return newOpError("pipewire", errors.Join(err, closeErr), false)
|
|
}
|
|
return newOpError("pipewire", err, false)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *pipewireOp) revert(sys *I, _ *Criteria) error {
|
|
if p.scc != nil {
|
|
sys.msg.Verbosef("hanging up pipewire socket on %q", p.dst)
|
|
return newOpError("pipewire", p.scc.Close(), true)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *pipewireOp) Is(o Op) bool {
|
|
target, ok := o.(*pipewireOp)
|
|
return ok && p != nil && target != nil &&
|
|
p.dst.Is(target.dst)
|
|
}
|
|
|
|
func (p *pipewireOp) Path() string { return p.dst.String() }
|
|
func (p *pipewireOp) String() string { return fmt.Sprintf("pipewire socket at %q", p.dst) }
|