1229997Sken/*-
2229997Sken * Copyright (c) 2005 Robert N. M. Watson
3229997Sken * All rights reserved.
4232604Strasz *
5229997Sken * Redistribution and use in source and binary forms, with or without
6229997Sken * modification, are permitted provided that the following conditions
7232604Strasz * are met:
8232604Strasz * 1. Redistributions of source code must retain the above copyright
9232604Strasz *    notice, this list of conditions and the following disclaimer.
10229997Sken * 2. Redistributions in binary form must reproduce the above copyright
11229997Sken *    notice, this list of conditions and the following disclaimer in the
12229997Sken *    documentation and/or other materials provided with the distribution.
13229997Sken *
14229997Sken * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15229997Sken * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16229997Sken * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17229997Sken * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18229997Sken * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19229997Sken * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20229997Sken * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21229997Sken * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22229997Sken * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23229997Sken * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24229997Sken * SUCH DAMAGE.
25229997Sken *
26229997Sken * $FreeBSD: releng/10.3/tests/sys/fifo/fifo_open.c 281450 2015-04-12 06:18:24Z ngie $
27229997Sken */
28229997Sken
29229997Sken#include <sys/types.h>
30229997Sken#include <sys/stat.h>
31229997Sken#include <sys/wait.h>
32229997Sken
33229997Sken#include <err.h>
34229997Sken#include <errno.h>
35229997Sken#include <fcntl.h>
36229997Sken#include <limits.h>
37229997Sken#include <signal.h>
38229997Sken#include <stdio.h>
39229997Sken#include <stdlib.h>
40229997Sken#include <string.h>
41229997Sken#include <unistd.h>
42229997Sken
43229997Sken/*
44229997Sken * Regression test to exercise various POSIX-defined parts of fifo behavior
45229997Sken * described for open(2):
46229997Sken *
47229997Sken * O_NONBLOCK
48229997Sken * When opening a FIFO with O_RDONLY or O_WRONLY set:
49229997Sken *
50229997Sken * - If O_NONBLOCK is set, an open() for reading-only shall return without
51229997Sken *   delay. An open() for writing-only shall return an error if no process
52229997Sken *   currently has the file open for reading.
53229997Sken *
54265634Smav * - If O_NONBLOCK is clear, an open() for reading-only shall block the
55229997Sken *   calling thread until a thread opens the file for writing. An open()
56229997Sken *   for writing-only shall block the calling thread until a thread opens
57229997Sken *   the file for reading.
58229997Sken *
59229997Sken * When opening a block special or character special file that supports
60229997Sken * non-blocking opens:
61229997Sken *
62229997Sken * - If O_NONBLOCK is set, the open() function shall return without blocking
63229997Sken *   for the device to be ready or available. Subsequent behavior of the
64229997Sken *   device is device-specific.
65229997Sken *
66229997Sken * - If O_NONBLOCK is clear, the open() function shall block the calling
67229997Sken *   thread until the device is ready or available before returning.
68229997Sken *
69229997Sken * Special errors:
70229997Sken *
71229997Sken * [ENXIO]
72229997Sken * O_NONBLOCK is set, the named file is a FIFO, O_WRONLY is set, and no
73275892Smav * process has the file open for reading.
74229997Sken */
75229997Sken
76229997Sken/*
77229997Sken * In order to test blocking/non-blocking behavior, test processes must
78229997Sken * potentially block themselves until released.  As bugs in blocking result
79229997Sken * in processes that won't un-block, we must sacrifice a process to the task,
80229997Sken * watching and potentially killing it after a time-out.  The main test
81229997Sken * process is never used to open or act directly on a fifo (other than to
82229997Sken * create or unlink it) in order to avoid the main test process being
83229997Sken * blocked.
84229997Sken */
85229997Sken
86229997Sken/*
87229997Sken * All activity occurs within a temporary directory created early in the
88229997Sken * test.
89229997Sken */
90229997Skenstatic char	temp_dir[PATH_MAX];
91229997Sken
92229997Skenstatic void __unused
93229997Skenatexit_temp_dir(void)
94229997Sken{
95265642Smav
96265642Smav	rmdir(temp_dir);
97229997Sken}
98268151Smav
99268151Smav/*
100265642Smav * Run a function in a particular test process.
101268151Smav */
102268151Smavstatic int
103229997Skenrun_in_process(int (*func)(void), pid_t *pidp, const char *errstr)
104229997Sken{
105229997Sken	pid_t pid;
106229997Sken
107229997Sken	pid = fork();
108229997Sken	if (pid < 0) {
109229997Sken		warn("%s: run_in_process: fork", errstr);
110229997Sken		return (-1);
111268150Smav	}
112268150Smav
113268151Smav	if (pid == 0)
114268151Smav		exit(func());
115268150Smav
116229997Sken	if (pidp != NULL)
117229997Sken		*pidp = pid;
118229997Sken
119229997Sken	return (0);
120229997Sken}
121229997Sken
122229997Sken/*
123229997Sken * Wait for a process on a timeout, and if the timeout expires, kill the
124229997Sken * process.  Test each second rather than waiting the full timeout at once to
125229997Sken * minimize the amount of time spent hanging around unnecessarily.
126229997Sken */
127229997Skenstatic int
128229997Skenwait_and_timeout(pid_t pid, int timeout, int *status, const char *errstr)
129229997Sken{
130229997Sken	pid_t wpid;
131229997Sken	int i;
132229997Sken
133229997Sken	/*
134229997Sken	 * Count up to the timeout, but do a non-hanging waitpid() after each
135229997Sken	 * second so we can avoid waiting a lot of extra time.
136229997Sken	 */
137229997Sken	for (i = 0; i < timeout; i++) {
138229997Sken		wpid = waitpid(pid, status, WNOHANG);
139229997Sken		if (wpid < 0) {
140229997Sken			warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
141229997Sken			return (-1);
142229997Sken		}
143229997Sken
144229997Sken		if (wpid == pid)
145229997Sken			return (0);
146229997Sken
147229997Sken		sleep(1);
148229997Sken	}
149229997Sken
150229997Sken	wpid = waitpid(pid, status, WNOHANG);
151274732Smav	if (wpid < 0) {
152274732Smav		warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
153229997Sken		return (-1);
154229997Sken	}
155229997Sken
156229997Sken	if (wpid == pid)
157229997Sken		return (0);
158229997Sken
159273315Smav	if (kill(pid, SIGTERM) < 0) {
160229997Sken		warn("%s: wait_and_timeout: kill %d", errstr, pid);
161229997Sken		return (-1);
162229997Sken	}
163229997Sken
164229997Sken	wpid = waitpid(pid, status, 0);
165229997Sken	if (wpid < 0) {
166229997Sken		warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
167229997Sken		return (-1);
168265634Smav	}
169275892Smav
170274732Smav	if (wpid != pid) {
171229997Sken		warn("%s: waitpid: returned %d not %d", errstr, wpid, pid);
172229997Sken		return (-1);
173229997Sken	}
174229997Sken
175229997Sken	warnx("%s: process blocked", errstr);
176264727Smav	return (-1);
177264727Smav}
178229997Sken
179229997Skenstatic int
180229997Skennon_blocking_open_reader(void)
181229997Sken{
182229997Sken	int fd;
183229997Sken
184229997Sken	fd = open("testfifo", O_RDONLY | O_NONBLOCK);
185229997Sken	if (fd < 0)
186229997Sken		return (errno);
187275892Smav	close(fd);
188229997Sken
189229997Sken	return (0);
190268549Smav}
191268549Smav
192229997Skenstatic int
193229997Skennon_blocking_open_writer(void)
194229997Sken{
195229997Sken	int fd;
196229997Sken
197229997Sken	fd = open("testfifo", O_WRONLY | O_NONBLOCK);
198229997Sken	if (fd < 0)
199229997Sken		return (errno);
200229997Sken	close(fd);
201229997Sken
202229997Sken	return (0);
203229997Sken}
204229997Sken
205229997Skenstatic int
206229997Skenblocking_open_reader(void)
207229997Sken{
208229997Sken	int fd;
209229997Sken
210229997Sken	fd = open("testfifo", O_RDONLY);
211229997Sken	if (fd < 0)
212229997Sken		return (errno);
213229997Sken	close(fd);
214229997Sken
215229997Sken	return (0);
216229997Sken}
217229997Sken
218229997Skenstatic int
219229997Skenblocking_open_writer(void)
220229997Sken{
221229997Sken	int fd;
222229997Sken
223229997Sken	fd = open("testfifo", O_WRONLY);
224229997Sken	if (fd < 0)
225229997Sken		return (errno);
226229997Sken	close(fd);
227265634Smav
228229997Sken	return (0);
229229997Sken}
230229997Sken
231229997Skenstatic void
232229997Skentest_blocking_reader(void)
233229997Sken{
234229997Sken	pid_t reader_pid, writer_pid, wpid;
235229997Sken	int error, status;
236229997Sken
237229997Sken	if (mkfifo("testfifo", 0600) < 0)
238229997Sken		err(-1, "test_blocking_reader: mkfifo: testfifo");
239229997Sken
240229997Sken	/*
241229997Sken	 * Block a process in opening the fifo.
242229997Sken	 */
243229997Sken	if (run_in_process(blocking_open_reader, &reader_pid,
244229997Sken	    "test_blocking_reader: blocking_open_reader") < 0) {
245229997Sken		(void)unlink("testfifo");
246275892Smav		exit(-1);
247275892Smav	}
248275893Smav
249275893Smav	/*
250229997Sken	 * Test that it blocked.
251229997Sken	 */
252265634Smav	sleep(5);
253265634Smav	wpid = waitpid(reader_pid, &status, WNOHANG);
254229997Sken	if (wpid < 0) {
255229997Sken		error = errno;
256274732Smav		(void)unlink("testfifo");
257274732Smav		errno = error;
258275892Smav		err(-1, "test_blocking_reader: waitpid %d", reader_pid);
259275892Smav	}
260229997Sken
261229997Sken	if (wpid != 0 && wpid != reader_pid) {
262229997Sken		(void)unlink("testfifo");
263229997Sken		errx(-1, "test_blocking_reader: waitpid %d returned %d",
264229997Sken		    reader_pid, wpid);
265229997Sken	}
266229997Sken
267229997Sken	if (wpid == reader_pid) {
268229997Sken		(void)unlink("testfifo");
269229997Sken		errx(-1, "test_blocking_reader: blocking child didn't "
270229997Sken		    "block");
271229997Sken	}
272229997Sken
273229997Sken	/*
274229997Sken	 * Unblock the blocking reader.
275229997Sken	 */
276229997Sken	if (run_in_process(blocking_open_writer, &writer_pid,
277229997Sken	    "test_blocking_reader: blocking_open_writer") < 0) {
278229997Sken		(void)unlink("testfifo");
279229997Sken		(void)kill(reader_pid, SIGTERM);
280232604Strasz		(void)waitpid(reader_pid, &status, 0);
281232604Strasz		exit(-1);
282232604Strasz	}
283232604Strasz
284232604Strasz	/*
285232604Strasz	 * Make sure both processes exited quickly (<1 second) to make sure
286229997Sken	 * they didn't block, and GC.
287229997Sken	 */
288229997Sken	if (wait_and_timeout(reader_pid, 1, &status,
289229997Sken	    "test_blocking_reader: blocking_open_reader") < 0) {
290229997Sken		(void)unlink("testinfo");
291229997Sken		(void)kill(reader_pid, SIGTERM);
292274732Smav		(void)kill(writer_pid, SIGTERM);
293229997Sken		exit(-1);
294229997Sken	}
295229997Sken
296229997Sken	if (wait_and_timeout(writer_pid, 1, &status,
297230334Sken	    "test_blocking_reader: blocking_open_writer") < 0) {
298230334Sken		(void)unlink("testinfo");
299230334Sken		(void)kill(writer_pid, SIGTERM);
300230334Sken		exit(-1);
301230334Sken	}
302230334Sken
303230334Sken	if (unlink("testfifo") < 0)
304230334Sken		err(-1, "test_blocking_reader: unlink: testfifo");
305274732Smav}
306274732Smavstatic void
307229997Skentest_blocking_writer(void)
308229997Sken{
309229997Sken	pid_t reader_pid, writer_pid, wpid;
310229997Sken	int error, status;
311229997Sken
312265494Strasz	if (mkfifo("testfifo", 0600) < 0)
313265494Strasz		err(-1, "test_blocking_writer: mkfifo: testfifo");
314229997Sken
315229997Sken	/*
316229997Sken	 * Block a process in opening the fifo.
317229997Sken	 */
318229997Sken	if (run_in_process(blocking_open_writer, &writer_pid,
319265494Strasz	    "test_blocking_writer: blocking_open_writer") < 0) {
320265494Strasz		(void)unlink("testfifo");
321229997Sken		exit(-1);
322229997Sken	}
323229997Sken
324229997Sken	/*
325229997Sken	 * Test that it blocked.
326229997Sken	 */
327229997Sken	sleep(5);
328229997Sken	wpid = waitpid(writer_pid, &status, WNOHANG);
329229997Sken	if (wpid < 0) {
330229997Sken		error = errno;
331229997Sken		(void)unlink("testfifo");
332229997Sken		errno = error;
333229997Sken		err(-1, "test_blocking_writer: waitpid %d", writer_pid);
334229997Sken	}
335229997Sken
336229997Sken	if (wpid != 0 && wpid != writer_pid) {
337229997Sken		(void)unlink("testfifo");
338268151Smav		errx(-1, "test_blocking_writer: waitpid %d returned %d",
339268151Smav		    writer_pid, wpid);
340268151Smav	}
341268151Smav
342268151Smav	if (wpid == writer_pid) {
343268151Smav		(void)unlink("testfifo");
344268151Smav		errx(-1, "test_blocking_writer: blocking child didn't "
345229997Sken		    "block");
346229997Sken	}
347229997Sken
348229997Sken	/*
349229997Sken	 * Unblock the blocking writer.
350229997Sken	 */
351229997Sken	if (run_in_process(blocking_open_reader, &reader_pid,
352265494Strasz	    "test_blocking_writer: blocking_open_reader") < 0) {
353229997Sken		(void)unlink("testfifo");
354229997Sken		(void)kill(writer_pid, SIGTERM);
355229997Sken		(void)waitpid(writer_pid, &status, 0);
356229997Sken		exit(-1);
357229997Sken	}
358268549Smav
359229997Sken	/*
360265634Smav	 * Make sure both processes exited quickly (<1 second) to make sure
361265634Smav	 * they didn't block, and GC.
362265634Smav	 */
363265634Smav	if (wait_and_timeout(writer_pid, 1, &status,
364268151Smav	    "test_blocking_writer: blocking_open_writer") < 0) {
365265634Smav		(void)unlink("testinfo");
366229997Sken		(void)kill(writer_pid, SIGTERM);
367229997Sken		(void)kill(reader_pid, SIGTERM);
368229997Sken		(void)waitpid(writer_pid, &status, 0);
369229997Sken		(void)waitpid(reader_pid, &status, 0);
370229997Sken		exit(-1);
371229997Sken	}
372229997Sken
373268151Smav	if (wait_and_timeout(reader_pid, 1, &status,
374229997Sken	    "test_blocking_writer: blocking_open_reader") < 0) {
375229997Sken		(void)unlink("testinfo");
376268151Smav		(void)kill(reader_pid, SIGTERM);
377268151Smav		(void)waitpid(reader_pid, &status, 0);
378229997Sken		exit(-1);
379268150Smav	}
380229997Sken
381229997Sken	if (unlink("testfifo") < 0)
382229997Sken		err(-1, "test_blocking_writer: unlink: testfifo");
383229997Sken}
384229997Sken
385229997Skenstatic void
386229997Skentest_non_blocking_reader(void)
387229997Sken{
388229997Sken	int status;
389229997Sken	pid_t pid;
390268151Smav
391229997Sken	if (mkfifo("testfifo", 0600) < 0)
392229997Sken		err(-1, "test_non_blocking_reader: mkfifo: testfifo");
393229997Sken
394229997Sken	if (run_in_process(non_blocking_open_reader, &pid,
395229997Sken	    "test_non_blocking_reader: non_blocking_open_reader") < 0) {
396275881Smav		(void)unlink("testfifo");
397275881Smav		exit(-1);
398275881Smav	}
399268151Smav
400268151Smav	status = -1;
401268151Smav	if (wait_and_timeout(pid, 5, &status,
402268151Smav	    "test_non_blocking_reader: non_blocking_open_reader") < 0) {
403268151Smav		(void)unlink("testfifo");
404268151Smav		exit(-1);
405268151Smav	}
406268151Smav
407268151Smav	if (WEXITSTATUS(status) != 0) {
408268151Smav		(void)unlink("testfifo");
409268151Smav		errno = WEXITSTATUS(status);
410268151Smav		err(-1, "test_non_blocking_reader: "
411268151Smav		    "non_blocking_open_reader: open: testfifo");
412268151Smav	}
413268151Smav
414268151Smav	if (unlink("testfifo") < 0)
415268151Smav		err(-1, "test_non_blocking_reader: unlink: testfifo");
416268151Smav}
417268151Smav
418268151Smavstatic void
419268151Smavtest_non_blocking_writer(void)
420268151Smav{
421275881Smav	int status;
422275881Smav	pid_t pid;
423275881Smav
424229997Sken	if (mkfifo("testfifo", 0600) < 0)
425229997Sken		err(-1, "test_non_blocking_writer: mkfifo: testfifo");
426229997Sken
427229997Sken	if (run_in_process(non_blocking_open_writer, &pid,
428229997Sken	    "test_non_blocking_writer: non_blocking_open_writer") < 0) {
429229997Sken		(void)unlink("testfifo");
430229997Sken		exit(-1);
431229997Sken	}
432229997Sken
433229997Sken	status = -1;
434229997Sken	if (wait_and_timeout(pid, 5, &status,
435229997Sken	    "test_non_blocking_writer: non_blocking_open_writer") < 0) {
436229997Sken		(void)unlink("testfifo");
437229997Sken		exit(-1);
438229997Sken	}
439229997Sken
440229997Sken	if (WEXITSTATUS(status) != ENXIO) {
441229997Sken		(void)unlink("testfifo");
442229997Sken
443229997Sken		errno = WEXITSTATUS(status);
444229997Sken		if (errno == 0)
445229997Sken			errx(-1, "test_non_blocking_writer: "
446229997Sken			    "non_blocking_open_writer: open succeeded");
447229997Sken		err(-1, "test_non_blocking_writer: "
448229997Sken		    "non_blocking_open_writer: open: testfifo");
449229997Sken	}
450229997Sken
451229997Sken	if (unlink("testfifo") < 0)
452229997Sken		err(-1, "test_non_blocking_writer: unlink: testfifo");
453229997Sken}
454229997Sken
455229997Skenint
456268549Smavmain(void)
457229997Sken{
458229997Sken
459229997Sken	if (geteuid() != 0)
460229997Sken		errx(-1, "must be run as root");
461229997Sken
462229997Sken	strcpy(temp_dir, "fifo_open.XXXXXXXXXXX");
463268549Smav	if (mkdtemp(temp_dir) == NULL)
464229997Sken		err(-1, "mkdtemp");
465229997Sken	if (chdir(temp_dir) < 0)
466229997Sken		err(-1, "chdir: %s", temp_dir);
467229997Sken	atexit(atexit_temp_dir);
468229997Sken
469229997Sken	test_non_blocking_reader();
470229997Sken	test_non_blocking_writer();
471229997Sken
472229997Sken	test_blocking_reader();
473229997Sken	test_blocking_writer();
474229997Sken
475229997Sken	return (0);
476262299Smav}
477229997Sken