From 76bb60b45a1c498821ac465603e335f7af9dbe48 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 23 Sep 2008 15:11:23 +0200 Subject: [PATCH] implement full-blown select(2) for winsock 2008-09-24 Paolo Bonzini * NEWS: Document increased portability that sys_select now provides. * lib/sys_select.in.h: Install select wrapper. * lib/sys_socket.in.h: Use more descriptive name when there is no select wrapper. * lib/winsock-select.c: New. * m4/sys_select_h.m4: Compile lib/winsock-select.c if WinSock is used. Require gl_HEADER_SYS_SOCKET. * modules/sys_select: Depend on alloca, add lib/winsock-select.c. * modules/sys_select-tests: Copy dependencies from modules/poll-tests. * tests/test-sys_select.c: Add functional tests. --- ChangeLog | 14 ++ NEWS | 6 + lib/sys_select.in.h | 7 + lib/sys_socket.in.h | 3 +- lib/winsock-select.c | 418 +++++++++++++++++++++++++++++++++++++++++++++++ m4/sys_select_h.m4 | 4 + modules/sys_select | 3 + modules/sys_select-tests | 9 + tests/test-sys_select.c | 352 ++++++++++++++++++++++++++++++++++++++- 9 files changed, 812 insertions(+), 4 deletions(-) create mode 100644 lib/winsock-select.c diff --git a/ChangeLog b/ChangeLog index 49c1b2f9f..bf4e95dbe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2008-09-24 Paolo Bonzini + + * NEWS: Document increased portability that sys_select now provides. + + * lib/sys_select.in.h: Install select wrapper. + * lib/sys_socket.in.h: Use more descriptive name when there is no + select wrapper. + * lib/winsock-select.c: New. + * m4/sys_select_h.m4: Compile lib/winsock-select.c if WinSock is used. + Require gl_HEADER_SYS_SOCKET. + * modules/sys_select: Depend on alloca, add lib/winsock-select.c. + * modules/sys_select-tests: Copy dependencies from modules/poll-tests. + * tests/test-sys_select.c: Add functional tests. + 2008-09-24 Eric Blake open, fopen: close fd leak in last patch diff --git a/NEWS b/NEWS index 16917cb03..5a85b8411 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,12 @@ User visible incompatible changes Date Modules Changes +2008-09-24 sys_select The limitation on `select', introduced 2008-09-23, + was removed. sys_select now includes a select + wrapper for Winsock. The wrapper expects socket + and file descriptors to be compatible as arranged + by the sys_socket on MinGW. + 2008-09-23 sys_socket Under Windows (MinGW), the module now adds wrappers around Winsock functions, so that socket descriptors are now compatible with diff --git a/lib/sys_select.in.h b/lib/sys_select.in.h index 86ae53931..341543b87 100644 --- a/lib/sys_select.in.h +++ b/lib/sys_select.in.h @@ -39,6 +39,13 @@ # include +# if @HAVE_WINSOCK2_H@ +# undef select +# define select rpl_select + +extern int rpl_select (int, fd_set *, fd_set *, fd_set *, struct timeval *); +# endif + #endif #endif /* _GL_SYS_SELECT_H */ diff --git a/lib/sys_socket.in.h b/lib/sys_socket.in.h index 53c71956b..c7cd57fea 100644 --- a/lib/sys_socket.in.h +++ b/lib/sys_socket.in.h @@ -156,9 +156,8 @@ rpl_fd_isset (int fd, fd_set * set) # define sendto rpl_sendto # undef setsockopt # define setsockopt rpl_setsockopt - # undef select -# define select select_not_supported_under_win32_use_poll +# define select select_used_without_including_sys_select_h extern int rpl_close(int); extern int rpl_socket (int, int, int protocol); diff --git a/lib/winsock-select.c b/lib/winsock-select.c new file mode 100644 index 000000000..00bf2c67d --- /dev/null +++ b/lib/winsock-select.c @@ -0,0 +1,418 @@ +/* Emulation for select(2) + Contributed by Paolo Bonzini. + + Copyright 2008 Free Software Foundation, Inc. + + This file is part of gnulib. + + 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 2, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include +#include + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct bitset { + unsigned char in[FD_SETSIZE / CHAR_BIT]; + unsigned char out[FD_SETSIZE / CHAR_BIT]; +}; + +/* Declare data structures for ntdll functions. */ +typedef struct _FILE_PIPE_LOCAL_INFORMATION { + ULONG NamedPipeType; + ULONG NamedPipeConfiguration; + ULONG MaximumInstances; + ULONG CurrentInstances; + ULONG InboundQuota; + ULONG ReadDataAvailable; + ULONG OutboundQuota; + ULONG WriteQuotaAvailable; + ULONG NamedPipeState; + ULONG NamedPipeEnd; +} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; + +typedef struct _IO_STATUS_BLOCK +{ + union { + DWORD Status; + PVOID Pointer; + } u; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef enum _FILE_INFORMATION_CLASS { + FilePipeLocalInformation = 24 +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; + +typedef DWORD (WINAPI *PNtQueryInformationFile) + (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS); + +#ifndef PIPE_BUF +#define PIPE_BUF 512 +#endif + +/* Compute output fd_sets for libc descriptor FD (whose Win32 handle is H). */ + +static int +win32_poll_handle (HANDLE h, int fd, struct bitset *rbits, struct bitset *wbits, + struct bitset *xbits) +{ + BOOL read, write, except; + int i, ret; + INPUT_RECORD *irbuffer; + DWORD avail, nbuffer; + BOOL bRet; + IO_STATUS_BLOCK iosb; + FILE_PIPE_LOCAL_INFORMATION fpli; + static PNtQueryInformationFile NtQueryInformationFile; + static BOOL once_only; + + read = write = except = FALSE; + switch (GetFileType (h)) + { + case FILE_TYPE_PIPE: + if (!once_only) + { + NtQueryInformationFile = (PNtQueryInformationFile) + GetProcAddress (GetModuleHandle ("ntdll.dll"), + "NtQueryInformationFile"); + once_only = TRUE; + } + + if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0) + { + if (avail) + read = TRUE; + } + + else + { + /* It was the write-end of the pipe. Check if it is writable. + If NtQueryInformationFile fails, optimistically assume the pipe is + writable. This could happen on Win9x, where NtQueryInformationFile + is not available, or if we inherit a pipe that doesn't permit + FILE_READ_ATTRIBUTES access on the write end (I think this should + not happen since WinXP SP2; WINE seems fine too). Otherwise, + ensure that enough space is available for atomic writes. */ + memset (&iosb, 0, sizeof (iosb)); + memset (&fpli, 0, sizeof (fpli)); + + if (!NtQueryInformationFile + || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli), + FilePipeLocalInformation) + || fpli.WriteQuotaAvailable >= PIPE_BUF + || (fpli.OutboundQuota < PIPE_BUF && + fpli.WriteQuotaAvailable == fpli.OutboundQuota)) + write = TRUE; + } + break; + + case FILE_TYPE_CHAR: + ret = WaitForSingleObject (h, 0); + write = TRUE; + if (ret == WAIT_OBJECT_0) + { + nbuffer = avail = 0; + bRet = GetNumberOfConsoleInputEvents (h, &nbuffer); + if (!bRet || nbuffer == 0) + except = TRUE; + + irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD)); + bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail); + if (!bRet || avail == 0) + except = TRUE; + + for (i = 0; i < avail; i++) + if (irbuffer[i].EventType == KEY_EVENT) + read = TRUE; + } + break; + + default: + ret = WaitForSingleObject (h, 0); + write = TRUE; + if (ret == WAIT_OBJECT_0) + read = TRUE; + + break; + } + + ret = 0; + if (read && (rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) + { + rbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); + ret++; + } + + if (write && (wbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) + { + wbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); + ret++; + } + + if (except && (xbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) + { + xbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); + ret++; + } + + return ret; +} + +int +rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, + struct timeval *timeout) +{ + static struct timeval tv0; + static HANDLE hEvent; + HANDLE h, handle_array[FD_SETSIZE + 2]; + fd_set handle_rfds, handle_wfds, handle_xfds; + struct bitset rbits, wbits, xbits; + unsigned char anyfds_in[FD_SETSIZE / CHAR_BIT]; + DWORD ret, wait_timeout, nhandles, nsock; + MSG msg; + int i, fd, rc; + + if (nfds > FD_SETSIZE) + nfds = FD_SETSIZE; + + if (!timeout) + wait_timeout = INFINITE; + else + { + wait_timeout = timeout->tv_sec + timeout->tv_usec / 1000; + + /* select is also used as a portable usleep. */ + if (!rfds && !wfds && !xfds) + { + Sleep (wait_timeout); + return 0; + } + } + + if (!hEvent) + hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); + + handle_array[0] = hEvent; + nhandles = 1; + nsock = 0; + + /* Copy descriptors to bitsets. */ + memset (&rbits, 0, sizeof (rbits)); + memset (&wbits, 0, sizeof (wbits)); + memset (&xbits, 0, sizeof (xbits)); + memset (anyfds_in, 0, sizeof (anyfds_in)); + if (rfds) + for (i = 0; i < rfds->fd_count; i++) + { + fd = rfds->fd_array[i]; + rbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + } + else + rfds = (fd_set *) alloca (sizeof (fd_set)); + + if (wfds) + for (i = 0; i < wfds->fd_count; i++) + { + fd = wfds->fd_array[i]; + wbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + } + else + wfds = (fd_set *) alloca (sizeof (fd_set)); + + if (xfds) + for (i = 0; i < xfds->fd_count; i++) + { + fd = xfds->fd_array[i]; + xbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); + } + else + xfds = (fd_set *) alloca (sizeof (fd_set)); + + /* Zero all the fd_sets, including the application's. */ + FD_ZERO (rfds); + FD_ZERO (wfds); + FD_ZERO (xfds); + FD_ZERO (&handle_rfds); + FD_ZERO (&handle_wfds); + FD_ZERO (&handle_xfds); + + /* Classify handles. Create fd sets for sockets, poll the others. */ + for (i = 0; i < nfds; i++) + { + WSANETWORKEVENTS ev; + + if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) + continue; + + h = (HANDLE) _get_osfhandle (i); + if (!h) + { + errno = EBADF; + return -1; + } + + /* Under Wine, it seems that getsockopt returns 0 for pipes too. + WSAEnumNetworkEvents instead distinguishes the two correctly. */ + ev.lNetworkEvents = 0xDEADBEEF; + WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); + if (ev.lNetworkEvents != 0xDEADBEEF) + { + int requested = FD_CLOSE; + + /* See above; socket handles are mapped onto select, but we + need to map descriptors to handles. */ + if (rbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + requested |= FD_READ | FD_ACCEPT; + FD_SET ((SOCKET) h, rfds); + FD_SET ((SOCKET) h, &handle_rfds); + } + if (wbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + requested |= FD_WRITE | FD_CONNECT; + FD_SET ((SOCKET) h, wfds); + FD_SET ((SOCKET) h, &handle_wfds); + } + if (xbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + requested |= FD_OOB; + FD_SET ((SOCKET) h, xfds); + FD_SET ((SOCKET) h, &handle_xfds); + } + + WSAEventSelect ((SOCKET) h, hEvent, requested); + nsock++; + } + else + { + handle_array[nhandles++] = h; + + /* Poll now. If we get an event, do not wait below. */ + if (wait_timeout != 0 + && win32_poll_handle (h, i, &rbits, &wbits, &xbits)) + wait_timeout = 0; + } + } + + if (wait_timeout == 0 || nsock == 0) + rc = 0; + else + { + /* See if we need to wait in the loop below. If any select is ready, + do MsgWaitForMultipleObjects anyway to dispatch messages, but + no need to call select again. */ + rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0); + if (rc == 0) + { + /* Restore the fd_sets for the other select we do below. */ + memcpy (&handle_rfds, rfds, sizeof (fd_set)); + memcpy (&handle_wfds, wfds, sizeof (fd_set)); + memcpy (&handle_xfds, xfds, sizeof (fd_set)); + } + else + wait_timeout = 0; + } + + for (;;) + { + ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE, + wait_timeout, QS_ALLINPUT); + + if (ret == WAIT_OBJECT_0 + nhandles) + { + /* new input of some other kind */ + BOOL bRet; + while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + else + break; + } + + /* If we haven't done it yet, check the status of the sockets. */ + if (rc == 0 && nsock > 0) + rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0); + + /* Now fill in the results. */ + FD_ZERO (rfds); + FD_ZERO (wfds); + FD_ZERO (xfds); + + /* Place a sentinel at the end of the array. */ + handle_array[nhandles] = NULL; + nhandles = 1; + for (i = 0; i < nfds; i++) + { + if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) + continue; + + h = (HANDLE) _get_osfhandle (i); + if (h != handle_array[nhandles]) + { + /* Perform handle->descriptor mapping. Don't update rc, as these + results are counted in the return value of Winsock's select. */ + WSAEventSelect ((SOCKET) h, NULL, 0); + if (FD_ISSET (h, &handle_rfds)) + FD_SET (i, rfds); + if (FD_ISSET (h, &handle_wfds)) + FD_SET (i, wfds); + if (FD_ISSET (h, &handle_xfds)) + FD_SET (i, xfds); + } + else + { + /* Not a socket. */ + nhandles++; + win32_poll_handle (h, i, &rbits, &wbits, &xbits); + if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + rc++; + FD_SET (i, rfds); + } + if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + rc++; + FD_SET (i, wfds); + } + if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) + { + rc++; + FD_SET (i, xfds); + } + } + } + + return rc; +} + +#endif /* Native Win32. */ diff --git a/m4/sys_select_h.m4 b/m4/sys_select_h.m4 index 8d1c36c12..c9ad02a48 100644 --- a/m4/sys_select_h.m4 +++ b/m4/sys_select_h.m4 @@ -6,6 +6,7 @@ dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([gl_HEADER_SYS_SELECT], [ + AC_REQUIRE([gl_HEADER_SYS_SOCKET]) AC_CACHE_CHECK([whether is self-contained], [gl_cv_header_sys_select_h_selfcontained], [ @@ -26,4 +27,7 @@ AC_DEFUN([gl_HEADER_SYS_SELECT], AC_SUBST([HAVE_SYS_SELECT_H]) fi AC_SUBST([SYS_SELECT_H]) + if test x$ac_cv_header_winsock2_h = xyes; then + AC_LIBOBJ(winsock-select) + fi ]) diff --git a/modules/sys_select b/modules/sys_select index 74e43b298..f2f769890 100644 --- a/modules/sys_select +++ b/modules/sys_select @@ -3,9 +3,11 @@ A for systems lacking it. Files: lib/sys_select.in.h +lib/winsock-select.c m4/sys_select_h.m4 Depends-on: +alloca include_next sys_socket @@ -26,6 +28,7 @@ sys/select.h: sys_select.in.h -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ -e 's|@''NEXT_SYS_SELECT_H''@|$(NEXT_SYS_SELECT_H)|g' \ -e 's|@''HAVE_SYS_SELECT_H''@|$(HAVE_SYS_SELECT_H)|g' \ + -e 's|@''HAVE_WINSOCK2_H''@|$(HAVE_WINSOCK2_H)|g' \ < $(srcdir)/sys_select.in.h; \ } > $@-t mv $@-t $@ diff --git a/modules/sys_select-tests b/modules/sys_select-tests index a07d40a38..e004b334d 100644 --- a/modules/sys_select-tests +++ b/modules/sys_select-tests @@ -2,6 +2,15 @@ Files: tests/test-sys_select.c Depends-on: +stdbool +sys_select +netinet_in +arpa_inet +extensions +inet_pton +errno +perror +sockets configure.ac: diff --git a/tests/test-sys_select.c b/tests/test-sys_select.c index 6c3547a75..38f9fa76c 100644 --- a/tests/test-sys_select.c +++ b/tests/test-sys_select.c @@ -1,5 +1,5 @@ /* Test of substitute. - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007, 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 @@ -15,13 +15,361 @@ along with this program. If not, see . */ /* Written by Bruno Haible , 2007. */ +/* Enhanced 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 () { - return 0; + 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); } -- 2.11.0