-
Notifications
You must be signed in to change notification settings - Fork 0
/
reaper.go
110 lines (100 loc) · 2.56 KB
/
reaper.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package pico
import (
"fmt"
"github.com/tuxdude/zzzlogi"
"golang.org/x/sys/unix"
)
// reapedProc stores information about a single reaped process.
type reapedProc struct {
// pid of the reaped process.
pid int
// Wait status obtained from the wait system call while reaping
// the process.
waitStatus unix.WaitStatus
}
// String returns the string representation of the reaped process information.
func (r *reapedProc) String() string {
return fmt.Sprintf("{pid: %d waitStatus: %v}", r.pid, r.waitStatus)
}
type zombieReaper struct {
// Logger used by the zombie reaper.
log zzzlogi.Logger
}
func newZombieReaper(log zzzlogi.Logger) *zombieReaper {
return &zombieReaper{
log: log,
}
}
// reap reaps zombie child processes if any by performing one or more
// non-blocking wait system calls, and returns once there are no further
// zombie child processes left.
func (z *zombieReaper) reap() []*reapedProc {
var result []*reapedProc
for {
var wstatus unix.WaitStatus
var pid int
var err error
err = unix.EINTR
for err == unix.EINTR {
pid, err = unix.Wait4(-1, &wstatus, unix.WNOHANG, nil)
}
proc := z.parseWait4Result(pid, err, wstatus)
if proc == nil {
break
}
result = append(result, proc)
}
return result
}
// logProcExitStatus logs the specified reaped process's exit status.
func (z *zombieReaper) logProcExitStatus(pid int, wstatus unix.WaitStatus) {
exitStatus := wstatus.ExitStatus()
if !wstatus.Exited() {
if wstatus.Signaled() {
z.log.Tracef(
"Reaped zombie pid: %d was terminated by signal: %s, wstatus: %v!",
pid,
sigInfo(wstatus.Signal()),
wstatus,
)
} else {
z.log.Tracef("Reaped zombie pid: %d did not exit gracefully, wstatus: %v!", pid, wstatus)
}
} else {
if exitStatus != 0 {
z.log.Tracef(
"Reaped zombie pid: %d, exited with failed exit status: %d, wstatus: %v",
pid,
exitStatus,
wstatus,
)
} else {
z.log.Tracef(
"Reaped zombie pid: %d, exited with successful exit status: %d, wstatus: %v",
pid,
exitStatus,
wstatus,
)
}
}
}
// parseWait4Result parses the wait status information to build the reaped
// process information.
func (z *zombieReaper) parseWait4Result(pid int, err error, wstatus unix.WaitStatus) *reapedProc {
if err == unix.ECHILD {
// No more children, nothing further to do here.
return nil
}
if err != nil {
z.log.Errorf("Zombie Reaper - Got an unexpected error during wait: %v", err)
return nil
}
if pid <= 0 {
return nil
}
z.logProcExitStatus(pid, wstatus)
return &reapedProc{
pid: pid,
waitStatus: wstatus,
}
}