add swifi to the build/install.
[minix.git] / test / t40e.c
blob5a5ba4c3931cfb5b5c15c3889267f1c4ec315efb
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 int fd_sock, fd_new, yes = 1, exitstatus;
223 int sockets[NUMCHILDREN], i;
224 fd_set fds_read, fds_write, fds_error;
225 fd_set fds_compare_read, fds_compare_write;
226 struct timeval tv;
227 int retval, childresults = 0;
229 struct sockaddr_in my_addr;
230 struct sockaddr_in other_addr;
231 socklen_t other_size;
233 if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
234 perror("Error getting socket\n");
235 exit(-1);
238 my_addr.sin_family = AF_INET;
239 my_addr.sin_port = htons(MYPORT); /* Short, network byte order */
240 my_addr.sin_addr.s_addr = INADDR_ANY;
241 /* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
242 #ifndef _MINIX
243 memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
244 #endif
246 /* Reuse port number. Not implemented in Minix. */
247 #ifndef _MINIX
248 if(setsockopt(fd_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
249 perror("Error setting port reuse option");
250 exit(-1);
252 #endif
254 /* Bind to port */
255 if(bind(fd_sock, (struct sockaddr *) &my_addr, sizeof my_addr) < 0) {
256 perror("Error binding to port");
257 exit(-1);
260 /* Mark socket to be used for incoming connections */
261 if(listen(fd_sock, 20) < 0) {
262 perror("Listen");
263 exit(-1);
266 /* Actual testing */
267 /* While sockets resemble file descriptors, they are not the same at all.
268 We can read/write from/to and close file descriptors, but we cannot open
269 them O_RDONLY or O_WRONLY; they are always O_RDWR (other flags do not make
270 sense regarding sockets). As such, we cannot provide wrong file descriptors
271 to select, except for descriptors that are not in use.
272 We will test standard behavior and what is described in [2]. [1] and [3]
273 are not possible to test on Minix, as Minix does not support OOB data. That
274 is, the TCP layer can handle it, but there is no socket interface for it.
275 Our test consists of waiting for input from the first two children and
276 waiting to write output [standard usage]. Then the first child closes its
277 connection we select for reading. This should fail with error set. Then we
278 close child number two on our side and select for reading. This should fail
279 with EBADF. Child number three shall then do a non-blocking connect (after
280 waiting for DO_PAUSE seconds) and do a select, resulting in being marked
281 ready for writing. Subsequently child number four also does a non-blocking
282 connect to loclhost on MYPORT+1 (causing the connect to fail) and then does
283 a select. This should result in write and error being set (error because of
284 pending error).
287 /* Accept and store connections from the first two children */
288 other_size = sizeof(other_addr);
289 for(i = 0; i < 2; i++) {
290 fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
291 if(fd_new < 0) break;
292 sockets[i] = fd_new;
295 /* If we break out of the for loop, we ran across an error and want to exit.
296 Check whether we broke out. */
297 if(fd_new < 0) {
298 perror("Error accepting connection");
299 exit(-1);
302 /* Select error condition checking */
303 for(childresults = 0; childresults < 2; childresults++) {
304 FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
305 FD_SET(sockets[childresults], &fds_read);
306 FD_SET(sockets[childresults], &fds_write);
307 FD_SET(sockets[childresults], &fds_error);
308 tv.tv_sec = DO_TIMEOUT;
309 tv.tv_usec = 0;
311 retval = select(sockets[childresults]+1, &fds_read, &fds_write, &fds_error,
312 &tv);
314 if(retval <= 0) {
315 snprintf(errbuf, sizeof(errbuf),
316 "two fds should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
317 e(1, errbuf);
320 FD_ZERO(&fds_compare_read); FD_ZERO(&fds_compare_write);
321 FD_SET(sockets[childresults], &fds_compare_write);
323 /* We can't say much about being ready for reading at this point or not. It
324 is not specified and the other side might have data ready for us to read
326 if(!compare_fds(sockets[childresults]+1, &fds_compare_write, &fds_write))
327 e(2, "write should be set");
329 if(!empty_fds(sockets[childresults]+1, &fds_error))
330 e(3, "no error should be set");
334 /* We continue by accepting a connection of child 3 */
335 fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
336 if(fd_new < 0) {
337 perror("Error accepting connection\n");
338 exit(-1);
340 sockets[2] = fd_new;
342 /* Child 4 will never connect */
344 /* Child 5 is still pending to be accepted. Open Group: "If the socket is
345 currently listening, then it shall be marked as readable if an incoming
346 connection request has been received, and a call to the accept() function
347 shall complete without blocking."*/
348 FD_ZERO(&fds_read);
349 FD_SET(fd_sock, &fds_read);
350 tv.tv_sec = DO_TIMEOUT;
351 tv.tv_usec = 0;
352 retval = select(fd_sock+1, &fds_read, NULL, NULL, &tv);
353 if(retval <= 0) {
354 snprintf(errbuf, sizeof(errbuf),
355 "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
356 e(4, errbuf);
359 /* Check read bit is set */
360 FD_ZERO(&fds_compare_read); FD_SET(fd_sock, &fds_compare_read);
361 if(!compare_fds(fd_sock+1, &fds_compare_read, &fds_read))
362 e(5, "read should be set");
365 /* Accept incoming connection to unblock child 5 */
366 fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
367 if(fd_new < 0) {
368 perror("Error accepting connection\n");
369 exit(-1);
371 sockets[4] = fd_new;
374 /* We're done, let's wait a second to synchronize children and parent. */
375 tv.tv_sec = DO_HANDLEDATA;
376 tv.tv_usec = 0;
377 select(0, NULL, NULL, NULL, &tv);
379 /* Close connection with children. */
380 for(i = 0; i < NUMCHILDREN; i++) {
381 if(i == 3) /* No need to disconnect child 4 that failed to connect. */
382 continue;
384 if(close(sockets[i]) < 0) {
385 perror(NULL);
389 /* Close listening socket */
390 if(close(fd_sock) < 0) {
391 perror("Closing listening socket");
392 errct++;
395 for(i = 0; i < NUMCHILDREN; i++) {
396 wait(&exitstatus); /* Wait for children */
397 if(exitstatus > 0)
398 errct += WEXITSTATUS(exitstatus); /* and count their errors, too. */
401 exit(errct);
404 int main(int argc, char **argv) {
405 int forkres, i;
407 /* Get subtest number */
408 if(argc != 2) {
409 printf("Usage: %s subtest_no\n", argv[0]);
410 exit(-2);
411 } else if(sscanf(argv[1], "%d", &subtest) != 1) {
412 printf("Usage: %s subtest_no\n", argv[0]);
413 exit(-2);
416 /* Fork off a bunch of children */
417 for(i = 0; i < NUMCHILDREN; i++) {
418 forkres = fork();
419 if(forkres == 0) do_child(i);
420 else if(forkres < 0) {
421 perror("Unable to fork");
422 exit(-1);
425 /* do_child always calls exit(), so when we end up here, we're the parent. */
426 do_parent();
428 exit(-2); /* We're not supposed to get here. Both do_* routines should exit.*/