aboutsummaryrefslogtreecommitdiff
path: root/win32/select.c
diff options
context:
space:
mode:
Diffstat (limited to 'win32/select.c')
-rw-r--r--win32/select.c592
1 files changed, 592 insertions, 0 deletions
diff --git a/win32/select.c b/win32/select.c
new file mode 100644
index 000000000..2be221ac8
--- /dev/null
+++ b/win32/select.c
@@ -0,0 +1,592 @@
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 > FD_SETSIZE)
277 nfds = FD_SETSIZE;
278
279 if (!timeout)
280 wait_timeout = INFINITE;
281 else
282 {
283 wait_timeout = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
284
285 /* select is also used as a portable usleep. */
286 if (!rfds && !wfds && !xfds)
287 {
288 Sleep (wait_timeout);
289 return 0;
290 }
291 }
292
293 if (!hEvent)
294 hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
295
296 handle_array[0] = hEvent;
297 nhandles = 1;
298 nsock = 0;
299
300 /* Copy descriptors to bitsets. At the same time, eliminate
301 bits in the "wrong" direction for console input buffers
302 and screen buffers, because screen buffers are waitable
303 and they will block until a character is available. */
304 memset (&rbits, 0, sizeof (rbits));
305 memset (&wbits, 0, sizeof (wbits));
306 memset (&xbits, 0, sizeof (xbits));
307 memset (anyfds_in, 0, sizeof (anyfds_in));
308 if (rfds)
309 for (i = 0; i < rfds->fd_count; i++)
310 {
311 fd = rfds->fd_array[i];
312 h = (HANDLE) _get_osfhandle (fd);
313 if (IsConsoleHandle (h)
314 && !GetNumberOfConsoleInputEvents (h, &nbuffer))
315 continue;
316
317 rbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
318 anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
319 }
320 else
321 rfds = (fd_set *) alloca (sizeof (fd_set));
322
323 if (wfds)
324 for (i = 0; i < wfds->fd_count; i++)
325 {
326 fd = wfds->fd_array[i];
327 h = (HANDLE) _get_osfhandle (fd);
328 if (IsConsoleHandle (h)
329 && GetNumberOfConsoleInputEvents (h, &nbuffer))
330 continue;
331
332 wbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
333 anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
334 }
335 else
336 wfds = (fd_set *) alloca (sizeof (fd_set));
337
338 if (xfds)
339 for (i = 0; i < xfds->fd_count; i++)
340 {
341 fd = xfds->fd_array[i];
342 xbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
343 anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
344 }
345 else
346 xfds = (fd_set *) alloca (sizeof (fd_set));
347
348 /* Zero all the fd_sets, including the application's. */
349 FD_ZERO (rfds);
350 FD_ZERO (wfds);
351 FD_ZERO (xfds);
352 FD_ZERO (&handle_rfds);
353 FD_ZERO (&handle_wfds);
354 FD_ZERO (&handle_xfds);
355
356 /* Classify handles. Create fd sets for sockets, poll the others. */
357 for (i = 0; i < nfds; i++)
358 {
359 if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
360 continue;
361
362 h = (HANDLE) _get_osfhandle (i);
363 if (!h)
364 {
365 errno = EBADF;
366 return -1;
367 }
368
369 if (IsSocketHandle (h))
370 {
371 int requested = FD_CLOSE;
372
373 /* See above; socket handles are mapped onto select, but we
374 need to map descriptors to handles. */
375 if (rbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
376 {
377 requested |= FD_READ | FD_ACCEPT;
378 FD_SET ((SOCKET) h, rfds);
379 FD_SET ((SOCKET) h, &handle_rfds);
380 }
381 if (wbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
382 {
383 requested |= FD_WRITE | FD_CONNECT;
384 FD_SET ((SOCKET) h, wfds);
385 FD_SET ((SOCKET) h, &handle_wfds);
386 }
387 if (xbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
388 {
389 requested |= FD_OOB;
390 FD_SET ((SOCKET) h, xfds);
391 FD_SET ((SOCKET) h, &handle_xfds);
392 }
393
394 WSAEventSelect ((SOCKET) h, hEvent, requested);
395 nsock++;
396 }
397 else
398 {
399 handle_array[nhandles++] = h;
400
401 /* Poll now. If we get an event, do not wait below. */
402 if (wait_timeout != 0
403 && windows_poll_handle (h, i, &rbits, &wbits, &xbits))
404 wait_timeout = 0;
405 }
406 }
407
408 /* Place a sentinel at the end of the array. */
409 handle_array[nhandles] = NULL;
410
411 /* When will the waiting period expire? */
412 if (wait_timeout != INFINITE)
413 tend = clock () + wait_timeout;
414
415restart:
416 if (wait_timeout == 0 || nsock == 0)
417 rc = 0;
418 else
419 {
420 /* See if we need to wait in the loop below. If any select is ready,
421 do MsgWaitForMultipleObjects anyway to dispatch messages, but
422 no need to call select again. */
423 rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
424 if (rc == 0)
425 {
426 /* Restore the fd_sets for the other select we do below. */
427 memcpy (&handle_rfds, rfds, sizeof (fd_set));
428 memcpy (&handle_wfds, wfds, sizeof (fd_set));
429 memcpy (&handle_xfds, xfds, sizeof (fd_set));
430 }
431 else
432 wait_timeout = 0;
433 }
434
435 /* How much is left to wait? */
436 if (wait_timeout != INFINITE)
437 {
438 clock_t tnow = clock ();
439 if (tend >= tnow)
440 wait_timeout = tend - tnow;
441 else
442 wait_timeout = 0;
443 }
444
445 for (;;)
446 {
447 ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
448 wait_timeout, QS_ALLINPUT);
449
450 if (ret == WAIT_OBJECT_0 + nhandles)
451 {
452 /* new input of some other kind */
453 BOOL bRet;
454 while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
455 {
456 TranslateMessage (&msg);
457 DispatchMessage (&msg);
458 }
459 }
460 else
461 break;
462 }
463
464 /* If we haven't done it yet, check the status of the sockets. */
465 if (rc == 0 && nsock > 0)
466 rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
467
468 if (nhandles > 1)
469 {
470 /* Count results that are not counted in the return value of select. */
471 nhandles = 1;
472 for (i = 0; i < nfds; i++)
473 {
474 if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
475 continue;
476
477 h = (HANDLE) _get_osfhandle (i);
478 if (h == handle_array[nhandles])
479 {
480 /* Not a socket. */
481 nhandles++;
482 windows_poll_handle (h, i, &rbits, &wbits, &xbits);
483 if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))
484 || wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))
485 || xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
486 rc++;
487 }
488 }
489
490 if (rc == 0
491 && (wait_timeout == INFINITE
492 /* If NHANDLES > 1, but no bits are set, it means we've
493 been told incorrectly that some handle was signaled.
494 This happens with anonymous pipes, which always cause
495 MsgWaitForMultipleObjects to exit immediately, but no
496 data is found ready to be read by windows_poll_handle.
497 To avoid a total failure (whereby we return zero and
498 don't wait at all), let's poll in a more busy loop. */
499 || (wait_timeout != 0 && nhandles > 1)))
500 {
501 /* Sleep 1 millisecond to avoid busy wait and retry with the
502 original fd_sets. */
503 memcpy (&handle_rfds, rfds, sizeof (fd_set));
504 memcpy (&handle_wfds, wfds, sizeof (fd_set));
505 memcpy (&handle_xfds, xfds, sizeof (fd_set));
506 SleepEx (1, TRUE);
507 goto restart;
508 }
509 if (timeout && wait_timeout == 0 && rc == 0)
510 timeout->tv_sec = timeout->tv_usec = 0;
511 }
512
513 /* Now fill in the results. */
514 FD_ZERO (rfds);
515 FD_ZERO (wfds);
516 FD_ZERO (xfds);
517 nhandles = 1;
518 for (i = 0; i < nfds; i++)
519 {
520 if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
521 continue;
522
523 h = (HANDLE) _get_osfhandle (i);
524 if (h != handle_array[nhandles])
525 {
526 /* Perform handle->descriptor mapping. */
527 SOCKET s = (SOCKET) h;
528 WSAEventSelect (s, NULL, 0);
529 if (FD_ISSET (s, &handle_rfds))
530 FD_SET (i, rfds);
531 if (FD_ISSET (s, &handle_wfds))
532 FD_SET (i, wfds);
533 if (FD_ISSET (s, &handle_xfds))
534 FD_SET (i, xfds);
535 }
536 else
537 {
538 /* Not a socket. */
539 nhandles++;
540 if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
541 FD_SET (i, rfds);
542 if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
543 FD_SET (i, wfds);
544 if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
545 FD_SET (i, xfds);
546 }
547 }
548
549 return rc;
550}
551
552#else /* ! Native Windows. */
553
554#include <stddef.h> /* NULL */
555#include <errno.h>
556#include <unistd.h>
557
558#undef select
559
560int
561rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
562 struct timeval *timeout)
563{
564 int i;
565
566 /* FreeBSD 8.2 has a bug: it does not always detect invalid fds. */
567 if (nfds < 0 || nfds > FD_SETSIZE)
568 {
569 errno = EINVAL;
570 return -1;
571 }
572 for (i = 0; i < nfds; i++)
573 {
574 if (((rfds && FD_ISSET (i, rfds))
575 || (wfds && FD_ISSET (i, wfds))
576 || (xfds && FD_ISSET (i, xfds)))
577 && dup2 (i, i) != i)
578 return -1;
579 }
580
581 /* Interix 3.5 has a bug: it does not support nfds == 0. */
582 if (nfds == 0)
583 {
584 nfds = 1;
585 rfds = NULL;
586 wfds = NULL;
587 xfds = NULL;
588 }
589 return select (nfds, rfds, wfds, xfds, timeout);
590}
591
592#endif