Source file
src/os/pidfd_linux_test.go
1
2
3
4
5 package os_test
6
7 import (
8 "errors"
9 "internal/syscall/unix"
10 "internal/testenv"
11 "os"
12 "os/exec"
13 "syscall"
14 "testing"
15 "time"
16 )
17
18 func TestFindProcessViaPidfd(t *testing.T) {
19 testenv.MustHaveGoBuild(t)
20 t.Parallel()
21
22 if err := os.CheckPidfdOnce(); err != nil {
23
24 t.Skipf("skipping: pidfd not available: %v", err)
25 }
26
27 p, err := os.StartProcess(testenv.GoToolPath(t), []string{"go"}, &os.ProcAttr{})
28 if err != nil {
29 t.Fatalf("starting test process: %v", err)
30 }
31 p.Wait()
32
33
34 proc, err := os.FindProcess(p.Pid)
35
36 if err != nil {
37 t.Fatalf("FindProcess: got error %v, want <nil>", err)
38 }
39
40 if proc == nil {
41 t.Fatal("FindProcess: got nil, want non-nil")
42 }
43 if proc.Status() != os.StatusDone {
44 t.Fatalf("got process status: %v, want %d", proc.Status(), os.StatusDone)
45 }
46
47
48
49 if err := proc.Kill(); err != os.ErrProcessDone {
50 t.Errorf("Kill: got %v, want %v", err, os.ErrProcessDone)
51 }
52 if err := proc.Signal(os.Kill); err != os.ErrProcessDone {
53 t.Errorf("Signal: got %v, want %v", err, os.ErrProcessDone)
54 }
55 if _, err := proc.Wait(); !errors.Is(err, syscall.ECHILD) {
56 t.Errorf("Wait: got %v, want %v", err, os.ErrProcessDone)
57 }
58
59 if err := proc.Release(); err != nil {
60 t.Fatalf("Release: got %v, want <nil>", err)
61 }
62 }
63
64 func TestStartProcessWithPidfd(t *testing.T) {
65 testenv.MustHaveGoBuild(t)
66 t.Parallel()
67
68 if err := os.CheckPidfdOnce(); err != nil {
69
70 t.Skipf("skipping: pidfd not available: %v", err)
71 }
72
73 var pidfd int
74 p, err := os.StartProcess(testenv.GoToolPath(t), []string{"go"}, &os.ProcAttr{
75 Sys: &syscall.SysProcAttr{
76 PidFD: &pidfd,
77 },
78 })
79 if err != nil {
80 t.Fatalf("starting test process: %v", err)
81 }
82 defer syscall.Close(pidfd)
83
84 if _, err := p.Wait(); err != nil {
85 t.Fatalf("Wait: got %v, want <nil>", err)
86 }
87
88
89 err = unix.PidFDSendSignal(uintptr(pidfd), syscall.Signal(0))
90 if !errors.Is(err, syscall.ESRCH) {
91 t.Errorf("SendSignal: got %v, want %v", err, syscall.ESRCH)
92 }
93 }
94
95
96 func TestPidfdLeak(t *testing.T) {
97 exe := testenv.Executable(t)
98
99
100
101
102 const count = 10
103 want := make([]int, count)
104 for i := range count {
105 var err error
106 want[i], err = syscall.Open(exe, syscall.O_RDONLY, 0)
107 if err != nil {
108 t.Fatal(err)
109 }
110 }
111
112
113 for _, d := range want {
114 syscall.Close(d)
115 }
116
117
118 for range 10 {
119
120
121
122 cmd := exec.Command("/noSuchExecutable")
123 cmd.Run()
124 }
125
126
127 got := make([]int, count)
128 for i := range count {
129 var err error
130 got[i], err = syscall.Open(exe, syscall.O_RDONLY, 0)
131 if err != nil {
132 t.Fatal(err)
133 }
134 }
135
136
137 for _, d := range got {
138 syscall.Close(d)
139 }
140
141 t.Logf("got %v", got)
142 t.Logf("want %v", want)
143
144
145 if got[count-1] > want[count-1]+5 {
146 t.Errorf("got descriptor %d, want %d", got[count-1], want[count-1])
147 }
148 }
149
150 func TestProcessWithHandleLinux(t *testing.T) {
151 t.Parallel()
152 havePidfd := os.CheckPidfdOnce() == nil
153
154 const envVar = "OSTEST_PROCESS_WITH_HANDLE"
155 if os.Getenv(envVar) != "" {
156 time.Sleep(1 * time.Minute)
157 return
158 }
159
160 cmd := testenv.CommandContext(t, t.Context(), testenv.Executable(t), "-test.run=^"+t.Name()+"$")
161 cmd = testenv.CleanCmdEnv(cmd)
162 cmd.Env = append(cmd.Env, envVar+"=1")
163 if err := cmd.Start(); err != nil {
164 t.Fatal(err)
165 }
166 defer func() {
167 cmd.Process.Kill()
168 cmd.Wait()
169 }()
170
171 const sig = syscall.SIGINT
172 called := false
173 err := cmd.Process.WithHandle(func(pidfd uintptr) {
174 called = true
175
176 err := unix.PidFDSendSignal(pidfd, sig)
177 if err != nil {
178 t.Errorf("PidFDSendSignal: got error %v, want nil", err)
179 }
180 })
181
182 if !havePidfd && err == nil {
183 t.Fatal("WithHandle: got nil, want error")
184 }
185
186 if havePidfd && err != nil {
187 t.Fatalf("WithHandle: got error %v, want nil", err)
188 }
189
190 if havePidfd != called {
191 t.Fatalf("WithHandle: havePidfd is %v, but called is %v", havePidfd, called)
192 }
193
194 if called {
195 err := cmd.Wait()
196 if err == nil {
197 t.Fatal("Wait: want error, got nil")
198 }
199 st := cmd.ProcessState.Sys().(syscall.WaitStatus)
200 if !st.Signaled() {
201 t.Fatal("ProcessState: want Signaled, got", err)
202 }
203 if gotSig := st.Signal(); sig != gotSig {
204 t.Fatalf("ProcessState.Signal: want %v, got %v", sig, gotSig)
205 }
206
207 called = false
208 err = cmd.Process.WithHandle(func(_ uintptr) {
209 called = true
210 })
211 if err != os.ErrProcessDone {
212 t.Fatalf("WithHandle: want os.ErrProcessDone, got %v", err)
213 }
214 if called {
215 t.Fatal("called: want false, got true")
216 }
217 }
218 }
219
View as plain text