/
watchdog.go
128 lines (97 loc) · 2.55 KB
/
watchdog.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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package common
import (
"bytes"
"fmt"
"os"
"os/exec"
"syscall"
"time"
)
// https://medium.com/@vCabbage/go-timeout-commands-with-os-exec-commandcontext-ba0c861ed738
type ErrWatchdog struct {
Msg string
}
func (e *ErrWatchdog) Error() string {
return e.Msg
}
func NewWatchdogCmd(cmd *exec.Cmd, timeout time.Duration) ([]byte, error) {
DebugFunc("%s: %d msec...", CmdToString(cmd), timeout.Milliseconds())
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = &buf
doneCh := make(chan error, 1)
start := time.Now()
go func() {
defer UnregisterGoRoutine(RegisterGoRoutine(2))
err := cmd.Start()
if Error(err) {
doneCh <- err
return
}
Debug("Watchdog process started pid: %d timeout: %v cmd: %s ...", cmd.Process.Pid, timeout, CmdToString(cmd))
err = cmd.Wait()
if Error(err) {
doneCh <- err
}
close(doneCh)
}()
select {
case <-time.After(timeout):
Debug("Watchdog process will be killed pid: %d timeout: %v cmd: %s time: %v", cmd.Process.Pid, timeout, CmdToString(cmd), time.Since(start))
Error(cmd.Process.Kill())
return nil, &ErrWatchdog{Msg: fmt.Sprintf("killed process pid: %d cmd: %s after: %v", cmd.Process.Pid, CmdToString(cmd), time.Since(start))}
case err := <-doneCh:
exitcode := 0
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
exitcode = exitError.ExitCode()
} else {
exitcode = -1
}
}
exitstate := ""
var output []byte
switch exitcode {
case 0:
exitstate = "successfull"
output = buf.Bytes()
default:
exitstate = "failed"
}
Debug("Watchdog process %s! pid: %d exitcode: %d timeout: %v cmd: %s time: %s", exitstate, cmd.Process.Pid, exitcode, timeout, CmdToString(cmd), time.Since(start))
Debug("%s", string(output))
return output, err
}
}
func NewWatchdogFunc(msg string, fn func() error, timeout time.Duration) error {
DebugFunc("%s: %d msec...", msg, timeout.Milliseconds())
doneCh := make(chan error, 1)
start := time.Now()
var err error
go func() {
defer UnregisterGoRoutine(RegisterGoRoutine(2))
doneCh <- fn()
}()
select {
case <-time.After(timeout):
Debug("Watchdog function killed! time: %v", time.Since(start))
return &ErrWatchdog{Msg: msg}
case err = <-doneCh:
exitstate := ""
if err != nil {
exitstate = "failed"
} else {
exitstate = "successfull"
}
Debug("Watchdog function %s! time: %s", exitstate, time.Since(start))
return err
}
}
func StillAlive(pid int) bool {
process, err := os.FindProcess(pid)
if err != nil {
return false
} else {
return process.Signal(syscall.Signal(0)) != nil
}
}