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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
/* 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 <sys/syslimits.h>
#include <errno.h>
#include <sys/wait.h>
#include <string.h>
#include "common.h"
#define TERMINALW "/dev/ttypf"
#define TERMINALR "/dev/ptypf"
#define SENDSTRING "minixrocks"
#define MAX_ERROR 5
static void open_terminal(int *child_fd, int *parent_fd) {
int fd1, fd2, i;
char opentermw[5+OPEN_MAX+1];
char opentermr[5+OPEN_MAX+1];
char *term[] = {"f","e","d","c","b","a","9","8","7","6","5","4","3","2","1"};
#define TERMS (sizeof(term)/sizeof(term[0]))
if (!child_fd || !parent_fd) exit(EXIT_FAILURE);
for (i = 0; i < TERMS; i++) {
snprintf(opentermw, 5+OPEN_MAX, "/dev/ttyp%s", term[i]);
snprintf(opentermr, 5+OPEN_MAX, "/dev/ptyp%s", term[i]);
/* Open master terminal for writing */
if((fd1 = open(opentermw, O_WRONLY)) == -1) continue;
/* Open slave terminal for reading */
if((fd2 = open(opentermr, O_RDONLY)) == -1) {
close(fd1);
continue;
}
*child_fd = fd1;
*parent_fd = fd2;
return;
}
/* If we get here we failed to find a terminal pair */
exit(EXIT_FAILURE);
}
static int do_child(int terminal) {
/* Going to sleep for two seconds to allow the parent proc to get ready */
sleep(2);
/* Try to write. Doesn't matter how many bytes we actually send. */
(void) write(terminal, SENDSTRING, strlen(SENDSTRING));
/* Wait for another second to allow the parent to process incoming data */
sleep(1);
/* Write some more, and wait some more. */
(void) write(terminal, SENDSTRING, strlen(SENDSTRING));
sleep(1);
close(terminal);
exit(0);
}
static int do_parent(int child, int terminal) {
fd_set fds_read, fds_read2, fds_write, fds_error;
int retval, terminal2, highest;
char buf[256];
/* Clear bit masks */
FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
/* Set read bits */
FD_SET(terminal, &fds_read);
FD_SET(terminal, &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(terminal+1, &fds_read, &fds_write, &fds_error, NULL);
if(retval != 1) em(1, "incorrect amount of ready file descriptors");
if(FD_ISSET(terminal, &fds_read)) em(2, "read should NOT be set");
if(!FD_ISSET(terminal, &fds_write)) em(3, "write should be set");
if(FD_ISSET(terminal, &fds_error)) em(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(terminal, &fds_read);
retval = select(terminal+1, &fds_read, NULL, &fds_error, NULL);
if(retval != 1) em(5, "incorrect amount of ready file descriptors");
if(!FD_ISSET(terminal, &fds_read)) em(6, "read should be set");
if(FD_ISSET(terminal, &fds_error)) em(7, "error should not be set");
FD_ZERO(&fds_read); FD_ZERO(&fds_error);
FD_SET(terminal, &fds_write);
retval = select(terminal+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) em(8, "incorrect amount or ready file descriptors");
/* See if selecting on the same object with two different fds results in both
* fds being returned as ready, immediately.
*/
terminal2 = dup(terminal);
if (terminal2 < 0) em(9, "unable to dup file descriptor");
FD_ZERO(&fds_read);
FD_SET(terminal, &fds_read);
FD_SET(terminal2, &fds_read);
fds_read2 = fds_read;
highest = terminal > terminal2 ? terminal : terminal2;
retval = select(highest+1, &fds_read, NULL, NULL, NULL);
if (retval != 2) em(10, "incorrect amount of ready file descriptors");
if (!FD_ISSET(terminal, &fds_read)) em(11, "first fd missing from set");
if (!FD_ISSET(terminal2, &fds_read)) em(12, "second fd missing from set");
/* Empty the buffer. */
if (read(terminal, buf, sizeof(buf)) <= 0) em(13, "unable to read data");
/* Repeat the test, now with a delay. */
retval = select(highest+1, &fds_read2, NULL, NULL, NULL);
if (retval != 2) em(10, "incorrect amount of ready file descriptors");
if (!FD_ISSET(terminal, &fds_read2)) em(11, "first fd missing from set");
if (!FD_ISSET(terminal2, &fds_read2)) em(12, "second fd missing from set");
close(terminal2);
close(terminal);
waitpid(child, &retval, 0);
exit(errct);
}
int main(int argc, char **argv) {
int forkres;
int master, slave;
/* 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);
}
open_terminal(&master, &slave);
forkres = fork();
if(forkres == 0) do_child(master);
else if(forkres > 0) do_parent(forkres, slave);
else { /* Fork failed */
perror("Unable to fork");
exit(-1);
}
exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/
}
|