-
Notifications
You must be signed in to change notification settings - Fork 0
/
process_tree.go
78 lines (69 loc) · 2.07 KB
/
process_tree.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package hellocap
import (
"errors"
"fmt"
"os"
"github.com/mitchellh/go-ps"
)
type errNoSuchProcess int
func (e errNoSuchProcess) Error() string {
return fmt.Sprintf("could not find process with PID '%d'", e)
}
type process struct {
ps.Process
osHandle *os.Process
children []process
}
// processTree returns a tree of processes and their children, rooted at the input process. If
// procSnapshot is nil, a new snapshot will be taken.
func processTree(pid int, procSnapshot []ps.Process) (root *process, err error) {
if procSnapshot == nil {
procSnapshot, err = ps.Processes()
if err != nil {
return nil, fmt.Errorf("failed to obtain snapshot of current processes: %w", err)
}
}
root, err = ptHelper(pid, procSnapshot)
if _, ok := err.(errNoSuchProcess); ok {
return nil, errors.New("could not find input process")
}
return
}
func ptHelper(rootPID int, allProcessesSnapshot []ps.Process) (*process, error) {
rootP, err := ps.FindProcess(rootPID)
if err != nil {
return nil, fmt.Errorf("failed to look up process by PID %d", rootPID)
}
if rootP == nil {
return nil, errNoSuchProcess(rootPID)
}
rootHandle, err := os.FindProcess(rootPID)
if err != nil {
return nil, fmt.Errorf("failed to obtain process handle: %w", err)
}
root := process{rootP, rootHandle, []process{}}
for _, p := range allProcessesSnapshot {
if p.PPid() == rootPID {
childTree, err := ptHelper(p.Pid(), allProcessesSnapshot)
if _, ok := err.(errNoSuchProcess); ok {
// The child may have died since the snapshot was taken, just exclude it.
continue
} else if err != nil {
return nil, fmt.Errorf(
"failed to look up child tree started by %s: %w", rootP.Executable(), err)
}
root.children = append(root.children, *childTree)
}
}
return &root, nil
}
// kill the process and all of its descendents via postorder traversal.
func (p *process) kill() error {
for _, child := range p.children {
if err := child.kill(); err != nil {
return fmt.Errorf(
"failed to kill process tree started by %s: %w", child.Executable(), err)
}
}
return p.osHandle.Kill()
}