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 *
decode_events(int events)29 decode_events(int events)
30 {
31 return (events ? "set" : "clear");
32 }
33
34 static void
report(int num,const char * state,int expected,int got)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
catch(int sig)53 catch(int sig)
54 {
55 state++;
56 }
57
58 static void
child(int fd,int num)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
parent(int fd)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
main(void)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