| /* Emulation for select(2) |
| Contributed by Paolo Bonzini. |
| |
| Copyright 2008-2022 Free Software Foundation, Inc. |
| |
| This file is part of gnulib. |
| |
| This file is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Lesser General Public License as |
| published by the Free Software Foundation; either version 2.1 of the |
| License, or (at your option) any later version. |
| |
| This file 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 Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public License |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
| |
| #include <config.h> |
| |
| /* Specification. */ |
| #include <sys/select.h> |
| |
| #if defined _WIN32 && ! defined __CYGWIN__ |
| /* Native Windows. */ |
| |
| #include <alloca.h> |
| #include <assert.h> |
| #include <sys/types.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> |
| |
| /* Get the overridden 'struct timeval'. */ |
| #include <sys/time.h> |
| |
| #if GNULIB_MSVC_NOTHROW |
| # include "msvc-nothrow.h" |
| #else |
| # include <io.h> |
| #endif |
| |
| #undef select |
| |
| /* Don't assume that UNICODE is not defined. */ |
| #undef GetModuleHandle |
| #define GetModuleHandle GetModuleHandleA |
| #undef PeekConsoleInput |
| #define PeekConsoleInput PeekConsoleInputA |
| #undef CreateEvent |
| #define CreateEvent CreateEventA |
| #undef PeekMessage |
| #define PeekMessage PeekMessageA |
| #undef DispatchMessage |
| #define DispatchMessage DispatchMessageA |
| |
| /* Avoid warnings from gcc -Wcast-function-type. */ |
| #define GetProcAddress \ |
| (void *) GetProcAddress |
| |
| 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 |
| |
| static BOOL IsConsoleHandle (HANDLE h) |
| { |
| DWORD mode; |
| return GetConsoleMode (h, &mode) != 0; |
| } |
| |
| static BOOL |
| IsSocketHandle (HANDLE h) |
| { |
| WSANETWORKEVENTS ev; |
| |
| if (IsConsoleHandle (h)) |
| return FALSE; |
| |
| /* 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); |
| return ev.lNetworkEvents != 0xDEADBEEF; |
| } |
| |
| /* Compute output fd_sets for libc descriptor FD (whose Windows handle is |
| H). */ |
| |
| static int |
| windows_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_DISK: |
| read = TRUE; |
| write = TRUE; |
| break; |
| |
| 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 if (GetLastError () == ERROR_BROKEN_PIPE) |
| ; |
| |
| 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 Windows 9x, 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 Windows XP 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: |
| write = TRUE; |
| if (!(rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) |
| break; |
| |
| ret = WaitForSingleObject (h, 0); |
| if (ret == WAIT_OBJECT_0) |
| { |
| if (!IsConsoleHandle (h)) |
| { |
| read = TRUE; |
| break; |
| } |
| |
| nbuffer = avail = 0; |
| bRet = GetNumberOfConsoleInputEvents (h, &nbuffer); |
| |
| /* Screen buffers handles are filtered earlier. */ |
| assert (bRet); |
| if (nbuffer == 0) |
| { |
| except = TRUE; |
| break; |
| } |
| |
| irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD)); |
| bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail); |
| if (!bRet || avail == 0) |
| { |
| except = TRUE; |
| break; |
| } |
| |
| 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) |
| #undef timeval |
| { |
| 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, nbuffer; |
| MSG msg; |
| int i, fd, rc; |
| clock_t tend; |
| |
| if (nfds > FD_SETSIZE) |
| nfds = FD_SETSIZE; |
| |
| if (!timeout) |
| wait_timeout = INFINITE; |
| else |
| { |
| wait_timeout = timeout->tv_sec * 1000 + 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. At the same time, eliminate |
| bits in the "wrong" direction for console input buffers |
| and screen buffers, because screen buffers are waitable |
| and they will block until a character is available. */ |
| 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]; |
| h = (HANDLE) _get_osfhandle (fd); |
| if (IsConsoleHandle (h) |
| && !GetNumberOfConsoleInputEvents (h, &nbuffer)) |
| continue; |
| |
| 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]; |
| h = (HANDLE) _get_osfhandle (fd); |
| if (IsConsoleHandle (h) |
| && GetNumberOfConsoleInputEvents (h, &nbuffer)) |
| continue; |
| |
| 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++) |
| { |
| if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) |
| continue; |
| |
| h = (HANDLE) _get_osfhandle (i); |
| if (!h) |
| { |
| errno = EBADF; |
| return -1; |
| } |
| |
| if (IsSocketHandle (h)) |
| { |
| 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 |
| && windows_poll_handle (h, i, &rbits, &wbits, &xbits)) |
| wait_timeout = 0; |
| } |
| } |
| |
| /* Place a sentinel at the end of the array. */ |
| handle_array[nhandles] = NULL; |
| |
| /* When will the waiting period expire? */ |
| if (wait_timeout != INFINITE) |
| tend = clock () + wait_timeout; |
| |
| restart: |
| 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; |
| } |
| |
| /* How much is left to wait? */ |
| if (wait_timeout != INFINITE) |
| { |
| clock_t tnow = clock (); |
| if (tend >= tnow) |
| wait_timeout = tend - tnow; |
| 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); |
| |
| if (nhandles > 1) |
| { |
| /* Count results that are not counted in the return value of select. */ |
| 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]) |
| { |
| /* Not a socket. */ |
| nhandles++; |
| windows_poll_handle (h, i, &rbits, &wbits, &xbits); |
| if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))) |
| || wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))) |
| || xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) |
| rc++; |
| } |
| } |
| |
| if (rc == 0 |
| && (wait_timeout == INFINITE |
| /* If NHANDLES > 1, but no bits are set, it means we've |
| been told incorrectly that some handle was signaled. |
| This happens with anonymous pipes, which always cause |
| MsgWaitForMultipleObjects to exit immediately, but no |
| data is found ready to be read by windows_poll_handle. |
| To avoid a total failure (whereby we return zero and |
| don't wait at all), let's poll in a more busy loop. */ |
| || (wait_timeout != 0 && nhandles > 1))) |
| { |
| /* Sleep 1 millisecond to avoid busy wait and retry with the |
| original fd_sets. */ |
| memcpy (&handle_rfds, rfds, sizeof (fd_set)); |
| memcpy (&handle_wfds, wfds, sizeof (fd_set)); |
| memcpy (&handle_xfds, xfds, sizeof (fd_set)); |
| SleepEx (1, TRUE); |
| goto restart; |
| } |
| if (timeout && wait_timeout == 0 && rc == 0) |
| timeout->tv_sec = timeout->tv_usec = 0; |
| } |
| |
| /* Now fill in the results. */ |
| FD_ZERO (rfds); |
| FD_ZERO (wfds); |
| FD_ZERO (xfds); |
| 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. */ |
| SOCKET s = (SOCKET) h; |
| WSAEventSelect (s, NULL, 0); |
| if (FD_ISSET (s, &handle_rfds)) |
| FD_SET (i, rfds); |
| if (FD_ISSET (s, &handle_wfds)) |
| FD_SET (i, wfds); |
| if (FD_ISSET (s, &handle_xfds)) |
| FD_SET (i, xfds); |
| } |
| else |
| { |
| /* Not a socket. */ |
| nhandles++; |
| if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) |
| FD_SET (i, rfds); |
| if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) |
| FD_SET (i, wfds); |
| if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) |
| FD_SET (i, xfds); |
| } |
| } |
| |
| return rc; |
| } |
| |
| #else /* ! Native Windows. */ |
| |
| #include <stddef.h> /* NULL */ |
| #include <errno.h> |
| #include <unistd.h> |
| |
| #undef select |
| |
| int |
| rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, |
| struct timeval *timeout) |
| { |
| int i; |
| |
| /* FreeBSD 8.2 has a bug: it does not always detect invalid fds. */ |
| if (nfds < 0 || nfds > FD_SETSIZE) |
| { |
| errno = EINVAL; |
| return -1; |
| } |
| for (i = 0; i < nfds; i++) |
| { |
| if (((rfds && FD_ISSET (i, rfds)) |
| || (wfds && FD_ISSET (i, wfds)) |
| || (xfds && FD_ISSET (i, xfds))) |
| && dup2 (i, i) != i) |
| return -1; |
| } |
| |
| /* Interix 3.5 has a bug: it does not support nfds == 0. */ |
| if (nfds == 0) |
| { |
| nfds = 1; |
| rfds = NULL; |
| wfds = NULL; |
| xfds = NULL; |
| } |
| return select (nfds, rfds, wfds, xfds, timeout); |
| } |
| |
| #endif |