]> git.saurik.com Git - apple/xnu.git/blobdiff - libsyscall/wrappers/select-base.c
xnu-3789.41.3.tar.gz
[apple/xnu.git] / libsyscall / wrappers / select-base.c
index 09f8816f662b53e0e8cb7b9c95dbcac402bb962a..f688d6f36bbcdc00f95711157a2b4a35fb1c41d8 100644 (file)
 #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 <sys/select.h>
+#include <sys/time.h>
+#include <sys/signal.h>
 #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;
+}