#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef INFTIM
#define INFTIM -1
#endif
void usage(void);
void sigalrm(int);
void sigusr1(int);
void dopoll(pid_t, int, int, char *, int);
void doselect(pid_t, int, int, int);
void runtest(char *, int, int);
void eoftest(char *, int, int);
#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
defined(__linux__)
extern char *__progname;
#else
char *__progname;
#endif
int
main(int argc, char **argv)
{
struct sigaction sa;
#if !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \
!defined(__linux__)
__progname = argv[0];
#endif
if (argc != 2)
usage();
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = sigalrm;
sigaction(SIGALRM, &sa, NULL);
sa.sa_flags = SA_RESTART;
sa.sa_handler = sigusr1;
sigaction(SIGUSR1, &sa, NULL);
runtest(argv[1], 0, 0);
runtest(argv[1], 0, INFTIM);
runtest(argv[1], O_NONBLOCK, 0);
runtest(argv[1], O_NONBLOCK, INFTIM);
eoftest(argv[1], O_NONBLOCK, INFTIM);
exit(0);
}
void
runtest(char *fifo, int flags, int timeout)
{
ssize_t nread;
int fd;
char buf[BUFSIZ];
(void)unlink(fifo);
if (mkfifo(fifo, 0644) != 0) {
printf("mkfifo %s: %s\n", fifo, strerror(errno));
exit(1);
}
alarm(2);
if ((fd = open(fifo, O_RDWR | flags)) == -1) {
printf("open %s: %s\n", fifo, strerror(errno));
exit(1);
}
alarm(0);
(void)unlink(fifo);
printf("\nOpened fifo %s%s\n", fifo,
(flags & O_NONBLOCK) ? " (nonblocking)" : "");
printf("\nTesting empty FIFO:\n");
dopoll(-1, fd, POLLIN|POLLOUT, "POLLIN|POLLOUT", timeout);
dopoll(-1, fd, POLLIN, "POLLIN", timeout);
dopoll(-1, fd, POLLOUT, "POLLOUT", timeout);
dopoll(-1, fd, 0, "(none)", timeout);
doselect(-1, fd, fd, timeout);
doselect(-1, fd, -1, timeout);
doselect(-1, -1, fd, timeout);
doselect(-1, -1, -1, timeout);
if (write(fd, "test", 4) != 4) {
printf("write error: %s\n", strerror(errno));
exit(1);
}
printf("\nTesting full FIFO:\n");
dopoll(-1, fd, POLLIN|POLLOUT, "POLLIN|POLLOUT", timeout);
dopoll(-1, fd, POLLIN, "POLLIN", timeout);
dopoll(-1, fd, POLLOUT, "POLLOUT", timeout);
dopoll(-1, fd, 0, "(none)", timeout);
doselect(-1, fd, fd, timeout);
doselect(-1, fd, -1, timeout);
doselect(-1, -1, fd, timeout);
doselect(-1, -1, -1, timeout);
if ((nread = read(fd, buf, sizeof(buf))) <= 0) {
printf("read error: %s\n", (nread == 0) ? "EOF" : strerror(errno));
exit(1);
}
buf[nread] = '\0';
printf("\treceived '%s' from FIFO\n", buf);
}
pid_t
eof_writer(const char *fifo, int flags)
{
int fd;
pid_t pid;
sigset_t mask, omask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigprocmask(SIG_BLOCK, &mask, &omask);
switch ((pid = fork())) {
case -1:
printf("fork: %s\n", strerror(errno));
return -1;
case 0:
break;
default:
sigprocmask(SIG_SETMASK, &omask, NULL);
return pid;
}
sigemptyset(&mask);
sigsuspend(&mask);
sigprocmask(SIG_SETMASK, &omask, NULL);
alarm(2);
fd = open(fifo, O_WRONLY | flags);
alarm(0);
if (fd == -1) {
printf("open %s O_WRONLY: %s\n", fifo, strerror(errno));
return -1;
}
usleep(100000);
close(fd);
_exit(0);
}
void
eoftest(char *fifo, int flags, int timeout)
{
ssize_t nread;
int fd = -1, pass, status;
pid_t writer;
char buf[BUFSIZ];
for (pass = 0; pass < 16; pass++) {
if ((pass & 1) == 0) {
if (fd != -1)
close(fd);
(void)unlink(fifo);
if (mkfifo(fifo, 0644) != 0) {
printf("mkfifo %s: %s\n", fifo, strerror(errno));
exit(1);
}
alarm(2);
if ((fd = open(fifo, O_RDONLY | flags, 0644)) == -1) {
printf("open %s: %s\n", fifo, strerror(errno));
exit(1);
}
alarm(0);
printf("\nOpened fifo for reading %s%s\n", fifo,
(flags & O_NONBLOCK) ? " (nonblocking)" : "");
}
printf("\nTesting EOF FIFO behavior (pass %d):\n", pass);
writer = eof_writer(fifo, flags);
if (writer == -1)
exit(1);
switch (pass) {
case 0:
case 1:
dopoll(writer, fd, POLLIN|POLLOUT, "POLLIN|POLLOUT", timeout);
break;
case 2:
case 3:
dopoll(writer, fd, POLLIN, "POLLIN", timeout);
break;
case 4:
case 5:
dopoll(writer, fd, POLLOUT, "POLLOUT", timeout);
break;
case 6:
case 7:
dopoll(writer, fd, 0, "(none)", timeout);
break;
case 8:
case 9:
doselect(writer, fd, fd, timeout);
break;
case 10:
case 11:
doselect(writer, fd, -1, timeout);
break;
case 12:
case 13:
doselect(writer, -1, fd, timeout);
break;
case 14:
case 15:
doselect(writer, -1, -1, timeout);
break;
}
wait(&status);
if ((nread = read(fd, buf, sizeof(buf))) < 0) {
printf("read error: %s\n", strerror(errno));
exit(1);
}
buf[nread] = '\0';
printf("\treceived %s%s%s from FIFO\n", nread ? "'" : "",
nread ? buf : "EOF", nread ? "'" : "");
}
close(fd);
(void)unlink(fifo);
}
void
dopoll(pid_t writer, int fd, int events, char *str, int timeout)
{
struct pollfd pfd;
int nready;
pfd.fd = fd;
pfd.events = events;
printf("\tpoll %s, timeout=%d\n", str, timeout);
pfd.events = events;
if (writer != -1)
kill(writer, SIGUSR1);
alarm(2);
nready = poll(&pfd, 1, timeout);
alarm(0);
if (nready < 0) {
printf("poll: %s\n", strerror(errno));
return;
}
printf("\t\t%d fd(s) ready%s", nready, nready ? ", revents ==" : "");
if (pfd.revents & POLLIN)
printf(" POLLIN");
if (pfd.revents & POLLOUT)
printf(" POLLOUT");
if (pfd.revents & POLLERR)
printf(" POLLERR");
if (pfd.revents & POLLHUP)
printf(" POLLHUP");
if (pfd.revents & POLLNVAL)
printf(" POLLNVAL");
printf("\n");
}
void
doselect(pid_t writer, int rfd, int wfd, int timeout)
{
struct timeval tv, *tvp;
fd_set *rfds = NULL, *wfds = NULL;
int nready, maxfd;
if (timeout == INFTIM)
tvp = NULL;
else {
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
tvp = &tv;
}
maxfd = rfd > wfd ? rfd : wfd;
if (rfd != -1) {
rfds = calloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
if (rfds == NULL) {
printf("unable to allocate memory\n");
exit(1);
}
FD_SET(rfd, rfds);
}
if (wfd != -1) {
wfds = calloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
if (wfds == NULL) {
printf("unable to allocate memory\n");
exit(1);
}
FD_SET(wfd, wfds);
}
printf("\tselect%s%s, timeout=%d\n", rfds ? " read" : "",
wfds ? " write" : rfds ? "" : " (none)", timeout);
if (writer != -1)
kill(writer, SIGUSR1);
alarm(2);
nready = select(maxfd + 1, rfds, wfds, NULL, tvp);
alarm(0);
if (nready < 0) {
printf("select: %s\n", strerror(errno));
goto cleanup;
}
printf("\t\t%d fd(s) ready", nready);
if (rfds != NULL && FD_ISSET(rfd, rfds))
printf(", readable");
if (wfds != NULL && FD_ISSET(wfd, wfds))
printf(", writeable");
printf("\n");
cleanup:
free(rfds);
free(wfds);
}
void
sigalrm(int dummy)
{
return;
}
void
sigusr1(int dummy)
{
return;
}
void
usage(void)
{
fprintf(stderr, "usage: %s fifoname\n", __progname);
exit(1);
}