Annotation of embedaddon/curl/lib/select.c, revision 1.1

1.1     ! misho       1: /***************************************************************************
        !             2:  *                                  _   _ ____  _
        !             3:  *  Project                     ___| | | |  _ \| |
        !             4:  *                             / __| | | | |_) | |
        !             5:  *                            | (__| |_| |  _ <| |___
        !             6:  *                             \___|\___/|_| \_\_____|
        !             7:  *
        !             8:  * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
        !             9:  *
        !            10:  * This software is licensed as described in the file COPYING, which
        !            11:  * you should have received as part of this distribution. The terms
        !            12:  * are also available at https://curl.haxx.se/docs/copyright.html.
        !            13:  *
        !            14:  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
        !            15:  * copies of the Software, and permit persons to whom the Software is
        !            16:  * furnished to do so, under the terms of the COPYING file.
        !            17:  *
        !            18:  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
        !            19:  * KIND, either express or implied.
        !            20:  *
        !            21:  ***************************************************************************/
        !            22: 
        !            23: #include "curl_setup.h"
        !            24: 
        !            25: #ifdef HAVE_SYS_SELECT_H
        !            26: #include <sys/select.h>
        !            27: #elif defined(HAVE_UNISTD_H)
        !            28: #include <unistd.h>
        !            29: #endif
        !            30: 
        !            31: #if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE)
        !            32: #error "We can't compile without select() or poll() support."
        !            33: #endif
        !            34: 
        !            35: #if defined(__BEOS__) && !defined(__HAIKU__)
        !            36: /* BeOS has FD_SET defined in socket.h */
        !            37: #include <socket.h>
        !            38: #endif
        !            39: 
        !            40: #ifdef MSDOS
        !            41: #include <dos.h>  /* delay() */
        !            42: #endif
        !            43: 
        !            44: #ifdef __VXWORKS__
        !            45: #include <strings.h>  /* bzero() in FD_SET */
        !            46: #endif
        !            47: 
        !            48: #include <curl/curl.h>
        !            49: 
        !            50: #include "urldata.h"
        !            51: #include "connect.h"
        !            52: #include "select.h"
        !            53: #include "warnless.h"
        !            54: 
        !            55: /* Convenience local macros */
        !            56: #define ELAPSED_MS() (int)Curl_timediff(Curl_now(), initial_tv)
        !            57: 
        !            58: /*
        !            59:  * Internal function used for waiting a specific amount of ms
        !            60:  * in Curl_socket_check() and Curl_poll() when no file descriptor
        !            61:  * is provided to wait on, just being used to delay execution.
        !            62:  * WinSock select() and poll() timeout mechanisms need a valid
        !            63:  * socket descriptor in a not null file descriptor set to work.
        !            64:  * Waiting indefinitely with this function is not allowed, a
        !            65:  * zero or negative timeout value will return immediately.
        !            66:  * Timeout resolution, accuracy, as well as maximum supported
        !            67:  * value is system dependent, neither factor is a citical issue
        !            68:  * for the intended use of this function in the library.
        !            69:  *
        !            70:  * Return values:
        !            71:  *   -1 = system call error, invalid timeout value, or interrupted
        !            72:  *    0 = specified timeout has elapsed
        !            73:  */
        !            74: int Curl_wait_ms(int timeout_ms)
        !            75: {
        !            76:   int r = 0;
        !            77: 
        !            78:   if(!timeout_ms)
        !            79:     return 0;
        !            80:   if(timeout_ms < 0) {
        !            81:     SET_SOCKERRNO(EINVAL);
        !            82:     return -1;
        !            83:   }
        !            84: #if defined(MSDOS)
        !            85:   delay(timeout_ms);
        !            86: #elif defined(USE_WINSOCK)
        !            87:   Sleep(timeout_ms);
        !            88: #else
        !            89: #if defined(HAVE_POLL_FINE)
        !            90:   r = poll(NULL, 0, timeout_ms);
        !            91: #else
        !            92:   {
        !            93:     struct timeval pending_tv;
        !            94:     pending_tv.tv_sec = timeout_ms / 1000;
        !            95:     pending_tv.tv_usec = (timeout_ms % 1000) * 1000;
        !            96:     r = select(0, NULL, NULL, NULL, &pending_tv);
        !            97:   }
        !            98: #endif /* HAVE_POLL_FINE */
        !            99: #endif /* USE_WINSOCK */
        !           100:   if(r)
        !           101:     r = -1;
        !           102:   return r;
        !           103: }
        !           104: 
        !           105: /*
        !           106:  * This is a wrapper around select() to aid in Windows compatibility.
        !           107:  * A negative timeout value makes this function wait indefinitely,
        !           108:  * unless no valid file descriptor is given, when this happens the
        !           109:  * negative timeout is ignored and the function times out immediately.
        !           110:  *
        !           111:  * Return values:
        !           112:  *   -1 = system call error or fd >= FD_SETSIZE
        !           113:  *    0 = timeout
        !           114:  *    N = number of signalled file descriptors
        !           115:  */
        !           116: int Curl_select(curl_socket_t maxfd,
        !           117:                 fd_set *fds_read,
        !           118:                 fd_set *fds_write,
        !           119:                 fd_set *fds_err,
        !           120:                 time_t timeout_ms)     /* milliseconds to wait */
        !           121: {
        !           122:   struct timeval pending_tv;
        !           123:   struct timeval *ptimeout;
        !           124:   int pending_ms;
        !           125:   int r;
        !           126: 
        !           127: #if SIZEOF_TIME_T != SIZEOF_INT
        !           128:   /* wrap-around precaution */
        !           129:   if(timeout_ms >= INT_MAX)
        !           130:     timeout_ms = INT_MAX;
        !           131: #endif
        !           132: 
        !           133: #ifdef USE_WINSOCK
        !           134:   /* WinSock select() can't handle zero events.  See the comment below. */
        !           135:   if((!fds_read || fds_read->fd_count == 0) &&
        !           136:      (!fds_write || fds_write->fd_count == 0) &&
        !           137:      (!fds_err || fds_err->fd_count == 0)) {
        !           138:     r = Curl_wait_ms((int)timeout_ms);
        !           139:     return r;
        !           140:   }
        !           141: #endif
        !           142: 
        !           143:   ptimeout = &pending_tv;
        !           144: 
        !           145:   if(timeout_ms < 0) {
        !           146:     ptimeout = NULL;
        !           147:   }
        !           148:   else if(timeout_ms > 0) {
        !           149:     pending_ms = (int)timeout_ms;
        !           150:     pending_tv.tv_sec = pending_ms / 1000;
        !           151:     pending_tv.tv_usec = (pending_ms % 1000) * 1000;
        !           152:   }
        !           153:   else if(!timeout_ms) {
        !           154:     pending_tv.tv_sec = 0;
        !           155:     pending_tv.tv_usec = 0;
        !           156:   }
        !           157: 
        !           158: #ifdef USE_WINSOCK
        !           159:   /* WinSock select() must not be called with an fd_set that contains zero
        !           160:     fd flags, or it will return WSAEINVAL.  But, it also can't be called
        !           161:     with no fd_sets at all!  From the documentation:
        !           162: 
        !           163:     Any two of the parameters, readfds, writefds, or exceptfds, can be
        !           164:     given as null. At least one must be non-null, and any non-null
        !           165:     descriptor set must contain at least one handle to a socket.
        !           166: 
        !           167:     It is unclear why WinSock doesn't just handle this for us instead of
        !           168:     calling this an error.
        !           169:   */
        !           170:   r = select((int)maxfd + 1,
        !           171:              fds_read && fds_read->fd_count ? fds_read : NULL,
        !           172:              fds_write && fds_write->fd_count ? fds_write : NULL,
        !           173:              fds_err && fds_err->fd_count ? fds_err : NULL, ptimeout);
        !           174: #else
        !           175:   r = select((int)maxfd + 1, fds_read, fds_write, fds_err, ptimeout);
        !           176: #endif
        !           177: 
        !           178:   return r;
        !           179: }
        !           180: 
        !           181: /*
        !           182:  * Wait for read or write events on a set of file descriptors. It uses poll()
        !           183:  * when a fine poll() is available, in order to avoid limits with FD_SETSIZE,
        !           184:  * otherwise select() is used.  An error is returned if select() is being used
        !           185:  * and a file descriptor is too large for FD_SETSIZE.
        !           186:  *
        !           187:  * A negative timeout value makes this function wait indefinitely,
        !           188:  * unless no valid file descriptor is given, when this happens the
        !           189:  * negative timeout is ignored and the function times out immediately.
        !           190:  *
        !           191:  * Return values:
        !           192:  *   -1 = system call error or fd >= FD_SETSIZE
        !           193:  *    0 = timeout
        !           194:  *    [bitmask] = action as described below
        !           195:  *
        !           196:  * CURL_CSELECT_IN - first socket is readable
        !           197:  * CURL_CSELECT_IN2 - second socket is readable
        !           198:  * CURL_CSELECT_OUT - write socket is writable
        !           199:  * CURL_CSELECT_ERR - an error condition occurred
        !           200:  */
        !           201: int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */
        !           202:                       curl_socket_t readfd1,
        !           203:                       curl_socket_t writefd, /* socket to write to */
        !           204:                       timediff_t timeout_ms)     /* milliseconds to wait */
        !           205: {
        !           206: #ifdef HAVE_POLL_FINE
        !           207:   struct pollfd pfd[3];
        !           208:   int pending_ms;
        !           209:   int num;
        !           210: #else
        !           211:   fd_set fds_read;
        !           212:   fd_set fds_write;
        !           213:   fd_set fds_err;
        !           214:   curl_socket_t maxfd;
        !           215: #endif
        !           216:   int r;
        !           217:   int ret;
        !           218: 
        !           219: #if SIZEOF_TIME_T != SIZEOF_INT
        !           220:   /* wrap-around precaution */
        !           221:   if(timeout_ms >= INT_MAX)
        !           222:     timeout_ms = INT_MAX;
        !           223: #endif
        !           224: 
        !           225:   if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) &&
        !           226:      (writefd == CURL_SOCKET_BAD)) {
        !           227:     /* no sockets, just wait */
        !           228:     r = Curl_wait_ms((int)timeout_ms);
        !           229:     return r;
        !           230:   }
        !           231: 
        !           232:   /* Avoid initial timestamp, avoid Curl_now() call, when elapsed
        !           233:      time in this function does not need to be measured. This happens
        !           234:      when function is called with a zero timeout or a negative timeout
        !           235:      value indicating a blocking call should be performed. */
        !           236: 
        !           237: #ifdef HAVE_POLL_FINE
        !           238: 
        !           239:   num = 0;
        !           240:   if(readfd0 != CURL_SOCKET_BAD) {
        !           241:     pfd[num].fd = readfd0;
        !           242:     pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
        !           243:     pfd[num].revents = 0;
        !           244:     num++;
        !           245:   }
        !           246:   if(readfd1 != CURL_SOCKET_BAD) {
        !           247:     pfd[num].fd = readfd1;
        !           248:     pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
        !           249:     pfd[num].revents = 0;
        !           250:     num++;
        !           251:   }
        !           252:   if(writefd != CURL_SOCKET_BAD) {
        !           253:     pfd[num].fd = writefd;
        !           254:     pfd[num].events = POLLWRNORM|POLLOUT;
        !           255:     pfd[num].revents = 0;
        !           256:     num++;
        !           257:   }
        !           258: 
        !           259:   if(timeout_ms > 0)
        !           260:     pending_ms = (int)timeout_ms;
        !           261:   else if(timeout_ms < 0)
        !           262:     pending_ms = -1;
        !           263:   else
        !           264:     pending_ms = 0;
        !           265:   r = poll(pfd, num, pending_ms);
        !           266: 
        !           267:   if(r < 0)
        !           268:     return -1;
        !           269:   if(r == 0)
        !           270:     return 0;
        !           271: 
        !           272:   ret = 0;
        !           273:   num = 0;
        !           274:   if(readfd0 != CURL_SOCKET_BAD) {
        !           275:     if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
        !           276:       ret |= CURL_CSELECT_IN;
        !           277:     if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
        !           278:       ret |= CURL_CSELECT_ERR;
        !           279:     num++;
        !           280:   }
        !           281:   if(readfd1 != CURL_SOCKET_BAD) {
        !           282:     if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
        !           283:       ret |= CURL_CSELECT_IN2;
        !           284:     if(pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
        !           285:       ret |= CURL_CSELECT_ERR;
        !           286:     num++;
        !           287:   }
        !           288:   if(writefd != CURL_SOCKET_BAD) {
        !           289:     if(pfd[num].revents & (POLLWRNORM|POLLOUT))
        !           290:       ret |= CURL_CSELECT_OUT;
        !           291:     if(pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
        !           292:       ret |= CURL_CSELECT_ERR;
        !           293:   }
        !           294: 
        !           295:   return ret;
        !           296: 
        !           297: #else  /* HAVE_POLL_FINE */
        !           298: 
        !           299:   FD_ZERO(&fds_err);
        !           300:   maxfd = (curl_socket_t)-1;
        !           301: 
        !           302:   FD_ZERO(&fds_read);
        !           303:   if(readfd0 != CURL_SOCKET_BAD) {
        !           304:     VERIFY_SOCK(readfd0);
        !           305:     FD_SET(readfd0, &fds_read);
        !           306:     FD_SET(readfd0, &fds_err);
        !           307:     maxfd = readfd0;
        !           308:   }
        !           309:   if(readfd1 != CURL_SOCKET_BAD) {
        !           310:     VERIFY_SOCK(readfd1);
        !           311:     FD_SET(readfd1, &fds_read);
        !           312:     FD_SET(readfd1, &fds_err);
        !           313:     if(readfd1 > maxfd)
        !           314:       maxfd = readfd1;
        !           315:   }
        !           316: 
        !           317:   FD_ZERO(&fds_write);
        !           318:   if(writefd != CURL_SOCKET_BAD) {
        !           319:     VERIFY_SOCK(writefd);
        !           320:     FD_SET(writefd, &fds_write);
        !           321:     FD_SET(writefd, &fds_err);
        !           322:     if(writefd > maxfd)
        !           323:       maxfd = writefd;
        !           324:   }
        !           325: 
        !           326:   /* We know that we have at least one bit set in at least two fd_sets in
        !           327:      this case, but we may have no bits set in either fds_read or fd_write,
        !           328:      so check for that and handle it.  Luckily, with WinSock, we can _also_
        !           329:      ask how many bits are set on an fd_set.
        !           330: 
        !           331:      Note also that WinSock ignores the first argument, so we don't worry
        !           332:      about the fact that maxfd is computed incorrectly with WinSock (since
        !           333:      curl_socket_t is unsigned in such cases and thus -1 is the largest
        !           334:      value).
        !           335:   */
        !           336:   r = Curl_select(maxfd, &fds_read, &fds_write, &fds_err, (time_t)timeout_ms);
        !           337: 
        !           338:   if(r < 0)
        !           339:     return -1;
        !           340:   if(r == 0)
        !           341:     return 0;
        !           342: 
        !           343:   ret = 0;
        !           344:   if(readfd0 != CURL_SOCKET_BAD) {
        !           345:     if(FD_ISSET(readfd0, &fds_read))
        !           346:       ret |= CURL_CSELECT_IN;
        !           347:     if(FD_ISSET(readfd0, &fds_err))
        !           348:       ret |= CURL_CSELECT_ERR;
        !           349:   }
        !           350:   if(readfd1 != CURL_SOCKET_BAD) {
        !           351:     if(FD_ISSET(readfd1, &fds_read))
        !           352:       ret |= CURL_CSELECT_IN2;
        !           353:     if(FD_ISSET(readfd1, &fds_err))
        !           354:       ret |= CURL_CSELECT_ERR;
        !           355:   }
        !           356:   if(writefd != CURL_SOCKET_BAD) {
        !           357:     if(FD_ISSET(writefd, &fds_write))
        !           358:       ret |= CURL_CSELECT_OUT;
        !           359:     if(FD_ISSET(writefd, &fds_err))
        !           360:       ret |= CURL_CSELECT_ERR;
        !           361:   }
        !           362: 
        !           363:   return ret;
        !           364: 
        !           365: #endif  /* HAVE_POLL_FINE */
        !           366: 
        !           367: }
        !           368: 
        !           369: /*
        !           370:  * This is a wrapper around poll().  If poll() does not exist, then
        !           371:  * select() is used instead.  An error is returned if select() is
        !           372:  * being used and a file descriptor is too large for FD_SETSIZE.
        !           373:  * A negative timeout value makes this function wait indefinitely,
        !           374:  * unless no valid file descriptor is given, when this happens the
        !           375:  * negative timeout is ignored and the function times out immediately.
        !           376:  *
        !           377:  * Return values:
        !           378:  *   -1 = system call error or fd >= FD_SETSIZE
        !           379:  *    0 = timeout
        !           380:  *    N = number of structures with non zero revent fields
        !           381:  */
        !           382: int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
        !           383: {
        !           384: #ifdef HAVE_POLL_FINE
        !           385:   int pending_ms;
        !           386: #else
        !           387:   fd_set fds_read;
        !           388:   fd_set fds_write;
        !           389:   fd_set fds_err;
        !           390:   curl_socket_t maxfd;
        !           391: #endif
        !           392:   bool fds_none = TRUE;
        !           393:   unsigned int i;
        !           394:   int r;
        !           395: 
        !           396:   if(ufds) {
        !           397:     for(i = 0; i < nfds; i++) {
        !           398:       if(ufds[i].fd != CURL_SOCKET_BAD) {
        !           399:         fds_none = FALSE;
        !           400:         break;
        !           401:       }
        !           402:     }
        !           403:   }
        !           404:   if(fds_none) {
        !           405:     r = Curl_wait_ms(timeout_ms);
        !           406:     return r;
        !           407:   }
        !           408: 
        !           409:   /* Avoid initial timestamp, avoid Curl_now() call, when elapsed
        !           410:      time in this function does not need to be measured. This happens
        !           411:      when function is called with a zero timeout or a negative timeout
        !           412:      value indicating a blocking call should be performed. */
        !           413: 
        !           414: #ifdef HAVE_POLL_FINE
        !           415: 
        !           416:   if(timeout_ms > 0)
        !           417:     pending_ms = timeout_ms;
        !           418:   else if(timeout_ms < 0)
        !           419:     pending_ms = -1;
        !           420:   else
        !           421:     pending_ms = 0;
        !           422:   r = poll(ufds, nfds, pending_ms);
        !           423: 
        !           424:   if(r < 0)
        !           425:     return -1;
        !           426:   if(r == 0)
        !           427:     return 0;
        !           428: 
        !           429:   for(i = 0; i < nfds; i++) {
        !           430:     if(ufds[i].fd == CURL_SOCKET_BAD)
        !           431:       continue;
        !           432:     if(ufds[i].revents & POLLHUP)
        !           433:       ufds[i].revents |= POLLIN;
        !           434:     if(ufds[i].revents & POLLERR)
        !           435:       ufds[i].revents |= (POLLIN|POLLOUT);
        !           436:   }
        !           437: 
        !           438: #else  /* HAVE_POLL_FINE */
        !           439: 
        !           440:   FD_ZERO(&fds_read);
        !           441:   FD_ZERO(&fds_write);
        !           442:   FD_ZERO(&fds_err);
        !           443:   maxfd = (curl_socket_t)-1;
        !           444: 
        !           445:   for(i = 0; i < nfds; i++) {
        !           446:     ufds[i].revents = 0;
        !           447:     if(ufds[i].fd == CURL_SOCKET_BAD)
        !           448:       continue;
        !           449:     VERIFY_SOCK(ufds[i].fd);
        !           450:     if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI|
        !           451:                           POLLRDNORM|POLLWRNORM|POLLRDBAND)) {
        !           452:       if(ufds[i].fd > maxfd)
        !           453:         maxfd = ufds[i].fd;
        !           454:       if(ufds[i].events & (POLLRDNORM|POLLIN))
        !           455:         FD_SET(ufds[i].fd, &fds_read);
        !           456:       if(ufds[i].events & (POLLWRNORM|POLLOUT))
        !           457:         FD_SET(ufds[i].fd, &fds_write);
        !           458:       if(ufds[i].events & (POLLRDBAND|POLLPRI))
        !           459:         FD_SET(ufds[i].fd, &fds_err);
        !           460:     }
        !           461:   }
        !           462: 
        !           463:   r = Curl_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms);
        !           464: 
        !           465:   if(r < 0)
        !           466:     return -1;
        !           467:   if(r == 0)
        !           468:     return 0;
        !           469: 
        !           470:   r = 0;
        !           471:   for(i = 0; i < nfds; i++) {
        !           472:     ufds[i].revents = 0;
        !           473:     if(ufds[i].fd == CURL_SOCKET_BAD)
        !           474:       continue;
        !           475:     if(FD_ISSET(ufds[i].fd, &fds_read))
        !           476:       ufds[i].revents |= POLLIN;
        !           477:     if(FD_ISSET(ufds[i].fd, &fds_write))
        !           478:       ufds[i].revents |= POLLOUT;
        !           479:     if(FD_ISSET(ufds[i].fd, &fds_err))
        !           480:       ufds[i].revents |= POLLPRI;
        !           481:     if(ufds[i].revents != 0)
        !           482:       r++;
        !           483:   }
        !           484: 
        !           485: #endif  /* HAVE_POLL_FINE */
        !           486: 
        !           487:   return r;
        !           488: }
        !           489: 
        !           490: #ifdef TPF
        !           491: /*
        !           492:  * This is a replacement for select() on the TPF platform.
        !           493:  * It is used whenever libcurl calls select().
        !           494:  * The call below to tpf_process_signals() is required because
        !           495:  * TPF's select calls are not signal interruptible.
        !           496:  *
        !           497:  * Return values are the same as select's.
        !           498:  */
        !           499: int tpf_select_libcurl(int maxfds, fd_set *reads, fd_set *writes,
        !           500:                        fd_set *excepts, struct timeval *tv)
        !           501: {
        !           502:    int rc;
        !           503: 
        !           504:    rc = tpf_select_bsd(maxfds, reads, writes, excepts, tv);
        !           505:    tpf_process_signals();
        !           506:    return rc;
        !           507: }
        !           508: #endif /* TPF */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>