From 2069a5fb31ede2f7f1129632f03b104b90a04bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Thu, 18 Nov 2010 02:00:12 +0300 Subject: [PATCH] poll() implementation for W32 --- src/include/gnunet_common.h | 12 + src/util/Makefile.am | 11 + src/util/test_w32io.c | 205 ++++ src/util/w32io.c | 2421 +++++++++++++++++++++++++++++++++++++++++++ src/util/w32io.h | 268 +++++ 5 files changed, 2917 insertions(+), 0 deletions(-) create mode 100644 src/util/test_w32io.c create mode 100644 src/util/w32io.c create mode 100644 src/util/w32io.h diff --git a/src/include/gnunet_common.h b/src/include/gnunet_common.h index badb528..c41e8d2 100644 --- a/src/include/gnunet_common.h +++ b/src/include/gnunet_common.h @@ -354,6 +354,18 @@ unsigned long long GNUNET_htonll (unsigned long long n); #define GNUNET_malloc(size) GNUNET_xmalloc_(size, __FILE__, __LINE__) /** + * Wrapper around malloc. Allocates enough memory to hold + * @number of variables if type @value_type. + * The memory will be zero'ed out. + * + * @param value_type the type to use for size calulations + * @param number the number of sizeof(@value_type)-sized chunks to + * allocate. sizeof(@value_type) * @number must be less than 40 MB. + * @return pointer to size bytes of memory, never NULL (!) + */ +#define GNUNET_new(value_type, number) GNUNET_xmalloc_(sizeof(value_type)*number, __FILE__, __LINE__) + +/** * Wrapper around malloc. Allocates size bytes of memory. * The memory will be zero'ed out. * diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 427ed25..b65d97b 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -6,8 +6,11 @@ if MINGW noinst_LTLIBRARIES = \ libgnunetutilwin.la libgnunetutilwin_la_SOURCES = \ + w32io.c \ + w32io.h \ win.cc \ winproc.c +WINTESTS = test_w32io libgnunetutilwin_la_LDFLAGS = \ -Wl,--no-undefined -Wl,--export-all-symbols libgnunetutilwin_la_LIBADD = \ @@ -108,6 +111,7 @@ libgnunet_plugin_test_la_LDFLAGS = \ $(GN_PLUGIN_LDFLAGS) check_PROGRAMS = \ + $(WINTESTS) \ test_bio \ test_client \ test_common_allocation \ @@ -157,6 +161,13 @@ if !DISABLE_TEST_RUN TESTS = $(check_PROGRAMS) endif +if MINGW +test_w32io_SOURCES = \ + test_w32io.c +test_w32io_LDADD =\ + $(top_builddir)/src/util/libgnunetutil.la +endif + test_bio_SOURCES = \ test_bio.c test_bio_LDADD = \ diff --git a/src/util/test_w32io.c b/src/util/test_w32io.c new file mode 100644 index 0000000..2c5a362 --- /dev/null +++ b/src/util/test_w32io.c @@ -0,0 +1,205 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors) + + GNUnet 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, or (at your + option) any later version. + + GNUnet 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 GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file util/test_w32io.c + * @brief testcase for util/w32io.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_os_lib.h" +#include "w32io.h" + +#define VERBOSE GNUNET_NO + +#define PORT 56421 + +SOCKET +create_listening_socket () +{ + struct sockaddr_in sa; + SOCKET s; + memset (&sa, 0, sizeof (sa)); +#if HAVE_SOCKADDR_IN_SIN_LEN + sa.sin_len = sizeof (sa); +#endif + sa.sin_addr.s_addr = inet_addr("127.0.0.1"); + sa.sin_port = htons (PORT); + sa.sin_family = AF_INET; + + s = WSASocket (AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); + GNUNET_assert (s != INVALID_SOCKET); + GNUNET_assert (bind (s, (struct sockaddr *) &sa, sizeof (sa)) == 0); + GNUNET_assert (listen (s, 5) == 0); + return s; +} + +SOCKET +create_connecting_socket () +{ + SOCKET s; + + s = WSASocket (AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); + GNUNET_assert (s != INVALID_SOCKET); + return s; +} + + +static int +test_socket_connect_server () +{ + int r; + intptr_t sr; + struct sockaddr sa; + int sizeofsa = sizeof (sa); + struct W32PollFd server[2]; + char buf[1024]; + SOCKET a; + SOCKET s = create_listening_socket (); + + server[0].fd = GNUNET_W32IO_fd_from_handle ((HANDLE) s, GNUNET_W32IO_SOCKET, GENERIC_READ | GENERIC_WRITE, 0, 0, 0); + GNUNET_assert (server[0].fd != NULL); + + sr = spawnlp (_P_NOWAIT, "test_w32io.exe", "test_w32io.exe", "client", NULL); + + server[0].fd->events = POLLIN | POLLOUT; + r = GNUNET_W32IO_ppoll (server, 1, NULL, NULL); + GNUNET_assert (r == 1); + GNUNET_assert (server[0].fd->revents & POLLIN); + + a = GNUNET_W32IO_accept (server[0].fd, &sa, &sizeofsa); + GNUNET_assert (a != INVALID_SOCKET); + + server[1].fd = GNUNET_W32IO_fd_from_handle ((HANDLE) a, GNUNET_W32IO_SOCKET, GENERIC_READ | GENERIC_WRITE, 1, 1, 0); + GNUNET_assert (server[1].fd != NULL); + + server[0].fd->events = 0; + server[1].fd->events = POLLIN | POLLOUT; + r = GNUNET_W32IO_ppoll (server, 2, NULL, NULL); + GNUNET_assert (r == 1); + GNUNET_assert ((server[1].fd->revents & POLLOUT) && !(server[1].fd->revents & POLLIN)); + + r = GNUNET_W32IO_send (server[1].fd, "12345", 5, 0); + + server[0].fd->events = 0; + server[1].fd->events = POLLIN; + r = GNUNET_W32IO_ppoll (server, 2, NULL, NULL); + GNUNET_assert (r == 1); + GNUNET_assert (!(server[1].fd->revents & POLLOUT) && (server[1].fd->revents & POLLIN)); + + r = GNUNET_W32IO_recv (server[1].fd, buf, 5, 0); + + GNUNET_assert (buf[0] == '5' && buf[1] == '4' && buf[2] == '3' && buf[3] == '2' && buf[4] == '1'); + + server[0].fd->events = 0; + server[1].fd->events = POLLOUT; + r = GNUNET_W32IO_ppoll (server, 2, NULL, NULL); + GNUNET_assert (r == 1); + GNUNET_assert ((server[1].fd->revents & POLLOUT) && !(server[1].fd->revents & POLLIN)); + + r = GNUNET_W32IO_send (server[1].fd, "98765", 5, 0); + + server[0].fd->events = 0; + server[1].fd->events = 0; + r = GNUNET_W32IO_ppoll (server, 2, NULL, NULL); + GNUNET_assert (r == 1); + GNUNET_assert (!(server[1].fd->revents & POLLOUT) && !(server[1].fd->revents & POLLIN) && (server[1].fd->revents & POLLHUP)); + + GNUNET_W32IO_close (server[0].fd, 1); + GNUNET_W32IO_close (server[1].fd, 1); + return 0; +} + +static int +test_socket_connect_client () +{ + int r; + char buf[1024]; + struct W32PollFd client; + struct sockaddr_in sa; + memset (&sa, 0, sizeof (sa)); +#if HAVE_SOCKADDR_IN_SIN_LEN + sa.sin_len = sizeof (sa); +#endif + sa.sin_addr.s_addr = inet_addr("127.0.0.1"); + sa.sin_port = htons (PORT); + sa.sin_family = AF_INET; + + + SOCKET s = create_connecting_socket (); + + client.fd = GNUNET_W32IO_fd_from_handle ((HANDLE) s, GNUNET_W32IO_SOCKET, GENERIC_READ | GENERIC_WRITE, 0, 0, 0); + GNUNET_assert (client.fd != NULL); + + GNUNET_W32IO_connect (client.fd, (struct sockaddr *) &sa, sizeof (sa)); + + client.fd->events = POLLOUT; + r = GNUNET_W32IO_ppoll (&client, 1, NULL, NULL); + GNUNET_assert (r == 1); + GNUNET_assert (client.fd->revents & POLLOUT); + + client.fd->events = POLLIN; + r = GNUNET_W32IO_ppoll (&client, 1, NULL, NULL); + GNUNET_assert (r == 1); + GNUNET_assert (client.fd->revents & POLLIN); + + GNUNET_W32IO_recv (client.fd, buf, 5, 0); + GNUNET_assert (buf[0] == '1' && buf[1] == '2' && buf[2] == '3' && buf[3] == '4' && buf[4] == '5'); + + client.fd->events = POLLIN | POLLOUT; + r = GNUNET_W32IO_ppoll (&client, 1, NULL, NULL); + GNUNET_assert (r == 1); + GNUNET_assert ((client.fd->revents & POLLOUT) && !(client.fd->revents & POLLIN)); + + GNUNET_W32IO_send (client.fd, "54321", 5, 0); + + client.fd->events = POLLIN; + r = GNUNET_W32IO_ppoll (&client, 1, NULL, NULL); + GNUNET_assert (r == 1); + GNUNET_assert (!(client.fd->revents & POLLOUT) && (client.fd->revents & POLLIN)); + + GNUNET_W32IO_recv (client.fd, buf, 5, 0); + GNUNET_assert (buf[0] == '9' && buf[1] == '8' && buf[2] == '7' && buf[3] == '6' && buf[4] == '5'); + + GNUNET_W32IO_close (client.fd, 1); + return 0; +} + +int +main (int argc, char *argv[]) +{ + int errCnt = 0; + + GNUNET_log_setup ("test-w32io", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + if (argc < 2) + { + if (0 != test_socket_connect_server ()) + errCnt++; + } + else if (0 != test_socket_connect_client ()) + errCnt++; + return errCnt; +} diff --git a/src/util/w32io.c b/src/util/w32io.c new file mode 100644 index 0000000..076abbb --- /dev/null +++ b/src/util/w32io.c @@ -0,0 +1,2421 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2005, 2006, 2009, 2010 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + GNUnet 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 GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file util/w32io.c + * @brief POSIX-compatible I/O under NT + * @author LRN + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "w32io.h" + +#if WINDOWS + +void * +GNUNET_W32IO_anonymous_pipe_thread_func (void *data); + +#define GNUNET_W32IO_PIPE_NAME_PREFIX "\\\\.\\pipe\\" + +#define SOCKET_POLL_EVENT POLLIN_BIT +#define SOCKET_WAKEUP_EVENT POLLOUT_BIT +#define ANON_PIPE_THREAD_POLL_EVENT POLLIN_BIT +#define ANON_PIPE_MAIN_THREAD_POLL_EVENT POLLOUT_BIT +#define ANON_PIPE_CLOSE_POLL_EVENT POLLHUP_BIT +#define FILE_READY_POLL_EVENT POLLIN_BIT + +#define ZeroOverlappedExceptEvent(o) o.Internal = o.InternalHigh = o.Offset = o.OffsetHigh = 0 + +void * +GNUNET_memdup (void *src, size_t size) +{ + return memcpy (GNUNET_malloc (size), src, size); +} + + +HANDLE +GNUNET_W32IO_fd_get_handle (struct W32Fd *fd) +{ + return fd->fd; +} + +void +GNUNET_W32IO_generate_random_local_pipe_name (char pipename[255]) +{ + int i; + static DWORD seed = 0; + static float scale = 32767 / 25; + static int prefixlen = 9; + if (seed == 0) + { + seed = GetCurrentProcessId (); + srand (seed); + } + /* Uniqueness is not guaranteed - but we can always regenerate the name until + * it comes out to be unique. + */ + memcpy (pipename, GNUNET_W32IO_PIPE_NAME_PREFIX, prefixlen); + for (i = prefixlen; i < 254; i++) + { + /* Get a random 0-32767 integer, then scale it to 65-90. This is not + * really a requirement - MSDN says that the only forbidden character + * for pipe names is the backslash. But i won't be taking any chances. + */ + /* TODO: use RtlRandom() or RtlRandomEx() instead of rand() */ + pipename[i] = 65 + (char) (rand() / scale); + } + pipename[254] = 0; +} + +int +GNUNET_W32IO_pipe (struct W32Fd *fd[2], int server_inheritable, int client_inheritable, DWORD server_mode, DWORD client_mode) +{ + char pipename[255]; + SECURITY_ATTRIBUTES sa; + DWORD err; + BOOL bret; + OVERLAPPED ov; + HANDLE p[2]; + p[0] = p[1] = NULL; + memset (&ov, 0, sizeof (ov)); + while (!p[0]) + { + sa.nLength = sizeof (sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = server_inheritable; + GNUNET_W32IO_generate_random_local_pipe_name (pipename); + /* FILE_FLAG_FIRST_PIPE_INSTANCE makes sure that we're first to create a + * pipe with such name. + * FILE_FLAG_OVERLAPPED creates async pipe. If the user wants to perform + * blocking I/O, we can easily create a blocking wrapper. + */ + p[0] = CreateNamedPipeA ((LPCSTR) pipename, server_mode | + FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED | + PIPE_ACCESS_INBOUND, /*inbound access is necessary to read pipe info*/ + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa); + if (p[0] == INVALID_HANDLE_VALUE) + { + err = GetLastError (); + switch (err) + { + case ERROR_ACCESS_DENIED: + p[0] = NULL; + continue; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create a named pipe. GLE: %d\n", err); + return FALSE; + } + } + bret = ConnectNamedPipe (p[0], &ov); + if (bret == 0) + { + err = GetLastError (); + if (err == ERROR_IO_PENDING) + { + //It's ok, it works in the background while we finish other things + } + else if (err != ERROR_PIPE_CONNECTED) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to connect a named pipe. GLE: %d\n", err); + CloseHandle (p[0]); + return FALSE; + } + } + sa.nLength = sizeof (sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = client_inheritable; + p[1] = CreateFileA ((LPCSTR) pipename, client_mode, 0, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL); + if (p[1] == INVALID_HANDLE_VALUE) + { + err = GetLastError (); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to open client side of a named pipe. GLE: %d\n", err); + CloseHandle(p[0]); + return FALSE; + } + //We've made both I/O calls, wait until the pipe is connected an opened + bret = GetOverlappedResult (p[0], &ov, &err, TRUE); + + /* Both are not readable because we know that there is no data to read (yet). + * Write end is writable, because no writing is being done. + * Read end is, however, not writable, because it is read-only. + */ + fd[0] = GNUNET_W32IO_fd_from_handle (p[0], GNUNET_W32IO_NAMED_PIPE, (server_mode & PIPE_ACCESS_OUTBOUND ? GENERIC_WRITE : 0) | (server_mode & PIPE_ACCESS_INBOUND ? GENERIC_READ : 0), TRUE, server_mode & PIPE_ACCESS_OUTBOUND, server_mode & PIPE_ACCESS_INBOUND); + fd[1] = GNUNET_W32IO_fd_from_handle (p[1], GNUNET_W32IO_NAMED_PIPE, (client_mode & GENERIC_READ ? GENERIC_READ : 0) | (client_mode & GENERIC_WRITE ? GENERIC_WRITE : 0), TRUE, client_mode & GENERIC_WRITE, client_mode & GENERIC_READ); + + return TRUE; +} + +int +GNUNET_W32IO_anonymous_pipe (struct W32Fd *fd[2], int read_inheritable, int write_inheritable) +{ + int ret = TRUE; + BOOL bret; + DWORD err; + HANDLE p[2] = { 0, 0 }; + SECURITY_ATTRIBUTES sa; + /* Mark handle as inheritable if at least one end must be inheritable */ + int create_inheritance = read_inheritable || write_inheritable; + + sa.nLength = sizeof (sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = create_inheritance; + + bret = CreatePipe (&p[0], &p[1], &sa, 4096); + if (bret == 0) + { + err = GetLastError (); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create an anonymous pipe. GLE: %d\n", err); + ret = FALSE; + } + if (create_inheritance && !read_inheritable) + { + /* Desired read inheritance does not match create_inheritance */ + HANDLE t = p[0]; + bret = DuplicateHandle (GetCurrentProcess (), t, GetCurrentProcess (), &p[0], + 0, read_inheritable, DUPLICATE_SAME_ACCESS); + if (bret == 0) + { + err = GetLastError (); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate read end of an anonymous pipe. GLE: %d\n", err); + ret = FALSE; + p[0] = t; + } + else + CloseHandle (t); + } + if (create_inheritance && !write_inheritable) + { + /* Desired write inheritance does not match create_inheritance */ + HANDLE t = p[1]; + bret = DuplicateHandle (GetCurrentProcess (), t, GetCurrentProcess (), &p[1], + 0, write_inheritable, DUPLICATE_SAME_ACCESS); + if (bret == 0) + { + err = GetLastError (); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate write end of an anonymous pipe. GLE: %d\n", err); + ret = FALSE; + p[1] = t; + } + else + CloseHandle (t); + } + + if (ret == FALSE) + { + if (p[0]) + CloseHandle (p[0]); + if (p[1]) + CloseHandle (p[1]); + return ret; + } + + fd[0] = GNUNET_W32IO_fd_from_handle (p[0], GNUNET_W32IO_ANONYMOUS_PIPE, GENERIC_READ, TRUE, FALSE, TRUE); + fd[1] = GNUNET_W32IO_fd_from_handle (p[1], GNUNET_W32IO_ANONYMOUS_PIPE, GENERIC_WRITE, TRUE, TRUE, FALSE); + + return ret; +} + +int +GNUNET_W32IO_fsync (struct W32Fd *wfd) +{ + if (wfd == NULL || wfd->fd == (void *) -1) + { + errno = EBADF; + return -1; + } + + FlushFileBuffers (wfd->fd); + return 0; +} + +int +GNUNET_W32IO_close (struct W32Fd *wfd, int closehandle) +{ + int i; + DWORD dwresult; + + int ret = 0; + if (wfd == NULL || wfd->fd == (void *) -1) + return ret; + switch (wfd->fd_type) + { + case GNUNET_W32IO_SOCKET: + /* Break the link between the socket and its event */ + WSAEventSelect ((SOCKET) wfd->fd, NULL, 0); + ret = CloseHandle (wfd->overlapped[SOCKET_POLL_EVENT].hEvent); + ret = CloseHandle (wfd->overlapped[SOCKET_WAKEUP_EVENT].hEvent); + if (closehandle) + { + ret = ret || closesocket ((SOCKET) wfd->fd); + } + break; + case GNUNET_W32IO_FILE: + case GNUNET_W32IO_NAMED_PIPE: + for (i = 0; i < POLLLAST; i++) + { + if (wfd->overlapped[i].hEvent == INVALID_HANDLE_VALUE) + continue; + + dwresult = WaitForSingleObject (wfd->overlapped[i].hEvent, 0); + if (dwresult == WAIT_TIMEOUT && (i == POLLIN_BIT || i == POLLOUT_BIT)) + { + /* Disabled state of the event means that there is still an + * operation going on (event has to be reset manually and should + * stay signalled after I/O is complete until it is started again). + * Except for FD_CONNECT (it is unset after pipe is connected) + */ + /* We're going to free the event. Have to cancel all I/O issued by + * this thread using this fd. + */ + if (CancelIo (wfd->fd)) + { + /* Wait until I/O is cancelled */ + GetOverlappedResult (wfd->fd, &wfd->overlapped[i], &dwresult, TRUE); + } + } + ret = ret || CloseHandle (wfd->overlapped[i].hEvent); + wfd->overlapped[i].hEvent = NULL; + } + + if (wfd->writebuffer) + { + GNUNET_free (wfd->writebuffer); + wfd->writebuffer = NULL; + } + if (closehandle) + { + if (wfd->fd_type == GNUNET_W32IO_NAMED_PIPE) + DisconnectNamedPipe (wfd->fd); + ret = ret || CloseHandle (wfd->fd); + } + break; + case GNUNET_W32IO_ANONYMOUS_PIPE: + /* FIXME: do the same as for named pipes - free events, etc */ + ret = GNUNET_W32IO_anonymous_pipe_close (wfd); + if (closehandle) + ret = ret || CloseHandle (wfd->fd); + break; + default: + errno = ENOSYS; + } + GNUNET_free (wfd); + return ret; +} + +int +GNUNET_W32IO_ppoll (struct W32PollFd *fds, uint64_t nfds, const struct timespec *t, const sigset_t *sigmask) +{ + HANDLE *hEvents = NULL; + int eventCount = 0; + DWORD timeout = 0; + DWORD dwResult = 0; + DWORD wait_ret = 0; + DWORD dwError = 0; + int result = 0; + int any_errors = FALSE; + int i, j, k; + int repeat; + int hits = 0; + + /* You *can't* get better than 1 millisecond resolution on Windows. */ + if (t != NULL) + timeout = t->tv_sec * 1000 + t->tv_nsec / 1000000; + else + timeout = -1; + + /* Special case for using ppoll() as a portable way tosleep */ + if (nfds == 0) + { + SetLastError (0); + dwResult = WaitForSingleObject (GetCurrentProcess (), timeout == -1 ? INFINITE : timeout); + dwError = GetLastError (); + if (dwResult == WAIT_FAILED) + { + SetErrnoFromWinError (dwError); + result = -1; + } + return result; + } + + /* Instead of counting or using dynamic memory allocation, we'll just + * allocate the amount that is guaranteed to cover our needs. + */ + hEvents = GNUNET_new(HANDLE, nfds * (POLLLAST + 1)); + + repeat = 1; + while (repeat == 1) + { + DWORD time1, time2; + eventCount = 0; + dwResult = 0; + wait_ret = 0; + dwError = 0; + result = 0; + repeat = -1; + for (i = 0; i < nfds; i++) + { + j = GNUNET_W32IO_prepare_for_poll (&fds[i], &hEvents[eventCount]); + + if (eventCount + j - 1 >= nfds * (POLLLAST + 1) || j < 0) + { + GNUNET_free (hEvents); + return -1; + } + else + eventCount += j; + } + + SetLastError (0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "WaitForMultipleObjectsEx (%d, %x, FALSE, %d, FALSE)\n", eventCount, + hEvents, timeout == -1 ? INFINITE : timeout); + time1 = GetTickCount (); + dwResult = WaitForMultipleObjectsEx (eventCount, hEvents, FALSE, + timeout == -1 ? INFINITE : timeout, FALSE); + dwError = GetLastError (); + if ((dwResult < eventCount && dwError == NO_ERROR) || dwResult == WAIT_TIMEOUT) + { + for (i = 0; i < nfds; i++) + { + int selected = 0; + if (fds[i].fd->fd_type == GNUNET_W32IO_SOCKET) + { + int enumerated = 0; + for (j = 0; j < POLLLAST; j++) + { + if (fds[i].fd->overlapped[j].hEvent == INVALID_HANDLE_VALUE) + continue; + + wait_ret = WaitForSingleObject (fds[i].fd->overlapped[j].hEvent, 0); + if (wait_ret == WAIT_TIMEOUT) + continue; + + if ((j == SOCKET_POLL_EVENT || j == SOCKET_WAKEUP_EVENT)) + { + WSANETWORKEVENTS events; + if (!enumerated) + { + enumerated = 1; + memset (&events, 0, sizeof (events)); + int enum_ret = WSAEnumNetworkEvents ((SOCKET) fds[i].fd->fd, fds[i].fd->overlapped[SOCKET_POLL_EVENT].hEvent, &events); + if (enum_ret != 0) { + dwError = GetLastError (); + any_errors = TRUE; + SetErrnoFromWinError (dwError); + /* FIXME: make sure this is correct */ + if (dwError == WSAECONNRESET) + { + fds[i].fd->revents |= POLLHUP; + } + else + fds[i].fd->revents |= POLLERR; + break; + } + /* This way we won't lose one-time events. However, we also must + * watch what we do with the socket and drop the bits from + * lNetworkEvents when appropriate. + */ + fds[i].fd->wsaevents.lNetworkEvents |= events.lNetworkEvents; + for (k = 0; k < FD_MAX_EVENTS; k++) + { + int bit = 1 << k; + if (fds[i].fd->events & bit & fds[i].fd->wsaevents.lNetworkEvents) + { + fds[i].fd->revents |= bit; + hits += 1; + selected = 1; + if (fds[i].fd->wsaevents.iErrorCode[k] != 0) + fds[i].fd->revents |= POLLERR; + } + } + if (fds[i].fd->revents & FD_CONNECT) + { + fds[i].fd->revents &= ~FD_CONNECT; + fds[i].fd->revents |= POLLOUT; + } + if (fds[i].fd->revents & FD_ACCEPT) + { + fds[i].fd->revents &= ~FD_ACCEPT; + fds[i].fd->revents |= POLLIN; + } + if (fds[i].fd->wsaevents.lNetworkEvents & FD_CLOSE) + { + fds[i].fd->revents |= POLLHUP; + fds[i].fd->revents &= ~POLLOUT; + hits += 1; + selected = 1; + } + } + if (j == SOCKET_WAKEUP_EVENT) + ResetEvent (fds[i].fd->overlapped[j].hEvent); + } + else + { + switch (j) + { + case POLLERR_BIT: + case POLLHUP_BIT: + case POLLNVAL_BIT: + fds[i].fd->revents |= 1 << j; + break; + default: + ; + } + ResetEvent (fds[i].fd->overlapped[j].hEvent); + } + } + } + else if (fds[i].fd->fd_type == GNUNET_W32IO_FILE) + { + /* TODO: better implementation (consider two processes sharing + * a file, both for reading and for writing; consider deleting + * a file while a process waits on it). + */ + for (j = 0; j < POLLLAST; j++) + { + DWORD eventSet; + BOOL bret; + int bit = 1 << j; + + if (fds[i].fd->overlapped[j].hEvent == INVALID_HANDLE_VALUE) + continue; + + eventSet = 0; + eventSet = WaitForSingleObject (fds[i].fd->overlapped[j].hEvent, 0); + if (eventSet == WAIT_FAILED) + { + // FIXME: handle the error + any_errors = TRUE; + continue; + } + if (eventSet == WAIT_TIMEOUT) + continue; + + /* common code for all three events */ + bret = 0; + switch (j) + { + case POLLIN_BIT: + case POLLOUT_BIT: + fds[i].fd->revents |= bit; + fds[i].fd->wsaevents.lNetworkEvents |= bit; + fds[i].fd->wsaevents.iErrorCode[j] = 0; + /* If ReadFile returned immediately, won't GetOvelappedResult() block forever? */ + /* + bret = GetOverlappedResult (fds[i].fd->fd, + &fds[i].fd->overlapped[j], &wait_ret, TRUE); + */ + selected = 1; + break; + case POLLERR_BIT: + case POLLNVAL_BIT: + fds[i].fd->wsaevents.iErrorCode[j] = fds[i].fd->dwerror; + default: + fds[i].fd->revents |= bit; + fds[i].fd->wsaevents.lNetworkEvents |= bit; + } + + switch (j) + { + case POLLIN_BIT: + /* + if (bret != 0) + break; + + + wait_ret = GetLastError (); + if (wait_ret == ERROR_HANDLE_EOF) { + fds[i].fd->revents &= ~FD_READ; + fds[i].fd->wsaevents.lNetworkEvents &= ~FD_READ; + } else { + any_errors = TRUE; + fds[i].fd->revents |= POLLERR; + fds[i].fd->wsaevents.iErrorCode[j] = wait_ret; + errno = gst_win32_error_to_errno(wait_ret); + } + */ + break; + case POLLOUT_BIT: + if (fds[i].fd->writebuffer) { + GNUNET_free (fds[i].fd->writebuffer); + fds[i].fd->writebuffer = NULL; + } + /* + if (bret != 0) + break; + + wait_ret = GetLastError (); + if (wait_ret != NO_ERROR) + { + any_errors = TRUE; + fds[i].fd->revents |= POLLERR; + fds[i].fd->wsaevents.iErrorCode[j] = wait_ret; + errno = gst_win32_error_to_errno(wait_ret); + } + */ + break; + default: + break; + } + ResetEvent (fds[i].fd->overlapped[j].hEvent); + } + } + else if (fds[i].fd->fd_type == GNUNET_W32IO_ANONYMOUS_PIPE) + { + for (j = 0; j < POLLLAST; j++) + { + DWORD eventSet; + BOOL bret; + int bit = 1 << j; + + if (fds[i].fd->overlapped[j].hEvent == INVALID_HANDLE_VALUE) + continue; + + if (j == POLLOUT_BIT && WaitForSingleObject (fds[i].fd->anon_thread_mutex, 0) == WAIT_OBJECT_0) + { + /* Writability is uncheckable and always assumed to be true whenever + * the pipe is not writing. + * POLLOUT event only indicates that a write operation is complete, + * but if no write operation took place, the code in the switch + * below will not be executed for POLLOUT, which is why this check + * is necessary. + */ + if (fds[i].fd->writebuffer == NULL) + { + fds[i].fd->revents |= bit; + fds[i].fd->wsaevents.lNetworkEvents |= bit; + SetEvent (fds[i].fd->overlapped[POLLOUT_BIT].hEvent); + } + ReleaseMutex (fds[i].fd->anon_thread_mutex); + } + + eventSet = 0; + eventSet = WaitForSingleObject (fds[i].fd->overlapped[j].hEvent, 0); + if (eventSet == WAIT_FAILED) + { + /* FIXME: handle the error */ + any_errors = TRUE; + continue; + } + if (eventSet == WAIT_TIMEOUT) + continue; + + /* common code for all three events */ + bret = 0; + switch (j) + { + case POLLIN_BIT: + case POLLOUT_BIT: + case POLLHUP_BIT: + fds[i].fd->revents |= bit; + fds[i].fd->wsaevents.lNetworkEvents |= bit; + fds[i].fd->wsaevents.iErrorCode[j] = 0; + + selected = 1; + WaitForSingleObject (fds[i].fd->anon_thread_mutex, INFINITE); + break; + case POLLERR_BIT: + case POLLNVAL_BIT: + fds[i].fd->wsaevents.iErrorCode[j] = fds[i].fd->dwerror; + default: + fds[i].fd->revents |= bit; + fds[i].fd->wsaevents.lNetworkEvents |= bit; + } + + switch (j) + { + case POLLIN_BIT: + if (fds[i].fd->dwerror == ERROR_BROKEN_PIPE) { + fds[i].fd->revents |= POLLHUP; + fds[i].fd->wsaevents.lNetworkEvents |= FD_CLOSE; + + SetEvent (fds[i].fd->overlapped[POLLHUP_BIT].hEvent); + } else if (fds[i].fd->dwerror != NO_ERROR) { + any_errors = TRUE; + fds[i].fd->revents |= POLLERR; + fds[i].fd->wsaevents.iErrorCode[j] = wait_ret; + SetErrnoFromWinError (fds[i].fd->dwerror); + } + break; + case POLLOUT_BIT: + if (fds[i].fd->writebuffer) { + GNUNET_free (fds[i].fd->writebuffer); + fds[i].fd->writebuffer = NULL; + } + + if (fds[i].fd->dwerror == ERROR_BROKEN_PIPE) { + fds[i].fd->revents |= POLLHUP; + fds[i].fd->wsaevents.lNetworkEvents |= FD_CLOSE; + + SetEvent (fds[i].fd->overlapped[POLLHUP_BIT].hEvent); + } else if (fds[i].fd->dwerror != NO_ERROR) { + any_errors = TRUE; + fds[i].fd->revents |= POLLERR; + fds[i].fd->wsaevents.iErrorCode[j] = fds[i].fd->dwerror; + SetErrnoFromWinError (fds[i].fd->dwerror); + } + break; + case POLLHUP_BIT: + if (!(fds[i].fd->events & (POLLIN | POLLOUT))) + { + if (fds[i].fd->dwerror == ERROR_BROKEN_PIPE) + { + fds[i].fd->revents |= POLLHUP; + fds[i].fd->wsaevents.lNetworkEvents |= FD_CLOSE; + } else if (fds[i].fd->dwerror == NO_ERROR) { + /* FIXME: make sure this is the right way... + * something might have set [POLLHUP_BIT].hEvent but not + * the dwerror + */ + ResetEvent (fds[i].fd->overlapped[POLLHUP_BIT].hEvent); + } else { + any_errors = TRUE; + fds[i].fd->revents |= POLLERR; + fds[i].fd->wsaevents.iErrorCode[j] = fds[i].fd->dwerror; + SetErrnoFromWinError (fds[i].fd->dwerror); + } + } + break; + default: + /* Never set */ + break; + } + + switch (j) + { + case POLLIN_BIT: + case POLLOUT_BIT: + case POLLHUP_BIT: + ReleaseMutex (fds[i].fd->anon_thread_mutex); + break; + default: + ; + } + ResetEvent (fds[i].fd->overlapped[j].hEvent); + } + } + else if (fds[i].fd->fd_type == GNUNET_W32IO_NAMED_PIPE) + { + for (j = 0; j < POLLLAST; j++) + { + DWORD eventSet; + int bret; + int bit = 1 << j; + + if (fds[i].fd->overlapped[j].hEvent == INVALID_HANDLE_VALUE) + continue; + + if (j == POLLOUT_BIT) + { + if (fds[i].fd->writebuffer == NULL) + { + fds[i].fd->revents |= bit; + fds[i].fd->wsaevents.lNetworkEvents |= bit; + SetEvent (fds[i].fd->overlapped[POLLOUT_BIT].hEvent); + } + } + + eventSet = 0; + eventSet = WaitForSingleObject (fds[i].fd->overlapped[j].hEvent, 0); + if (eventSet == WAIT_FAILED) + { + /* FIXME: handle the error */ + any_errors = TRUE; + continue; + } + if (eventSet == WAIT_TIMEOUT) + continue; + + /* common code for all three events */ + bret = 0; + switch (j) + { + case POLLIN_BIT: + case FD_CONNECT_BIT: + case POLLOUT_BIT: + fds[i].fd->revents |= bit; + fds[i].fd->wsaevents.lNetworkEvents |= bit; + fds[i].fd->wsaevents.iErrorCode[j] = 0; + bret = GetOverlappedResult (fds[i].fd->fd, + &fds[i].fd->overlapped[j], &wait_ret, TRUE); + selected = 1; + break; + default: + ; + } + + switch (j) + { + case POLLIN_BIT: + /* Our read-zero-bytes operation is complete, which means that + * there is data to be read. Keep the event set. It will be + * unset by our read() implementation or by *_prepare_for_poll + */ + if (bret != 0) + break; + + wait_ret = GetLastError (); + if (wait_ret == ERROR_BROKEN_PIPE) { + fds[i].fd->revents |= FD_CLOSE; + fds[i].fd->wsaevents.lNetworkEvents |= FD_CLOSE; + + SetEvent (fds[i].fd->overlapped[FD_CLOSE_BIT].hEvent); + + /* Set FD_CONNECT event so that we can attempt to connect again */ + SetEvent (fds[i].fd->overlapped[FD_CONNECT_BIT].hEvent); + } else { + any_errors = TRUE; + fds[i].fd->revents |= POLLERR; + fds[i].fd->wsaevents.iErrorCode[j] = wait_ret; + SetErrnoFromWinError (wait_ret); + } + break; + case FD_CONNECT_BIT: + /* Our ConnectNamedPipe() operation is complete, which means that + * the pipe was not connected before, but is connected now. + */ + /* Unset the event - that would prevent *_prepare_for_poll from + * trying to re-connect the pipe each time it is called. + */ + ResetEvent (fds[i].fd->overlapped[j].hEvent); + + if (bret != 0) + break; + + wait_ret = GetLastError (); + if (wait_ret == ERROR_NO_DATA) { + fds[i].fd->revents |= FD_CLOSE; + fds[i].fd->wsaevents.lNetworkEvents |= FD_CLOSE; + SetEvent (fds[i].fd->overlapped[FD_CLOSE_BIT].hEvent); + SetEvent (fds[i].fd->overlapped[FD_CONNECT_BIT].hEvent); + } else { + any_errors = TRUE; + fds[i].fd->revents |= POLLERR; + fds[i].fd->wsaevents.iErrorCode[j] = wait_ret; + SetErrnoFromWinError (wait_ret); + } + + break; + case POLLOUT_BIT: + /* Our WriteFile() operation is complete, which means that the pipe + * is ready to write once more. Keep the event set - it will be + * unset by our write() implementation. + */ + if (fds[i].fd->writebuffer) { + GNUNET_free (fds[i].fd->writebuffer); + fds[i].fd->writebuffer = NULL; + } + + if (bret != 0) + break; + + wait_ret = GetLastError (); + if (wait_ret == ERROR_BROKEN_PIPE) { + fds[i].fd->revents |= FD_CLOSE; + fds[i].fd->wsaevents.lNetworkEvents |= FD_CLOSE; + + SetEvent (fds[i].fd->overlapped[FD_CLOSE_BIT].hEvent); + SetEvent (fds[i].fd->overlapped[FD_CONNECT_BIT].hEvent); + } else { + any_errors = TRUE; + fds[i].fd->revents |= POLLERR; + fds[i].fd->wsaevents.iErrorCode[j] = wait_ret; + SetErrnoFromWinError (wait_ret); + } + + break; + case POLLHUP_BIT: + if (!(fds[i].fd->events & (POLLIN | POLLOUT))) + { + if (!(fds[i].fd->revents & POLLHUP)) + { + bret = GetOverlappedResult (fds[i].fd->fd, + &fds[i].fd->overlapped[POLLHUP_BIT], &wait_ret, TRUE); + selected = 1; + + wait_ret = GetLastError (); + if (wait_ret != NO_ERROR) + { + fds[i].fd->revents |= POLLHUP_BIT; + fds[i].fd->wsaevents.lNetworkEvents |= FD_CLOSE; + } else { + ResetEvent (fds[i].fd->overlapped[FD_CLOSE_BIT].hEvent); + fds[i].fd->revents &= ~POLLHUP_BIT; + fds[i].fd->wsaevents.lNetworkEvents &= ~FD_CLOSE; + } + } + else + { + selected = 1; + fds[i].fd->revents |= POLLHUP; + fds[i].fd->wsaevents.lNetworkEvents |= FD_CLOSE; + } + } + break; + default: + break; + } + ResetEvent (fds[i].fd->overlapped[j].hEvent); + } + } + if (fds[i].fd->fd_type != GNUNET_W32IO_SOCKET) + hits += 1; + result += selected; + } + } + else if (dwResult == WAIT_TIMEOUT) + { + /* result = 0; */ + } + else if (dwResult == WAIT_FAILED) + { + SetErrnoFromWinError (dwError); + result = -1; + } + if (any_errors && result >= 0) + { + result = -1; + } + if (hits == 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Repeating ppoll()\n"); + time2 = GetTickCount (); + if (timeout != (DWORD) -1) + { + DWORD new_timeout = timeout - time2 > time1 ? time2 - time1 : (0xFFFFFFFF - time1) + time2; + if (new_timeout > timeout) + timeout = -1; + else + timeout = new_timeout; + } + hits = 0; + repeat = 1; + } + } + + GNUNET_free (hEvents); + return result; +} + +/** + * Prepares @wpfd for polling + */ +int +GNUNET_W32IO_prepare_for_poll (struct W32PollFd *wpfd, HANDLE *polllist) +{ + struct W32Fd *wfd = wpfd->fd; + + int ret; + DWORD dwret; + int result = 0; + wfd->revents = 0; + if (wfd->fd_type == GNUNET_W32IO_SOCKET) + { + //DWORD sockevents = wfd->events & FD_ALL_EVENTS; + WSANETWORKEVENTS events; + int hits = 0; + memset (&events, 0, sizeof (events)); + int enum_ret = WSAEnumNetworkEvents ((SOCKET) wfd->fd, wfd->overlapped[SOCKET_POLL_EVENT].hEvent, &events); + if (enum_ret != 0) { + DWORD dwError = GetLastError (); + SetErrnoFromWinError (dwError); + /* FIXME: make sure this is correct */ + if (dwError == WSAECONNRESET) + { + wfd->wsaevents.lNetworkEvents |= FD_CLOSE; + } + else + { + /* FIXME: Not sure about this one. Not all errors lead to FD_CLOSE */ + wfd->wsaevents.lNetworkEvents |= FD_CLOSE; + wfd->wsaevents.iErrorCode[FD_CLOSE] = dwError; + } + } + else + { + /* This way we won't lose one-time events. However, we also must + * watch what we do with the socket and drop the bits from + * lNetworkEvents when appropriate. + */ + int k; + wfd->wsaevents.lNetworkEvents |= events.lNetworkEvents; + for (k = 0; k < FD_MAX_EVENTS; k++) + { + int bit = 1 << k; + if (wfd->events & bit & wfd->wsaevents.lNetworkEvents) + { + wfd->revents |= bit; + hits += 1; + if (wfd->wsaevents.iErrorCode[k] != 0) + wfd->revents |= POLLERR; + } + } + if (wfd->revents & FD_CONNECT) + { + wfd->revents &= ~FD_CONNECT; + wfd->revents |= POLLOUT; + } + if (wfd->revents & FD_ACCEPT) + { + wfd->revents &= ~FD_ACCEPT; + wfd->revents |= POLLIN; + } + if (wfd->wsaevents.lNetworkEvents & FD_CLOSE) + { + wfd->revents |= POLLHUP; + wfd->revents &= ~POLLOUT; + hits += 1; + } + } + if (hits > 0 || ((wfd->events & POLLOUT) && (wfd->wsaevents.lNetworkEvents & FD_WRITE)) || (wfd->wsaevents.lNetworkEvents & FD_CLOSE)) + SetEvent (wfd->overlapped[SOCKET_WAKEUP_EVENT].hEvent); + polllist[result++] = wfd->overlapped[SOCKET_POLL_EVENT].hEvent; + polllist[result++] = wfd->overlapped[SOCKET_WAKEUP_EVENT].hEvent; + wfd->revents = 0; + } + else if (wfd->fd_type == GNUNET_W32IO_FILE) + { + /* Only POLLIN and POLLOUT are relevant for files + * Error conditions will be reported later on their own accord, we do not + * need event objects to detect these + */ + wfd->events &= POLLIN | POLLOUT; + + if (wfd->events & POLLIN || wfd->events & POLLOUT) + { + /* Ready to read/write unconditinally */ + SetEvent (wfd->overlapped[FILE_READY_POLL_EVENT].hEvent); + polllist[result++] = wfd->overlapped[FILE_READY_POLL_EVENT].hEvent; + } + else + ResetEvent (wfd->overlapped[FILE_READY_POLL_EVENT].hEvent); + polllist[result++] = wfd->overlapped[POLLERR_BIT].hEvent; + polllist[result++] = wfd->overlapped[POLLNVAL_BIT].hEvent; + } + else if (wfd->fd_type == GNUNET_W32IO_ANONYMOUS_PIPE) + { + /* Anonymous pipes support only POLLIN and POLLOUT. + * Also don't poll for reading/writing on write-only/read-only pipes + */ + wfd->events &= (wfd->access & GENERIC_READ ? POLLIN : 0) | (wfd->access & GENERIC_WRITE ? POLLOUT : 0); + if (wfd->events & POLLIN) + { + if (WaitForSingleObject (wfd->anon_thread_mutex, 0) == WAIT_OBJECT_0) + { + /* We've locked the mutex - which means that worker thread is not + * working at the moment. + */ + /* Try to read 0 bytes from the pipe. This will enable the event + * when there is data to read. + */ + /* Don't worry about the impossibility of polling for readability + * and writing simultaneously. Anonymous pipes are either write-only + * or read-only, so the application naturally shouldn't try to do + * both. If it does, the behaviour is... undefined. + */ + wfd->work = 0; /* read */ + wfd->readbuffer = NULL; + wfd->to_read = 0; + + /* Working thread waits with WaitForMultipleObjects, so it will only + * wake up when the event is set AND the mutex is available. + */ + SetEvent (wfd->overlapped[ANON_PIPE_THREAD_POLL_EVENT].hEvent); + ReleaseMutex (wfd->anon_thread_mutex); + } + /* Else worker thread is already working. Just as well */; + + /* We will wait upon wfd->overlapped[ANON_PIPE_MAIN_THREAD_POLL_EVENT].hEvent. When it is enabled, + * the worker thread have completed its work and released the mutex, + * and we can obtain the data. + */ + polllist[result++] = wfd->overlapped[ANON_PIPE_MAIN_THREAD_POLL_EVENT].hEvent; + } + if (wfd->events & POLLOUT) + { + /* We will wait upon wfd->overlapped[ANON_PIPE_MAIN_THREAD_POLL_EVENT].hEvent. When it is enabled, + * the worker thread have completed its work and released the mutex, + * and we can write once again. + */ + polllist[result++] = wfd->overlapped[ANON_PIPE_MAIN_THREAD_POLL_EVENT].hEvent; + } + /* Read or write operations will reveal a broken pipe, but if we + * have neither, we have to do something to detect that. + */ + if (!(wfd->events & FD_READ) && !(wfd->events & FD_WRITE) && WaitForSingleObject (wfd->anon_thread_mutex, 0) == WAIT_OBJECT_0) + { + if (wfd->access & GENERIC_READ) + { + /* Prefer reading */ + wfd->work = 0; /* read */ + wfd->readbuffer = GNUNET_malloc (1); + /* Try to read 1 byte from the pipe. + * If we succeed, we can give this byte later to the caller from + * the read() function. If we fail, we will know that the pipe + * is broken. + * Reading 0 bytes won't really let us detect a broken pipe. + */ + wfd->to_read = 1; + + /* Working thread waits with WaitForMultipleObjects, so it will only + * wake up when the event is set AND the mutex is available. + */ + SetEvent (wfd->overlapped[ANON_PIPE_THREAD_POLL_EVENT].hEvent); + } + else if (wfd->access & GENERIC_WRITE) + { + int skipwriting = FALSE; + if (wfd->writebuffer) + { + /* We've been writing somethings */ + GNUNET_free (wfd->writebuffer); + wfd->writebuffer = NULL; + + /* Process any error conditions from previous write operation */ + if (wfd->bret == 0) + { + SetEvent (wfd->overlapped[POLLHUP_BIT].hEvent); + skipwriting = TRUE; + } + } + + /* Writing a byte into the pipe. BAD IDEA! Disabled. */ + if (!skipwriting && 0) + { + wfd->work = 1; /* write */ + wfd->writebuffer = GNUNET_malloc (1); + wfd->writebuffer[0] = 0; + wfd->to_write = 0; + + SetEvent (wfd->overlapped[ANON_PIPE_THREAD_POLL_EVENT].hEvent); + } + } + ReleaseMutex (wfd->anon_thread_mutex); + /* We will wait upon wfd->overlapped[POLLHUP_BIT].hEvent, + * which will be set by the working thread when the pipe breaks + */ + } + polllist[result++] = wfd->overlapped[POLLERR_BIT].hEvent; + polllist[result++] = wfd->overlapped[POLLNVAL_BIT].hEvent; + polllist[result++] = wfd->overlapped[POLLHUP_BIT].hEvent; + } + else if (wfd->fd_type == GNUNET_W32IO_NAMED_PIPE) + { + /* POLLIN and POLLOUT might mean that the caller is interested in + * connection events, not reads/writes, so we won't check these + * against access + */ + wfd->events &= POLLIN | POLLOUT; + if (wfd->events & (POLLIN | POLLOUT)) + { + if (WaitForSingleObject (wfd->overlapped[FD_CONNECT_BIT].hEvent, 0) == + WAIT_OBJECT_0) + { + /* Try to connect the pipe every time it is possible to do that. + * Most of the time it will return immediately with the + * ERROR_PIPE_CONNECTED error code to indicate that the pipe is + * already connected, else it will set the pipe working on + * becoming connected. + */ + ZeroOverlappedExceptEvent(wfd->overlapped[FD_CONNECT_BIT]); + dwret = ConnectNamedPipe (wfd->fd, &wfd->overlapped[FD_CONNECT_BIT]); + if (dwret == 0) + { + dwret = GetLastError (); + if (dwret == ERROR_PIPE_CONNECTED || + dwret == ERROR_INVALID_FUNCTION) + { + /* Already connected or we're using client end of the pipe. + * Disable the event to prevent further connection attempts. + */ + ResetEvent (wfd->overlapped[FD_CONNECT_BIT].hEvent); + } + else + { + SetEvent (wfd->overlapped[POLLERR_BIT].hEvent); + wfd->dwerror = dwret; + } + } + } + polllist[result++] = wfd->overlapped[FD_CONNECT_BIT].hEvent; + } + if (wfd->events & POLLIN) + { + if (WaitForSingleObject (wfd->overlapped[POLLIN_BIT].hEvent, 0) == + WAIT_OBJECT_0) + { + /* Try to read 0 bytes from the pipe. This will enable the event + * when there is data to read. + */ + ZeroOverlappedExceptEvent(wfd->overlapped[POLLIN_BIT]); + dwret = ReadFile (wfd->fd, (void *) &wfd->to_read, 0, + NULL, &wfd->overlapped[POLLIN_BIT]); + if (dwret == 0) + { + dwret = GetLastError (); + if (dwret == ERROR_BROKEN_PIPE) + { + SetEvent (wfd->overlapped[POLLHUP_BIT].hEvent); + } + else if (dwret != ERROR_IO_PENDING) + { + SetEvent (wfd->overlapped[POLLERR_BIT].hEvent); + wfd->dwerror = dwret; + } + } + } + polllist[result++] = wfd->overlapped[POLLIN_BIT].hEvent; + } + /* There is no need to prepare for POLLOUT - underlying writing is + * asynchronous and is always available if there is no writing operations + * pending. + */ + if (wfd->events & POLLOUT) + { + polllist[result++] = wfd->overlapped[POLLOUT_BIT].hEvent; + } + /* read or write operations will reveal a broken pipe, but if we + * have neither, we have to do something to detect that. + */ + if (!(wfd->events & POLLIN) && !(wfd->events & POLLOUT)) + { + if (wfd->access & GENERIC_READ) + { + /* Prefer reading */ + if (WaitForSingleObject (wfd->overlapped[POLLIN_BIT].hEvent, 0) == + WAIT_OBJECT_0) + { + /* Try to read 0 bytes from the pipe. This will enable the event + * when there is data to read. + */ + ZeroOverlappedExceptEvent(wfd->overlapped[POLLIN_BIT]); + dwret = ReadFile (wfd->fd, (void *) &wfd->to_read, 0, + NULL, &wfd->overlapped[POLLIN_BIT]); + if (dwret == 0) + { + dwret = GetLastError (); + if (dwret == ERROR_BROKEN_PIPE) + { + SetEvent (wfd->overlapped[POLLHUP_BIT].hEvent); + } + else if (dwret != ERROR_IO_PENDING) + { + SetEvent (wfd->overlapped[POLLERR_BIT].hEvent); + wfd->dwerror = dwret; + } + } else { + ResetEvent (wfd->overlapped[POLLHUP_BIT].hEvent); + } + } + } + else if (wfd->access & GENERIC_WRITE && 0) + { + if (wfd->writebuffer != NULL) + { + if (WaitForSingleObject (wfd->overlapped[POLLOUT_BIT].hEvent, 0) == + WAIT_OBJECT_0 && + WaitForSingleObject (wfd->overlapped[POLLHUP_BIT].hEvent, 0) == + WAIT_TIMEOUT) + { + /* Get the operation result, free the buffer */ + DWORD wait_ret; + ret = GetOverlappedResult ((HANDLE) wfd->fd, + &wfd->overlapped[POLLOUT_BIT], &wait_ret, TRUE); + GNUNET_free (wfd->writebuffer); + wfd->writebuffer = NULL; + } + } + if (wfd->writebuffer == NULL) + { + ZeroOverlappedExceptEvent(wfd->overlapped[POLLOUT_BIT]); + BOOL bret = WriteFile ((HANDLE) wfd->fd, (void *) &wfd->to_write, 0, NULL, + &wfd->overlapped[POLLOUT_BIT]); + if (bret == 0) { + DWORD dwret = GetLastError (); + if (dwret != ERROR_IO_PENDING) + { + if (dwret == ERROR_BROKEN_PIPE) + SetEvent (wfd->overlapped[POLLHUP_BIT].hEvent); + else + SetEvent (wfd->overlapped[POLLERR_BIT].hEvent); + } + } else { + ResetEvent (wfd->overlapped[POLLHUP_BIT].hEvent); + } + } + } + } + polllist[result++] = wfd->overlapped[POLLERR_BIT].hEvent; + polllist[result++] = wfd->overlapped[POLLNVAL_BIT].hEvent; + polllist[result++] = wfd->overlapped[POLLHUP_BIT].hEvent; + } + + return result; +} + +GNUNET_W32IO_HandleType +GNUNET_W32IO_find_handle_type (HANDLE h) +{ + DWORD dwret, dwerr, dwret2, dwerr2; + WSAPROTOCOL_INFO pi; + dwret = GetFileType (h); + dwerr = GetLastError (); + switch (dwret) { + case FILE_TYPE_DISK: + return GNUNET_W32IO_FILE; + case FILE_TYPE_CHAR: + return GNUNET_W32IO_CONSOLE; + case FILE_TYPE_PIPE: + /* It's a socket or a pipe */ + dwret2 = WSADuplicateSocket ((SOCKET) h, 0, &pi); + dwerr2 = GetLastError (); + if (dwret2 == 0) { + /* WSADuplicateSocket() should always fail when called with these + * arguments. + */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "This should not happen\n"); + return GNUNET_W32IO_UNSPECIFIED; + } else { + if (dwerr2 == WSAEINVAL) { + /* Definitely a socket */ + return GNUNET_W32IO_SOCKET; + } else { + /* Not likely a socket. A pipe? */ + dwret2 = GetNamedPipeInfo (h, NULL, NULL, NULL, NULL); + dwerr2 = GetLastError (); + if (dwret2 != 0) { + /* It's a kind of pipe */ + dwret2 = GetNamedPipeHandleStateA (h, NULL, NULL, NULL, + NULL, NULL, 0); + dwerr2 = GetLastError (); + if (dwret2 == 0) { + if (dwerr2 == ERROR_INVALID_PARAMETER) { + /* It's an anonymous pipe */ + return GNUNET_W32IO_ANONYMOUS_PIPE; + } else { + /* We have no idea what it is */ + return GNUNET_W32IO_UNKNOWN; + } + } else { + /* It's a named pipe */ + return GNUNET_W32IO_NAMED_PIPE; + } + } else { + /* If it was something other than a pipe, we would have found + * it by now, and now it's not even a pipe. + * It could be that this is a pipe, but damaged (or in a wrong + * state), or not a pipe at all. + */ + return GNUNET_W32IO_UNKNOWN; + } + } + } + break; + case FILE_TYPE_UNKNOWN: + default: + if (dwerr == NO_ERROR) + return GNUNET_W32IO_UNKNOWN; + else + return GNUNET_W32IO_UNSPECIFIED; + } + return GNUNET_W32IO_UNSPECIFIED; +} + +/** + * Allows us to create W32Fd from a HANDLE we've already got somewhere else. + * Will try to find the handle type on its own, or we can point it out. + * + * @param h handle + * @param htype type of the handle @h + * @param access access rights to the handle @h + * @param connected 1 if @h is connected, 0 otherwise + * @param writeable 1 if it is possible to write into @h without blocking, 0 otherwise + * @param readable 1 if it is possible to read from @h without blocking, 0 otherwise + */ +struct W32Fd * +GNUNET_W32IO_fd_from_handle (HANDLE h, GNUNET_W32IO_HandleType htype, DWORD access, + int connected, int writeable, int readable) +{ + int i; + struct W32Fd *wfd = NULL; + DWORD error_code = 0; + BOOL bret = FALSE; + + wfd = GNUNET_new(struct W32Fd, 1); + + wfd->fd = h; + wfd->fd_type = htype; + if (htype == GNUNET_W32IO_UNSPECIFIED) + wfd->fd_type = GNUNET_W32IO_find_handle_type (h); + + for (i = 0; i < POLLLAST; i++) + wfd->overlapped[i].hEvent = INVALID_HANDLE_VALUE; + + wfd->anon_thread = INVALID_HANDLE_VALUE; + wfd->anon_thread_mutex = INVALID_HANDLE_VALUE; + wfd->work = -1; + + switch (wfd->fd_type) { + case GNUNET_W32IO_UNSPECIFIED: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Handle %p is of unspecified type\n", h); + goto error; + break; + case GNUNET_W32IO_UNKNOWN: + /* Failed to find the type - should not happen, unless we're + * feeding unexpected handles to the function. + */ + /* A special hacky logic will allow us to create W32Fds with + * zero handles. They will have one valid event (like sockets do). + * This can be used for wakeup purposes, i hope. + */ + /* + * I've trashed wakeup-event concept in favor of normal control socket + * read/write, so i'm not sure this hack is necessary (besides, i've + * never tried to actually use it). + */ + /* wfd->overlapped[0].hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); */ + goto error; + break; + case GNUNET_W32IO_FILE: + for (i = 0; i < POLLLAST; i++) + { + switch (i) + { + case FILE_READY_POLL_EVENT: + case POLLHUP_BIT: + case POLLERR_BIT: + case POLLNVAL_BIT: + SetLastError (0); + wfd->overlapped[i].hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + error_code = GetLastError (); + if (wfd->overlapped[i].hEvent == INVALID_HANDLE_VALUE || error_code != NO_ERROR) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateEvent() returned %d. GLE: %d\n", wfd->overlapped[i].hEvent, error_code); + goto error; + } + break; + default: + break; + } + } + wfd->access = access; + break; + case GNUNET_W32IO_NAMED_PIPE: + for (i = 0; i < POLLLAST; i++) + { + switch (i) + { + case POLLIN_BIT: + case POLLOUT_BIT: + case FD_CONNECT_BIT: + case POLLHUP_BIT: + case POLLERR_BIT: + case POLLNVAL_BIT: + SetLastError (0); + wfd->overlapped[i].hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + error_code = GetLastError (); + if (wfd->overlapped[i].hEvent == INVALID_HANDLE_VALUE || error_code != NO_ERROR) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateEvent() returned %d. GLE: %d\n", wfd->overlapped[i].hEvent, error_code); + goto error; + } + break; + default: + break; + } + } + /* Enable the overlapped read/write events where applicable, + * indicating that a write/read operation is not in progress. + * Enable the connect overlapped event if the pipe is not connected, + * indicating that a connection attempt can be made. + */ + if (!connected) + { + SetLastError (0); + bret = SetEvent (wfd->overlapped[FD_CONNECT_BIT].hEvent); + error_code = GetLastError (); + if (error_code != NO_ERROR || !bret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "SetEvent() returned %d. GLE: %d\n", bret, error_code); + goto error; + } + } + else + { + if (writeable && (access & GENERIC_WRITE)) + { + SetLastError (0); + bret = SetEvent (wfd->overlapped[POLLOUT_BIT].hEvent); + error_code = GetLastError (); + if (error_code != NO_ERROR || !bret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "SetEvent() returned %d. GLE: %d\n", bret, error_code); + goto error; + } + } + if (readable && (access & GENERIC_READ)) + { + SetLastError (0); + bret = SetEvent (wfd->overlapped[POLLIN_BIT].hEvent); + error_code = GetLastError (); + if (error_code != NO_ERROR || !bret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "SetEvent() returned %d. GLE: %d\n", bret, error_code); + goto error; + } + } + } + wfd->access = access; + break; + case GNUNET_W32IO_ANONYMOUS_PIPE: + SetLastError (0); + wfd->anon_thread_mutex = CreateMutex (NULL, FALSE, NULL); + error_code = GetLastError (); + if (wfd->anon_thread_mutex == INVALID_HANDLE_VALUE || error_code != NO_ERROR) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateMutex() returned %d. GLE: %d\n", wfd->anon_thread_mutex, error_code); + goto error; + } + for (i = 0; i < POLLLAST; i++) + { + switch (i) + { + case ANON_PIPE_THREAD_POLL_EVENT: + case ANON_PIPE_MAIN_THREAD_POLL_EVENT: + case POLLHUP_BIT: + case POLLERR_BIT: + case POLLNVAL_BIT: + SetLastError (0); + wfd->overlapped[i].hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + error_code = GetLastError (); + if (wfd->overlapped[i].hEvent == INVALID_HANDLE_VALUE || error_code != NO_ERROR) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateEvent() returned %d. GLE: %d\n", wfd->overlapped[i].hEvent, error_code); + goto error; + } + break; + default: + break; + } + } + wfd->alive = TRUE; + if ((writeable && (access & GENERIC_WRITE)) || (readable && (access & GENERIC_READ))) + { + SetLastError (0); + bret = SetEvent (wfd->overlapped[ANON_PIPE_MAIN_THREAD_POLL_EVENT].hEvent); + error_code = GetLastError (); + if (error_code != NO_ERROR || !bret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "SetEvent() returned %d. GLE: %d\n", bret, error_code); + goto error; + } + } + wfd->access = access; + /* The thread will be stared immediately, and will go into wait upon + * the wfd->overlapped[ANON_PIPE_THREAD_POLL_EVENT].hEvent (which is + * disabled at creation) soon after that + */ + SetLastError (0); + wfd->anon_thread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) &GNUNET_W32IO_anonymous_pipe_thread_func, (LPVOID) wfd, 0, &error_code); + error_code = GetLastError (); + if (wfd->anon_thread == INVALID_HANDLE_VALUE || error_code != NO_ERROR) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateThread() returned %d. GLE: %d\n", wfd->anon_thread, error_code); + goto error; + } + break; + case GNUNET_W32IO_CONSOLE: + /* It's a console handle or a CRT file descriptor. + * FIXME: does it support async I/O? Use threads? + */ + break; + case GNUNET_W32IO_SOCKET: + for (i = 0; i < POLLLAST; i++) + { + switch (i) + { + case SOCKET_POLL_EVENT: + case SOCKET_WAKEUP_EVENT: + case POLLHUP_BIT: + case POLLERR_BIT: + case POLLNVAL_BIT: + SetLastError (0); + wfd->overlapped[i].hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); + error_code = GetLastError (); + if (wfd->overlapped[i].hEvent == INVALID_HANDLE_VALUE || error_code != NO_ERROR) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CreateEvent() returned %d. GLE: %d\n", wfd->overlapped[i].hEvent, error_code); + goto error; + } + break; + default: + break; + } + if (i == SOCKET_POLL_EVENT) + { + /* Bind wfd->overlapped[SOCKET_POLL_EVENT].hEvent object to the socket wfd->fd. + * It will be set when one of the events from sockevents occurrs + * This call clears internal socket event record in wfd->fd, so + * any one-time events (events that are recorded only once per + * occurrence) will be lost. To preserve them an internal event field + * in W32Fd is used. + */ + SetLastError (0); + bret = WSAEventSelect ((SOCKET) wfd->fd, wfd->overlapped[SOCKET_POLL_EVENT].hEvent, FD_ALL_EVENTS); + error_code = GetLastError (); + if (bret != 0 || error_code != NO_ERROR) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "WSAEventSelect() returned %d. GLE: %d\n", bret, error_code); + goto error; + } + } + } + if (connected) + { + if (writeable && (access & GENERIC_WRITE)) + { + wfd->wsaevents.lNetworkEvents |= FD_WRITE; + SetEvent (wfd->overlapped[SOCKET_WAKEUP_EVENT].hEvent); + } + if (readable && (access & GENERIC_READ)) + { + wfd->wsaevents.lNetworkEvents |= FD_READ; + SetEvent (wfd->overlapped[SOCKET_WAKEUP_EVENT].hEvent); + } + } + wfd->access = access; + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Wrong value returned from GNUNET_W32IO_find_handle_type()\n"); + goto error; + } + goto end; +error: + for (i = 0; i < POLLLAST; i++) + if (wfd->overlapped[i].hEvent != INVALID_HANDLE_VALUE) + CloseHandle (wfd->overlapped[i].hEvent); + if (wfd->anon_thread != INVALID_HANDLE_VALUE) + CloseHandle (wfd->anon_thread); + if (wfd->anon_thread_mutex != INVALID_HANDLE_VALUE) + CloseHandle (wfd->anon_thread_mutex); + GNUNET_free (wfd); + wfd = NULL; +end: + return wfd; +} + +int +GNUNET_W32IO_read_async_named_pipe (struct W32Fd *fd, char *in, size_t to_read) +{ + BOOL bret; + DWORD dwret; + int ret = to_read; + while (to_read > 0) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ReadFile (%x, in, %d, &dwret, &fd->overlapped[POLLIN_BIT]);\n", fd->fd, to_read); + ZeroOverlappedExceptEvent(fd->overlapped[POLLIN_BIT]); + bret = ReadFile (fd->fd, in, to_read, &dwret, + &fd->overlapped[POLLIN_BIT]); + if (bret == 0) + { + dwret = GetLastError (); + if (dwret == ERROR_BROKEN_PIPE) { + errno = EPIPE; + SetEvent (fd->overlapped[POLLHUP_BIT].hEvent); + return -1; + } + + /* TODO: figure out when it is appropriate to enable [POLLERR_BIT].hEvent */ + if (dwret != ERROR_IO_PENDING) + goto error_and_fail; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "WaitForSingleObject (%x, INFINITE);\n", fd->overlapped[POLLIN_BIT].hEvent); + dwret = WaitForSingleObject (fd->overlapped[POLLIN_BIT].hEvent, + INFINITE); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "done\n"); + if (dwret != WAIT_OBJECT_0) + { + dwret = GetLastError (); + goto error_and_fail; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GetOverlappedResult (%x, &fd->overlapped[POLLIN_BIT], &dwret, TRUE);\n", fd->fd); + bret = GetOverlappedResult ((HANDLE) fd->fd, + &fd->overlapped[POLLIN_BIT], &dwret, TRUE); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got it\n"); + if (bret == 0) + { + dwret = GetLastError (); + goto error_and_fail; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %d bytes\n", dwret); + to_read -= dwret; + in += dwret; + } +#if 0 + /* If can't peek or pipe is empty - mark the pipe as unreadable. + * This will only affect the application if it actually checks fd for + * readability. It won't prevent subsequent calls to _read() from blocking. + */ + if (!PeekNamedPipe ((HANDLE) fd->fd, NULL, 0, 0, &dwret, NULL) || !dwret) { + fd->revents &= ~FD_READ; + fd->wsaevents.lNetworkEvents &= ~FD_READ; + } +#endif + return ret; + +error_and_fail: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to read\n"); + SetErrnoFromWinError (dwret); + return -1; +} + +int +GNUNET_W32IO_write_async_named_pipe (struct W32Fd *fd, char *out, size_t to_write) +{ + BOOL bret; + DWORD dwret; + DWORD ret; + + /* writebuffer isn't NULL -> there's a writing operation going on */ + if (fd->writebuffer != NULL) { + /* Wait until event is set (meaning that operation is complete). + * This might block for a while (until the other end reads), that is - + * you can't have more than one writing operation working at one pipe. + * This is somewhat mitigated by the pipe buffer (which is better be > 0). + * TODO: implement multiple buffering, so that it is possible to write + * more than once (especially if we're talking about writing small + * amounts of data multiple times). + */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "WaitForSingleObject (%x, INFINITE);\n", fd->overlapped[POLLOUT_BIT].hEvent); + if (WaitForSingleObject (fd->overlapped[POLLOUT_BIT].hEvent, INFINITE) == + WAIT_OBJECT_0) + { + /* Get the operation result, free the buffer */ + DWORD wait_ret; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GetOverlappedResult (%x, %x, %x, TRUE);\n", fd->fd, &fd->overlapped[POLLOUT_BIT], &wait_ret); + ret = GetOverlappedResult ((HANDLE) fd->fd, + &fd->overlapped[POLLOUT_BIT], &wait_ret, TRUE); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got it\n"); + if (fd->writebuffer) { + GNUNET_free (fd->writebuffer); + fd->writebuffer = NULL; + } + + if (ret == 0) { + errno = EIO; + return -1; + } + } + } + + if (out) + fd->writebuffer = (char *) GNUNET_memdup (out, to_write); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "WriteFile (%x, %x, %d, NULL, %x);\n", fd->fd, fd->writebuffer, to_write, &fd->overlapped[POLLOUT_BIT]); + ZeroOverlappedExceptEvent(fd->overlapped[POLLOUT_BIT]); + bret = WriteFile ((HANDLE) fd->fd, fd->writebuffer, to_write, NULL, + &fd->overlapped[POLLOUT_BIT]); + if (bret == 0) { + dwret = GetLastError (); + if (dwret != ERROR_IO_PENDING) + goto error_and_fail; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Wrote into heap buffer\n"); + } else { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Wrote into kernel buffer\n"); + GNUNET_free (fd->writebuffer); + fd->writebuffer = NULL; + } + return to_write; + +error_and_fail: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write\n"); + GNUNET_free (fd->writebuffer); + fd->writebuffer = NULL; + SetErrnoFromWinError (dwret); + return -1; +} + +int +GNUNET_W32IO_read (struct W32Fd *ifd, char *in, size_t to_read) +{ + switch (ifd->fd_type) + { + case GNUNET_W32IO_NAMED_PIPE: + /* Do not check whether the pipe is readable or not. If it isn't, it will + * block anyway. + */ + return GNUNET_W32IO_read_async_named_pipe (ifd, in, to_read); + break; + case GNUNET_W32IO_FILE: + return GNUNET_W32IO_read_file (ifd, in, to_read); + break; + case GNUNET_W32IO_SOCKET: + return GNUNET_W32IO_recv (ifd, in, to_read, 0); + case GNUNET_W32IO_ANONYMOUS_PIPE: + return GNUNET_W32IO_anonymous_pipe_read (ifd, in, to_read); + default: + return 0; + /* FIXME: handle other descriptor types */ + } +} + +int +GNUNET_W32IO_write (struct W32Fd *ifd, char *out, size_t to_write) +{ + int ret; + switch (ifd->fd_type) + { + case GNUNET_W32IO_NAMED_PIPE: + return GNUNET_W32IO_write_async_named_pipe (ifd, out, to_write); + break; + case GNUNET_W32IO_FILE: + return GNUNET_W32IO_write_file (ifd, out, to_write); + break; + case GNUNET_W32IO_SOCKET: + return GNUNET_W32IO_send (ifd, out, to_write, 0); + case GNUNET_W32IO_ANONYMOUS_PIPE: + ret = GNUNET_W32IO_anonymous_pipe_write (ifd, out, to_write); + return ret; + default: + return 0; + /* FIXME: handle other descriptor types */ + } +} + +int +GNUNET_W32IO_recv (struct W32Fd *fd, char *buffer, size_t length, int flags) +{ + int ret; + switch (fd->fd_type) + { + case GNUNET_W32IO_SOCKET: + ret = recv ((SOCKET) fd->fd, (char *) buffer, length, flags); + if (ret == SOCKET_ERROR) + { + DWORD error_code = GetLastError (); + if (error_code == WSAEWOULDBLOCK) + fd->wsaevents.lNetworkEvents &= ~FD_READ; + SetErrnoFromWinError (error_code); + } + else + { + fd->wsaevents.lNetworkEvents &= ~FD_READ; + fd->wsaevents.iErrorCode[FD_READ] = 0; + } + return ret; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Can't recv() - fd is not a socket\n"); + return 0; + } +} + +int +GNUNET_W32IO_recvfrom (struct W32Fd *fd, char *buffer, size_t length, int flags, struct sockaddr *address, int *address_len) +{ + int ret; + switch (fd->fd_type) + { + case GNUNET_W32IO_SOCKET: + ret = recvfrom ((SOCKET) fd->fd, (char *) buffer, length, flags, address, address_len); + if (ret == SOCKET_ERROR) + { + DWORD error_code = GetLastError (); + if (error_code == WSAEWOULDBLOCK) + fd->wsaevents.lNetworkEvents &= ~FD_READ; + SetErrnoFromWinError (error_code); + } + else + { + fd->wsaevents.iErrorCode[FD_READ] = 0; + } + return ret; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Can't recvfrom() - fd is not a socket\n"); + return 0; + } +} + +int +GNUNET_W32IO_send (struct W32Fd *fd, char *buffer, size_t length, int flags) +{ + int ret; + switch (fd->fd_type) + { + case GNUNET_W32IO_SOCKET: + ret = send ((SOCKET) fd->fd, (char *) buffer, length, flags); + if (ret == SOCKET_ERROR) + { + DWORD error_code = GetLastError (); + if (error_code == WSAEWOULDBLOCK) + fd->wsaevents.lNetworkEvents &= ~FD_WRITE; + SetErrnoFromWinError (error_code); + } + else + { + fd->wsaevents.iErrorCode[FD_WRITE] = 0; + } + return ret; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Can't send() - fd is not a socket\n"); + return 0; + } +} + +int +GNUNET_W32IO_sendto (struct W32Fd *fd, char *buffer, size_t length, int flags, struct sockaddr *address, int address_len) +{ + int ret; + switch (fd->fd_type) + { + case GNUNET_W32IO_SOCKET: + ret = sendto ((SOCKET) fd->fd, (char *) buffer, length, flags, address, address_len); + if (ret == SOCKET_ERROR) + { + DWORD error_code = GetLastError (); + if (error_code == WSAEWOULDBLOCK) + fd->wsaevents.lNetworkEvents &= ~FD_WRITE; + SetErrnoFromWinError (error_code); + } + else + { + fd->wsaevents.iErrorCode[FD_WRITE] = 0; + } + return ret; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Can't sendto() - fd is not a socket\n"); + return 0; + } +} + +int +GNUNET_W32IO_connect (struct W32Fd *fd, const struct sockaddr *name, + int namelen) +{ + int ret; + switch (fd->fd_type) + { + case GNUNET_W32IO_SOCKET: + ret = WSAConnect ((SOCKET) fd->fd, name, namelen, NULL, NULL, NULL, NULL); + if (ret == SOCKET_ERROR) + { + SetErrnoFromWinError (GetLastError()); + } + else + { + fd->wsaevents.lNetworkEvents &= ~FD_CONNECT; + fd->wsaevents.iErrorCode[FD_CONNECT] = 0; + } + return ret; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Can't connect() - fd is not a socket\n"); + return 0; + } +} + +SOCKET +GNUNET_W32IO_accept (struct W32Fd *fd, struct sockaddr *name, int *namelen) +{ + SOCKET ret; + switch (fd->fd_type) + { + case GNUNET_W32IO_SOCKET: + ret = WSAAccept ((SOCKET) fd->fd, name, namelen, NULL, 0); + if (ret == SOCKET_ERROR) + { + SetErrnoFromWinError (GetLastError()); + } + else + { + fd->wsaevents.lNetworkEvents &= ~FD_ACCEPT; + fd->wsaevents.iErrorCode[FD_ACCEPT] = 0; + } + return ret; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Can't accept() - fd is not a socket\n"); + return 0; + } +} + +int +GNUNET_W32IO_listen (struct W32Fd *fd, int backlog) +{ + int ret; + switch (fd->fd_type) + { + case GNUNET_W32IO_SOCKET: + ret = listen ((SOCKET) fd->fd, backlog); + if (ret == SOCKET_ERROR) + { + SetErrnoFromWinError (GetLastError()); + } + else + { + fd->wsaevents.lNetworkEvents &= ~(FD_READ | FD_WRITE | FD_ACCEPT | FD_CONNECT); + fd->wsaevents.iErrorCode[FD_READ] = 0; + fd->wsaevents.iErrorCode[FD_WRITE] = 0; + fd->wsaevents.iErrorCode[FD_ACCEPT] = 0; + fd->wsaevents.iErrorCode[FD_CONNECT] = 0; + } + return ret; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Can't accept() - fd is not a socket\n"); + return 0; + } +} + +/** + * Anonymous pipes on NT do not support asynchronous operations, which is + * why we need a separate thread to perform writing and reading operations, + * and the state of this thread can be easily checked by waiting upon + * an event object. + */ +void * +GNUNET_W32IO_anonymous_pipe_thread_func (void *data) +{ + struct W32Fd *wfd = (struct W32Fd *) data; + HANDLE h[2]; + h[0] = wfd->overlapped[ANON_PIPE_THREAD_POLL_EVENT].hEvent; + h[1] = wfd->anon_thread_mutex; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "locking\n"); + WaitForSingleObject (wfd->anon_thread_mutex, INFINITE); + while (wfd->alive) + { + do + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "waiting\n"); + ReleaseMutex (h[1]); + ResetEvent (h[0]); + WaitForMultipleObjects(2, h, TRUE, INFINITE); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "done waiting\n"); + } while (wfd->work < 0); + switch (wfd->work) + { + case 0: /* read */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "anonymous read %x %d\n", wfd->fd, wfd->to_read); + wfd->bret = ReadFile (wfd->fd, wfd->readbuffer, wfd->to_read, &wfd->bytes_read, NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "done, read %d\n", wfd->bytes_read); + if (wfd->bret == 0) + { + wfd->dwerror = GetLastError (); + } + break; + case 1: /* write */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "anonymous write %x %d\n", wfd->fd, wfd->to_write); + wfd->bret = WriteFile (wfd->fd, wfd->writebuffer, wfd->to_write, &wfd->bytes_written, NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "done, write %d\n", wfd->bytes_written); + if (wfd->bret == 0) + { + wfd->dwerror = GetLastError (); + } + break; + case 2: /* close */ + wfd->alive = FALSE; + break; + default: + continue; + } + wfd->work = -1; + + SetEvent (wfd->overlapped[ANON_PIPE_MAIN_THREAD_POLL_EVENT].hEvent); + } + + return NULL; +} + +int +GNUNET_W32IO_anonymous_pipe_read (struct W32Fd *fd, char *in, size_t to_read) +{ + BOOL bret; + DWORD dwret; + int ret = to_read; + HANDLE h[2]; + h[0] = fd->overlapped[ANON_PIPE_MAIN_THREAD_POLL_EVENT].hEvent; + h[1] = fd->anon_thread_mutex; + while (to_read > 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "locking\n"); + WaitForSingleObject (fd->anon_thread_mutex, INFINITE); + /* We've locked the mutex - which means that worker thread is not + * working at the moment. + */ + + fd->work = 0; /* read */ + fd->readbuffer = in; + fd->to_read = to_read; + + SetEvent (fd->overlapped[ANON_PIPE_THREAD_POLL_EVENT].hEvent); + /* We've signaled the worker thread event. Once we unlock the mutex, + * the worker thread will wake up. + */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "waiting\n"); + ResetEvent (h[0]); + ReleaseMutex (h[1]); + WaitForMultipleObjects(2, h, TRUE, INFINITE); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "done waiting\n"); + /* We've unlocked the mutex. The worker thread now wakes up and sees that + * work is >= 0 and begins to read. + */ + /* Once the read operation is complete, it fills wfd->* with data and + * enables the main event, then waits on thread event (while unlocking threadmutex), + * which is the moment when wait-for-multiple-objects takes the opportunity and locks + * threadmutex for us here and finally wakes up. + */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "unlocking\n"); + ReleaseMutex (h[1]); + + bret = fd->bret; + dwret = fd->bytes_read; + if (bret == 0) + { + dwret = fd->dwerror; + if (dwret == ERROR_BROKEN_PIPE) { + errno = EPIPE; + return -1; + } + goto error_and_fail; + } + to_read -= dwret; + in += dwret; + } +#if 0 + /* Mark the pipe as unreadable, because we won't know if it has any data to + * read until we wait on it. Of course, that will not prevent the application + * from trying to read it, but reading might block. + */ + fd->wsaevents.lNetworkEvents &= ~FD_READ; + fd->revents &= ~FD_READ; +#endif + /* SetEvent (fd->overlapped[FD_READ_BIT].hEvent); */ + return ret; + +error_and_fail: + SetErrnoFromWinError (dwret); + return -1; +} + +int +GNUNET_W32IO_anonymous_pipe_write (struct W32Fd *fd, char *out, size_t to_write) +{ + BOOL bret; + DWORD dwret; + int ret = to_write; + HANDLE h[2]; + h[0] = fd->overlapped[ANON_PIPE_MAIN_THREAD_POLL_EVENT].hEvent; + h[1] = fd->anon_thread_mutex; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "locking\n"); + WaitForSingleObject (fd->anon_thread_mutex, INFINITE); + /* We've locked the mutex - which means that worker thread is not + * working at the moment. If the thread was writing at the moment of + * locking, g_mutex_lock() will block until writing is complete, then + * the worker thread will unlock for a short time, letting us get + * the lock. + */ + if (fd->writebuffer) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "freeing the buffer\n"); + /* We've been writing somethings */ + GNUNET_free (fd->writebuffer); + fd->writebuffer = NULL; + + /* Process any error conditions from previous write operation */ + bret = fd->bret; + dwret = fd->bytes_written; + if (bret == 0) + { + dwret = fd->dwerror; + if (dwret == ERROR_BROKEN_PIPE) { + errno = EPIPE; + return -1; + } + goto error_and_fail; + } + } + + fd->work = 1; /* write */ + fd->writebuffer = GNUNET_memdup (out, to_write); + fd->to_write = to_write; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "signalling\n"); + SetEvent (fd->overlapped[ANON_PIPE_THREAD_POLL_EVENT].hEvent); + /* We've signaled the worker thread condition. Once we unlock the mutex, + * the worker thread will wake up. + */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "unlocking\n"); + ReleaseMutex (fd->anon_thread_mutex); + /* Unlock the mutex and let the worker thread do its thing. Return + * to_write, while actual writing takes place in background. + * If another write is attempted while thread is busy, it will block. + * And any other operation will block too, of course. + */ + + /* We will get errorneous condition eventually, if we do another + * (blocking) write or wait, but not immediately. + * That happens because of the assumption we make about I/O things: + * It is always writable no matter what, but it is only readable when + * there is data to read. Real I/O things may not be like that - + * they may return an error while writing takes place. + * So you should assume that this function will return an error only + * when errorneous condition is obvious (wrong fd or a pipe broken + * during previous operation). + */ + +#if 0 + /* Mark the pipe as unwritable. Of course, that will not prevent the + * application from trying to write into it, but writing might block. + */ + + fd->wsaevents.lNetworkEvents &= ~FD_WRITE; + fd->revents &= ~FD_WRITE; +#endif + /* SetEvent (fd->overlapped[FD_WRITE_BIT].hEvent); */ + return ret; + +error_and_fail: + SetErrnoFromWinError (dwret); + return -1; +} + +int +GNUNET_W32IO_anonymous_pipe_close (struct W32Fd *fd) +{ + int ret = 0; + HANDLE h[2]; + h[0] = fd->overlapped[ANON_PIPE_MAIN_THREAD_POLL_EVENT].hEvent; + h[1] = fd->anon_thread_mutex; + WaitForSingleObject (fd->anon_thread_mutex, INFINITE); + /* We've locked the mutex - which means that worker thread is not + * working at the moment. + */ + /* Do not check for errors - we can't report them here (any errors + * reported should be related to close(), not to some previous + * operation). + */ + + if (fd->writebuffer) + { + GNUNET_free (fd->writebuffer); + fd->writebuffer = NULL; + } + + fd->work = 2; /* close */ + + SetEvent (fd->overlapped[ANON_PIPE_THREAD_POLL_EVENT].hEvent); + /* We've signaled the worker thread condition. Once we unlock the mutex, + * the worker thread will wake up. + */ + ReleaseMutex (fd->anon_thread_mutex); + /* We've unlocked the mutex. The worker thread now wakes up and sees that + * work is 2, signals maincond, then sets ->alive to FALSE and terminates. + */ + WaitForSingleObject (fd->anon_thread, INFINITE); + + return ret; +} + +off_t +GNUNET_W32IO_lseek_handle (HANDLE fd, uint64_t new_offset, int origin) +{ + BOOL bret; + DWORD dwError; + LARGE_INTEGER offset; + LARGE_INTEGER ret; + LARGE_INTEGER current; + off_t result; + + if (origin != SEEK_SET && origin != SEEK_CUR && origin != SEEK_END) + { + errno = EINVAL; + return -1; + } + + offset.QuadPart = 0; + SetLastError (0); + bret = SetFilePointerEx (fd, offset, ¤t, FILE_CURRENT); + + dwError = GetLastError (); + + if (bret == 0 && dwError != NO_ERROR) + { + SetErrnoFromWinError (dwError); + return -1; + } + + offset.QuadPart = new_offset; + SetLastError (0); + bret = SetFilePointerEx (fd, offset, &ret, + (origin == SEEK_SET ? FILE_BEGIN : + (origin == SEEK_CUR ? FILE_CURRENT : + (origin == SEEK_END ? FILE_END : origin)))); + + dwError = GetLastError (); + + if (bret == 0 && dwError != NO_ERROR) + { + SetFilePointerEx (fd, current, NULL, FILE_BEGIN); + SetErrnoFromWinError (dwError); + return -1; + } + + /* FIXME: make sure this check works correctly with 32-bit values */ + result = ret.QuadPart; + offset.QuadPart = result; + if (offset.QuadPart != ret.QuadPart) + { + SetFilePointerEx (fd, current, NULL, FILE_BEGIN); + errno = EOVERFLOW; + return -1; + } + + return ret.QuadPart; +} + +off_t +GNUNET_W32IO_lseek (struct W32Fd *fd, uint64_t new_offset, int origin) +{ + if (fd->fd_type != GNUNET_W32IO_FILE) + { + errno = ESPIPE; + return -1; + } + + return GNUNET_W32IO_lseek_handle (fd->fd, new_offset, origin); +} + +int +GNUNET_W32IO_read_file (struct W32Fd *fd, char *in, size_t to_read) +{ + BOOL bret; + DWORD dwret; + int ret = to_read; + while (to_read > 0) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ReadFile (%x, in, %d, &dwret, &fd->overlapped[POLLIN_BIT]);\n", fd->fd, to_read); + ZeroOverlappedExceptEvent(fd->overlapped[POLLIN_BIT]); + bret = ReadFile (fd->fd, in, to_read, &dwret, + &fd->overlapped[POLLIN_BIT]); + if (bret == 0) + { + dwret = GetLastError (); + if (dwret == ERROR_HANDLE_EOF) { + /* FIXME: make sure this is correct */ + return ret; + } + + if (dwret != ERROR_IO_PENDING) + goto error_and_fail; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "WaitForSingleObject (%x, INFINITE);\n", fd->overlapped[POLLIN_BIT].hEvent); + dwret = WaitForSingleObject (fd->overlapped[POLLIN_BIT].hEvent, + INFINITE); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "done\n"); + if (dwret != WAIT_OBJECT_0) + { + dwret = GetLastError (); + goto error_and_fail; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GetOverlappedResult (%x, &fd->overlapped[FD_READ_BIT], &dwret, TRUE);\n", fd->fd); + bret = GetOverlappedResult ((HANDLE) fd->fd, + &fd->overlapped[POLLIN_BIT], &dwret, TRUE); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got it\n"); + if (bret == 0) + { + dwret = GetLastError (); + if (dwret == ERROR_HANDLE_EOF) { + /* FIXME: make sure this is correct */ + return ret; + } + goto error_and_fail; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %d bytes\n", dwret); + to_read -= dwret; + in += dwret; + } + return ret; + +error_and_fail: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to read\n"); + SetErrnoFromWinError (dwret); + return -1; +} + +int +GNUNET_W32IO_write_file (struct W32Fd *fd, char *out, size_t to_write) +{ + BOOL bret; + DWORD dwret; + DWORD ret; + + /* writebuffer isn't NULL -> there's a writing operation going on */ + if (fd->writebuffer != NULL) { + /* Wait until event is set (meaning that operation is complete). + * This might block for a while (until the other end reads), that is - + * you can't have more than one writing operation working at one pipe. + * TODO: implement multiple buffering, so that it is possible to write + * more than once (especially if we're talking about writing small + * amounts of data multiple times). + */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "WaitForSingleObject (%x, INFINITE);\n", fd->overlapped[POLLOUT_BIT].hEvent); + if (WaitForSingleObject (fd->overlapped[POLLOUT_BIT].hEvent, INFINITE) == + WAIT_OBJECT_0) + { + /* Get the operation result, free the buffer */ + DWORD wait_ret; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "GetOverlappedResult (%x, %x, %x, TRUE);\n", fd->fd, &fd->overlapped[POLLOUT_BIT], &wait_ret); + ret = GetOverlappedResult ((HANDLE) fd->fd, + &fd->overlapped[POLLOUT_BIT], &wait_ret, TRUE); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got it\n"); + if (fd->writebuffer) { + GNUNET_free (fd->writebuffer); + fd->writebuffer = NULL; + } + + if (ret == 0) { + errno = EIO; + return -1; + } + } + } + + if (out) + fd->writebuffer = (char *) GNUNET_memdup (out, to_write); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "WriteFile (%x, %x, %d, NULL, %x);\n", fd->fd, fd->writebuffer, to_write, &fd->overlapped[POLLOUT_BIT]); + ZeroOverlappedExceptEvent(fd->overlapped[POLLOUT_BIT]); + bret = WriteFile ((HANDLE) fd->fd, fd->writebuffer, to_write, NULL, + &fd->overlapped[POLLOUT_BIT]); + if (bret == 0) { + dwret = GetLastError (); + /* FIXME: handle ERROR_INVALID_USER_BUFFER or ERROR_NOT_ENOUGH_MEMORY as EAGAIN */ + if (dwret != ERROR_IO_PENDING) + goto error_and_fail; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Wrote into heap buffer\n"); + } else { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Wrote into kernel buffer\n"); + GNUNET_free (fd->writebuffer); + fd->writebuffer = NULL; + } + return to_write; + +error_and_fail: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write\n"); + GNUNET_free (fd->writebuffer); + fd->writebuffer = NULL; + SetErrnoFromWinError (dwret); + return -1; +} + +#endif /* WINDOWS */ \ No newline at end of file diff --git a/src/util/w32io.h b/src/util/w32io.h new file mode 100644 index 0000000..c66f2e1 --- /dev/null +++ b/src/util/w32io.h @@ -0,0 +1,268 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2005, 2006, 2009, 2010 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + GNUnet 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 GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef __GNUNET_W32IO_H__ +#define __GNUNET_W32IO_H__ +#include "platform.h" +#include "gnunet_common.h" + +#if WINDOWS + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#include + +/** + * GNUNET_W32IO_UNSPECIFIED - not specified by user. Should be + * autodetected when necessary + * GNUNET_W32IO_UNKNOWN - autodetection failed, type unknown + * GNUNET_W32IO_FILE - returned by CreateFile() + * GNUNET_W32IO_NAMED_PIPE - returned by CreateNamedPipe() (server) + * and CreateFile() (client) + * GNUNET_W32IO_ANONYMOUS_PIPE - returned by CreatePipe() + * GNUNET_W32IO_CONSOLE - returned by CreateFile() + * GNUNET_W32IO_SOCKET - returned by socket() + * GNUNET_W32IO_LAST - do not use + */ +typedef enum +{ + GNUNET_W32IO_UNSPECIFIED = 0, + GNUNET_W32IO_UNKNOWN, + GNUNET_W32IO_FILE, + GNUNET_W32IO_NAMED_PIPE, + GNUNET_W32IO_ANONYMOUS_PIPE, + GNUNET_W32IO_CONSOLE, + GNUNET_W32IO_SOCKET, + GNUNET_W32IO_LAST +} GNUNET_W32IO_HandleType; + +typedef enum +{ + POLLIN = FD_READ | FD_ACCEPT, + POLLRDNORM = FD_READ | FD_ACCEPT, + POLLRDBAND = FD_OOB, + POLLOUT = FD_WRITE | FD_CONNECT, + POLLWRNORM = FD_WRITE | FD_CONNECT, + POLLHUP = FD_CLOSE, + POLLPRI = 1 << FD_MAX_EVENTS, + POLLWRBAND = 1 << FD_MAX_EVENTS << 1, + POLLERR = 1 << FD_MAX_EVENTS << 2, + POLLNVAL = 1 << FD_MAX_EVENTS << 3, + POLLLAST = FD_MAX_EVENTS + 4 +} GNUNET_W32IO_PollEvents; + +#define POLLIN_BIT FD_READ_BIT +#define POLLOUT_BIT FD_WRITE_BIT +#define POLLHUP_BIT FD_CLOSE_BIT +#define POLLERR_BIT FD_MAX_EVENTS + 2 +#define POLLNVAL_BIT FD_MAX_EVENTS + 3 + +struct timespec { + long tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; + +/** + * + */ +struct W32Fd +{ + /** + * Untyped I/O descriptor (type depends on fd_type) + */ + HANDLE fd; + + /** + * Events that have occurred on a socket + * (as returned by WSAEnumNetworkEvents) + * Used to preserve some one-time events that + * are not re-posted by Winsock. + */ + WSANETWORKEVENTS wsaevents; + + /** + * An array of structures for file/pipe async I/O. + * Its 0th member is used by sockets. + * Its 0th and 1st members are used by @anon_thread as, respectedly, + * the @anon_thread event (tells the @anon_thread to wake up and do some + * work) and the main thread event (tells the main thread to wake up, since + * the @anon_thead have finished its work). + */ + OVERLAPPED overlapped[POLLLAST]; + + /** + * fd_type - type of the descriptor + */ + GNUNET_W32IO_HandleType fd_type; + + /** + * The access we have to the handle + */ + DWORD access; + + /** + * The events in which the caller is interested in. + * May be modified internally by ppoll() + */ + int16_t events; + + /** + * The result of a ppoll() call - contains the events + * that have occurred on this W32Fd. + */ + int16_t revents; + + /*_______Anonymous pipe I/O implementation details_______*/ + + /** + * A thread that performs operations on anonymous pipe + */ + HANDLE anon_thread; + + /** + * A mutex that locks this W32Fd to prevent the main thread and + * the @anon_thread from accessing it at the same time. + */ + HANDLE anon_thread_mutex; + + /** + * Number of bytes to read + */ + DWORD to_read; + + /** + * Number of bytes to write + */ + DWORD to_write; + + /** + * Number of bytes read + */ + DWORD bytes_read; + + /** + * Number of bytes written + */ + DWORD bytes_written; + + /** + * A pointer to a block of memory that will receive data from a reading + * operation. + */ + char *readbuffer; + + /** + * A pointer to a block of memory that will provide data for a writing + * operation. + */ + char *writebuffer; + + /** + * A value returned by ReadFile()/WriteFile() within @anon_thread + */ + BOOL bret; + + /** + * A value returned by GetLastError() within @anon_thread + */ + DWORD dwerror; + + /** + * A type of work to be done. + * -1 - none + * 0 - read + * 1 - write + */ + int work; + + /** + * A boolean condition that can be set to FALSE to indicate that + * the @anon_thread must (when it wakes up) return. + */ + BOOL alive; +}; + +/** + * Sometimes pollfd is used as a struct, not as a pointer, which is why + * we need to wrap W32Fd - to make sure it always stays a pointer + */ +struct W32PollFd +{ + struct W32Fd *fd; +}; + +int GNUNET_W32IO_error_to_errno (DWORD last_error); + +HANDLE GNUNET_W32IO_fd_get_handle (struct W32Fd *fd); + +void GNUNET_W32IO_generate_random_local_pipe_name (char *pipename); + +int GNUNET_W32IO_pipe (struct W32Fd *fd[2], int server_inheritable, int client_inheritable, DWORD server_mode, DWORD client_mode); + +int GNUNET_W32IO_anonymous_pipe (struct W32Fd *fd[2], int read_inheritable, int write_inheritable); + +int GNUNET_W32IO_fsync (struct W32Fd *wfd); + +int GNUNET_W32IO_close (struct W32Fd *wfd, int closehandle); + +GNUNET_W32IO_HandleType GNUNET_W32IO_find_handle_type (HANDLE h); + +struct W32Fd *GNUNET_W32IO_fd_from_handle (HANDLE h, GNUNET_W32IO_HandleType htype, DWORD handle_access, int connected, + int writeable, int readable); + +int GNUNET_W32IO_read_async_named_pipe (struct W32Fd *fd, char *in, size_t to_read); + +int GNUNET_W32IO_write_async_named_pipe (struct W32Fd *fd, char *out, size_t to_write); + +int GNUNET_W32IO_read (struct W32Fd *ifd, char *in, size_t to_read); + +int GNUNET_W32IO_write (struct W32Fd *ifd, char *out, size_t to_write); + +int GNUNET_W32IO_ppoll (struct W32PollFd *fds, uint64_t nfds, const struct timespec *t, const sigset_t *sigmask); + +int GNUNET_W32IO_prepare_for_poll (struct W32PollFd *wfd, HANDLE *events); + +int GNUNET_W32IO_recv (struct W32Fd *fd, char *buffer, size_t length, int flags); +int GNUNET_W32IO_recvfrom (struct W32Fd *fd, char *buffer, size_t length, int flags, struct sockaddr *address, int *address_len); +int GNUNET_W32IO_send (struct W32Fd *fd, char *buffer, size_t length, int flags); +int GNUNET_W32IO_sendto (struct W32Fd *fd, char *buffer, size_t length, int flags, struct sockaddr *address, int address_len); + +int GNUNET_W32IO_connect (struct W32Fd *fd, const struct sockaddr *name, int namelen); +SOCKET GNUNET_W32IO_accept (struct W32Fd *fd, struct sockaddr *name, int *namelen); +int GNUNET_W32IO_listen (struct W32Fd *fd, int backlog); + +int GNUNET_W32IO_anonymous_pipe_read (struct W32Fd *fd, char *in, size_t to_read); +int GNUNET_W32IO_anonymous_pipe_write (struct W32Fd *fd, char *out, size_t to_write); +int GNUNET_W32IO_anonymous_pipe_close (struct W32Fd *fd); + +off_t GNUNET_W32IO_lseek_handle (HANDLE fd, uint64_t new_offset, int origin); +off_t GNUNET_W32IO_lseek (struct W32Fd *fd, uint64_t new_offset, int origin); + +int GNUNET_W32IO_read_file (struct W32Fd *fd, char *in, size_t to_read); +int GNUNET_W32IO_write_file (struct W32Fd *fd, char *out, size_t to_write); + +#endif /* WINDOWS */ +#endif /* __GNUNET_W32IO_H__ */ \ No newline at end of file -- 1.7.3.1.msysgit.0