aboutsummaryrefslogtreecommitdiff
path: root/win32/poll.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--win32/poll.c656
1 files changed, 656 insertions, 0 deletions
diff --git a/win32/poll.c b/win32/poll.c
new file mode 100644
index 000000000..8ab6bbf29
--- /dev/null
+++ b/win32/poll.c
@@ -0,0 +1,656 @@
1/* Emulation for poll(2)
2 Contributed by Paolo Bonzini.
3
4 Copyright 2001-2003, 2006-2024 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/* Tell gcc not to warn about the (nfd < 0) tests, below. */
22#if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__
23# pragma GCC diagnostic ignored "-Wtype-limits"
24#endif
25
26#include "libbb.h"
27#include <malloc.h>
28
29#include <sys/types.h>
30
31/* Specification. */
32#include <poll.h>
33
34#include <errno.h>
35#include <limits.h>
36#include <assert.h>
37
38#if defined _WIN32 && ! defined __CYGWIN__
39# define WINDOWS_NATIVE
40# include <winsock2.h>
41# include <windows.h>
42# include <io.h>
43# include <stdio.h>
44# include <conio.h>
45#else
46# include <sys/time.h>
47# include <unistd.h>
48#endif
49
50#include <sys/select.h>
51#include <sys/socket.h>
52
53#ifdef HAVE_SYS_IOCTL_H
54# include <sys/ioctl.h>
55#endif
56#ifdef HAVE_SYS_FILIO_H
57# include <sys/filio.h>
58#endif
59
60#include <time.h>
61
62#ifndef INFTIM
63# define INFTIM (-1)
64#endif
65
66/* BeOS does not have MSG_PEEK. */
67#ifndef MSG_PEEK
68# define MSG_PEEK 0
69#endif
70
71#ifdef WINDOWS_NATIVE
72
73/* Don't assume that UNICODE is not defined. */
74# undef GetModuleHandle
75# define GetModuleHandle GetModuleHandleA
76# undef PeekConsoleInput
77# define PeekConsoleInput PeekConsoleInputA
78# undef CreateEvent
79# define CreateEvent CreateEventA
80# undef PeekMessage
81# define PeekMessage PeekMessageA
82# undef DispatchMessage
83# define DispatchMessage DispatchMessageA
84
85/* Do *not* use the function WSAPoll
86 <https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-wsapoll>
87 because there is a bug named “Windows 8 Bugs 309411 - WSAPoll does not
88 report failed connections” that Microsoft won't fix.
89 See Daniel Stenberg: "WASPoll is broken"
90 <https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/>. */
91
92/* Here we need the recv() function from Windows, that takes a SOCKET as
93 first argument, not any possible gnulib override. */
94# undef recv
95
96/* Here we need the select() function from Windows, because we pass bit masks
97 of SOCKETs, not bit masks of FDs. */
98# undef select
99
100/* Here we need timeval from Windows since this is what the select() function
101 from Windows requires. */
102# undef timeval
103
104/* Avoid warnings from gcc -Wcast-function-type. */
105# define GetProcAddress \
106 (void *) GetProcAddress
107
108static BOOL IsConsoleHandle (HANDLE h)
109{
110 DWORD mode;
111 return GetConsoleMode (h, &mode) != 0;
112}
113
114static BOOL
115IsSocketHandle (HANDLE h)
116{
117 WSANETWORKEVENTS ev;
118
119 if (IsConsoleHandle (h))
120 return FALSE;
121
122 /* Under Wine, it seems that getsockopt returns 0 for pipes too.
123 WSAEnumNetworkEvents instead distinguishes the two correctly. */
124 ev.lNetworkEvents = 0xDEADBEEF;
125 WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
126 return ev.lNetworkEvents != 0xDEADBEEF;
127}
128
129/* Declare data structures for ntdll functions. */
130typedef struct _FILE_PIPE_LOCAL_INFORMATION {
131 ULONG NamedPipeType;
132 ULONG NamedPipeConfiguration;
133 ULONG MaximumInstances;
134 ULONG CurrentInstances;
135 ULONG InboundQuota;
136 ULONG ReadDataAvailable;
137 ULONG OutboundQuota;
138 ULONG WriteQuotaAvailable;
139 ULONG NamedPipeState;
140 ULONG NamedPipeEnd;
141} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
142
143typedef struct _IO_STATUS_BLOCK
144{
145 union {
146 DWORD Status;
147 PVOID Pointer;
148 } u;
149 ULONG_PTR Information;
150} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
151
152typedef enum _FILE_INFORMATION_CLASS {
153 FilePipeLocalInformation = 24
154} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
155
156typedef DWORD (WINAPI *PNtQueryInformationFile)
157 (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
158
159# ifndef PIPE_BUF
160# define PIPE_BUF 512
161# endif
162
163/* Compute revents values for file handle H. If some events cannot happen
164 for the handle, eliminate them from *P_SOUGHT. */
165
166static int
167windows_compute_revents (HANDLE h, int *p_sought)
168{
169 int i, ret, happened;
170 INPUT_RECORD *irbuffer;
171 DWORD avail, nbuffer;
172 BOOL bRet;
173#if 0
174 IO_STATUS_BLOCK iosb;
175 FILE_PIPE_LOCAL_INFORMATION fpli;
176 static PNtQueryInformationFile NtQueryInformationFile;
177 static BOOL once_only;
178#endif
179
180 switch (GetFileType (h))
181 {
182 case FILE_TYPE_PIPE:
183#if 0
184 if (!once_only)
185 {
186 NtQueryInformationFile = (PNtQueryInformationFile)
187 GetProcAddress (GetModuleHandle ("ntdll.dll"),
188 "NtQueryInformationFile");
189 once_only = TRUE;
190 }
191#endif
192
193 happened = 0;
194 if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
195 {
196 if (avail)
197 happened |= *p_sought & (POLLIN | POLLRDNORM);
198 }
199 else if (GetLastError () == ERROR_BROKEN_PIPE)
200 happened |= POLLHUP;
201
202 else
203 {
204 /* The writability of a pipe can't be detected reliably on Windows.
205 * Just say it's OK.
206 *
207 * Details:
208 *
209 * https://github.com/git-for-windows/git/commit/94f4d01932279c419844aa708bec31a26056bc6b
210 */
211#if 0
212 /* It was the write-end of the pipe. Check if it is writable.
213 If NtQueryInformationFile fails, optimistically assume the pipe is
214 writable. This could happen on Windows 9x, where
215 NtQueryInformationFile is not available, or if we inherit a pipe
216 that doesn't permit FILE_READ_ATTRIBUTES access on the write end
217 (I think this should not happen since Windows XP SP2; WINE seems
218 fine too). Otherwise, ensure that enough space is available for
219 atomic writes. */
220 memset (&iosb, 0, sizeof (iosb));
221 memset (&fpli, 0, sizeof (fpli));
222
223 if (!NtQueryInformationFile
224 || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
225 FilePipeLocalInformation)
226 || fpli.WriteQuotaAvailable >= PIPE_BUF
227 || (fpli.OutboundQuota < PIPE_BUF &&
228 fpli.WriteQuotaAvailable == fpli.OutboundQuota))
229#endif
230 happened |= *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
231 }
232 return happened;
233
234 case FILE_TYPE_CHAR:
235 // Fall through to default case for non-console, e.g. /dev/null.
236 if (IsConsoleHandle (h)) {
237 nbuffer = avail = 0;
238 bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
239 if (bRet)
240 {
241 /* Input buffer. */
242 *p_sought &= POLLIN | POLLRDNORM;
243 if (nbuffer == 0)
244 // Having no unread events isn't an error condition.
245 return 0 /* was POLLHUP */;
246 if (!*p_sought)
247 return 0;
248
249 irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
250 bRet = PeekConsoleInputW (h, irbuffer, nbuffer, &avail);
251 if (!bRet || avail == 0)
252 return POLLHUP;
253
254 for (i = 0; i < avail; i++)
255 // Ignore key release.
256 if (irbuffer[i].EventType == KEY_EVENT &&
257 irbuffer[i].Event.KeyEvent.bKeyDown)
258 return *p_sought;
259 return 0;
260 }
261 else
262 {
263 /* Screen buffer. */
264 *p_sought &= POLLOUT | POLLWRNORM | POLLWRBAND;
265 return *p_sought;
266 }
267 }
268 /* fall through */
269
270 default:
271 ret = WaitForSingleObject (h, 0);
272 if (ret == WAIT_OBJECT_0)
273 return *p_sought & ~(POLLPRI | POLLRDBAND);
274
275 // Add (POLLIN | POLLRDNORM). Why only support write?
276 return *p_sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND);
277 }
278}
279
280/* Convert fd_sets returned by select into revents values. */
281
282static int
283windows_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents)
284{
285 int happened = 0;
286
287 if ((lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) == FD_ACCEPT)
288 happened |= (POLLIN | POLLRDNORM) & sought;
289
290 else if (lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))
291 {
292 int r, error;
293
294 char data[64];
295 WSASetLastError (0);
296 r = recv (h, data, sizeof (data), MSG_PEEK);
297 error = WSAGetLastError ();
298 WSASetLastError (0);
299
300 if (r > 0 || error == WSAENOTCONN)
301 happened |= (POLLIN | POLLRDNORM) & sought;
302
303 /* Distinguish hung-up sockets from other errors. */
304 else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET
305 || error == WSAECONNABORTED || error == WSAENETRESET)
306 happened |= POLLHUP;
307
308 else
309 happened |= POLLERR;
310 }
311
312 if (lNetworkEvents & (FD_WRITE | FD_CONNECT))
313 happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
314
315 if (lNetworkEvents & FD_OOB)
316 happened |= (POLLPRI | POLLRDBAND) & sought;
317
318 return happened;
319}
320
321#else /* !MinGW */
322
323/* Convert select(2) returned fd_sets into poll(2) revents values. */
324static int
325compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds)
326{
327 int happened = 0;
328 if (FD_ISSET (fd, rfds))
329 {
330 int r;
331 int socket_errno;
332
333# if defined __MACH__ && defined __APPLE__
334 /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
335 for some kinds of descriptors. Detect if this descriptor is a
336 connected socket, a server socket, or something else using a
337 0-byte recv, and use ioctl(2) to detect POLLHUP. */
338 r = recv (fd, NULL, 0, MSG_PEEK);
339 socket_errno = (r < 0) ? errno : 0;
340 if (r == 0 || socket_errno == ENOTSOCK)
341 ioctl (fd, FIONREAD, &r);
342# else
343 char data[64];
344 r = recv (fd, data, sizeof (data), MSG_PEEK);
345 socket_errno = (r < 0) ? errno : 0;
346# endif
347 if (r == 0)
348 happened |= POLLHUP;
349
350 /* If the event happened on an unconnected server socket,
351 that's fine. */
352 else if (r > 0 || ( /* (r == -1) && */ socket_errno == ENOTCONN))
353 happened |= (POLLIN | POLLRDNORM) & sought;
354
355 /* Distinguish hung-up sockets from other errors. */
356 else if (socket_errno == ESHUTDOWN || socket_errno == ECONNRESET
357 || socket_errno == ECONNABORTED || socket_errno == ENETRESET)
358 happened |= POLLHUP;
359
360 /* some systems can't use recv() on non-socket, including HP NonStop */
361 else if (socket_errno == ENOTSOCK)
362 happened |= (POLLIN | POLLRDNORM) & sought;
363
364 else
365 happened |= POLLERR;
366 }
367
368 if (FD_ISSET (fd, wfds))
369 happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
370
371 if (FD_ISSET (fd, efds))
372 happened |= (POLLPRI | POLLRDBAND) & sought;
373
374 return happened;
375}
376#endif /* !MinGW */
377
378int
379poll (struct pollfd *pfd, nfds_t nfd, int timeout)
380{
381#ifndef WINDOWS_NATIVE
382 fd_set rfds, wfds, efds;
383 struct timeval tv;
384 struct timeval *ptv;
385 int maxfd, rc;
386 nfds_t i;
387
388 if (nfd > INT_MAX)
389 {
390 errno = EINVAL;
391 return -1;
392 }
393 /* Don't check directly for NFD greater than OPEN_MAX. Any practical use
394 of a too-large NFD is caught by one of the other checks below, and
395 checking directly for getdtablesize is too much of a portability
396 and/or performance and/or correctness hassle. */
397
398 /* EFAULT is not necessary to implement, but let's do it in the
399 simplest case. */
400 if (!pfd && nfd)
401 {
402 errno = EFAULT;
403 return -1;
404 }
405
406 /* convert timeout number into a timeval structure */
407 if (timeout == 0)
408 {
409 ptv = &tv;
410 tv = (struct timeval) {0};
411 }
412 else if (timeout > 0)
413 {
414 ptv = &tv;
415 tv = (struct timeval) {
416 .tv_sec = timeout / 1000,
417 .tv_usec = (timeout % 1000) * 1000
418 };
419 }
420 else if (timeout == INFTIM)
421 /* wait forever */
422 ptv = NULL;
423 else
424 {
425 errno = EINVAL;
426 return -1;
427 }
428
429 /* create fd sets and determine max fd */
430 maxfd = -1;
431 FD_ZERO (&rfds);
432 FD_ZERO (&wfds);
433 FD_ZERO (&efds);
434 for (i = 0; i < nfd; i++)
435 {
436 if (pfd[i].fd < 0)
437 continue;
438 if (maxfd < pfd[i].fd)
439 {
440 maxfd = pfd[i].fd;
441 if (FD_SETSIZE <= maxfd)
442 {
443 errno = EINVAL;
444 return -1;
445 }
446 }
447 if (pfd[i].events & (POLLIN | POLLRDNORM))
448 FD_SET (pfd[i].fd, &rfds);
449 /* see select(2): "the only exceptional condition detectable
450 is out-of-band data received on a socket", hence we push
451 POLLWRBAND events onto wfds instead of efds. */
452 if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND))
453 FD_SET (pfd[i].fd, &wfds);
454 if (pfd[i].events & (POLLPRI | POLLRDBAND))
455 FD_SET (pfd[i].fd, &efds);
456 }
457
458 /* examine fd sets */
459 rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
460 if (rc < 0)
461 return rc;
462
463 /* establish results */
464 rc = 0;
465 for (i = 0; i < nfd; i++)
466 {
467 pfd[i].revents = (pfd[i].fd < 0
468 ? 0
469 : compute_revents (pfd[i].fd, pfd[i].events,
470 &rfds, &wfds, &efds));
471 rc += pfd[i].revents != 0;
472 }
473
474 return rc;
475#else
476 static struct timeval tv0;
477 static HANDLE hEvent;
478 WSANETWORKEVENTS ev;
479 HANDLE h, handle_array[FD_SETSIZE + 2];
480 DWORD ret, wait_timeout, nhandles;
481 fd_set rfds, wfds, xfds;
482 BOOL poll_again;
483 MSG msg;
484 int rc = 0;
485 nfds_t i;
486 DWORD real_timeout = 0;
487 int save_timeout = timeout;
488 clock_t tend = clock () + timeout;
489
490 if (nfd > INT_MAX || timeout < -1)
491 {
492 errno = EINVAL;
493 return -1;
494 }
495
496 if (!hEvent)
497 hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
498
499restart:
500 /* How much is left to wait? */
501 timeout = save_timeout;
502 if (timeout != INFTIM)
503 {
504 clock_t now = clock ();
505 real_timeout = tend > now ? tend - now : 0;
506 }
507
508 handle_array[0] = hEvent;
509 nhandles = 1;
510 FD_ZERO (&rfds);
511 FD_ZERO (&wfds);
512 FD_ZERO (&xfds);
513
514 /* Classify socket handles and create fd sets. */
515 for (i = 0; i < nfd; i++)
516 {
517 int sought = pfd[i].events;
518 pfd[i].revents = 0;
519 if (pfd[i].fd < 0)
520 continue;
521 if (!(sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND
522 | POLLPRI | POLLRDBAND)))
523 continue;
524
525 h = (HANDLE) _get_osfhandle (pfd[i].fd);
526 assert (h != NULL);
527 if (IsSocketHandle (h))
528 {
529 int requested = FD_CLOSE;
530
531 /* see above; socket handles are mapped onto select. */
532 if (sought & (POLLIN | POLLRDNORM))
533 {
534 requested |= FD_READ | FD_ACCEPT;
535 FD_SET ((SOCKET) h, &rfds);
536 }
537 if (sought & (POLLOUT | POLLWRNORM | POLLWRBAND))
538 {
539 requested |= FD_WRITE | FD_CONNECT;
540 FD_SET ((SOCKET) h, &wfds);
541 }
542 if (sought & (POLLPRI | POLLRDBAND))
543 {
544 requested |= FD_OOB;
545 FD_SET ((SOCKET) h, &xfds);
546 }
547
548 if (requested)
549 WSAEventSelect ((SOCKET) h, hEvent, requested);
550 }
551 else
552 {
553 /* Poll now. If we get an event, do not poll again. Also,
554 screen buffer handles are waitable, and they'll block until
555 a character is available. windows_compute_revents eliminates
556 bits for the "wrong" direction. */
557 pfd[i].revents = windows_compute_revents (h, &sought);
558 if (sought)
559 handle_array[nhandles++] = h;
560 if (pfd[i].revents)
561 timeout = 0;
562 }
563 }
564
565 if (select (0, &rfds, &wfds, &xfds, &tv0) > 0)
566 {
567 /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but
568 no need to call select again. */
569 poll_again = FALSE;
570 wait_timeout = 0;
571 }
572 else
573 {
574 poll_again = TRUE;
575 if (timeout == INFTIM)
576 wait_timeout = INFINITE;
577 else
578 wait_timeout = timeout;
579 }
580
581 for (;;)
582 {
583 ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
584 wait_timeout, QS_ALLINPUT);
585
586 if (ret == WAIT_OBJECT_0 + nhandles)
587 {
588 /* new input of some other kind */
589 BOOL bRet;
590 while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
591 {
592 TranslateMessage (&msg);
593 DispatchMessage (&msg);
594 }
595 }
596 else
597 break;
598 }
599
600 if (poll_again)
601 select (0, &rfds, &wfds, &xfds, &tv0);
602
603 /* Place a sentinel at the end of the array. */
604 handle_array[nhandles] = NULL;
605 nhandles = 1;
606 for (i = 0; i < nfd; i++)
607 {
608 int happened;
609
610 if (pfd[i].fd < 0)
611 continue;
612 if (!(pfd[i].events & (POLLIN | POLLRDNORM |
613 POLLOUT | POLLWRNORM | POLLWRBAND)))
614 continue;
615
616 h = (HANDLE) _get_osfhandle (pfd[i].fd);
617 if (h != handle_array[nhandles])
618 {
619 /* It's a socket. */
620 WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
621 WSAEventSelect ((SOCKET) h, 0, 0);
622
623 /* If we're lucky, WSAEnumNetworkEvents already provided a way
624 to distinguish FD_READ and FD_ACCEPT; this saves a recv later. */
625 if (FD_ISSET ((SOCKET) h, &rfds)
626 && !(ev.lNetworkEvents & (FD_READ | FD_ACCEPT)))
627 ev.lNetworkEvents |= FD_READ | FD_ACCEPT;
628 if (FD_ISSET ((SOCKET) h, &wfds))
629 ev.lNetworkEvents |= FD_WRITE | FD_CONNECT;
630 if (FD_ISSET ((SOCKET) h, &xfds))
631 ev.lNetworkEvents |= FD_OOB;
632
633 happened = windows_compute_revents_socket ((SOCKET) h, pfd[i].events,
634 ev.lNetworkEvents);
635 }
636 else
637 {
638 /* Not a socket. */
639 int sought = pfd[i].events;
640 happened = windows_compute_revents (h, &sought);
641 nhandles++;
642 }
643
644 if ((pfd[i].revents |= happened) != 0)
645 rc++;
646 }
647
648 if (!rc && (save_timeout == INFTIM || (real_timeout != 0 && nhandles > 1)))
649 {
650 SleepEx (1, TRUE);
651 goto restart;
652 }
653
654 return rc;
655#endif
656}