(root)/
findutils-4.9.0/
gnulib-tests/
select.c
       1  /* Emulation for select(2)
       2     Contributed by Paolo Bonzini.
       3  
       4     Copyright 2008-2022 Free Software Foundation, Inc.
       5  
       6     This file is part of gnulib.
       7  
       8     This file is free software: you can redistribute it and/or modify
       9     it under the terms of the GNU Lesser General Public License as
      10     published by the Free Software Foundation; either version 2.1 of the
      11     License, or (at your option) any later version.
      12  
      13     This file is distributed in the hope that it will be useful,
      14     but WITHOUT ANY WARRANTY; without even the implied warranty of
      15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16     GNU Lesser General Public License for more details.
      17  
      18     You should have received a copy of the GNU Lesser General Public License
      19     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      20  
      21  #include <config.h>
      22  
      23  /* Specification.  */
      24  #include <sys/select.h>
      25  
      26  #if defined _WIN32 && ! defined __CYGWIN__
      27  /* Native Windows.  */
      28  
      29  #include <alloca.h>
      30  #include <assert.h>
      31  #include <sys/types.h>
      32  #include <errno.h>
      33  #include <limits.h>
      34  
      35  #include <winsock2.h>
      36  #include <windows.h>
      37  #include <io.h>
      38  #include <stdio.h>
      39  #include <conio.h>
      40  #include <time.h>
      41  
      42  /* Get the overridden 'struct timeval'.  */
      43  #include <sys/time.h>
      44  
      45  #if GNULIB_MSVC_NOTHROW
      46  # include "msvc-nothrow.h"
      47  #else
      48  # include <io.h>
      49  #endif
      50  
      51  #undef select
      52  
      53  /* Don't assume that UNICODE is not defined.  */
      54  #undef GetModuleHandle
      55  #define GetModuleHandle GetModuleHandleA
      56  #undef PeekConsoleInput
      57  #define PeekConsoleInput PeekConsoleInputA
      58  #undef CreateEvent
      59  #define CreateEvent CreateEventA
      60  #undef PeekMessage
      61  #define PeekMessage PeekMessageA
      62  #undef DispatchMessage
      63  #define DispatchMessage DispatchMessageA
      64  
      65  /* Avoid warnings from gcc -Wcast-function-type.  */
      66  #define GetProcAddress \
      67    (void *) GetProcAddress
      68  
      69  struct bitset {
      70    unsigned char in[FD_SETSIZE / CHAR_BIT];
      71    unsigned char out[FD_SETSIZE / CHAR_BIT];
      72  };
      73  
      74  /* Declare data structures for ntdll functions.  */
      75  typedef struct _FILE_PIPE_LOCAL_INFORMATION {
      76    ULONG NamedPipeType;
      77    ULONG NamedPipeConfiguration;
      78    ULONG MaximumInstances;
      79    ULONG CurrentInstances;
      80    ULONG InboundQuota;
      81    ULONG ReadDataAvailable;
      82    ULONG OutboundQuota;
      83    ULONG WriteQuotaAvailable;
      84    ULONG NamedPipeState;
      85    ULONG NamedPipeEnd;
      86  } FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
      87  
      88  typedef struct _IO_STATUS_BLOCK
      89  {
      90    union {
      91      DWORD Status;
      92      PVOID Pointer;
      93    } u;
      94    ULONG_PTR Information;
      95  } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
      96  
      97  typedef enum _FILE_INFORMATION_CLASS {
      98    FilePipeLocalInformation = 24
      99  } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
     100  
     101  typedef DWORD (WINAPI *PNtQueryInformationFile)
     102           (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
     103  
     104  #ifndef PIPE_BUF
     105  #define PIPE_BUF        512
     106  #endif
     107  
     108  static BOOL IsConsoleHandle (HANDLE h)
     109  {
     110    DWORD mode;
     111    return GetConsoleMode (h, &mode) != 0;
     112  }
     113  
     114  static BOOL
     115  IsSocketHandle (HANDLE h)
     116  {
     117    WSANETWORKEVENTS ev;
     118  
     119    if (IsConsoleHandle (h))
     120      return FALSE;
     121  
     122    /* Under Wine, it seems that getsockopt returns 0 for pipes too.
     123       WSAEnumNetworkEvents instead distinguishes the two correctly.  */
     124    ev.lNetworkEvents = 0xDEADBEEF;
     125    WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
     126    return ev.lNetworkEvents != 0xDEADBEEF;
     127  }
     128  
     129  /* Compute output fd_sets for libc descriptor FD (whose Windows handle is
     130     H).  */
     131  
     132  static int
     133  windows_poll_handle (HANDLE h, int fd,
     134                       struct bitset *rbits,
     135                       struct bitset *wbits,
     136                       struct bitset *xbits)
     137  {
     138    BOOL read, write, except;
     139    int i, ret;
     140    INPUT_RECORD *irbuffer;
     141    DWORD avail, nbuffer;
     142    BOOL bRet;
     143    IO_STATUS_BLOCK iosb;
     144    FILE_PIPE_LOCAL_INFORMATION fpli;
     145    static PNtQueryInformationFile NtQueryInformationFile;
     146    static BOOL once_only;
     147  
     148    read = write = except = FALSE;
     149    switch (GetFileType (h))
     150      {
     151      case FILE_TYPE_DISK:
     152        read = TRUE;
     153        write = TRUE;
     154        break;
     155  
     156      case FILE_TYPE_PIPE:
     157        if (!once_only)
     158          {
     159            NtQueryInformationFile = (PNtQueryInformationFile)
     160              GetProcAddress (GetModuleHandle ("ntdll.dll"),
     161                              "NtQueryInformationFile");
     162            once_only = TRUE;
     163          }
     164  
     165        if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
     166          {
     167            if (avail)
     168              read = TRUE;
     169          }
     170        else if (GetLastError () == ERROR_BROKEN_PIPE)
     171          ;
     172  
     173        else
     174          {
     175            /* It was the write-end of the pipe.  Check if it is writable.
     176               If NtQueryInformationFile fails, optimistically assume the pipe is
     177               writable.  This could happen on Windows 9x, where
     178               NtQueryInformationFile is not available, or if we inherit a pipe
     179               that doesn't permit FILE_READ_ATTRIBUTES access on the write end
     180               (I think this should not happen since Windows XP SP2; WINE seems
     181               fine too).  Otherwise, ensure that enough space is available for
     182               atomic writes.  */
     183            memset (&iosb, 0, sizeof (iosb));
     184            memset (&fpli, 0, sizeof (fpli));
     185  
     186            if (!NtQueryInformationFile
     187                || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
     188                                           FilePipeLocalInformation)
     189                || fpli.WriteQuotaAvailable >= PIPE_BUF
     190                || (fpli.OutboundQuota < PIPE_BUF &&
     191                    fpli.WriteQuotaAvailable == fpli.OutboundQuota))
     192              write = TRUE;
     193          }
     194        break;
     195  
     196      case FILE_TYPE_CHAR:
     197        write = TRUE;
     198        if (!(rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
     199          break;
     200  
     201        ret = WaitForSingleObject (h, 0);
     202        if (ret == WAIT_OBJECT_0)
     203          {
     204            if (!IsConsoleHandle (h))
     205              {
     206                read = TRUE;
     207                break;
     208              }
     209  
     210            nbuffer = avail = 0;
     211            bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
     212  
     213            /* Screen buffers handles are filtered earlier.  */
     214            assert (bRet);
     215            if (nbuffer == 0)
     216              {
     217                except = TRUE;
     218                break;
     219              }
     220  
     221            irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
     222            bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail);
     223            if (!bRet || avail == 0)
     224              {
     225                except = TRUE;
     226                break;
     227              }
     228  
     229            for (i = 0; i < avail; i++)
     230              if (irbuffer[i].EventType == KEY_EVENT)
     231                read = TRUE;
     232          }
     233        break;
     234  
     235      default:
     236        ret = WaitForSingleObject (h, 0);
     237        write = TRUE;
     238        if (ret == WAIT_OBJECT_0)
     239          read = TRUE;
     240  
     241        break;
     242      }
     243  
     244    ret = 0;
     245    if (read && (rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
     246      {
     247        rbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
     248        ret++;
     249      }
     250  
     251    if (write && (wbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
     252      {
     253        wbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
     254        ret++;
     255      }
     256  
     257    if (except && (xbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
     258      {
     259        xbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
     260        ret++;
     261      }
     262  
     263    return ret;
     264  }
     265  
     266  int
     267  rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
     268              struct timeval *timeout)
     269  #undef timeval
     270  {
     271    static struct timeval tv0;
     272    static HANDLE hEvent;
     273    HANDLE h, handle_array[FD_SETSIZE + 2];
     274    fd_set handle_rfds, handle_wfds, handle_xfds;
     275    struct bitset rbits, wbits, xbits;
     276    unsigned char anyfds_in[FD_SETSIZE / CHAR_BIT];
     277    DWORD ret, wait_timeout, nhandles, nsock, nbuffer;
     278    MSG msg;
     279    int i, fd, rc;
     280    clock_t tend;
     281  
     282    if (nfds > FD_SETSIZE)
     283      nfds = FD_SETSIZE;
     284  
     285    if (!timeout)
     286      wait_timeout = INFINITE;
     287    else
     288      {
     289        wait_timeout = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
     290  
     291        /* select is also used as a portable usleep.  */
     292        if (!rfds && !wfds && !xfds)
     293          {
     294            Sleep (wait_timeout);
     295            return 0;
     296          }
     297      }
     298  
     299    if (!hEvent)
     300      hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
     301  
     302    handle_array[0] = hEvent;
     303    nhandles = 1;
     304    nsock = 0;
     305  
     306    /* Copy descriptors to bitsets.  At the same time, eliminate
     307       bits in the "wrong" direction for console input buffers
     308       and screen buffers, because screen buffers are waitable
     309       and they will block until a character is available.  */
     310    memset (&rbits, 0, sizeof (rbits));
     311    memset (&wbits, 0, sizeof (wbits));
     312    memset (&xbits, 0, sizeof (xbits));
     313    memset (anyfds_in, 0, sizeof (anyfds_in));
     314    if (rfds)
     315      for (i = 0; i < rfds->fd_count; i++)
     316        {
     317          fd = rfds->fd_array[i];
     318          h = (HANDLE) _get_osfhandle (fd);
     319          if (IsConsoleHandle (h)
     320              && !GetNumberOfConsoleInputEvents (h, &nbuffer))
     321            continue;
     322  
     323          rbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
     324          anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
     325        }
     326    else
     327      rfds = (fd_set *) alloca (sizeof (fd_set));
     328  
     329    if (wfds)
     330      for (i = 0; i < wfds->fd_count; i++)
     331        {
     332          fd = wfds->fd_array[i];
     333          h = (HANDLE) _get_osfhandle (fd);
     334          if (IsConsoleHandle (h)
     335              && GetNumberOfConsoleInputEvents (h, &nbuffer))
     336            continue;
     337  
     338          wbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
     339          anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
     340        }
     341    else
     342      wfds = (fd_set *) alloca (sizeof (fd_set));
     343  
     344    if (xfds)
     345      for (i = 0; i < xfds->fd_count; i++)
     346        {
     347          fd = xfds->fd_array[i];
     348          xbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
     349          anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
     350        }
     351    else
     352      xfds = (fd_set *) alloca (sizeof (fd_set));
     353  
     354    /* Zero all the fd_sets, including the application's.  */
     355    FD_ZERO (rfds);
     356    FD_ZERO (wfds);
     357    FD_ZERO (xfds);
     358    FD_ZERO (&handle_rfds);
     359    FD_ZERO (&handle_wfds);
     360    FD_ZERO (&handle_xfds);
     361  
     362    /* Classify handles.  Create fd sets for sockets, poll the others. */
     363    for (i = 0; i < nfds; i++)
     364      {
     365        if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
     366          continue;
     367  
     368        h = (HANDLE) _get_osfhandle (i);
     369        if (!h)
     370          {
     371            errno = EBADF;
     372            return -1;
     373          }
     374  
     375        if (IsSocketHandle (h))
     376          {
     377            int requested = FD_CLOSE;
     378  
     379            /* See above; socket handles are mapped onto select, but we
     380               need to map descriptors to handles.  */
     381            if (rbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
     382              {
     383                requested |= FD_READ | FD_ACCEPT;
     384                FD_SET ((SOCKET) h, rfds);
     385                FD_SET ((SOCKET) h, &handle_rfds);
     386              }
     387            if (wbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
     388              {
     389                requested |= FD_WRITE | FD_CONNECT;
     390                FD_SET ((SOCKET) h, wfds);
     391                FD_SET ((SOCKET) h, &handle_wfds);
     392              }
     393            if (xbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
     394              {
     395                requested |= FD_OOB;
     396                FD_SET ((SOCKET) h, xfds);
     397                FD_SET ((SOCKET) h, &handle_xfds);
     398              }
     399  
     400            WSAEventSelect ((SOCKET) h, hEvent, requested);
     401            nsock++;
     402          }
     403        else
     404          {
     405            handle_array[nhandles++] = h;
     406  
     407            /* Poll now.  If we get an event, do not wait below.  */
     408            if (wait_timeout != 0
     409                && windows_poll_handle (h, i, &rbits, &wbits, &xbits))
     410              wait_timeout = 0;
     411          }
     412      }
     413  
     414    /* Place a sentinel at the end of the array.  */
     415    handle_array[nhandles] = NULL;
     416  
     417    /* When will the waiting period expire?  */
     418    if (wait_timeout != INFINITE)
     419      tend = clock () + wait_timeout;
     420  
     421  restart:
     422    if (wait_timeout == 0 || nsock == 0)
     423      rc = 0;
     424    else
     425      {
     426        /* See if we need to wait in the loop below.  If any select is ready,
     427           do MsgWaitForMultipleObjects anyway to dispatch messages, but
     428           no need to call select again.  */
     429        rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
     430        if (rc == 0)
     431          {
     432            /* Restore the fd_sets for the other select we do below.  */
     433            memcpy (&handle_rfds, rfds, sizeof (fd_set));
     434            memcpy (&handle_wfds, wfds, sizeof (fd_set));
     435            memcpy (&handle_xfds, xfds, sizeof (fd_set));
     436          }
     437        else
     438          wait_timeout = 0;
     439      }
     440  
     441    /* How much is left to wait?  */
     442    if (wait_timeout != INFINITE)
     443      {
     444        clock_t tnow = clock ();
     445        if (tend >= tnow)
     446          wait_timeout = tend - tnow;
     447        else
     448          wait_timeout = 0;
     449      }
     450  
     451    for (;;)
     452      {
     453        ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
     454                                         wait_timeout, QS_ALLINPUT);
     455  
     456        if (ret == WAIT_OBJECT_0 + nhandles)
     457          {
     458            /* new input of some other kind */
     459            BOOL bRet;
     460            while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
     461              {
     462                TranslateMessage (&msg);
     463                DispatchMessage (&msg);
     464              }
     465          }
     466        else
     467          break;
     468      }
     469  
     470    /* If we haven't done it yet, check the status of the sockets.  */
     471    if (rc == 0 && nsock > 0)
     472      rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
     473  
     474    if (nhandles > 1)
     475      {
     476        /* Count results that are not counted in the return value of select.  */
     477        nhandles = 1;
     478        for (i = 0; i < nfds; i++)
     479          {
     480            if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
     481              continue;
     482  
     483            h = (HANDLE) _get_osfhandle (i);
     484            if (h == handle_array[nhandles])
     485              {
     486                /* Not a socket.  */
     487                nhandles++;
     488                windows_poll_handle (h, i, &rbits, &wbits, &xbits);
     489                if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))
     490                    || wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))
     491                    || xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
     492                  rc++;
     493              }
     494          }
     495  
     496        if (rc == 0
     497            && (wait_timeout == INFINITE
     498                /* If NHANDLES > 1, but no bits are set, it means we've
     499                   been told incorrectly that some handle was signaled.
     500                   This happens with anonymous pipes, which always cause
     501                   MsgWaitForMultipleObjects to exit immediately, but no
     502                   data is found ready to be read by windows_poll_handle.
     503                   To avoid a total failure (whereby we return zero and
     504                   don't wait at all), let's poll in a more busy loop.  */
     505                || (wait_timeout != 0 && nhandles > 1)))
     506          {
     507            /* Sleep 1 millisecond to avoid busy wait and retry with the
     508               original fd_sets.  */
     509            memcpy (&handle_rfds, rfds, sizeof (fd_set));
     510            memcpy (&handle_wfds, wfds, sizeof (fd_set));
     511            memcpy (&handle_xfds, xfds, sizeof (fd_set));
     512            SleepEx (1, TRUE);
     513            goto restart;
     514          }
     515        if (timeout && wait_timeout == 0 && rc == 0)
     516          timeout->tv_sec = timeout->tv_usec = 0;
     517      }
     518  
     519    /* Now fill in the results.  */
     520    FD_ZERO (rfds);
     521    FD_ZERO (wfds);
     522    FD_ZERO (xfds);
     523    nhandles = 1;
     524    for (i = 0; i < nfds; i++)
     525      {
     526        if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
     527          continue;
     528  
     529        h = (HANDLE) _get_osfhandle (i);
     530        if (h != handle_array[nhandles])
     531          {
     532            /* Perform handle->descriptor mapping.  */
     533            SOCKET s = (SOCKET) h;
     534            WSAEventSelect (s, NULL, 0);
     535            if (FD_ISSET (s, &handle_rfds))
     536              FD_SET (i, rfds);
     537            if (FD_ISSET (s, &handle_wfds))
     538              FD_SET (i, wfds);
     539            if (FD_ISSET (s, &handle_xfds))
     540              FD_SET (i, xfds);
     541          }
     542        else
     543          {
     544            /* Not a socket.  */
     545            nhandles++;
     546            if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
     547              FD_SET (i, rfds);
     548            if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
     549              FD_SET (i, wfds);
     550            if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
     551              FD_SET (i, xfds);
     552          }
     553      }
     554  
     555    return rc;
     556  }
     557  
     558  #else /* ! Native Windows.  */
     559  
     560  #include <stddef.h> /* NULL */
     561  #include <errno.h>
     562  #include <unistd.h>
     563  
     564  #undef select
     565  
     566  int
     567  rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
     568              struct timeval *timeout)
     569  {
     570    int i;
     571  
     572    /* FreeBSD 8.2 has a bug: it does not always detect invalid fds.  */
     573    if (nfds < 0 || nfds > FD_SETSIZE)
     574      {
     575        errno = EINVAL;
     576        return -1;
     577      }
     578    for (i = 0; i < nfds; i++)
     579      {
     580        if (((rfds && FD_ISSET (i, rfds))
     581             || (wfds && FD_ISSET (i, wfds))
     582             || (xfds && FD_ISSET (i, xfds)))
     583            && dup2 (i, i) != i)
     584          return -1;
     585      }
     586  
     587    /* Interix 3.5 has a bug: it does not support nfds == 0.  */
     588    if (nfds == 0)
     589      {
     590        nfds = 1;
     591        rfds = NULL;
     592        wfds = NULL;
     593        xfds = NULL;
     594      }
     595    return select (nfds, rfds, wfds, xfds, timeout);
     596  }
     597  
     598  #endif