<sys/socket.h>: turn off MSG_NOSIGNAL
[minix3.git] / test / t40e.c
blob52a3483c00bf7f90e792bfe2791e1983292dae95
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 #include "common.h"
67 #define DO_HANDLEDATA 1
68 #define DO_PAUSE 3
69 #define DO_TIMEOUT 7
70 #define MYPORT 3490
71 #define NUMCHILDREN 5
72 #define MAX_ERROR 10
74 char errbuf[1000];
76 /* All *_fds routines are helping routines. They intentionally use FD_* macros
77 in order to prevent making assumptions on how the macros are implemented.*/
79 #if 0
80 static int count_fds(int nfds, fd_set *fds) {
81 /* Return number of bits set in fds */
82 int i, result = 0;
83 assert(fds != NULL && nfds > 0);
84 for(i = 0; i < nfds; i++) {
85 if(FD_ISSET(i, fds)) result++;
87 return result;
89 #endif
91 static int empty_fds(int nfds, fd_set *fds) {
92 /* Returns nonzero if the first bits up to nfds in fds are not set */
93 int i;
94 assert(fds != NULL && nfds > 0);
95 for(i = 0; i < nfds; i++) if(FD_ISSET(i, fds)) return 0;
96 return 1;
99 static int compare_fds(int nfds, fd_set *lh, fd_set *rh) {
100 /* Returns nonzero if lh equals rh up to nfds bits */
101 int i;
102 assert(lh != NULL && rh != NULL && nfds > 0);
103 for(i = 0; i < nfds; i++) {
104 if((FD_ISSET(i, lh) && !FD_ISSET(i, rh)) ||
105 (!FD_ISSET(i, lh) && FD_ISSET(i, rh))) {
106 return 0;
109 return 1;
112 #if 0
113 static void dump_fds(int nfds, fd_set *fds) {
114 /* Print a graphical representation of bits in fds */
115 int i;
116 if(fds != NULL && nfds > 0) {
117 for(i = 0; i < nfds; i++) printf("%d ", (FD_ISSET(i, fds) ? 1 : 0));
118 printf("\n");
121 #endif
123 static void do_child(int childno) {
124 int fd_sock, port;
125 int retval;
127 fd_set fds_read, fds_write, fds_error;
128 fd_set fds_compare_write;
130 struct hostent *he;
131 struct sockaddr_in server;
132 struct timeval tv;
134 if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
135 perror("Error getting socket\n");
136 exit(-1);
139 if((he = gethostbyname("127.0.0.1")) == NULL){/*"localhost" might be unknown*/
140 perror("Error resolving");
141 exit(-1);
144 /* Child 4 connects to the wrong port. See Actual testing description below.*/
145 port = (childno == 3 ? MYPORT + 1 : MYPORT);
147 memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
148 server.sin_family = AF_INET;
149 server.sin_port = htons(port);
151 #if 0
152 printf("Going to connect to: %s:%d\n", inet_ntoa(server.sin_addr),
153 ntohs(server.sin_port));
154 #endif
156 /* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
157 #if !defined(__minix)
158 memset(server.sin_zero, '\0', sizeof server.sin_zero);
159 #endif
161 /* Wait for parent to set up connection */
162 tv.tv_sec = (childno <= 1 ? DO_PAUSE : DO_TIMEOUT);
163 tv.tv_usec = 0;
164 retval = select(0, NULL, NULL, NULL, &tv);
166 /* All set, let's do some testing */
167 /* Children 3 and 4 do a non-blocking connect */
168 if(childno == 2 || childno == 3)
169 fcntl(fd_sock, F_SETFL, fcntl(fd_sock, F_GETFL, 0) | O_NONBLOCK);
171 if(connect(fd_sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
172 /* Well, we don't actually care. The connect is non-blocking and is
173 supposed to "in progress" at this point. */
176 if(childno == 2 || childno == 3) { /* Children 3 and 4 */
177 /* Open Group: "If a non-blocking call to the connect() function has been
178 made for a socket, and the connection attempt has either succeeded or
179 failed leaving a pending error, the socket shall be marked as writable.
181 A socket shall be considered to have an exceptional condition pending if
182 a receive operation with O_NONBLOCK clear for the open file description
183 and with the MSG_OOB flag set would return out-of-band data without
184 blocking. (It is protocol-specific whether the MSG_OOB flag would be used
185 to read out-of-band data.) A socket shall also be considered to have an
186 exceptional condition pending if an out-of-band data mark is present in
187 the receive queue. Other circumstances under which a socket may be
188 considered to have an exceptional condition pending are protocol-specific
189 and implementation-defined."
191 In other words, it only makes sense for us to check the write set as the
192 read set is not expected to be set, but is allowed to be set (i.e.,
193 unspecified) and whether the error set is set is implementation-defined.
195 FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
196 FD_SET(fd_sock, &fds_write);
197 tv.tv_sec = DO_TIMEOUT;
198 tv.tv_usec = 0;
199 retval = select(fd_sock+1, NULL, &fds_write, NULL, &tv);
202 if(retval <= 0) em(6, "expected one fd to be ready");
204 FD_ZERO(&fds_compare_write); FD_SET(fd_sock, &fds_compare_write);
205 if(!compare_fds(fd_sock+1, &fds_compare_write, &fds_compare_write))
206 em(7, "write should be set");
209 if(close(fd_sock) < 0) {
210 perror("Error disconnecting");
211 exit(-1);
214 exit(errct);
217 static void do_parent(void) {
218 #if !defined(__minix)
219 int yes = 1;
220 #endif
221 int fd_sock, fd_new, exitstatus;
222 int sockets[NUMCHILDREN], i;
223 fd_set fds_read, fds_write, fds_error;
224 fd_set fds_compare_read, fds_compare_write;
225 struct timeval tv;
226 int retval, childresults = 0;
228 struct sockaddr_in my_addr;
229 struct sockaddr_in other_addr;
230 socklen_t other_size;
232 if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
233 perror("Error getting socket\n");
234 exit(-1);
237 my_addr.sin_family = AF_INET;
238 my_addr.sin_port = htons(MYPORT); /* Short, network byte order */
239 my_addr.sin_addr.s_addr = INADDR_ANY;
240 /* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
241 #if !defined(__minix)
242 memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
243 #endif
245 /* Reuse port number. Not implemented in Minix. */
246 #if !defined(__minix)
247 if(setsockopt(fd_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
248 perror("Error setting port reuse option");
249 exit(-1);
251 #endif
253 /* Bind to port */
254 if(bind(fd_sock, (struct sockaddr *) &my_addr, sizeof my_addr) < 0) {
255 perror("Error binding to port");
256 exit(-1);
259 /* Mark socket to be used for incoming connections */
260 if(listen(fd_sock, 20) < 0) {
261 perror("Listen");
262 exit(-1);
265 /* Actual testing */
266 /* While sockets resemble file descriptors, they are not the same at all.
267 We can read/write from/to and close file descriptors, but we cannot open
268 them O_RDONLY or O_WRONLY; they are always O_RDWR (other flags do not make
269 sense regarding sockets). As such, we cannot provide wrong file descriptors
270 to select, except for descriptors that are not in use.
271 We will test standard behavior and what is described in [2]. [1] and [3]
272 are not possible to test on Minix, as Minix does not support OOB data. That
273 is, the TCP layer can handle it, but there is no socket interface for it.
274 Our test consists of waiting for input from the first two children and
275 waiting to write output [standard usage]. Then the first child closes its
276 connection we select for reading. This should fail with error set. Then we
277 close child number two on our side and select for reading. This should fail
278 with EBADF. Child number three shall then do a non-blocking connect (after
279 waiting for DO_PAUSE seconds) and do a select, resulting in being marked
280 ready for writing. Subsequently child number four also does a non-blocking
281 connect to loclhost on MYPORT+1 (causing the connect to fail) and then does
282 a select. This should result in write and error being set (error because of
283 pending error).
286 /* Accept and store connections from the first two children */
287 other_size = sizeof(other_addr);
288 for(i = 0; i < 2; i++) {
289 fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
290 if(fd_new < 0) break;
291 sockets[i] = fd_new;
294 /* If we break out of the for loop, we ran across an error and want to exit.
295 Check whether we broke out. */
296 if(fd_new < 0) {
297 perror("Error accepting connection");
298 exit(-1);
301 /* Select error condition checking */
302 for(childresults = 0; childresults < 2; childresults++) {
303 FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
304 FD_SET(sockets[childresults], &fds_read);
305 FD_SET(sockets[childresults], &fds_write);
306 FD_SET(sockets[childresults], &fds_error);
307 tv.tv_sec = DO_TIMEOUT;
308 tv.tv_usec = 0;
310 retval = select(sockets[childresults]+1, &fds_read, &fds_write, &fds_error,
311 &tv);
313 if(retval <= 0) {
314 snprintf(errbuf, sizeof(errbuf),
315 "two fds should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
316 em(1, errbuf);
319 FD_ZERO(&fds_compare_read); FD_ZERO(&fds_compare_write);
320 FD_SET(sockets[childresults], &fds_compare_write);
322 /* We can't say much about being ready for reading at this point or not. It
323 is not specified and the other side might have data ready for us to read
325 if(!compare_fds(sockets[childresults]+1, &fds_compare_write, &fds_write))
326 em(2, "write should be set");
328 if(!empty_fds(sockets[childresults]+1, &fds_error))
329 em(3, "no error should be set");
333 /* We continue by accepting a connection of child 3 */
334 fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
335 if(fd_new < 0) {
336 perror("Error accepting connection\n");
337 exit(-1);
339 sockets[2] = fd_new;
341 /* Child 4 will never connect */
343 /* Child 5 is still pending to be accepted. Open Group: "If the socket is
344 currently listening, then it shall be marked as readable if an incoming
345 connection request has been received, and a call to the accept() function
346 shall complete without blocking."*/
347 FD_ZERO(&fds_read);
348 FD_SET(fd_sock, &fds_read);
349 tv.tv_sec = DO_TIMEOUT;
350 tv.tv_usec = 0;
351 retval = select(fd_sock+1, &fds_read, NULL, NULL, &tv);
352 if(retval <= 0) {
353 snprintf(errbuf, sizeof(errbuf),
354 "one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
355 em(4, errbuf);
358 /* Check read bit is set */
359 FD_ZERO(&fds_compare_read); FD_SET(fd_sock, &fds_compare_read);
360 if(!compare_fds(fd_sock+1, &fds_compare_read, &fds_read))
361 em(5, "read should be set");
364 /* Accept incoming connection to unblock child 5 */
365 fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
366 if(fd_new < 0) {
367 perror("Error accepting connection\n");
368 exit(-1);
370 sockets[4] = fd_new;
373 /* We're done, let's wait a second to synchronize children and parent. */
374 tv.tv_sec = DO_HANDLEDATA;
375 tv.tv_usec = 0;
376 select(0, NULL, NULL, NULL, &tv);
378 /* Close connection with children. */
379 for(i = 0; i < NUMCHILDREN; i++) {
380 if(i == 3) /* No need to disconnect child 4 that failed to connect. */
381 continue;
383 if(close(sockets[i]) < 0) {
384 perror(NULL);
388 /* Close listening socket */
389 if(close(fd_sock) < 0) {
390 perror("Closing listening socket");
391 errct++;
394 for(i = 0; i < NUMCHILDREN; i++) {
395 wait(&exitstatus); /* Wait for children */
396 if(exitstatus > 0)
397 errct += WEXITSTATUS(exitstatus); /* and count their errors, too. */
400 exit(errct);
403 int main(int argc, char **argv) {
404 int forkres, i;
406 /* Get subtest number */
407 if(argc != 2) {
408 printf("Usage: %s subtest_no\n", argv[0]);
409 exit(-2);
410 } else if(sscanf(argv[1], "%d", &subtest) != 1) {
411 printf("Usage: %s subtest_no\n", argv[0]);
412 exit(-2);
415 /* Fork off a bunch of children */
416 for(i = 0; i < NUMCHILDREN; i++) {
417 forkres = fork();
418 if(forkres == 0) do_child(i);
419 else if(forkres < 0) {
420 perror("Unable to fork");
421 exit(-1);
424 /* do_child always calls exit(), so when we end up here, we're the parent. */
425 do_parent();
427 exit(-2); /* We're not supposed to get here. Both do_* routines should exit.*/