59 lines
1.2 KiB
Go
59 lines
1.2 KiB
Go
|
// +build !windows
|
||
|
|
||
|
package internal
|
||
|
|
||
|
import (
|
||
|
"log"
|
||
|
"os/exec"
|
||
|
"syscall"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// KillGrace is the amount of time we allow a process to shutdown before
|
||
|
// sending a SIGKILL.
|
||
|
const KillGrace = 5 * time.Second
|
||
|
|
||
|
// WaitTimeout waits for the given command to finish with a timeout.
|
||
|
// It assumes the command has already been started.
|
||
|
// If the command times out, it attempts to kill the process.
|
||
|
func WaitTimeout(c *exec.Cmd, timeout time.Duration) error {
|
||
|
var kill *time.Timer
|
||
|
term := time.AfterFunc(timeout, func() {
|
||
|
err := c.Process.Signal(syscall.SIGTERM)
|
||
|
if err != nil {
|
||
|
log.Printf("E! [agent] Error terminating process: %s", err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
kill = time.AfterFunc(KillGrace, func() {
|
||
|
err := c.Process.Kill()
|
||
|
if err != nil {
|
||
|
log.Printf("E! [agent] Error killing process: %s", err)
|
||
|
return
|
||
|
}
|
||
|
})
|
||
|
})
|
||
|
|
||
|
err := c.Wait()
|
||
|
|
||
|
// Shutdown all timers
|
||
|
if kill != nil {
|
||
|
kill.Stop()
|
||
|
}
|
||
|
termSent := !term.Stop()
|
||
|
|
||
|
// If the process exited without error treat it as success. This allows a
|
||
|
// process to do a clean shutdown on signal.
|
||
|
if err == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// If SIGTERM was sent then treat any process error as a timeout.
|
||
|
if termSent {
|
||
|
return TimeoutErr
|
||
|
}
|
||
|
|
||
|
// Otherwise there was an error unrelated to termination.
|
||
|
return err
|
||
|
}
|