blob: dccdddd593d88565beeea988e8409bd6ea2cee2a [file] [log] [blame]
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
func init() {
runCmdContext = runCmdContextLinux
// runCmdContextLinux 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.
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)
return ctx.Err()