Repository navigation
Expand file tree
/
Copy pathnative_process.go
More file actions
92 lines (89 loc) · 2.38 KB
/
Copy pathnative_process.go
File metadata and controls
92 lines (89 loc) · 2.38 KB
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
package acpruntime
import (
"context"
"errors"
"io"
"os/exec"
"sync"
"syscall"
"time"
)
// nativeProcessWait retains the one owned Wait result across cleanup retries.
type nativeProcessWait struct {
mu sync.Mutex
cmd *exec.Cmd
scope NativeProcessScope
once sync.Once
done chan struct{}
err error
stopped bool
custodyErr error
}
func stopNativeProcess(ctx context.Context, cmd *exec.Cmd, stdin io.Closer, wait *nativeProcessWait) error {
wait.mu.Lock()
defer wait.mu.Unlock()
if wait.stopped {
return wait.custodyErr
}
if cmd == nil {
cmd = wait.cmd
}
if stdin != nil {
_ = stdin.Close()
}
if cmd != nil && cmd.Process != nil {
wait.once.Do(func() {
wait.done = make(chan struct{})
go func() { wait.err = cmd.Wait(); close(wait.done) }()
})
}
if wait.scope != nil {
if err := wait.scope.Stop(ctx); err != nil {
return wrapError(ErrorProcess, "native.scope.stop", "provider process domain stop is not confirmed", err)
}
}
if cmd == nil || cmd.Process == nil {
wait.stopped = true
return wait.custodyErr
}
// A supplied scope owns all signalling. Do not target numeric PIDs after
// the host has already stopped its domain; they may have been reused.
pgid := 0
if wait.scope == nil {
pgid = processGroupIDAfterStart(cmd)
}
for _, stage := range []struct {
duration time.Duration
signal syscall.Signal
}{
{1500 * time.Millisecond, syscall.SIGTERM},
{time.Second, syscall.SIGKILL},
{3 * time.Second, 0},
} {
timer := time.NewTimer(stage.duration)
select {
case <-wait.done:
timer.Stop()
if wait.scope == nil {
_ = signalProcessTree(pgid, nil, syscall.SIGTERM)
}
var exit *exec.ExitError
if wait.err != nil && !errors.As(wait.err, &exit) {
return wrapError(ErrorProcess, "native.wait", "provider process Wait failed", wait.err)
}
wait.stopped = true
return wait.custodyErr
case <-ctx.Done():
timer.Stop()
if wait.scope == nil {
_ = signalProcessTree(pgid, cmd.Process, syscall.SIGKILL)
}
return wrapError(ErrorProcess, "native.cleanup", "provider process exit is not confirmed", ctx.Err())
case <-timer.C:
if wait.scope == nil && stage.signal != 0 {
_ = signalProcessTree(pgid, cmd.Process, stage.signal)
}
}
}
return wrapError(ErrorProcess, "native.cleanup", "provider process exit was not confirmed after cleanup", context.DeadlineExceeded)
}