From 01fc91c816bf74f1511bf8851fd4dc9ac998e1e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Sun, 21 Mar 2021 00:18:04 -0400
Subject: [PATCH] Stop using select() in *nix for xpdev stuff.

---
 src/xpdev/multisock.c | 65 ++++++++++++++++++++++++++++++++-----------
 src/xpdev/sockwrap.c  | 62 ++++++++++++++++++++++++++++-------------
 2 files changed, 92 insertions(+), 35 deletions(-)

diff --git a/src/xpdev/multisock.c b/src/xpdev/multisock.c
index 6de32e1089..e6483f1a2b 100644
--- a/src/xpdev/multisock.c
+++ b/src/xpdev/multisock.c
@@ -255,22 +255,13 @@ static void btox(char *hexstr, const char *srcbuf, size_t srcbuflen, size_t hexs
 
 static BOOL read_socket(SOCKET sock, char *buffer, size_t len, int (*lprintf)(int level, const char *fmt, ...))
 {
-	fd_set         socket_set;
-	struct timeval tv;
 	size_t            i;
-	int            j,rd;
+	int            rd;
 	unsigned char           ch;
 	char           err[128];
 
 	for (i=0;i<len;i++) {
-		FD_ZERO(&socket_set);
-		FD_SET(sock,&socket_set);
-
-		// We'll wait 1 secs to read from the socket.
-		tv.tv_sec=1;
-		tv.tv_usec=0;
-
-		if((j=select(sock+1,&socket_set,NULL,NULL,&tv))>0) {
+		if (socket_readable(sock, 1000)) {
 			rd = recv(sock,&ch,1,0);
 			if (rd == 0) {
 				lprintf(LOG_WARNING,"%04d multisock read_socket() - remote closed the connection",sock);
@@ -284,13 +275,10 @@ static BOOL read_socket(SOCKET sock, char *buffer, size_t len, int (*lprintf)(in
 				return FALSE;
 			}
 
-		} else if (j==0) {
+		} else {
 			lprintf(LOG_WARNING,"%04d multisock read_socket() - No data?",sock);
 			return FALSE;
 
-		} else {
-			lprintf(LOG_WARNING,"%04d multisock read_socket() - select() returned [%d] with error [%s].",sock,j,socket_strerror(socket_errno,err,sizeof(err)));
-			return FALSE;
 		}
 	}
 
@@ -324,11 +312,17 @@ static BOOL read_socket_line(SOCKET sock, char *buffer, size_t buflen, int (*lpr
 SOCKET DLLCALL xpms_accept(struct xpms_set *xpms_set, union xp_sockaddr * addr, 
 	socklen_t * addrlen, unsigned int timeout, uint32_t flags, void **cb_data)
 {
+#ifdef _WIN32	// Use select()
 	fd_set         read_fs;
-	size_t         i;
 	struct timeval tv;
 	struct timeval *tvp;
 	SOCKET         max_sock=0;
+#else	// Use poll()
+	struct pollfd *fds;
+	int poll_timeout;
+	nfds_t scnt = 0;
+#endif
+	size_t         i;
 	SOCKET         ret;
 	char           hapstr[128];
 	char           haphex[256];
@@ -336,6 +330,10 @@ SOCKET DLLCALL xpms_accept(struct xpms_set *xpms_set, union xp_sockaddr * addr,
 	long           l;
 	void           *vp;
 
+	if (xpms_set->sock_count < 1)
+		return INVALID_SOCKET;
+
+#ifdef _WIN32
 	FD_ZERO(&read_fs);
 	for(i=0; i<xpms_set->sock_count; i++) {
 		if(xpms_set->socks[i].sock == INVALID_SOCKET)
@@ -362,6 +360,41 @@ SOCKET DLLCALL xpms_accept(struct xpms_set *xpms_set, union xp_sockaddr * addr,
 				if(xpms_set->socks[i].sock == INVALID_SOCKET)
 					continue;
 				if(FD_ISSET(xpms_set->socks[i].sock, &read_fs)) {
+#else
+	fds = calloc(xpms_set->sock_count, sizeof(*fds));
+	for (i = 0; i < xpms_set->sock_count; i++) {
+		if (xpms_set->socks[i].sock == INVALID_SOCKET)
+			continue;
+		fds[scnt].fd = xpms_set->socks[i].sock;
+		fds[scnt].events = POLLIN;
+		scnt++;
+	}
+
+	if (timeout == XPMS_FOREVER)
+		poll_timeout = -1;
+	else if (timeout > INT_MAX)
+		poll_timeout = INT_MAX;
+	else
+		poll_timeout = timeout;
+
+	switch (poll(fds, scnt, timeout)) {
+		case 0:
+			return INVALID_SOCKET;
+		case -1:
+			return SOCKET_ERROR;
+		default:
+			scnt = 0;
+			for(i=0; i<xpms_set->sock_count; i++) {
+				if(xpms_set->socks[i].sock == INVALID_SOCKET)
+					continue;
+				if ((fds[scnt].revents & POLLIN) == 0) {
+					closesocket(xpms_set->socks[i].sock);
+					xpms_set->lprintf(LOG_ERR, "%04d * Listening socket went bad", xpms_set->socks[i].sock);
+					xpms_set->socks[i].sock = INVALID_SOCKET;
+					continue;
+				}
+				else {
+#endif
 					if(cb_data)
 						*cb_data=xpms_set->socks[i].cb_data;
 					ret =  accept(xpms_set->socks[i].sock, &addr->addr, addrlen);
diff --git a/src/xpdev/sockwrap.c b/src/xpdev/sockwrap.c
index d45fd475a3..e0abe84747 100644
--- a/src/xpdev/sockwrap.c
+++ b/src/xpdev/sockwrap.c
@@ -272,6 +272,7 @@ off_t recvfilesocket(int sock, int file, off_t *offset, off_t count)
 /* Return true if connected, optionally sets *rd_p to true if read data available */
 BOOL socket_check(SOCKET sock, BOOL* rd_p, BOOL* wr_p, DWORD timeout)
 {
+#ifdef _WIN32
 	char	ch;
 	int		i,rd;
 	fd_set	rd_set;
@@ -327,6 +328,42 @@ BOOL socket_check(SOCKET sock, BOOL* rd_p, BOOL* wr_p, DWORD timeout)
 	}
 
 	return(FALSE);
+#else
+	struct pollfd pfd = {0};
+	int j;
+
+	if(rd_p!=NULL)
+		*rd_p=FALSE;
+
+	if(wr_p!=NULL)
+		*wr_p=FALSE;
+
+	if(sock==INVALID_SOCKET)
+		return(FALSE);
+
+	pfd.fd = sock;
+	pfd.events = POLLIN;
+	if (wr_p != NULL)
+		pfd.events |= POLLOUT;
+
+	j = poll(&pfd, 1, timeout);
+
+	if (j == 0)
+		return TRUE;
+
+	if (j == 1) {
+		if (wr_p != NULL && (pfd.revents & POLLOUT))
+			*wr_p = TRUE;
+		if (rd_p != NULL && (pfd.revents & POLLIN)) {
+			*rd_p = TRUE;
+			return TRUE;
+		}
+
+		if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL))
+			return FALSE;
+	}
+	return FALSE;
+#endif
 }
 
 /*
@@ -494,32 +531,19 @@ int retry_bind(SOCKET s, const struct sockaddr *addr, socklen_t addrlen
 int nonblocking_connect(SOCKET sock, struct sockaddr* addr, size_t size, unsigned timeout)
 {
 	int result;
+	socklen_t optlen;
 
 	result=connect(sock, addr, size);
 
 	if(result==SOCKET_ERROR) {
 		result=ERROR_VALUE;
 		if(result==EWOULDBLOCK || result==EINPROGRESS) {
-			fd_set		wsocket_set;
-			fd_set		esocket_set;
-			struct		timeval tv;
-			socklen_t	optlen=sizeof(result);
-			tv.tv_sec = timeout;
-			tv.tv_usec = 0;
-			FD_ZERO(&wsocket_set);
-			FD_SET(sock,&wsocket_set);
-			FD_ZERO(&esocket_set);
-			FD_SET(sock,&esocket_set);
-			switch(select(sock+1,NULL,&wsocket_set,&esocket_set,&tv)) {
-				case 1:
-					if(getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&result, &optlen)==SOCKET_ERROR)
-						result=ERROR_VALUE;
-					break;
-				case 0:
-					break;
-				case SOCKET_ERROR:
+			if (socket_writable(sock, timeout * 1000)) {
+				result = 0;
+			}
+			else {
+				if(getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&result, &optlen)==SOCKET_ERROR)
 					result=ERROR_VALUE;
-					break;
 			}
 		}
 	}
-- 
GitLab