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