xref: /freebsd/tools/regression/poll/pipeselect.c (revision c697fb7f)
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 
27 static int filetype;
28 
29 static const char *
30 decode_events(int events)
31 {
32 	return (events ? "set" : "clear");
33 }
34 
35 static void
36 report(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 
49 static pid_t cpid;
50 static pid_t ppid;
51 static volatile sig_atomic_t state;
52 
53 static void
54 catch(int sig)
55 {
56 	state++;
57 }
58 
59 static void
60 child(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 		;
146 state4:
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 
217 static void
218 parent(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 
273 int
274 main(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