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