@@ -4,12 +4,17 @@ package commandconn
44
55import (
66 "context"
7+ "errors"
78 "io"
89 "io/fs"
10+ "os"
11+ "path/filepath"
12+ "runtime"
13+ "strconv"
14+ "syscall"
915 "testing"
1016 "time"
1117
12- "github.com/docker/docker/pkg/process"
1318 "gotest.tools/v3/assert"
1419 is "gotest.tools/v3/assert/cmp"
1520)
@@ -51,16 +56,16 @@ func TestCloseRunningCommand(t *testing.T) {
5156 c , err := New (ctx , "sh" , "-c" , "while true; do sleep 1; done" )
5257 assert .NilError (t , err )
5358 cmdConn := c .(* commandConn )
54- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
59+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
5560
5661 n , err := c .Write ([]byte ("hello" ))
5762 assert .Check (t , is .Equal (len ("hello" ), n ))
5863 assert .NilError (t , err )
59- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
64+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
6065
6166 err = cmdConn .Close ()
6267 assert .NilError (t , err )
63- assert .Check (t , ! process . Alive (cmdConn .cmd .Process .Pid ))
68+ assert .Check (t , ! processAlive (cmdConn .cmd .Process .Pid ))
6469 done <- struct {}{}
6570 }()
6671
@@ -79,7 +84,7 @@ func TestCloseTwice(t *testing.T) {
7984 c , err := New (ctx , "sh" , "-c" , "echo hello; sleep 1; exit 0" )
8085 assert .NilError (t , err )
8186 cmdConn := c .(* commandConn )
82- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
87+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
8388
8489 b := make ([]byte , 32 )
8590 n , err := c .Read (b )
@@ -88,11 +93,11 @@ func TestCloseTwice(t *testing.T) {
8893
8994 err = cmdConn .Close ()
9095 assert .NilError (t , err )
91- assert .Check (t , ! process . Alive (cmdConn .cmd .Process .Pid ))
96+ assert .Check (t , ! processAlive (cmdConn .cmd .Process .Pid ))
9297
9398 err = cmdConn .Close ()
9499 assert .NilError (t , err )
95- assert .Check (t , ! process . Alive (cmdConn .cmd .Process .Pid ))
100+ assert .Check (t , ! processAlive (cmdConn .cmd .Process .Pid ))
96101 done <- struct {}{}
97102 }()
98103
@@ -111,7 +116,7 @@ func TestEOFTimeout(t *testing.T) {
111116 c , err := New (ctx , "sh" , "-c" , "sleep 20" )
112117 assert .NilError (t , err )
113118 cmdConn := c .(* commandConn )
114- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
119+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
115120
116121 cmdConn .stdout = mockStdoutEOF {}
117122
@@ -148,7 +153,7 @@ func TestCloseWhileWriting(t *testing.T) {
148153 c , err := New (ctx , "sh" , "-c" , "while true; do sleep 1; done" )
149154 assert .NilError (t , err )
150155 cmdConn := c .(* commandConn )
151- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
156+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
152157
153158 writeErrC := make (chan error )
154159 go func () {
@@ -164,7 +169,7 @@ func TestCloseWhileWriting(t *testing.T) {
164169
165170 err = c .Close ()
166171 assert .NilError (t , err )
167- assert .Check (t , ! process . Alive (cmdConn .cmd .Process .Pid ))
172+ assert .Check (t , ! processAlive (cmdConn .cmd .Process .Pid ))
168173
169174 writeErr := <- writeErrC
170175 assert .ErrorContains (t , writeErr , "file already closed" )
@@ -176,7 +181,7 @@ func TestCloseWhileReading(t *testing.T) {
176181 c , err := New (ctx , "sh" , "-c" , "while true; do sleep 1; done" )
177182 assert .NilError (t , err )
178183 cmdConn := c .(* commandConn )
179- assert .Check (t , process . Alive (cmdConn .cmd .Process .Pid ))
184+ assert .Check (t , processAlive (cmdConn .cmd .Process .Pid ))
180185
181186 readErrC := make (chan error )
182187 go func () {
@@ -193,8 +198,37 @@ func TestCloseWhileReading(t *testing.T) {
193198
194199 err = cmdConn .Close ()
195200 assert .NilError (t , err )
196- assert .Check (t , ! process . Alive (cmdConn .cmd .Process .Pid ))
201+ assert .Check (t , ! processAlive (cmdConn .cmd .Process .Pid ))
197202
198203 readErr := <- readErrC
199204 assert .Check (t , is .ErrorIs (readErr , fs .ErrClosed ))
200205}
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