From 6efb8b405ef8f6d6df76aefdd58374fea1039797 Mon Sep 17 00:00:00 2001 From: "Roland C. Dowdeswell" Date: Sun, 24 May 2026 18:34:54 +0100 Subject: [PATCH] roken: provide poll fallback --- cf/roken-frag.m4 | 2 +- lib/ipc/hi_locl.h | 2 - lib/roken/Makefile.am | 3 + lib/roken/NTMakefile | 7 +- lib/roken/poll-test.c | 131 ++++++++++++++++++++++++++++++ lib/roken/poll.c | 152 +++++++++++++++++++++++++++++++++++ lib/roken/roken.h.in | 40 +++++++++ lib/roken/version-script.map | 1 + 8 files changed, 334 insertions(+), 4 deletions(-) create mode 100644 lib/roken/poll-test.c create mode 100644 lib/roken/poll.c diff --git a/cf/roken-frag.m4 b/cf/roken-frag.m4 index 2e7d333db..99c1dd0c6 100644 --- a/cf/roken-frag.m4 +++ b/cf/roken-frag.m4 @@ -192,7 +192,6 @@ AC_CHECK_FUNCS([ \ mkdtemp \ mkostemp \ on_exit \ - poll \ random \ secure_getenv \ setprogname \ @@ -360,6 +359,7 @@ AC_BROKEN([ \ mergesort \ mergesort_r \ mkstemp \ + poll \ pread \ putenv \ rcmd \ diff --git a/lib/ipc/hi_locl.h b/lib/ipc/hi_locl.h index cf0231ea6..d655f618f 100644 --- a/lib/ipc/hi_locl.h +++ b/lib/ipc/hi_locl.h @@ -41,8 +41,6 @@ #include #endif -#include - #include #include #include diff --git a/lib/roken/Makefile.am b/lib/roken/Makefile.am index 115e759e8..6c53cdfc6 100644 --- a/lib/roken/Makefile.am +++ b/lib/roken/Makefile.am @@ -38,6 +38,7 @@ check_PROGRAMS = \ parse_bytes-test \ parse_reply-test \ parse_time-test \ + poll-test \ snprintf-test \ strpftime-test \ timeval \ @@ -282,6 +283,8 @@ EXTRA_DIST = \ getline.c \ ndbm_wrap.c \ ndbm_wrap.h \ + poll.c \ + poll-test.c \ rename.c \ simple_exec_w32.c \ sleep.c \ diff --git a/lib/roken/NTMakefile b/lib/roken/NTMakefile index 64557ab9b..0433fc3a3 100644 --- a/lib/roken/NTMakefile +++ b/lib/roken/NTMakefile @@ -90,6 +90,7 @@ libroken_la_OBJS = \ $(OBJ)\parse_bytes.obj \ $(OBJ)\parse_time.obj \ $(OBJ)\parse_units.obj \ + $(OBJ)\poll.obj \ $(OBJ)\pread.obj \ $(OBJ)\realloc.obj \ $(OBJ)\rename.obj \ @@ -214,6 +215,7 @@ TEST_PROGS = \ $(OBJ)\parse_bytes-test.exe \ $(OBJ)\parse_reply-test.exe \ $(OBJ)\parse_time-test.exe \ + $(OBJ)\poll-test.exe \ $(OBJ)\snprintf-test.exe \ $(OBJ)\strpftime-test.exe \ $(OBJ)\dirent-test.exe \ @@ -246,6 +248,9 @@ $(OBJ)\test-readenv.exe: $(OBJ)\test-readenv.obj $(OBJ)\test-mem.obj $(LIBROKEN) $(OBJ)\parse_time-test.exe: $(OBJ)\parse_time-test.obj $(OBJ)\test-mem.obj $(LIBROKEN) $(EXECONLINK) +$(OBJ)\poll-test.exe: $(OBJ)\poll-test.obj $(LIBROKEN) + $(EXECONLINK) + $(OBJ)\strpftime-test.obj: strpftime-test.c $(C2OBJ) -DTEST_STRPFTIME -DBUILD_ROKEN_LIB @@ -307,9 +312,9 @@ test-run: # Need to rewrite this test: # -parse_reply-test.exe -parse_time-test.exe + poll-test.exe -snprintf-test.exe -strpftime-test.exe cd $(SRCDIR) test:: test-binaries test-run - diff --git a/lib/roken/poll-test.c b/lib/roken/poll-test.c new file mode 100644 index 000000000..7def91b16 --- /dev/null +++ b/lib/roken/poll-test.c @@ -0,0 +1,131 @@ +#include +#include + +static void +close_pair(rk_socket_t a, rk_socket_t b) +{ + if (!rk_IS_BAD_SOCKET(a)) + rk_closesocket(a); + if (!rk_IS_BAD_SOCKET(b)) + rk_closesocket(b); +} + +static int +connected_pair(rk_socket_t *a, rk_socket_t *b) +{ + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + rk_socket_t ls = rk_INVALID_SOCKET; + rk_socket_t cs = rk_INVALID_SOCKET; + rk_socket_t as = rk_INVALID_SOCKET; + int one = 1; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sin.sin_port = 0; + + ls = socket(AF_INET, SOCK_STREAM, 0); + if (rk_IS_BAD_SOCKET(ls)) + return rk_SOCK_ERRNO; + + setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); + + if (rk_IS_SOCKET_ERROR(bind(ls, (struct sockaddr *)&sin, sizeof(sin))) || + rk_IS_SOCKET_ERROR(listen(ls, 1)) || + rk_IS_SOCKET_ERROR(getsockname(ls, (struct sockaddr *)&sin, &len))) { + int ret = rk_SOCK_ERRNO; + rk_closesocket(ls); + return ret; + } + + cs = socket(AF_INET, SOCK_STREAM, 0); + if (rk_IS_BAD_SOCKET(cs)) { + int ret = rk_SOCK_ERRNO; + rk_closesocket(ls); + return ret; + } + + if (rk_IS_SOCKET_ERROR(connect(cs, (struct sockaddr *)&sin, len))) { + int ret = rk_SOCK_ERRNO; + close_pair(ls, cs); + return ret; + } + + as = accept(ls, NULL, NULL); + if (rk_IS_BAD_SOCKET(as)) { + int ret = rk_SOCK_ERRNO; + close_pair(ls, cs); + return ret; + } + + rk_closesocket(ls); + *a = as; + *b = cs; + return 0; +} + +int +main(int argc, char **argv) +{ + struct pollfd pfd; + rk_socket_t a = rk_INVALID_SOCKET; + rk_socket_t b = rk_INVALID_SOCKET; + char c = 'x'; + int ret; + + setprogname(argv[0]); + + ret = rk_SOCK_INIT(); + if (ret) + err(1, "rk_SOCK_INIT"); + + ret = poll(NULL, 0, 0); + if (ret != 0) + errx(1, "poll with no fds returned %d", ret); + + ret = connected_pair(&a, &b); + if (ret) + errx(1, "connected_pair failed: %d", ret); + + pfd.fd = a; + pfd.events = POLLIN; + pfd.revents = 0; + ret = poll(&pfd, 1, 0); + if (ret != 0) + errx(1, "poll readable before send returned %d", ret); + if (pfd.revents != 0) + errx(1, "poll timeout left revents as %#x", pfd.revents); + + ret = send(b, &c, 1, 0); + if (rk_IS_SOCKET_ERROR(ret)) + err(1, "send"); + + pfd.fd = a; + pfd.events = POLLIN; + pfd.revents = 0; + ret = poll(&pfd, 1, 5000); + if (ret != 1) + errx(1, "poll readable after send returned %d", ret); + if ((pfd.revents & POLLIN) == 0) + errx(1, "poll readable missing POLLIN in %#x", pfd.revents); + + ret = recv(a, &c, 1, 0); + if (rk_IS_SOCKET_ERROR(ret)) + err(1, "recv"); + if (ret != 1) + errx(1, "recv returned %d", ret); + + pfd.fd = b; + pfd.events = POLLOUT; + pfd.revents = 0; + ret = poll(&pfd, 1, 5000); + if (ret != 1) + errx(1, "poll writable returned %d", ret); + if ((pfd.revents & POLLOUT) == 0) + errx(1, "poll writable missing POLLOUT in %#x", pfd.revents); + + close_pair(a, b); + rk_SOCK_EXIT(); + return 0; +} diff --git a/lib/roken/poll.c b/lib/roken/poll.c new file mode 100644 index 000000000..a6a59fe96 --- /dev/null +++ b/lib/roken/poll.c @@ -0,0 +1,152 @@ +#include +#include "roken.h" + +#ifndef HAVE_POLL + +#ifdef HAVE_WINSOCK +#include + +static int +poll_errno_from_winsock(void) +{ + switch (WSAGetLastError()) { + case WSAEINTR: + return EINTR; + case WSAEINVAL: + return EINVAL; + case WSAENOTSOCK: + return EBADF; + default: + return EIO; + } +} +#endif + +static void +poll_timeout(int timeout, struct timeval **tvp, struct timeval *tv) +{ + if (timeout < 0) { + *tvp = NULL; + return; + } + + tv->tv_sec = timeout / 1000; + tv->tv_usec = (timeout % 1000) * 1000; + *tvp = tv; +} + +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL +poll(struct pollfd fds[], nfds_t nfds, int timeout) +{ + struct timeval tv, *tvp; + fd_set rfds, wfds, efds; + rk_socket_t maxfd = rk_INVALID_SOCKET; + int have_rfds = 0, have_wfds = 0, have_efds = 0; + int width, ret, nready = 0; + nfds_t i; + + if (timeout < -1) { + errno = EINVAL; + return -1; + } + + poll_timeout(timeout, &tvp, &tv); + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + + for (i = 0; i < nfds; i++) { + short events; + + fds[i].revents = 0; + + if (rk_IS_BAD_SOCKET(fds[i].fd)) + continue; + +#ifndef NO_LIMIT_FD_SETSIZE + if (fds[i].fd >= FD_SETSIZE) { + fds[i].revents = POLLNVAL; + nready++; + continue; + } +#endif + + events = fds[i].events; + + if (events & (POLLIN | POLLPRI)) { + FD_SET(fds[i].fd, &rfds); + have_rfds = 1; + } + if (events & POLLOUT) { + FD_SET(fds[i].fd, &wfds); + have_wfds = 1; + } + + FD_SET(fds[i].fd, &efds); + have_efds = 1; + + if (maxfd == rk_INVALID_SOCKET || fds[i].fd > maxfd) + maxfd = fds[i].fd; + } + + if (nready) + return nready; + + if (maxfd == rk_INVALID_SOCKET) { +#ifdef HAVE_WINSOCK + Sleep(timeout < 0 ? INFINITE : (DWORD)timeout); + return 0; +#else + return select(0, NULL, NULL, NULL, tvp); +#endif + } + +#ifdef HAVE_WINSOCK + width = 0; +#else + width = maxfd + 1; +#endif + + ret = select(width, + have_rfds ? &rfds : NULL, + have_wfds ? &wfds : NULL, + have_efds ? &efds : NULL, + tvp); + if (rk_IS_SOCKET_ERROR(ret)) { +#ifdef HAVE_WINSOCK + errno = poll_errno_from_winsock(); +#endif + return -1; + } + if (ret == 0) + return 0; + + nready = 0; + for (i = 0; i < nfds; i++) { + short revents = 0; + + if (rk_IS_BAD_SOCKET(fds[i].fd)) + continue; + +#ifndef NO_LIMIT_FD_SETSIZE + if (fds[i].fd >= FD_SETSIZE) + continue; +#endif + + if (have_rfds && FD_ISSET(fds[i].fd, &rfds)) + revents |= fds[i].events & (POLLIN | POLLPRI); + if (have_wfds && FD_ISSET(fds[i].fd, &wfds)) + revents |= POLLOUT; + if (have_efds && FD_ISSET(fds[i].fd, &efds)) + revents |= (fds[i].events & POLLPRI) ? POLLPRI : POLLERR; + + fds[i].revents = revents; + if (revents) + nready++; + } + + return nready; +} + +#endif /* !HAVE_POLL */ diff --git a/lib/roken/roken.h.in b/lib/roken/roken.h.in index 868e9595b..efdf3542c 100644 --- a/lib/roken/roken.h.in +++ b/lib/roken/roken.h.in @@ -192,6 +192,46 @@ static inline ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_SOCK_INIT(void) { #endif /* WinSock */ +#ifdef HAVE_POLL_H +#include +#else +/* + * poll(2) compatibility. Windows may provide struct pollfd and the + * POLL* constants through winsock2.h even though it has no . + */ +typedef unsigned long nfds_t; +# ifndef POLLIN +struct pollfd { + rk_socket_t fd; + short events; + short revents; +}; +# endif +# ifndef POLLIN +# define POLLIN 0x0001 +# endif +# ifndef POLLPRI +# define POLLPRI 0x0002 +# endif +# ifndef POLLOUT +# define POLLOUT 0x0004 +# endif +# ifndef POLLERR +# define POLLERR 0x0008 +# endif +# ifndef POLLHUP +# define POLLHUP 0x0010 +# endif +# ifndef POLLNVAL +# define POLLNVAL 0x0020 +# endif +#endif + +#ifndef HAVE_POLL +#define poll rk_poll +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL poll(struct pollfd *, nfds_t, int); +#endif + #ifndef IN_LOOPBACKNET #define IN_LOOPBACKNET 127 #endif diff --git a/lib/roken/version-script.map b/lib/roken/version-script.map index 76dbedad8..bc5048ac6 100644 --- a/lib/roken/version-script.map +++ b/lib/roken/version-script.map @@ -107,6 +107,7 @@ HEIMDAL_ROKEN_2.0 { rk_pid_file_write; rk_pidfile; rk_pipe_execv; + rk_poll; rk_print_flags_table; rk_print_time_table; rk_print_units_table;