Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 08cf9b4

Browse filesBrowse files
authored
Merge pull request #34 from arduino/kill_process_darwin
`Kill` now kills all the childrens in the process-tree on MacOS and Windows.
2 parents 73c658f + 8d1e1d5 commit 08cf9b4
Copy full SHA for 08cf9b4

File tree

Expand file treeCollapse file tree

6 files changed

+129
-5
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+129
-5
lines changed

‎go.mod

Copy file name to clipboardExpand all lines: go.mod
+7-2Lines changed: 7 additions & 2 deletions
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

Copy file name to clipboardExpand all lines: go.sum
+2Lines changed: 2 additions & 0 deletions
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.go

Copy file name to clipboardExpand all lines: process.go
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func NewProcess(extraEnv []string, args ...string) (*Process, error) {
5656
}
5757
p.cmd.Env = append(os.Environ(), extraEnv...)
5858
tellCommandNotToSpawnShell(p.cmd) // windows specific
59-
tellCommandToStartOnNewProcessGroup(p.cmd) // linux specific
59+
tellCommandToStartOnNewProcessGroup(p.cmd) // linux and macosx specific
6060

6161
// This is required because some tools detects if the program is running
6262
// from terminal by looking at the stdin/out bindings.
@@ -67,6 +67,8 @@ func NewProcess(extraEnv []string, args ...string) (*Process, error) {
6767

6868
// TellCommandNotToSpawnShell avoids that the specified Cmd display a small
6969
// command prompt while runnning on Windows. It has no effects on other OS.
70+
//
71+
// Deprecated: TellCommandNotToSpawnShell is now always applied by default, there is no need to call it anymore.
7072
func (p *Process) TellCommandNotToSpawnShell() {
7173
tellCommandNotToSpawnShell(p.cmd)
7274
}

‎process_darwin.go

Copy file name to clipboard
+62Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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+
package paths
31+
32+
import (
33+
"os/exec"
34+
"syscall"
35+
)
36+
37+
func tellCommandNotToSpawnShell(_ *exec.Cmd) {
38+
// no op
39+
}
40+
41+
func tellCommandToStartOnNewProcessGroup(oscmd *exec.Cmd) {
42+
// https://groups.google.com/g/golang-nuts/c/XoQ3RhFBJl8
43+
44+
// Start the process in a new process group.
45+
// This is needed to kill the process and its children
46+
// if we need to kill the process.
47+
if oscmd.SysProcAttr == nil {
48+
oscmd.SysProcAttr = &syscall.SysProcAttr{}
49+
}
50+
oscmd.SysProcAttr.Setpgid = true
51+
}
52+
53+
func kill(oscmd *exec.Cmd) error {
54+
// https://groups.google.com/g/golang-nuts/c/XoQ3RhFBJl8
55+
56+
// Kill the process group
57+
pgid, err := syscall.Getpgid(oscmd.Process.Pid)
58+
if err != nil {
59+
return err
60+
}
61+
return syscall.Kill(-pgid, syscall.SIGKILL)
62+
}

‎process_others.go

Copy file name to clipboardExpand all lines: process_others.go
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
// the GNU General Public License.
2828
//
2929

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

3232
package paths
3333

‎process_windows.go

Copy file name to clipboardExpand all lines: process_windows.go
+54-1Lines changed: 54 additions & 1 deletion
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.