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