Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/fifo/fifo_open.c
39534 views
1
/*-
2
* Copyright (c) 2005 Robert N. M. Watson
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/types.h>
28
#include <sys/stat.h>
29
#include <sys/wait.h>
30
31
#include <err.h>
32
#include <errno.h>
33
#include <fcntl.h>
34
#include <limits.h>
35
#include <signal.h>
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <unistd.h>
40
41
/*
42
* Regression test to exercise various POSIX-defined parts of fifo behavior
43
* described for open(2):
44
*
45
* O_NONBLOCK
46
* When opening a FIFO with O_RDONLY or O_WRONLY set:
47
*
48
* - If O_NONBLOCK is set, an open() for reading-only shall return without
49
* delay. An open() for writing-only shall return an error if no process
50
* currently has the file open for reading.
51
*
52
* - If O_NONBLOCK is clear, an open() for reading-only shall block the
53
* calling thread until a thread opens the file for writing. An open()
54
* for writing-only shall block the calling thread until a thread opens
55
* the file for reading.
56
*
57
* When opening a block special or character special file that supports
58
* non-blocking opens:
59
*
60
* - If O_NONBLOCK is set, the open() function shall return without blocking
61
* for the device to be ready or available. Subsequent behavior of the
62
* device is device-specific.
63
*
64
* - If O_NONBLOCK is clear, the open() function shall block the calling
65
* thread until the device is ready or available before returning.
66
*
67
* Special errors:
68
*
69
* [ENXIO]
70
* O_NONBLOCK is set, the named file is a FIFO, O_WRONLY is set, and no
71
* process has the file open for reading.
72
*/
73
74
/*
75
* In order to test blocking/non-blocking behavior, test processes must
76
* potentially block themselves until released. As bugs in blocking result
77
* in processes that won't un-block, we must sacrifice a process to the task,
78
* watching and potentially killing it after a time-out. The main test
79
* process is never used to open or act directly on a fifo (other than to
80
* create or unlink it) in order to avoid the main test process being
81
* blocked.
82
*/
83
84
/*
85
* All activity occurs within a temporary directory created early in the
86
* test.
87
*/
88
static char temp_dir[PATH_MAX];
89
90
static void __unused
91
atexit_temp_dir(void)
92
{
93
94
rmdir(temp_dir);
95
}
96
97
/*
98
* Run a function in a particular test process.
99
*/
100
static int
101
run_in_process(int (*func)(void), pid_t *pidp, const char *errstr)
102
{
103
pid_t pid;
104
105
pid = fork();
106
if (pid < 0) {
107
warn("%s: run_in_process: fork", errstr);
108
return (-1);
109
}
110
111
if (pid == 0)
112
exit(func());
113
114
if (pidp != NULL)
115
*pidp = pid;
116
117
return (0);
118
}
119
120
/*
121
* Wait for a process on a timeout, and if the timeout expires, kill the
122
* process. Test each second rather than waiting the full timeout at once to
123
* minimize the amount of time spent hanging around unnecessarily.
124
*/
125
static int
126
wait_and_timeout(pid_t pid, int timeout, int *status, const char *errstr)
127
{
128
pid_t wpid;
129
int i;
130
131
/*
132
* Count up to the timeout, but do a non-hanging waitpid() after each
133
* second so we can avoid waiting a lot of extra time.
134
*/
135
for (i = 0; i < timeout; i++) {
136
wpid = waitpid(pid, status, WNOHANG);
137
if (wpid < 0) {
138
warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
139
return (-1);
140
}
141
142
if (wpid == pid)
143
return (0);
144
145
sleep(1);
146
}
147
148
wpid = waitpid(pid, status, WNOHANG);
149
if (wpid < 0) {
150
warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
151
return (-1);
152
}
153
154
if (wpid == pid)
155
return (0);
156
157
if (kill(pid, SIGTERM) < 0) {
158
warn("%s: wait_and_timeout: kill %d", errstr, pid);
159
return (-1);
160
}
161
162
wpid = waitpid(pid, status, 0);
163
if (wpid < 0) {
164
warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
165
return (-1);
166
}
167
168
if (wpid != pid) {
169
warn("%s: waitpid: returned %d not %d", errstr, wpid, pid);
170
return (-1);
171
}
172
173
warnx("%s: process blocked", errstr);
174
return (-1);
175
}
176
177
static int
178
non_blocking_open_reader(void)
179
{
180
int fd;
181
182
fd = open("testfifo", O_RDONLY | O_NONBLOCK);
183
if (fd < 0)
184
return (errno);
185
close(fd);
186
187
return (0);
188
}
189
190
static int
191
non_blocking_open_writer(void)
192
{
193
int fd;
194
195
fd = open("testfifo", O_WRONLY | O_NONBLOCK);
196
if (fd < 0)
197
return (errno);
198
close(fd);
199
200
return (0);
201
}
202
203
static int
204
blocking_open_reader(void)
205
{
206
int fd;
207
208
fd = open("testfifo", O_RDONLY);
209
if (fd < 0)
210
return (errno);
211
close(fd);
212
213
return (0);
214
}
215
216
static int
217
blocking_open_writer(void)
218
{
219
int fd;
220
221
fd = open("testfifo", O_WRONLY);
222
if (fd < 0)
223
return (errno);
224
close(fd);
225
226
return (0);
227
}
228
229
static void
230
test_blocking_reader(void)
231
{
232
pid_t reader_pid, writer_pid, wpid;
233
int error, status;
234
235
if (mkfifo("testfifo", 0600) < 0)
236
err(-1, "test_blocking_reader: mkfifo: testfifo");
237
238
/*
239
* Block a process in opening the fifo.
240
*/
241
if (run_in_process(blocking_open_reader, &reader_pid,
242
"test_blocking_reader: blocking_open_reader") < 0) {
243
(void)unlink("testfifo");
244
exit(-1);
245
}
246
247
/*
248
* Test that it blocked.
249
*/
250
sleep(5);
251
wpid = waitpid(reader_pid, &status, WNOHANG);
252
if (wpid < 0) {
253
error = errno;
254
(void)unlink("testfifo");
255
errno = error;
256
err(-1, "test_blocking_reader: waitpid %d", reader_pid);
257
}
258
259
if (wpid != 0 && wpid != reader_pid) {
260
(void)unlink("testfifo");
261
errx(-1, "test_blocking_reader: waitpid %d returned %d",
262
reader_pid, wpid);
263
}
264
265
if (wpid == reader_pid) {
266
(void)unlink("testfifo");
267
errx(-1, "test_blocking_reader: blocking child didn't "
268
"block");
269
}
270
271
/*
272
* Unblock the blocking reader.
273
*/
274
if (run_in_process(blocking_open_writer, &writer_pid,
275
"test_blocking_reader: blocking_open_writer") < 0) {
276
(void)unlink("testfifo");
277
(void)kill(reader_pid, SIGTERM);
278
(void)waitpid(reader_pid, &status, 0);
279
exit(-1);
280
}
281
282
/*
283
* Make sure both processes exited quickly (<1 second) to make sure
284
* they didn't block, and GC.
285
*/
286
if (wait_and_timeout(reader_pid, 1, &status,
287
"test_blocking_reader: blocking_open_reader") < 0) {
288
(void)unlink("testinfo");
289
(void)kill(reader_pid, SIGTERM);
290
(void)kill(writer_pid, SIGTERM);
291
exit(-1);
292
}
293
294
if (wait_and_timeout(writer_pid, 1, &status,
295
"test_blocking_reader: blocking_open_writer") < 0) {
296
(void)unlink("testinfo");
297
(void)kill(writer_pid, SIGTERM);
298
exit(-1);
299
}
300
301
if (unlink("testfifo") < 0)
302
err(-1, "test_blocking_reader: unlink: testfifo");
303
}
304
static void
305
test_blocking_writer(void)
306
{
307
pid_t reader_pid, writer_pid, wpid;
308
int error, status;
309
310
if (mkfifo("testfifo", 0600) < 0)
311
err(-1, "test_blocking_writer: mkfifo: testfifo");
312
313
/*
314
* Block a process in opening the fifo.
315
*/
316
if (run_in_process(blocking_open_writer, &writer_pid,
317
"test_blocking_writer: blocking_open_writer") < 0) {
318
(void)unlink("testfifo");
319
exit(-1);
320
}
321
322
/*
323
* Test that it blocked.
324
*/
325
sleep(5);
326
wpid = waitpid(writer_pid, &status, WNOHANG);
327
if (wpid < 0) {
328
error = errno;
329
(void)unlink("testfifo");
330
errno = error;
331
err(-1, "test_blocking_writer: waitpid %d", writer_pid);
332
}
333
334
if (wpid != 0 && wpid != writer_pid) {
335
(void)unlink("testfifo");
336
errx(-1, "test_blocking_writer: waitpid %d returned %d",
337
writer_pid, wpid);
338
}
339
340
if (wpid == writer_pid) {
341
(void)unlink("testfifo");
342
errx(-1, "test_blocking_writer: blocking child didn't "
343
"block");
344
}
345
346
/*
347
* Unblock the blocking writer.
348
*/
349
if (run_in_process(blocking_open_reader, &reader_pid,
350
"test_blocking_writer: blocking_open_reader") < 0) {
351
(void)unlink("testfifo");
352
(void)kill(writer_pid, SIGTERM);
353
(void)waitpid(writer_pid, &status, 0);
354
exit(-1);
355
}
356
357
/*
358
* Make sure both processes exited quickly (<1 second) to make sure
359
* they didn't block, and GC.
360
*/
361
if (wait_and_timeout(writer_pid, 1, &status,
362
"test_blocking_writer: blocking_open_writer") < 0) {
363
(void)unlink("testinfo");
364
(void)kill(writer_pid, SIGTERM);
365
(void)kill(reader_pid, SIGTERM);
366
(void)waitpid(writer_pid, &status, 0);
367
(void)waitpid(reader_pid, &status, 0);
368
exit(-1);
369
}
370
371
if (wait_and_timeout(reader_pid, 1, &status,
372
"test_blocking_writer: blocking_open_reader") < 0) {
373
(void)unlink("testinfo");
374
(void)kill(reader_pid, SIGTERM);
375
(void)waitpid(reader_pid, &status, 0);
376
exit(-1);
377
}
378
379
if (unlink("testfifo") < 0)
380
err(-1, "test_blocking_writer: unlink: testfifo");
381
}
382
383
static void
384
test_non_blocking_reader(void)
385
{
386
int status;
387
pid_t pid;
388
389
if (mkfifo("testfifo", 0600) < 0)
390
err(-1, "test_non_blocking_reader: mkfifo: testfifo");
391
392
if (run_in_process(non_blocking_open_reader, &pid,
393
"test_non_blocking_reader: non_blocking_open_reader") < 0) {
394
(void)unlink("testfifo");
395
exit(-1);
396
}
397
398
status = -1;
399
if (wait_and_timeout(pid, 5, &status,
400
"test_non_blocking_reader: non_blocking_open_reader") < 0) {
401
(void)unlink("testfifo");
402
exit(-1);
403
}
404
405
if (WEXITSTATUS(status) != 0) {
406
(void)unlink("testfifo");
407
errno = WEXITSTATUS(status);
408
err(-1, "test_non_blocking_reader: "
409
"non_blocking_open_reader: open: testfifo");
410
}
411
412
if (unlink("testfifo") < 0)
413
err(-1, "test_non_blocking_reader: unlink: testfifo");
414
}
415
416
static void
417
test_non_blocking_writer(void)
418
{
419
int status;
420
pid_t pid;
421
422
if (mkfifo("testfifo", 0600) < 0)
423
err(-1, "test_non_blocking_writer: mkfifo: testfifo");
424
425
if (run_in_process(non_blocking_open_writer, &pid,
426
"test_non_blocking_writer: non_blocking_open_writer") < 0) {
427
(void)unlink("testfifo");
428
exit(-1);
429
}
430
431
status = -1;
432
if (wait_and_timeout(pid, 5, &status,
433
"test_non_blocking_writer: non_blocking_open_writer") < 0) {
434
(void)unlink("testfifo");
435
exit(-1);
436
}
437
438
if (WEXITSTATUS(status) != ENXIO) {
439
(void)unlink("testfifo");
440
441
errno = WEXITSTATUS(status);
442
if (errno == 0)
443
errx(-1, "test_non_blocking_writer: "
444
"non_blocking_open_writer: open succeeded");
445
err(-1, "test_non_blocking_writer: "
446
"non_blocking_open_writer: open: testfifo");
447
}
448
449
if (unlink("testfifo") < 0)
450
err(-1, "test_non_blocking_writer: unlink: testfifo");
451
}
452
453
int
454
main(void)
455
{
456
457
if (geteuid() != 0)
458
errx(-1, "must be run as root");
459
460
strcpy(temp_dir, "fifo_open.XXXXXXXXXXX");
461
if (mkdtemp(temp_dir) == NULL)
462
err(-1, "mkdtemp");
463
if (chdir(temp_dir) < 0)
464
err(-1, "chdir: %s", temp_dir);
465
atexit(atexit_temp_dir);
466
467
test_non_blocking_reader();
468
test_non_blocking_writer();
469
470
test_blocking_reader();
471
test_blocking_writer();
472
473
return (0);
474
}
475
476