30
30
package paths
31
31
32
32
import (
33
+ "fmt"
33
34
"os/exec"
34
35
"syscall"
36
+ "unsafe"
37
+
38
+ "golang.org/x/sys/windows"
35
39
)
36
40
37
41
func tellCommandNotToSpawnShell (oscmd * exec.Cmd ) {
@@ -46,5 +50,54 @@ func tellCommandToStartOnNewProcessGroup(_ *exec.Cmd) {
46
50
}
47
51
48
52
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 )
50
103
}
0 commit comments