summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--NEWS6
-rw-r--r--lib/sys_select.in.h7
-rw-r--r--lib/sys_socket.in.h3
-rw-r--r--lib/winsock-select.c418
-rw-r--r--m4/sys_select_h.m44
-rw-r--r--modules/sys_select3
-rw-r--r--modules/sys_select-tests9
-rw-r--r--tests/test-sys_select.c352
9 files changed, 812 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 49c1b2f9fb..bf4e95dbe3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2008-09-24 Paolo Bonzini <bonzini@gnu.org>
+
+ * 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 <ebb9@byu.net>
open, fopen: close fd leak in last patch
diff --git a/NEWS b/NEWS
index 16917cb03a..5a85b8411c 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 86ae53931b..341543b87e 100644
--- a/lib/sys_select.in.h
+++ b/lib/sys_select.in.h
@@ -39,6 +39,13 @@
# include <sys/socket.h>
+# 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 53c71956b6..c7cd57fea5 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 0000000000..00bf2c67d5
--- /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 <config.h>
+#include <alloca.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+#include <sys/types.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <winsock2.h>
+#include <windows.h>
+#include <io.h>
+#include <stdio.h>
+#include <conio.h>
+#include <time.h>
+
+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 8d1c36c124..c9ad02a483 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 <sys/select.h> 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 74e43b2987..f2f769890c 100644
--- a/modules/sys_select
+++ b/modules/sys_select
@@ -3,9 +3,11 @@ A <sys/select.h> 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 a07d40a385..e004b334de 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 6c3547a755..38f9fa76c9 100644
--- a/tests/test-sys_select.c
+++ b/tests/test-sys_select.c
@@ -1,5 +1,5 @@
/* Test of <sys/select.h> 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 <http://www.gnu.org/licenses/>. */
/* Written by Bruno Haible <bruno@clisp.org>, 2007. */
+/* Enhanced by Paolo Bonzini, 2008. */
#include <config.h>
+#include <stdio.h>
+#include <string.h>
#include <sys/select.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#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 <io.h>
+#define pipe(x) _pipe(x, 256, O_BINARY)
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#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);
}