2017-03-10 00:07:35 +00:00
|
|
|
package reaper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"os/exec"
|
|
|
|
"sync"
|
|
|
|
|
2017-04-03 20:14:15 +00:00
|
|
|
"github.com/containerd/containerd/sys"
|
2017-03-10 00:07:35 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Reap should be called when the process receives an SIGCHLD. Reap will reap
|
|
|
|
// all exited processes and close their wait channels
|
2017-03-10 23:28:21 +00:00
|
|
|
func Reap() error {
|
2017-03-29 15:09:08 +00:00
|
|
|
exits, err := sys.Reap(false)
|
2017-03-10 00:07:35 +00:00
|
|
|
for _, e := range exits {
|
2017-03-10 23:28:21 +00:00
|
|
|
Default.Lock()
|
2017-03-10 00:07:35 +00:00
|
|
|
c, ok := Default.cmds[e.Pid]
|
2017-03-10 23:28:21 +00:00
|
|
|
Default.Unlock()
|
2017-03-10 00:07:35 +00:00
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
2017-03-10 23:28:21 +00:00
|
|
|
if c.c != nil {
|
|
|
|
// 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.Lock()
|
2017-03-10 00:07:35 +00:00
|
|
|
delete(Default.cmds, e.Pid)
|
2017-03-10 23:28:21 +00:00
|
|
|
Default.Unlock()
|
2017-03-10 00:07:35 +00:00
|
|
|
}
|
2017-03-10 23:28:21 +00:00
|
|
|
return err
|
2017-03-10 00:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var Default = &Monitor{
|
2017-03-10 23:28:21 +00:00
|
|
|
cmds: make(map[int]*Cmd),
|
2017-03-10 00:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Monitor struct {
|
2017-03-10 23:28:21 +00:00
|
|
|
sync.Mutex
|
|
|
|
cmds map[int]*Cmd
|
2017-03-10 00:07:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2017-03-10 23:28:21 +00:00
|
|
|
rc := &Cmd{
|
2017-03-10 00:07:35 +00:00
|
|
|
c: c,
|
2017-03-10 23:28:21 +00:00
|
|
|
ExitCh: make(chan int, 1),
|
2017-03-10 00:07:35 +00:00
|
|
|
}
|
2017-03-10 23:28:21 +00:00
|
|
|
m.Lock()
|
2017-03-10 00:07:35 +00:00
|
|
|
// start the process
|
|
|
|
if err := rc.c.Start(); err != nil {
|
2017-03-10 23:28:21 +00:00
|
|
|
m.Unlock()
|
2017-03-10 00:07:35 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
m.cmds[rc.c.Process.Pid] = rc
|
2017-03-10 23:28:21 +00:00
|
|
|
m.Unlock()
|
2017-03-10 00:07:35 +00:00
|
|
|
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) {
|
2017-03-10 23:28:21 +00:00
|
|
|
return m.WaitPid(c.Process.Pid)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Monitor) Register(pid int, c *Cmd) {
|
|
|
|
m.Lock()
|
|
|
|
m.cmds[pid] = c
|
|
|
|
m.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Monitor) WaitPid(pid int) (int, error) {
|
|
|
|
m.Lock()
|
|
|
|
rc, ok := m.cmds[pid]
|
|
|
|
m.Unlock()
|
2017-03-10 00:07:35 +00:00
|
|
|
if !ok {
|
|
|
|
return 255, fmt.Errorf("process does not exist")
|
|
|
|
}
|
2017-03-10 23:28:21 +00:00
|
|
|
return <-rc.ExitCh, nil
|
2017-03-10 00:07:35 +00:00
|
|
|
}
|
|
|
|
|
2017-03-10 23:28:21 +00:00
|
|
|
type Cmd struct {
|
2017-03-10 00:07:35 +00:00
|
|
|
c *exec.Cmd
|
2017-03-10 23:28:21 +00:00
|
|
|
ExitCh chan int
|
2017-03-10 00:07:35 +00:00
|
|
|
}
|