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
atexit_temp_dir(void)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
run_in_process(int (* func)(void),pid_t * pidp,const char * errstr)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
wait_and_timeout(pid_t pid,int timeout,int * status,const char * errstr)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
non_blocking_open_reader(void)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
non_blocking_open_writer(void)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
blocking_open_reader(void)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
blocking_open_writer(void)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
test_blocking_reader(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
test_blocking_writer(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
test_non_blocking_reader(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
test_non_blocking_writer(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
main(void)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