[Midnightbsd-cvs] src [11953] trunk/tests/sys/fifo: add fifo tests

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Sat Jul 21 16:17:57 EDT 2018


Revision: 11953
          http://svnweb.midnightbsd.org/src/?rev=11953
Author:   laffer1
Date:     2018-07-21 16:17:56 -0400 (Sat, 21 Jul 2018)
Log Message:
-----------
add fifo tests

Added Paths:
-----------
    trunk/tests/sys/fifo/
    trunk/tests/sys/fifo/Makefile
    trunk/tests/sys/fifo/fifo_create.c
    trunk/tests/sys/fifo/fifo_io.c
    trunk/tests/sys/fifo/fifo_misc.c
    trunk/tests/sys/fifo/fifo_open.c

Added: trunk/tests/sys/fifo/Makefile
===================================================================
--- trunk/tests/sys/fifo/Makefile	                        (rev 0)
+++ trunk/tests/sys/fifo/Makefile	2018-07-21 20:17:56 UTC (rev 11953)
@@ -0,0 +1,14 @@
+# $MidnightBSD$
+# $FreeBSD: stable/10/tests/sys/fifo/Makefile 282067 2015-04-27 08:15:17Z ngie $
+
+TESTSDIR=	${TESTSBASE}/sys/fifo
+
+PLAIN_TESTS_C+=	fifo_create
+PLAIN_TESTS_C+=	fifo_io
+PLAIN_TESTS_C+=	fifo_misc
+PLAIN_TESTS_C+=	fifo_open
+
+TEST_METADATA.fifo_create+=	required_user="root"
+TEST_METADATA.fifo_open+=	required_user="root"
+
+.include <bsd.test.mk>


Property changes on: trunk/tests/sys/fifo/Makefile
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/tests/sys/fifo/fifo_create.c
===================================================================
--- trunk/tests/sys/fifo/fifo_create.c	                        (rev 0)
+++ trunk/tests/sys/fifo/fifo_create.c	2018-07-21 20:17:56 UTC (rev 11953)
@@ -0,0 +1,283 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2005-2008 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/10/tests/sys/fifo/fifo_create.c 281450 2015-04-12 06:18:24Z ngie $
+ */
+
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Simple regression test for the creation and destruction of POSIX fifos in
+ * the file system name space.  Using a specially created directory, create
+ * a fifo in it and check that the following properties are present, as
+ * specified in IEEE Std 1003.1, 2004 Edition:
+ *
+ * - When mkfifo() or mknod(S_IFIFO) is called, on success, a fifo is
+ *   created.
+ *
+ * - On an error, no fifo is created. (XXX: Not tested)
+ *
+ * - The mode bits on the fifo are a product of combining the umask and
+ *   requested mode.
+ *
+ * - The fifo's owner will be the processes effective user ID. (XXX: Not
+ *   tested)
+ *
+ * - The fifo's group will be the parent directory's group or the effective
+ *   group ID of the process.  For historical reasons, BSD prefers the group
+ *   ID of the process, so we will generate an error if it's not that. (XXX:
+ *   Not tested)
+ *
+ * - The st_atime, st_ctime, st_mtime of the fifo will be set appropriately,
+ *   and st_ctime and st_mtime on the directory will be updated. (XXX: We
+ *   test they are updated, not correct)
+ *
+ * - EEXIST is returned if the named file already exists.
+ *
+ * In addition, we check that we can unlink the fifo, and that if we do, it
+ * disappears.
+ *
+ * This test must run as root in order to usefully frob the process
+ * credential to test permission parts.
+ */
+
+/*
+ * All activity occurs within a temporary directory created early in the
+ * test.
+ */
+static char	temp_dir[PATH_MAX];
+
+static void __unused
+atexit_temp_dir(void)
+{
+
+	rmdir(temp_dir);
+}
+
+/*
+ * Basic creation tests: verify that mkfifo(2) (or mknod(2)) creates a fifo,
+ * that the time stamps on the directory are updated, that if we try twice we
+ * get EEXIST, and that we can unlink it.
+ */
+static void
+fifo_create_test(int use_mkfifo)
+{
+	struct stat old_dirsb, dirsb, fifosb;
+	const char *testname;
+	char path[] = "testfifo";
+	int error;
+
+	if (use_mkfifo)
+		testname = "mkfifo";
+	else
+		testname = "mknod";
+
+	/*
+	 * Sleep to make sure that the time stamp on the directory will be
+	 * updated.
+	 */
+	if (stat(".", &old_dirsb) < 0)
+		err(-1, "basic_create_test: %s: stat: %s", testname,
+		    temp_dir);
+
+	sleep(2);
+
+	if (use_mkfifo) {
+		if (mkfifo(path, 0600) < 0)
+			err(-1, "basic_create_test: %s: %s", testname, path);
+	} else {
+		if (mknod(path, S_IFIFO | 0600, 0) < 0)
+			err(-1, "basic_create_test: %s: %s", testname, path);
+	}
+
+	if (stat(path, &fifosb) < 0) {
+		error = errno;
+		(void)unlink(path);
+		errno = error;
+		err(-1, "basic_create_test: %s: stat: %s", testname, path);
+	}
+
+	if (!(S_ISFIFO(fifosb.st_mode))) {
+		(void)unlink(path);
+		errx(-1, "basic_create_test: %s produced non-fifo",
+		    testname);
+	}
+
+	if (use_mkfifo) {
+		if (mkfifo(path, 0600) == 0)
+			errx(-1, "basic_create_test: dup %s succeeded",
+			    testname);
+	} else {
+		if (mknod(path, S_IFIFO | 0600, 0) == 0)
+			errx(-1, "basic_create_test: dup %s succeeded",
+			    testname);
+	}
+
+	if (errno != EEXIST)
+		err(-1, "basic_create_test: dup %s unexpected error",
+		    testname);
+
+	if (stat(".", &dirsb) < 0) {
+		error = errno;
+		(void)unlink(path);
+		errno = error;
+		err(-1, "basic_create_test: %s: stat: %s", testname,
+		    temp_dir);
+	}
+
+	if (old_dirsb.st_ctime == dirsb.st_ctime) {
+		(void)unlink(path);
+		errx(-1, "basic_create_test: %s: old_dirsb.st_ctime == "
+		    "dirsb.st_ctime", testname);
+	}
+
+	if (old_dirsb.st_mtime == dirsb.st_mtime) {
+		(void)unlink(path);
+		errx(-1, "basic_create_test: %s: old_dirsb.st_mtime == "
+		    "dirsb.st_mtime", testname);
+	}
+
+	if (unlink(path) < 0)
+		err(-1, "basic_create_test: %s: unlink: %s", testname, path);
+
+	if (stat(path, &fifosb) == 0)
+		errx(-1, "basic_create_test: %s: unlink failed to unlink",
+		    testname);
+	if (errno != ENOENT)
+		err(-1, "basic_create_test: %s: unlink unexpected error",
+		    testname);
+}
+
+/*
+ * Having determined that basic create/remove/etc functionality is present
+ * for fifos, now make sure that the umask, requested permissions, and
+ * resulting mode are handled properly.
+ */
+static const struct permission_test {
+	mode_t	pt_umask;
+	mode_t	pt_reqmode;
+	mode_t	pt_mode;
+} permission_test[] = {
+	{0000, 0, S_IFIFO},
+	{0000, S_IRWXU, S_IFIFO | S_IRWXU},
+	{0000, S_IRWXU | S_IRWXG | S_IRWXO, S_IFIFO | S_IRWXU | S_IRWXG |
+	    S_IRWXO },
+	{0077, S_IRWXU, S_IFIFO | S_IRWXU},
+	{0077, S_IRWXU | S_IRWXG | S_IRWXO, S_IFIFO | S_IRWXU},
+};
+static const int permission_test_count = sizeof(permission_test) /
+    sizeof(struct permission_test);
+
+static void
+fifo_permission_test(int use_mkfifo)
+{
+	const struct permission_test *ptp;
+	mode_t __unused old_umask;
+	char path[] = "testfifo";
+	const char *testname;
+	struct stat sb;
+	int error, i;
+
+	if (use_mkfifo)
+		testname = "mkfifo";
+	else
+		testname = "mknod";
+
+	old_umask = umask(0022);
+	for (i = 0; i < permission_test_count; i++) {
+		ptp = &permission_test[i];
+
+		umask(ptp->pt_umask);
+		if (use_mkfifo) {
+			if (mkfifo(path, ptp->pt_reqmode) < 0)
+				err(-1, "fifo_permission_test: %s: %08o "
+				    "%08o %08o\n", testname, ptp->pt_umask,
+				    ptp->pt_reqmode, ptp->pt_mode);
+		} else {
+			if (mknod(path, S_IFIFO | ptp->pt_reqmode, 0) < 0)
+				err(-1, "fifo_permission_test: %s: %08o "
+				    "%08o %08o\n", testname, ptp->pt_umask,
+				    ptp->pt_reqmode, ptp->pt_mode);
+		}
+
+		if (stat(path, &sb) < 0) {
+			error = errno;
+			(void)unlink(path);
+			errno = error;
+			err(-1, "fifo_permission_test: %s: %s", testname,
+			    path);
+		}
+
+		if (sb.st_mode != ptp->pt_mode) {
+			(void)unlink(path);
+			errx(-1, "fifo_permission_test: %s: %08o %08o %08o "
+			    "got %08o", testname, ptp->pt_umask,
+			    ptp->pt_reqmode, ptp->pt_mode, sb.st_mode);
+		}
+
+		if (unlink(path) < 0)
+			err(-1, "fifo_permission_test: %s: unlink: %s",
+			    testname, path);
+	}
+	umask(old_umask);
+}
+
+int
+main(void)
+{
+	int i;
+
+	if (geteuid() != 0)
+		errx(-1, "must be run as root");
+
+	strcpy(temp_dir, "fifo_create.XXXXXXXXXXX");
+	if (mkdtemp(temp_dir) == NULL)
+		err(-1, "mkdtemp");
+	atexit(atexit_temp_dir);
+
+	if (chdir(temp_dir) < 0)
+		err(-1, "chdir");
+
+	/*
+	 * Run each test twice, once with mknod(2) and a second time with
+	 * mkfifo(2).  Historically, BSD has not allowed mknod(2) to be used
+	 * to create fifos, but the Single UNIX Specification requires it.
+	 */
+	for (i = 0; i < 2; i++) {
+		fifo_create_test(i);
+		fifo_permission_test(i);
+	}
+
+	return (0);
+}


