libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

commit 8aecaea7dc0a6c3fe3c47cc5bde3b1e16ac5900c
parent a2d1055b7c724a1e7564887339fbdf4abaf4bea8
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date:   Sun, 11 May 2025 21:29:19 +0200

configure and the code: check for poll() quirks

This should fix compatibility with Haiku

Diffstat:
Mconfigure.ac | 1+
Am4/mhd_check_poll_quirks.m4 | 390+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/events_process.c | 17++++++++++++++++-
3 files changed, 407 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac @@ -4763,6 +4763,7 @@ MHD_CHECK_FUNC([pread], ]] ) +AS_VAR_IF([have_poll],["yes"],[MHD_CHECK_POLL_QUIRKS()]) # check for various sendfile functions AC_ARG_ENABLE([sendfile], diff --git a/m4/mhd_check_poll_quirks.m4 b/m4/mhd_check_poll_quirks.m4 @@ -0,0 +1,390 @@ +# SYNOPSIS +# +# MHD_CHECK_POLL_QUIRKS +# +# DESCRIPTION +# +# Check whether the system poll() (or WSAPoll()) function has any known +# quirks. +# +# If any such quirks are detected, corresponding preprocessor macros +# are defined. +# +# LICENSE +# +# Copyright (c) 2025 Karlson2k (Evgeny Grin) <k2k@drgrin.dev> +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 1 + +AC_DEFUN([MHD_CHECK_POLL_QUIRKS],[dnl + AC_PREREQ([2.64])dnl + AC_REQUIRE([AC_CANONICAL_HOST])dnl + AC_REQUIRE([AC_PROG_CC])dnl + AC_LANG_ASSERT([C])dnl + AS_IF([test -z "$use_itc"],[AC_MSG_FAILURE([\$use_itc is not set])]) + AS_VAR_IF([have_poll],["yes"],[:],[AC_MSG_FAILURE([\$have_poll is not 'yes'])]) + AC_CHECK_HEADERS([string.h sys/types.h sys/socket.h netinet/in.h time.h sys/select.h netinet/tcp.h],[],[], [AC_INCLUDES_DEFAULT]) + AC_CACHE_CHECK([[whether poll() clobbers fds.events]], + [[mhd_cv_poll_clobbers_events]], [dnl + AC_RUN_IFELSE([ + AC_LANG_SOURCE([[ + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif +#ifdef HAVE_SYS_SOCKET_H +# include <sys/socket.h> +#endif +#ifdef HAVE_SOCKLIB_H +# include <sockLib.h> +#endif +#ifdef HAVE_INETLIB_H +# include <inetLib.h> +#endif +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif +#ifdef HAVE_SYS_UN_H +# include <sys/un.h> +#endif +#ifdef HAVE_ARPA_INET_H +# include <arpa/inet.h> +#endif +#if defined(HAVE_NETDB_H) +# include <netdb.h> +#endif +#ifdef HAVE_NETINET_TCP_H +# include <netinet/tcp.h> +#endif +#if !defined(_WIN32) || defined(__CYGWIN__) +# include <poll.h> +#else +# include <winsock2.h> +# include <ws2tcpip.h> +# ifndef MHD_ITC_SOCKETPAIR_ +#error Only socketpair ITC supported on native W32 +choke me now +# endif +#endif +#include <stdio.h> + +#if defined(MHD_ITC_EVENTFD_) +# include <sys/eventfd.h> +#elif defined(MHD_ITC_PIPE_) +/* No Additional includes */ +#elif defined(MHD_ITC_SOCKETPAIR_) +/* No Additional includes */ +#else +#error No selected ITC type +choke me now +#endif + +#if defined(PF_INET) +# define my_PF_INET PF_INET +#else +# define my_PF_INET AF_INET +#endif + +#if !defined(_WIN32) || defined(__CYGWIN__) +# define CHECK_USE_POSIX_SOCKETS 1 +# define my_sckt_type int +# define MY_INVALID_SOCKET (-1) +# define my_send3(s,b,l) send((s),(b),(l),0) +# define my_fd_close(skt) close((skt)) +#else +# define CHECK_USE_WINSOCK_SOCKETS 1 +# define my_sckt_type SOCKET +# define MY_INVALID_SOCKET INVALID_SOCKET +# define my_send3(s,b,l) send((s),(const char *)(b),(l),0) +# define my_fd_close(skt) closesocket((skt)) +#endif + +#if defined(MHD_ITC_SOCKETPAIR_) +# ifdef CHECK_USE_POSIX_SOCKETS +# if defined(AF_UNIX) +# define my_socketpair(sk_arr) socketpair(AF_UNIX, SOCK_STREAM, 0, (sk_arr)) +# elif defined(AF_LOCAL) +# define my_socketpair(sk_arr) socketpair(AF_LOCAL, SOCK_STREAM, 0, (sk_arr)) +# else +# define my_socketpair(sk_arr) socketpair(AF_INET, SOCK_STREAM, 0, (sk_arr)) +# endif +# else /* WinSock sockets */ +static int my_socket_nonblocking (SOCKET sckt); + +static int my_socketpair (my_sckt_type sckt[2]) +{ + int i; + +#define PAIR_MAX_TRIES 511 + for (i = 0; i < PAIR_MAX_TRIES; i++) + { + struct sockaddr_in listen_addr; + my_sckt_type listen_s; + static const socklen_t c_addinlen = sizeof(struct sockaddr_in); /* Try to help compiler to optimise */ + socklen_t addr_len = c_addinlen; + + listen_s = socket (AF_INET, + SOCK_STREAM, + 0); + if (INVALID_SOCKET == listen_s) + break; /* can't create even single socket */ + + listen_addr.sin_family = AF_INET; + listen_addr.sin_port = 0; /* same as htons(0) */ + listen_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + if ( ((0 == bind (listen_s, + (struct sockaddr *) &listen_addr, + c_addinlen)) && + (0 == listen (listen_s, + 1) ) && + (0 == getsockname (listen_s, + (struct sockaddr *) &listen_addr, + &addr_len))) ) + { + my_sckt_type client_s = socket (AF_INET, SOCK_STREAM, 0); + struct sockaddr_in accepted_from_addr; + struct sockaddr_in client_addr; + + if (INVALID_SOCKET != client_s) + { + if (my_socket_nonblocking (client_s) && + ( (0 == connect (client_s, + (struct sockaddr *) &listen_addr, + c_addinlen)) || + (WSAEWOULDBLOCK == WSAGetLastError()) )) + { + my_sckt_type server_s; + + addr_len = c_addinlen; + server_s = accept (listen_s, + (struct sockaddr *) &accepted_from_addr, + &addr_len); + if (INVALID_SOCKET != server_s) + { + addr_len = c_addinlen; + if ( (0 == getsockname (client_s, + (struct sockaddr *) &client_addr, + &addr_len)) && + (accepted_from_addr.sin_port == client_addr.sin_port) && + (accepted_from_addr.sin_addr.s_addr == + client_addr.sin_addr.s_addr) ) + { + closesocket (listen_s); + return 0; + } + closesocket (server_s); + } + } + closesocket (client_s); + } + } + closesocket (listen_s); + } + + sckt[0] = INVALID_SOCKET; + sckt[1] = INVALID_SOCKET; + + return -1; +} + +static int my_socket_nonblocking (SOCKET sckt) +{ + unsigned long set_flag = 1; + + if (0 == ioctlsocket (sckt, (long) FIONBIO, &set_flag)) + return !0; + + return 0; +} + +# endif +#endif + +static int check_poll_fn(void) +{ + int res; + int err_res = 0; + struct pollfd fds[2]; +#ifdef MHD_ITC_EVENTFD_ + my_sckt_type itc_fd; +#else + my_sckt_type itc_pair[2]; +#endif + my_sckt_type listen_skt; + +#if defined(MHD_ITC_EVENTFD_) + itc_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (0 > itc_fd) + err_res = 10; + else + { + if (1) /* for local scope */ + { + unsigned char val[8] = {1u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; + if (0 > write (itc_fd, (void *) val, 8)) /* "activate" ITC */ + (void) res; /* ignore result */ + } + fds[0].fd = itc_fd; +#elif defined(MHD_ITC_PIPE_) +# ifdef HAVE_PIPE2_FUNC + res = pipe2(itc_pair, 0 +# ifdef O_CLOEXEC + | O_CLOEXEC +# endif +# ifdef O_NONBLOCK + | O_NONBLOCK +# endif + ); +# else + res = pipe(itc_pair); +# endif + if (0 != res) + err_res = 11; + else + { + if (1) /* for local scope */ + { + unsigned char val = 1u; + if (0 > write (itc_pair[1], (void *) &val, 1)) /* "activate" ITC */ + (void) res; /* ignore result */ + } + fds[0].fd = itc_pair[0]; +#elif defined(MHD_ITC_SOCKETPAIR_) + res = my_socketpair(itc_pair); + if (0 != res) + err_res = 12; + else + { + if (1) /* for local scope */ + { + unsigned char val = 1u; + if (0 > my_send3(itc_pair[1], (void *) &val, 1)) /* "activate" ITC */ + (void) 0; /* ignore result */ + } + fds[0].fd = itc_pair[0]; +#endif + fds[0].events = POLLIN; + + + listen_skt = socket(my_PF_INET, SOCK_STREAM, 0); + if (MY_INVALID_SOCKET == listen_skt) + err_res = 13; + else + { + if (1) /* for local scope */ + { + struct sockaddr_in sk_addr; + + sk_addr.sin_family = AF_INET; + sk_addr.sin_addr.s_addr = INADDR_ANY; + sk_addr.sin_port = 0; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sk_addr.sin_len = sizeof(sk_addr); +#endif + + res = bind(listen_skt, (struct sockaddr *) &sk_addr, sizeof(sk_addr)); + (void) res; /* ignore result */ + } + + res = listen(listen_skt, 8); + (void) res; /* ignore result */ + + fds[1].fd = listen_skt; + fds[1].events = POLLIN; + +#ifdef CHECK_USE_POSIX_SOCKETS + res = poll(fds, 2, 0); +#else + res = WSAPoll(fds, 2, 0); +#endif + if (0 > res) + err_res = 14; + + if (POLLIN != fds[0].events) + { + fprintf (stderr, "fds[0].events changed from 0x%X to 0x%X\n", (unsigned int) POLLIN, (unsigned int) fds[0].events); + err_res = 97; /* Magic number */ + } + if (POLLIN != fds[1].events) + { + fprintf (stderr, "fds[1].events changed from 0x%X to 0x%X\n", (unsigned int) POLLIN, (unsigned int) fds[1].events); + err_res = 97; /* Magic number */ + } + res = my_fd_close(listen_skt); + (void) res; /* Ignore result */ + } +#if defined(MHD_ITC_EVENTFD_) + res = my_fd_close(itc_fd); + (void) res; /* Ignore result */ +#else + res = my_fd_close(itc_pair[1]); + (void) res; /* Ignore result */ + res = my_fd_close(itc_pair[0]); + (void) res; /* Ignore result */ +#endif + } + return err_res; +} + +int main(void) +{ + int err_res; +#ifdef CHECK_USE_WINSOCK_SOCKETS + if (1) /* for local scope */ + { + WSADATA wsa_d; + if (0 != WSAStartup(MAKEWORD(2, 2), &wsa_d)) + return 5; + } +#endif + err_res = check_poll_fn(); + if ((0 != err_res) && (97 != err_res)) + fprintf (stderr, "Failed to preform poll() checking.\n"); + +#ifdef MHD_SOCKETS_KIND_WINSOCK + (void) WSACleanup(); +#endif /* MHD_SOCKETS_KIND_WINSOCK */ + + return err_res; +} + + ]] + ) + ], + [mhd_cv_poll_clobbers_events="no"], + [ + test_res="$?" + AS_IF([test "$test_res" -eq 0],[AC_MSG_FAILURE()], + [test "$test_res" -eq 97],[mhd_cv_poll_clobbers_events="yes"] # Test magic number + ,[mhd_cv_poll_clobbers_events="assuming yes"] + ) + AS_UNSET([test_res]) + ], + [ + AS_CASE(["$host_os"], + [haiku],[mhd_cv_poll_clobbers_events="assuming yes"], + [mhd_cv_poll_clobbers_events="guessing no"] + ) + ] + ) + ] + ) + AS_CASE([$mhd_cv_poll_clobbers_events], + [*yes], + [AC_DEFINE([HAVE_POLL_CLOBBERS_EVENTS], + [1],[Define to '1' is poll() change value of 'events' member of array pointed by 'fds']) + ] + ) +]) diff --git a/src/mhd2/events_process.c b/src/mhd2/events_process.c @@ -1162,9 +1162,13 @@ poll_update_fds (struct MHD_Daemon *restrict d, mhd_assert (mhd_ITC_IS_VALID (d->threading.itc)); mhd_assert (d->events.data.poll.fds[i_s].fd == \ mhd_itc_r_fd (d->threading.itc)); - mhd_assert (POLLIN == d->events.data.poll.fds[i_s].events); mhd_assert (mhd_SOCKET_REL_MARKER_ITC == \ d->events.data.poll.rel[i_s].fd_id); +#ifndef HAVE_POLL_CLOBBERS_EVENTS + mhd_assert (POLLIN == d->events.data.poll.fds[i_s].events); +#else /* HAVE_POLL_CLOBBERS_EVENTS */ + d->events.data.poll.fds[i_s].events = POLLIN; +#endif /* HAVE_POLL_CLOBBERS_EVENTS */ mhd_dbg_print_fd_mon_req ("ITC", \ mhd_itc_r_fd (d->threading.itc), \ true, \ @@ -1178,6 +1182,10 @@ poll_update_fds (struct MHD_Daemon *restrict d, mhd_assert (d->events.data.poll.fds[i_s].fd == d->net.listen.fd); mhd_assert (mhd_SOCKET_REL_MARKER_LISTEN == \ d->events.data.poll.rel[i_s].fd_id); +#ifndef HAVE_POLL_CLOBBERS_EVENTS + mhd_assert ((POLLIN == d->events.data.poll.fds[i_s].events) || + (0 == d->events.data.poll.fds[i_s].events)); +#endif /* ! HAVE_POLL_CLOBBERS_EVENTS */ d->events.data.poll.fds[i_s].events = d->conns.block_new ? 0 : POLLIN; mhd_dbg_print_fd_mon_req ("lstn", \ d->net.listen.fd, \ @@ -1249,6 +1257,9 @@ poll_update_statuses_from_fds (struct MHD_Daemon *restrict d, mhd_itc_r_fd (d->threading.itc)); mhd_assert (mhd_SOCKET_REL_MARKER_ITC == \ d->events.data.poll.rel[i_s].fd_id); +#ifndef HAVE_POLL_CLOBBERS_EVENTS + mhd_assert (POLLIN == d->events.data.poll.fds[i_s].events); +#endif /* ! HAVE_POLL_CLOBBERS_EVENTS */ dbg_print_fd_state_update ( \ "ITC", \ d->events.data.poll.fds[i_s].fd, \ @@ -1286,6 +1297,10 @@ poll_update_statuses_from_fds (struct MHD_Daemon *restrict d, mhd_assert (d->events.data.poll.fds[i_s].fd == d->net.listen.fd); mhd_assert (mhd_SOCKET_REL_MARKER_LISTEN == \ d->events.data.poll.rel[i_s].fd_id); +#ifndef HAVE_POLL_CLOBBERS_EVENTS + mhd_assert ((POLLIN == d->events.data.poll.fds[i_s].events) || + (0 == d->events.data.poll.fds[i_s].events)); +#endif /* ! HAVE_POLL_CLOBBERS_EVENTS */ dbg_print_fd_state_update ("lstn", \ d->events.data.poll.fds[i_s].fd, \ 0 != (revents & (MHD_POLL_IN | POLLIN)), \