vm: fix region reporting bug
[minix.git] / test / t40e.c
blob1a8f8293a2c96089fb1d71e94d6671fb48a8d5a8
1 /* t40e.c
3 * Test sockets
5 * Select works on regular files, (pseudo) terminal devices, streams-based
6 * files, FIFOs, pipes, and sockets. This test verifies selecting for sockets.
8 * This test is part of a bigger select test. It expects as argument which sub-
9 * test it is.
11 * Specific rules for sockets:
12 * If a socket has a pending error, it shall be considered to have an
13 * exceptional condition pending. Otherwise, what constitutes an exceptional
14 * condition is file type-specific. For a file descriptor for use with a
15 * socket, it is protocol-specific except as noted below. For other file types
16 * it is implementation-defined. If the operation is meaningless for a
17 * particular file type, pselect() or select() shall indicate that the
18 * descriptor is ready for read or write operations, and shall indicate that
19 * the descriptor has no exceptional condition pending.
21 * [1] If a descriptor refers to a socket, the implied input function is the
22 * recvmsg()function with parameters requesting normal and ancillary data, such
23 * that the presence of either type shall cause the socket to be marked as
24 * readable. The presence of out-of-band data shall be checked if the socket
25 * option SO_OOBINLINE has been enabled, as out-of-band data is enqueued with
26 * normal data. If the socket is currently listening, then it shall be marked
27 * as readable if an incoming connection request has been received, and a call
28 * to the accept() function shall complete without blocking.
30 * [2] If a descriptor refers to a socket, the implied output function is the
31 * sendmsg() function supplying an amount of normal data equal to the current
32 * value of the SO_SNDLOWAT option for the socket. If a non-blocking call to
33 * the connect() function has been made for a socket, and the connection
34 * attempt has either succeeded or failed leaving a pending error, the socket
35 * shall be marked as writable.
37 * [3] A socket shall be considered to have an exceptional condition pending if
38 * a receive operation with O_NONBLOCK clear for the open file description and
39 * with the MSG_OOB flag set would return out-of-band data without blocking.
40 * (It is protocol-specific whether the MSG_OOB flag would be used to read
41 * out-of-band data.) A socket shall also be considered to have an exceptional
42 * condition pending if an out-of-band data mark is present in the receive
43 * queue. Other circumstances under which a socket may be considered to have an
44 * exceptional condition pending are protocol-specific and
45 * implementation-defined.
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <sys/wait.h>
54 #include <sys/select.h>
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
58 #include <fcntl.h>
59 #include <errno.h>
60 #include <string.h>
61 #include <time.h>
62 #include <assert.h>
63 #include <netdb.h>
65 #define DO_HANDLEDATA 1
66 #define DO_PAUSE 3
67 #define DO_TIMEOUT 7
68 #define MYPORT 3490
69 #define NUMCHILDREN 5
70 #define MAX_ERROR 10
72 int errct = 0, subtest = -1;
73 char errbuf[1000];
75 void e(int n, char *s) {
76 printf("Subtest %d, error %d, %s\n", subtest, n, s);
78 if (errct++ > MAX_ERROR) {
79 printf("Too many errors; test aborted\n");
80 exit(errct);
84 /* All *_fds routines are helping routines. They intentionally use FD_* macros
85 in order to prevent making assumptions on how the macros are implemented.*/
87 int count_fds(int nfds, fd_set *fds) {
88 /* Return number of bits set in fds */
89 int i, result = 0;
90 assert(fds != NULL && nfds > 0);
91 for(i = 0; i < nfds; i++) {
92 if(FD_ISSET(i, fds)) result++;
94 return result;
97 int empty_fds(int nfds, fd_set *fds) {
98 /* Returns nonzero if the first bits up to nfds in fds are not set */
99 int i;
100 assert(fds != NULL && nfds > 0);
101 for(i = 0; i < nfds; i++) if(FD_ISSET(i, fds)) return 0;
102 return 1;
105 int compare_fds(int nfds, fd_set *lh, fd_set *rh) {
106 /* Returns nonzero if lh equals rh up to nfds bits */
107 int i;
108 assert(lh != NULL && rh != NULL && nfds > 0);
109 for(i = 0; i < nfds; i++) {
110 if((FD_ISSET(i, lh) && !FD_ISSET(i, rh)) ||
111 (!FD_ISSET(i, lh) && FD_ISSET(i, rh))) {
112 return 0;
115 return 1;
118 void dump_fds(int nfds, fd_set *fds) {
119 /* Print a graphical representation of bits in fds */
120 int i;
121 if(fds != NULL && nfds > 0) {
122 for(i = 0; i < nfds; i++) printf("%d ", (FD_ISSET(i, fds) ? 1 : 0));
123 printf("\n");
127 void do_child(int childno) {
128 int fd_sock, port;
129 int retval;
131 fd_set fds_read, fds_write, fds_error;
132 fd_set fds_compare_write;
134 struct hostent *he;
135 struct sockaddr_in server;
136 struct timeval tv;
138 if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
139 perror("Error getting socket\n");
140 exit(-1);
143 if((he = gethostbyname("127.0.0.1")) == NULL){/*"localhost" might be unknown*/
144 perror("Error resolving");
145 exit(-1);
148 /* Child 4 connects to the wrong port. See Actual testing description below.*/
149 port = (childno == 3 ? MYPORT + 1 : MYPORT);
151 memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
152 server.sin_family = AF_INET;
153 server.sin_port = htons(port);
155 #if 0
156 printf("Going to connect to: %s:%d\n", inet_ntoa(server.sin_addr),
157 ntohs(server.sin_port));
158 #endif
160 /* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
161 #ifndef _MINIX
162 memset(server.sin_zero, '\0', sizeof server.sin_zero);
163 #endif
165 /* Wait for parent to set up connection */
166 tv.tv_sec = (childno <= 1 ? DO_PAUSE : DO_TIMEOUT);
167 tv.tv_usec = 0;
168 retval = select(0, NULL, NULL, NULL, &tv);
170 /* All set, let's do some testing */
171 /* Children 3 and 4 do a non-blocking connect */
172 if(childno == 2 || childno == 3)
173 fcntl(fd_sock, F_SETFL, fcntl(fd_sock, F_GETFL, 0) | O_NONBLOCK);
175 if(connect(fd_sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
176 /* Well, we don't actually care. The connect is non-blocking and is
177 supposed to "in progress" at this point. */
180 if(childno == 2 || childno == 3) { /* Children 3 and 4 */
181 /* Open Group: "If a non-blocking call to the connect() function has been
182 made for a socket, and the connection attempt has either succeeded or
183 failed leaving a pending error, the socket shall be marked as writable.
185 A socket shall be considered to have an exceptional condition pending if
186 a receive operation with O_NONBLOCK clear for the open file description
187 and with the MSG_OOB flag set would return out-of-band data without
188 blocking. (It is protocol-specific whether the MSG_OOB flag would be used
189 to read out-of-band data.) A socket shall also be considered to have an
190 exceptional condition pending if an out-of-band data mark is present in
191 the receive queue. Other circumstances under which a socket may be
192 considered to have an exceptional condition pending are protocol-specific
193 and implementation-defined."
195 In other words, it only makes sense for us to check the write set as the
196 read set is not expected to be set, but is allowed to be set (i.e.,
197 unspecified) and whether the error set is set is implementation-defined.
199 FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
200 FD_SET(fd_sock, &fds_write);
201 tv.tv_sec = DO_TIMEOUT;
202 tv.tv_usec = 0;
203 retval = select(fd_sock+1, NULL, &fds_write, NULL, &tv);
206 if(retval <= 0) e(6, "expected one fd to be ready");
208 FD_ZERO(&fds_compare_write); FD_SET(fd_sock, &fds_compare_write);
209 if(!compare_fds(fd_sock+1, &fds_compare_write, &fds_compare_write))
210 e(7, "write should be set");
213 if(close(fd_sock) < 0) {
214 perror("Error disconnecting");
215 exit(-1);
218 exit(errct);
221 void do_parent(void) {
222 #ifndef _MINIX
223 int yes = 1;
224 #endif
225 int fd_sock, fd_new, exitstatus;
226 int sockets[NUMCHILDREN], i;
227 fd_set fds_read, fds_write, fds_error;
228 fd_set fds_compare_read, fds_compare_write;
229 struct timeval tv;
230 int retval, childresults = 0;
232 struct sockaddr_in my_addr;
233 struct sockaddr_in other_addr;
234 socklen_t other_size;
236 if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
237 perror("Error getting socket\n");
238 exit(-1);
241 my_addr.sin_family = AF_INET;
242 my_addr.sin_port = htons(MYPORT); /* Short, network byte order */
243 my_addr.sin_addr.s_addr = INADDR_ANY;
244 /* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
245 #ifndef _MINIX
246 memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
247 #endif
249 /* Reuse port number. Not implemented in Minix. */
250 #ifndef _MINIX
251 if(setsockopt(fd_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
252 perror("Error setting port reuse option");
253 exit(-1);
255 #endif
257 /* Bind to port */
258 if(bind(fd_sock, (struct sockaddr *) &my_addr, sizeof my_addr) < 0) {
259 perror("Error binding to port");
260 exit(-1);
263 /* Mark socket to be used for incoming connections */
264 if(listen(fd_sock, 20) < 0) {
265 perror("Listen");
266 exit(-1);
269 /* Actual testing */
270 /* While sockets resemble file descriptors, they are not the same at all.
271 We can read/write from/to and close file descriptors, but we cannot open
272 them O_RDONLY or O_WRONLY; they are always O_RDWR (other flags do not make
273 sense regarding sockets). As such, we cannot provide wrong file descriptors
274 to select, except for descriptors that are not in use.
275 We will test standard behavior and what is described in [2]. [1] and [3]
276 are not possible to test on Minix, as Minix does not support OOB data. That
277 is, the TCP layer can handle it, but there is no socket interface for it.
278 Our test consists of waiting for input from the first two children and
279 waiting to write output [standard usage]. Then the first child closes its
280 connection we select for reading. This should fail with error set. Then we
281 close child number two on our side and select for reading. This should fail
282 with EBADF. Child number three shall then do a non-blocking connect (after
283 waiting for DO_PAUSE seconds) and do a select, resulting in being marked
284 ready for writing. Subsequently child number four also does a non-blocking
285 connect to loclhost on MYPORT+1 (causing the connect to fail) and then does
286 a select. This should result in write and error being set (error because of
287 pending error).
290 /* Accept and store connections from the first two children */
291 other_size = sizeof(other_addr);
292 for(i = 0; i < 2; i++) {
293 fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
294 if(fd_new < 0) break;
295 sockets[i] = fd_new;
298 /* If we break out of the for loop, we ran across an error and want to exit.
299 Check whether we broke out. */
300 if(fd_new < 0) {
301 perror("Error accepting connection");
302 exit(-1);
305 /* Select error condition checking */
306 for(childresults = 0; childresults < 2; childresults++) {
307 FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
308 FD_SET(sockets[childresults], &fds_read);
309 FD_SET(sockets[childresults], &fds_write);
310 FD_SET(sockets[childresults], &fds_error);
311 tv.tv_sec = DO_TIMEOUT;
312 tv.tv_usec = 0;
314 retval = select(sockets[childresults]+1, &fds_read, &fds_write, &fds_error,
315 &tv);
317 if(retval <= 0) {
318 snprintf(errbuf, sizeof(errbuf),
319 "two fds should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
320 e(1, errbuf);
323 FD_ZERO(&fds_compare_read); FD_ZERO(&fds_compare_write);
324 FD_SET(sockets[childresults], &fds_compare_write);
326 /* We can't say much about being ready for reading at this point or not. It
327 is not specified and the other side might have data ready for us to read
329 if(!compare_fds(sockets[childresults]+1, &fds_compare_write, &fds_write))
330 e(2, "write should be set");
332 if(!empty_fds(sockets[childresults]+1, &fds_error))
333 e(3, "no error should be set");
337 /* We continue by accepting a connection of child 3 */
338 fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
339 if(fd_new < 0) {
340 perror("Error accepting connection\n");
341 exit(-1);
343 sockets[2] = fd_new;
345 /* Child 4 will never connect */
347 /* Child 5 is still pending to be accepted. Open Group: "If the socket is
348 currently listening, then it shall be marked as readable if an incoming
349 connection request has been received, and a call to the accept() function
350 shall complete without blocking."*/
351 FD_ZERO(&fds_read);
352 FD_SET(fd_sock, &fds_read);
353 tv.tv_sec = DO_TIMEOUT;
354 tv.tv_usec = 0;
355 retval = select(fd_sock+1, &fds_read, NULL, NULL, &tv);
356 if(retval <= 0) {
357 snprintf(errbuf, sizeof(errbuf),
358 "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
359 e(4, errbuf);
362 /* Check read bit is set */
363 FD_ZERO(&fds_compare_read); FD_SET(fd_sock, &fds_compare_read);
364 if(!compare_fds(fd_sock+1, &fds_compare_read, &fds_read))
365 e(5, "read should be set");
368 /* Accept incoming connection to unblock child 5 */
369 fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
370 if(fd_new < 0) {
371 perror("Error accepting connection\n");
372 exit(-1);
374 sockets[4] = fd_new;
377 /* We're done, let's wait a second to synchronize children and parent. */
378 tv.tv_sec = DO_HANDLEDATA;
379 tv.tv_usec = 0;
380 select(0, NULL, NULL, NULL, &tv);
382 /* Close connection with children. */
383 for(i = 0; i < NUMCHILDREN; i++) {
384 if(i == 3) /* No need to disconnect child 4 that failed to connect. */
385 continue;
387 if(close(sockets[i]) < 0) {
388 perror(NULL);
392 /* Close listening socket */
393 if(close(fd_sock) < 0) {
394 perror("Closing listening socket");
395 errct++;
398 for(i = 0; i < NUMCHILDREN; i++) {
399 wait(&exitstatus); /* Wait for children */
400 if(exitstatus > 0)
401 errct += WEXITSTATUS(exitstatus); /* and count their errors, too. */
404 exit(errct);
407 int main(int argc, char **argv) {
408 int forkres, i;
410 /* Get subtest number */
411 if(argc != 2) {
412 printf("Usage: %s subtest_no\n", argv[0]);
413 exit(-2);
414 } else if(sscanf(argv[1], "%d", &subtest) != 1) {
415 printf("Usage: %s subtest_no\n", argv[0]);
416 exit(-2);
419 /* Fork off a bunch of children */
420 for(i = 0; i < NUMCHILDREN; i++) {
421 forkres = fork();
422 if(forkres == 0) do_child(i);
423 else if(forkres < 0) {
424 perror("Unable to fork");
425 exit(-1);
428 /* do_child always calls exit(), so when we end up here, we're the parent. */
429 do_parent();
431 exit(-2); /* We're not supposed to get here. Both do_* routines should exit.*/