3030package paths
3131
3232import (
33+ "fmt"
3334 "os/exec"
3435 "syscall"
36+ "unsafe"
37+
38+ "golang.org/x/sys/windows"
3539)
3640
3741func tellCommandNotToSpawnShell (oscmd * exec.Cmd ) {
@@ -46,5 +50,54 @@ func tellCommandToStartOnNewProcessGroup(_ *exec.Cmd) {
4650}
4751
4852func 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