@@ -4,12 +4,17 @@ package commandconn
4
4
5
5
import (
6
6
"context"
7
+ "errors"
7
8
"io"
8
9
"io/fs"
10
+ "os"
11
+ "path/filepath"
12
+ "runtime"
13
+ "strconv"
14
+ "syscall"
9
15
"testing"
10
16
"time"
11
17
12
- "github.com/docker/docker/pkg/process"
13
18
"gotest.tools/v3/assert"
14
19
is "gotest.tools/v3/assert/cmp"
15
20
)
@@ -51,16 +56,16 @@ func TestCloseRunningCommand(t *testing.T) {
51
56
c , err := New (ctx , "sh" , "-c" , "while true; do sleep 1; done" )
52
57
assert .NilError (t , err )
53
58
cmdConn := c .(* commandConn )
54
- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
59
+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
55
60
56
61
n , err := c .Write ([]byte ("hello" ))
57
62
assert .Check (t , is .Equal (len ("hello" ), n ))
58
63
assert .NilError (t , err )
59
- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
64
+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
60
65
61
66
err = cmdConn .Close ()
62
67
assert .NilError (t , err )
63
- assert .Check (t , ! process . Alive (cmdConn .cmd .Process .Pid ))
68
+ assert .Check (t , ! processAlive (cmdConn .cmd .Process .Pid ))
64
69
done <- struct {}{}
65
70
}()
66
71
@@ -79,7 +84,7 @@ func TestCloseTwice(t *testing.T) {
79
84
c , err := New (ctx , "sh" , "-c" , "echo hello; sleep 1; exit 0" )
80
85
assert .NilError (t , err )
81
86
cmdConn := c .(* commandConn )
82
- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
87
+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
83
88
84
89
b := make ([]byte , 32 )
85
90
n , err := c .Read (b )
@@ -88,11 +93,11 @@ func TestCloseTwice(t *testing.T) {
88
93
89
94
err = cmdConn .Close ()
90
95
assert .NilError (t , err )
91
- assert .Check (t , ! process . Alive (cmdConn .cmd .Process .Pid ))
96
+ assert .Check (t , ! processAlive (cmdConn .cmd .Process .Pid ))
92
97
93
98
err = cmdConn .Close ()
94
99
assert .NilError (t , err )
95
- assert .Check (t , ! process . Alive (cmdConn .cmd .Process .Pid ))
100
+ assert .Check (t , ! processAlive (cmdConn .cmd .Process .Pid ))
96
101
done <- struct {}{}
97
102
}()
98
103
@@ -111,7 +116,7 @@ func TestEOFTimeout(t *testing.T) {
111
116
c , err := New (ctx , "sh" , "-c" , "sleep 20" )
112
117
assert .NilError (t , err )
113
118
cmdConn := c .(* commandConn )
114
- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
119
+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
115
120
116
121
cmdConn .stdout = mockStdoutEOF {}
117
122
@@ -148,7 +153,7 @@ func TestCloseWhileWriting(t *testing.T) {
148
153
c , err := New (ctx , "sh" , "-c" , "while true; do sleep 1; done" )
149
154
assert .NilError (t , err )
150
155
cmdConn := c .(* commandConn )
151
- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
156
+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
152
157
153
158
writeErrC := make (chan error )
154
159
go func () {
@@ -164,7 +169,7 @@ func TestCloseWhileWriting(t *testing.T) {
164
169
165
170
err = c .Close ()
166
171
assert .NilError (t , err )
167
- assert .Check (t , ! process . Alive (cmdConn .cmd .Process .Pid ))
172
+ assert .Check (t , ! processAlive (cmdConn .cmd .Process .Pid ))
168
173
169
174
writeErr := <- writeErrC
170
175
assert .ErrorContains (t , writeErr , "file already closed" )
@@ -176,7 +181,7 @@ func TestCloseWhileReading(t *testing.T) {
176
181
c , err := New (ctx , "sh" , "-c" , "while true; do sleep 1; done" )
177
182
assert .NilError (t , err )
178
183
cmdConn := c .(* commandConn )
179
- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
184
+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
180
185
181
186
readErrC := make (chan error )
182
187
go func () {
@@ -193,8 +198,37 @@ func TestCloseWhileReading(t *testing.T) {
193
198
194
199
err = cmdConn .Close ()
195
200
assert .NilError (t , err )
196
- assert .Check (t , ! process . Alive (cmdConn .cmd .Process .Pid ))
201
+ assert .Check (t , ! processAlive (cmdConn .cmd .Process .Pid ))
197
202
198
203
readErr := <- readErrC
199
204
assert .Check (t , is .ErrorIs (readErr , fs .ErrClosed ))
200
205
}
206
+
207
+ // processAlive returns true if a process with a given pid is running. It only considers
208
+ // positive PIDs; 0 (all processes in the current process group), -1 (all processes
209
+ // with a PID larger than 1), and negative (-n, all processes in process group
210
+ // "n") values for pid are never considered to be alive.
211
+ //
212
+ // It was forked from https://github.com/moby/moby/blob/v28.3.3/pkg/process/process_unix.go#L17-L42
213
+ func processAlive (pid int ) bool {
214
+ if pid < 1 {
215
+ return false
216
+ }
217
+ switch runtime .GOOS {
218
+ case "darwin" :
219
+ // OS X does not have a proc filesystem. Use kill -0 pid to judge if the
220
+ // process exists. From KILL(2): https://www.freebsd.org/cgi/man.cgi?query=kill&sektion=2&manpath=OpenDarwin+7.2.1
221
+ //
222
+ // Sig may be one of the signals specified in sigaction(2) or it may
223
+ // be 0, in which case error checking is performed but no signal is
224
+ // actually sent. This can be used to check the validity of pid.
225
+ err := syscall .Kill (pid , 0 )
226
+
227
+ // Either the PID was found (no error), or we get an EPERM, which means
228
+ // the PID exists, but we don't have permissions to signal it.
229
+ return err == nil || errors .Is (err , syscall .EPERM )
230
+ default :
231
+ _ , err := os .Stat (filepath .Join ("/proc" , strconv .Itoa (pid )))
232
+ return err == nil
233
+ }
234
+ }
0 commit comments