xref: /minix/minix/tests/t40c.c (revision 83ee113e)
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