1 /* t40c.c 2 * 3 * Test (pseudo) terminal devices 4 * 5 * Select works on regular files, (pseudo) terminal devices, streams-based 6 * files, FIFOs, pipes, and sockets. This test verifies selecting for (pseudo) 7 * terminal devices. 8 * 9 * This test is part of a bigger select test. It expects as argument which sub- 10 * test it is. 11 */ 12 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <unistd.h> 16 #include <sys/types.h> 17 #include <sys/stat.h> 18 #include <fcntl.h> 19 #include <sys/select.h> 20 #include <sys/syslimits.h> 21 #include <errno.h> 22 #include <sys/wait.h> 23 #include <string.h> 24 25 #include "common.h" 26 27 #define TERMINALW "/dev/ttypf" 28 #define TERMINALR "/dev/ptypf" 29 #define SENDSTRING "minixrocks" 30 #define MAX_ERROR 5 31 32 static void open_terminal(int *child_fd, int *parent_fd) { 33 int fd1, fd2, i; 34 char opentermw[5+OPEN_MAX+1]; 35 char opentermr[5+OPEN_MAX+1]; 36 char *term[] = {"f","e","d","c","b","a","9","8","7","6","5","4","3","2","1"}; 37 #define TERMS (sizeof(term)/sizeof(term[0])) 38 39 if (!child_fd || !parent_fd) exit(EXIT_FAILURE); 40 41 for (i = 0; i < TERMS; i++) { 42 snprintf(opentermw, 5+OPEN_MAX, "/dev/ttyp%s", term[i]); 43 snprintf(opentermr, 5+OPEN_MAX, "/dev/ptyp%s", term[i]); 44 45 /* Open master terminal for writing */ 46 if((fd1 = open(opentermw, O_WRONLY)) == -1) continue; 47 48 /* Open slave terminal for reading */ 49 if((fd2 = open(opentermr, O_RDONLY)) == -1) { 50 close(fd1); 51 continue; 52 } 53 54 *child_fd = fd1; 55 *parent_fd = fd2; 56 return; 57 } 58 59 /* If we get here we failed to find a terminal pair */ 60 exit(EXIT_FAILURE); 61 } 62 63 static int do_child(int terminal) { 64 /* Going to sleep for two seconds to allow the parent proc to get ready */ 65 sleep(2); 66 67 /* Try to write. Doesn't matter how many bytes we actually send. */ 68 (void) write(terminal, SENDSTRING, strlen(SENDSTRING)); 69 70 /* Wait for another second to allow the parent to process incoming data */ 71 sleep(1); 72 73 /* Write some more, and wait some more. */ 74 (void) write(terminal, SENDSTRING, strlen(SENDSTRING)); 75 76 sleep(1); 77 78 close(terminal); 79 exit(0); 80 } 81 82 static int do_parent(int child, int terminal) { 83 fd_set fds_read, fds_read2, fds_write, fds_error; 84 int retval, terminal2, highest; 85 char buf[256]; 86 87 /* Clear bit masks */ 88 FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error); 89 /* Set read bits */ 90 FD_SET(terminal, &fds_read); 91 FD_SET(terminal, &fds_write); 92 93 /* Test if we can read or write from/to fd. As fd is opened read only we 94 * cannot actually write, so the select should return immediately with fd 95 * set in fds_write, but not in fds_read. Note that the child waits two 96 * seconds before sending data. This gives us the opportunity run this 97 * sub-test as reading from fd is blocking at this point. */ 98 retval = select(terminal+1, &fds_read, &fds_write, &fds_error, NULL); 99 100 if(retval != 1) em(1, "incorrect amount of ready file descriptors"); 101 102 if(FD_ISSET(terminal, &fds_read)) em(2, "read should NOT be set"); 103 if(!FD_ISSET(terminal, &fds_write)) em(3, "write should be set"); 104 if(FD_ISSET(terminal, &fds_error)) em(4, "error should NOT be set"); 105 106 /* Block until ready; until child wrote stuff */ 107 FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error); 108 FD_SET(terminal, &fds_read); 109 retval = select(terminal+1, &fds_read, NULL, &fds_error, NULL); 110 111 if(retval != 1) em(5, "incorrect amount of ready file descriptors"); 112 if(!FD_ISSET(terminal, &fds_read)) em(6, "read should be set"); 113 if(FD_ISSET(terminal, &fds_error)) em(7, "error should not be set"); 114 115 FD_ZERO(&fds_read); FD_ZERO(&fds_error); 116 FD_SET(terminal, &fds_write); 117 retval = select(terminal+1, NULL, &fds_write, NULL, NULL); 118 /* As it is impossible to write to a read only fd, this select should return 119 * immediately with fd set in fds_write. */ 120 if(retval != 1) em(8, "incorrect amount or ready file descriptors"); 121 122 /* See if selecting on the same object with two different fds results in both 123 * fds being returned as ready, immediately. 124 */ 125 terminal2 = dup(terminal); 126 if (terminal2 < 0) em(9, "unable to dup file descriptor"); 127 128 FD_ZERO(&fds_read); 129 FD_SET(terminal, &fds_read); 130 FD_SET(terminal2, &fds_read); 131 fds_read2 = fds_read; 132 highest = terminal > terminal2 ? terminal : terminal2; 133 134 retval = select(highest+1, &fds_read, NULL, NULL, NULL); 135 if (retval != 2) em(10, "incorrect amount of ready file descriptors"); 136 if (!FD_ISSET(terminal, &fds_read)) em(11, "first fd missing from set"); 137 if (!FD_ISSET(terminal2, &fds_read)) em(12, "second fd missing from set"); 138 139 /* Empty the buffer. */ 140 if (read(terminal, buf, sizeof(buf)) <= 0) em(13, "unable to read data"); 141 142 /* Repeat the test, now with a delay. */ 143 retval = select(highest+1, &fds_read2, NULL, NULL, NULL); 144 if (retval != 2) em(10, "incorrect amount of ready file descriptors"); 145 if (!FD_ISSET(terminal, &fds_read2)) em(11, "first fd missing from set"); 146 if (!FD_ISSET(terminal2, &fds_read2)) em(12, "second fd missing from set"); 147 148 close(terminal2); 149 close(terminal); 150 waitpid(child, &retval, 0); 151 exit(errct); 152 } 153 154 int main(int argc, char **argv) { 155 int forkres; 156 int master, slave; 157 158 /* Get subtest number */ 159 if(argc != 2) { 160 printf("Usage: %s subtest_no\n", argv[0]); 161 exit(-1); 162 } else if(sscanf(argv[1], "%d", &subtest) != 1) { 163 printf("Usage: %s subtest_no\n", argv[0]); 164 exit(-1); 165 } 166 167 open_terminal(&master, &slave); 168 169 forkres = fork(); 170 if(forkres == 0) do_child(master); 171 else if(forkres > 0) do_parent(forkres, slave); 172 else { /* Fork failed */ 173 perror("Unable to fork"); 174 exit(-1); 175 } 176 177 exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/ 178 179 } 180