5 * Select works on regular files, (pseudo) terminal devices, streams-based
6 * files, FIFOs, pipes, and sockets. This test verifies selecting for FIFOs
7 * (named pipes) and pipes (anonymous pipes). This test will not verify most
8 * error file descriptors, as the setting of this fdset in the face of an error
9 * condition is implementation-specific (except for regular files (alway set)
10 * and sockets (protocol-specific or OOB data received), but those file types
11 * are not being tested in this specific test).
13 * This test is part of a bigger select test. It expects as argument which sub-
16 * [1] If a socket has a pending error, it shall be considered to have an
17 * exceptional condition pending. Otherwise, what constitutes an exceptional
18 * condition is file type-specific. For a file descriptor for use with a
19 * socket, it is protocol-specific except as noted below. For other file types
20 * it is implementation-defined. If the operation is meaningless for a
21 * particular file type, pselect() or select() shall indicate that the
22 * descriptor is ready for read or write operations, and shall indicate that
23 * the descriptor has no exceptional condition pending.
29 #include <sys/types.h>
32 #include <sys/select.h>
39 #define NAMEDPIPE1 "selecttestd-1"
40 #define NAMEDPIPE2 "selecttestd-2"
41 #define SENDSTRING "minixrocks"
42 #define DO_HANDLEDATA 1
47 int errct
= 0, subtest
= -1;
49 int fd_ap
[2]; /* Anonymous pipe; read from fd_ap[0], write to fd_ap[1] */
50 int fd_np1
; /* Named pipe */
51 int fd_np2
; /* Named pipe */
53 void e(int n
, char *s
) {
54 printf("Subtest %d, error %d, %s\n", subtest
, n
, s
);
56 if (errct
++ > MAX_ERROR
) {
57 printf("Too many errors; test aborted\n");
65 /* Open named pipe for writing. This will block until a reader arrives. */
66 if((fd_np1
= open(NAMEDPIPE1
, O_WRONLY
)) == -1) {
67 printf("Error opening %s for writing, signalling parent to quit\n",
70 printf("Please make sure that %s is not in use while running this test\n",
75 /* Going to sleep for three seconds to allow the parent proc to get ready */
76 tv
.tv_sec
= DO_HANDLEDATA
;
78 select(0, NULL
, NULL
, NULL
, &tv
);
80 /* Try to write. Doesn't matter how many bytes we actually send. */
81 (void) write(fd_np1
, SENDSTRING
, strlen(SENDSTRING
));
83 /* Wait for another second to allow the parent to process incoming data */
84 tv
.tv_sec
= DO_HANDLEDATA
;
86 (void) select(0,NULL
, NULL
, NULL
, &tv
);
90 /* Wait for another second to allow the parent to process incoming data */
91 tv
.tv_sec
= DO_HANDLEDATA
;
93 (void) select(0,NULL
, NULL
, NULL
, &tv
);
95 /* Open named pipe for reading. This will block until a writer arrives. */
96 if((fd_np2
= open(NAMEDPIPE2
, O_RDONLY
)) == -1) {
97 printf("Error opening %s for reading, signalling parent to quit\n",
100 printf("Please make sure that %s is not in use while running this test\n",
105 /* Wait for another second to allow the parent to run some tests. */
106 tv
.tv_sec
= DO_HANDLEDATA
;
108 (void) select(0, NULL
, NULL
, NULL
, &tv
);
114 /* Let the parent do initial read and write tests from and to the pipe. */
115 tv
.tv_sec
= DO_PAUSE
;
117 (void) select(0, NULL
, NULL
, NULL
, &tv
);
119 /* Unblock blocking read select by writing data */
120 if(write(fd_ap
[1], SENDSTRING
, strlen(SENDSTRING
)) < 0) {
121 perror("Could not write to anonymous pipe");
128 int count_fds(int nfds
, fd_set
*fds
) {
129 /* Return number of bits set in fds */
131 assert(fds
!= NULL
&& nfds
> 0);
132 for(i
= 0; i
< nfds
; i
++) {
133 if(FD_ISSET(i
, fds
)) result
++;
138 int empty_fds(int nfds
, fd_set
*fds
) {
139 /* Returns nonzero if the first bits up to nfds in fds are not set */
141 assert(fds
!= NULL
&& nfds
> 0);
142 for(i
= 0; i
< nfds
; i
++) if(FD_ISSET(i
, fds
)) return 0;
146 int compare_fds(int nfds
, fd_set
*lh
, fd_set
*rh
) {
147 /* Returns nonzero if lh equals rh up to nfds bits */
149 assert(lh
!= NULL
&& rh
!= NULL
&& nfds
> 0);
150 for(i
= 0; i
< nfds
; i
++) {
151 if((FD_ISSET(i
, lh
) && !FD_ISSET(i
, rh
)) ||
152 (!FD_ISSET(i
, lh
) && FD_ISSET(i
, rh
))) {
159 void dump_fds(int nfds
, fd_set
*fds
) {
160 /* Print a graphical representation of bits in fds */
162 if(fds
!= NULL
&& nfds
> 0) {
163 for(i
= 0; i
< nfds
; i
++) printf("%d ", (FD_ISSET(i
, fds
) ? 1 : 0));
168 void do_parent(int child
) {
169 fd_set fds_read
, fds_write
, fds_error
;
170 fd_set fds_compare_read
, fds_compare_write
;
176 /* Open named pipe for reading. This will block until a writer arrives. */
177 if((fd_np1
= open(NAMEDPIPE1
, O_RDONLY
)) == -1) {
178 printf("Error opening %s for reading\n", NAMEDPIPE1
);
180 printf("Please make sure that %s is not in use while running this test.\n",
182 waitpid(child
, &retval
, 0);
186 /* Clear bit masks */
187 FD_ZERO(&fds_read
); FD_ZERO(&fds_write
);
188 /* Set read and write bits */
189 FD_SET(fd_np1
, &fds_read
);
190 FD_SET(fd_np1
, &fds_write
);
191 tv
.tv_sec
= DO_TIMEOUT
;
194 /* Test if we can read or write from/to fd_np1. As fd_np1 is opened read only
195 * we cannot actually write, so the select should return immediately [1] and
196 * the offending bit set in the fd set. We read from a pipe that is opened
197 * with O_NONBLOCKING cleared, so it is guaranteed we can read.
198 * However, at this moment the writer is sleeping, so the pipe is empty and
199 * read is supposed to block. Therefore, only 1 file descriptor should be
200 * ready. A timeout value is still set in case an error occurs in a faulty
202 retval
= select(fd_np1
+1, &fds_read
, &fds_write
, NULL
, &tv
);
204 /* Did we receive an error? */
206 snprintf(errbuf
, sizeof(errbuf
),
207 "one fd should be set%s", (retval
== 0 ? " (TIMEOUT)" : ""));
211 if(!empty_fds(fd_np1
+1,&fds_read
)) e(2, "no read bits should be set");
214 /* Make sure the write bit is set (and just 1 bit) */
215 FD_ZERO(&fds_compare_write
); FD_SET(fd_np1
, &fds_compare_write
);
216 if(!compare_fds(fd_np1
+1, &fds_compare_write
, &fds_write
))
217 e(3, "write should be set");
219 /* Clear sets and set up new bit masks */
220 FD_ZERO(&fds_read
); FD_ZERO(&fds_write
);
221 FD_SET(fd_np1
, &fds_read
);
222 tv
.tv_sec
= DO_TIMEOUT
; /* To make sure we get to see some error messages
223 instead of blocking forever when the
224 implementation is faulty. A timeout causes retval
227 /* The sleeping writer is about to wake up and write data to the pipe. */
228 retval
= select(fd_np1
+1, &fds_read
, &fds_write
, NULL
, &tv
);
230 /* Correct amount of ready file descriptors? Just 1 read */
232 snprintf(errbuf
, sizeof(errbuf
),
233 "one fd should be set%s", (retval
== 0 ? " (TIMEOUT)" : ""));
237 if(!FD_ISSET(fd_np1
, &fds_read
)) e(5, "read should be set");
239 /* Note that we left the write set empty. This should be equivalent to
240 * setting this parameter to NULL. */
241 if(!empty_fds(fd_np1
+1, &fds_write
)) e(6, "write should NOT be set");
243 /* In case something went wrong above, we might end up with a child process
244 * blocking on a write call we close the file descriptor now. Synchronize on
246 if(read(fd_np1
, buf
, sizeof(SENDSTRING
)) < 0) perror("Read error");
248 /* Close file descriptor. We're going to reverse the test */
251 /* Wait for a second to allow the child to close the pipe as well */
252 tv
.tv_sec
= DO_HANDLEDATA
;
254 retval
= select(0,NULL
, NULL
, NULL
, &tv
);
256 /* Open named pipe for writing. This call blocks until a reader arrives. */
257 if((fd_np2
= open(NAMEDPIPE2
, O_WRONLY
)) == -1) {
258 printf("Error opening %s for writing\n",
261 printf("Please make sure that %s is not in use while running this test\n",
266 /* At this moment the child process has opened the named pipe for reading and
267 * we have opened it for writing. We're now going to reverse some of the
268 * tests we've done earlier. */
270 /* Clear sets and set up bit masks */
271 FD_ZERO(&fds_read
); FD_ZERO(&fds_write
); FD_ZERO(&fds_error
);
272 FD_SET(fd_np2
, &fds_read
);
273 FD_SET(fd_np2
, &fds_write
);
274 tv
.tv_sec
= DO_TIMEOUT
;
276 /* Select for reading from an fd opened O_WRONLY. This should return
277 * immediately as it is not a meaningful operation [1] and is therefore not
278 * blocking. The select should return two file descriptors are ready (the
279 * failing read and valid write). */
281 retval
= select(fd_np2
+1, &fds_read
, &fds_write
, &fds_error
, &tv
);
283 /* Did we receive an error? */
285 snprintf(errbuf
, sizeof(errbuf
),
286 "two fds should be set%s", (retval
== 0 ? " (TIMEOUT)" : ""));
290 /* Make sure read bit is set (and just 1 bit) */
291 FD_ZERO(&fds_compare_read
); FD_SET(fd_np2
, &fds_compare_read
);
292 if(!compare_fds(fd_np2
+1, &fds_compare_read
, &fds_read
))
293 e(8, "read should be set");
295 /* Write bit should be set (and just 1 bit) */
296 FD_ZERO(&fds_compare_write
); FD_SET(fd_np2
, &fds_compare_write
);
297 if(!compare_fds(fd_np2
+1, &fds_compare_write
, &fds_write
))
298 e(9, "write should be set");
300 if(!empty_fds(fd_np2
+1, &fds_error
))
301 e(10, "Error should NOT be set");
303 FD_ZERO(&fds_read
); FD_ZERO(&fds_write
);
304 FD_SET(fd_np2
, &fds_write
);
305 tv
.tv_sec
= DO_TIMEOUT
;
307 retval
= select(fd_np2
+1, &fds_read
, &fds_write
, NULL
, &tv
);
309 /* Correct amount of ready file descriptors? Just 1 write */
311 snprintf(errbuf
, sizeof(errbuf
),
312 "one fd should be set%s", (retval
== 0 ? " (TIMEOUT)" : ""));
316 if(!empty_fds(fd_np2
+1, &fds_read
)) e(12, "read should NOT be set");
320 /* Check if we can write to the pipe */
321 FD_ZERO(&fds_read
); FD_ZERO(&fds_write
);
322 FD_SET(fd_ap
[1], &fds_write
);
323 tv
.tv_sec
= DO_TIMEOUT
;
325 retval
= select(fd_ap
[1]+1, NULL
, &fds_write
, NULL
, &tv
);
327 /* Correct amount of ready file descriptors? Just 1 write */
329 snprintf(errbuf
, sizeof(errbuf
),
330 "one fd should be set%s", (retval
== 0 ? " (TIMEOUT)" : ""));
334 /* Make sure write bit is set (and just 1 bit) */
335 FD_ZERO(&fds_compare_write
); FD_SET(fd_ap
[1], &fds_compare_write
);
336 if(!compare_fds(fd_ap
[1]+1, &fds_compare_write
, &fds_write
))
337 e(14, "write should be set");
339 /* Intentionally test reading from pipe and letting it time out. */
340 FD_SET(fd_ap
[0], &fds_read
);
344 retval
= select(fd_ap
[0]+1, &fds_read
, NULL
, NULL
, &tv
);
347 /* Did we time out? */
348 if(retval
!= 0) e(15, "we should have timed out");
350 /* Did it take us approximately 1 second? */
351 if((int) (end
- start
) != 1) {
352 snprintf(errbuf
, sizeof(errbuf
),
353 "time out is not 1 second (instead, it is %ld)",
354 (long int) (end
- start
));
358 /* Do another read, but this time we expect incoming data from child. */
360 FD_SET(fd_ap
[0], &fds_read
);
361 tv
.tv_sec
= DO_TIMEOUT
;
363 retval
= select(fd_ap
[0]+1, &fds_read
, NULL
, NULL
, &tv
);
365 /* Correct amount of ready file descriptors? Just 1 read. */
366 if(retval
!= 1) e(17, "one fd should be set");
368 /* Is the read bit set? And just 1 bit. */
369 FD_ZERO(&fds_compare_read
); FD_SET(fd_ap
[0], &fds_compare_read
);
370 if(!compare_fds(fd_ap
[0]+1, &fds_compare_read
, &fds_read
))
371 e(18, "read should be set.");
373 /* By convention fd_ap[0] is meant to be used for reading from the pipe and
374 * fd_ap[1] is meant for writing, where fd_ap is a an anonymous pipe.
375 * However, it is unspecified what happens when fd_ap[0] is used for writing
376 * and fd_ap[1] for reading. (It is unsupported on Minix.) As such, it is not
377 * necessary to make test cases for wrong pipe file descriptors using select.
380 waitpid(child
, &retval
, 0);
386 int main(int argc
, char **argv
) {
389 /* Get subtest number */
391 printf("Usage: %s subtest_no\n", argv
[0]);
393 } else if(sscanf(argv
[1], "%d", &subtest
) != 1) {
394 printf("Usage: %s subtest_no\n", argv
[0]);
398 /* Set up anonymous pipe */
399 if(pipe(fd_ap
) < 0) {
400 perror("Could not create anonymous pipe");
404 /* Create named pipe2. It is unlinked by do_parent. */
405 if(mkfifo(NAMEDPIPE1
, 0600) < 0) {
406 printf("Could not create named pipe %s", NAMEDPIPE1
);
411 if(mkfifo(NAMEDPIPE2
, 0600) < 0) {
412 printf("Could not create named pipe %s", NAMEDPIPE2
);
418 if(forkres
== 0) do_child();
419 else if(forkres
> 0) do_parent(forkres
);
420 else { /* Fork failed */
421 perror("Unable to fork");
425 exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/