1195636Skib/* $FreeBSD$ */
2195636Skib
3195636Skib#include <sys/socket.h>
4195636Skib#include <sys/select.h>
5195636Skib#include <sys/stat.h>
6195636Skib
7195636Skib#include <err.h>
8195636Skib#include <fcntl.h>
9195636Skib#include <signal.h>
10195636Skib#include <stdio.h>
11195636Skib#include <stdlib.h>
12195636Skib#include <unistd.h>
13195636Skib
14195636Skib#define	FIFONAME	"fifo.tmp"
15195636Skib#define	FT_END		3
16195636Skib#define	FT_FIFO		2
17195636Skib#define	FT_PIPE		0
18195636Skib#define	FT_SOCKETPAIR	1
19195636Skib
20195636Skib#define	SETUP(fd, rfds, tv) do {				\
21195636Skib	FD_ZERO(&(rfds));					\
22195636Skib	FD_SET((fd), &(rfds));					\
23195636Skib	(tv).tv_sec = 0;					\
24195636Skib	(tv).tv_usec = 0;					\
25195636Skib} while (0)
26195636Skib
27195636Skibstatic int filetype;
28195636Skib
29195636Skibstatic const char *
30195636Skibdecode_events(int events)
31195636Skib{
32195636Skib	return (events ? "set" : "clear");
33195636Skib}
34195636Skib
35195636Skibstatic void
36195636Skibreport(int num, const char *state, int expected, int got)
37195636Skib{
38195636Skib	if (!expected == !got)
39195636Skib		printf("ok %-2d    ", num);
40195636Skib	else
41195636Skib		printf("not ok %-2d", num);
42195636Skib	printf(" %s state %s: expected %s; got %s\n",
43195636Skib	    filetype == FT_PIPE ? "Pipe" :
44195636Skib	    filetype == FT_SOCKETPAIR ? "Sock" : "FIFO",
45195636Skib	    state, decode_events(expected), decode_events(got));
46195636Skib	fflush(stdout);
47195636Skib}
48195636Skib
49195636Skibstatic pid_t cpid;
50195636Skibstatic pid_t ppid;
51195636Skibstatic volatile sig_atomic_t state;
52195636Skib
53195636Skibstatic void
54195636Skibcatch(int sig)
55195636Skib{
56195636Skib	state++;
57195636Skib}
58195636Skib
59195636Skibstatic void
60195636Skibchild(int fd, int num)
61195636Skib{
62195636Skib	fd_set rfds;
63195636Skib	struct timeval tv;
64195636Skib	int fd1, fd2;
65195636Skib	char buf[256];
66195636Skib
67195636Skib	if (filetype == FT_FIFO) {
68195636Skib		fd = open(FIFONAME, O_RDONLY | O_NONBLOCK);
69195636Skib		if (fd < 0)
70195636Skib			err(1, "open for read");
71195636Skib	}
72195636Skib	if (fd >= FD_SETSIZE)
73195636Skib		errx(1, "fd = %d too large for select()", fd);
74195636Skib
75195636Skib	if (filetype == FT_FIFO) {
76195636Skib		SETUP(fd, rfds, tv);
77195636Skib		if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
78195636Skib			err(1, "select");
79195636Skib		/*
80195636Skib		 * This state (a reader for which there has never been a
81195636Skib		 * writer) is reported quite differently for select() than
82195636Skib		 * for poll().  select() must see a ready-to-read descriptor
83195636Skib		 * since read() will see EOF and not block; it cannot
84195636Skib		 * distinguish this state from the one of a reader for which
85195636Skib		 * there has been a writer but all writers have gone away
86195636Skib		 * and all data has been read.  poll() and distinguish these
87195636Skib		 * states by returning POLLHUP only for the latter; it does
88195636Skib		 * this, although this makes it inconsistent with the
89195636Skib		 * blockability of read() in the former.
90195636Skib		 */
91195636Skib		report(num++, "0", 1, FD_ISSET(fd, &rfds));
92195636Skib	}
93195636Skib	kill(ppid, SIGUSR1);
94195636Skib
95195636Skib	usleep(1);
96195636Skib	while (state != 1)
97195636Skib		;
98195636Skib	if (filetype != FT_FIFO) {
99195636Skib		/*
100195636Skib		 * The connection cannot be reestablished.  Use the code that
101195636Skib		 * delays the read until after the writer disconnects since
102195636Skib		 * that case is more interesting.
103195636Skib		 */
104195636Skib		state = 4;
105195636Skib		goto state4;
106195636Skib	}
107195636Skib	SETUP(fd, rfds, tv);
108195636Skib	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
109195636Skib		err(1, "select");
110195636Skib	report(num++, "1", 0, FD_ISSET(fd, &rfds));
111195636Skib	kill(ppid, SIGUSR1);
112195636Skib
113195636Skib	usleep(1);
114195636Skib	while (state != 2)
115195636Skib		;
116195636Skib	SETUP(fd, rfds, tv);
117195636Skib	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
118195636Skib		err(1, "select");
119195636Skib	report(num++, "2", 1, FD_ISSET(fd, &rfds));
120195636Skib	if (read(fd, buf, sizeof buf) != 1)
121195636Skib		err(1, "read");
122195636Skib	SETUP(fd, rfds, tv);
123195636Skib	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
124195636Skib		err(1, "select");
125195636Skib	report(num++, "2a", 0, FD_ISSET(fd, &rfds));
126195636Skib	kill(ppid, SIGUSR1);
127195636Skib
128195636Skib	usleep(1);
129195636Skib	while (state != 3)
130195636Skib		;
131195636Skib	SETUP(fd, rfds, tv);
132195636Skib	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
133195636Skib		err(1, "select");
134195636Skib	report(num++, "3", 1, FD_ISSET(fd, &rfds));
135195636Skib	kill(ppid, SIGUSR1);
136195636Skib
137195636Skib	/*
138195636Skib	 * Now we expect a new writer, and a new connection too since
139195636Skib	 * we read all the data.  The only new point is that we didn't
140195636Skib	 * start quite from scratch since the read fd is not new.  Check
141195636Skib	 * startup state as above, but don't do the read as above.
142195636Skib	 */
143195636Skib	usleep(1);
144195636Skib	while (state != 4)
145195636Skib		;
146195636Skibstate4:
147195636Skib	SETUP(fd, rfds, tv);
148195636Skib	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
149195636Skib		err(1, "select");
150195636Skib	report(num++, "4", 0, FD_ISSET(fd, &rfds));
151195636Skib	kill(ppid, SIGUSR1);
152195636Skib
153195636Skib	usleep(1);
154195636Skib	while (state != 5)
155195636Skib		;
156195636Skib	SETUP(fd, rfds, tv);
157195636Skib	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
158195636Skib		err(1, "select");
159195636Skib	report(num++, "5", 1, FD_ISSET(fd, &rfds));
160195636Skib	kill(ppid, SIGUSR1);
161195636Skib
162195636Skib	usleep(1);
163195636Skib	while (state != 6)
164195636Skib		;
165195636Skib	/*
166195636Skib	 * Now we have no writer, but should still have data from the old
167195636Skib	 * writer.  Check that we have a data-readable condition, and that
168195636Skib	 * the data can be read in the usual way.
169195636Skib	 */
170195636Skib	SETUP(fd, rfds, tv);
171195636Skib	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
172195636Skib		err(1, "select");
173195636Skib	report(num++, "6", 1, FD_ISSET(fd, &rfds));
174195636Skib	if (read(fd, buf, sizeof buf) != 1)
175195636Skib		err(1, "read");
176195636Skib	SETUP(fd, rfds, tv);
177195636Skib	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
178195636Skib		err(1, "select");
179195636Skib	report(num++, "6a", 1, FD_ISSET(fd, &rfds));
180195636Skib	if (filetype == FT_FIFO) {
181195636Skib		/*
182195636Skib		 * Check that the readable-data condition is sticky for a
183195636Skib		 * new reader and for the old reader.  We really only have
184195636Skib		 * a hangup condition, but select() can only see this as
185195636Skib		 * a readable-data condition for null data.  select()
186195636Skib		 * cannot distinguish this state from the initial state
187195636Skib		 * where there is a reader but has never been a writer, so
188195636Skib		 * the following tests (to follow the pattern in pipepoll.c)
189195636Skib		 * essentially test state 0 again.
190195636Skib		 */
191195636Skib		fd2 = open(FIFONAME, O_RDONLY | O_NONBLOCK);
192195636Skib		if (fd2 < 0)
193195636Skib			err(1, "open for read");
194195636Skib		fd1 = fd;
195195636Skib		fd = fd2;
196195636Skib		SETUP(fd, rfds, tv);
197195636Skib		if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
198195636Skib			err(1, "select");
199195636Skib		report(num++, "6b", 1, FD_ISSET(fd, &rfds));
200195636Skib		fd = fd1;
201195636Skib		SETUP(fd, rfds, tv);
202195636Skib		if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
203195636Skib			err(1, "select");
204195636Skib		report(num++, "6c", 1, FD_ISSET(fd, &rfds));
205195636Skib		close(fd2);
206195636Skib		SETUP(fd, rfds, tv);
207195636Skib		if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
208195636Skib			err(1, "select");
209195636Skib		report(num++, "6d", 1, FD_ISSET(fd, &rfds));
210195636Skib	}
211195636Skib	close(fd);
212195636Skib	kill(ppid, SIGUSR1);
213195636Skib
214195636Skib	exit(0);
215195636Skib}
216195636Skib
217195636Skibstatic void
218195636Skibparent(int fd)
219195636Skib{
220195636Skib	usleep(1);
221195636Skib	while (state != 1)
222195636Skib		;
223195636Skib	if (filetype == FT_FIFO) {
224195636Skib		fd = open(FIFONAME, O_WRONLY | O_NONBLOCK);
225195636Skib		if (fd < 0)
226195636Skib			err(1, "open for write");
227195636Skib	}
228195636Skib	kill(cpid, SIGUSR1);
229195636Skib
230195636Skib	usleep(1);
231195636Skib	while (state != 2)
232195636Skib		;
233195636Skib	if (write(fd, "", 1) != 1)
234195636Skib		err(1, "write");
235195636Skib	kill(cpid, SIGUSR1);
236195636Skib
237195636Skib	usleep(1);
238195636Skib	while (state != 3)
239195636Skib		;
240195636Skib	if (close(fd) != 0)
241195636Skib		err(1, "close for write");
242195636Skib	kill(cpid, SIGUSR1);
243195636Skib
244195636Skib	usleep(1);
245195636Skib	while (state != 4)
246195636Skib		;
247195636Skib	if (filetype != FT_FIFO)
248195636Skib		return;
249195636Skib	fd = open(FIFONAME, O_WRONLY | O_NONBLOCK);
250195636Skib	if (fd < 0)
251195636Skib		err(1, "open for write");
252195636Skib	kill(cpid, SIGUSR1);
253195636Skib
254195636Skib	usleep(1);
255195636Skib	while (state != 5)
256195636Skib		;
257195636Skib	if (write(fd, "", 1) != 1)
258195636Skib		err(1, "write");
259195636Skib	kill(cpid, SIGUSR1);
260195636Skib
261195636Skib	usleep(1);
262195636Skib	while (state != 6)
263195636Skib		;
264195636Skib	if (close(fd) != 0)
265195636Skib		err(1, "close for write");
266195636Skib	kill(cpid, SIGUSR1);
267195636Skib
268195636Skib	usleep(1);
269195636Skib	while (state != 7)
270195636Skib		;
271195636Skib}
272195636Skib
273195636Skibint
274195636Skibmain(void)
275195636Skib{
276195636Skib	int fd[2], num;
277195636Skib
278195636Skib	num = 1;
279195636Skib	printf("1..20\n");
280195636Skib	fflush(stdout);
281195636Skib	signal(SIGUSR1, catch);
282195636Skib	ppid = getpid();
283195636Skib	for (filetype = 0; filetype < FT_END; filetype++) {
284195636Skib		switch (filetype) {
285195636Skib		case FT_FIFO:
286195636Skib			if (mkfifo(FIFONAME, 0666) != 0)
287195636Skib				err(1, "mkfifo");
288195636Skib			fd[0] = -1;
289195636Skib			fd[1] = -1;
290195636Skib			break;
291195636Skib		case FT_SOCKETPAIR:
292195636Skib			if (socketpair(AF_UNIX, SOCK_STREAM, AF_UNSPEC,
293195636Skib			    fd) != 0)
294195636Skib				err(1, "socketpair");
295195636Skib			break;
296195636Skib		case FT_PIPE:
297195636Skib			if (pipe(fd) != 0)
298195636Skib				err(1, "pipe");
299195636Skib			break;
300195636Skib		}
301195636Skib		state = 0;
302195636Skib		switch (cpid = fork()) {
303195636Skib		case -1:
304195636Skib			err(1, "fork");
305195636Skib		case 0:
306195636Skib			(void)close(fd[1]);
307195636Skib			child(fd[0], num);
308195636Skib			break;
309195636Skib		default:
310195636Skib			(void)close(fd[0]);
311195636Skib			parent(fd[1]);
312195636Skib			break;
313195636Skib		}
314195636Skib		num += filetype == FT_FIFO ? 12 : 4;
315195636Skib	}
316195636Skib	(void)unlink(FIFONAME);
317195636Skib	return (0);
318195636Skib}
319