package service import ( "io/ioutil" "strconv" "time" "github.com/mitchellh/go-ps" "github.com/pkg/errors" ) // checkForPID checks path for pid. func checkForPID(path string) (int, error) { exists, err := pathExists(path) if err != nil { return -1, err } if !exists { return -1, nil } data, err := ioutil.ReadFile(path) // #nosec if err != nil { return -1, err } n, err := strconv.Atoi(string(data)) if err != nil { return -1, err } if n < 0 { return -1, errors.Errorf("negative pid") } return n, nil } var checkNoop = func() error { return nil } // waitForPID waits for PID at path and service status. func waitForPID(pidPath string, checkFn func() error, delay time.Duration, wait time.Duration) (int, error) { ticker := time.NewTicker(delay) defer ticker.Stop() type result struct { pid int err error } resultChan := make(chan result, 1) go func() { for { <-ticker.C pid, perr := checkForPID(pidPath) if perr != nil { resultChan <- result{pid: -1, err: perr} return } if checkFn != nil { if err := checkFn(); err != nil { resultChan <- result{pid: -1, err: err} return } } // If PID not found, continue if pid == -1 { continue } resultChan <- result{pid: pid} return } }() select { case res := <-resultChan: return res.pid, res.err case <-time.After(wait): return -1, errors.Errorf("timed out waiting for pid") } } // waitForProcessExit waits for PID to exit. func waitForProcessExit(pid int, delay time.Duration, wait time.Duration) error { ticker := time.NewTicker(delay) defer ticker.Stop() type result struct { err error } resultChan := make(chan result, 1) go func() { for { <-ticker.C process, findErr := ps.FindProcess(pid) if findErr != nil { resultChan <- result{err: findErr} return } if process == nil { resultChan <- result{} return } } }() select { case res := <-resultChan: return res.err case <-time.After(wait): return errors.Errorf("timed out waiting for pid to exit") } }