fortify/dbus/run.go

135 lines
2.6 KiB
Go

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 := p.seal.WriteTo(argsP); 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()
}