1149985Srwatson/*-
2149985Srwatson * Copyright (c) 2005 Robert N. M. Watson
3149985Srwatson * All rights reserved.
4149985Srwatson *
5149985Srwatson * Redistribution and use in source and binary forms, with or without
6149985Srwatson * modification, are permitted provided that the following conditions
7149985Srwatson * are met:
8149985Srwatson * 1. Redistributions of source code must retain the above copyright
9149985Srwatson *    notice, this list of conditions and the following disclaimer.
10149985Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11149985Srwatson *    notice, this list of conditions and the following disclaimer in the
12149985Srwatson *    documentation and/or other materials provided with the distribution.
13149985Srwatson *
14149985Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15149985Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16149985Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17149985Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18149985Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19149985Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20149985Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21149985Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22149985Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23149985Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24149985Srwatson * SUCH DAMAGE.
25149985Srwatson *
26149985Srwatson * $FreeBSD$
27149985Srwatson */
28149985Srwatson
29149985Srwatson#include <sys/types.h>
30149985Srwatson#include <sys/stat.h>
31149985Srwatson#include <sys/wait.h>
32149985Srwatson
33149985Srwatson#include <err.h>
34149985Srwatson#include <errno.h>
35149985Srwatson#include <fcntl.h>
36149985Srwatson#include <limits.h>
37149985Srwatson#include <signal.h>
38149985Srwatson#include <stdio.h>
39149985Srwatson#include <stdlib.h>
40149985Srwatson#include <string.h>
41149985Srwatson#include <unistd.h>
42149985Srwatson
43149985Srwatson/*
44149985Srwatson * Regression test to exercise various POSIX-defined parts of fifo behavior
45149985Srwatson * described for open(2):
46149985Srwatson *
47149985Srwatson * O_NONBLOCK
48149985Srwatson * When opening a FIFO with O_RDONLY or O_WRONLY set:
49149985Srwatson *
50149985Srwatson * - If O_NONBLOCK is set, an open() for reading-only shall return without
51149985Srwatson *   delay. An open() for writing-only shall return an error if no process
52149985Srwatson *   currently has the file open for reading.
53149985Srwatson *
54149985Srwatson * - If O_NONBLOCK is clear, an open() for reading-only shall block the
55149985Srwatson *   calling thread until a thread opens the file for writing. An open()
56149985Srwatson *   for writing-only shall block the calling thread until a thread opens
57149985Srwatson *   the file for reading.
58149985Srwatson *
59149985Srwatson * When opening a block special or character special file that supports
60149985Srwatson * non-blocking opens:
61149985Srwatson *
62149985Srwatson * - If O_NONBLOCK is set, the open() function shall return without blocking
63149985Srwatson *   for the device to be ready or available. Subsequent behavior of the
64149985Srwatson *   device is device-specific.
65149985Srwatson *
66149985Srwatson * - If O_NONBLOCK is clear, the open() function shall block the calling
67149985Srwatson *   thread until the device is ready or available before returning.
68149985Srwatson *
69149985Srwatson * Special errors:
70149985Srwatson *
71149985Srwatson * [ENXIO]
72149985Srwatson * O_NONBLOCK is set, the named file is a FIFO, O_WRONLY is set, and no
73149985Srwatson * process has the file open for reading.
74149985Srwatson */
75149985Srwatson
76149985Srwatson/*
77149985Srwatson * In order to test blocking/non-blocking behavior, test processes must
78149985Srwatson * potentially block themselves until released.  As bugs in blocking result
79149985Srwatson * in processes that won't un-block, we must sacrifice a process to the task,
80149985Srwatson * watching and potentially killing it after a time-out.  The main test
81149985Srwatson * process is never used to open or act directly on a fifo (other than to
82149985Srwatson * create or unlink it) in order to avoid the main test process being
83149985Srwatson * blocked.
84149985Srwatson */
85149985Srwatson
86149985Srwatson/*
87149985Srwatson * All activity occurs within a temporary directory created early in the
88149985Srwatson * test.
89149985Srwatson */
90281450Sngiestatic char	temp_dir[PATH_MAX];
91149985Srwatson
92149985Srwatsonstatic void __unused
93149985Srwatsonatexit_temp_dir(void)
94149985Srwatson{
95149985Srwatson
96149985Srwatson	rmdir(temp_dir);
97149985Srwatson}
98149985Srwatson
99149985Srwatson/*
100149985Srwatson * Run a function in a particular test process.
101149985Srwatson */
102149985Srwatsonstatic int
103149985Srwatsonrun_in_process(int (*func)(void), pid_t *pidp, const char *errstr)
104149985Srwatson{
105149985Srwatson	pid_t pid;
106149985Srwatson
107149985Srwatson	pid = fork();
108149985Srwatson	if (pid < 0) {
109149985Srwatson		warn("%s: run_in_process: fork", errstr);
110149985Srwatson		return (-1);
111149985Srwatson	}
112149985Srwatson
113149985Srwatson	if (pid == 0)
114149985Srwatson		exit(func());
115149985Srwatson
116149985Srwatson	if (pidp != NULL)
117149985Srwatson		*pidp = pid;
118149985Srwatson
119149985Srwatson	return (0);
120149985Srwatson}
121149985Srwatson
122149985Srwatson/*
123149985Srwatson * Wait for a process on a timeout, and if the timeout expires, kill the
124149985Srwatson * process.  Test each second rather than waiting the full timeout at once to
125149985Srwatson * minimize the amount of time spent hanging around unnecessarily.
126149985Srwatson */
127149985Srwatsonstatic int
128149985Srwatsonwait_and_timeout(pid_t pid, int timeout, int *status, const char *errstr)
129149985Srwatson{
130149985Srwatson	pid_t wpid;
131149985Srwatson	int i;
132149985Srwatson
133149985Srwatson	/*
134149985Srwatson	 * Count up to the timeout, but do a non-hanging waitpid() after each
135149985Srwatson	 * second so we can avoid waiting a lot of extra time.
136149985Srwatson	 */
137149985Srwatson	for (i = 0; i < timeout; i++) {
138149985Srwatson		wpid = waitpid(pid, status, WNOHANG);
139149985Srwatson		if (wpid < 0) {
140149985Srwatson			warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
141149985Srwatson			return (-1);
142149985Srwatson		}
143149985Srwatson
144149985Srwatson		if (wpid == pid)
145149985Srwatson			return (0);
146149985Srwatson
147149985Srwatson		sleep(1);
148149985Srwatson	}
149149985Srwatson
150149985Srwatson	wpid = waitpid(pid, status, WNOHANG);
151149985Srwatson	if (wpid < 0) {
152149985Srwatson		warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
153149985Srwatson		return (-1);
154149985Srwatson	}
155149985Srwatson
156149985Srwatson	if (wpid == pid)
157149985Srwatson		return (0);
158149985Srwatson
159149985Srwatson	if (kill(pid, SIGTERM) < 0) {
160149985Srwatson		warn("%s: wait_and_timeout: kill %d", errstr, pid);
161149985Srwatson		return (-1);
162149985Srwatson	}
163149985Srwatson
164149985Srwatson	wpid = waitpid(pid, status, 0);
165149985Srwatson	if (wpid < 0) {
166149985Srwatson		warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
167149985Srwatson		return (-1);
168149985Srwatson	}
169149985Srwatson
170149985Srwatson	if (wpid != pid) {
171149985Srwatson		warn("%s: waitpid: returned %d not %d", errstr, wpid, pid);
172149985Srwatson		return (-1);
173149985Srwatson	}
174149985Srwatson
175149985Srwatson	warnx("%s: process blocked", errstr);
176149985Srwatson	return (-1);
177149985Srwatson}
178149985Srwatson
179149985Srwatsonstatic int
180149985Srwatsonnon_blocking_open_reader(void)
181149985Srwatson{
182149985Srwatson	int fd;
183149985Srwatson
184149985Srwatson	fd = open("testfifo", O_RDONLY | O_NONBLOCK);
185149985Srwatson	if (fd < 0)
186149985Srwatson		return (errno);
187149985Srwatson	close(fd);
188149985Srwatson
189149985Srwatson	return (0);
190149985Srwatson}
191149985Srwatson
192149985Srwatsonstatic int
193149985Srwatsonnon_blocking_open_writer(void)
194149985Srwatson{
195149985Srwatson	int fd;
196149985Srwatson
197149985Srwatson	fd = open("testfifo", O_WRONLY | O_NONBLOCK);
198149985Srwatson	if (fd < 0)
199149985Srwatson		return (errno);
200149985Srwatson	close(fd);
201149985Srwatson
202149985Srwatson	return (0);
203149985Srwatson}
204149985Srwatson
205149985Srwatsonstatic int
206149985Srwatsonblocking_open_reader(void)
207149985Srwatson{
208149985Srwatson	int fd;
209149985Srwatson
210149985Srwatson	fd = open("testfifo", O_RDONLY);
211149985Srwatson	if (fd < 0)
212149985Srwatson		return (errno);
213149985Srwatson	close(fd);
214149985Srwatson
215149985Srwatson	return (0);
216149985Srwatson}
217149985Srwatson
218149985Srwatsonstatic int
219149985Srwatsonblocking_open_writer(void)
220149985Srwatson{
221149985Srwatson	int fd;
222149985Srwatson
223149985Srwatson	fd = open("testfifo", O_WRONLY);
224149985Srwatson	if (fd < 0)
225149985Srwatson		return (errno);
226149985Srwatson	close(fd);
227149985Srwatson
228149985Srwatson	return (0);
229149985Srwatson}
230149985Srwatson
231149985Srwatsonstatic void
232149985Srwatsontest_blocking_reader(void)
233149985Srwatson{
234149985Srwatson	pid_t reader_pid, writer_pid, wpid;
235149985Srwatson	int error, status;
236149985Srwatson
237149985Srwatson	if (mkfifo("testfifo", 0600) < 0)
238149985Srwatson		err(-1, "test_blocking_reader: mkfifo: testfifo");
239149985Srwatson
240149985Srwatson	/*
241149985Srwatson	 * Block a process in opening the fifo.
242149985Srwatson	 */
243149985Srwatson	if (run_in_process(blocking_open_reader, &reader_pid,
244149985Srwatson	    "test_blocking_reader: blocking_open_reader") < 0) {
245149985Srwatson		(void)unlink("testfifo");
246149985Srwatson		exit(-1);
247149985Srwatson	}
248149985Srwatson
249149985Srwatson	/*
250149985Srwatson	 * Test that it blocked.
251149985Srwatson	 */
252149985Srwatson	sleep(5);
253149985Srwatson	wpid = waitpid(reader_pid, &status, WNOHANG);
254149985Srwatson	if (wpid < 0) {
255149985Srwatson		error = errno;
256149985Srwatson		(void)unlink("testfifo");
257149985Srwatson		errno = error;
258149985Srwatson		err(-1, "test_blocking_reader: waitpid %d", reader_pid);
259149985Srwatson	}
260149985Srwatson
261149985Srwatson	if (wpid != 0 && wpid != reader_pid) {
262149985Srwatson		(void)unlink("testfifo");
263149985Srwatson		errx(-1, "test_blocking_reader: waitpid %d returned %d",
264149985Srwatson		    reader_pid, wpid);
265149985Srwatson	}
266149985Srwatson
267149985Srwatson	if (wpid == reader_pid) {
268149985Srwatson		(void)unlink("testfifo");
269149985Srwatson		errx(-1, "test_blocking_reader: blocking child didn't "
270149985Srwatson		    "block");
271149985Srwatson	}
272149985Srwatson
273149985Srwatson	/*
274149985Srwatson	 * Unblock the blocking reader.
275149985Srwatson	 */
276149985Srwatson	if (run_in_process(blocking_open_writer, &writer_pid,
277149985Srwatson	    "test_blocking_reader: blocking_open_writer") < 0) {
278149985Srwatson		(void)unlink("testfifo");
279149985Srwatson		(void)kill(reader_pid, SIGTERM);
280149985Srwatson		(void)waitpid(reader_pid, &status, 0);
281149985Srwatson		exit(-1);
282149985Srwatson	}
283149985Srwatson
284149985Srwatson	/*
285149985Srwatson	 * Make sure both processes exited quickly (<1 second) to make sure
286149985Srwatson	 * they didn't block, and GC.
287149985Srwatson	 */
288149985Srwatson	if (wait_and_timeout(reader_pid, 1, &status,
289149985Srwatson	    "test_blocking_reader: blocking_open_reader") < 0) {
290149985Srwatson		(void)unlink("testinfo");
291149985Srwatson		(void)kill(reader_pid, SIGTERM);
292149985Srwatson		(void)kill(writer_pid, SIGTERM);
293149985Srwatson		exit(-1);
294149985Srwatson	}
295149985Srwatson
296149985Srwatson	if (wait_and_timeout(writer_pid, 1, &status,
297149985Srwatson	    "test_blocking_reader: blocking_open_writer") < 0) {
298149985Srwatson		(void)unlink("testinfo");
299149985Srwatson		(void)kill(writer_pid, SIGTERM);
300149985Srwatson		exit(-1);
301149985Srwatson	}
302149985Srwatson
303149985Srwatson	if (unlink("testfifo") < 0)
304149985Srwatson		err(-1, "test_blocking_reader: unlink: testfifo");
305149985Srwatson}
306149985Srwatsonstatic void
307149985Srwatsontest_blocking_writer(void)
308149985Srwatson{
309149985Srwatson	pid_t reader_pid, writer_pid, wpid;
310149985Srwatson	int error, status;
311149985Srwatson
312149985Srwatson	if (mkfifo("testfifo", 0600) < 0)
313149985Srwatson		err(-1, "test_blocking_writer: mkfifo: testfifo");
314149985Srwatson
315149985Srwatson	/*
316149985Srwatson	 * Block a process in opening the fifo.
317149985Srwatson	 */
318149985Srwatson	if (run_in_process(blocking_open_writer, &writer_pid,
319149985Srwatson	    "test_blocking_writer: blocking_open_writer") < 0) {
320149985Srwatson		(void)unlink("testfifo");
321149985Srwatson		exit(-1);
322149985Srwatson	}
323149985Srwatson
324149985Srwatson	/*
325149985Srwatson	 * Test that it blocked.
326149985Srwatson	 */
327149985Srwatson	sleep(5);
328149985Srwatson	wpid = waitpid(writer_pid, &status, WNOHANG);
329149985Srwatson	if (wpid < 0) {
330149985Srwatson		error = errno;
331149985Srwatson		(void)unlink("testfifo");
332149985Srwatson		errno = error;
333149985Srwatson		err(-1, "test_blocking_writer: waitpid %d", writer_pid);
334149985Srwatson	}
335149985Srwatson
336149985Srwatson	if (wpid != 0 && wpid != writer_pid) {
337149985Srwatson		(void)unlink("testfifo");
338149985Srwatson		errx(-1, "test_blocking_writer: waitpid %d returned %d",
339149985Srwatson		    writer_pid, wpid);
340149985Srwatson	}
341149985Srwatson
342149985Srwatson	if (wpid == writer_pid) {
343149985Srwatson		(void)unlink("testfifo");
344149985Srwatson		errx(-1, "test_blocking_writer: blocking child didn't "
345149985Srwatson		    "block");
346149985Srwatson	}
347149985Srwatson
348149985Srwatson	/*
349149985Srwatson	 * Unblock the blocking writer.
350149985Srwatson	 */
351149985Srwatson	if (run_in_process(blocking_open_reader, &reader_pid,
352149985Srwatson	    "test_blocking_writer: blocking_open_reader") < 0) {
353149985Srwatson		(void)unlink("testfifo");
354149985Srwatson		(void)kill(writer_pid, SIGTERM);
355149985Srwatson		(void)waitpid(writer_pid, &status, 0);
356149985Srwatson		exit(-1);
357149985Srwatson	}
358149985Srwatson
359149985Srwatson	/*
360149985Srwatson	 * Make sure both processes exited quickly (<1 second) to make sure
361149985Srwatson	 * they didn't block, and GC.
362149985Srwatson	 */
363149985Srwatson	if (wait_and_timeout(writer_pid, 1, &status,
364149985Srwatson	    "test_blocking_writer: blocking_open_writer") < 0) {
365149985Srwatson		(void)unlink("testinfo");
366149985Srwatson		(void)kill(writer_pid, SIGTERM);
367149985Srwatson		(void)kill(reader_pid, SIGTERM);
368149985Srwatson		(void)waitpid(writer_pid, &status, 0);
369149985Srwatson		(void)waitpid(reader_pid, &status, 0);
370149985Srwatson		exit(-1);
371149985Srwatson	}
372149985Srwatson
373149985Srwatson	if (wait_and_timeout(reader_pid, 1, &status,
374149985Srwatson	    "test_blocking_writer: blocking_open_reader") < 0) {
375149985Srwatson		(void)unlink("testinfo");
376149985Srwatson		(void)kill(reader_pid, SIGTERM);
377149985Srwatson		(void)waitpid(reader_pid, &status, 0);
378149985Srwatson		exit(-1);
379149985Srwatson	}
380149985Srwatson
381149985Srwatson	if (unlink("testfifo") < 0)
382149985Srwatson		err(-1, "test_blocking_writer: unlink: testfifo");
383149985Srwatson}
384149985Srwatson
385149985Srwatsonstatic void
386149985Srwatsontest_non_blocking_reader(void)
387149985Srwatson{
388149985Srwatson	int status;
389149985Srwatson	pid_t pid;
390149985Srwatson
391149985Srwatson	if (mkfifo("testfifo", 0600) < 0)
392149985Srwatson		err(-1, "test_non_blocking_reader: mkfifo: testfifo");
393149985Srwatson
394149985Srwatson	if (run_in_process(non_blocking_open_reader, &pid,
395149985Srwatson	    "test_non_blocking_reader: non_blocking_open_reader") < 0) {
396149985Srwatson		(void)unlink("testfifo");
397149985Srwatson		exit(-1);
398149985Srwatson	}
399149985Srwatson
400149985Srwatson	status = -1;
401149985Srwatson	if (wait_and_timeout(pid, 5, &status,
402149985Srwatson	    "test_non_blocking_reader: non_blocking_open_reader") < 0) {
403149985Srwatson		(void)unlink("testfifo");
404149985Srwatson		exit(-1);
405149985Srwatson	}
406149985Srwatson
407149985Srwatson	if (WEXITSTATUS(status) != 0) {
408149985Srwatson		(void)unlink("testfifo");
409149985Srwatson		errno = WEXITSTATUS(status);
410149985Srwatson		err(-1, "test_non_blocking_reader: "
411149985Srwatson		    "non_blocking_open_reader: open: testfifo");
412149985Srwatson	}
413149985Srwatson
414149985Srwatson	if (unlink("testfifo") < 0)
415149985Srwatson		err(-1, "test_non_blocking_reader: unlink: testfifo");
416149985Srwatson}
417149985Srwatson
418149985Srwatsonstatic void
419149985Srwatsontest_non_blocking_writer(void)
420149985Srwatson{
421149985Srwatson	int status;
422149985Srwatson	pid_t pid;
423149985Srwatson
424149985Srwatson	if (mkfifo("testfifo", 0600) < 0)
425149985Srwatson		err(-1, "test_non_blocking_writer: mkfifo: testfifo");
426149985Srwatson
427149985Srwatson	if (run_in_process(non_blocking_open_writer, &pid,
428149985Srwatson	    "test_non_blocking_writer: non_blocking_open_writer") < 0) {
429149985Srwatson		(void)unlink("testfifo");
430149985Srwatson		exit(-1);
431149985Srwatson	}
432149985Srwatson
433149985Srwatson	status = -1;
434149985Srwatson	if (wait_and_timeout(pid, 5, &status,
435149985Srwatson	    "test_non_blocking_writer: non_blocking_open_writer") < 0) {
436149985Srwatson		(void)unlink("testfifo");
437149985Srwatson		exit(-1);
438149985Srwatson	}
439149985Srwatson
440149985Srwatson	if (WEXITSTATUS(status) != ENXIO) {
441149985Srwatson		(void)unlink("testfifo");
442149985Srwatson
443149985Srwatson		errno = WEXITSTATUS(status);
444149985Srwatson		if (errno == 0)
445149985Srwatson			errx(-1, "test_non_blocking_writer: "
446149985Srwatson			    "non_blocking_open_writer: open succeeded");
447149985Srwatson		err(-1, "test_non_blocking_writer: "
448149985Srwatson		    "non_blocking_open_writer: open: testfifo");
449149985Srwatson	}
450149985Srwatson
451149985Srwatson	if (unlink("testfifo") < 0)
452149985Srwatson		err(-1, "test_non_blocking_writer: unlink: testfifo");
453149985Srwatson}
454149985Srwatson
455149985Srwatsonint
456281450Sngiemain(void)
457149985Srwatson{
458149985Srwatson
459149985Srwatson	if (geteuid() != 0)
460149985Srwatson		errx(-1, "must be run as root");
461149985Srwatson
462281450Sngie	strcpy(temp_dir, "fifo_open.XXXXXXXXXXX");
463149985Srwatson	if (mkdtemp(temp_dir) == NULL)
464149985Srwatson		err(-1, "mkdtemp");
465149998Srwatson	if (chdir(temp_dir) < 0)
466149998Srwatson		err(-1, "chdir: %s", temp_dir);
467149985Srwatson	atexit(atexit_temp_dir);
468149985Srwatson
469149985Srwatson	test_non_blocking_reader();
470149985Srwatson	test_non_blocking_writer();
471149985Srwatson
472149985Srwatson	test_blocking_reader();
473149985Srwatson	test_blocking_writer();
474149985Srwatson
475149985Srwatson	return (0);
476149985Srwatson}
477