Primarily this fixes an off-by-one buffer overwrite (rare but still existing).
[platform/upstream/curl.git] / lib / select.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2007, 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 http://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  * $Id$
22  ***************************************************************************/
23
24 #include "setup.h"
25
26 #ifdef HAVE_SYS_SELECT_H
27 #include <sys/select.h>
28 #endif
29 #ifdef HAVE_SYS_TIME_H
30 #include <sys/time.h>
31 #endif
32
33 #if !defined(HAVE_SELECT) && !defined(HAVE_POLL_FINE)
34 #error "We can't compile without select() or poll() support."
35 #endif
36
37 #ifdef __BEOS__
38 /* BeOS has FD_SET defined in socket.h */
39 #include <socket.h>
40 #endif
41
42 #ifdef MSDOS
43 #include <dos.h>  /* delay() */
44 #endif
45
46 #include <curl/curl.h>
47
48 #include "urldata.h"
49 #include "connect.h"
50 #include "select.h"
51
52 /* Winsock and TPF sockets are not in range [0..FD_SETSIZE-1] */
53
54 #if defined(USE_WINSOCK) || defined(TPF)
55 #define VERIFY_SOCK(x) do { } while (0)
56 #define VERIFY_NFDS(x) do { } while (0)
57 #else
58 #define VALID_SOCK(s) (((s) >= 0) && ((s) < FD_SETSIZE))
59 #define VERIFY_SOCK(x) do { \
60   if(!VALID_SOCK(x)) { \
61     SET_SOCKERRNO(EINVAL); \
62     return -1; \
63   } \
64 } while(0)
65 #define VALID_NFDS(n) (((n) >= 0) && ((n) <= FD_SETSIZE))
66 #define VERIFY_NFDS(x) do { \
67   if(!VALID_NFDS(x)) { \
68     SET_SOCKERRNO(EINVAL); \
69     return -1; \
70   } \
71 } while(0)
72 #endif
73
74 /* Convenience local macros */
75
76 #define elapsed_ms  (int)curlx_tvdiff(curlx_tvnow(), initial_tv)
77
78 #ifdef CURL_ACKNOWLEDGE_EINTR
79 #define error_is_EINTR  (error == EINTR)
80 #else
81 #define error_is_EINTR  (0)
82 #endif
83
84 #define SMALL_POLLNFDS  0x20
85
86 /*
87  * Internal function used for waiting a specific amount of ms
88  * in Curl_socket_ready() and Curl_poll() when no file descriptor
89  * is provided to wait on, just being used to delay execution.
90  * WinSock select() and poll() timeout mechanisms need a valid
91  * socket descriptor in a not null file descriptor set to work.
92  * Waiting indefinitely with this function is not allowed, a
93  * zero or negative timeout value will return immediately.
94  * Timeout resolution, accuracy, as well as maximum supported
95  * value is system dependant, neither factor is a citical issue
96  * for the intended use of this function in the library.
97  * On non-DOS and non-Winsock platforms, when compiled with
98  * CURL_ACKNOWLEDGE_EINTR defined, EINTR condition is honored
99  * and function might exit early without awaiting full timeout,
100  * otherwise EINTR will be ignored and full timeout will elapse.
101  *
102  * Return values:
103  *   -1 = system call error, invalid timeout value, or interrupted
104  *    0 = specified timeout has elapsed
105  */
106 static int wait_ms(int timeout_ms)
107 {
108 #if !defined(MSDOS) && !defined(USE_WINSOCK)
109 #ifndef HAVE_POLL_FINE
110   struct timeval pending_tv;
111 #endif
112   struct timeval initial_tv;
113   int pending_ms;
114   int error;
115 #endif
116   int r = 0;
117
118   if (!timeout_ms)
119     return 0;
120   if (timeout_ms < 0) {
121     SET_SOCKERRNO(EINVAL);
122     return -1;
123   }
124 #if defined(MSDOS)
125   delay(timeout_ms);
126 #elif defined(USE_WINSOCK)
127   Sleep(timeout_ms);
128 #else
129   pending_ms = timeout_ms;
130   initial_tv = curlx_tvnow();
131   do {
132 #if defined(HAVE_POLL_FINE)
133     r = poll(NULL, 0, pending_ms);
134 #else
135     pending_tv.tv_sec = pending_ms / 1000;
136     pending_tv.tv_usec = (pending_ms % 1000) * 1000;
137     r = select(0, NULL, NULL, NULL, &pending_tv);
138 #endif /* HAVE_POLL_FINE */
139     if (r != -1)
140       break;
141     error = SOCKERRNO;
142     if ((error == EINVAL) || error_is_EINTR)
143       break;
144     pending_ms = timeout_ms - elapsed_ms;
145     if (pending_ms <= 0)
146       break;
147   } while (r == -1);
148 #endif /* USE_WINSOCK */
149   if (r)
150     r = -1;
151   return r;
152 }
153
154 /*
155  * This is an internal function used for waiting for read or write
156  * events on a pair of file descriptors.  It uses poll() when a fine
157  * poll() is available, in order to avoid limits with FD_SETSIZE,
158  * otherwise select() is used.  An error is returned if select() is
159  * being used and a file descriptor is too large for FD_SETSIZE.
160  * A negative timeout value makes this function wait indefinitely,
161  * unles no valid file descriptor is given, when this happens the
162  * negative timeout is ignored and the function times out immediately.
163  * When compiled with CURL_ACKNOWLEDGE_EINTR defined, EINTR condition
164  * is honored and function might exit early without awaiting timeout,
165  * otherwise EINTR will be ignored.
166  *
167  * Return values:
168  *   -1 = system call error or fd >= FD_SETSIZE
169  *    0 = timeout
170  *    CURL_CSELECT_IN | CURL_CSELECT_OUT | CURL_CSELECT_ERR
171  */
172 int Curl_socket_ready(curl_socket_t readfd, curl_socket_t writefd,
173                       int timeout_ms)
174 {
175 #ifdef HAVE_POLL_FINE
176   struct pollfd pfd[2];
177   int num;
178 #else
179   struct timeval pending_tv;
180   struct timeval *ptimeout;
181   fd_set fds_read;
182   fd_set fds_write;
183   fd_set fds_err;
184   curl_socket_t maxfd;
185 #endif
186   struct timeval initial_tv;
187   int pending_ms = 0;
188   int error;
189   int r;
190   int ret;
191
192   if((readfd == CURL_SOCKET_BAD) && (writefd == CURL_SOCKET_BAD)) {
193     r = wait_ms(timeout_ms);
194     return r;
195   }
196
197   /* Avoid initial timestamp, avoid gettimeofday() call, when elapsed
198      time in this function does not need to be measured. This happens
199      when function is called with a zero timeout or a negative timeout
200      value indicating a blocking call should be performed. */
201
202   if (timeout_ms > 0) {
203     pending_ms = timeout_ms;
204     initial_tv = curlx_tvnow();
205   }
206
207 #ifdef HAVE_POLL_FINE
208
209   num = 0;
210   if (readfd != CURL_SOCKET_BAD) {
211     pfd[num].fd = readfd;
212     pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
213     pfd[num].revents = 0;
214     num++;
215   }
216   if (writefd != CURL_SOCKET_BAD) {
217     pfd[num].fd = writefd;
218     pfd[num].events = POLLWRNORM|POLLOUT;
219     pfd[num].revents = 0;
220     num++;
221   }
222
223   do {
224     if (timeout_ms < 0)
225       pending_ms = -1;
226     else if (!timeout_ms)
227       pending_ms = 0;
228     r = poll(pfd, num, pending_ms);
229     if (r != -1)
230       break;
231     error = SOCKERRNO;
232     if ((error == EINVAL) || error_is_EINTR)
233       break;
234     if (timeout_ms > 0) {
235       pending_ms = timeout_ms - elapsed_ms;
236       if (pending_ms <= 0)
237         break;
238     }
239   } while (r == -1);
240
241   if (r < 0)
242     return -1;
243   if (r == 0)
244     return 0;
245
246   ret = 0;
247   num = 0;
248   if (readfd != CURL_SOCKET_BAD) {
249     if (pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP))
250       ret |= CURL_CSELECT_IN;
251     if (pfd[num].revents & (POLLRDBAND|POLLPRI|POLLNVAL))
252       ret |= CURL_CSELECT_ERR;
253     num++;
254   }
255   if (writefd != CURL_SOCKET_BAD) {
256     if (pfd[num].revents & (POLLWRNORM|POLLOUT))
257       ret |= CURL_CSELECT_OUT;
258     if (pfd[num].revents & (POLLERR|POLLHUP|POLLNVAL))
259       ret |= CURL_CSELECT_ERR;
260   }
261
262   return ret;
263
264 #else  /* HAVE_POLL_FINE */
265
266   FD_ZERO(&fds_err);
267   maxfd = (curl_socket_t)-1;
268
269   FD_ZERO(&fds_read);
270   if (readfd != CURL_SOCKET_BAD) {
271     VERIFY_SOCK(readfd);
272     FD_SET(readfd, &fds_read);
273     FD_SET(readfd, &fds_err);
274     maxfd = readfd;
275   }
276
277   FD_ZERO(&fds_write);
278   if (writefd != CURL_SOCKET_BAD) {
279     VERIFY_SOCK(writefd);
280     FD_SET(writefd, &fds_write);
281     FD_SET(writefd, &fds_err);
282     if (writefd > maxfd)
283       maxfd = writefd;
284   }
285
286   ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
287
288   do {
289     if (timeout_ms > 0) {
290       pending_tv.tv_sec = pending_ms / 1000;
291       pending_tv.tv_usec = (pending_ms % 1000) * 1000;
292     }
293     else if (!timeout_ms) {
294       pending_tv.tv_sec = 0;
295       pending_tv.tv_usec = 0;
296     }
297     r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
298     if (r != -1)
299       break;
300     error = SOCKERRNO;
301     if ((error == EINVAL) || (error == EBADF) || error_is_EINTR)
302       break;
303     if (timeout_ms > 0) {
304       pending_ms = timeout_ms - elapsed_ms;
305       if (pending_ms <= 0)
306         break;
307     }
308   } while (r == -1);
309
310   if (r < 0)
311     return -1;
312   if (r == 0)
313     return 0;
314
315   ret = 0;
316   if (readfd != CURL_SOCKET_BAD) {
317     if (FD_ISSET(readfd, &fds_read))
318       ret |= CURL_CSELECT_IN;
319     if (FD_ISSET(readfd, &fds_err))
320       ret |= CURL_CSELECT_ERR;
321   }
322   if (writefd != CURL_SOCKET_BAD) {
323     if (FD_ISSET(writefd, &fds_write))
324       ret |= CURL_CSELECT_OUT;
325     if (FD_ISSET(writefd, &fds_err))
326       ret |= CURL_CSELECT_ERR;
327   }
328
329   return ret;
330
331 #endif  /* HAVE_POLL_FINE */
332
333 }
334
335 /*
336  * This is a wrapper around poll().  If poll() does not exist, then
337  * select() is used instead.  An error is returned if select() is
338  * being used and a file descriptor is too large for FD_SETSIZE.
339  * A negative timeout value makes this function wait indefinitely,
340  * unles no valid file descriptor is given, when this happens the
341  * negative timeout is ignored and the function times out immediately.
342  * When compiled with CURL_ACKNOWLEDGE_EINTR defined, EINTR condition
343  * is honored and function might exit early without awaiting timeout,
344  * otherwise EINTR will be ignored.
345  *
346  * Return values:
347  *   -1 = system call error or fd >= FD_SETSIZE
348  *    0 = timeout
349  *    N = number of structures with non zero revent fields
350  */
351 int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms)
352 {
353 #ifndef HAVE_POLL_FINE
354   struct timeval pending_tv;
355   struct timeval *ptimeout;
356   fd_set fds_read;
357   fd_set fds_write;
358   fd_set fds_err;
359   curl_socket_t maxfd;
360 #endif
361   struct timeval initial_tv;
362   bool fds_none = TRUE;
363   unsigned int i;
364   int pending_ms = 0;
365   int error;
366   int r;
367
368   if (ufds) {
369     for (i = 0; i < nfds; i++) {
370       if (ufds[i].fd != CURL_SOCKET_BAD) {
371         fds_none = FALSE;
372         break;
373       }
374     }
375   }
376   if (fds_none) {
377     r = wait_ms(timeout_ms);
378     return r;
379   }
380
381   /* Avoid initial timestamp, avoid gettimeofday() call, when elapsed
382      time in this function does not need to be measured. This happens
383      when function is called with a zero timeout or a negative timeout
384      value indicating a blocking call should be performed. */
385
386   if (timeout_ms > 0) {
387     pending_ms = timeout_ms;
388     initial_tv = curlx_tvnow();
389   }
390
391 #ifdef HAVE_POLL_FINE
392
393   do {
394     if (timeout_ms < 0)
395       pending_ms = -1;
396     else if (!timeout_ms)
397       pending_ms = 0;
398     r = poll(ufds, nfds, pending_ms);
399     if (r != -1)
400       break;
401     error = SOCKERRNO;
402     if ((error == EINVAL) || error_is_EINTR)
403       break;
404     if (timeout_ms > 0) {
405       pending_ms = timeout_ms - elapsed_ms;
406       if (pending_ms <= 0)
407         break;
408     }
409   } while (r == -1);
410
411 #else  /* HAVE_POLL_FINE */
412
413   FD_ZERO(&fds_read);
414   FD_ZERO(&fds_write);
415   FD_ZERO(&fds_err);
416   maxfd = (curl_socket_t)-1;
417
418   for (i = 0; i < nfds; i++) {
419     ufds[i].revents = 0;
420     if (ufds[i].fd == CURL_SOCKET_BAD)
421       continue;
422     VERIFY_SOCK(ufds[i].fd);
423     if (ufds[i].events & (POLLIN|POLLOUT|POLLPRI|
424                           POLLRDNORM|POLLWRNORM|POLLRDBAND)) {
425       if (ufds[i].fd > maxfd)
426         maxfd = ufds[i].fd;
427       if (ufds[i].events & (POLLRDNORM|POLLIN))
428         FD_SET(ufds[i].fd, &fds_read);
429       if (ufds[i].events & (POLLWRNORM|POLLOUT))
430         FD_SET(ufds[i].fd, &fds_write);
431       if (ufds[i].events & (POLLRDBAND|POLLPRI))
432         FD_SET(ufds[i].fd, &fds_err);
433     }
434   }
435
436   ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
437
438   do {
439     if (timeout_ms > 0) {
440       pending_tv.tv_sec = pending_ms / 1000;
441       pending_tv.tv_usec = (pending_ms % 1000) * 1000;
442     }
443     else if (!timeout_ms) {
444       pending_tv.tv_sec = 0;
445       pending_tv.tv_usec = 0;
446     }
447     r = select((int)maxfd + 1, &fds_read, &fds_write, &fds_err, ptimeout);
448     if (r != -1)
449       break;
450     error = SOCKERRNO;
451     if ((error == EINVAL) || (error == EBADF) || error_is_EINTR)
452       break;
453     if (timeout_ms > 0) {
454       pending_ms = timeout_ms - elapsed_ms;
455       if (pending_ms <= 0)
456         break;
457     }
458   } while (r == -1);
459
460   if (r < 0)
461     return -1;
462   if (r == 0)
463     return 0;
464
465   r = 0;
466   for (i = 0; i < nfds; i++) {
467     ufds[i].revents = 0;
468     if (ufds[i].fd == CURL_SOCKET_BAD)
469       continue;
470     if (FD_ISSET(ufds[i].fd, &fds_read))
471       ufds[i].revents |= POLLIN;
472     if (FD_ISSET(ufds[i].fd, &fds_write))
473       ufds[i].revents |= POLLOUT;
474     if (FD_ISSET(ufds[i].fd, &fds_err))
475       ufds[i].revents |= POLLPRI;
476     if (ufds[i].revents != 0)
477       r++;
478   }
479
480 #endif  /* HAVE_POLL_FINE */
481
482   return r;
483 }
484
485 /*
486  * This is a wrapper around select().  It uses poll() when a fine
487  * poll() is available, in order to avoid limits with FD_SETSIZE,
488  * otherwise select() is used.  An error is returned if select() is
489  * being used and a the number of file descriptors is larger than
490  * FD_SETSIZE.  A NULL timeout pointer makes this function wait
491  * indefinitely, unles no valid file descriptor is given, when this
492  * happens the NULL timeout is ignored and the function times out
493  * immediately.  When compiled with CURL_ACKNOWLEDGE_EINTR defined,
494  * EINTR condition is honored and function might exit early without
495  * awaiting timeout, otherwise EINTR will be ignored.
496  *
497  * Return values:
498  *   -1 = system call error or nfds > FD_SETSIZE
499  *    0 = timeout
500  *    N = number of file descriptors kept in file descriptor sets.
501  */
502 int Curl_select(int nfds,
503                 fd_set *fds_read, fd_set *fds_write, fd_set *fds_excep,
504                 struct timeval *timeout)
505 {
506   struct timeval initial_tv;
507   int timeout_ms;
508   int pending_ms = 0;
509   int error;
510   int r;
511 #ifdef HAVE_POLL_FINE
512   struct pollfd small_fds[SMALL_POLLNFDS];
513   struct pollfd *poll_fds;
514   int ix;
515   int fd;
516   int poll_nfds = 0;
517 #else
518   struct timeval pending_tv;
519   struct timeval *ptimeout;
520 #endif
521   int ret = 0;
522
523   if ((nfds < 0) ||
524      ((nfds > 0) && (!fds_read && !fds_write && !fds_excep))) {
525     SET_SOCKERRNO(EINVAL);
526     return -1;
527   }
528
529   if (timeout) {
530     if ((timeout->tv_sec < 0) ||
531         (timeout->tv_usec < 0) ||
532         (timeout->tv_usec >= 1000000)) {
533       SET_SOCKERRNO(EINVAL);
534       return -1;
535     }
536     timeout_ms = (int)(timeout->tv_sec * 1000) +
537       (int)(timeout->tv_usec / 1000);
538   }
539   else {
540     timeout_ms = -1;
541   }
542
543   if ((!nfds) || (!fds_read && !fds_write && !fds_excep)) {
544     r = wait_ms(timeout_ms);
545     return r;
546   }
547
548   /* Avoid initial timestamp, avoid gettimeofday() call, when elapsed
549      time in this function does not need to be measured. This happens
550      when function is called with a zero timeout in the timeval struct
551      referenced argument or when a NULL pointer is received as timeval
552      reference indicating a blocking call should be performed. */
553
554   if (timeout_ms > 0) {
555     pending_ms = timeout_ms;
556     initial_tv = curlx_tvnow();
557   }
558
559 #ifdef HAVE_POLL_FINE
560
561   if (fds_read || fds_write || fds_excep) {
562     fd = nfds;
563     while (fd--) {
564       if ((fds_read && (0 != FD_ISSET(fd, fds_read))) ||
565           (fds_write && (0 != FD_ISSET(fd, fds_write))) ||
566           (fds_excep && (0 != FD_ISSET(fd, fds_excep))))
567         poll_nfds++;
568     }
569   }
570
571   if (!poll_nfds)
572     poll_fds = NULL;
573   else if (poll_nfds <= SMALL_POLLNFDS)
574     poll_fds = small_fds;
575   else {
576     poll_fds = malloc(poll_nfds * sizeof(struct pollfd));
577     if (!poll_fds) {
578       SET_SOCKERRNO(ENOBUFS);
579       return -1;
580     }
581   }
582
583   if (poll_fds) {
584     int events;
585     ix = 0;
586     fd = nfds;
587     while (fd--) {
588       events = 0;
589       if (fds_read && (0 != FD_ISSET(fd, fds_read)))
590         events |= (POLLRDNORM|POLLIN);
591       if (fds_write && (0 != FD_ISSET(fd, fds_write)))
592         events |= (POLLWRNORM|POLLOUT);
593       if (fds_excep && (0 != FD_ISSET(fd, fds_excep)))
594         events |= (POLLRDBAND|POLLPRI);
595       if (events) {
596         poll_fds[ix].events = events;
597         poll_fds[ix].fd = fd;
598         poll_fds[ix].revents = 0;
599         ix++;
600         if(ix == poll_nfds)
601           /* since we know this is the total amount of descriptors with
602              interesting actions, we can skip the rest of the loop at this
603              point */
604           break;
605       }
606     }
607   }
608
609   do {
610     if (timeout_ms < 0)
611       pending_ms = -1;
612     else if (!timeout_ms)
613       pending_ms = 0;
614     r = poll(poll_fds, poll_nfds, pending_ms);
615     if (r != -1)
616       break;
617     error = SOCKERRNO;
618     if ((error == EINVAL) || error_is_EINTR)
619       break;
620     if (timeout_ms > 0) {
621       pending_ms = timeout_ms - elapsed_ms;
622       if (pending_ms <= 0)
623         break;
624     }
625   } while (r == -1);
626
627   if (r < 0)
628     ret = -1;
629
630   if (r > 0) {
631     ix = poll_nfds;
632     while (ix--) {
633       if (poll_fds[ix].revents & POLLNVAL) {
634         SET_SOCKERRNO(EBADF);
635         ret = -1;
636         break;
637       }
638     }
639   }
640
641   if (!ret) {
642     ix = poll_nfds;
643     while (ix--) {
644       if (fds_read && (0 != FD_ISSET(poll_fds[ix].fd, fds_read))) {
645         if (0 == (poll_fds[ix].revents & (POLLRDNORM|POLLERR|POLLHUP|POLLIN)))
646           FD_CLR(poll_fds[ix].fd, fds_read);
647         else
648           ret++;
649       }
650       if (fds_write && (0 != FD_ISSET(poll_fds[ix].fd, fds_write))) {
651         if (0 == (poll_fds[ix].revents & (POLLWRNORM|POLLERR|POLLHUP|POLLOUT)))
652           FD_CLR(poll_fds[ix].fd, fds_write);
653         else
654           ret++;
655       }
656       if (fds_excep && (0 != FD_ISSET(poll_fds[ix].fd, fds_excep))) {
657         if (0 == (poll_fds[ix].revents & (POLLRDBAND|POLLERR|POLLHUP|POLLPRI)))
658           FD_CLR(poll_fds[ix].fd, fds_excep);
659         else
660           ret++;
661       }
662     }
663   }
664
665   if (poll_fds && (poll_nfds > SMALL_POLLNFDS))
666     free(poll_fds);
667
668 #else  /* HAVE_POLL_FINE */
669
670   VERIFY_NFDS(nfds);
671
672   ptimeout = (timeout_ms < 0) ? NULL : &pending_tv;
673
674   do {
675     if (timeout_ms > 0) {
676       pending_tv.tv_sec = pending_ms / 1000;
677       pending_tv.tv_usec = (pending_ms % 1000) * 1000;
678     }
679     else if (!timeout_ms) {
680       pending_tv.tv_sec = 0;
681       pending_tv.tv_usec = 0;
682     }
683     r = select(nfds, fds_read, fds_write, fds_excep, ptimeout);
684     if (r != -1)
685       break;
686     error = SOCKERRNO;
687     if ((error == EINVAL) || (error == EBADF) || error_is_EINTR)
688       break;
689     if (timeout_ms > 0) {
690       pending_ms = timeout_ms - elapsed_ms;
691       if (pending_ms <= 0)
692         break;
693     }
694   } while (r == -1);
695
696   if (r < 0)
697     ret = -1;
698   else
699     ret = r;
700
701 #endif  /* HAVE_POLL_FINE */
702
703   return ret;
704 }
705
706 #ifdef TPF
707 /*
708  * This is a replacement for select() on the TPF platform.
709  * It is used whenever libcurl calls select().
710  * The call below to tpf_process_signals() is required because
711  * TPF's select calls are not signal interruptible.
712  *
713  * Return values are the same as select's.
714  */
715 int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes,
716                        fd_set* excepts, struct timeval* tv)
717 {
718    int rc;
719
720    rc = tpf_select_bsd(maxfds, reads, writes, excepts, tv);
721    tpf_process_signals();
722    return(rc);
723 }
724 #endif /* TPF */