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