containerd/reaper/reaper.go

102 lines
1.9 KiB
Go

package reaper
import (
"bytes"
"fmt"
"os/exec"
"sync"
"github.com/docker/containerd/utils"
)
// Reap should be called when the process receives an SIGCHLD. Reap will reap
// all exited processes and close their wait channels
func Reap() ([]utils.Exit, error) {
exits, err := utils.Reap(false)
for _, e := range exits {
Default.mu.Lock()
c, ok := Default.cmds[e.Pid]
Default.mu.Unlock()
if !ok {
continue
}
// after we get an exit, call wait on the go process to make sure all
// pipes are closed and finalizers are run on the process
c.c.Wait()
c.exitCh <- e.Status
Default.mu.Lock()
delete(Default.cmds, e.Pid)
Default.mu.Unlock()
}
return exits, err
}
var Default = &Monitor{
cmds: make(map[int]*cmd),
}
type Monitor struct {
mu sync.Mutex
cmds map[int]*cmd
}
func (m *Monitor) Output(c *exec.Cmd) ([]byte, error) {
var b bytes.Buffer
c.Stdout = &b
if err := m.Run(c); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func (m *Monitor) CombinedOutput(c *exec.Cmd) ([]byte, error) {
var b bytes.Buffer
c.Stdout = &b
c.Stderr = &b
if err := m.Run(c); err != nil {
return nil, err
}
return b.Bytes(), nil
}
// Start starts the command a registers the process with the reaper
func (m *Monitor) Start(c *exec.Cmd) error {
rc := &cmd{
c: c,
exitCh: make(chan int, 1),
}
m.mu.Lock()
// start the process
if err := rc.c.Start(); err != nil {
m.mu.Unlock()
return err
}
m.cmds[rc.c.Process.Pid] = rc
m.mu.Unlock()
return nil
}
// Run runs and waits for the command to finish
func (m *Monitor) Run(c *exec.Cmd) error {
if err := m.Start(c); err != nil {
return err
}
_, err := m.Wait(c)
return err
}
func (m *Monitor) Wait(c *exec.Cmd) (int, error) {
m.mu.Lock()
rc, ok := m.cmds[c.Process.Pid]
m.mu.Unlock()
if !ok {
return 255, fmt.Errorf("process does not exist")
}
return <-rc.exitCh, nil
}
type cmd struct {
c *exec.Cmd
exitCh chan int
}