X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/6d2010ae8f7a6078e10b361c6962983bab233e0f..eee3565979933af707c711411001ba11fe406a3c:/libsyscall/wrappers/select-base.c?ds=sidebyside diff --git a/libsyscall/wrappers/select-base.c b/libsyscall/wrappers/select-base.c index 09f8816f6..f688d6f36 100644 --- a/libsyscall/wrappers/select-base.c +++ b/libsyscall/wrappers/select-base.c @@ -26,24 +26,37 @@ #define __DARWIN_NON_CANCELABLE 0 #endif /* __LP64__ && (VARIANT_CANCELABLE || VARIANT_PRE1050) */ +#if defined(VARIANT_DARWIN_EXTSN) +#define _DARWIN_C_SOURCE +#define _DARWIN_UNLIMITED_SELECT +#endif + #include +#include +#include #include "_errno.h" #if defined(VARIANT_CANCELABLE) || defined(VARIANT_PRE1050) +#if !defined(VARIANT_DARWIN_EXTSN) extern int __select(int, fd_set * __restrict, fd_set * __restrict, fd_set * __restrict, struct timeval * __restrict); +#endif +int __pselect(int, fd_set * __restrict, fd_set * __restrict, + fd_set * __restrict, const struct timespec * __restrict, const sigset_t * __restrict); #else /* !VARIANT_CANCELABLE && !VARIANT_PRE1050 */ +#if !defined(VARIANT_DARWIN_EXTSN) int __select_nocancel(int, fd_set * __restrict, fd_set * __restrict, fd_set * __restrict, struct timeval * __restrict); +#endif +int __pselect_nocancel(int, fd_set * __restrict, fd_set * __restrict, + fd_set * __restrict, const struct timespec * __restrict, const sigset_t * __restrict); #endif /* VARIANT_CANCELABLE || VARIANT_PRE1050 */ +#if !defined(VARIANT_DARWIN_EXTSN) /* - * select stub, return error if nfds > FD_SETSIZE - * add pthread cancelability - * mandated for conformance. - * - * This is only for (non DARWINEXTSN) UNIX03 (both cancelable and - * non-cancelable) and for legacy + * select() implementation for 1050 and legacy (cancelable and non-cancelable) + * variants. The darwin extension variants (both cancelable & non-cancelable) are + * mapped directly to the syscall stub. */ int select(int nfds, fd_set * __restrict readfds, fd_set * __restrict writefds, @@ -55,7 +68,6 @@ select(int nfds, fd_set * __restrict readfds, fd_set * __restrict writefds, #endif /* VARIANT_LEGACY || VARIANT_PRE1050 */ ) { - #if defined(VARIANT_LEGACY) || defined(VARIANT_PRE1050) struct timeval tb, *timeout; @@ -66,17 +78,111 @@ select(int nfds, fd_set * __restrict readfds, fd_set * __restrict writefds, tb.tv_sec = 0; tb.tv_usec = 10000; timeout = &tb; - } else + } else { timeout = intimeout; + } #else /* !VARIANT_LEGACY && !VARIANT_PRE1050 */ if (nfds > FD_SETSIZE) { errno = EINVAL; return -1; } -#endif /* VARIANT_LEGACY || VARIANT_PRE1050 */ +#endif + #if defined(VARIANT_CANCELABLE) || defined(VARIANT_PRE1050) return __select(nfds, readfds, writefds, exceptfds, timeout); #else /* !VARIANT_CANCELABLE && !VARIANT_PRE1050 */ return __select_nocancel(nfds, readfds, writefds, exceptfds, timeout); #endif /* VARIANT_CANCELABLE || VARIANT_PRE1050 */ } +#endif /* !defined(VARIANT_DARWIN_EXTSN) */ + + +/* + * User-space emulation of pselect() syscall for B&I + * TODO: remove when B&I move to xnu with native pselect() + */ +extern int __pthread_sigmask(int, const sigset_t *, sigset_t *); +static int +_pselect_emulated(int count, fd_set * __restrict rfds, fd_set * __restrict wfds, + fd_set * __restrict efds, const struct timespec * __restrict timo, + const sigset_t * __restrict mask) +{ + sigset_t omask; + struct timeval tvtimo, *tvp; + int rv, sverrno; + + if (timo) { + tvtimo.tv_sec = timo->tv_sec; + tvtimo.tv_usec = (__darwin_suseconds_t)(timo->tv_nsec / 1000); + tvp = &tvtimo; + } else { + tvp = 0; + } + + if (mask != 0) { + rv = __pthread_sigmask(SIG_SETMASK, mask, &omask); + if (rv != 0) + return rv; + } + + rv = select(count, rfds, wfds, efds, tvp); + if (mask != 0) { + sverrno = errno; + __pthread_sigmask(SIG_SETMASK, &omask, (sigset_t *)0); + errno = sverrno; + } + + return rv; +} + +/* + * pselect() implementation for all variants. Unlike select(), we implement the + * darwin extension variants here to catch cases where xnu doesn't implement + * pselect and we need to emulate. + */ +int +pselect(int nfds, fd_set * __restrict readfds, fd_set * __restrict writefds, + fd_set * __restrict exceptfds, const struct timespec * __restrict +#if defined(VARIANT_LEGACY) || defined(VARIANT_PRE1050) + intimeout, +#else /* !VARIANT_LEGACY && !VARIANT_PRE1050 */ + timeout, +#endif /* VARIANT_LEGACY || VARIANT_PRE1050 */ + const sigset_t * __restrict sigmask) +{ + int ret; +#if defined(VARIANT_LEGACY) || defined(VARIANT_PRE1050) + struct timespec tb; + const struct timespec *timeout; + + /* + * Legacy select behavior is minimum 10 msec when tv_usec is non-zero + */ + if (intimeout && intimeout->tv_sec == 0 && intimeout->tv_nsec > 0 && intimeout->tv_nsec < 10000000L) { + tb.tv_sec = 0; + tb.tv_nsec = 10000000L; + timeout = &tb; + } else { + timeout = intimeout; + } +#elif defined(VARIANT_DARWIN_EXTSN) +#else + /* 1050 variant */ + if (nfds > FD_SETSIZE) { + errno = EINVAL; + return -1; + } +#endif + +#if defined(VARIANT_CANCELABLE) || defined(VARIANT_PRE1050) + ret = __pselect(nfds, readfds, writefds, exceptfds, timeout, sigmask); +#else /* !VARIANT_CANCELABLE && !VARIANT_PRE1050 */ + ret = __pselect_nocancel(nfds, readfds, writefds, exceptfds, timeout, sigmask); +#endif /* VARIANT_CANCELABLE || VARIANT_PRE1050 */ + + if (ret == -1 && errno == ENOSYS) { + ret = _pselect_emulated(nfds, readfds, writefds, exceptfds, timeout, sigmask); + } + + return ret; +}