| 🛟 Updated. 28d5985 k33g 21h ago | 1 | //go:build unix |
| 2 | |
| 3 | package tools |
| 4 | |
| 5 | import ( |
| 6 | "os" |
| 7 | "os/exec" |
| 8 | "syscall" |
| 9 | ) |
| 10 | |
| 11 | // shellProgram is the shell every Unix has. |
| 12 | func shellProgram() string { return "/bin/sh" } |
| 13 | |
| 14 | // shellArgs makes the shell run one command line and exit. |
| 15 | func shellArgs(command string) []string { return []string{"-c", command} } |
| 16 | |
| 17 | // childAttributes puts a command in a process group of its own. |
| 18 | // |
| 19 | // Without it, stopping a command kills only the shell. `go test ./...` spawns a |
| 20 | // test binary per package, `make` spawns compilers: those keep running, and — |
| 21 | // worse — they inherit the output pipe, so the goroutine reading it blocks |
| 22 | // until they finish rather than when the command was stopped. |
| 23 | func childAttributes(*exec.Cmd, string) *syscall.SysProcAttr { |
| 24 | return &syscall.SysProcAttr{Setpgid: true} |
| 25 | } |
| 26 | |
| 27 | // processGroup is the group a Unix command runs in, addressed by its |
| 28 | // leader's pid. |
| 29 | type processGroup struct{ process *os.Process } |
| 30 | |
| 31 | // newGroup returns the group a started command belongs to. |
| 32 | func newGroup(process *os.Process) group { return processGroup{process: process} } |
| 33 | |
| 34 | // kill stops the process and everything it started. |
| 35 | // |
| 36 | // The negative pid is what makes it the group rather than the one process. A |
| 37 | // group that has already gone gives an error nobody can act on, so it is |
| 38 | // dropped: stopping something twice, or stopping something that just ended, is |
| 39 | // not a failure. |
| 40 | func (g processGroup) kill() { |
| 41 | if err := syscall.Kill(-g.process.Pid, syscall.SIGKILL); err != nil { |
| 42 | _ = g.process.Kill() // it may not have got its own group; kill it alone |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | // release has nothing to give back: a process group is not a resource. |
| 47 | func (processGroup) release() {} |