(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
select.c
       1  /* Linux select implementation.
       2     Copyright (C) 2017-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library.  If not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <sys/time.h>
      20  #include <sys/types.h>
      21  #include <sys/select.h>
      22  #include <errno.h>
      23  #include <sysdep-cancel.h>
      24  
      25  /* Check the first NFDS descriptors each in READFDS (if not NULL) for read
      26     readiness, in WRITEFDS (if not NULL) for write readiness, and in EXCEPTFDS
      27     (if not NULL) for exceptional conditions.  If TIMEOUT is not NULL, time out
      28     after waiting the interval specified therein.  Returns the number of ready
      29     descriptors, or -1 for errors.  */
      30  
      31  int
      32  __select64 (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
      33  	    struct __timeval64 *timeout)
      34  {
      35    __time64_t s = timeout != NULL ? timeout->tv_sec : 0;
      36    int32_t us = timeout != NULL ? timeout->tv_usec : 0;
      37    int32_t ns;
      38  
      39    if (s < 0 || us < 0)
      40      return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
      41  
      42    /* Normalize the timeout, as legacy Linux __NR_select and __NR__newselect.
      43       Different than syscall, it also handle possible overflow.  */
      44    if (us / USEC_PER_SEC > INT64_MAX - s)
      45      {
      46        s = INT64_MAX;
      47        ns = NSEC_PER_SEC - 1;
      48      }
      49    else
      50      {
      51        s += us / USEC_PER_SEC;
      52        us = us % USEC_PER_SEC;
      53        ns = us * NSEC_PER_USEC;
      54      }
      55  
      56    struct __timespec64 ts64, *pts64 = NULL;
      57     if (timeout != NULL)
      58       {
      59         ts64.tv_sec = s;
      60         ts64.tv_nsec = ns;
      61         pts64 = &ts64;
      62       }
      63  
      64  #ifndef __NR_pselect6_time64
      65  # define __NR_pselect6_time64 __NR_pselect6
      66  #endif
      67  
      68  #ifdef __ASSUME_TIME64_SYSCALLS
      69    int r = SYSCALL_CANCEL (pselect6_time64, nfds, readfds, writefds, exceptfds,
      70  			  pts64, NULL);
      71    if (timeout != NULL)
      72      TIMESPEC_TO_TIMEVAL (timeout, pts64);
      73    return r;
      74  #else
      75    bool need_time64 = timeout != NULL && !in_int32_t_range (timeout->tv_sec);
      76    if (need_time64)
      77      {
      78        int r = SYSCALL_CANCEL (pselect6_time64, nfds, readfds, writefds,
      79  			      exceptfds, pts64, NULL);
      80        if ((r >= 0 || errno != ENOSYS) && timeout != NULL)
      81  	{
      82  	  TIMESPEC_TO_TIMEVAL (timeout, &ts64);
      83  	}
      84        else
      85  	__set_errno (EOVERFLOW);
      86        return r;
      87      }
      88  
      89  # ifdef __ASSUME_PSELECT
      90    struct timespec ts32, *pts32 = NULL;
      91    if (pts64 != NULL)
      92      {
      93        ts32.tv_sec = pts64->tv_sec;
      94        ts32.tv_nsec = pts64->tv_nsec;
      95        pts32 = &ts32;
      96      }
      97  
      98    int r = SYSCALL_CANCEL (pselect6, nfds, readfds, writefds, exceptfds, pts32,
      99  			  NULL);
     100    if (timeout != NULL)
     101      TIMESPEC_TO_TIMEVAL (timeout, pts32);
     102    return r;
     103  # else
     104    struct timeval tv32, *ptv32 = NULL;
     105    if (pts64 != NULL)
     106      {
     107        tv32 = valid_timespec64_to_timeval (*pts64);
     108        ptv32 = &tv32;
     109      }
     110  
     111    int r = SYSCALL_CANCEL (_newselect, nfds, readfds, writefds, exceptfds, ptv32);
     112    if (timeout != NULL)
     113      *timeout = valid_timeval_to_timeval64 (tv32);
     114    return r;
     115  # endif /* __ASSUME_PSELECT  */
     116  #endif
     117  }
     118  
     119  #if __TIMESIZE != 64
     120  libc_hidden_def (__select64)
     121  
     122  int
     123  __select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
     124  	  struct timeval *timeout)
     125  {
     126    struct __timeval64 tv64, *ptv64 = NULL;
     127    if (timeout != NULL)
     128      {
     129        tv64 = valid_timeval_to_timeval64 (*timeout);
     130        ptv64 = &tv64;
     131      }
     132    int r = __select64 (nfds, readfds, writefds, exceptfds, ptv64);
     133    if (timeout != NULL)
     134      /* The remanining timeout will be always less the input TIMEOUT.  */
     135      *timeout = valid_timeval64_to_timeval (tv64);
     136    return r;
     137  }
     138  #endif
     139  libc_hidden_def (__select)
     140  
     141  weak_alias (__select, select)
     142  weak_alias (__select, __libc_select)