All checks were successful
Test / Create distribution (push) Successful in 26s
Test / Hakurei (push) Successful in 45s
Test / Sandbox (push) Successful in 43s
Test / Hakurei (race detector) (push) Successful in 45s
Test / Sandbox (race detector) (push) Successful in 43s
Test / Planterette (push) Successful in 43s
Test / Flake checks (push) Successful in 1m27s
This part of the interface is very unintuitive and only used for testing, even in testing it is inelegant and can be done better. Signed-off-by: Ophestra <cat@gensokyo.uk>
182 lines
4.2 KiB
Go
182 lines
4.2 KiB
Go
package dbus
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"slices"
|
|
"strconv"
|
|
"syscall"
|
|
|
|
"hakurei.app/container"
|
|
"hakurei.app/container/seccomp"
|
|
"hakurei.app/helper"
|
|
"hakurei.app/ldd"
|
|
)
|
|
|
|
// Start starts and configures a D-Bus proxy process.
|
|
func (p *Proxy) Start() error {
|
|
if p.final == nil || p.final.WriterTo == nil {
|
|
return syscall.ENOTRECOVERABLE
|
|
}
|
|
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
p.pmu.Lock()
|
|
defer p.pmu.Unlock()
|
|
|
|
if p.cancel != nil || p.cause != nil {
|
|
return errors.New("dbus: already started")
|
|
}
|
|
|
|
ctx, cancel := context.WithCancelCause(p.ctx)
|
|
|
|
if !p.useSandbox {
|
|
p.helper = helper.NewDirect(ctx, p.name, p.final, true, argF, func(cmd *exec.Cmd) {
|
|
if p.output != nil {
|
|
cmd.Stdout, cmd.Stderr = p.output, p.output
|
|
}
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
|
cmd.Env = make([]string, 0)
|
|
}, nil)
|
|
} else {
|
|
toolPath := p.name
|
|
if filepath.Base(p.name) == p.name {
|
|
if s, err := exec.LookPath(p.name); err != nil {
|
|
return err
|
|
} else {
|
|
toolPath = s
|
|
}
|
|
}
|
|
|
|
var libPaths []string
|
|
if entries, err := ldd.Exec(ctx, toolPath); err != nil {
|
|
return err
|
|
} else {
|
|
libPaths = ldd.Path(entries)
|
|
}
|
|
|
|
p.helper = helper.New(
|
|
ctx, toolPath,
|
|
p.final, true,
|
|
argF, func(z *container.Container) {
|
|
z.SeccompFlags |= seccomp.AllowMultiarch
|
|
z.SeccompPresets |= seccomp.PresetStrict
|
|
z.Hostname = "hakurei-dbus"
|
|
if p.output != nil {
|
|
z.Stdout, z.Stderr = p.output, p.output
|
|
}
|
|
|
|
// these lib paths are unpredictable, so mount them first so they cannot cover anything
|
|
for _, name := range libPaths {
|
|
z.Bind(name, name, 0)
|
|
}
|
|
|
|
// upstream bus directories
|
|
upstreamPaths := make([]string, 0, 2)
|
|
for _, addr := range [][]AddrEntry{p.final.SessionUpstream, p.final.SystemUpstream} {
|
|
for _, ent := range addr {
|
|
if ent.Method != "unix" {
|
|
continue
|
|
}
|
|
for _, pair := range ent.Values {
|
|
if pair[0] != "path" || !path.IsAbs(pair[1]) {
|
|
continue
|
|
}
|
|
upstreamPaths = append(upstreamPaths, path.Dir(pair[1]))
|
|
}
|
|
}
|
|
}
|
|
slices.Sort(upstreamPaths)
|
|
upstreamPaths = slices.Compact(upstreamPaths)
|
|
for _, name := range upstreamPaths {
|
|
z.Bind(name, name, 0)
|
|
}
|
|
|
|
// parent directories of bind paths
|
|
sockDirPaths := make([]string, 0, 2)
|
|
if d := path.Dir(p.final.Session[1]); path.IsAbs(d) {
|
|
sockDirPaths = append(sockDirPaths, d)
|
|
}
|
|
if d := path.Dir(p.final.System[1]); path.IsAbs(d) {
|
|
sockDirPaths = append(sockDirPaths, d)
|
|
}
|
|
slices.Sort(sockDirPaths)
|
|
sockDirPaths = slices.Compact(sockDirPaths)
|
|
for _, name := range sockDirPaths {
|
|
z.Bind(name, name, container.BindWritable)
|
|
}
|
|
|
|
// xdg-dbus-proxy bin path
|
|
binPath := path.Dir(toolPath)
|
|
z.Bind(binPath, binPath, 0)
|
|
}, nil)
|
|
}
|
|
|
|
if err := p.helper.Start(); err != nil {
|
|
cancel(err)
|
|
p.helper = nil
|
|
return err
|
|
}
|
|
|
|
p.cancel, p.cause = cancel, func() error { return context.Cause(ctx) }
|
|
return nil
|
|
}
|
|
|
|
var proxyClosed = errors.New("proxy closed")
|
|
|
|
// Wait blocks until xdg-dbus-proxy exits and releases resources.
|
|
func (p *Proxy) Wait() error {
|
|
p.mu.RLock()
|
|
defer p.mu.RUnlock()
|
|
|
|
p.pmu.RLock()
|
|
if p.helper == nil || p.cancel == nil || p.cause == nil {
|
|
p.pmu.RUnlock()
|
|
return errors.New("dbus: not started")
|
|
}
|
|
|
|
errs := make([]error, 3)
|
|
|
|
errs[0] = p.helper.Wait()
|
|
if errors.Is(errs[0], context.Canceled) &&
|
|
errors.Is(p.cause(), proxyClosed) {
|
|
errs[0] = nil
|
|
}
|
|
p.pmu.RUnlock()
|
|
|
|
// ensure socket removal so ephemeral directory is empty at revert
|
|
if err := os.Remove(p.final.Session[1]); err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
errs[1] = err
|
|
}
|
|
if p.final.System[1] != "" {
|
|
if err := os.Remove(p.final.System[1]); err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
errs[2] = err
|
|
}
|
|
}
|
|
|
|
return errors.Join(errs...)
|
|
}
|
|
|
|
// Close cancels the context passed to the helper instance attached to xdg-dbus-proxy.
|
|
func (p *Proxy) Close() {
|
|
p.pmu.Lock()
|
|
defer p.pmu.Unlock()
|
|
|
|
if p.cancel == nil {
|
|
panic("dbus: not started")
|
|
}
|
|
p.cancel(proxyClosed)
|
|
}
|
|
|
|
func argF(argsFd, statFd int) []string {
|
|
if statFd == -1 {
|
|
return []string{"--args=" + strconv.Itoa(argsFd)}
|
|
} else {
|
|
return []string{"--args=" + strconv.Itoa(argsFd), "--fd=" + strconv.Itoa(statFd)}
|
|
}
|
|
}
|