1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <assert.h>
  30 #include <unistd.h>
  31 #include <fcntl.h>
  32 #include <errno.h>
  33 #include <stdio.h>
  34 #include <stdlib.h>
  35 #include <alloca.h>
  36 #include <signal.h>
  37 #include <strings.h>
  38 #include <sys/param.h>
  39 #include <sys/brand.h>
  40 #include <sys/poll.h>
  41 #include <sys/syscall.h>
  42 #include <sys/lx_debug.h>
  43 #include <sys/lx_poll.h>
  44 #include <sys/lx_syscall.h>
  45 #include <sys/lx_brand.h>
  46 #include <sys/lx_misc.h>
  47 
  48 extern int select_large_fdset(int nfds, fd_set *in0, fd_set *out0, fd_set *ex0,
  49         struct timeval *tv);
  50 
  51 int
  52 lx_select(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
  53         uintptr_t p5)
  54 {
  55         int nfds = (int)p1;
  56         fd_set *rfdsp = NULL;
  57         fd_set *wfdsp = NULL;
  58         fd_set *efdsp = NULL;
  59         struct timeval tv, *tvp = NULL;
  60         int fd_set_len = howmany(nfds, 8);
  61         int r;
  62         hrtime_t start = NULL, end;
  63 
  64         lx_debug("\tselect(%d, 0x%p, x%p, 0x%p. 0x%p, 0x%p)",
  65             nfds, rfdsp, wfdsp, efdsp, tvp);
  66 
  67         if (nfds > 0) {
  68                 if (p2 != NULL) {
  69                         rfdsp = SAFE_ALLOCA(fd_set_len);
  70                         if (rfdsp == NULL)
  71                                 return (-ENOMEM);
  72                         if (uucopy((void *)p2, rfdsp, fd_set_len) != 0)
  73                                 return (-errno);
  74                 }
  75                 if (p3 != NULL) {
  76                         wfdsp = SAFE_ALLOCA(fd_set_len);
  77                         if (wfdsp == NULL)
  78                                 return (-ENOMEM);
  79                         if (uucopy((void *)p3, wfdsp, fd_set_len) != 0)
  80                                 return (-errno);
  81                 }
  82                 if (p4 != NULL) {
  83                         efdsp = SAFE_ALLOCA(fd_set_len);
  84                         if (efdsp == NULL)
  85                                 return (-ENOMEM);
  86                         if (uucopy((void *)p4, efdsp, fd_set_len) != 0)
  87                                 return (-errno);
  88                 }
  89         }
  90         if (p5 != NULL) {
  91                 tvp = &tv;
  92                 if (uucopy((void *)p5, &tv, sizeof (tv)) != 0)
  93                         return (-errno);
  94                 start = gethrtime();
  95         }
  96 
  97         if (nfds >= FD_SETSIZE)
  98                 r = select_large_fdset(nfds, rfdsp, wfdsp, efdsp, tvp);
  99         else
 100                 r = select(nfds, rfdsp, wfdsp, efdsp, tvp);
 101         if (r < 0)
 102                 return (-errno);
 103 
 104         if (tvp != NULL) {
 105                 long long tv_total;
 106 
 107                 /*
 108                  * Linux updates the timeval parameter for select() calls
 109                  * with the amount of time that left before the select
 110                  * would have timed out.
 111                  */
 112                 end = gethrtime();
 113                 tv_total = (tv.tv_sec * MICROSEC) + tv.tv_usec;
 114                 tv_total -= ((end - start) / (NANOSEC / MICROSEC));
 115                 if (tv_total < 0) {
 116                         tv.tv_sec = 0;
 117                         tv.tv_usec = 0;
 118                 } else {
 119                         tv.tv_sec = tv_total / MICROSEC;
 120                         tv.tv_usec = tv_total % MICROSEC;
 121                 }
 122 
 123                 if (uucopy(&tv, (void *)p5, sizeof (tv)) != 0)
 124                         return (-errno);
 125         }
 126 
 127         if ((rfdsp != NULL) && (uucopy(rfdsp, (void *)p2, fd_set_len) != 0))
 128                 return (-errno);
 129         if ((wfdsp != NULL) && (uucopy(wfdsp, (void *)p3, fd_set_len) != 0))
 130                 return (-errno);
 131         if ((efdsp != NULL) && (uucopy(efdsp, (void *)p4, fd_set_len) != 0))
 132                 return (-errno);
 133 
 134         return (r);
 135 }
 136 
 137 int
 138 lx_poll(uintptr_t p1, uintptr_t p2, uintptr_t p3)
 139 {
 140         struct pollfd   *lfds, *sfds;
 141         nfds_t          nfds = (nfds_t)p2;
 142         int             fds_size, i, rval, revents;
 143 
 144         /*
 145          * Note: we are assuming that the Linux and Solaris pollfd
 146          * structures are identical.  Copy in the linux poll structure.
 147          */
 148         fds_size = sizeof (struct pollfd) * nfds;
 149         lfds = (struct pollfd *)SAFE_ALLOCA(fds_size);
 150         if (lfds == NULL)
 151                 return (-ENOMEM);
 152         if (uucopy((void *)p1, lfds, fds_size) != 0)
 153                 return (-errno);
 154 
 155         /*
 156          * The poll system call modifies the poll structures passed in
 157          * so we'll need to make an exra copy of them.
 158          */
 159         sfds = (struct pollfd *)SAFE_ALLOCA(fds_size);
 160         if (sfds == NULL)
 161                 return (-ENOMEM);
 162 
 163         /* Convert the Linux events bitmask into the Solaris equivalent. */
 164         for (i = 0; i < nfds; i++) {
 165                 /*
 166                  * If the caller is polling for an unsupported event, we
 167                  * have to bail out.
 168                  */
 169                 if (lfds[i].events & ~LX_POLL_SUPPORTED_EVENTS) {
 170                         lx_unsupported("unsupported poll events requested: "
 171                             "events=0x%x", lfds[i].events);
 172                         return (-ENOTSUP);
 173                 }
 174 
 175                 sfds[i].fd = lfds[i].fd;
 176                 sfds[i].events = lfds[i].events & LX_POLL_COMMON_EVENTS;
 177                 if (lfds[i].events & LX_POLLWRNORM)
 178                         sfds[i].events |= POLLWRNORM;
 179                 if (lfds[i].events & LX_POLLWRBAND)
 180                         sfds[i].events |= POLLWRBAND;
 181                 sfds[i].revents = 0;
 182         }
 183 
 184         lx_debug("\tpoll(0x%p, %u, %d)", sfds, nfds, (int)p3);
 185 
 186         if ((rval = poll(sfds, nfds, (int)p3)) < 0)
 187                 return (-errno);
 188 
 189         /* Convert the Solaris revents bitmask into the Linux equivalent */
 190         for (i = 0; i < nfds; i++) {
 191                 revents = sfds[i].revents & LX_POLL_COMMON_EVENTS;
 192                 if (sfds[i].revents & POLLWRBAND)
 193                         revents |= LX_POLLWRBAND;
 194 
 195                 /*
 196                  * Be carefull because on solaris POLLOUT and POLLWRNORM
 197                  * are defined to the same values but on linux they
 198                  * are not.
 199                  */
 200                 if (sfds[i].revents & POLLOUT) {
 201                         if ((lfds[i].events & LX_POLLOUT) == 0)
 202                                 revents &= ~LX_POLLOUT;
 203                         if (lfds[i].events & LX_POLLWRNORM)
 204                                 revents |= LX_POLLWRNORM;
 205                 }
 206 
 207                 lfds[i].revents = revents;
 208         }
 209 
 210         /* Copy out the results */
 211         if (uucopy(lfds, (void *)p1, fds_size) != 0)
 212                 return (-errno);
 213 
 214         return (rval);
 215 }