1/* $FreeBSD$ */
2
3#include <sys/socket.h>
4#include <sys/select.h>
5#include <sys/stat.h>
6
7#include <err.h>
8#include <fcntl.h>
9#include <signal.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <unistd.h>
13
14#define	FIFONAME	"fifo.tmp"
15#define	FT_END		3
16#define	FT_FIFO		2
17#define	FT_PIPE		0
18#define	FT_SOCKETPAIR	1
19
20#define	SETUP(fd, rfds, tv) do {				\
21	FD_ZERO(&(rfds));					\
22	FD_SET((fd), &(rfds));					\
23	(tv).tv_sec = 0;					\
24	(tv).tv_usec = 0;					\
25} while (0)
26
27static int filetype;
28
29static const char *
30decode_events(int events)
31{
32	return (events ? "set" : "clear");
33}
34
35static void
36report(int num, const char *state, int expected, int got)
37{
38	if (!expected == !got)
39		printf("ok %-2d    ", num);
40	else
41		printf("not ok %-2d", num);
42	printf(" %s state %s: expected %s; got %s\n",
43	    filetype == FT_PIPE ? "Pipe" :
44	    filetype == FT_SOCKETPAIR ? "Sock" : "FIFO",
45	    state, decode_events(expected), decode_events(got));
46	fflush(stdout);
47}
48
49static pid_t cpid;
50static pid_t ppid;
51static volatile sig_atomic_t state;
52
53static void
54catch(int sig)
55{
56	state++;
57}
58
59static void
60child(int fd, int num)
61{
62	fd_set rfds;
63	struct timeval tv;
64	int fd1, fd2;
65	char buf[256];
66
67	if (filetype == FT_FIFO) {
68		fd = open(FIFONAME, O_RDONLY | O_NONBLOCK);
69		if (fd < 0)
70			err(1, "open for read");
71	}
72	if (fd >= FD_SETSIZE)
73		errx(1, "fd = %d too large for select()", fd);
74
75	if (filetype == FT_FIFO) {
76		SETUP(fd, rfds, tv);
77		if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
78			err(1, "select");
79		/*
80		 * This state (a reader for which there has never been a
81		 * writer) is reported quite differently for select() than
82		 * for poll().  select() must see a ready-to-read descriptor
83		 * since read() will see EOF and not block; it cannot
84		 * distinguish this state from the one of a reader for which
85		 * there has been a writer but all writers have gone away
86		 * and all data has been read.  poll() and distinguish these
87		 * states by returning POLLHUP only for the latter; it does
88		 * this, although this makes it inconsistent with the
89		 * blockability of read() in the former.
90		 */
91		report(num++, "0", 1, FD_ISSET(fd, &rfds));
92	}
93	kill(ppid, SIGUSR1);
94
95	usleep(1);
96	while (state != 1)
97		;
98	if (filetype != FT_FIFO) {
99		/*
100		 * The connection cannot be reestablished.  Use the code that
101		 * delays the read until after the writer disconnects since
102		 * that case is more interesting.
103		 */
104		state = 4;
105		goto state4;
106	}
107	SETUP(fd, rfds, tv);
108	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
109		err(1, "select");
110	report(num++, "1", 0, FD_ISSET(fd, &rfds));
111	kill(ppid, SIGUSR1);
112
113	usleep(1);
114	while (state != 2)
115		;
116	SETUP(fd, rfds, tv);
117	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
118		err(1, "select");
119	report(num++, "2", 1, FD_ISSET(fd, &rfds));
120	if (read(fd, buf, sizeof buf) != 1)
121		err(1, "read");
122	SETUP(fd, rfds, tv);
123	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
124		err(1, "select");
125	report(num++, "2a", 0, FD_ISSET(fd, &rfds));
126	kill(ppid, SIGUSR1);
127
128	usleep(1);
129	while (state != 3)
130		;
131	SETUP(fd, rfds, tv);
132	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
133		err(1, "select");
134	report(num++, "3", 1, FD_ISSET(fd, &rfds));
135	kill(ppid, SIGUSR1);
136
137	/*
138	 * Now we expect a new writer, and a new connection too since
139	 * we read all the data.  The only new point is that we didn't
140	 * start quite from scratch since the read fd is not new.  Check
141	 * startup state as above, but don't do the read as above.
142	 */
143	usleep(1);
144	while (state != 4)
145		;
146state4:
147	SETUP(fd, rfds, tv);
148	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
149		err(1, "select");
150	report(num++, "4", 0, FD_ISSET(fd, &rfds));
151	kill(ppid, SIGUSR1);
152
153	usleep(1);
154	while (state != 5)
155		;
156	SETUP(fd, rfds, tv);
157	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
158		err(1, "select");
159	report(num++, "5", 1, FD_ISSET(fd, &rfds));
160	kill(ppid, SIGUSR1);
161
162	usleep(1);
163	while (state != 6)
164		;
165	/*
166	 * Now we have no writer, but should still have data from the old
167	 * writer.  Check that we have a data-readable condition, and that
168	 * the data can be read in the usual way.
169	 */
170	SETUP(fd, rfds, tv);
171	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
172		err(1, "select");
173	report(num++, "6", 1, FD_ISSET(fd, &rfds));
174	if (read(fd, buf, sizeof buf) != 1)
175		err(1, "read");
176	SETUP(fd, rfds, tv);
177	if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
178		err(1, "select");
179	report(num++, "6a", 1, FD_ISSET(fd, &rfds));
180	if (filetype == FT_FIFO) {
181		/*
182		 * Check that the readable-data condition is sticky for a
183		 * new reader and for the old reader.  We really only have
184		 * a hangup condition, but select() can only see this as
185		 * a readable-data condition for null data.  select()
186		 * cannot distinguish this state from the initial state
187		 * where there is a reader but has never been a writer, so
188		 * the following tests (to follow the pattern in pipepoll.c)
189		 * essentially test state 0 again.
190		 */
191		fd2 = open(FIFONAME, O_RDONLY | O_NONBLOCK);
192		if (fd2 < 0)
193			err(1, "open for read");
194		fd1 = fd;
195		fd = fd2;
196		SETUP(fd, rfds, tv);
197		if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
198			err(1, "select");
199		report(num++, "6b", 1, FD_ISSET(fd, &rfds));
200		fd = fd1;
201		SETUP(fd, rfds, tv);
202		if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
203			err(1, "select");
204		report(num++, "6c", 1, FD_ISSET(fd, &rfds));
205		close(fd2);
206		SETUP(fd, rfds, tv);
207		if (select(fd + 1, &rfds, NULL, NULL, &tv) < 0)
208			err(1, "select");
209		report(num++, "6d", 1, FD_ISSET(fd, &rfds));
210	}
211	close(fd);
212	kill(ppid, SIGUSR1);
213
214	exit(0);
215}
216
217static void
218parent(int fd)
219{
220	usleep(1);
221	while (state != 1)
222		;
223	if (filetype == FT_FIFO) {
224		fd = open(FIFONAME, O_WRONLY | O_NONBLOCK);
225		if (fd < 0)
226			err(1, "open for write");
227	}
228	kill(cpid, SIGUSR1);
229
230	usleep(1);
231	while (state != 2)
232		;
233	if (write(fd, "", 1) != 1)
234		err(1, "write");
235	kill(cpid, SIGUSR1);
236
237	usleep(1);
238	while (state != 3)
239		;
240	if (close(fd) != 0)
241		err(1, "close for write");
242	kill(cpid, SIGUSR1);
243
244	usleep(1);
245	while (state != 4)
246		;
247	if (filetype != FT_FIFO)
248		return;
249	fd = open(FIFONAME, O_WRONLY | O_NONBLOCK);
250	if (fd < 0)
251		err(1, "open for write");
252	kill(cpid, SIGUSR1);
253
254	usleep(1);
255	while (state != 5)
256		;
257	if (write(fd, "", 1) != 1)
258		err(1, "write");
259	kill(cpid, SIGUSR1);
260
261	usleep(1);
262	while (state != 6)
263		;
264	if (close(fd) != 0)
265		err(1, "close for write");
266	kill(cpid, SIGUSR1);
267
268	usleep(1);
269	while (state != 7)
270		;
271}
272
273int
274main(void)
275{
276	int fd[2], num;
277
278	num = 1;
279	printf("1..20\n");
280	fflush(stdout);
281	signal(SIGUSR1, catch);
282	ppid = getpid();
283	for (filetype = 0; filetype < FT_END; filetype++) {
284		switch (filetype) {
285		case FT_FIFO:
286			if (mkfifo(FIFONAME, 0666) != 0)
287				err(1, "mkfifo");
288			fd[0] = -1;
289			fd[1] = -1;
290			break;
291		case FT_SOCKETPAIR:
292			if (socketpair(AF_UNIX, SOCK_STREAM, AF_UNSPEC,
293			    fd) != 0)
294				err(1, "socketpair");
295			break;
296		case FT_PIPE:
297			if (pipe(fd) != 0)
298				err(1, "pipe");
299			break;
300		}
301		state = 0;
302		switch (cpid = fork()) {
303		case -1:
304			err(1, "fork");
305		case 0:
306			(void)close(fd[1]);
307			child(fd[0], num);
308			break;
309		default:
310			(void)close(fd[0]);
311			parent(fd[1]);
312			break;
313		}
314		num += filetype == FT_FIFO ? 12 : 4;
315	}
316	(void)unlink(FIFONAME);
317	return (0);
318}
319