cmd/gitmirror: kill child processes upon timeout
We've seen evidence that we're not terminating Git subprocesses on
timeout. This is probably an instance of https://golang.org/issue/23019,
which we can deal with by killing all the children rather than just the
one we started.
Updates golang/go#38887.
Change-Id: Ie7999122f9c063f04de1a107a57fd307e7a5c1d2
Reviewed-on: https://go-review.googlesource.com/c/build/+/347294
Trust: Heschi Kreinick <heschi@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/cmd/gitmirror/gitmirror.go b/cmd/gitmirror/gitmirror.go
index f68e8a6..f664aab 100644
--- a/cmd/gitmirror/gitmirror.go
+++ b/cmd/gitmirror/gitmirror.go
@@ -723,8 +723,12 @@
}
}
-// runCommandContext runs cmd controlled by ctx.
-func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
+// runCmdContext allows OS-specific overrides of process execution behavior.
+// See runCmdContextLinux.
+var runCmdContext = runCmdContextDefault
+
+// runCommandContextDefault runs cmd controlled by ctx.
+func runCmdContextDefault(ctx context.Context, cmd *exec.Cmd) error {
if err := cmd.Start(); err != nil {
return err
}
diff --git a/cmd/gitmirror/gitmirror_linux.go b/cmd/gitmirror/gitmirror_linux.go
new file mode 100644
index 0000000..960443b
--- /dev/null
+++ b/cmd/gitmirror/gitmirror_linux.go
@@ -0,0 +1,48 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "os/exec"
+ "syscall"
+ "time"
+)
+
+func init() {
+ runCmdContext = runCmdContextLinux
+}
+
+// runCommandContext runs cmd controlled by ctx, killing it and all its
+// children if necessary. cmd.SysProcAttr must be unset.
+func runCmdContextLinux(ctx context.Context, cmd *exec.Cmd) error {
+ if cmd.SysProcAttr != nil {
+ return fmt.Errorf("cmd.SysProcAttr must be nil")
+ }
+ cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
+ if err := cmd.Start(); err != nil {
+ return err
+ }
+ resChan := make(chan error, 1)
+ go func() {
+ resChan <- cmd.Wait()
+ }()
+
+ select {
+ case err := <-resChan:
+ return err
+ case <-ctx.Done():
+ }
+ // Canceled. Interrupt and see if it ends voluntarily.
+ cmd.Process.Signal(os.Interrupt)
+ select {
+ case <-resChan:
+ return ctx.Err()
+ case <-time.After(time.Second):
+ }
+ // Didn't shut down in response to interrupt. It may have child processes
+ // holding stdout/sterr open. Kill its process group hard.
+ syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
+ <-resChan
+ return ctx.Err()
+}