Added select test
This commit is contained in:
parent
ce916bcb91
commit
70d25344a2
9 changed files with 1543 additions and 4 deletions
|
@ -7,7 +7,7 @@ OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \
|
|||
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
|
||||
|
@ -28,7 +28,7 @@ $(ROOTOBJ):
|
|||
|
||||
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
|
||||
|
@ -72,3 +72,10 @@ test36: test36.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
|
||||
|
|
4
test/run
4
test/run
|
@ -12,12 +12,12 @@ badones= # list of tests that failed
|
|||
|
||||
# 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
|
||||
|
|
141
test/t40a.c
Normal file
141
test/t40a.c
Normal file
|
@ -0,0 +1,141 @@
|
|||
/* 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);
|
||||
}
|
148
test/t40b.c
Normal file
148
test/t40b.c
Normal file
|
@ -0,0 +1,148 @@
|
|||
/* 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);
|
||||
}
|
148
test/t40c.c
Normal file
148
test/t40c.c
Normal file
|
@ -0,0 +1,148 @@
|
|||
/* 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*/
|
||||
|
||||
}
|
428
test/t40d.c
Normal file
428
test/t40d.c
Normal file
|
@ -0,0 +1,428 @@
|
|||
/* 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*/
|
||||
|
||||
}
|
429
test/t40e.c
Normal file
429
test/t40e.c
Normal file
|
@ -0,0 +1,429 @@
|
|||
/* 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.*/
|
||||
}
|
185
test/t40f.c
Normal file
185
test/t40f.c
Normal file
|
@ -0,0 +1,185 @@
|
|||
/* 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*/
|
||||
|
||||
}
|
53
test/test40.c
Normal file
53
test/test40.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
/* 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 */
|
||||
}
|
||||
|
Loading…
Reference in a new issue