/* Test of select() substitute. Copyright (C) 2008 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* Written by Paolo Bonzini, 2008. */ #include #include #include #include #include #include #include #include #include #include #include "sockets.h" enum { SEL_IN = 1, SEL_OUT = 2, SEL_EXC = 4 }; #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ # define WIN32_NATIVE #endif #ifdef WIN32_NATIVE #include #define pipe(x) _pipe(x, 256, O_BINARY) #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_WAIT_H #include #endif #ifndef SO_REUSEPORT #define SO_REUSEPORT SO_REUSEADDR #endif #define TEST_PORT 12345 /* Minimal testing infrastructure. */ static int failures; static void failed (const char *reason) { if (++failures > 1) printf (" "); printf ("failed (%s)\n", reason); } static int test (void (*fn) (void), const char *msg) { failures = 0; printf ("%s... ", msg); fflush (stdout); fn (); if (!failures) printf ("passed\n"); return failures; } /* Funny socket code. */ static int open_server_socket () { int s, x; struct sockaddr_in ia; s = socket (AF_INET, SOCK_STREAM, 0); memset (&ia, 0, sizeof (ia)); ia.sin_family = AF_INET; inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr); ia.sin_port = htons (TEST_PORT); if (bind (s, (struct sockaddr *) &ia, sizeof (ia)) < 0) { perror ("bind"); exit (77); } x = 1; setsockopt (s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof (x)); if (listen (s, 1) < 0) { perror ("listen"); exit (77); } return s; } static int connect_to_socket (int blocking) { int s; struct sockaddr_in ia; s = socket (AF_INET, SOCK_STREAM, 0); memset (&ia, 0, sizeof (ia)); ia.sin_family = AF_INET; inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr); ia.sin_port = htons (TEST_PORT); if (!blocking) { #ifdef WIN32_NATIVE unsigned long iMode = 1; ioctl (s, FIONBIO, (char *) &iMode); #elif defined F_GETFL int oldflags = fcntl (s, F_GETFL, NULL); if (!(oldflags & O_NONBLOCK)) fcntl (s, F_SETFL, oldflags | O_NONBLOCK); #endif } if (connect (s, (struct sockaddr *) &ia, sizeof (ia)) < 0 && (blocking || errno != EINPROGRESS)) { perror ("connect"); exit (77); } return s; } /* A slightly more convenient interface to select(2). */ static int do_select (int fd, int ev, struct timeval *tv) { fd_set rfds, wfds, xfds; int r, rev; FD_ZERO (&rfds); FD_ZERO (&wfds); FD_ZERO (&xfds); if (ev & SEL_IN) FD_SET (fd, &rfds); if (ev & SEL_OUT) FD_SET (fd, &wfds); if (ev & SEL_EXC) FD_SET (fd, &xfds); r = select (fd + 1, &rfds, &wfds, &xfds, tv); if (r < 0) return r; rev = 0; if (FD_ISSET (fd, &rfds)) rev |= SEL_IN; if (FD_ISSET (fd, &wfds)) rev |= SEL_OUT; if (FD_ISSET (fd, &xfds)) rev |= SEL_EXC; if (rev && r == 0) failed ("select returned 0"); if (rev & ~ev) failed ("select returned unrequested events"); return rev; } static int do_select_nowait (int fd, int ev) { static struct timeval tv0; return do_select (fd, ev, &tv0); } static int do_select_wait (int fd, int ev) { return do_select (fd, ev, NULL); } /* Test poll(2) for TTYs. */ #ifdef INTERACTIVE static void test_tty (void) { if (do_select_nowait (0, SEL_IN) != 0) failed ("can read"); if (do_select_nowait (0, SEL_OUT) == 0) failed ("cannot write"); if (do_select_wait (0, SEL_IN) == 0) failed ("return with infinite timeout"); getchar (); if (do_select_nowait (0, SEL_IN) != 0) failed ("can read after getc"); } #endif /* Test poll(2) for unconnected nonblocking sockets. */ static void test_connect_first (void) { int s = open_server_socket (); struct sockaddr_in ia; socklen_t addrlen; int c1, c2; if (do_select_nowait (s, SEL_IN | SEL_EXC) != 0) failed ("can read, socket not connected"); c1 = connect_to_socket (false); if (do_select_wait (s, SEL_IN | SEL_EXC) != SEL_IN) failed ("expecting readability on passive socket"); if (do_select_nowait (s, SEL_IN | SEL_EXC) != SEL_IN) failed ("expecting readability on passive socket"); addrlen = sizeof (ia); c2 = accept (s, (struct sockaddr *) &ia, &addrlen); close (s); close (c1); close (c2); } /* Test poll(2) for unconnected blocking sockets. */ static void test_accept_first (void) { #ifndef WIN32_NATIVE int s = open_server_socket (); struct sockaddr_in ia; socklen_t addrlen; char buf[3]; int c, pid; pid = fork (); if (pid < 0) return; if (pid == 0) { addrlen = sizeof (ia); c = accept (s, (struct sockaddr *) &ia, &addrlen); close (s); write (c, "foo", 3); read (c, buf, 3); shutdown (c, SHUT_RD); close (c); exit (0); } else { close (s); c = connect_to_socket (true); if (do_select_nowait (c, SEL_OUT) != SEL_OUT) failed ("cannot write after blocking connect"); write (c, "foo", 3); wait (&pid); if (do_select_wait (c, SEL_IN) != SEL_IN) failed ("cannot read data left in the socket by closed process"); read (c, buf, 3); write (c, "foo", 3); close (c); } #endif } /* Common code for pipes and connected sockets. */ static void test_pair (int rd, int wd) { char buf[3]; if (do_select_wait (wd, SEL_IN | SEL_OUT | SEL_EXC) != SEL_OUT) failed ("expecting writability before writing"); if (do_select_nowait (wd, SEL_IN | SEL_OUT | SEL_EXC) != SEL_OUT) failed ("expecting writability before writing"); write (wd, "foo", 3); if (do_select_wait (rd, SEL_IN) != SEL_IN) failed ("expecting readability after writing"); if (do_select_nowait (rd, SEL_IN) != SEL_IN) failed ("expecting readability after writing"); read (rd, buf, 3); } /* Test poll(2) on connected sockets. */ static void test_socket_pair (void) { struct sockaddr_in ia; socklen_t addrlen = sizeof (ia); int s = open_server_socket (); int c1 = connect_to_socket (false); int c2 = accept (s, (struct sockaddr *) &ia, &addrlen); close (s); test_pair (c1, c2); close (c1); write (c2, "foo", 3); close (c2); } /* Test poll(2) on pipes. */ static void test_pipe (void) { int fd[2]; pipe (fd); test_pair (fd[0], fd[1]); close (fd[0]); close (fd[1]); } /* Do them all. */ int main () { int result; gl_sockets_startup (SOCKETS_1_1); #ifdef INTERACTIVE printf ("Please press Enter\n"); test (test_tty, "TTY"); #endif result = test (test_connect_first, "Unconnected socket test"); result += test (test_socket_pair, "Connected sockets test"); result += test (test_accept_first, "General socket test with fork"); result += test (test_pipe, "Pipe test"); exit (result); }