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,41 @@ func tellCommandToStartOnNewProcessGroup(_ *exec.Cmd) {
46
50
}
47
51
48
52
func kill (oscmd * exec.Cmd ) error {
49
- return oscmd .Process .Kill ()
53
+ return killProcessTree (uint32 (oscmd .Process .Pid ))
54
+ }
55
+
56
+ func killProcessTree (pid uint32 ) error {
57
+ // Inspired by: https://stackoverflow.com/a/36089871/1655275
58
+
59
+ // Make a snapshot of the current running processes
60
+ snapshot , err := windows .CreateToolhelp32Snapshot (windows .TH32CS_SNAPPROCESS , 0 )
61
+ if err != nil {
62
+ return fmt .Errorf ("getting running processes snapshot: %w" , err )
63
+ }
64
+
65
+ // Iterate and filter child processes of the current process
66
+ var processEntry windows.ProcessEntry32
67
+ processEntry .Size = uint32 (unsafe .Sizeof (processEntry ))
68
+ if windows .Process32First (snapshot , & processEntry ) == nil {
69
+ for {
70
+ if processEntry .ParentProcessID == pid {
71
+ // Recurse on the childrens
72
+ if err := killProcessTree (processEntry .ProcessID ); err != nil {
73
+ return fmt .Errorf ("error killing child process: %w" , err )
74
+ }
75
+ }
76
+
77
+ if windows .Process32Next (snapshot , & processEntry ) != nil {
78
+ break
79
+ }
80
+ }
81
+ }
82
+
83
+ // And finally, kill the parent
84
+ process , err := windows .OpenProcess (windows .PROCESS_ALL_ACCESS , false , pid )
85
+ if err != nil {
86
+ return fmt .Errorf ("opening process for kill: %w" , err )
87
+ }
88
+ defer windows .CloseHandle (process )
89
+ return windows .TerminateProcess (process , 128 )
50
90
}
0 commit comments