forked from influxdata/telegraf
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Send TERM to exec processes before sending KILL signal (influxdata#6302)
- Loading branch information
1 parent
33e9e37
commit b97fe1b
Showing
4 changed files
with
129 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package internal | ||
|
||
import ( | ||
"bytes" | ||
"os/exec" | ||
"time" | ||
) | ||
|
||
// CombinedOutputTimeout runs the given command with the given timeout and | ||
// returns the combined output of stdout and stderr. | ||
// If the command times out, it attempts to kill the process. | ||
func CombinedOutputTimeout(c *exec.Cmd, timeout time.Duration) ([]byte, error) { | ||
var b bytes.Buffer | ||
c.Stdout = &b | ||
c.Stderr = &b | ||
if err := c.Start(); err != nil { | ||
return nil, err | ||
} | ||
err := WaitTimeout(c, timeout) | ||
return b.Bytes(), err | ||
} | ||
|
||
// RunTimeout runs the given command with the given timeout. | ||
// If the command times out, it attempts to kill the process. | ||
func RunTimeout(c *exec.Cmd, timeout time.Duration) error { | ||
if err := c.Start(); err != nil { | ||
return err | ||
} | ||
return WaitTimeout(c, timeout) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// +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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// +build windows | ||
|
||
package internal | ||
|
||
import ( | ||
"log" | ||
"os/exec" | ||
"time" | ||
) | ||
|
||
// 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 { | ||
timer := time.AfterFunc(timeout, func() { | ||
err := c.Process.Kill() | ||
if err != nil { | ||
log.Printf("E! [agent] Error killing process: %s", err) | ||
return | ||
} | ||
}) | ||
|
||
err := c.Wait() | ||
|
||
// Shutdown all timers | ||
termSent := !timer.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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters