tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

uxwrap.c (13746B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /*
      7 *------------------------------------------------------------------------
      8 * File: uxwrap.c
      9 *
     10 *     Our wrapped versions of the Unix select() and poll() system calls.
     11 *
     12 *------------------------------------------------------------------------
     13 */
     14 
     15 #include "primpl.h"
     16 
     17 #if defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) || defined(QNX)
     18 /* Do not wrap select() and poll(). */
     19 #else /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */
     20 /* The include files for select() */
     21 #  include <string.h>
     22 #  include <sys/types.h>
     23 #  include <sys/time.h>
     24 
     25 #  define ZAP_SET(_to, _width)                                               \
     26    PR_BEGIN_MACRO                                                           \
     27    memset(                                                                  \
     28        _to, 0,                                                              \
     29        ((_width + 8 * sizeof(int) - 1) / (8 * sizeof(int))) * sizeof(int)); \
     30    PR_END_MACRO
     31 
     32 /* see comments in ns/cmd/xfe/mozilla.c (look for "PR_XGetXtHackFD") */
     33 static int _pr_xt_hack_fd = -1;
     34 
     35 int PR_XGetXtHackFD(void) {
     36  int fds[2];
     37 
     38  if (_pr_xt_hack_fd == -1) {
     39    if (!pipe(fds)) {
     40      _pr_xt_hack_fd = fds[0];
     41    }
     42  }
     43  return _pr_xt_hack_fd;
     44 }
     45 
     46 static int (*_pr_xt_hack_okayToReleaseXLock)(void) = 0;
     47 
     48 void PR_SetXtHackOkayToReleaseXLockFn(int (*fn)(void)) {
     49  _pr_xt_hack_okayToReleaseXLock = fn;
     50 }
     51 
     52 /*
     53 *-----------------------------------------------------------------------
     54 *  select() --
     55 *
     56 *    Wrap up the select system call so that we can deschedule
     57 *    a thread that tries to wait for i/o.
     58 *
     59 *-----------------------------------------------------------------------
     60 */
     61 
     62 #  if defined(AIX_RENAME_SELECT)
     63 int wrap_select(unsigned long width, void* rl, void* wl, void* el,
     64                struct timeval* tv)
     65 #  elif defined(_PR_SELECT_CONST_TIMEVAL)
     66 int select(int width, fd_set* rd, fd_set* wr, fd_set* ex,
     67           const struct timeval* tv)
     68 #  else
     69 int select(int width, fd_set* rd, fd_set* wr, fd_set* ex, struct timeval* tv)
     70 #  endif
     71 {
     72  int osfd;
     73  _PRUnixPollDesc *unixpds, *unixpd, *eunixpd;
     74  PRInt32 pdcnt;
     75  PRIntervalTime timeout;
     76  int retVal;
     77 #  if defined(AIX_RENAME_SELECT)
     78  fd_set* rd = (fd_set*)rl;
     79  fd_set* wr = (fd_set*)wl;
     80  fd_set* ex = (fd_set*)el;
     81 #  endif
     82 
     83 #  if 0
     84    /*
     85     * Easy special case: zero timeout.  Simply call the native
     86     * select() with no fear of blocking.
     87     */
     88    if (tv != NULL && tv->tv_sec == 0 && tv->tv_usec == 0) {
     89 #    if defined(AIX_RENAME_SELECT)
     90        return _MD_SELECT(width, rl, wl, el, tv);
     91 #    else
     92        return _MD_SELECT(width, rd, wr, ex, tv);
     93 #    endif
     94    }
     95 #  endif
     96 
     97  if (!_pr_initialized) {
     98    _PR_ImplicitInitialization();
     99  }
    100 
    101 #  ifndef _PR_LOCAL_THREADS_ONLY
    102  if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) {
    103    return _MD_SELECT(width, rd, wr, ex, tv);
    104  }
    105 #  endif
    106 
    107  if (width < 0 || width > FD_SETSIZE) {
    108    errno = EINVAL;
    109    return -1;
    110  }
    111 
    112  /* Compute timeout */
    113  if (tv) {
    114    /*
    115     * These acceptable ranges for t_sec and t_usec are taken
    116     * from the select() man pages.
    117     */
    118    if (tv->tv_sec < 0 || tv->tv_sec > 100000000 || tv->tv_usec < 0 ||
    119        tv->tv_usec >= 1000000) {
    120      errno = EINVAL;
    121      return -1;
    122    }
    123 
    124    /* Convert microseconds to ticks */
    125    timeout = PR_MicrosecondsToInterval(1000000 * tv->tv_sec + tv->tv_usec);
    126  } else {
    127    /* tv being a NULL pointer means blocking indefinitely */
    128    timeout = PR_INTERVAL_NO_TIMEOUT;
    129  }
    130 
    131  /* Check for no descriptors case (just doing a timeout) */
    132  if ((!rd && !wr && !ex) || !width) {
    133    PR_Sleep(timeout);
    134    return 0;
    135  }
    136 
    137  /*
    138   * Set up for PR_Poll().  The PRPollDesc array is allocated
    139   * dynamically.  If this turns out to have high performance
    140   * penalty, one can change to use a large PRPollDesc array
    141   * on the stack, and allocate dynamically only when it turns
    142   * out to be not large enough.
    143   *
    144   * I allocate an array of size 'width', which is the maximum
    145   * number of fds we may need to poll.
    146   */
    147  unixpds = (_PRUnixPollDesc*)PR_CALLOC(width * sizeof(_PRUnixPollDesc));
    148  if (!unixpds) {
    149    errno = ENOMEM;
    150    return -1;
    151  }
    152 
    153  pdcnt = 0;
    154  unixpd = unixpds;
    155  for (osfd = 0; osfd < width; osfd++) {
    156    int in_flags = 0;
    157    if (rd && FD_ISSET(osfd, rd)) {
    158      in_flags |= _PR_UNIX_POLL_READ;
    159    }
    160    if (wr && FD_ISSET(osfd, wr)) {
    161      in_flags |= _PR_UNIX_POLL_WRITE;
    162    }
    163    if (ex && FD_ISSET(osfd, ex)) {
    164      in_flags |= _PR_UNIX_POLL_EXCEPT;
    165    }
    166    if (in_flags) {
    167      unixpd->osfd = osfd;
    168      unixpd->in_flags = in_flags;
    169      unixpd->out_flags = 0;
    170      unixpd++;
    171      pdcnt++;
    172    }
    173  }
    174 
    175  /*
    176   * see comments in mozilla/cmd/xfe/mozilla.c (look for
    177   * "PR_XGetXtHackFD")
    178   */
    179  {
    180    int needToLockXAgain;
    181 
    182    needToLockXAgain = 0;
    183    if (rd && (_pr_xt_hack_fd != -1) && FD_ISSET(_pr_xt_hack_fd, rd) &&
    184        _PR_XIsLocked() &&
    185        (!_pr_xt_hack_okayToReleaseXLock || _pr_xt_hack_okayToReleaseXLock())) {
    186      _PR_XUnlock();
    187      needToLockXAgain = 1;
    188    }
    189 
    190    /* This is the potentially blocking step */
    191    retVal = _PR_WaitForMultipleFDs(unixpds, pdcnt, timeout);
    192 
    193    if (needToLockXAgain) {
    194      _PR_XLock();
    195    }
    196  }
    197 
    198  if (retVal > 0) {
    199    /* Compute select results */
    200    if (rd) {
    201      ZAP_SET(rd, width);
    202    }
    203    if (wr) {
    204      ZAP_SET(wr, width);
    205    }
    206    if (ex) {
    207      ZAP_SET(ex, width);
    208    }
    209 
    210    /*
    211     * The return value can be either the number of ready file
    212     * descriptors or the number of set bits in the three fd_set's.
    213     */
    214    retVal = 0; /* we're going to recompute */
    215    eunixpd = unixpds + pdcnt;
    216    for (unixpd = unixpds; unixpd < eunixpd; unixpd++) {
    217      if (unixpd->out_flags) {
    218        int nbits = 0; /* The number of set bits on for this fd */
    219 
    220        if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) {
    221          errno = EBADF;
    222          PR_LOG(_pr_io_lm, PR_LOG_ERROR,
    223                 ("select returns EBADF for %d", unixpd->osfd));
    224          retVal = -1;
    225          break;
    226        }
    227        /*
    228         * If a socket has a pending error, it is considered
    229         * both readable and writable.  (See W. Richard Stevens,
    230         * Unix Network Programming, Vol. 1, 2nd Ed., Section 6.3,
    231         * pp. 153-154.)  We also consider a socket readable if
    232         * it has a hangup condition.
    233         */
    234        if (rd && (unixpd->in_flags & _PR_UNIX_POLL_READ) &&
    235            (unixpd->out_flags &
    236             (_PR_UNIX_POLL_READ | _PR_UNIX_POLL_ERR | _PR_UNIX_POLL_HUP))) {
    237          FD_SET(unixpd->osfd, rd);
    238          nbits++;
    239        }
    240        if (wr && (unixpd->in_flags & _PR_UNIX_POLL_WRITE) &&
    241            (unixpd->out_flags & (_PR_UNIX_POLL_WRITE | _PR_UNIX_POLL_ERR))) {
    242          FD_SET(unixpd->osfd, wr);
    243          nbits++;
    244        }
    245        if (ex && (unixpd->in_flags & _PR_UNIX_POLL_WRITE) &&
    246            (unixpd->out_flags & PR_POLL_EXCEPT)) {
    247          FD_SET(unixpd->osfd, ex);
    248          nbits++;
    249        }
    250        PR_ASSERT(nbits > 0);
    251 #  if defined(HPUX) || defined(SOLARIS) || defined(AIX)
    252        retVal += nbits;
    253 #  endif
    254      }
    255    }
    256  }
    257 
    258  PR_ASSERT(tv || retVal != 0);
    259  PR_LOG(_pr_io_lm, PR_LOG_MIN, ("select returns %d", retVal));
    260  PR_DELETE(unixpds);
    261 
    262  return retVal;
    263 }
    264 
    265 /*
    266 * Redefine poll, when supported on platforms, for local threads
    267 */
    268 
    269 /*
    270 * I am commenting out the poll() wrapper for Linux for now
    271 * because it is difficult to define _MD_POLL that works on all
    272 * Linux varieties.  People reported that glibc 2.0.7 on Debian
    273 * 2.0 Linux machines doesn't have the __syscall_poll symbol
    274 * defined.  (WTC 30 Nov. 1998)
    275 */
    276 #  if defined(_PR_POLL_AVAILABLE) && !defined(LINUX)
    277 
    278 /*
    279 *-----------------------------------------------------------------------
    280 * poll() --
    281 *
    282 * RETURN VALUES:
    283 *     -1:  fails, errno indicates the error.
    284 *      0:  timed out, the revents bitmasks are not set.
    285 *      positive value: the number of file descriptors for which poll()
    286 *          has set the revents bitmask.
    287 *
    288 *-----------------------------------------------------------------------
    289 */
    290 
    291 #    include <poll.h>
    292 
    293 #    if defined(AIX_RENAME_SELECT)
    294 int wrap_poll(void* listptr, unsigned long nfds, long timeout)
    295 #    elif (defined(AIX) && !defined(AIX_RENAME_SELECT))
    296 int poll(void* listptr, unsigned long nfds, long timeout)
    297 #    elif defined(NETBSD)
    298 int poll(struct pollfd* filedes, nfds_t nfds, int timeout)
    299 #    elif defined(OPENBSD)
    300 int poll(struct pollfd filedes[], nfds_t nfds, int timeout)
    301 #    elif defined(FREEBSD)
    302 int poll(struct pollfd* filedes, unsigned nfds, int timeout)
    303 #    else
    304 int poll(struct pollfd* filedes, unsigned long nfds, int timeout)
    305 #    endif
    306 {
    307 #    ifdef AIX
    308  struct pollfd* filedes = (struct pollfd*)listptr;
    309 #    endif
    310  struct pollfd *pfd, *epfd;
    311  _PRUnixPollDesc *unixpds, *unixpd, *eunixpd;
    312  PRIntervalTime ticks;
    313  PRInt32 pdcnt;
    314  int ready;
    315 
    316  /*
    317   * Easy special case: zero timeout.  Simply call the native
    318   * poll() with no fear of blocking.
    319   */
    320  if (timeout == 0) {
    321 #    if defined(AIX)
    322    return _MD_POLL(listptr, nfds, timeout);
    323 #    else
    324    return _MD_POLL(filedes, nfds, timeout);
    325 #    endif
    326  }
    327 
    328  if (!_pr_initialized) {
    329    _PR_ImplicitInitialization();
    330  }
    331 
    332 #    ifndef _PR_LOCAL_THREADS_ONLY
    333  if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) {
    334    return _MD_POLL(filedes, nfds, timeout);
    335  }
    336 #    endif
    337 
    338  /* We do not support the pollmsg structures on AIX */
    339 #    ifdef AIX
    340  PR_ASSERT((nfds & 0xff00) == 0);
    341 #    endif
    342 
    343  if (timeout < 0 && timeout != -1) {
    344    errno = EINVAL;
    345    return -1;
    346  }
    347 
    348  /* Convert timeout from miliseconds to ticks */
    349  if (timeout == -1) {
    350    ticks = PR_INTERVAL_NO_TIMEOUT;
    351  } else {
    352    ticks = PR_MillisecondsToInterval(timeout);
    353  }
    354 
    355  /* Check for no descriptor case (just do a timeout) */
    356  if (nfds == 0) {
    357    PR_Sleep(ticks);
    358    return 0;
    359  }
    360 
    361  unixpds = (_PRUnixPollDesc*)PR_MALLOC(nfds * sizeof(_PRUnixPollDesc));
    362  if (NULL == unixpds) {
    363    errno = EAGAIN;
    364    return -1;
    365  }
    366 
    367  pdcnt = 0;
    368  epfd = filedes + nfds;
    369  unixpd = unixpds;
    370  for (pfd = filedes; pfd < epfd; pfd++) {
    371    /*
    372     * poll() ignores negative fd's.
    373     */
    374    if (pfd->fd >= 0) {
    375      unixpd->osfd = pfd->fd;
    376 #    ifdef _PR_USE_POLL
    377      unixpd->in_flags = pfd->events;
    378 #    else
    379      /*
    380       * Map the poll events to one of the three that can be
    381       * represented by the select fd_sets:
    382       *     POLLIN, POLLRDNORM  ===> readable
    383       *     POLLOUT, POLLWRNORM ===> writable
    384       *     POLLPRI, POLLRDBAND ===> exception
    385       *     POLLNORM, POLLWRBAND (and POLLMSG on some platforms)
    386       *     are ignored.
    387       *
    388       * The output events POLLERR and POLLHUP are never turned on.
    389       * POLLNVAL may be turned on.
    390       */
    391      unixpd->in_flags = 0;
    392      if (pfd->events & (POLLIN
    393 #      ifdef POLLRDNORM
    394                         | POLLRDNORM
    395 #      endif
    396                         )) {
    397        unixpd->in_flags |= _PR_UNIX_POLL_READ;
    398      }
    399      if (pfd->events & (POLLOUT
    400 #      ifdef POLLWRNORM
    401                         | POLLWRNORM
    402 #      endif
    403                         )) {
    404        unixpd->in_flags |= _PR_UNIX_POLL_WRITE;
    405      }
    406      if (pfd->events & (POLLPRI
    407 #      ifdef POLLRDBAND
    408                         | POLLRDBAND
    409 #      endif
    410                         )) {
    411        unixpd->in_flags |= PR_POLL_EXCEPT;
    412      }
    413 #    endif /* _PR_USE_POLL */
    414      unixpd->out_flags = 0;
    415      unixpd++;
    416      pdcnt++;
    417    }
    418  }
    419 
    420  ready = _PR_WaitForMultipleFDs(unixpds, pdcnt, ticks);
    421  if (-1 == ready) {
    422    if (PR_GetError() == PR_PENDING_INTERRUPT_ERROR) {
    423      errno = EINTR; /* XXX we aren't interrupted by a signal, but... */
    424    } else {
    425      errno = PR_GetOSError();
    426    }
    427  }
    428  if (ready <= 0) {
    429    goto done;
    430  }
    431 
    432  /*
    433   * Copy the out_flags from the _PRUnixPollDesc structures to the
    434   * user's pollfd structures and free the allocated memory
    435   */
    436  unixpd = unixpds;
    437  for (pfd = filedes; pfd < epfd; pfd++) {
    438    pfd->revents = 0;
    439    if (pfd->fd >= 0) {
    440 #    ifdef _PR_USE_POLL
    441      pfd->revents = unixpd->out_flags;
    442 #    else
    443      if (0 != unixpd->out_flags) {
    444        if (unixpd->out_flags & _PR_UNIX_POLL_READ) {
    445          if (pfd->events & POLLIN) {
    446            pfd->revents |= POLLIN;
    447          }
    448 #      ifdef POLLRDNORM
    449          if (pfd->events & POLLRDNORM) {
    450            pfd->revents |= POLLRDNORM;
    451          }
    452 #      endif
    453        }
    454        if (unixpd->out_flags & _PR_UNIX_POLL_WRITE) {
    455          if (pfd->events & POLLOUT) {
    456            pfd->revents |= POLLOUT;
    457          }
    458 #      ifdef POLLWRNORM
    459          if (pfd->events & POLLWRNORM) {
    460            pfd->revents |= POLLWRNORM;
    461          }
    462 #      endif
    463        }
    464        if (unixpd->out_flags & _PR_UNIX_POLL_EXCEPT) {
    465          if (pfd->events & POLLPRI) {
    466            pfd->revents |= POLLPRI;
    467          }
    468 #      ifdef POLLRDBAND
    469          if (pfd->events & POLLRDBAND) {
    470            pfd->revents |= POLLRDBAND;
    471          }
    472 #      endif
    473        }
    474        if (unixpd->out_flags & _PR_UNIX_POLL_ERR) {
    475          pfd->revents |= POLLERR;
    476        }
    477        if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) {
    478          pfd->revents |= POLLNVAL;
    479        }
    480        if (unixpd->out_flags & _PR_UNIX_POLL_HUP) {
    481          pfd->revents |= POLLHUP;
    482        }
    483      }
    484 #    endif /* _PR_USE_POLL */
    485      unixpd++;
    486    }
    487  }
    488 
    489 done:
    490  PR_DELETE(unixpds);
    491  return ready;
    492 }
    493 
    494 #  endif /* !defined(LINUX) */
    495 
    496 #endif /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */
    497 
    498 /* uxwrap.c */