diff --git a/pkg/paths/process.go b/pkg/paths/process.go index 4c869286..ebfe7134 100644 --- a/pkg/paths/process.go +++ b/pkg/paths/process.go @@ -55,7 +55,8 @@ func NewProcess(extraEnv []string, args ...string) (*Process, error) { cmd: exec.Command(args[0], args[1:]...), } p.cmd.Env = append(os.Environ(), extraEnv...) - p.TellCommandNotToSpawnShell() + tellCommandNotToSpawnShell(p.cmd) // windows specific + tellCommandToStartOnNewProcessGroup(p.cmd) // linux specific // This is required because some tools detects if the program is running // from terminal by looking at the stdin/out bindings. @@ -146,7 +147,7 @@ func (p *Process) Signal(sig os.Signal) error { // actually exited. This only kills the Process itself, not any other processes it may // have started. func (p *Process) Kill() error { - return p.cmd.Process.Kill() + return kill(p.cmd) } // SetDir sets the working directory of the command. If Dir is the empty string, Run diff --git a/pkg/paths/process_others.go b/pkg/paths/process_linux.go similarity index 69% rename from pkg/paths/process_others.go rename to pkg/paths/process_linux.go index 39bd3e16..5735a85c 100644 --- a/pkg/paths/process_others.go +++ b/pkg/paths/process_linux.go @@ -31,8 +31,34 @@ package paths -import "os/exec" +import ( + "os/exec" + "syscall" +) func tellCommandNotToSpawnShell(_ *exec.Cmd) { // no op } + +func tellCommandToStartOnNewProcessGroup(oscmd *exec.Cmd) { + // https://groups.google.com/g/golang-nuts/c/XoQ3RhFBJl8 + + // Start the process in a new process group. + // This is needed to kill the process and its children + // if we need to kill the process. + if oscmd.SysProcAttr == nil { + oscmd.SysProcAttr = &syscall.SysProcAttr{} + } + oscmd.SysProcAttr.Setpgid = true +} + +func kill(oscmd *exec.Cmd) error { + // https://groups.google.com/g/golang-nuts/c/XoQ3RhFBJl8 + + // Kill the process group + pgid, err := syscall.Getpgid(oscmd.Process.Pid) + if err != nil { + return err + } + return syscall.Kill(-pgid, syscall.SIGKILL) +}