diff options
| author | Ophestra Umiker <cat@ophivana.moe> | 2024-09-09 03:11:50 +0900 |
|---|---|---|
| committer | Ophestra Umiker <cat@ophivana.moe> | 2024-09-09 03:11:50 +0900 |
| commit | 357cc4ce4d2888033a8163fe39861c8a88301da6 (patch) | |
| tree | a6dbdd09fc5ef2dea82ad1c85627b6455c2f3477 /dbus/run.go | |
| parent | 3242ce340621839d03ecf34bb3ed20d7c6f3cbb3 (diff) | |
dbus: implement xdg-dbus-proxy wrapper
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
Diffstat (limited to 'dbus/run.go')
| -rw-r--r-- | dbus/run.go | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/dbus/run.go b/dbus/run.go new file mode 100644 index 00000000..315b31de --- /dev/null +++ b/dbus/run.go @@ -0,0 +1,134 @@ +package dbus + +import ( + "errors" + "os" + "os/exec" +) + +// Start launches the D-Bus proxy and sets up the Wait method. +// ready should be buffered and should only be received from once. +func (p *Proxy) Start(ready *chan bool) error { + p.lock.Lock() + defer p.lock.Unlock() + + if p.seal == nil { + return errors.New("proxy not sealed") + } + + // acquire pipes + if pr, pw, err := os.Pipe(); err != nil { + return err + } else { + p.statP[0], p.statP[1] = pr, pw + } + if pr, pw, err := os.Pipe(); err != nil { + return err + } else { + p.argsP[0], p.argsP[1] = pr, pw + } + + p.cmd = exec.Command(p.path, + // ExtraFiles: If non-nil, entry i becomes file descriptor 3+i. + "--fd=3", + "--args=4", + ) + p.cmd.Env = []string{} + p.cmd.ExtraFiles = []*os.File{p.statP[1], p.argsP[0]} + p.cmd.Stdout = os.Stdout + p.cmd.Stderr = os.Stderr + if err := p.cmd.Start(); err != nil { + return err + } + + statsP, argsP := p.statP[0], p.argsP[1] + + if _, err := argsP.Write([]byte(*p.seal)); err != nil { + if err1 := p.cmd.Process.Kill(); err1 != nil { + panic(err1) + } + return err + } else { + if err = argsP.Close(); err != nil { + if err1 := p.cmd.Process.Kill(); err1 != nil { + panic(err1) + } + return err + } + } + + wait := make(chan error) + go func() { + // live out the lifespan of the process + wait <- p.cmd.Wait() + }() + + read := make(chan error) + go func() { + n, err := statsP.Read(make([]byte, 1)) + switch n { + case -1: + if err1 := p.cmd.Process.Kill(); err1 != nil { + panic(err1) + } + read <- err + case 0: + read <- err + case 1: + *ready <- true + read <- nil + default: + panic("unreachable") // unexpected read count + } + }() + + p.wait = &wait + p.read = &read + p.ready = ready + + return nil +} + +// Wait waits for xdg-dbus-proxy to exit or fault. +func (p *Proxy) Wait() error { + p.lock.RLock() + defer p.lock.RUnlock() + + if p.wait == nil || p.read == nil { + return errors.New("proxy not running") + } + + defer func() { + if err1 := p.statP[0].Close(); err1 != nil && !errors.Is(err1, os.ErrClosed) { + panic(err1) + } + if err1 := p.statP[1].Close(); err1 != nil && !errors.Is(err1, os.ErrClosed) { + panic(err1) + } + + if err1 := p.argsP[0].Close(); err1 != nil && !errors.Is(err1, os.ErrClosed) { + panic(err1) + } + if err1 := p.argsP[1].Close(); err1 != nil && !errors.Is(err1, os.ErrClosed) { + panic(err1) + } + + }() + + select { + case err := <-*p.wait: + *p.ready <- false + return err + case err := <-*p.read: + if err != nil { + *p.ready <- false + return err + } + return <-*p.wait + } +} + +// Close closes the status file descriptor passed to xdg-dbus-proxy, causing it to stop. +func (p *Proxy) Close() error { + return p.statP[0].Close() +} |
