95 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			95 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // +build !windows
 | |
| 
 | |
| package reaper
 | |
| 
 | |
| import (
 | |
| 	"os/exec"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/containerd/containerd/sys"
 | |
| 	runc "github.com/containerd/go-runc"
 | |
| 	"github.com/pkg/errors"
 | |
| )
 | |
| 
 | |
| // ErrNoSuchProcess is returned when the process no longer exists
 | |
| var ErrNoSuchProcess = errors.New("no such process")
 | |
| 
 | |
| const bufferSize = 2048
 | |
| 
 | |
| // Reap should be called when the process receives an SIGCHLD.  Reap will reap
 | |
| // all exited processes and close their wait channels
 | |
| func Reap() error {
 | |
| 	now := time.Now()
 | |
| 	exits, err := sys.Reap(false)
 | |
| 	Default.Lock()
 | |
| 	for c := range Default.subscribers {
 | |
| 		for _, e := range exits {
 | |
| 			c <- runc.Exit{
 | |
| 				Timestamp: now,
 | |
| 				Pid:       e.Pid,
 | |
| 				Status:    e.Status,
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 	Default.Unlock()
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // Default is the default monitor initialized for the package
 | |
| var Default = &Monitor{
 | |
| 	subscribers: make(map[chan runc.Exit]struct{}),
 | |
| }
 | |
| 
 | |
| // Monitor monitors the underlying system for process status changes
 | |
| type Monitor struct {
 | |
| 	sync.Mutex
 | |
| 
 | |
| 	subscribers map[chan runc.Exit]struct{}
 | |
| }
 | |
| 
 | |
| // Start starts the command a registers the process with the reaper
 | |
| func (m *Monitor) Start(c *exec.Cmd) (chan runc.Exit, error) {
 | |
| 	ec := m.Subscribe()
 | |
| 	if err := c.Start(); err != nil {
 | |
| 		m.Unsubscribe(ec)
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return ec, nil
 | |
| }
 | |
| 
 | |
| // Wait blocks until a process is signal as dead.
 | |
| // User should rely on the value of the exit status to determine if the
 | |
| // command was successful or not.
 | |
| func (m *Monitor) Wait(c *exec.Cmd, ec chan runc.Exit) (int, error) {
 | |
| 	for e := range ec {
 | |
| 		if e.Pid == c.Process.Pid {
 | |
| 			// make sure we flush all IO
 | |
| 			c.Wait()
 | |
| 			m.Unsubscribe(ec)
 | |
| 			return e.Status, nil
 | |
| 		}
 | |
| 	}
 | |
| 	// return no such process if the ec channel is closed and no more exit
 | |
| 	// events will be sent
 | |
| 	return -1, ErrNoSuchProcess
 | |
| }
 | |
| 
 | |
| // Subscribe to process exit changes
 | |
| func (m *Monitor) Subscribe() chan runc.Exit {
 | |
| 	c := make(chan runc.Exit, bufferSize)
 | |
| 	m.Lock()
 | |
| 	m.subscribers[c] = struct{}{}
 | |
| 	m.Unlock()
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| // Unsubscribe to process exit changes
 | |
| func (m *Monitor) Unsubscribe(c chan runc.Exit) {
 | |
| 	m.Lock()
 | |
| 	delete(m.subscribers, c)
 | |
| 	close(c)
 | |
| 	m.Unlock()
 | |
| }
 | 
