Commit | Line | Data |
---|---|---|
3dac6181 AW |
1 | /* Emulation for select(2) |
2 | Contributed by Paolo Bonzini. | |
3 | ||
5e69ceb7 | 4 | Copyright 2008-2014 Free Software Foundation, Inc. |
3dac6181 AW |
5 | |
6 | This file is part of gnulib. | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2, or (at your option) | |
11 | any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License along | |
19 | with this program; if not, see <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | #include <config.h> | |
22 | #include <alloca.h> | |
23 | #include <assert.h> | |
24 | ||
25 | #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ | |
26 | /* Native Windows. */ | |
27 | ||
28 | #include <sys/types.h> | |
29 | #include <errno.h> | |
30 | #include <limits.h> | |
31 | ||
32 | #include <winsock2.h> | |
33 | #include <windows.h> | |
34 | #include <io.h> | |
35 | #include <stdio.h> | |
36 | #include <conio.h> | |
37 | #include <time.h> | |
38 | ||
39 | /* Get the overridden 'struct timeval'. */ | |
40 | #include <sys/time.h> | |
41 | ||
42 | #include "msvc-nothrow.h" | |
43 | ||
44 | #undef select | |
45 | ||
46 | struct bitset { | |
47 | unsigned char in[FD_SETSIZE / CHAR_BIT]; | |
48 | unsigned char out[FD_SETSIZE / CHAR_BIT]; | |
49 | }; | |
50 | ||
51 | /* Declare data structures for ntdll functions. */ | |
52 | typedef struct _FILE_PIPE_LOCAL_INFORMATION { | |
53 | ULONG NamedPipeType; | |
54 | ULONG NamedPipeConfiguration; | |
55 | ULONG MaximumInstances; | |
56 | ULONG CurrentInstances; | |
57 | ULONG InboundQuota; | |
58 | ULONG ReadDataAvailable; | |
59 | ULONG OutboundQuota; | |
60 | ULONG WriteQuotaAvailable; | |
61 | ULONG NamedPipeState; | |
62 | ULONG NamedPipeEnd; | |
63 | } FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; | |
64 | ||
65 | typedef struct _IO_STATUS_BLOCK | |
66 | { | |
67 | union { | |
68 | DWORD Status; | |
69 | PVOID Pointer; | |
70 | } u; | |
71 | ULONG_PTR Information; | |
72 | } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; | |
73 | ||
74 | typedef enum _FILE_INFORMATION_CLASS { | |
75 | FilePipeLocalInformation = 24 | |
76 | } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; | |
77 | ||
78 | typedef DWORD (WINAPI *PNtQueryInformationFile) | |
79 | (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS); | |
80 | ||
81 | #ifndef PIPE_BUF | |
82 | #define PIPE_BUF 512 | |
83 | #endif | |
84 | ||
85 | /* Optimized test whether a HANDLE refers to a console. | |
86 | See <http://lists.gnu.org/archive/html/bug-gnulib/2009-08/msg00065.html>. */ | |
87 | #define IsConsoleHandle(h) (((intptr_t) (h) & 3) == 3) | |
88 | ||
89 | static BOOL | |
90 | IsSocketHandle (HANDLE h) | |
91 | { | |
92 | WSANETWORKEVENTS ev; | |
93 | ||
94 | if (IsConsoleHandle (h)) | |
95 | return FALSE; | |
96 | ||
97 | /* Under Wine, it seems that getsockopt returns 0 for pipes too. | |
98 | WSAEnumNetworkEvents instead distinguishes the two correctly. */ | |
99 | ev.lNetworkEvents = 0xDEADBEEF; | |
100 | WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); | |
101 | return ev.lNetworkEvents != 0xDEADBEEF; | |
102 | } | |
103 | ||
104 | /* Compute output fd_sets for libc descriptor FD (whose Windows handle is | |
105 | H). */ | |
106 | ||
107 | static int | |
108 | windows_poll_handle (HANDLE h, int fd, | |
109 | struct bitset *rbits, | |
110 | struct bitset *wbits, | |
111 | struct bitset *xbits) | |
112 | { | |
113 | BOOL read, write, except; | |
114 | int i, ret; | |
115 | INPUT_RECORD *irbuffer; | |
116 | DWORD avail, nbuffer; | |
117 | BOOL bRet; | |
118 | IO_STATUS_BLOCK iosb; | |
119 | FILE_PIPE_LOCAL_INFORMATION fpli; | |
120 | static PNtQueryInformationFile NtQueryInformationFile; | |
121 | static BOOL once_only; | |
122 | ||
123 | read = write = except = FALSE; | |
124 | switch (GetFileType (h)) | |
125 | { | |
126 | case FILE_TYPE_DISK: | |
127 | read = TRUE; | |
128 | write = TRUE; | |
129 | break; | |
130 | ||
131 | case FILE_TYPE_PIPE: | |
132 | if (!once_only) | |
133 | { | |
134 | NtQueryInformationFile = (PNtQueryInformationFile) | |
135 | GetProcAddress (GetModuleHandle ("ntdll.dll"), | |
136 | "NtQueryInformationFile"); | |
137 | once_only = TRUE; | |
138 | } | |
139 | ||
140 | if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0) | |
141 | { | |
142 | if (avail) | |
143 | read = TRUE; | |
144 | } | |
145 | else if (GetLastError () == ERROR_BROKEN_PIPE) | |
146 | ; | |
147 | ||
148 | else | |
149 | { | |
150 | /* It was the write-end of the pipe. Check if it is writable. | |
151 | If NtQueryInformationFile fails, optimistically assume the pipe is | |
152 | writable. This could happen on Windows 9x, where | |
153 | NtQueryInformationFile is not available, or if we inherit a pipe | |
154 | that doesn't permit FILE_READ_ATTRIBUTES access on the write end | |
155 | (I think this should not happen since Windows XP SP2; WINE seems | |
156 | fine too). Otherwise, ensure that enough space is available for | |
157 | atomic writes. */ | |
158 | memset (&iosb, 0, sizeof (iosb)); | |
159 | memset (&fpli, 0, sizeof (fpli)); | |
160 | ||
161 | if (!NtQueryInformationFile | |
162 | || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli), | |
163 | FilePipeLocalInformation) | |
164 | || fpli.WriteQuotaAvailable >= PIPE_BUF | |
165 | || (fpli.OutboundQuota < PIPE_BUF && | |
166 | fpli.WriteQuotaAvailable == fpli.OutboundQuota)) | |
167 | write = TRUE; | |
168 | } | |
169 | break; | |
170 | ||
171 | case FILE_TYPE_CHAR: | |
172 | write = TRUE; | |
173 | if (!(rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | |
174 | break; | |
175 | ||
176 | ret = WaitForSingleObject (h, 0); | |
177 | if (ret == WAIT_OBJECT_0) | |
178 | { | |
179 | if (!IsConsoleHandle (h)) | |
180 | { | |
181 | read = TRUE; | |
182 | break; | |
183 | } | |
184 | ||
185 | nbuffer = avail = 0; | |
186 | bRet = GetNumberOfConsoleInputEvents (h, &nbuffer); | |
187 | ||
188 | /* Screen buffers handles are filtered earlier. */ | |
189 | assert (bRet); | |
190 | if (nbuffer == 0) | |
191 | { | |
192 | except = TRUE; | |
193 | break; | |
194 | } | |
195 | ||
196 | irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD)); | |
197 | bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail); | |
198 | if (!bRet || avail == 0) | |
199 | { | |
200 | except = TRUE; | |
201 | break; | |
202 | } | |
203 | ||
204 | for (i = 0; i < avail; i++) | |
205 | if (irbuffer[i].EventType == KEY_EVENT) | |
206 | read = TRUE; | |
207 | } | |
208 | break; | |
209 | ||
210 | default: | |
211 | ret = WaitForSingleObject (h, 0); | |
212 | write = TRUE; | |
213 | if (ret == WAIT_OBJECT_0) | |
214 | read = TRUE; | |
215 | ||
216 | break; | |
217 | } | |
218 | ||
219 | ret = 0; | |
220 | if (read && (rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | |
221 | { | |
222 | rbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); | |
223 | ret++; | |
224 | } | |
225 | ||
226 | if (write && (wbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | |
227 | { | |
228 | wbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); | |
229 | ret++; | |
230 | } | |
231 | ||
232 | if (except && (xbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | |
233 | { | |
234 | xbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); | |
235 | ret++; | |
236 | } | |
237 | ||
238 | return ret; | |
239 | } | |
240 | ||
241 | int | |
242 | rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, | |
243 | struct timeval *timeout) | |
244 | #undef timeval | |
245 | { | |
246 | static struct timeval tv0; | |
247 | static HANDLE hEvent; | |
248 | HANDLE h, handle_array[FD_SETSIZE + 2]; | |
249 | fd_set handle_rfds, handle_wfds, handle_xfds; | |
250 | struct bitset rbits, wbits, xbits; | |
251 | unsigned char anyfds_in[FD_SETSIZE / CHAR_BIT]; | |
252 | DWORD ret, wait_timeout, nhandles, nsock, nbuffer; | |
253 | MSG msg; | |
254 | int i, fd, rc; | |
255 | ||
256 | if (nfds > FD_SETSIZE) | |
257 | nfds = FD_SETSIZE; | |
258 | ||
259 | if (!timeout) | |
260 | wait_timeout = INFINITE; | |
261 | else | |
262 | { | |
263 | wait_timeout = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; | |
264 | ||
265 | /* select is also used as a portable usleep. */ | |
266 | if (!rfds && !wfds && !xfds) | |
267 | { | |
268 | Sleep (wait_timeout); | |
269 | return 0; | |
270 | } | |
271 | } | |
272 | ||
273 | if (!hEvent) | |
274 | hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); | |
275 | ||
276 | handle_array[0] = hEvent; | |
277 | nhandles = 1; | |
278 | nsock = 0; | |
279 | ||
280 | /* Copy descriptors to bitsets. At the same time, eliminate | |
281 | bits in the "wrong" direction for console input buffers | |
282 | and screen buffers, because screen buffers are waitable | |
283 | and they will block until a character is available. */ | |
284 | memset (&rbits, 0, sizeof (rbits)); | |
285 | memset (&wbits, 0, sizeof (wbits)); | |
286 | memset (&xbits, 0, sizeof (xbits)); | |
287 | memset (anyfds_in, 0, sizeof (anyfds_in)); | |
288 | if (rfds) | |
289 | for (i = 0; i < rfds->fd_count; i++) | |
290 | { | |
291 | fd = rfds->fd_array[i]; | |
292 | h = (HANDLE) _get_osfhandle (fd); | |
293 | if (IsConsoleHandle (h) | |
294 | && !GetNumberOfConsoleInputEvents (h, &nbuffer)) | |
295 | continue; | |
296 | ||
297 | rbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | |
298 | anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | |
299 | } | |
300 | else | |
301 | rfds = (fd_set *) alloca (sizeof (fd_set)); | |
302 | ||
303 | if (wfds) | |
304 | for (i = 0; i < wfds->fd_count; i++) | |
305 | { | |
306 | fd = wfds->fd_array[i]; | |
307 | h = (HANDLE) _get_osfhandle (fd); | |
308 | if (IsConsoleHandle (h) | |
309 | && GetNumberOfConsoleInputEvents (h, &nbuffer)) | |
310 | continue; | |
311 | ||
312 | wbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | |
313 | anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | |
314 | } | |
315 | else | |
316 | wfds = (fd_set *) alloca (sizeof (fd_set)); | |
317 | ||
318 | if (xfds) | |
319 | for (i = 0; i < xfds->fd_count; i++) | |
320 | { | |
321 | fd = xfds->fd_array[i]; | |
322 | xbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | |
323 | anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | |
324 | } | |
325 | else | |
326 | xfds = (fd_set *) alloca (sizeof (fd_set)); | |
327 | ||
328 | /* Zero all the fd_sets, including the application's. */ | |
329 | FD_ZERO (rfds); | |
330 | FD_ZERO (wfds); | |
331 | FD_ZERO (xfds); | |
332 | FD_ZERO (&handle_rfds); | |
333 | FD_ZERO (&handle_wfds); | |
334 | FD_ZERO (&handle_xfds); | |
335 | ||
336 | /* Classify handles. Create fd sets for sockets, poll the others. */ | |
337 | for (i = 0; i < nfds; i++) | |
338 | { | |
339 | if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) | |
340 | continue; | |
341 | ||
342 | h = (HANDLE) _get_osfhandle (i); | |
343 | if (!h) | |
344 | { | |
345 | errno = EBADF; | |
346 | return -1; | |
347 | } | |
348 | ||
349 | if (IsSocketHandle (h)) | |
350 | { | |
351 | int requested = FD_CLOSE; | |
352 | ||
353 | /* See above; socket handles are mapped onto select, but we | |
354 | need to map descriptors to handles. */ | |
355 | if (rbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | |
356 | { | |
357 | requested |= FD_READ | FD_ACCEPT; | |
358 | FD_SET ((SOCKET) h, rfds); | |
359 | FD_SET ((SOCKET) h, &handle_rfds); | |
360 | } | |
361 | if (wbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | |
362 | { | |
363 | requested |= FD_WRITE | FD_CONNECT; | |
364 | FD_SET ((SOCKET) h, wfds); | |
365 | FD_SET ((SOCKET) h, &handle_wfds); | |
366 | } | |
367 | if (xbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | |
368 | { | |
369 | requested |= FD_OOB; | |
370 | FD_SET ((SOCKET) h, xfds); | |
371 | FD_SET ((SOCKET) h, &handle_xfds); | |
372 | } | |
373 | ||
374 | WSAEventSelect ((SOCKET) h, hEvent, requested); | |
375 | nsock++; | |
376 | } | |
377 | else | |
378 | { | |
379 | handle_array[nhandles++] = h; | |
380 | ||
381 | /* Poll now. If we get an event, do not wait below. */ | |
382 | if (wait_timeout != 0 | |
383 | && windows_poll_handle (h, i, &rbits, &wbits, &xbits)) | |
384 | wait_timeout = 0; | |
385 | } | |
386 | } | |
387 | ||
388 | /* Place a sentinel at the end of the array. */ | |
389 | handle_array[nhandles] = NULL; | |
390 | ||
391 | restart: | |
392 | if (wait_timeout == 0 || nsock == 0) | |
393 | rc = 0; | |
394 | else | |
395 | { | |
396 | /* See if we need to wait in the loop below. If any select is ready, | |
397 | do MsgWaitForMultipleObjects anyway to dispatch messages, but | |
398 | no need to call select again. */ | |
399 | rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0); | |
400 | if (rc == 0) | |
401 | { | |
402 | /* Restore the fd_sets for the other select we do below. */ | |
403 | memcpy (&handle_rfds, rfds, sizeof (fd_set)); | |
404 | memcpy (&handle_wfds, wfds, sizeof (fd_set)); | |
405 | memcpy (&handle_xfds, xfds, sizeof (fd_set)); | |
406 | } | |
407 | else | |
408 | wait_timeout = 0; | |
409 | } | |
410 | ||
411 | for (;;) | |
412 | { | |
413 | ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE, | |
414 | wait_timeout, QS_ALLINPUT); | |
415 | ||
416 | if (ret == WAIT_OBJECT_0 + nhandles) | |
417 | { | |
418 | /* new input of some other kind */ | |
419 | BOOL bRet; | |
420 | while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0) | |
421 | { | |
422 | TranslateMessage (&msg); | |
423 | DispatchMessage (&msg); | |
424 | } | |
425 | } | |
426 | else | |
427 | break; | |
428 | } | |
429 | ||
430 | /* If we haven't done it yet, check the status of the sockets. */ | |
431 | if (rc == 0 && nsock > 0) | |
432 | rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0); | |
433 | ||
434 | if (nhandles > 1) | |
435 | { | |
436 | /* Count results that are not counted in the return value of select. */ | |
437 | nhandles = 1; | |
438 | for (i = 0; i < nfds; i++) | |
439 | { | |
440 | if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) | |
441 | continue; | |
442 | ||
443 | h = (HANDLE) _get_osfhandle (i); | |
444 | if (h == handle_array[nhandles]) | |
445 | { | |
446 | /* Not a socket. */ | |
447 | nhandles++; | |
448 | windows_poll_handle (h, i, &rbits, &wbits, &xbits); | |
449 | if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))) | |
450 | || wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))) | |
451 | || xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | |
452 | rc++; | |
453 | } | |
454 | } | |
455 | ||
456 | if (rc == 0 && wait_timeout == INFINITE) | |
457 | { | |
458 | /* Sleep 1 millisecond to avoid busy wait and retry with the | |
459 | original fd_sets. */ | |
460 | memcpy (&handle_rfds, rfds, sizeof (fd_set)); | |
461 | memcpy (&handle_wfds, wfds, sizeof (fd_set)); | |
462 | memcpy (&handle_xfds, xfds, sizeof (fd_set)); | |
463 | SleepEx (1, TRUE); | |
464 | goto restart; | |
465 | } | |
466 | } | |
467 | ||
468 | /* Now fill in the results. */ | |
469 | FD_ZERO (rfds); | |
470 | FD_ZERO (wfds); | |
471 | FD_ZERO (xfds); | |
472 | nhandles = 1; | |
473 | for (i = 0; i < nfds; i++) | |
474 | { | |
475 | if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) | |
476 | continue; | |
477 | ||
478 | h = (HANDLE) _get_osfhandle (i); | |
479 | if (h != handle_array[nhandles]) | |
480 | { | |
481 | /* Perform handle->descriptor mapping. */ | |
482 | WSAEventSelect ((SOCKET) h, NULL, 0); | |
483 | if (FD_ISSET (h, &handle_rfds)) | |
484 | FD_SET (i, rfds); | |
485 | if (FD_ISSET (h, &handle_wfds)) | |
486 | FD_SET (i, wfds); | |
487 | if (FD_ISSET (h, &handle_xfds)) | |
488 | FD_SET (i, xfds); | |
489 | } | |
490 | else | |
491 | { | |
492 | /* Not a socket. */ | |
493 | nhandles++; | |
494 | if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | |
495 | FD_SET (i, rfds); | |
496 | if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | |
497 | FD_SET (i, wfds); | |
498 | if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | |
499 | FD_SET (i, xfds); | |
500 | } | |
501 | } | |
502 | ||
503 | return rc; | |
504 | } | |
505 | ||
506 | #else /* ! Native Windows. */ | |
507 | ||
508 | #include <sys/select.h> | |
509 | #include <stddef.h> /* NULL */ | |
510 | #include <errno.h> | |
511 | #include <unistd.h> | |
512 | ||
513 | #undef select | |
514 | ||
515 | int | |
516 | rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, | |
517 | struct timeval *timeout) | |
518 | { | |
519 | int i; | |
520 | ||
521 | /* FreeBSD 8.2 has a bug: it does not always detect invalid fds. */ | |
522 | if (nfds < 0 || nfds > FD_SETSIZE) | |
523 | { | |
524 | errno = EINVAL; | |
525 | return -1; | |
526 | } | |
527 | for (i = 0; i < nfds; i++) | |
528 | { | |
529 | if (((rfds && FD_ISSET (i, rfds)) | |
530 | || (wfds && FD_ISSET (i, wfds)) | |
531 | || (xfds && FD_ISSET (i, xfds))) | |
532 | && dup2 (i, i) != i) | |
533 | return -1; | |
534 | } | |
535 | ||
536 | /* Interix 3.5 has a bug: it does not support nfds == 0. */ | |
537 | if (nfds == 0) | |
538 | { | |
539 | nfds = 1; | |
540 | rfds = NULL; | |
541 | wfds = NULL; | |
542 | xfds = NULL; | |
543 | } | |
544 | return select (nfds, rfds, wfds, xfds, timeout); | |
545 | } | |
546 | ||
547 | #endif |