Skip to content

Commit 2288ccb

Browse files
committed
Process.Kill() on Linux now terminates the process group
The kill method now kills also the children processes.
1 parent d7db0c8 commit 2288ccb

File tree

4 files changed

+91
-5
lines changed

4 files changed

+91
-5
lines changed

Diff for: process.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ func NewProcess(extraEnv []string, args ...string) (*Process, error) {
5555
cmd: exec.Command(args[0], args[1:]...),
5656
}
5757
p.cmd.Env = append(os.Environ(), extraEnv...)
58-
p.TellCommandNotToSpawnShell()
58+
tellCommandNotToSpawnShell(p.cmd) // windows specific
59+
tellCommandToStartOnNewProcessGroup(p.cmd) // linux specific
5960

6061
// This is required because some tools detects if the program is running
6162
// from terminal by looking at the stdin/out bindings.
@@ -146,7 +147,7 @@ func (p *Process) Signal(sig os.Signal) error {
146147
// actually exited. This only kills the Process itself, not any other processes it may
147148
// have started.
148149
func (p *Process) Kill() error {
149-
return p.cmd.Process.Kill()
150+
return kill(p.cmd)
150151
}
151152

152153
// SetDir sets the working directory of the command. If Dir is the empty string, Run

Diff for: process_linux.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//
2+
// This file is part of PathsHelper library.
3+
//
4+
// Copyright 2023 Arduino AG (http://www.arduino.cc/)
5+
//
6+
// PathsHelper library is free software; you can redistribute it and/or modify
7+
// it under the terms of the GNU General Public License as published by
8+
// the Free Software Foundation; either version 2 of the License, or
9+
// (at your option) any later version.
10+
//
11+
// This program is distributed in the hope that it will be useful,
12+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
// GNU General Public License for more details.
15+
//
16+
// You should have received a copy of the GNU General Public License
17+
// along with this program; if not, write to the Free Software
18+
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19+
//
20+
// As a special exception, you may use this file as part of a free software
21+
// library without restriction. Specifically, if other files instantiate
22+
// templates or use macros or inline functions from this file, or you compile
23+
// this file and link it with other files to produce an executable, this
24+
// file does not by itself cause the resulting executable to be covered by
25+
// the GNU General Public License. This exception does not however
26+
// invalidate any other reasons why the executable file might be covered by
27+
// the GNU General Public License.
28+
//
29+
30+
//go:build !windows
31+
32+
package paths
33+
34+
import (
35+
"os/exec"
36+
"syscall"
37+
)
38+
39+
func tellCommandNotToSpawnShell(_ *exec.Cmd) {
40+
// no op
41+
}
42+
43+
func tellCommandToStartOnNewProcessGroup(oscmd *exec.Cmd) {
44+
// https://groups.google.com/g/golang-nuts/c/XoQ3RhFBJl8
45+
46+
// Start the process in a new process group.
47+
// This is needed to kill the process and its children
48+
// if we need to kill the process.
49+
if oscmd.SysProcAttr == nil {
50+
oscmd.SysProcAttr = &syscall.SysProcAttr{}
51+
}
52+
oscmd.SysProcAttr.Setpgid = true
53+
}
54+
55+
func kill(oscmd *exec.Cmd) error {
56+
// https://groups.google.com/g/golang-nuts/c/XoQ3RhFBJl8
57+
58+
// Kill the process group
59+
pgid, err := syscall.Getpgid(oscmd.Process.Pid)
60+
if err != nil {
61+
return err
62+
}
63+
return syscall.Kill(-pgid, syscall.SIGKILL)
64+
}

Diff for: process_others.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,22 @@
2727
// the GNU General Public License.
2828
//
2929

30-
//go:build !windows
30+
//go:build !windows && !linux
3131

3232
package paths
3333

34-
import "os/exec"
34+
import (
35+
"os/exec"
36+
)
3537

3638
func tellCommandNotToSpawnShell(_ *exec.Cmd) {
3739
// no op
3840
}
41+
42+
func tellCommandToStartOnNewProcessGroup(_ *exec.Cmd) {
43+
// no op
44+
}
45+
46+
func kill(oscmd *exec.Cmd) error {
47+
return oscmd.Process.Kill()
48+
}

Diff for: process_windows.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,16 @@ import (
3535
)
3636

3737
func tellCommandNotToSpawnShell(oscmd *exec.Cmd) {
38-
oscmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
38+
if oscmd.SysProcAttr == nil {
39+
oscmd.SysProcAttr = &syscall.SysProcAttr{}
40+
}
41+
oscmd.SysProcAttr.HideWindow = true
42+
}
43+
44+
func tellCommandToStartOnNewProcessGroup(_ *exec.Cmd) {
45+
// no op
46+
}
47+
48+
func kill(oscmd *exec.Cmd) error {
49+
return oscmd.Process.Kill()
3950
}

0 commit comments

Comments
 (0)