golang.org/x/build@v0.0.0-20240506185731-218518f32b70/cmd/gitmirror/gitmirror_linux.go (about)

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"os"
    11  	"os/exec"
    12  	"syscall"
    13  	"time"
    14  )
    15  
    16  func init() {
    17  	runCmdContext = runCmdContextLinux
    18  }
    19  
    20  // runCmdContextLinux runs cmd controlled by ctx, killing it and all its
    21  // children if necessary. cmd.SysProcAttr must be unset.
    22  func runCmdContextLinux(ctx context.Context, cmd *exec.Cmd) error {
    23  	if cmd.SysProcAttr != nil {
    24  		return fmt.Errorf("cmd.SysProcAttr must be nil")
    25  	}
    26  	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
    27  	if err := cmd.Start(); err != nil {
    28  		return err
    29  	}
    30  	resChan := make(chan error, 1)
    31  	go func() {
    32  		resChan <- cmd.Wait()
    33  	}()
    34  
    35  	select {
    36  	case err := <-resChan:
    37  		return err
    38  	case <-ctx.Done():
    39  	}
    40  	// Canceled. Interrupt and see if it ends voluntarily.
    41  	cmd.Process.Signal(os.Interrupt)
    42  	select {
    43  	case <-resChan:
    44  		return ctx.Err()
    45  	case <-time.After(time.Second):
    46  	}
    47  	// Didn't shut down in response to interrupt. It may have child processes
    48  	// holding stdout/sterr open. Kill its process group hard.
    49  	syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
    50  	<-resChan
    51  	return ctx.Err()
    52  }