/* 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 #include #include #include #include #include #include #include #include #include #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*/ }