xref: /openbsd-src/regress/sys/fifofs/fifotest.c (revision 49a6e16f2c2c8e509184b1f777366d1a6f337e1c)
10e93eeb0Smillert /*
2bf198cc6Smillert  * Copyright (c) 2004, 2014-2015 Todd C. Miller <millert@openbsd.org>
30e93eeb0Smillert  *
40e93eeb0Smillert  * Permission to use, copy, modify, and distribute this software for any
50e93eeb0Smillert  * purpose with or without fee is hereby granted, provided that the above
60e93eeb0Smillert  * copyright notice and this permission notice appear in all copies.
70e93eeb0Smillert  *
80e93eeb0Smillert  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
90e93eeb0Smillert  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
100e93eeb0Smillert  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
110e93eeb0Smillert  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
120e93eeb0Smillert  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
130e93eeb0Smillert  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
140e93eeb0Smillert  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
150e93eeb0Smillert  */
160e93eeb0Smillert 
17*49a6e16fSderaadt #include <sys/types.h>
18aa90103aSmillert #include <sys/stat.h>
1995424978Smillert #include <sys/wait.h>
20aa90103aSmillert #include <errno.h>
21aa90103aSmillert #include <fcntl.h>
2295424978Smillert #include <poll.h>
23aa90103aSmillert #include <signal.h>
24aa90103aSmillert #include <stdio.h>
25aa90103aSmillert #include <stdlib.h>
26aa90103aSmillert #include <string.h>
27aa90103aSmillert #include <unistd.h>
28aa90103aSmillert 
292277d5f8Smillert #ifndef INFTIM
302277d5f8Smillert #define	INFTIM	-1
312277d5f8Smillert #endif
322277d5f8Smillert 
33aa90103aSmillert void usage(void);
34aa90103aSmillert void sigalrm(int);
352ddff956Smillert void sigusr1(int);
362ddff956Smillert void dopoll(pid_t, int, int, char *, int);
372ddff956Smillert void doselect(pid_t, int, int, int);
382277d5f8Smillert void runtest(char *, int, int);
3995424978Smillert void eoftest(char *, int, int);
40aa90103aSmillert 
410e93eeb0Smillert #if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
420e93eeb0Smillert     defined(__linux__)
43aa90103aSmillert extern char *__progname;
44aa90103aSmillert #else
45aa90103aSmillert char *__progname;
46aa90103aSmillert #endif
47aa90103aSmillert 
48aa90103aSmillert /*
490e93eeb0Smillert  * Test FIFOs and poll(2) both with an emtpy and full FIFO.
50aa90103aSmillert  */
512277d5f8Smillert int
main(int argc,char ** argv)522277d5f8Smillert main(int argc, char **argv)
53aa90103aSmillert {
5495424978Smillert 	struct sigaction sa;
552277d5f8Smillert #if !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \
562277d5f8Smillert     !defined(__linux__)
57aa90103aSmillert 	__progname = argv[0];
58aa90103aSmillert #endif
59aa90103aSmillert 	if (argc != 2)
60aa90103aSmillert 		usage();
61aa90103aSmillert 
622ddff956Smillert 	/* Just want EINTR from SIGALRM */
6395424978Smillert 	sigemptyset(&sa.sa_mask);
6495424978Smillert 	sa.sa_flags = 0;
6595424978Smillert 	sa.sa_handler = sigalrm;
6695424978Smillert 	sigaction(SIGALRM, &sa, NULL);
6795424978Smillert 
685f5b07b3Smiod 	/* SIGUSR1 is used for synchronization only. */
692ddff956Smillert 	sa.sa_flags = SA_RESTART;
702ddff956Smillert 	sa.sa_handler = sigusr1;
712ddff956Smillert 	sigaction(SIGUSR1, &sa, NULL);
722ddff956Smillert 
732277d5f8Smillert 	runtest(argv[1], 0, 0);
742277d5f8Smillert 	runtest(argv[1], 0, INFTIM);
752277d5f8Smillert 	runtest(argv[1], O_NONBLOCK, 0);
762277d5f8Smillert 	runtest(argv[1], O_NONBLOCK, INFTIM);
7795424978Smillert 	eoftest(argv[1], O_NONBLOCK, INFTIM);
782277d5f8Smillert 
792277d5f8Smillert 	exit(0);
802277d5f8Smillert }
812277d5f8Smillert 
822277d5f8Smillert void
runtest(char * fifo,int flags,int timeout)832277d5f8Smillert runtest(char *fifo, int flags, int timeout)
842277d5f8Smillert {
852277d5f8Smillert 	ssize_t nread;
862277d5f8Smillert 	int fd;
872277d5f8Smillert 	char buf[BUFSIZ];
882277d5f8Smillert 
89aa90103aSmillert 	(void)unlink(fifo);
90aa90103aSmillert 	if (mkfifo(fifo, 0644) != 0) {
91aa90103aSmillert 		printf("mkfifo %s: %s\n", fifo, strerror(errno));
92aa90103aSmillert 		exit(1);
93aa90103aSmillert 	}
94aa90103aSmillert 
952ddff956Smillert 	/* Note: O_RDWR not required by POSIX */
96aa90103aSmillert 	alarm(2);
9717ec856cSderaadt 	if ((fd = open(fifo, O_RDWR | flags)) == -1) {
98aa90103aSmillert 		printf("open %s: %s\n", fifo, strerror(errno));
99aa90103aSmillert 		exit(1);
100aa90103aSmillert 	}
101aa90103aSmillert 	alarm(0);
102aa90103aSmillert 	(void)unlink(fifo);
1032277d5f8Smillert 	printf("\nOpened fifo %s%s\n", fifo,
1042277d5f8Smillert 	    (flags & O_NONBLOCK) ? " (nonblocking)" : "");
105aa90103aSmillert 
106aa90103aSmillert 	printf("\nTesting empty FIFO:\n");
1072ddff956Smillert 	dopoll(-1, fd, POLLIN|POLLOUT, "POLLIN|POLLOUT", timeout);
1082ddff956Smillert 	dopoll(-1, fd, POLLIN, "POLLIN", timeout);
1092ddff956Smillert 	dopoll(-1, fd, POLLOUT, "POLLOUT", timeout);
1102ddff956Smillert 	dopoll(-1, fd, 0, "(none)", timeout);
1112ddff956Smillert 	doselect(-1, fd, fd, timeout);
1122ddff956Smillert 	doselect(-1, fd, -1, timeout);
1132ddff956Smillert 	doselect(-1, -1, fd, timeout);
1142ddff956Smillert 	doselect(-1, -1, -1, timeout);
115aa90103aSmillert 
116aa90103aSmillert 	if (write(fd, "test", 4) != 4) {
117aa90103aSmillert 		printf("write error: %s\n", strerror(errno));
118aa90103aSmillert 		exit(1);
119aa90103aSmillert 	}
120aa90103aSmillert 
121aa90103aSmillert 	printf("\nTesting full FIFO:\n");
1222ddff956Smillert 	dopoll(-1, fd, POLLIN|POLLOUT, "POLLIN|POLLOUT", timeout);
1232ddff956Smillert 	dopoll(-1, fd, POLLIN, "POLLIN", timeout);
1242ddff956Smillert 	dopoll(-1, fd, POLLOUT, "POLLOUT", timeout);
1252ddff956Smillert 	dopoll(-1, fd, 0, "(none)", timeout);
1262ddff956Smillert 	doselect(-1, fd, fd, timeout);
1272ddff956Smillert 	doselect(-1, fd, -1, timeout);
1282ddff956Smillert 	doselect(-1, -1, fd, timeout);
1292ddff956Smillert 	doselect(-1, -1, -1, timeout);
130aa90103aSmillert 
131aa90103aSmillert 	if ((nread = read(fd, buf, sizeof(buf))) <= 0) {
132aa90103aSmillert 		printf("read error: %s\n", (nread == 0) ? "EOF" : strerror(errno));
133aa90103aSmillert 		exit(1);
134aa90103aSmillert 	}
135aa90103aSmillert 	buf[nread] = '\0';
136aa90103aSmillert 	printf("\treceived '%s' from FIFO\n", buf);
137aa90103aSmillert }
138aa90103aSmillert 
1392ddff956Smillert pid_t
eof_writer(const char * fifo,int flags)14095424978Smillert eof_writer(const char *fifo, int flags)
14195424978Smillert {
14295424978Smillert 	int fd;
1432ddff956Smillert 	pid_t pid;
1442ddff956Smillert 	sigset_t mask, omask;
14595424978Smillert 
1462ddff956Smillert 	/* Block SIGUSR1 (in child). */
1472ddff956Smillert 	sigemptyset(&mask);
1482ddff956Smillert 	sigaddset(&mask, SIGUSR1);
1492ddff956Smillert 	sigprocmask(SIG_BLOCK, &mask, &omask);
1502ddff956Smillert 
1512ddff956Smillert 	switch ((pid = fork())) {
15295424978Smillert 	case -1:
15395424978Smillert 		printf("fork: %s\n", strerror(errno));
15495424978Smillert 		return -1;
15595424978Smillert 	case 0:
15695424978Smillert 		/* child */
15795424978Smillert 		break;
15895424978Smillert 	default:
15995424978Smillert 		/* parent */
1602ddff956Smillert 		sigprocmask(SIG_SETMASK, &omask, NULL);
1612ddff956Smillert 		return pid;
16295424978Smillert 	}
16395424978Smillert 
1642ddff956Smillert 	/* Wait for reader. */
1652ddff956Smillert 	sigemptyset(&mask);
1662ddff956Smillert 	sigsuspend(&mask);
1672ddff956Smillert 	sigprocmask(SIG_SETMASK, &omask, NULL);
1682ddff956Smillert 
1692ddff956Smillert 	/* connect to FIFO. */
17095424978Smillert 	alarm(2);
17117ec856cSderaadt 	fd = open(fifo, O_WRONLY | flags);
17295424978Smillert 	alarm(0);
1732ddff956Smillert 	if (fd == -1) {
17495424978Smillert 		printf("open %s O_WRONLY: %s\n", fifo, strerror(errno));
17595424978Smillert 		return -1;
17695424978Smillert 	}
17795424978Smillert 
17895424978Smillert 	/*
1792ddff956Smillert 	 * We need to give the reader time to call poll() or select()
1802ddff956Smillert 	 * before we close the fd.  This is racey...
18195424978Smillert 	 */
18295424978Smillert 	usleep(100000);
18395424978Smillert 	close(fd);
18495424978Smillert 	_exit(0);
18595424978Smillert }
18695424978Smillert 
18795424978Smillert void
eoftest(char * fifo,int flags,int timeout)18895424978Smillert eoftest(char *fifo, int flags, int timeout)
18995424978Smillert {
19095424978Smillert 	ssize_t nread;
1912ddff956Smillert 	int fd = -1, pass, status;
1922ddff956Smillert 	pid_t writer;
19395424978Smillert 	char buf[BUFSIZ];
19495424978Smillert 
19595424978Smillert 	/*
19695424978Smillert 	 * Test all combinations of select and poll.
19795424978Smillert 	 */
19847336ab6Smillert 	for (pass = 0; pass < 16; pass++) {
19995424978Smillert 		/*
20095424978Smillert 		 * We run each test twice, once with a fresh fifo,
20195424978Smillert 		 * and once with a reused one.
20295424978Smillert 		 */
20395424978Smillert 		if ((pass & 1) == 0) {
20495424978Smillert 			if (fd != -1)
20595424978Smillert 				close(fd);
20695424978Smillert 			(void)unlink(fifo);
20795424978Smillert 			if (mkfifo(fifo, 0644) != 0) {
20895424978Smillert 				printf("mkfifo %s: %s\n", fifo, strerror(errno));
20995424978Smillert 				exit(1);
21095424978Smillert 			}
21195424978Smillert 
21295424978Smillert 			/* XXX - also verify that we get alarm for O_RDWR */
21395424978Smillert 			alarm(2);
21495424978Smillert 			if ((fd = open(fifo, O_RDONLY | flags, 0644)) == -1) {
21595424978Smillert 				printf("open %s: %s\n", fifo, strerror(errno));
21695424978Smillert 				exit(1);
21795424978Smillert 			}
21895424978Smillert 			alarm(0);
21995424978Smillert 
22095424978Smillert 			printf("\nOpened fifo for reading %s%s\n", fifo,
22195424978Smillert 			    (flags & O_NONBLOCK) ? " (nonblocking)" : "");
22295424978Smillert 		}
22395424978Smillert 
22495424978Smillert 		printf("\nTesting EOF FIFO behavior (pass %d):\n", pass);
22595424978Smillert 
22695424978Smillert 		/*
22795424978Smillert 		 * The writer will sleep for a bit to give the reader time
22895424978Smillert 		 * to call select() before anything has been written.
22995424978Smillert 		 */
2302ddff956Smillert 		writer = eof_writer(fifo, flags);
2312ddff956Smillert 		if (writer == -1)
23295424978Smillert 			exit(1);
23395424978Smillert 
23495424978Smillert 		switch (pass) {
23595424978Smillert 		case 0:
23695424978Smillert 		case 1:
2372ddff956Smillert 		    dopoll(writer, fd, POLLIN|POLLOUT, "POLLIN|POLLOUT", timeout);
23895424978Smillert 		    break;
23995424978Smillert 		case 2:
24095424978Smillert 		case 3:
2412ddff956Smillert 		    dopoll(writer, fd, POLLIN, "POLLIN", timeout);
24295424978Smillert 		    break;
24395424978Smillert 		case 4:
24495424978Smillert 		case 5:
2452ddff956Smillert 		    dopoll(writer, fd, POLLOUT, "POLLOUT", timeout);
24695424978Smillert 		    break;
24795424978Smillert 		case 6:
24895424978Smillert 		case 7:
2492ddff956Smillert 		    dopoll(writer, fd, 0, "(none)", timeout);
25095424978Smillert 		    break;
25195424978Smillert 		case 8:
25295424978Smillert 		case 9:
2532ddff956Smillert 		    doselect(writer, fd, fd, timeout);
25495424978Smillert 		    break;
25595424978Smillert 		case 10:
25695424978Smillert 		case 11:
2572ddff956Smillert 		    doselect(writer, fd, -1, timeout);
25847336ab6Smillert 		    break;
25947336ab6Smillert 		case 12:
26047336ab6Smillert 		case 13:
2612ddff956Smillert 		    doselect(writer, -1, fd, timeout);
26295424978Smillert 		    break;
26347336ab6Smillert 		case 14:
26447336ab6Smillert 		case 15:
2652ddff956Smillert 		    doselect(writer, -1, -1, timeout);
26647336ab6Smillert 		    break;
26795424978Smillert 		}
26895424978Smillert 		wait(&status);
26995424978Smillert 		if ((nread = read(fd, buf, sizeof(buf))) < 0) {
27095424978Smillert 			printf("read error: %s\n", strerror(errno));
27195424978Smillert 			exit(1);
27295424978Smillert 		}
27395424978Smillert 		buf[nread] = '\0';
27495424978Smillert 		printf("\treceived %s%s%s from FIFO\n", nread ? "'" : "",
27595424978Smillert 		    nread ? buf : "EOF", nread ? "'" : "");
27695424978Smillert 	}
27795424978Smillert 	close(fd);
27895424978Smillert 	(void)unlink(fifo);
27995424978Smillert }
28095424978Smillert 
281aa90103aSmillert void
dopoll(pid_t writer,int fd,int events,char * str,int timeout)2822ddff956Smillert dopoll(pid_t writer, int fd, int events, char *str, int timeout)
283aa90103aSmillert {
284aa90103aSmillert 	struct pollfd pfd;
285aa90103aSmillert 	int nready;
286aa90103aSmillert 
287aa90103aSmillert 	pfd.fd = fd;
288aa90103aSmillert 	pfd.events = events;
289aa90103aSmillert 
2902277d5f8Smillert 	printf("\tpoll %s, timeout=%d\n", str, timeout);
291aa90103aSmillert 	pfd.events = events;
2922ddff956Smillert 	if (writer != -1)
2932ddff956Smillert 		kill(writer, SIGUSR1);
294aa90103aSmillert 	alarm(2);
2952277d5f8Smillert 	nready = poll(&pfd, 1, timeout);
296aa90103aSmillert 	alarm(0);
297aa90103aSmillert 	if (nready < 0) {
298aa90103aSmillert 		printf("poll: %s\n", strerror(errno));
299aa90103aSmillert 		return;
300aa90103aSmillert 	}
301aa90103aSmillert 	printf("\t\t%d fd(s) ready%s", nready, nready ? ", revents ==" : "");
302aa90103aSmillert 	if (pfd.revents & POLLIN)
303aa90103aSmillert 		printf(" POLLIN");
304aa90103aSmillert 	if (pfd.revents & POLLOUT)
305aa90103aSmillert 		printf(" POLLOUT");
306aa90103aSmillert 	if (pfd.revents & POLLERR)
307aa90103aSmillert 		printf(" POLLERR");
308aa90103aSmillert 	if (pfd.revents & POLLHUP)
309aa90103aSmillert 		printf(" POLLHUP");
310aa90103aSmillert 	if (pfd.revents & POLLNVAL)
311aa90103aSmillert 		printf(" POLLNVAL");
312aa90103aSmillert 	printf("\n");
313aa90103aSmillert }
314aa90103aSmillert 
315aa90103aSmillert void
doselect(pid_t writer,int rfd,int wfd,int timeout)3162ddff956Smillert doselect(pid_t writer, int rfd, int wfd, int timeout)
3172277d5f8Smillert {
3182277d5f8Smillert 	struct timeval tv, *tvp;
3192277d5f8Smillert 	fd_set *rfds = NULL, *wfds = NULL;
3202277d5f8Smillert 	int nready, maxfd;
3212277d5f8Smillert 
3222277d5f8Smillert 	if (timeout == INFTIM)
3232277d5f8Smillert 		tvp = NULL;
3242277d5f8Smillert 	else {
3252277d5f8Smillert 		tv.tv_sec = timeout / 1000;
3262277d5f8Smillert 		tv.tv_usec = (timeout % 1000) * 1000;
3272277d5f8Smillert 		tvp = &tv;
3282277d5f8Smillert 	}
3292277d5f8Smillert 	maxfd = rfd > wfd ? rfd : wfd;
3302277d5f8Smillert 	if (rfd != -1) {
3312277d5f8Smillert 		rfds = calloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
3322277d5f8Smillert 		if (rfds == NULL) {
3332277d5f8Smillert 			printf("unable to allocate memory\n");
3342277d5f8Smillert 			exit(1);
3352277d5f8Smillert 		}
3362277d5f8Smillert 		FD_SET(rfd, rfds);
3372277d5f8Smillert 	}
3382277d5f8Smillert 	if (wfd != -1) {
3392277d5f8Smillert 		wfds = calloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
3402277d5f8Smillert 		if (wfds == NULL) {
3412277d5f8Smillert 			printf("unable to allocate memory\n");
3422277d5f8Smillert 			exit(1);
3432277d5f8Smillert 		}
3442277d5f8Smillert 		FD_SET(wfd, wfds);
3452277d5f8Smillert 	}
3462277d5f8Smillert 
34747336ab6Smillert 	printf("\tselect%s%s, timeout=%d\n", rfds ? " read" : "",
34847336ab6Smillert 	    wfds ? " write" : rfds ? "" : " (none)", timeout);
3492ddff956Smillert 	if (writer != -1)
3502ddff956Smillert 		kill(writer, SIGUSR1);
3512277d5f8Smillert 	alarm(2);
352bb40c233Smillert 	nready = select(maxfd + 1, rfds, wfds, NULL, tvp);
3532277d5f8Smillert 	alarm(0);
3542277d5f8Smillert 	if (nready < 0) {
3552277d5f8Smillert 		printf("select: %s\n", strerror(errno));
3562277d5f8Smillert 		goto cleanup;
3572277d5f8Smillert 	}
3582277d5f8Smillert 	printf("\t\t%d fd(s) ready", nready);
3592277d5f8Smillert 	if (rfds != NULL && FD_ISSET(rfd, rfds))
3602277d5f8Smillert 		printf(", readable");
3612277d5f8Smillert 	if (wfds != NULL && FD_ISSET(wfd, wfds))
3622277d5f8Smillert 		printf(", writeable");
3632277d5f8Smillert 	printf("\n");
3642277d5f8Smillert cleanup:
3652277d5f8Smillert 	free(rfds);
3662277d5f8Smillert 	free(wfds);
3672277d5f8Smillert }
3682277d5f8Smillert 
3692277d5f8Smillert void
sigalrm(int dummy)370aa90103aSmillert sigalrm(int dummy)
371aa90103aSmillert {
372aa90103aSmillert 	/* Just cause EINTR */
373aa90103aSmillert 	return;
374aa90103aSmillert }
375aa90103aSmillert 
376aa90103aSmillert void
sigusr1(int dummy)3772ddff956Smillert sigusr1(int dummy)
3782ddff956Smillert {
3792ddff956Smillert 	return;
3802ddff956Smillert }
3812ddff956Smillert 
3822ddff956Smillert void
usage(void)383aa90103aSmillert usage(void)
384aa90103aSmillert {
385aa90103aSmillert 	fprintf(stderr, "usage: %s fifoname\n", __progname);
386aa90103aSmillert 	exit(1);
387aa90103aSmillert }
388