test10 test12 test13 test14 test15 test16 test17 test18 test19 \
test21 test22 test23 test25 test26 test27 test28 test29 \
test30 test31 test32 test34 test35 test36 test37 test38 \
- test39 t10a t11a t11b
+ test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f
BIGOBJ= test20 test24
ROOTOBJ= test11 test33
clean:
cd select && make clean
- -rm -rf *.o *.s *.bak test? test?? t10a t11a t11b DIR*
+ -rm -rf *.o *.s *.bak test? test?? t10a t11a t11b t40a t40b t40c t40d t40e t40f DIR*
test1: test1.c
test2: test2.c
test37: test37.c
test38: test38.c
test39: test39.c
+test40: test40.c
+t40a: t40a.c
+t40b: t40b.c
+t40c: t40c.c
+t40d: t40d.c
+t40e: t40e.c
+t40f: t40f.c
# Print test welcome message
clr
-echo "Running POSIX compliance test suite. There are 39 tests in total."
+echo "Running POSIX compliance test suite. There are 42 tests in total."
echo " "
# Run all the tests, keeping track of who failed.
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
- 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 \
+ 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
sh1.sh sh2.sh
do total=`expr $total + 1`
if ./test$i
--- /dev/null
+/* t40a.c
+ *
+ * Test FD_* macros
+ *
+ * Select works on regular files, (pseudo) terminal devices, streams-based
+ * files, FIFOs, pipes, and sockets. This test verifies the FD_* macros.
+ *
+ * This test is part of a bigger select test. It expects as argument which sub-
+ * test it is.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifndef OPEN_MAX
+# define OPEN_MAX 1024
+#endif
+
+#define MAX_ERROR 5
+
+int errct = 0, subtest = -1;
+
+void e(int n, char *s) {
+ printf("Subtest %d, error %d, %s\n", subtest, n, s);
+
+ if (errct++ > MAX_ERROR) {
+ printf("Too many errors; test aborted\n");
+ exit(errct);
+ }
+}
+
+int main(int argc, char **argv) {
+ fd_set fds;
+ int i;
+
+ /* Get subtest number */
+ if(argc != 2) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-1);
+ } else if(sscanf(argv[1], "%d", &subtest) != 1) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-1);
+ }
+
+ /* FD_ZERO */
+ FD_ZERO(&fds);
+ for(i = 0; i < OPEN_MAX; i++) {
+ if(FD_ISSET(i, &fds)) {
+ e(1, "fd set should be completely empty");
+ break;
+ }
+ }
+
+ /* FD_SET */
+ for(i = 0; i < OPEN_MAX; i++) FD_SET(i, &fds);
+ for(i = 0; i < OPEN_MAX; i++) {
+ if(!FD_ISSET(i, &fds)) {
+ e(2, "fd set should be completely filled");
+ break;
+ }
+ }
+
+ /* Reset to empty set and verify it's really empty */
+ FD_ZERO(&fds);
+ for(i = 0; i < OPEN_MAX; i++) {
+ if(FD_ISSET(i, &fds)) {
+ e(3, "fd set should be completely empty");
+ break;
+ }
+ }
+
+ /* Let's try a variation on filling the set */
+ for(i = 0; i < OPEN_MAX; i += 2) FD_SET(i, &fds);
+ for(i = 0; i < OPEN_MAX - 1; i+= 2 ) {
+ if(!(FD_ISSET(i, &fds) && !FD_ISSET(i+1, &fds))) {
+ e(4, "bit pattern does not match");
+ break;
+ }
+ }
+
+ /* Reset to empty set and verify it's really empty */
+ FD_ZERO(&fds);
+ for(i = 0; i < OPEN_MAX; i++) {
+ if(FD_ISSET(i, &fds)) {
+ e(5,"fd set should be completely empty");
+ break;
+ }
+ }
+
+ /* Let's try another variation on filling the set */
+ for(i = 0; i < OPEN_MAX - 1; i += 2) FD_SET(i+1, &fds);
+ for(i = 0; i < OPEN_MAX - 1; i+= 2 ) {
+ if(!(FD_ISSET(i+1, &fds) && !FD_ISSET(i, &fds))) {
+ e(6, "bit pattern does not match");
+ break;
+ }
+ }
+
+ /* Reset to empty set and verify it's really empty */
+ FD_ZERO(&fds);
+ for(i = 0; i < OPEN_MAX; i++) {
+ if(FD_ISSET(i, &fds)) {
+ e(7, "fd set should be completely empty");
+ break;
+ }
+ }
+
+ /* FD_CLR */
+ for(i = 0; i < OPEN_MAX; i++) FD_SET(i, &fds); /* Set all bits */
+ for(i = 0; i < OPEN_MAX; i++) FD_CLR(i, &fds); /* Clear all bits */
+ for(i = 0; i < OPEN_MAX; i++) {
+ if(FD_ISSET(i, &fds)) {
+ e(8, "all bits in fd set should be cleared");
+ break;
+ }
+ }
+
+ /* Reset to empty set and verify it's really empty */
+ FD_ZERO(&fds);
+ for(i = 0; i < OPEN_MAX; i++) {
+ if(FD_ISSET(i, &fds)) {
+ e(9, "fd set should be completely empty");
+ break;
+ }
+ }
+
+ for(i = 0; i < OPEN_MAX; i++) FD_SET(i, &fds); /* Set all bits */
+ for(i = 0; i < OPEN_MAX; i += 2) FD_CLR(i, &fds); /* Clear all bits */
+ for(i = 0; i < OPEN_MAX; i += 2) {
+ if(FD_ISSET(i, &fds)) {
+ e(10, "all even bits in fd set should be cleared");
+ break;
+ }
+ }
+
+ exit(errct);
+}
--- /dev/null
+/* t40b.c
+ *
+ * Test regular files
+ *
+ * Select works on regular files, (pseudo) terminal devices, streams-based
+ * files, FIFOs, pipes, and sockets. This test verifies selecting for regular
+ * file descriptors. "File descriptors associated with regular files shall
+ * always select true for ready to read, ready to write, and error conditions"
+ * - Open Group. Although we set a timeout, the select should return
+ * immediately.
+ *
+ * This test is part of a bigger select test. It expects as argument which sub-
+ * test it is.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <errno.h>
+#include <time.h>
+
+#define FILE1 "/tmp/selecttest01-1"
+#define FILES 2
+#define TIME 3
+
+#define MAX_ERROR 10
+
+int errct = 0, subtest = -1;
+char errorbuf[1000];
+
+void e(int n, char *s) {
+ printf("Subtest %d, error %d, %s\n", subtest, n, s);
+
+ if (errct++ > MAX_ERROR) {
+ printf("Too many errors; test aborted\n");
+ exit(errct);
+ }
+}
+
+int main(int argc, char **argv) {
+ int fd1, fd2, retval;
+ fd_set fds_read, fds_write, fds_error;
+ struct timeval tv;
+ time_t start, end;
+
+ /* Get subtest number */
+ if(argc != 2) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-1);
+ } else if(sscanf(argv[1], "%d", &subtest) != 1) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-1);
+ }
+
+ /* Set timeout */
+ tv.tv_sec = TIME;
+ tv.tv_usec = 0;
+
+ /* Open a file for writing */
+ if((fd1 = open(FILE1, O_WRONLY|O_CREAT, 0644)) == -1) {
+ snprintf(errorbuf, sizeof(errorbuf), "failed to open file %s for writing",
+ FILE1);
+ e(1, errorbuf);
+ perror(NULL);
+ exit(1);
+ }
+
+ /* Open the same file for reading */
+ if((fd2 = open(FILE1, O_RDONLY)) == -1) {
+ snprintf(errorbuf, sizeof(errorbuf), "failed to open file %s for reading",
+ FILE1);
+ e(2, errorbuf);
+ perror(NULL);
+ exit(1);
+ }
+
+ /* Clear file descriptor bit masks */
+ FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
+
+ /* Fill bit mask */
+ FD_SET(fd1, &fds_write);
+ FD_SET(fd2, &fds_read);
+ FD_SET(fd1, &fds_error);
+ FD_SET(fd2, &fds_error);
+
+ /* Do the select and time how long it takes */
+ start = time(NULL);
+ retval = select(fd2+1, &fds_read, &fds_write, &fds_error, &tv);
+ end = time(NULL);
+
+ /* Correct amount of ready file descriptors? 1 read + 1 write + 2 errors */
+ if(retval != 4) {
+ e(3, "four fds should be set");
+ }
+
+ /* Test resulting bit masks */
+ if(!FD_ISSET(fd1, &fds_write)) e(4, "write should be set");
+ if(!FD_ISSET(fd2, &fds_read)) e(5, "read should be set");
+ if(!FD_ISSET(fd1, &fds_error)) e(6, "error should be set");
+ if(!FD_ISSET(fd2, &fds_error)) e(7, "error should be set");
+
+ /* Was it instantaneous? */
+ if(end-start != TIME - TIME) {
+ snprintf(errorbuf,sizeof(errorbuf),"time spent blocking is not %d, but %ld",
+ TIME - TIME, (long int) (end-start));
+ e(8, "time spent blocking is not %d, but %ld");
+ }
+
+ /* Wait for read to become ready on O_WRONLY. This should fail immediately. */
+ FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
+ FD_SET(fd1, &fds_read);
+ FD_SET(fd1, &fds_error);
+ FD_SET(fd2, &fds_error);
+ tv.tv_sec = TIME;
+ tv.tv_usec = 0;
+ retval = select(fd2+1, &fds_read, NULL, &fds_error, &tv);
+
+ /* Correct amount of ready file descriptors? 1 read + 2 error */
+ if(retval != 3) e(9, "incorrect amount of ready file descriptors");
+ if(!FD_ISSET(fd1, &fds_read)) e(10, "read should be set");
+ if(!FD_ISSET(fd1, &fds_error)) e(11, "error should be set");
+ if(!FD_ISSET(fd2, &fds_error)) e(12, "error should be set");
+
+ /* Try again as above, bit this time with O_RDONLY in the write set */
+ FD_ZERO(&fds_error);
+ FD_SET(fd2, &fds_write);
+ FD_SET(fd1, &fds_error);
+ FD_SET(fd2, &fds_error);
+ tv.tv_sec = TIME;
+ tv.tv_usec = 0;
+ retval = select(fd2+1, NULL, &fds_write, &fds_error, &tv);
+
+ /* Correct amount of ready file descriptors? 1 write + 2 errors */
+ if(retval != 3) e(13, "incorrect amount of ready file descriptors");
+ if(!FD_ISSET(fd2, &fds_write)) e(14, "write should be set");
+ if(!FD_ISSET(fd1, &fds_error)) e(15, "error should be set");
+ if(!FD_ISSET(fd2, &fds_error)) e(16, "error should be set");
+
+ close(fd1);
+ close(fd2);
+ unlink(FILE1);
+
+ exit(errct);
+}
--- /dev/null
+/* t40c.c
+ *
+ * Test (pseudo) terminal devices
+ *
+ * Select works on regular files, (pseudo) terminal devices, streams-based
+ * files, FIFOs, pipes, and sockets. This test verifies selecting for (pseudo)
+ * terminal devices.
+ *
+ * This test is part of a bigger select test. It expects as argument which sub-
+ * test it is.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <string.h>
+
+#define TERMINALW "/dev/ttyp0"
+#define TERMINALR "/dev/ptyp0"
+#define SENDSTRING "minixrocks"
+#define MAX_ERROR 5
+
+int errct = 0, subtest = -1;
+
+void e(int n, char *s) {
+ printf("Subtest %d, error %d, %s\n", subtest, n, s);
+
+ if (errct++ > MAX_ERROR) {
+ printf("Too many errors; test aborted\n");
+ exit(errct);
+ }
+}
+
+int do_child(void) {
+ int fd, retval;
+ struct timeval tv;
+
+ /* Opening master terminal for writing */
+ if((fd = open(TERMINALW, O_WRONLY)) == -1) {
+ printf("Error opening %s for writing, signalling parent to quit\n",
+ TERMINALW);
+ perror(NULL);
+ printf("Please make sure that %s is not in use while running this test\n",
+ TERMINALW);
+ exit(-1);
+ }
+
+ /* Going to sleep for two seconds to allow the parent proc to get ready */
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ select(0, NULL, NULL, NULL, &tv);
+
+ /* Try to write. Doesn't matter how many bytes we actually send. */
+ retval = write(fd, SENDSTRING, strlen(SENDSTRING));
+ close(fd);
+
+ /* Wait for another second to allow the parent to process incoming data */
+ tv.tv_usec = 1000000;
+ retval = select(0,NULL, NULL, NULL, &tv);
+ exit(0);
+}
+
+int do_parent(int child) {
+ int fd;
+ fd_set fds_read, fds_write, fds_error;
+ int retval;
+
+ /* Open slave terminal for reading */
+ if((fd = open(TERMINALR, O_RDONLY)) == -1) {
+ printf("Error opening %s for reading\n", TERMINALR);
+ perror(NULL);
+ printf("Please make sure that %s is not in use while running this test.\n",
+ TERMINALR);
+ waitpid(child, &retval, 0);
+ exit(-1);
+ }
+
+ /* Clear bit masks */
+ FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
+ /* Set read bits */
+ FD_SET(fd, &fds_read);
+ FD_SET(fd, &fds_write);
+
+ /* Test if we can read or write from/to fd. As fd is opened read only we
+ * cannot actually write, so the select should return immediately with fd
+ * set in fds_write, but not in fds_read. Note that the child waits two
+ * seconds before sending data. This gives us the opportunity run this
+ * sub-test as reading from fd is blocking at this point. */
+ retval = select(fd+1, &fds_read, &fds_write, &fds_error, NULL);
+
+ if(retval != 1) e(1, "incorrect amount of ready file descriptors");
+
+
+ if(FD_ISSET(fd, &fds_read)) e(2, "read should NOT be set");
+ if(!FD_ISSET(fd, &fds_write)) e(3, "write should be set");
+ if(FD_ISSET(fd, &fds_error)) e(4, "error should NOT be set");
+
+ /* Block until ready; until child wrote stuff */
+ FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
+ FD_SET(fd, &fds_read);
+ retval = select(fd+1, &fds_read, NULL, &fds_error, NULL);
+
+ if(retval != 1) e(5, "incorrect amount of ready file descriptors");
+ if(!FD_ISSET(fd, &fds_read)) e(6, "read should be set");
+ if(FD_ISSET(fd, &fds_error)) e(7, "error should not be set");
+
+
+ FD_ZERO(&fds_read); FD_ZERO(&fds_error);
+ FD_SET(fd,&fds_write);
+ retval = select(fd+1, NULL, &fds_write, NULL, NULL);
+ /* As it is impossible to write to a read only fd, this select should return
+ * immediately with fd set in fds_write. */
+ if(retval != 1) e(8, "incorrect amount or ready file descriptors");
+
+ close(fd);
+ waitpid(child, &retval, 0);
+ exit(errct);
+}
+
+int main(int argc, char **argv) {
+ int forkres;
+
+ /* Get subtest number */
+ if(argc != 2) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-1);
+ } else if(sscanf(argv[1], "%d", &subtest) != 1) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-1);
+ }
+
+ forkres = fork();
+ if(forkres == 0) do_child();
+ else if(forkres > 0) do_parent(forkres);
+ else { /* Fork failed */
+ perror("Unable to fork");
+ exit(-1);
+ }
+
+ exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/
+
+}
--- /dev/null
+/* t40d.c
+ *
+ * Test FIFOs and pipes
+ *
+ * Select works on regular files, (pseudo) terminal devices, streams-based
+ * files, FIFOs, pipes, and sockets. This test verifies selecting for FIFOs
+ * (named pipes) and pipes (anonymous pipes). This test will not verify most
+ * error file descriptors, as the setting of this fdset in the face of an error
+ * condition is implementation-specific (except for regular files (alway set)
+ * and sockets (protocol-specific or OOB data received), but those file types
+ * are not being tested in this specific test).
+ *
+ * This test is part of a bigger select test. It expects as argument which sub-
+ * test it is.
+ *
+ * [1] If a socket has a pending error, it shall be considered to have an
+ * exceptional condition pending. Otherwise, what constitutes an exceptional
+ * condition is file type-specific. For a file descriptor for use with a
+ * socket, it is protocol-specific except as noted below. For other file types
+ * it is implementation-defined. If the operation is meaningless for a
+ * particular file type, pselect() or select() shall indicate that the
+ * descriptor is ready for read or write operations, and shall indicate that
+ * the descriptor has no exceptional condition pending.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+
+#define NAMEDPIPE1 "/tmp/selecttest03-1"
+#define NAMEDPIPE2 "/tmp/selecttest03-2"
+#define SENDSTRING "minixrocks"
+#define DO_HANDLEDATA 1
+#define DO_PAUSE 3
+#define DO_TIMEOUT 7
+#define MAX_ERROR 5
+
+int errct = 0, subtest = -1;
+char errbuf[1000];
+int fd_ap[2]; /* Anonymous pipe; read from fd_ap[0], write to fd_ap[1] */
+int fd_np1; /* Named pipe */
+int fd_np2; /* Named pipe */
+
+void e(int n, char *s) {
+ printf("Subtest %d, error %d, %s\n", subtest, n, s);
+
+ if (errct++ > MAX_ERROR) {
+ printf("Too many errors; test aborted\n");
+ exit(errct);
+ }
+}
+
+void do_child(void) {
+ struct timeval tv;
+ int retval;
+
+ /* Open named pipe for writing. This will block until a reader arrives. */
+ if((fd_np1 = open(NAMEDPIPE1, O_WRONLY)) == -1) {
+ printf("Error opening %s for writing, signalling parent to quit\n",
+ NAMEDPIPE1);
+ perror(NULL);
+ printf("Please make sure that %s is not in use while running this test\n",
+ NAMEDPIPE1);
+ exit(-1);
+ }
+
+ /* Going to sleep for three seconds to allow the parent proc to get ready */
+ tv.tv_sec = DO_HANDLEDATA;
+ tv.tv_usec = 0;
+ select(0, NULL, NULL, NULL, &tv);
+
+ /* Try to write. Doesn't matter how many bytes we actually send. */
+ retval = write(fd_np1, SENDSTRING, strlen(SENDSTRING));
+
+ /* Wait for another second to allow the parent to process incoming data */
+ tv.tv_sec = DO_HANDLEDATA;
+ tv.tv_usec = 0;
+ retval = select(0,NULL, NULL, NULL, &tv);
+
+ close(fd_np1);
+
+ /* Wait for another second to allow the parent to process incoming data */
+ tv.tv_sec = DO_HANDLEDATA;
+ tv.tv_usec = 0;
+ retval = select(0,NULL, NULL, NULL, &tv);
+
+ /* Open named pipe for reading. This will block until a writer arrives. */
+ if((fd_np2 = open(NAMEDPIPE2, O_RDONLY)) == -1) {
+ printf("Error opening %s for reading, signalling parent to quit\n",
+ NAMEDPIPE2);
+ perror(NULL);
+ printf("Please make sure that %s is not in use while running this test\n",
+ NAMEDPIPE2);
+ exit(-1);
+ }
+
+ /* Wait for another second to allow the parent to run some tests. */
+ tv.tv_sec = DO_HANDLEDATA;
+ tv.tv_usec = 0;
+ retval = select(0, NULL, NULL, NULL, &tv);
+
+ close(fd_np2);
+
+ /* Anonymous pipe */
+
+ /* Let the parent do initial read and write tests from and to the pipe. */
+ tv.tv_sec = DO_PAUSE;
+ tv.tv_usec = 0;
+ retval = select(0, NULL, NULL, NULL, &tv);
+
+ /* Unblock blocking read select by writing data */
+ if(write(fd_ap[1], SENDSTRING, strlen(SENDSTRING)) < 0) {
+ perror("Could not write to anonymous pipe");
+ exit(-1);
+ }
+
+ exit(0);
+}
+
+int count_fds(int nfds, fd_set *fds) {
+ /* Return number of bits set in fds */
+ int i, result = 0;
+ assert(fds != NULL && nfds > 0);
+ for(i = 0; i < nfds; i++) {
+ if(FD_ISSET(i, fds)) result++;
+ }
+ return result;
+}
+
+int empty_fds(int nfds, fd_set *fds) {
+ /* Returns nonzero if the first bits up to nfds in fds are not set */
+ int i;
+ assert(fds != NULL && nfds > 0);
+ for(i = 0; i < nfds; i++) if(FD_ISSET(i, fds)) return 0;
+ return 1;
+}
+
+int compare_fds(int nfds, fd_set *lh, fd_set *rh) {
+ /* Returns nonzero if lh equals rh up to nfds bits */
+ int i;
+ assert(lh != NULL && rh != NULL && nfds > 0);
+ for(i = 0; i < nfds; i++) {
+ if((FD_ISSET(i, lh) && !FD_ISSET(i, rh)) ||
+ (!FD_ISSET(i, lh) && FD_ISSET(i, rh))) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void dump_fds(int nfds, fd_set *fds) {
+ /* Print a graphical representation of bits in fds */
+ int i;
+ if(fds != NULL && nfds > 0) {
+ for(i = 0; i < nfds; i++) printf("%d ", (FD_ISSET(i, fds) ? 1 : 0));
+ printf("\n");
+ }
+}
+
+void do_parent(int child) {
+ fd_set fds_read, fds_write, fds_error;
+ fd_set fds_compare_read, fds_compare_write;
+ struct timeval tv;
+ time_t start, end;
+ int retval;
+ char buf[20];
+
+ /* Open named pipe for reading. This will block until a writer arrives. */
+ if((fd_np1 = open(NAMEDPIPE1, O_RDONLY)) == -1) {
+ printf("Error opening %s for reading\n", NAMEDPIPE1);
+ perror(NULL);
+ printf("Please make sure that %s is not in use while running this test.\n",
+ NAMEDPIPE1);
+ waitpid(child, &retval, 0);
+ exit(-1);
+ }
+
+ /* Clear bit masks */
+ FD_ZERO(&fds_read); FD_ZERO(&fds_write);
+ /* Set read and write bits */
+ FD_SET(fd_np1, &fds_read);
+ FD_SET(fd_np1, &fds_write);
+ tv.tv_sec = DO_TIMEOUT;
+ tv.tv_usec = 0;
+
+ /* Test if we can read or write from/to fd_np1. As fd_np1 is opened read only
+ * we cannot actually write, so the select should return immediately [1] and
+ * the offending bit set in the fd set. We read from a pipe that is opened
+ * with O_NONBLOCKING cleared, so it is guaranteed we can read.
+ * However, at this moment the writer is sleeping, so the pipe is empty and
+ * read is supposed to block. Therefore, only 1 file descriptor should be
+ * ready. A timeout value is still set in case an error occurs in a faulty
+ * implementation. */
+ retval = select(fd_np1+1, &fds_read, &fds_write, NULL, &tv);
+
+ /* Did we receive an error? */
+ if(retval <= 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
+ e(1, errbuf);
+ }
+
+ if(!empty_fds(fd_np1+1,&fds_read)) e(2, "no read bits should be set");
+
+
+ /* Make sure the write bit is set (and just 1 bit) */
+ FD_ZERO(&fds_compare_write); FD_SET(fd_np1, &fds_compare_write);
+ if(!compare_fds(fd_np1+1, &fds_compare_write, &fds_write))
+ e(3, "write should be set");
+
+ /* Clear sets and set up new bit masks */
+ FD_ZERO(&fds_read); FD_ZERO(&fds_write);
+ FD_SET(fd_np1, &fds_read);
+ tv.tv_sec = DO_TIMEOUT; /* To make sure we get to see some error messages
+ instead of blocking forever when the
+ implementation is faulty. A timeout causes retval
+ to be 0. */
+ tv.tv_usec = 0;
+ /* The sleeping writer is about to wake up and write data to the pipe. */
+ retval = select(fd_np1+1, &fds_read, &fds_write, NULL, &tv);
+
+ /* Correct amount of ready file descriptors? Just 1 read */
+ if(retval != 1) {
+ snprintf(errbuf, sizeof(errbuf),
+ "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
+ e(4, errbuf);
+ }
+
+ if(!FD_ISSET(fd_np1, &fds_read)) e(5, "read should be set");
+
+ /* Note that we left the write set empty. This should be equivalent to
+ * setting this parameter to NULL. */
+ if(!empty_fds(fd_np1+1, &fds_write)) e(6, "write should NOT be set");
+
+ /* In case something went wrong above, we might end up with a child process
+ * blocking on a write call we close the file descriptor now. Synchronize on
+ * a read. */
+ if(read(fd_np1, buf, sizeof(SENDSTRING)) < 0) perror("Read error");
+
+ /* Close file descriptor. We're going to reverse the test */
+ close(fd_np1);
+
+ /* Wait for a second to allow the child to close the pipe as well */
+ tv.tv_sec = DO_HANDLEDATA;
+ tv.tv_usec = 0;
+ retval = select(0,NULL, NULL, NULL, &tv);
+
+ /* Open named pipe for writing. This call blocks until a reader arrives. */
+ if((fd_np2 = open(NAMEDPIPE2, O_WRONLY)) == -1) {
+ printf("Error opening %s for writing\n",
+ NAMEDPIPE2);
+ perror(NULL);
+ printf("Please make sure that %s is not in use while running this test\n",
+ NAMEDPIPE2);
+ exit(-1);
+ }
+
+ /* At this moment the child process has opened the named pipe for reading and
+ * we have opened it for writing. We're now going to reverse some of the
+ * tests we've done earlier. */
+
+ /* Clear sets and set up bit masks */
+ FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
+ FD_SET(fd_np2, &fds_read);
+ FD_SET(fd_np2, &fds_write);
+ tv.tv_sec = DO_TIMEOUT;
+ tv.tv_usec = 0;
+ /* Select for reading from an fd opened O_WRONLY. This should return
+ * immediately as it is not a meaningful operation [1] and is therefore not
+ * blocking. The select should return two file descriptors are ready (the
+ * failing read and valid write). */
+
+ retval = select(fd_np2+1, &fds_read, &fds_write, &fds_error, &tv);
+
+ /* Did we receive an error? */
+ if(retval <= 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "two fds should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
+ e(7, errbuf);
+ }
+
+ /* Make sure read bit is set (and just 1 bit) */
+ FD_ZERO(&fds_compare_read); FD_SET(fd_np2, &fds_compare_read);
+ if(!compare_fds(fd_np2+1, &fds_compare_read, &fds_read))
+ e(8, "read should be set");
+
+ /* Write bit should be set (and just 1 bit) */
+ FD_ZERO(&fds_compare_write); FD_SET(fd_np2, &fds_compare_write);
+ if(!compare_fds(fd_np2+1, &fds_compare_write, &fds_write))
+ e(9, "write should be set");
+
+ if(!empty_fds(fd_np2+1, &fds_error))
+ e(10, "Error should NOT be set");
+
+ FD_ZERO(&fds_read); FD_ZERO(&fds_write);
+ FD_SET(fd_np2, &fds_write);
+ tv.tv_sec = DO_TIMEOUT;
+ tv.tv_usec = 0;
+ retval = select(fd_np2+1, &fds_read, &fds_write, NULL, &tv);
+
+ /* Correct amount of ready file descriptors? Just 1 write */
+ if(retval != 1) {
+ snprintf(errbuf, sizeof(errbuf),
+ "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
+ e(11, errbuf);
+ }
+
+ if(!empty_fds(fd_np2+1, &fds_read)) e(12, "read should NOT be set");
+
+ /* Anonymous pipe */
+
+ /* Check if we can write to the pipe */
+ FD_ZERO(&fds_read); FD_ZERO(&fds_write);
+ FD_SET(fd_ap[1], &fds_write);
+ tv.tv_sec = DO_TIMEOUT;
+ tv.tv_usec = 0;
+ retval = select(fd_ap[1]+1, NULL, &fds_write, NULL, &tv);
+
+ /* Correct amount of ready file descriptors? Just 1 write */
+ if(retval != 1) {
+ snprintf(errbuf, sizeof(errbuf),
+ "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
+ e(13, errbuf);
+ }
+
+ /* Make sure write bit is set (and just 1 bit) */
+ FD_ZERO(&fds_compare_write); FD_SET(fd_ap[1], &fds_compare_write);
+ if(!compare_fds(fd_ap[1]+1, &fds_compare_write, &fds_write))
+ e(14, "write should be set");
+
+ /* Intentionally test reading from pipe and letting it time out. */
+ FD_SET(fd_ap[0], &fds_read);
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ start = time(NULL);
+ retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
+ end = time(NULL);
+
+ /* Did we time out? */
+ if(retval != 0) e(15, "we should have timed out");
+
+ /* Did it take us approximately 1 second? */
+ if((int) (end - start) != 1) {
+ snprintf(errbuf, sizeof(errbuf),
+ "time out is not 1 second (instead, it is %ld)",
+ (long int) (end - start));
+ e(16, errbuf);
+ }
+
+ /* Do another read, but this time we expect incoming data from child. */
+ FD_ZERO(&fds_read);
+ FD_SET(fd_ap[0], &fds_read);
+ tv.tv_sec = DO_TIMEOUT;
+ tv.tv_usec = 0;
+ retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
+
+ /* Correct amount of ready file descriptors? Just 1 read. */
+ if(retval != 1) e(17, "one fd should be set");
+
+ /* Is the read bit set? And just 1 bit. */
+ FD_ZERO(&fds_compare_read); FD_SET(fd_ap[0], &fds_compare_read);
+ if(!compare_fds(fd_ap[0]+1, &fds_compare_read, &fds_read))
+ e(18, "read should be set.");
+
+ /* By convention fd_ap[0] is meant to be used for reading from the pipe and
+ * fd_ap[1] is meant for writing, where fd_ap is a an anonymous pipe.
+ * However, it is unspecified what happens when fd_ap[0] is used for writing
+ * and fd_ap[1] for reading. (It is unsupported on Minix.) As such, it is not
+ * necessary to make test cases for wrong pipe file descriptors using select.
+ */
+
+ waitpid(child, &retval, 0);
+ unlink(NAMEDPIPE2);
+ unlink(NAMEDPIPE1);
+ exit(errct);
+}
+
+int main(int argc, char **argv) {
+ int forkres;
+
+ /* Get subtest number */
+ if(argc != 2) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-2);
+ } else if(sscanf(argv[1], "%d", &subtest) != 1) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-2);
+ }
+
+ /* Set up anonymous pipe */
+ if(pipe(fd_ap) < 0) {
+ perror("Could not create anonymous pipe");
+ exit(-1);
+ }
+
+ /* Create named pipe2. It is unlinked by do_parent. */
+ if(mkfifo(NAMEDPIPE1, 0600) < 0) {
+ printf("Could not create named pipe %s", NAMEDPIPE1);
+ perror(NULL);
+ exit(-1);
+ }
+
+ if(mkfifo(NAMEDPIPE2, 0600) < 0) {
+ printf("Could not create named pipe %s", NAMEDPIPE2);
+ perror(NULL);
+ exit(-1);
+ }
+
+ forkres = fork();
+ if(forkres == 0) do_child();
+ else if(forkres > 0) do_parent(forkres);
+ else { /* Fork failed */
+ perror("Unable to fork");
+ exit(-1);
+ }
+
+ exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/
+
+}
--- /dev/null
+/* t40e.c
+ *
+ * Test sockets
+ *
+ * Select works on regular files, (pseudo) terminal devices, streams-based
+ * files, FIFOs, pipes, and sockets. This test verifies selecting for sockets.
+ *
+ * This test is part of a bigger select test. It expects as argument which sub-
+ * test it is.
+ *
+ * Specific rules for sockets:
+ * If a socket has a pending error, it shall be considered to have an
+ * exceptional condition pending. Otherwise, what constitutes an exceptional
+ * condition is file type-specific. For a file descriptor for use with a
+ * socket, it is protocol-specific except as noted below. For other file types
+ * it is implementation-defined. If the operation is meaningless for a
+ * particular file type, pselect() or select() shall indicate that the
+ * descriptor is ready for read or write operations, and shall indicate that
+ * the descriptor has no exceptional condition pending.
+ *
+ * [1] If a descriptor refers to a socket, the implied input function is the
+ * recvmsg()function with parameters requesting normal and ancillary data, such
+ * that the presence of either type shall cause the socket to be marked as
+ * readable. The presence of out-of-band data shall be checked if the socket
+ * option SO_OOBINLINE has been enabled, as out-of-band data is enqueued with
+ * normal data. If the socket is currently listening, then it shall be marked
+ * as readable if an incoming connection request has been received, and a call
+ * to the accept() function shall complete without blocking.
+ *
+ * [2] If a descriptor refers to a socket, the implied output function is the
+ * sendmsg() function supplying an amount of normal data equal to the current
+ * value of the SO_SNDLOWAT option for the socket. If a non-blocking call to
+ * the connect() function has been made for a socket, and the connection
+ * attempt has either succeeded or failed leaving a pending error, the socket
+ * shall be marked as writable.
+ *
+ * [3] A socket shall be considered to have an exceptional condition pending if
+ * a receive operation with O_NONBLOCK clear for the open file description and
+ * with the MSG_OOB flag set would return out-of-band data without blocking.
+ * (It is protocol-specific whether the MSG_OOB flag would be used to read
+ * out-of-band data.) A socket shall also be considered to have an exceptional
+ * condition pending if an out-of-band data mark is present in the receive
+ * queue. Other circumstances under which a socket may be considered to have an
+ * exceptional condition pending are protocol-specific and
+ * implementation-defined.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+#include <netdb.h>
+
+#define DO_HANDLEDATA 1
+#define DO_PAUSE 3
+#define DO_TIMEOUT 7
+#define MYPORT 3490
+#define NUMCHILDREN 5
+#define MAX_ERROR 10
+
+int errct = 0, subtest = -1;
+char errbuf[1000];
+
+void e(int n, char *s) {
+ printf("Subtest %d, error %d, %s\n", subtest, n, s);
+
+ if (errct++ > MAX_ERROR) {
+ printf("Too many errors; test aborted\n");
+ exit(errct);
+ }
+}
+
+/* All *_fds routines are helping routines. They intentionally use FD_* macros
+ in order to prevent making assumptions on how the macros are implemented.*/
+
+int count_fds(int nfds, fd_set *fds) {
+ /* Return number of bits set in fds */
+ int i, result = 0;
+ assert(fds != NULL && nfds > 0);
+ for(i = 0; i < nfds; i++) {
+ if(FD_ISSET(i, fds)) result++;
+ }
+ return result;
+}
+
+int empty_fds(int nfds, fd_set *fds) {
+ /* Returns nonzero if the first bits up to nfds in fds are not set */
+ int i;
+ assert(fds != NULL && nfds > 0);
+ for(i = 0; i < nfds; i++) if(FD_ISSET(i, fds)) return 0;
+ return 1;
+}
+
+int compare_fds(int nfds, fd_set *lh, fd_set *rh) {
+ /* Returns nonzero if lh equals rh up to nfds bits */
+ int i;
+ assert(lh != NULL && rh != NULL && nfds > 0);
+ for(i = 0; i < nfds; i++) {
+ if((FD_ISSET(i, lh) && !FD_ISSET(i, rh)) ||
+ (!FD_ISSET(i, lh) && FD_ISSET(i, rh))) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+void dump_fds(int nfds, fd_set *fds) {
+ /* Print a graphical representation of bits in fds */
+ int i;
+ if(fds != NULL && nfds > 0) {
+ for(i = 0; i < nfds; i++) printf("%d ", (FD_ISSET(i, fds) ? 1 : 0));
+ printf("\n");
+ }
+}
+
+void do_child(int childno) {
+ int fd_sock, port;
+ int retval;
+
+ fd_set fds_read, fds_write, fds_error;
+ fd_set fds_compare_write;
+
+ struct hostent *he;
+ struct sockaddr_in server;
+ struct timeval tv;
+
+ if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("Error getting socket\n");
+ exit(-1);
+ }
+
+ if((he = gethostbyname("127.0.0.1")) == NULL){/*"localhost" might be unknown*/
+ perror("Error resolving");
+ exit(-1);
+ }
+
+ /* Child 4 connects to the wrong port. See Actual testing description below.*/
+ port = (childno == 3 ? MYPORT + 1 : MYPORT);
+
+ memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
+ server.sin_family = AF_INET;
+ server.sin_port = htons(port);
+
+#if 0
+ printf("Going to connect to: %s:%d\n", inet_ntoa(server.sin_addr),
+ ntohs(server.sin_port));
+#endif
+
+ /* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
+#ifndef _MINIX
+ memset(server.sin_zero, '\0', sizeof server.sin_zero);
+#endif
+
+ /* Wait for parent to set up connection */
+ tv.tv_sec = (childno <= 1 ? DO_PAUSE : DO_TIMEOUT);
+ tv.tv_usec = 0;
+ retval = select(0, NULL, NULL, NULL, &tv);
+
+ /* All set, let's do some testing */
+ /* Children 3 and 4 do a non-blocking connect */
+ if(childno == 2 || childno == 3)
+ fcntl(fd_sock, F_SETFL, fcntl(fd_sock, F_GETFL, 0) | O_NONBLOCK);
+
+ if(connect(fd_sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
+ /* Well, we don't actually care. The connect is non-blocking and is
+ supposed to "in progress" at this point. */
+ }
+
+ if(childno == 2 || childno == 3) { /* Children 3 and 4 */
+ /* Open Group: "If a non-blocking call to the connect() function has been
+ made for a socket, and the connection attempt has either succeeded or
+ failed leaving a pending error, the socket shall be marked as writable.
+ ...
+ A socket shall be considered to have an exceptional condition pending if
+ a receive operation with O_NONBLOCK clear for the open file description
+ and with the MSG_OOB flag set would return out-of-band data without
+ blocking. (It is protocol-specific whether the MSG_OOB flag would be used
+ to read out-of-band data.) A socket shall also be considered to have an
+ exceptional condition pending if an out-of-band data mark is present in
+ the receive queue. Other circumstances under which a socket may be
+ considered to have an exceptional condition pending are protocol-specific
+ and implementation-defined."
+
+ In other words, it only makes sense for us to check the write set as the
+ read set is not expected to be set, but is allowed to be set (i.e.,
+ unspecified) and whether the error set is set is implementation-defined.
+ */
+ FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
+ FD_SET(fd_sock, &fds_write);
+ tv.tv_sec = DO_TIMEOUT;
+ tv.tv_usec = 0;
+ retval = select(fd_sock+1, NULL, &fds_write, NULL, &tv);
+
+
+ if(retval <= 0) e(6, "expected one fd to be ready");
+
+ FD_ZERO(&fds_compare_write); FD_SET(fd_sock, &fds_compare_write);
+ if(!compare_fds(fd_sock+1, &fds_compare_write, &fds_compare_write))
+ e(7, "write should be set");
+ }
+
+ if(close(fd_sock) < 0) {
+ perror("Error disconnecting");
+ exit(-1);
+ }
+
+ exit(errct);
+}
+
+void do_parent(void) {
+ int fd_sock, fd_new, yes = 1, exitstatus;
+ int sockets[NUMCHILDREN], i;
+ fd_set fds_read, fds_write, fds_error;
+ fd_set fds_compare_read, fds_compare_write;
+ struct timeval tv;
+ int retval, childresults = 0;
+
+ struct sockaddr_in my_addr;
+ struct sockaddr_in other_addr;
+ socklen_t other_size;
+
+ if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("Error getting socket\n");
+ exit(-1);
+ }
+
+ my_addr.sin_family = AF_INET;
+ my_addr.sin_port = htons(MYPORT); /* Short, network byte order */
+ my_addr.sin_addr.s_addr = INADDR_ANY;
+ /* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
+#ifndef _MINIX
+ memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
+#endif
+
+ /* Reuse port number. Not implemented in Minix. */
+#ifndef _MINIX
+ if(setsockopt(fd_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
+ perror("Error setting port reuse option");
+ exit(-1);
+ }
+#endif
+
+ /* Bind to port */
+ if(bind(fd_sock, (struct sockaddr *) &my_addr, sizeof my_addr) < 0) {
+ perror("Error binding to port");
+ exit(-1);
+ }
+
+ /* Mark socket to be used for incoming connections */
+ if(listen(fd_sock, 20) < 0) {
+ perror("Listen");
+ exit(-1);
+ }
+
+ /* Actual testing */
+ /* While sockets resemble file descriptors, they are not the same at all.
+ We can read/write from/to and close file descriptors, but we cannot open
+ them O_RDONLY or O_WRONLY; they are always O_RDWR (other flags do not make
+ sense regarding sockets). As such, we cannot provide wrong file descriptors
+ to select, except for descriptors that are not in use.
+ We will test standard behavior and what is described in [2]. [1] and [3]
+ are not possible to test on Minix, as Minix does not support OOB data. That
+ is, the TCP layer can handle it, but there is no socket interface for it.
+ Our test consists of waiting for input from the first two children and
+ waiting to write output [standard usage]. Then the first child closes its
+ connection we select for reading. This should fail with error set. Then we
+ close child number two on our side and select for reading. This should fail
+ with EBADF. Child number three shall then do a non-blocking connect (after
+ waiting for DO_PAUSE seconds) and do a select, resulting in being marked
+ ready for writing. Subsequently child number four also does a non-blocking
+ connect to loclhost on MYPORT+1 (causing the connect to fail) and then does
+ a select. This should result in write and error being set (error because of
+ pending error).
+ */
+
+ /* Accept and store connections from the first two children */
+ other_size = sizeof(other_addr);
+ for(i = 0; i < 2; i++) {
+ fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
+ if(fd_new < 0) break;
+ sockets[i] = fd_new;
+ }
+
+ /* If we break out of the for loop, we ran across an error and want to exit.
+ Check whether we broke out. */
+ if(fd_new < 0) {
+ perror("Error accepting connection");
+ exit(-1);
+ }
+
+ /* Select error condition checking */
+ for(childresults = 0; childresults < 2; childresults++) {
+ FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
+ FD_SET(sockets[childresults], &fds_read);
+ FD_SET(sockets[childresults], &fds_write);
+ FD_SET(sockets[childresults], &fds_error);
+ tv.tv_sec = DO_TIMEOUT;
+ tv.tv_usec = 0;
+
+ retval = select(sockets[childresults]+1, &fds_read, &fds_write, &fds_error,
+ &tv);
+
+ if(retval <= 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "two fds should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
+ e(1, errbuf);
+ }
+
+ FD_ZERO(&fds_compare_read); FD_ZERO(&fds_compare_write);
+ FD_SET(sockets[childresults], &fds_compare_write);
+
+ /* We can't say much about being ready for reading at this point or not. It
+ is not specified and the other side might have data ready for us to read
+ */
+ if(!compare_fds(sockets[childresults]+1, &fds_compare_write, &fds_write))
+ e(2, "write should be set");
+
+ if(!empty_fds(sockets[childresults]+1, &fds_error))
+ e(3, "no error should be set");
+ }
+
+
+ /* We continue by accepting a connection of child 3 */
+ fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
+ if(fd_new < 0) {
+ perror("Error accepting connection\n");
+ exit(-1);
+ }
+ sockets[2] = fd_new;
+
+ /* Child 4 will never connect */
+
+ /* Child 5 is still pending to be accepted. Open Group: "If the socket is
+ currently listening, then it shall be marked as readable if an incoming
+ connection request has been received, and a call to the accept() function
+ shall complete without blocking."*/
+ FD_ZERO(&fds_read);
+ FD_SET(fd_sock, &fds_read);
+ tv.tv_sec = DO_TIMEOUT;
+ tv.tv_usec = 0;
+ retval = select(fd_sock+1, &fds_read, NULL, NULL, &tv);
+ if(retval <= 0) {
+ snprintf(errbuf, sizeof(errbuf),
+ "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
+ e(4, errbuf);
+ }
+
+ /* Check read bit is set */
+ FD_ZERO(&fds_compare_read); FD_SET(fd_sock, &fds_compare_read);
+ if(!compare_fds(fd_sock+1, &fds_compare_read, &fds_read))
+ e(5, "read should be set");
+
+
+ /* Accept incoming connection to unblock child 5 */
+ fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
+ if(fd_new < 0) {
+ perror("Error accepting connection\n");
+ exit(-1);
+ }
+ sockets[4] = fd_new;
+
+
+ /* We're done, let's wait a second to synchronize children and parent. */
+ tv.tv_sec = DO_HANDLEDATA;
+ tv.tv_usec = 0;
+ select(0, NULL, NULL, NULL, &tv);
+
+ /* Close connection with children. */
+ for(i = 0; i < NUMCHILDREN; i++) {
+ if(i == 3) /* No need to disconnect child 4 that failed to connect. */
+ continue;
+
+ if(close(sockets[i]) < 0) {
+ perror(NULL);
+ }
+ }
+
+ /* Close listening socket */
+ if(close(fd_sock) < 0) {
+ perror("Closing listening socket");
+ errct++;
+ }
+
+ for(i = 0; i < NUMCHILDREN; i++) {
+ wait(&exitstatus); /* Wait for children */
+ if(exitstatus > 0)
+ errct += WEXITSTATUS(exitstatus); /* and count their errors, too. */
+ }
+
+ exit(errct);
+}
+
+int main(int argc, char **argv) {
+ int forkres, i;
+
+ /* Get subtest number */
+ if(argc != 2) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-2);
+ } else if(sscanf(argv[1], "%d", &subtest) != 1) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-2);
+ }
+
+ /* Fork off a bunch of children */
+ for(i = 0; i < NUMCHILDREN; i++) {
+ forkres = fork();
+ if(forkres == 0) do_child(i);
+ else if(forkres < 0) {
+ perror("Unable to fork");
+ exit(-1);
+ }
+ }
+ /* do_child always calls exit(), so when we end up here, we're the parent. */
+ do_parent();
+
+ exit(-2); /* We're not supposed to get here. Both do_* routines should exit.*/
+}
--- /dev/null
+/* t40f.c
+ *
+ * Test timing
+ *
+ * Select works on regular files, (pseudo) terminal devices, streams-based
+ * files, FIFOs, pipes, and sockets. This test verifies selecting with a time
+ * out set.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+
+#define DO_HANDLEDATA 1
+#define DO_PAUSE 3
+#define DO_TIMEOUT 7
+#define DO_DELTA 0.5
+#define MAX_ERROR 5
+#define DELTA(x,y) (x.tv_sec - y.tv_sec) * CLOCKS_PER_SEC \
+ + (x.tv_usec - y.tv_usec) * CLOCKS_PER_SEC / 1000000
+
+int errct = 0, subtest = -1, got_signal = 0;
+int fd_ap[2];
+
+void catch_signal(int sig_no) {
+ got_signal = 1;
+}
+
+void e(int n, char *s) {
+ printf("Subtest %d, error %d, %s\n", subtest, n, s);
+
+ if (errct++ > MAX_ERROR) {
+ printf("Too many errors; test aborted\n");
+ exit(errct);
+ }
+}
+
+float compute_diff(struct timeval start, struct timeval end, float compare) {
+ /* Compute time difference. It is assumed that the value of start <= end. */
+ clock_t delta;
+ int seconds, hundreths;
+ float diff;
+
+ delta = DELTA(end, start); /* delta is in ticks */
+ seconds = (int) (delta / CLOCKS_PER_SEC);
+ hundreths = (int) (delta * 100 / CLOCKS_PER_SEC) - (seconds * 100);
+
+ diff = seconds + (hundreths / 100.0);
+ diff -= compare;
+ if(diff < 0) diff *= -1; /* Make diff a positive value */
+
+ return diff;
+}
+
+void do_child(void) {
+ struct timeval tv;
+ int retval;
+
+ /* Let the parent do initial read and write tests from and to the pipe. */
+ tv.tv_sec = DO_PAUSE + DO_PAUSE + DO_PAUSE + 1;
+ tv.tv_usec = 0;
+ retval = select(0, NULL, NULL, NULL, &tv);
+
+ /* At this point the parent has a pending select with a DO_TIMEOUT timeout.
+ We're going to interrupt by sending a signal */
+ if(kill(getppid(), SIGUSR1) < 0) perror("Failed to send signal");
+
+ exit(0);
+}
+
+void do_parent(int child) {
+ fd_set fds_read;
+ struct timeval tv, start_time, end_time;
+ int retval;
+
+ /* Install signal handler for SIGUSR1 */
+ signal(SIGUSR1, catch_signal);
+
+ /* Parent and child share an anonymous pipe. Select for read and wait for the
+ timeout to occur. We wait for DO_PAUSE seconds. Let's see if that's
+ approximately right.*/
+ FD_ZERO(&fds_read);
+ FD_SET(fd_ap[0], &fds_read);
+ tv.tv_sec = DO_PAUSE;
+ tv.tv_usec = 0;
+
+ (void) gettimeofday(&start_time, NULL); /* Record starting time */
+ retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
+ (void) gettimeofday(&end_time, NULL); /* Record ending time */
+
+ /* Did we time out? */
+ if(retval != 0) e(1, "Should have timed out");
+
+ /* Approximately right? The standard does not specify how precise the timeout
+ should be. Instead, the granularity is implementation-defined. In this
+ test we assume that the difference should be no more than half a second.*/
+ if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
+ e(2, "Time difference too large");
+
+ /* Let's wait for another DO_PAUSE seconds, expressed as microseconds */
+ FD_ZERO(&fds_read);
+ FD_SET(fd_ap[0], &fds_read);
+ tv.tv_sec = 0;
+ tv.tv_usec = DO_PAUSE * 1000000L;
+
+ (void) gettimeofday(&start_time, NULL); /* Record starting time */
+ retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
+ (void) gettimeofday(&end_time, NULL); /* Record ending time */
+
+ if(retval != 0) e(3, "Should have timed out");
+ if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
+ e(4, "Time difference too large");
+
+ /* Let's wait for another DO_PAUSE seconds, expressed in seconds and micro
+ seconds. */
+ FD_ZERO(&fds_read);
+ FD_SET(fd_ap[0], &fds_read);
+ tv.tv_sec = DO_PAUSE - 1;
+ tv.tv_usec = (DO_PAUSE - tv.tv_sec) * 1000000L;
+
+ (void) gettimeofday(&start_time, NULL); /* Record starting time */
+ retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
+ (void) gettimeofday(&end_time, NULL); /* Record ending time */
+
+ if(retval != 0) e(5, "Should have timed out");
+ if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
+ e(6, "Time difference too large");
+
+ /* Finally, we test if our timeout is interrupted by a signal */
+ FD_ZERO(&fds_read);
+ FD_SET(fd_ap[0], &fds_read);
+ tv.tv_sec = DO_TIMEOUT;
+ tv.tv_usec = 0;
+
+ (void) gettimeofday(&start_time, NULL); /* Record starting time */
+ retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
+ (void) gettimeofday(&end_time, NULL); /* Record ending time */
+
+ if(retval != -1) e(7, "Should have been interrupted");
+ if(compute_diff(start_time, end_time, DO_TIMEOUT) < DO_DELTA)
+ e(8, "Failed to get interrupted by a signal");
+
+ if(!got_signal) e(9, "Failed to get interrupted by a signal");
+
+ waitpid(child, &retval, 0);
+ exit(errct);
+}
+
+int main(int argc, char **argv) {
+ int forkres;
+
+ /* Get subtest number */
+ if(argc != 2) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-2);
+ } else if(sscanf(argv[1], "%d", &subtest) != 1) {
+ printf("Usage: %s subtest_no\n", argv[0]);
+ exit(-2);
+ }
+
+ /* Set up anonymous pipe */
+ if(pipe(fd_ap) < 0) {
+ perror("Could not create anonymous pipe");
+ exit(-1);
+ }
+
+ forkres = fork();
+ if(forkres == 0) do_child();
+ else if(forkres > 0) do_parent(forkres);
+ else { /* Fork failed */
+ perror("Unable to fork");
+ exit(-1);
+ }
+
+ exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/
+
+}
--- /dev/null
+/* Test40.c
+ *
+ * Test select(...) system call
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+int main(int argc, char **argv) {
+ char *tests[] = {"t40a", "t40b", "t40c", "t40d", "t40e", "t40f"};
+ int no_tests, i, forkres, status = 0, errorct = 0;
+
+ no_tests = sizeof(tests) / sizeof(char *);
+
+ printf("Test 40 ");
+ fflush(stdout);
+
+ for(i = 0; i < no_tests; i++) {
+ char subtest[2];
+ sprintf(subtest, "%d", i+1);
+
+ forkres = fork();
+ if(forkres == 0) { /* Child */
+ execl(tests[i], tests[i], subtest, (char *) 0);
+ printf("Failed to execute subtest %s\n", tests[i]);
+ exit(-2);
+ } else if(forkres > 0) { /* Parent */
+ if(waitpid(forkres, &status, 0) > 0 && WEXITSTATUS(status) < 20) {
+ errorct += WEXITSTATUS(status); /* Count errors */
+ }
+ status = 0; /* Reset */
+ } else {
+ printf("Failed to fork\n");
+ exit(-2);
+ }
+ }
+
+ if(errorct == 0) {
+ printf("Ok\n");
+ exit(0);
+ } else {
+ printf("%d error(s)\n", errorct);
+ exit(1);
+ }
+
+ return (-1); /* Impossible */
+}
+