Property changes on: trunk/tests/sys/fifo/fifo_create.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/tests/sys/fifo/fifo_io.c
===================================================================
--- trunk/tests/sys/fifo/fifo_io.c	                        (rev 0)
+++ trunk/tests/sys/fifo/fifo_io.c	2018-07-21 20:17:56 UTC (rev 11953)
@@ -0,0 +1,1400 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/10/tests/sys/fifo/fifo_io.c 281450 2015-04-12 06:18:24Z ngie $
+ */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Regression test to exercise POSIX fifo I/O.
+ *
+ * We test a number of aspect of behavior, including:
+ *
+ * - If there's no data to read, then for blocking fifos, we block, and for
+ *   non-blocking, we return EAGAIN.
+ *
+ * - If we write ten bytes, ten bytes can be read, and they're the same
+ *   bytes, in the same order.
+ *
+ * - If we write two batches of five bytes, we can read the same ten bytes in
+ *   one read of ten bytes.
+ *
+ * - If we write ten bytes, we can read the same ten bytes in two reads of
+ *   five bytes each.
+ *
+ * - If we over-fill a buffer (by writing 512k, which we take to be a large
+ *   number above default buffer sizes), we block if there is no reader.
+ *
+ * - That once 512k (ish) is read from the other end, the blocked writer
+ *   wakes up.
+ *
+ * - When a fifo is empty, poll, select, kqueue, and fionread report it is
+ *   writable but not readable.
+ *
+ * - When a fifo has data in it, poll, select, and kqueue report that it is
+ *   writable.
+ *
+ * - XXX: blocked reader semantics?
+ *
+ * - XXX: event behavior on remote close?
+ *
+ * Although behavior of O_RDWR isn't defined for fifos by POSIX, we expect
+ * "reasonable" behavior, and run some additional tests relating to event
+ * management on O_RDWR fifo descriptors.
+ */
+
+#define	KQUEUE_MAX_EVENT	8
+
+/*
+ * All activity occurs within a temporary directory created early in the
+ * test.
+ */
+static char	temp_dir[PATH_MAX];
+
+static void __unused
+atexit_temp_dir(void)
+{
+
+	rmdir(temp_dir);
+}
+
+static void
+makefifo(const char *fifoname, const char *testname)
+{
+
+	if (mkfifo(fifoname, 0700) < 0)
+		err(-1, "%s: makefifo: mkfifo: %s", testname, fifoname);
+}
+
+static void
+cleanfifo2(const char *fifoname, int fd1, int fd2)
+{
+
+	if (fd1 != -1)
+		close(fd1);
+	if (fd2 != -1)
+		close(fd2);
+	(void)unlink(fifoname);
+}
+
+static void
+cleanfifo3(const char *fifoname, int fd1, int fd2, int fd3)
+{
+
+	if (fd3 != -1)
+		close(fd3);
+	cleanfifo2(fifoname, fd1, fd2);
+}
+
+/*
+ * Open two different file descriptors for a fifo: one read, one write.  Do
+ * so using non-blocking opens in order to avoid deadlocking the process.
+ */
+static int
+openfifo(const char *fifoname, int *reader_fdp, int *writer_fdp)
+{
+	int error, fd1, fd2;
+
+	fd1 = open(fifoname, O_RDONLY | O_NONBLOCK);
+	if (fd1 < 0)
+		return (-1);
+	fd2 = open(fifoname, O_WRONLY | O_NONBLOCK);
+	if (fd2 < 0) {
+		error = errno;
+		close(fd1);
+		errno = error;
+		return (-1);
+	}
+	*reader_fdp = fd1;
+	*writer_fdp = fd2;
+
+	return (0);
+}
+
+/*
+ * Open one file descriptor for the fifo, supporting both read and write.
+ */
+static int
+openfifo_rw(const char *fifoname, int *fdp)
+{
+	int fd;
+
+	fd = open(fifoname, O_RDWR);
+	if (fd < 0)
+		return (-1);
+	*fdp = fd;
+
+	return (0);
+}
+
+static int
+set_nonblocking(int fd, const char *testname)
+{
+	int flags;
+
+	flags = fcntl(fd, F_GETFL);
+	if (flags < 0) {
+		warn("%s: fcntl(fd, F_GETFL)", testname);
+		return(-1);
+	}
+
+	flags |= O_NONBLOCK;
+
+	if (fcntl(fd, F_SETFL, flags) < 0) {
+		warn("%s: fcntl(fd, 0x%x)", testname, flags);
+		return (-1);
+	}
+
+	return (0);
+}
+
+static int
+set_blocking(int fd, const char *testname)
+{
+	int flags;
+
+	flags = fcntl(fd, F_GETFL);
+	if (flags < 0) {
+		warn("%s: fcntl(fd, F_GETFL)", testname);
+		return(-1);
+	}
+
+	flags &= ~O_NONBLOCK;
+
+	if (fcntl(fd, F_SETFL, flags) < 0) {
+		warn("%s: fcntl(fd, 0x%x)", testname, flags);
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * Drain a file descriptor (fifo) of any readable data.  Note: resets the
+ * blocking state.
+ */
+static int
+drain_fd(int fd, const char *testname)
+{
+	ssize_t len;
+	u_char ch;
+
+	if (set_nonblocking(fd, testname) < 0)
+		return (-1);
+
+	while ((len = read(fd, &ch, sizeof(ch))) > 0);
+	if (len < 0) {
+		switch (errno) {
+		case EAGAIN:
+			return (0);
+		default:
+			warn("%s: drain_fd: read", testname);
+			return (-1);
+		}
+	}
+	warn("%s: drain_fd: read: returned 0 bytes", testname);
+	return (-1);
+}
+
+/*
+ * Simple I/O test: write ten integers, and make sure we get back the same
+ * integers in the same order.  This assumes a minimum fifo buffer > 10
+ * bytes in order to not block and deadlock.
+ */
+static void
+test_simpleio(void)
+{
+	int i, reader_fd, writer_fd;
+	u_char buffer[10];
+	ssize_t len;
+
+	makefifo("testfifo", __func__);
+	if (openfifo("testfifo", &reader_fd, &writer_fd)
+	    < 0) {
+		warn("test_simpleio: openfifo: testfifo");
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	for (i = 0; i < 10; i++)
+		buffer[i] = i;
+
+	len = write(writer_fd, (char *)buffer, sizeof(buffer));
+	if (len < 0) {
+		warn("test_simpleio: write");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != sizeof(buffer)) {
+		warnx("test_simplio: tried %zu but wrote %zd", sizeof(buffer),
+		    len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	len = read(reader_fd, (char *)buffer, sizeof(buffer));
+	if (len < 0) {
+		warn("test_simpleio: read");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != sizeof(buffer)) {
+		warnx("test_simpleio: tried %zu but read %zd", sizeof(buffer),
+		    len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	for (i = 0; i < 10; i++) {
+		if (buffer[i] == i)
+			continue;
+		warnx("test_simpleio: write byte %d as 0x%02x, but read "
+		    "0x%02x", i, i, buffer[i]);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+static volatile int alarm_fired;
+/*
+ * Non-destructive SIGALRM handler.
+ */
+static void
+sigalarm(int signum __unused)
+{
+
+	alarm_fired = 1;
+}
+
+/*
+ * Wrapper function for write, which uses a timer to interrupt any blocking.
+ * Because we can't reliably detect EINTR for blocking I/O, we also track
+ * whether or not our timeout fired.
+ */
+static int __unused
+timed_write(int fd, void *data, size_t len, ssize_t *written_lenp,
+    int timeout, int *timedoutp, const char *testname)
+{
+	struct sigaction act, oact;
+	ssize_t written_len;
+	int error;
+
+	alarm_fired = 0;
+	bzero(&act, sizeof(oact));
+	act.sa_handler = sigalarm;
+	if (sigaction(SIGALRM, &act, &oact) < 0) {
+	 	warn("%s: timed_write: sigaction", testname);
+		return (-1);
+	}
+	alarm(timeout);
+	written_len = write(fd, data, len);
+	error = errno;
+	alarm(0);
+	if (sigaction(SIGALRM, &oact, NULL) < 0) {
+	 	warn("%s: timed_write: sigaction", testname);
+		return (-1);
+	}
+	if (alarm_fired)
+		*timedoutp = 1;
+	else
+		*timedoutp = 0;
+
+	errno = error;
+	if (written_len < 0)
+		return (-1);
+	*written_lenp = written_len;
+	return (0);
+}
+
+/*
+ * Wrapper function for read, which uses a timer to interrupt any blocking.
+ * Because we can't reliably detect EINTR for blocking I/O, we also track
+ * whether or not our timeout fired.
+ */
+static int
+timed_read(int fd, void *data, size_t len, ssize_t *read_lenp,
+    int timeout, int *timedoutp, const char *testname)
+{
+	struct sigaction act, oact;
+	ssize_t read_len;
+	int error;
+
+	alarm_fired = 0;
+	bzero(&act, sizeof(oact));
+	act.sa_handler = sigalarm;
+	if (sigaction(SIGALRM, &act, &oact) < 0) {
+	 	warn("%s: timed_write: sigaction", testname);
+		return (-1);
+	}
+	alarm(timeout);
+	read_len = read(fd, data, len);
+	error = errno;
+	alarm(0);
+	if (sigaction(SIGALRM, &oact, NULL) < 0) {
+	 	warn("%s: timed_write: sigaction", testname);
+		return (-1);
+	}
+	if (alarm_fired)
+		*timedoutp = 1;
+	else
+		*timedoutp = 0;
+
+	errno = error;
+	if (read_len < 0)
+		return (-1);
+	*read_lenp = read_len;
+	return (0);
+}
+
+/*
+ * This test operates on blocking and non-blocking fifo file descriptors, in
+ * order to determine whether they block at good moments or not.  By good we
+ * mean: don't block for non-blocking sockets, and do block for blocking
+ * ones, assuming there isn't I/O buffer to satisfy the request.
+ *
+ * We use a timeout of 5 seconds, concluding that in 5 seconds either all I/O
+ * that can take place will, and that if we reach the end of the timeout,
+ * then blocking has occurred.
+ *
+ * We assume that the buffer size on a fifo is <512K, and as such, that
+ * writing that much data without an active reader will result in blocking.
+ */
+static void
+test_blocking_read_empty(void)
+{
+	int reader_fd, ret, timedout, writer_fd;
+	ssize_t len;
+	u_char ch;
+
+	makefifo("testfifo", __func__);
+	if (openfifo("testfifo", &reader_fd, &writer_fd)
+	    < 0) {
+		warn("test_blocking_read_empty: openfifo: testfifo");
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	/*
+	 * Read one byte from an empty blocking fifo, block as there is no
+	 * data.
+	 */
+	if (set_blocking(reader_fd, __func__) < 0) {
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	ret = timed_read(reader_fd, &ch, sizeof(ch), &len, 5, &timedout,
+	    __func__);
+	if (ret != -1) {
+		warnx("test_blocking_read_empty: timed_read: returned "
+		    "success");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (errno != EINTR) {
+		warn("test_blocking_read_empty: timed_read");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	/*
+	 * Read one byte from an empty non-blocking fifo, return EAGAIN as
+	 * there is no data.
+	 */
+	if (set_nonblocking(reader_fd, __func__) < 0) {
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	ret = timed_read(reader_fd, &ch, sizeof(ch), &len, 5, &timedout,
+	    __func__);
+	if (ret != -1) {
+		warnx("test_blocking_read_empty: timed_read: returned "
+		    "success");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (errno != EAGAIN) {
+		warn("test_blocking_read_empty: timed_read");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * Write one byte to an empty fifo, then try to read one byte and make sure
+ * we don't block in either the write or the read.  This tests both for
+ * improper blocking in the send and receive code.
+ */
+static void
+test_blocking_one_byte(void)
+{
+	int reader_fd, ret, timedout, writer_fd;
+	ssize_t len;
+	u_char ch;
+
+	makefifo("testfifo", __func__);
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("test_blocking: openfifo: testfifo");
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	if (set_blocking(writer_fd, __func__) < 0) {
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (set_blocking(reader_fd, __func__) < 0) {
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	ch = 0xfe;
+	ret = timed_write(writer_fd, &ch, sizeof(ch), &len, 5, &timedout,
+	    __func__);
+	if (ret < 0) {
+		warn("test_blocking_one_byte: timed_write");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != sizeof(ch)) {
+		warnx("test_blocking_one_byte: timed_write: tried to write "
+		    "%zu, wrote %zd", sizeof(ch), len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	ch = 0xab;
+	ret = timed_read(reader_fd, &ch, sizeof(ch), &len, 5, &timedout,
+	    __func__);
+	if (ret < 0) {
+		warn("test_blocking_one_byte: timed_read");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != sizeof(ch)) {
+		warnx("test_blocking_one_byte: timed_read: wanted %zu, "
+		    "read %zd", sizeof(ch), len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (ch != 0xfe) {
+		warnx("test_blocking_one_byte: timed_read: expected to read "
+		    "0x%02x, read 0x%02x", 0xfe, ch);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * Write one byte to an empty fifo, then try to read one byte and make sure
+ * we don't get back EAGAIN.
+ */
+static void
+test_nonblocking_one_byte(void)
+{
+	int reader_fd, ret, timedout, writer_fd;
+	ssize_t len;
+	u_char ch;
+
+	makefifo("testfifo", __func__);
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("test_nonblocking: openfifo: testfifo");
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	if (set_nonblocking(reader_fd, __func__) < 0) {
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	ch = 0xfe;
+	ret = timed_write(writer_fd, &ch, sizeof(ch), &len, 5, &timedout,
+	    __func__);
+	if (ret < 0) {
+		warn("test_nonblocking_one_byte: timed_write");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != sizeof(ch)) {
+		warnx("test_nonblocking_one_byte: timed_write: tried to write "
+		    "%zu, wrote %zd", sizeof(ch), len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	ch = 0xab;
+	ret = timed_read(reader_fd, &ch, sizeof(ch), &len, 5, &timedout,
+	    __func__);
+	if (ret < 0) {
+		warn("test_nonblocking_one_byte: timed_read");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != sizeof(ch)) {
+		warnx("test_nonblocking_one_byte: timed_read: wanted %zu, read "
+		    "%zd", sizeof(ch), len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (ch != 0xfe) {
+		warnx("test_nonblocking_one_byte: timed_read: expected to read "
+		    "0x%02x, read 0x%02x", 0xfe, ch);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * First of two test cases involving a 512K buffer: write the buffer into a
+ * blocking file descriptor.  We'd like to know it blocks, but the closest we
+ * can get is to see if SIGALRM fired during the I/O resulting in a partial
+ * write.
+ */
+static void
+test_blocking_partial_write(void)
+{
+	int reader_fd, ret, timedout, writer_fd;
+	u_char *buffer;
+	ssize_t len;
+
+	makefifo("testfifo", __func__);
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("test_blocking_partial_write: openfifo: testfifo");
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	if (set_blocking(writer_fd, __func__) < 0) {
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	buffer = malloc(512*1024);
+	if (buffer == NULL) {
+		warn("test_blocking_partial_write: malloc");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	bzero(buffer, 512*1024);
+
+	ret = timed_write(writer_fd, buffer, 512*1024, &len, 5, &timedout,
+	    __func__);
+	if (ret < 0) {
+		warn("test_blocking_partial_write: timed_write");
+		free(buffer);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (!timedout) {
+		warnx("test_blocking_partial_write: timed_write: blocking "
+		    "socket didn't time out");
+		free(buffer);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	free(buffer);
+
+	if (drain_fd(reader_fd, __func__) < 0) {
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * Write a 512K buffer to an empty fifo using a non-blocking file descriptor,
+ * and make sure it doesn't block.
+ */
+static void
+test_nonblocking_partial_write(void)
+{
+	int reader_fd, ret, timedout, writer_fd;
+	u_char *buffer;
+	ssize_t len;
+
+	makefifo("testfifo", __func__);
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("test_blocking_partial_write: openfifo: testfifo");
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	if (set_nonblocking(writer_fd, __func__) < 0) {
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	buffer = malloc(512*1024);
+	if (buffer == NULL) {
+		warn("test_blocking_partial_write: malloc");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	bzero(buffer, 512*1024);
+
+	ret = timed_write(writer_fd, buffer, 512*1024, &len, 5, &timedout,
+	    __func__);
+	if (ret < 0) {
+		warn("test_blocking_partial_write: timed_write");
+		free(buffer);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (timedout) {
+		warnx("test_blocking_partial_write: timed_write: "
+		    "non-blocking socket timed out");
+		free(buffer);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (len == 0 || len >= 512*1024) {
+		warnx("test_blocking_partial_write: timed_write: requested "
+		    "%d, sent %zd", 512*1024, len);
+		free(buffer);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	free(buffer);
+
+	if (drain_fd(reader_fd, __func__) < 0) {
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	cleanfifo2("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * test_coalesce_big_read() verifies that data mingles in the fifo across
+ * message boundaries by performing two small writes, then a bigger read
+ * that should return data from both writes.
+ */
+static void
+test_coalesce_big_read(void)
+{
+	int i, reader_fd, writer_fd;
+	u_char buffer[10];
+	ssize_t len;
+
+	makefifo("testfifo", __func__);
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("test_coalesce_big_read: openfifo: testfifo");
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	/* Write five, write five, read ten. */
+	for (i = 0; i < 10; i++)
+		buffer[i] = i;
+
+	len = write(writer_fd, buffer, 5);
+	if (len < 0) {
+		warn("test_coalesce_big_read: write 5");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != 5) {
+		warnx("test_coalesce_big_read: write 5 wrote %zd", len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	len = write(writer_fd, buffer + 5, 5);
+	if (len < 0) {
+		warn("test_coalesce_big_read: write 5");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != 5) {
+		warnx("test_coalesce_big_read: write 5 wrote %zd", len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	len = read(reader_fd, buffer, 10);
+	if (len < 0) {
+		warn("test_coalesce_big_read: read 10");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != 10) {
+		warnx("test_coalesce_big_read: read 10 read %zd", len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	for (i = 0; i < 10; i++) {
+		if (buffer[i] == i)
+			continue;
+		warnx("test_coalesce_big_read: expected to read 0x%02x, "
+		    "read 0x%02x", i, buffer[i]);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	cleanfifo2("testfifo", -1, -1);
+}
+
+/*
+ * test_coalesce_big_write() verifies that data mingles in the fifo across
+ * message boundaries by performing one big write, then two smaller reads
+ * that should return sequential elements of data from the write.
+ */
+static void
+test_coalesce_big_write(void)
+{
+	int i, reader_fd, writer_fd;
+	u_char buffer[10];
+	ssize_t len;
+
+	makefifo("testfifo", __func__);
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("test_coalesce_big_write: openfifo: testfifo");
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	/* Write ten, read five, read five. */
+	for (i = 0; i < 10; i++)
+		buffer[i] = i;
+
+	len = write(writer_fd, buffer, 10);
+	if (len < 0) {
+		warn("test_coalesce_big_write: write 10");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != 10) {
+		warnx("test_coalesce_big_write: write 10 wrote %zd", len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	len = read(reader_fd, buffer, 5);
+	if (len < 0) {
+		warn("test_coalesce_big_write: read 5");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != 5) {
+		warnx("test_coalesce_big_write: read 5 read %zd", len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	len = read(reader_fd, buffer + 5, 5);
+	if (len < 0) {
+		warn("test_coalesce_big_write: read 5");
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (len != 5) {
+		warnx("test_coalesce_big_write: read 5 read %zd", len);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	for (i = 0; i < 10; i++) {
+		if (buffer[i] == i)
+			continue;
+		warnx("test_coalesce_big_write: expected to read 0x%02x, "
+		    "read 0x%02x", i, buffer[i]);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	cleanfifo2("testfifo", -1, -1);
+}
+
+static int
+poll_status(int fd, int *readable, int *writable, int *exception,
+    const char *testname)
+{	
+	struct pollfd fds[1];
+
+	fds[0].fd = fd;
+	fds[0].events = POLLIN | POLLOUT | POLLERR;
+	fds[0].revents = 0;
+
+	if (poll(fds, 1, 0) < 0) {
+		warn("%s: poll", testname);
+		return (-1);
+	}
+	*readable = (fds[0].revents & POLLIN) ? 1 : 0;
+	*writable = (fds[0].revents & POLLOUT) ? 1 : 0;
+	*exception = (fds[0].revents & POLLERR) ? 1 : 0;
+	return (0);
+}
+
+static int
+select_status(int fd, int *readable, int *writable, int *exception,
+    const char *testname)
+{
+	struct fd_set readfds, writefds, exceptfds;
+	struct timeval timeout;
+
+	FD_ZERO(&readfds);
+	FD_ZERO(&writefds);
+	FD_ZERO(&exceptfds);
+	FD_SET(fd, &readfds);
+	FD_SET(fd, &writefds);
+	FD_SET(fd, &exceptfds);
+	timeout.tv_sec = 0;
+	timeout.tv_usec = 0;
+	if (select(fd+1, &readfds, &writefds, &exceptfds, &timeout) < 0) {
+		warn("%s: select", testname);
+		return (-1);
+	}
+	*readable = FD_ISSET(fd, &readfds) ? 1 : 0;
+	*writable = FD_ISSET(fd, &writefds) ? 1 : 0;
+	*exception = FD_ISSET(fd, &exceptfds) ? 1 : 0;
+	return (0);
+}
+
+/*
+ * Given an existing kqueue, set up read and write event filters for the
+ * passed file descriptor.  Typically called once for the read endpoint, and
+ * once for the write endpoint.
+ */
+static int
+kqueue_setup(int kqueue_fd, int fd, const char *testname)
+{
+	struct kevent kevent_changelist[2];
+	struct kevent kevent_eventlist[KQUEUE_MAX_EVENT], *kp;
+	struct timespec timeout;
+	int i, ret;
+
+	timeout.tv_sec = 0;
+	timeout.tv_nsec = 0;
+
+	bzero(&kevent_changelist, sizeof(kevent_changelist));
+	EV_SET(&kevent_changelist[0], fd, EVFILT_READ, EV_ADD, 0, 0, 0);
+	EV_SET(&kevent_changelist[1], fd, EVFILT_WRITE, EV_ADD, 0, 0, 0);
+
+	bzero(&kevent_eventlist, sizeof(kevent_eventlist));
+	ret = kevent(kqueue_fd, kevent_changelist, 2, kevent_eventlist,
+	    KQUEUE_MAX_EVENT, &timeout);
+	if (ret < 0) {
+		warn("%s:%s: kevent initial register", testname, __func__);
+		return (-1);
+	}
+
+	/*
+	 * Verify that the events registered alright.
+	 */
+	for (i = 0; i < ret; i++) {
+		kp = &kevent_eventlist[i];
+		if (kp->flags != EV_ERROR)
+			continue;
+		errno = kp->data;
+		warn("%s:%s: kevent register index %d", testname, __func__,
+		    i);
+		return (-1);
+	}
+
+	return (0);
+}
+
+static int
+kqueue_status(int kqueue_fd, int fd, int *readable, int *writable,
+    int *exception, const char *testname)
+{
+	struct kevent kevent_eventlist[KQUEUE_MAX_EVENT], *kp;
+	struct timespec timeout;
+	int i, ret;
+
+	timeout.tv_sec = 0;
+	timeout.tv_nsec = 0;
+
+	ret = kevent(kqueue_fd, NULL, 0, kevent_eventlist, KQUEUE_MAX_EVENT,
+	    &timeout);
+	if (ret < 0) {
+		warn("%s: %s: kevent", testname, __func__);
+		return (-1);
+	}
+
+	*readable = *writable = *exception = 0;
+	for (i = 0; i < ret; i++) {
+		kp = &kevent_eventlist[i];
+		if (kp->ident != (u_int)fd)
+			continue;
+		if (kp->filter == EVFILT_READ)
+			*readable = 1;
+		if (kp->filter == EVFILT_WRITE)
+			*writable = 1;
+	}
+
+	return (0);
+}
+
+static int
+fionread_status(int fd, int *readable, const char *testname)
+{
+	int i;
+
+	if (ioctl(fd, FIONREAD, &i) < 0) {
+		warn("%s: ioctl(FIONREAD)", testname);
+		return (-1);
+	}
+
+	if (i > 0)
+		*readable = 1;
+	else
+		*readable = 0;
+	return (0);
+}
+
+#define	READABLE	1
+#define	WRITABLE	1
+#define	EXCEPTION	1
+
+#define	NOT_READABLE	0
+#define	NOT_WRITABLE	0
+#define	NOT_EXCEPTION	0
+
+static int
+assert_status(int fd, int kqueue_fd, int assert_readable,
+    int assert_writable, int assert_exception, const char *testname,
+    const char *conditionname, const char *fdname)
+{
+	int readable, writable, exception;
+
+	if (poll_status(fd, &readable, &writable, &exception, testname) < 0)
+		return (-1);
+
+	if (readable != assert_readable || writable != assert_writable ||
+	    exception != assert_exception) {
+		warnx("%s: %s polls r:%d, w:%d, e:%d on %s", testname,
+		    fdname, readable, writable, exception, conditionname);
+		return (-1);
+	}
+
+	if (select_status(fd, &readable, &writable, &exception, testname) < 0)
+		return (-1);
+
+	if (readable != assert_readable || writable != assert_writable ||
+	    exception != assert_exception) {
+		warnx("%s: %s selects r:%d, w:%d, e:%d on %s", testname,
+		    fdname, readable, writable, exception, conditionname);
+		return (-1);
+	}
+
+	if (kqueue_status(kqueue_fd, fd, &readable, &writable, &exception,
+	    testname) < 0)
+		return (-1);
+
+	if (readable != assert_readable || writable != assert_writable ||
+	    exception != assert_exception) {
+		warnx("%s: %s kevent r:%d, w:%d, e:%d on %s", testname,
+		    fdname, readable, writable, exception, conditionname);
+		return (-1);
+	}
+
+	if (fionread_status(fd, &readable, __func__) < 0)
+		return (-1);
+
+	if (readable != assert_readable) {
+		warnx("%s: %s fionread r:%d on %s", testname, fdname,
+		    readable, conditionname);
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * test_events() uses poll(), select(), and kevent() to query the status of
+ * fifo file descriptors and determine whether they match expected state
+ * based on earlier semantic tests: specifically, whether or not poll/select/
+ * kevent will correctly inform on readable/writable state following I/O.
+ *
+ * It would be nice to also test status changes as a result of closing of one
+ * or another fifo endpoint.
+ */
+static void
+test_events_outofbox(void)
+{
+	int kqueue_fd, reader_fd, writer_fd;
+
+	makefifo("testfifo", __func__);
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("test_events_outofbox: openfifo: testfifo");
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	kqueue_fd = kqueue();
+	if (kqueue_fd < 0) {
+		warn("%s: kqueue", __func__);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (kqueue_setup(kqueue_fd, reader_fd, __func__) < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	if (kqueue_setup(kqueue_fd, writer_fd, __func__) < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	/*
+	 * Make sure that fresh, out-of-the-box fifo file descriptors have
+	 * good initial states.  The reader_fd should have no active state,
+	 * since it will not be readable (no data in pipe), writable (it's
+	 * a read-only descriptor), and there's no reason for error yet.
+	 */
+	if (assert_status(reader_fd, kqueue_fd, NOT_READABLE, NOT_WRITABLE,
+	    NOT_EXCEPTION, __func__, "create", "reader_fd") < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	/*
+	 * Make sure that fresh, out-of-the-box fifo file descriptors have
+	 * good initial states.  The writer_fd should be ready to write.
+	 */
+	if (assert_status(writer_fd, kqueue_fd, NOT_READABLE, WRITABLE,
+	    NOT_EXCEPTION, __func__, "create", "writer_fd") < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+}
+
+static void
+test_events_write_read_byte(void)
+{
+	int kqueue_fd, reader_fd, writer_fd;
+	ssize_t len;
+	u_char ch;
+
+	makefifo("testfifo", __func__);
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("test_events_write_read_byte: openfifo: testfifo");
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	kqueue_fd = kqueue();
+	if (kqueue_fd < 0) {
+		warn("%s: kqueue", __func__);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (kqueue_setup(kqueue_fd, reader_fd, __func__) < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	if (kqueue_setup(kqueue_fd, writer_fd, __func__) < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	/*
+	 * Write a byte to the fifo, and make sure that the read end becomes
+	 * readable, and that the write end remains writable (small write).
+	 */
+	ch = 0x00;
+	len = write(writer_fd, &ch, sizeof(ch));
+	if (len < 0) {
+		warn("%s: write", __func__);
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	if (assert_status(reader_fd, kqueue_fd, READABLE, NOT_WRITABLE,
+	    NOT_EXCEPTION, __func__, "write", "reader_fd") < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	/*
+	 * the writer_fd should remain writable.
+	 */
+	if (assert_status(writer_fd, kqueue_fd, NOT_READABLE, WRITABLE,
+	    NOT_EXCEPTION, __func__, "write", "writer_fd") < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	/*
+	 * Read the byte from the reader_fd, and now confirm that that fifo
+	 * becomes unreadable.
+	 */
+	len = read(reader_fd, &ch, sizeof(ch));
+	if (len < 0) {
+		warn("%s: read", __func__);
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	if (assert_status(reader_fd, kqueue_fd, NOT_READABLE, NOT_WRITABLE,
+	    NOT_EXCEPTION, __func__, "write+read", "reader_fd") < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	/*
+	 * The writer_fd should remain writable.
+	 */
+	if (assert_status(writer_fd, kqueue_fd, NOT_READABLE, WRITABLE,
+	    NOT_EXCEPTION, __func__, "write+read", "writer_fd") < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+}
+
+/*
+ * Write a 512k buffer to the fifo in non-blocking mode, and make sure that
+ * the write end becomes un-writable as a result of a partial write that
+ * fills the fifo buffer.
+ */
+static void
+test_events_partial_write(void)
+{
+	int kqueue_fd, reader_fd, writer_fd;
+	u_char *buffer;
+	ssize_t len;
+
+	makefifo("testfifo", __func__);
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("test_events_partial_write: openfifo: testfifo");
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	kqueue_fd = kqueue();
+	if (kqueue_fd < 0) {
+		warn("%s: kqueue", __func__);
+		cleanfifo2("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (kqueue_setup(kqueue_fd, reader_fd, __func__) < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	if (kqueue_setup(kqueue_fd, writer_fd, __func__) < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	if (set_nonblocking(writer_fd, "test_events") < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	buffer = malloc(512*1024);
+	if (buffer == NULL) {
+		warn("test_events_partial_write: malloc");
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+	bzero(buffer, 512*1024);
+
+	len = write(writer_fd, buffer, 512*1024);
+	if (len < 0) {
+		warn("test_events_partial_write: write");
+		free(buffer);
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	free(buffer);
+
+	if (assert_status(writer_fd, kqueue_fd, NOT_READABLE, NOT_WRITABLE,
+	    NOT_EXCEPTION, __func__, "big write", "writer_fd") < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	if (drain_fd(reader_fd, "test_events") < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	/*
+	 * Test that the writer_fd has been restored to writable state after
+	 * draining.
+	 */
+	if (assert_status(writer_fd, kqueue_fd, NOT_READABLE, WRITABLE,
+	    NOT_EXCEPTION, __func__, "big write + drain", "writer_fd") < 0) {
+		cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+		exit(-1);
+	}
+
+	cleanfifo3("testfifo", reader_fd, writer_fd, kqueue_fd);
+}
+
+/*
+ * We don't comprehensively test O_RDWR file descriptors, but do run a couple
+ * of event tests to make sure that the fifo implementation doesn't mixed up
+ * status checks.  In particular, at least one past FreeBSD bug exists in
+ * which the FIONREAD test was performed on the wrong socket implementing the
+ * fifo, resulting in the fifo never returning readable.
+ */
+static void
+test_events_rdwr(void)
+{
+	int fd, kqueue_fd;
+	ssize_t len;
+	char ch;
+
+	makefifo("testfifo", __func__);
+	if (openfifo_rw("testfifo", &fd) < 0) {
+		warn("%s: openfifo_rw: testfifo", __func__);
+		cleanfifo2("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	kqueue_fd = kqueue();
+	if (kqueue_fd < 0) {
+		warn("%s: kqueue", __func__);
+		cleanfifo2("testifo", fd, -1);
+		exit(-1);
+	}
+
+	if (kqueue_setup(kqueue_fd, fd, __func__) < 0) {
+		cleanfifo2("testfifo", fd, kqueue_fd);
+		exit(-1);
+	}
+
+	/*
+	 * On first creation, the O_RDWR descriptor should be writable but
+	 * not readable.
+	 */
+	if (assert_status(fd, kqueue_fd, NOT_READABLE, WRITABLE,
+	    NOT_EXCEPTION, __func__, "create", "fd") < 0) {
+		cleanfifo2("testfifo", fd, kqueue_fd);
+		exit(-1);
+	}
+
+	/*
+	 * Write a byte, which should cause the file descriptor to become
+	 * readable and writable.
+	 */
+	ch = 0x00;
+	len = write(fd, &ch, sizeof(ch));
+	if (len < 0) {
+		warn("%s: write", __func__);
+		cleanfifo2("testfifo", fd, kqueue_fd);
+		exit(-1);
+	}
+
+	if (assert_status(fd, kqueue_fd, READABLE, WRITABLE, NOT_EXCEPTION,
+	    __func__, "write", "fd") < 0) {
+		cleanfifo2("testfifo", fd, kqueue_fd);
+		exit(-1);
+	}
+
+	/*
+	 * Read a byte, which should cause the file descriptor to return to
+	 * simply being writable.
+	 */
+	len = read(fd, &ch, sizeof(ch));
+	if (len < 0) {
+		warn("%s: read", __func__);
+		cleanfifo2("testfifo", fd, kqueue_fd);
+		exit(-1);
+	}
+
+	if (assert_status(fd, kqueue_fd, NOT_READABLE, WRITABLE,
+	    NOT_EXCEPTION, __func__, "write+read", "fd") < 0) {
+		cleanfifo2("testfifo", fd, kqueue_fd);
+		exit(-1);
+	}
+
+	cleanfifo2("testfifo", fd, kqueue_fd);
+}
+
+int
+main(void)
+{
+
+	strcpy(temp_dir, "fifo_io.XXXXXXXXXXX");
+	if (mkdtemp(temp_dir) == NULL)
+		err(-1, "mkdtemp");
+	atexit(atexit_temp_dir);
+
+	if (chdir(temp_dir) < 0)
+		err(-1, "chdir %s", temp_dir);
+
+	test_simpleio();
+	test_blocking_read_empty();
+	test_blocking_one_byte();
+	test_nonblocking_one_byte();
+	test_blocking_partial_write();
+	test_nonblocking_partial_write();
+	test_coalesce_big_read();
+	test_coalesce_big_write();
+	test_events_outofbox();
+	test_events_write_read_byte();
+	test_events_partial_write();
+	test_events_rdwr();
+
+	return (0);
+}


Property changes on: trunk/tests/sys/fifo/fifo_io.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/tests/sys/fifo/fifo_misc.c
===================================================================
--- trunk/tests/sys/fifo/fifo_misc.c	                        (rev 0)
+++ trunk/tests/sys/fifo/fifo_misc.c	2018-07-21 20:17:56 UTC (rev 11953)
@@ -0,0 +1,337 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * Copyright (c) 2012 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/10/tests/sys/fifo/fifo_misc.c 286612 2015-08-11 01:51:38Z rodrigc $
+ */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/filio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Regression test for piddling details of fifos.
+ */
+
+/*
+ * All activity occurs within a temporary directory created early in the
+ * test.
+ */
+static char	temp_dir[PATH_MAX];
+
+static void __unused
+atexit_temp_dir(void)
+{
+
+	rmdir(temp_dir);
+}
+
+static void
+makefifo(const char *fifoname, const char *testname)
+{
+
+	if (mkfifo(fifoname, 0700) < 0)
+		err(-1, "%s: makefifo: mkfifo: %s", testname, fifoname);
+}
+
+static void
+cleanfifo(const char *fifoname, int fd1, int fd2)
+{
+
+	if (fd1 != -1)
+		close(fd1);
+	if (fd2 != -1)
+		close(fd2);
+	(void)unlink(fifoname);
+}
+
+static int
+openfifo(const char *fifoname, int *reader_fdp, int *writer_fdp)
+{
+	int error, fd1, fd2;
+
+	fd1 = open(fifoname, O_RDONLY | O_NONBLOCK);
+	if (fd1 < 0)
+		return (-1);
+	fd2 = open(fifoname, O_WRONLY | O_NONBLOCK);
+	if (fd2 < 0) {
+		error = errno;
+		close(fd1);
+		errno = error;
+		return (-1);
+	}
+	*reader_fdp = fd1;
+	*writer_fdp = fd2;
+
+	return (0);
+}
+
+/*
+ * POSIX does not allow lseek(2) on fifos, so we expect ESPIPE as a result.
+ */
+static void
+test_lseek(void)
+{
+	int reader_fd, writer_fd;
+
+	makefifo("testfifo", __func__);
+
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("%s: openfifo", __func__);
+		cleanfifo("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	if (lseek(reader_fd, 1, SEEK_CUR) >= 0) {
+		warnx("%s: lseek succeeded instead of returning ESPIPE",
+		    __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (errno != ESPIPE) {
+		warn("%s: lseek returned instead of ESPIPE", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	cleanfifo("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * truncate(2) on FIFO should silently return success.
+ */
+static void
+test_truncate(void)
+{
+
+	makefifo("testfifo", __func__);
+
+	if (truncate("testfifo", 1024) != 0) {
+		warn("%s: truncate", __func__);
+		cleanfifo("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	cleanfifo("testfifo", -1, -1);
+}
+
+static int
+test_ioctl_setclearflag(int fd, unsigned long flag, const char *testname,
+    const char *fdname, const char *flagname)
+{
+	int i;
+
+	i = 1;
+	if (ioctl(fd, flag, &i) < 0) {
+		warn("%s:%s: ioctl(%s, %s, 1)", testname, __func__, fdname,
+		    flagname);
+		cleanfifo("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	i = 0;
+	if (ioctl(fd, flag, &i) < 0) {
+		warn("%s:%s: ioctl(%s, %s, 0)", testname, __func__, fdname,
+		    flagname);
+		cleanfifo("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	return (0);
+}
+
+/*
+ * Test that various ioctls can be issued against the file descriptor.  We
+ * don't currently test the semantics of these changes here.
+ */
+static void
+test_ioctl(void)
+{
+	int reader_fd, writer_fd;
+
+	makefifo("testfifo", __func__);
+
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("%s: openfifo", __func__);
+		cleanfifo("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	/*
+	 * Set and remove the non-blocking I/O flag.
+	 */
+	if (test_ioctl_setclearflag(reader_fd, FIONBIO, __func__,
+	    "reader_fd", "FIONBIO") < 0) {
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (test_ioctl_setclearflag(writer_fd, FIONBIO, __func__,
+	    "writer_fd", "FIONBIO") < 0) {
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	/*
+	 * Set and remove the async I/O flag.
+	 */
+	if (test_ioctl_setclearflag(reader_fd, FIOASYNC, __func__,
+	    "reader_fd", "FIOASYNC") < 0) {
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (test_ioctl_setclearflag(writer_fd, FIOASYNC, __func__,
+	    "writer_fd", "FIONASYNC") < 0) {
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	cleanfifo("testfifo", reader_fd, writer_fd);
+}
+
+/*
+ * fchmod(2)/fchown(2) on FIFO should work.
+ */
+static void
+test_chmodchown(void)
+{
+	struct stat sb;
+	int reader_fd, writer_fd;
+	uid_t u;
+	gid_t g;
+
+	makefifo("testfifo", __func__);
+
+	if (openfifo("testfifo", &reader_fd, &writer_fd) < 0) {
+		warn("%s: openfifo", __func__);
+		cleanfifo("testfifo", -1, -1);
+		exit(-1);
+	}
+
+	if (fchmod(reader_fd, 0666) != 0) {
+		warn("%s: fchmod", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (stat("testfifo", &sb) != 0) {
+		warn("%s: stat", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if ((sb.st_mode & 0777) != 0666) {
+		warnx("%s: stat chmod result", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (fstat(writer_fd, &sb) != 0) {
+		warn("%s: fstat", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if ((sb.st_mode & 0777) != 0666) {
+		warnx("%s: fstat chmod result", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (fchown(reader_fd, -1, -1) != 0) {
+		warn("%s: fchown 1", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	u = geteuid();
+	if (u == 0)
+		u = 1;
+	g = getegid();
+	if (fchown(reader_fd, u, g) != 0) {
+		warn("%s: fchown 2", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+	if (stat("testfifo", &sb) != 0) {
+		warn("%s: stat", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (sb.st_uid != u || sb.st_gid != g) {
+		warnx("%s: stat chown result", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (fstat(writer_fd, &sb) != 0) {
+		warn("%s: fstat", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	if (sb.st_uid != u || sb.st_gid != g) {
+		warnx("%s: fstat chown result", __func__);
+		cleanfifo("testfifo", reader_fd, writer_fd);
+		exit(-1);
+	}
+
+	cleanfifo("testfifo", -1, -1);
+}
+
+int
+main(void)
+{
+
+	strcpy(temp_dir, "fifo_misc.XXXXXXXXXXX");
+	if (mkdtemp(temp_dir) == NULL)
+		err(-1, "mkdtemp");
+	atexit(atexit_temp_dir);
+
+	if (chdir(temp_dir) < 0)
+		err(-1, "chdir %s", temp_dir);
+
+	test_lseek();
+	test_truncate();
+	test_ioctl();
+	test_chmodchown();
+
+	return (0);
+}


Property changes on: trunk/tests/sys/fifo/fifo_misc.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/tests/sys/fifo/fifo_open.c
===================================================================
--- trunk/tests/sys/fifo/fifo_open.c	                        (rev 0)
+++ trunk/tests/sys/fifo/fifo_open.c	2018-07-21 20:17:56 UTC (rev 11953)
@@ -0,0 +1,477 @@
+/* $MidnightBSD$ */
+/*-
+ * Copyright (c) 2005 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: stable/10/tests/sys/fifo/fifo_open.c 281450 2015-04-12 06:18:24Z ngie $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * Regression test to exercise various POSIX-defined parts of fifo behavior
+ * described for open(2):
+ *
+ * O_NONBLOCK
+ * When opening a FIFO with O_RDONLY or O_WRONLY set:
+ *
+ * - If O_NONBLOCK is set, an open() for reading-only shall return without
+ *   delay. An open() for writing-only shall return an error if no process
+ *   currently has the file open for reading.
+ *
+ * - If O_NONBLOCK is clear, an open() for reading-only shall block the
+ *   calling thread until a thread opens the file for writing. An open()
+ *   for writing-only shall block the calling thread until a thread opens
+ *   the file for reading.
+ *
+ * When opening a block special or character special file that supports
+ * non-blocking opens:
+ *
+ * - If O_NONBLOCK is set, the open() function shall return without blocking
+ *   for the device to be ready or available. Subsequent behavior of the
+ *   device is device-specific.
+ *
+ * - If O_NONBLOCK is clear, the open() function shall block the calling
+ *   thread until the device is ready or available before returning.
+ *
+ * Special errors:
+ *
+ * [ENXIO]
+ * O_NONBLOCK is set, the named file is a FIFO, O_WRONLY is set, and no
+ * process has the file open for reading.
+ */
+
+/*
+ * In order to test blocking/non-blocking behavior, test processes must
+ * potentially block themselves until released.  As bugs in blocking result
+ * in processes that won't un-block, we must sacrifice a process to the task,
+ * watching and potentially killing it after a time-out.  The main test
+ * process is never used to open or act directly on a fifo (other than to
+ * create or unlink it) in order to avoid the main test process being
+ * blocked.
+ */
+
+/*
+ * All activity occurs within a temporary directory created early in the
+ * test.
+ */
+static char	temp_dir[PATH_MAX];
+
+static void __unused
+atexit_temp_dir(void)
+{
+
+	rmdir(temp_dir);
+}
+
+/*
+ * Run a function in a particular test process.
+ */
+static int
+run_in_process(int (*func)(void), pid_t *pidp, const char *errstr)
+{
+	pid_t pid;
+
+	pid = fork();
+	if (pid < 0) {
+		warn("%s: run_in_process: fork", errstr);
+		return (-1);
+	}
+
+	if (pid == 0)
+		exit(func());
+
+	if (pidp != NULL)
+		*pidp = pid;
+
+	return (0);
+}
+
+/*
+ * Wait for a process on a timeout, and if the timeout expires, kill the
+ * process.  Test each second rather than waiting the full timeout at once to
+ * minimize the amount of time spent hanging around unnecessarily.
+ */
+static int
+wait_and_timeout(pid_t pid, int timeout, int *status, const char *errstr)
+{
+	pid_t wpid;
+	int i;
+
+	/*
+	 * Count up to the timeout, but do a non-hanging waitpid() after each
+	 * second so we can avoid waiting a lot of extra time.
+	 */
+	for (i = 0; i < timeout; i++) {
+		wpid = waitpid(pid, status, WNOHANG);
+		if (wpid < 0) {
+			warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
+			return (-1);
+		}
+
+		if (wpid == pid)
+			return (0);
+
+		sleep(1);
+	}
+
+	wpid = waitpid(pid, status, WNOHANG);
+	if (wpid < 0) {
+		warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
+		return (-1);
+	}
+
+	if (wpid == pid)
+		return (0);
+
+	if (kill(pid, SIGTERM) < 0) {
+		warn("%s: wait_and_timeout: kill %d", errstr, pid);
+		return (-1);
+	}
+
+	wpid = waitpid(pid, status, 0);
+	if (wpid < 0) {
+		warn("%s: wait_and_timeout: waitpid %d", errstr, pid);
+		return (-1);
+	}
+
+	if (wpid != pid) {
+		warn("%s: waitpid: returned %d not %d", errstr, wpid, pid);
+		return (-1);
+	}
+
+	warnx("%s: process blocked", errstr);
+	return (-1);
+}
+
+static int
+non_blocking_open_reader(void)
+{
+	int fd;
+
+	fd = open("testfifo", O_RDONLY | O_NONBLOCK);
+	if (fd < 0)
+		return (errno);
+	close(fd);
+
+	return (0);
+}
+
+static int
+non_blocking_open_writer(void)
+{
+	int fd;
+
+	fd = open("testfifo", O_WRONLY | O_NONBLOCK);
+	if (fd < 0)
+		return (errno);
+	close(fd);
+
+	return (0);
+}
+
+static int
+blocking_open_reader(void)
+{
+	int fd;
+
+	fd = open("testfifo", O_RDONLY);
+	if (fd < 0)
+		return (errno);
+	close(fd);
+
+	return (0);
+}
+
+static int
+blocking_open_writer(void)
+{
+	int fd;
+
+	fd = open("testfifo", O_WRONLY);
+	if (fd < 0)
+		return (errno);
+	close(fd);
+
+	return (0);
+}
+
+static void
+test_blocking_reader(void)
+{
+	pid_t reader_pid, writer_pid, wpid;
+	int error, status;
+
+	if (mkfifo("testfifo", 0600) < 0)
+		err(-1, "test_blocking_reader: mkfifo: testfifo");
+
+	/*
+	 * Block a process in opening the fifo.
+	 */
+	if (run_in_process(blocking_open_reader, &reader_pid,
+	    "test_blocking_reader: blocking_open_reader") < 0) {
+		(void)unlink("testfifo");
+		exit(-1);
+	}
+
+	/*
+	 * Test that it blocked.
+	 */
+	sleep(5);
+	wpid = waitpid(reader_pid, &status, WNOHANG);
+	if (wpid < 0) {
+		error = errno;
+		(void)unlink("testfifo");
+		errno = error;
+		err(-1, "test_blocking_reader: waitpid %d", reader_pid);
+	}
+
+	if (wpid != 0 && wpid != reader_pid) {
+		(void)unlink("testfifo");
+		errx(-1, "test_blocking_reader: waitpid %d returned %d",
+		    reader_pid, wpid);
+	}
+
+	if (wpid == reader_pid) {
+		(void)unlink("testfifo");
+		errx(-1, "test_blocking_reader: blocking child didn't "
+		    "block");
+	}
+
+	/*
+	 * Unblock the blocking reader.
+	 */
+	if (run_in_process(blocking_open_writer, &writer_pid,
+	    "test_blocking_reader: blocking_open_writer") < 0) {
+		(void)unlink("testfifo");
+		(void)kill(reader_pid, SIGTERM);
+		(void)waitpid(reader_pid, &status, 0);
+		exit(-1);
+	}
+
+	/*
+	 * Make sure both processes exited quickly (<1 second) to make sure
+	 * they didn't block, and GC.
+	 */
+	if (wait_and_timeout(reader_pid, 1, &status,
+	    "test_blocking_reader: blocking_open_reader") < 0) {
+		(void)unlink("testinfo");
+		(void)kill(reader_pid, SIGTERM);
+		(void)kill(writer_pid, SIGTERM);
+		exit(-1);
+	}
+	
+	if (wait_and_timeout(writer_pid, 1, &status,
+	    "test_blocking_reader: blocking_open_writer") < 0) {
+		(void)unlink("testinfo");
+		(void)kill(writer_pid, SIGTERM);
+		exit(-1);
+	}
+	
+	if (unlink("testfifo") < 0)
+		err(-1, "test_blocking_reader: unlink: testfifo");
+}
+static void
+test_blocking_writer(void)
+{
+	pid_t reader_pid, writer_pid, wpid;
+	int error, status;
+
+	if (mkfifo("testfifo", 0600) < 0)
+		err(-1, "test_blocking_writer: mkfifo: testfifo");
+
+	/*
+	 * Block a process in opening the fifo.
+	 */
+	if (run_in_process(blocking_open_writer, &writer_pid,
+	    "test_blocking_writer: blocking_open_writer") < 0) {
+		(void)unlink("testfifo");
+		exit(-1);
+	}
+
+	/*
+	 * Test that it blocked.
+	 */
+	sleep(5);
+	wpid = waitpid(writer_pid, &status, WNOHANG);
+	if (wpid < 0) {
+		error = errno;
+		(void)unlink("testfifo");
+		errno = error;
+		err(-1, "test_blocking_writer: waitpid %d", writer_pid);
+	}
+
+	if (wpid != 0 && wpid != writer_pid) {
+		(void)unlink("testfifo");
+		errx(-1, "test_blocking_writer: waitpid %d returned %d",
+		    writer_pid, wpid);
+	}
+
+	if (wpid == writer_pid) {
+		(void)unlink("testfifo");
+		errx(-1, "test_blocking_writer: blocking child didn't "
+		    "block");
+	}
+
+	/*
+	 * Unblock the blocking writer.
+	 */
+	if (run_in_process(blocking_open_reader, &reader_pid,
+	    "test_blocking_writer: blocking_open_reader") < 0) {
+		(void)unlink("testfifo");
+		(void)kill(writer_pid, SIGTERM);
+		(void)waitpid(writer_pid, &status, 0);
+		exit(-1);
+	}
+
+	/*
+	 * Make sure both processes exited quickly (<1 second) to make sure
+	 * they didn't block, and GC.
+	 */
+	if (wait_and_timeout(writer_pid, 1, &status,
+	    "test_blocking_writer: blocking_open_writer") < 0) {
+		(void)unlink("testinfo");
+		(void)kill(writer_pid, SIGTERM);
+		(void)kill(reader_pid, SIGTERM);
+		(void)waitpid(writer_pid, &status, 0);
+		(void)waitpid(reader_pid, &status, 0);
+		exit(-1);
+	}
+	
+	if (wait_and_timeout(reader_pid, 1, &status,
+	    "test_blocking_writer: blocking_open_reader") < 0) {
+		(void)unlink("testinfo");
+		(void)kill(reader_pid, SIGTERM);
+		(void)waitpid(reader_pid, &status, 0);
+		exit(-1);
+	}
+	
+	if (unlink("testfifo") < 0)
+		err(-1, "test_blocking_writer: unlink: testfifo");
+}
+
+static void
+test_non_blocking_reader(void)
+{
+	int status;
+	pid_t pid;
+
+	if (mkfifo("testfifo", 0600) < 0)
+		err(-1, "test_non_blocking_reader: mkfifo: testfifo");
+
+	if (run_in_process(non_blocking_open_reader, &pid,
+	    "test_non_blocking_reader: non_blocking_open_reader") < 0) {
+		(void)unlink("testfifo");
+		exit(-1);
+	}
+
+	status = -1;
+	if (wait_and_timeout(pid, 5, &status,
+	    "test_non_blocking_reader: non_blocking_open_reader") < 0) {
+		(void)unlink("testfifo");
+		exit(-1);
+	}
+
+	if (WEXITSTATUS(status) != 0) {
+		(void)unlink("testfifo");
+		errno = WEXITSTATUS(status);
+		err(-1, "test_non_blocking_reader: "
+		    "non_blocking_open_reader: open: testfifo");
+	}
+
+	if (unlink("testfifo") < 0)
+		err(-1, "test_non_blocking_reader: unlink: testfifo");
+}
+
+static void
+test_non_blocking_writer(void)
+{
+	int status;
+	pid_t pid;
+
+	if (mkfifo("testfifo", 0600) < 0)
+		err(-1, "test_non_blocking_writer: mkfifo: testfifo");
+
+	if (run_in_process(non_blocking_open_writer, &pid,
+	    "test_non_blocking_writer: non_blocking_open_writer") < 0) {
+		(void)unlink("testfifo");
+		exit(-1);
+	}
+
+	status = -1;
+	if (wait_and_timeout(pid, 5, &status,
+	    "test_non_blocking_writer: non_blocking_open_writer") < 0) {
+		(void)unlink("testfifo");
+		exit(-1);
+	}
+
+	if (WEXITSTATUS(status) != ENXIO) {
+		(void)unlink("testfifo");
+
+		errno = WEXITSTATUS(status);
+		if (errno == 0)
+			errx(-1, "test_non_blocking_writer: "
+			    "non_blocking_open_writer: open succeeded");
+		err(-1, "test_non_blocking_writer: "
+		    "non_blocking_open_writer: open: testfifo");
+	}
+
+	if (unlink("testfifo") < 0)
+		err(-1, "test_non_blocking_writer: unlink: testfifo");
+}
+
+int
+main(void)
+{
+
+	if (geteuid() != 0)
+		errx(-1, "must be run as root");
+
+	strcpy(temp_dir, "fifo_open.XXXXXXXXXXX");
+	if (mkdtemp(temp_dir) == NULL)
+		err(-1, "mkdtemp");
+	if (chdir(temp_dir) < 0)
+		err(-1, "chdir: %s", temp_dir);
+	atexit(atexit_temp_dir);
+
+	test_non_blocking_reader();
+	test_non_blocking_writer();
+
+	test_blocking_reader();
+	test_blocking_writer();
+
+	return (0);
+}


Property changes on: trunk/tests/sys/fifo/fifo_open.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property


More information about the Midnightbsd-cvs mailing list