Skip to content

Commit 4391f08

Browse files
committed
Windows: kill the whole process-tree when terminating a process
1 parent 5d076fd commit 4391f08

File tree

3 files changed

+63
-3
lines changed

3 files changed

+63
-3
lines changed

go.mod

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
module github.com/arduino/go-paths-helper
22

3-
go 1.21
3+
go 1.23.0
44

5-
require github.com/stretchr/testify v1.8.4
5+
toolchain go1.23.2
6+
7+
require (
8+
github.com/stretchr/testify v1.8.4
9+
golang.org/x/sys v0.32.0
10+
)
611

712
require (
813
github.com/davecgh/go-spew v1.1.1 // indirect

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
44
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
55
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
66
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
7+
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
8+
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
79
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
810
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
911
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

process_windows.go

+54-1
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@
3030
package paths
3131

3232
import (
33+
"fmt"
3334
"os/exec"
3435
"syscall"
36+
"unsafe"
37+
38+
"golang.org/x/sys/windows"
3539
)
3640

3741
func tellCommandNotToSpawnShell(oscmd *exec.Cmd) {
@@ -46,5 +50,54 @@ func tellCommandToStartOnNewProcessGroup(_ *exec.Cmd) {
4650
}
4751

4852
func kill(oscmd *exec.Cmd) error {
49-
return oscmd.Process.Kill()
53+
parentProcessMap, err := createParentProcessSnapshot()
54+
if err != nil {
55+
return err
56+
}
57+
return killPidTree(uint32(oscmd.Process.Pid), parentProcessMap)
58+
}
59+
60+
// createParentProcessSnapshot returns a map that correlate a process
61+
// with its parent process: childPid -> parentPid
62+
func createParentProcessSnapshot() (map[uint32]uint32, error) {
63+
// Inspired by: https://stackoverflow.com/a/36089871/1655275
64+
65+
// Make a snapshot of the current running processes
66+
snapshot, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
67+
if err != nil {
68+
return nil, fmt.Errorf("getting running processes snapshot: %w", err)
69+
}
70+
defer windows.CloseHandle(snapshot)
71+
72+
// Iterate the result and extract the parent-child relationship
73+
processParentMap := map[uint32]uint32{}
74+
var processEntry windows.ProcessEntry32
75+
processEntry.Size = uint32(unsafe.Sizeof(processEntry))
76+
hasData := (windows.Process32First(snapshot, &processEntry) == nil)
77+
for hasData {
78+
processParentMap[processEntry.ProcessID] = processEntry.ParentProcessID
79+
hasData = (windows.Process32Next(snapshot, &processEntry) == nil)
80+
}
81+
return processParentMap, nil
82+
}
83+
84+
func killPidTree(pid uint32, parentProcessMap map[uint32]uint32) error {
85+
for childPid, parentPid := range parentProcessMap {
86+
if parentPid == pid {
87+
// Descend process tree
88+
if err := killPidTree(childPid, parentProcessMap); err != nil {
89+
return fmt.Errorf("error killing child process: %w", err)
90+
}
91+
}
92+
}
93+
return killPid(pid)
94+
}
95+
96+
func killPid(pid uint32) error {
97+
process, err := windows.OpenProcess(windows.PROCESS_ALL_ACCESS, false, pid)
98+
if err != nil {
99+
return fmt.Errorf("opening process for kill: %w", err)
100+
}
101+
defer windows.CloseHandle(process)
102+
return windows.TerminateProcess(process, 128)
50103
}

0 commit comments

Comments
 (0)