diff --git a/src/sbbs3/execnet.cpp b/src/sbbs3/execnet.cpp
index bda3041be4f6e0b9c5a9c54133f61cba01fc2627..effeae4fadc4b6c10e9efd0baaad80fd3894f9b4 100644
--- a/src/sbbs3/execnet.cpp
+++ b/src/sbbs3/execnet.cpp
@@ -643,15 +643,12 @@ bool sbbs_t::ftp_get(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest, bool d
 	char		rsp[512];
 	char		buf[4097];
 	int			rd;
-	int			result;
 	BOOL		data_avail;
 	ulong		total=0;
 	SOCKET		data_sock;
 	union xp_sockaddr	addr;
 	socklen_t	addr_len;
 	FILE*		fp=NULL;
-	struct timeval	tv;
-	fd_set			socket_set;
 
 	if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr.in))==INVALID_SOCKET)
 		return(false);
@@ -684,16 +681,7 @@ bool sbbs_t::ftp_get(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest, bool d
 	}
 
 	if(!(csi->ftp_mode&CS_FTP_PASV)) {	/* Normal (Active) FTP */
-
-		/* Setup for select() */
-		tv.tv_sec=TIMEOUT_SOCK_LISTEN;
-		tv.tv_usec=0;
-
-		FD_ZERO(&socket_set);
-		FD_SET(data_sock,&socket_set);
-
-		result=select(data_sock+1,&socket_set,NULL,NULL,&tv);
-		if(result<1) {
+		if(!socket_readable(data_sock, TIMEOUT_SOCK_LISTEN * 1000)) {
 			csi->socket_error=ERROR_VALUE;
 			closesocket(data_sock);
 			return(false);
@@ -770,15 +758,12 @@ bool sbbs_t::ftp_put(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest)
 	char		path[MAX_PATH+1];
 	char		buf[4097];
 	int			rd;
-	int			result;
 	ulong		total=0;
 	SOCKET		data_sock;
 	union xp_sockaddr	addr;
 	socklen_t	addr_len;
 	FILE*		fp=NULL;
 	bool		error=false;
-	struct timeval	tv;
-	fd_set			socket_set;
 
 	SAFECOPY(path,src);
 
@@ -823,16 +808,7 @@ bool sbbs_t::ftp_put(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest)
 	}
 
 	if(!(csi->ftp_mode&CS_FTP_PASV)) {	/* Normal (Active) FTP */
-
-		/* Setup for select() */
-		tv.tv_sec=TIMEOUT_SOCK_LISTEN;
-		tv.tv_usec=0;
-
-		FD_ZERO(&socket_set);
-		FD_SET(data_sock,&socket_set);
-
-		result=select(data_sock+1,&socket_set,NULL,NULL,&tv);
-		if(result<1) {
+		if(!socket_readable(data_sock, TIMEOUT_SOCK_LISTEN * 1000)) {
 			csi->socket_error=ERROR_VALUE;
 			closesocket(data_sock);
 			fclose(fp);
diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c
index 7e086b041cd0a09118b29e2db730bc7537c1efe5..5ae915ec5489c5d93b8a4364bd72d87a15fcf92b 100644
--- a/src/sbbs3/ftpsrvr.c
+++ b/src/sbbs3/ftpsrvr.c
@@ -310,8 +310,6 @@ static int sockprintf(SOCKET sock, CRYPT_SESSION sess, char *fmt, ...)
 	int		result;
 	va_list argptr;
 	char	sbuf[1024];
-	fd_set	socket_set;
-	struct timeval tv;
 	char	*estr;
 
     va_start(argptr,fmt);
@@ -330,20 +328,9 @@ static int sockprintf(SOCKET sock, CRYPT_SESSION sess, char *fmt, ...)
 		return(0);
 	}
 
-	/* Check socket for writability (using select) */
-	tv.tv_sec=300;
-	tv.tv_usec=0;
-
-	FD_ZERO(&socket_set);
-	FD_SET(sock,&socket_set);
-
-	if((result=select(sock+1,NULL,&socket_set,NULL,&tv))<1) {
-		if(result==0)
-			lprintf(LOG_WARNING,"%04d !TIMEOUT selecting socket for send"
-				,sock);
-		else
-			lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for send"
-				,sock, ERROR_VALUE);
+	/* Check socket for writability */
+	if(!socket_writable(sock, 300000)) {
+		lprintf(LOG_WARNING,"%04d !WARNING socket not ready for write" ,sock);
 		return(0);
 	}
 
@@ -458,8 +445,6 @@ void recverror(SOCKET socket, int rd, int line)
 static int sock_recvbyte(SOCKET sock, CRYPT_SESSION sess, char *buf, time_t *lastactive)
 {
 	int len=0;
-	fd_set	socket_set;
-	struct	timeval	tv;
 	int ret;
 	int i;
 	char *estr;
@@ -476,7 +461,7 @@ static int sock_recvbyte(SOCKET sock, CRYPT_SESSION sess, char *buf, time_t *las
 			GCES(ret, sock, sess, estr, "setting read timeout");
 		while (1) {
 			ret = cryptPopData(sess, buf, 1, &len);
-			/* Successive reads will be with the full timeout after a select() */
+			/* Successive reads will be with the full timeout after a socket_readable() */
 			cryptSetAttribute(sess, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity);
 			switch(ret) {
 				case CRYPT_OK:
@@ -506,50 +491,26 @@ static int sock_recvbyte(SOCKET sock, CRYPT_SESSION sess, char *buf, time_t *las
 				return(0);
 			}
 
-			tv.tv_sec=startup->max_inactivity;
-			tv.tv_usec=0;
-
-			FD_ZERO(&socket_set);
-			FD_SET(sock,&socket_set);
-
-			i=select(sock+1,&socket_set,NULL,NULL,&tv);
-
-			if(i<1) {
-				if(i==0) {
-					if((time(NULL)-(*lastactive))>startup->max_inactivity) {
-						lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
-						sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."
-							,startup->max_inactivity);
-						return(0);
-					}
-					continue;
+			if (!socket_readable(sock, startup->max_inactivity * 1000)) {
+				if((time(NULL)-(*lastactive))>startup->max_inactivity) {
+					lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
+					sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."
+						,startup->max_inactivity);
+					return(0);
 				}
-				recverror(sock,i,__LINE__);
-				return(i);
 			}
 		}
 	}
 	else {
 		while (1) {
-			tv.tv_sec=startup->max_inactivity;
-			tv.tv_usec=0;
-
-			FD_ZERO(&socket_set);
-			FD_SET(sock,&socket_set);
-
-			i=select(sock+1,&socket_set,NULL,NULL,&tv);
-
-			if(i<1) {
-				if(i==0) {
-					if((time(NULL)-(*lastactive))>startup->max_inactivity) {
-						lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
-						sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."
-							,startup->max_inactivity);
-						return(0);
-					}
-					continue;
+			if (!socket_readable(sock, startup->max_inactivity * 1000)) {
+				if((time(NULL)-(*lastactive))>startup->max_inactivity) {
+					lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
+					sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."
+						,startup->max_inactivity);
+					return(0);
 				}
-				return(i);
+				continue;
 			}
 	#ifdef SOCKET_DEBUG_RECV_CHAR
 			socket_debug[sock]|=SOCKET_DEBUG_RECV_CHAR;
@@ -661,8 +622,6 @@ static void send_thread(void* arg)
 	user_t		uploader;
 	union xp_sockaddr	addr;
 	socklen_t	addr_len;
-	fd_set		socket_set;
-	struct timeval tv;
 	char		*estr;
 
 	xfer=*(xfer_t*)arg;
@@ -742,22 +701,8 @@ static void send_thread(void* arg)
 			break;
 		}
 
-		/* Check socket for writability (using select) */
-		tv.tv_sec=1;
-		tv.tv_usec=0;
-
-		FD_ZERO(&socket_set);
-		FD_SET(*xfer.data_sock,&socket_set);
-
-		i=select((*xfer.data_sock)+1,NULL,&socket_set,NULL,&tv);
-		if(i==SOCKET_ERROR) {
-			lprintf(LOG_WARNING,"%04d <%s> !DATA ERROR %d selecting socket %d for send"
-				,xfer.ctrl_sock, xfer.user->alias, ERROR_VALUE, *xfer.data_sock);
-			sockprintf(xfer.ctrl_sock,xfer.ctrl_sess,"426 Transfer error.");
-			error=TRUE;
-			break;
-		}
-		if(i<1)
+		/* Check socket for writability */
+		if (!socket_writable(*xfer.data_sock, 1000))
 			continue;
 
 		(void)fseek(fp,xfer.filepos+total,SEEK_SET);
@@ -954,8 +899,6 @@ static void receive_thread(void* arg)
 	time_t		now;
 	time_t		start;
 	time_t		last_report;
-	fd_set		socket_set;
-	struct timeval tv;
 	CRYPT_SESSION	sess = -1;
 	char		*estr;
 
@@ -1029,22 +972,8 @@ static void receive_thread(void* arg)
 			break;
 		}
 
-		/* Check socket for readability (using select) */
-		tv.tv_sec=1;
-		tv.tv_usec=0;
-
-		FD_ZERO(&socket_set);
-		FD_SET(*xfer.data_sock,&socket_set);
-
-		i=select((*xfer.data_sock)+1,&socket_set,NULL,NULL,&tv);
-		if(i==SOCKET_ERROR) {
-			lprintf(LOG_WARNING,"%04d <%s> !DATA ERROR %d selecting socket %d for receive"
-				,xfer.ctrl_sock, xfer.user->alias, ERROR_VALUE, *xfer.data_sock);
-			sockprintf(xfer.ctrl_sock,sess,"426 Transfer error.");
-			error=TRUE;
-			break;
-		}
-		if(i<1)
+		/* Check socket for readability */
+		if (!socket_readable(*xfer.data_sock, 1000))
 			continue;
 
 #if defined(SOCKET_DEBUG_RECV_BUF)
@@ -1333,8 +1262,6 @@ static void filexfer(union xp_sockaddr* addr, SOCKET ctrl_sock, CRYPT_SESSION ct
 	union xp_sockaddr	server_addr;
 	BOOL		reuseaddr;
 	xfer_t*		xfer;
-	struct timeval	tv;
-	fd_set			socket_set;
 	char		host_ip[INET6_ADDRSTRLEN];
 
 	if((*inprogress)==TRUE) {
@@ -1434,23 +1361,9 @@ static void filexfer(union xp_sockaddr* addr, SOCKET ctrl_sock, CRYPT_SESSION ct
 					,ctrl_sock, user->alias,pasv_sock,host_ip,inet_addrport(addr));
 		}
 
-		/* Setup for select() */
-		tv.tv_sec=TIMEOUT_SOCKET_LISTEN;
-		tv.tv_usec=0;
-
-		FD_ZERO(&socket_set);
-		FD_SET(pasv_sock,&socket_set);
-
-#if defined(SOCKET_DEBUG_SELECT)
-		socket_debug[ctrl_sock]|=SOCKET_DEBUG_SELECT;
-#endif
-		result=select(pasv_sock+1,&socket_set,NULL,NULL,&tv);
-#if defined(SOCKET_DEBUG_SELECT)
-		socket_debug[ctrl_sock]&=~SOCKET_DEBUG_SELECT;
-#endif
-		if(result<1) {
-			lprintf(LOG_WARNING,"%04d <%s> PASV !DATA select returned %d (error: %d)"
-				,ctrl_sock, user->alias,result,ERROR_VALUE);
+		if (!socket_readable(pasv_sock, TIMEOUT_SOCKET_LISTEN * 1000)) {
+			lprintf(LOG_WARNING,"%04d <%s> PASV !WARNING socket not readable"
+				,ctrl_sock, user->alias);
 			sockprintf(ctrl_sock,ctrl_sess,"425 Error %d selecting socket for connection",ERROR_VALUE);
 			if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES))
 				(void)ftp_remove(ctrl_sock, __LINE__, filename, user->alias);
diff --git a/src/sbbs3/ident.c b/src/sbbs3/ident.c
index 7c1d24b6fc9ec53c50dc9b158b786ba6723c774e..6fe4e4e6dff17c17cf58c8821f0485be126521ab 100644
--- a/src/sbbs3/ident.c
+++ b/src/sbbs3/ident.c
@@ -42,14 +42,11 @@ BOOL identify(union xp_sockaddr *client_addr, u_short local_port, char* buf
 			   ,size_t maxlen, int timeout)
 {
 	char		req[128];
-	int			i;
 	int			result;
 	int			rd;
 	ulong		val;
 	SOCKET		sock=INVALID_SOCKET;
 	union xp_sockaddr	addr;
-	struct timeval	tv;
-	fd_set			socket_set;
 	BOOL		success=FALSE;
 
 	if(client_addr->addr.sa_family != AF_INET && client_addr->addr.sa_family != AF_INET6)
@@ -73,12 +70,7 @@ BOOL identify(union xp_sockaddr *client_addr, u_short local_port, char* buf
 
 		if(result==SOCKET_ERROR
 			&& (ERROR_VALUE==EWOULDBLOCK || ERROR_VALUE==EINPROGRESS)) {
-			tv.tv_sec=timeout;
-			tv.tv_usec=0;
-
-			FD_ZERO(&socket_set);
-			FD_SET(sock,&socket_set);
-			if(select(sock+1,NULL,&socket_set,NULL,&tv)==1)
+			if (socket_writable(sock, timeout * 1000))
 				result=0;	/* success */
 		}
 		if(result!=0) {
@@ -89,14 +81,7 @@ BOOL identify(union xp_sockaddr *client_addr, u_short local_port, char* buf
 		val=0;
 		ioctlsocket(sock,FIONBIO,&val);	
 
-		tv.tv_sec=10;
-		tv.tv_usec=0;
-
-		FD_ZERO(&socket_set);
-		FD_SET(sock,&socket_set);
-
-		i=select(sock+1,NULL,&socket_set,NULL,&tv);
-		if(i<1) {
+		if(!socket_writable(sock, 10000)) {
 			sprintf(buf,"ERROR %d selecting socket for send",ERROR_VALUE);
 			break;
 		}
@@ -107,14 +92,7 @@ BOOL identify(union xp_sockaddr *client_addr, u_short local_port, char* buf
 			break;
 		}
 
-		tv.tv_sec=10;
-		tv.tv_usec=0;
-
-		FD_ZERO(&socket_set);
-		FD_SET(sock,&socket_set);
-
-		i=select(sock+1,&socket_set,NULL,NULL,&tv);
-		if(i<1) {
+		if(!socket_readable(sock, 10000)) {
 			sprintf(buf,"ERROR %d detecting response",ERROR_VALUE);
 			break;
 		}
diff --git a/src/sbbs3/js_file.c b/src/sbbs3/js_file.c
index 9c395972dd12f964deabed9b18479d638b2acd3a..fe05c0474750034eb958133526b0eadd595ca3c9 100644
--- a/src/sbbs3/js_file.c
+++ b/src/sbbs3/js_file.c
@@ -287,10 +287,6 @@ js_raw_pollin(JSContext *cx, uintN argc, jsval *arglist)
 	private_t*	p;
 	jsrefcount	rc;
 	int32		timeout = -1;
-#ifdef __unix__
-	fd_set		rd;
-	struct	timeval tv = {0, 0};
-#endif
 
 	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
 		return(JS_FALSE);
@@ -307,13 +303,11 @@ js_raw_pollin(JSContext *cx, uintN argc, jsval *arglist)
 	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(FALSE));
 	rc=JS_SUSPENDREQUEST(cx);
 #ifdef __unix__
-	if (timeout >= 0) {
-		tv.tv_sec = timeout / 1000;
-		tv.tv_usec = (timeout%1000)*1000;
-	}
-	FD_ZERO(&rd);
-	FD_SET(fileno(p->fp), &rd);
-	if (select(fileno(p->fp)+1, &rd, NULL, NULL, timeout < 0 ? NULL : &tv) == 1)
+	/*
+	 * TODO: macOS poll() page has the ominous statement that "The poll() system call currently does not support devices."
+	 *       But, since we don't support OS X in Synchronet that's likely OK?
+	 */
+	if (socket_readable(fileno(p->fp), timeout))
 		JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(TRUE));
 #else
 	while(timeout) {
diff --git a/src/sbbs3/js_global.c b/src/sbbs3/js_global.c
index 96413cee194cb351f68e8776283d4f226b035eaa..ee90b87355f9dcd3dda3c83f7fa6a2749f7ece59 100644
--- a/src/sbbs3/js_global.c
+++ b/src/sbbs3/js_global.c
@@ -3695,12 +3695,21 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
 	JSObject*	robj;
 	JSObject*	rarray;
 	BOOL		poll_for_write=FALSE;
+#ifdef _WIN32
 	fd_set		socket_set[3];
 	fd_set*		sets[3] = {NULL, NULL, NULL};
-	uintN		argn;
-	SOCKET		sock;
 	SOCKET		maxsock=0;
 	struct		timeval tv = {0, 0};
+	SOCKET		sock;
+#else
+	struct pollfd *fds;
+	int poll_timeout = 0;
+	nfds_t nfds;
+	short events;
+	int scount;
+	int k;
+#endif
+	uintN		argn;
 	jsuint		i;
 	jsuint		j;
 	jsuint      limit[3];
@@ -3717,8 +3726,13 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
 			poll_for_write=JSVAL_TO_BOOLEAN(argv[argn]);
 		else if(JSVAL_IS_OBJECT(argv[argn]))
 			inarray[inarray_cnt++] = JSVAL_TO_OBJECT(argv[argn]);
+#ifdef _WIN32
 		else if(JSVAL_IS_NUMBER(argv[argn]))
 			js_timeval(cx,argv[argn],&tv);
+#else
+		else if(JSVAL_IS_NUMBER(argv[argn]))
+			poll_timeout = js_polltimeout(cx, argv[argn]);
+#endif
 	}
 
 	if(inarray_cnt == 0)
@@ -3739,6 +3753,7 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
 		/* Return array */
 		if((robj = JS_NewArrayObject(cx, 0, NULL))==NULL)
 			return(JS_FALSE);
+#ifdef _WIN32
 		FD_ZERO(&socket_set[0]);
 		if(poll_for_write)
 			sets[1]=&socket_set[0];
@@ -3773,6 +3788,56 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
 
 			JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(robj));
 		}
+#else
+		// First, count the number of sockets...
+		nfds = 0;
+		for (i = 0; i < limit[0]; i++) {
+			if(!JS_GetElement(cx, inarray[0], i, &val))
+				break;
+			nfds += js_socket_numsocks(cx, val);
+		}
+		if (nfds == 0)
+			return JS_TRUE;
+
+		fds = calloc(nfds, sizeof(*fds));
+		if (fds == NULL) {
+			JS_ReportError(cx, "Error allocating %d elements of %lu bytes at %s:%d"
+				, nfds, sizeof(*fds), getfname(__FILE__), __LINE__);
+			return JS_FALSE;
+		}
+		nfds = 0;
+		for (i = 0; i < limit[0]; i++) {
+			if(!JS_GetElement(cx, inarray[0], i, &val))
+				break;
+			nfds += js_socket_add(cx, val, &fds[nfds], poll_for_write ? POLLOUT : POLLIN);
+		}
+
+		rc = JS_SUSPENDREQUEST(cx);
+		if (poll(fds, nfds, poll_timeout) >= 0) {
+			nfds = 0;
+			for (i = 0; i < limit[0]; i++) {
+				if(!JS_GetElement(cx, inarray[0], i, &val))
+					break;
+				scount = js_socket_numsocks(cx, val);
+				for (k = 0; k < scount; k++) {
+					if (fds[nfds + k].revents & (poll_for_write ? POLLOUT : POLLIN | POLLHUP)) {
+						val=INT_TO_JSVAL(i);
+						JS_RESUMEREQUEST(cx, rc);
+						if(!JS_SetElement(cx, robj, len++, &val)) {
+							rc=JS_SUSPENDREQUEST(cx);
+							break;
+						}
+						rc=JS_SUSPENDREQUEST(cx);
+						break;
+					}
+				}
+				nfds += scount;
+			}
+
+			JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(robj));
+		}
+		free(fds);
+#endif
 		JS_RESUMEREQUEST(cx, rc);
 
 		return(JS_TRUE);
@@ -3781,6 +3846,7 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
 		/* Return object */
 		if((robj = JS_NewObject(cx, NULL, NULL, NULL))==NULL)
 			return(JS_FALSE);
+#ifdef _WIN32
 		for (j = 0; j < inarray_cnt; j++) {
 			if (limit[j] > 0) {
 				FD_ZERO(&socket_set[j]);
@@ -3830,6 +3896,122 @@ js_socket_select(JSContext *cx, uintN argc, jsval *arglist)
 			}
 			JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(robj));
 		}
+#else
+		/*
+		 * So, we need to collapse all the FDs into a list with flags set...
+		 * readfd corresponds to POLLIN
+		 * writefd corresponds to POLLOUT
+		 * and exceptfd corresponds to POLLPRI
+		 *
+		 * We don't want duplicates, and we don't want to set things that
+		 * weren't requested.
+		 *
+		 * Brute-force method is to add them all to an array, sort the array,
+		 * then remove duplicates after ORing the events together.
+		 *
+		 * NOTE that some of the socket objects may actually be duplicates
+		 * themselves, and all of them should set with the appropriate status.
+		 *
+		 * Alternatively, we can just add them all in-order and pass a larger
+		 * array to poll().  This will certainly be more efficient when
+		 * generating the returned arrays.
+		 */
+		nfds = 0;
+		for (j = 0; j < inarray_cnt; j++) {
+			if (limit[j] > 0) {
+				for (i = 0; i < limit[j]; i++) {
+					if(!JS_GetElement(cx, inarray[0], i, &val))
+						break;
+					nfds += js_socket_numsocks(cx, val);
+				}
+			}
+		}
+		if (nfds == 0)
+			return JS_TRUE;
+
+		fds = calloc(nfds, sizeof(*fds));
+		if (fds == NULL) {
+			JS_ReportError(cx, "Error allocating %d elements of %lu bytes at %s:%d"
+				, nfds, sizeof(*fds), getfname(__FILE__), __LINE__);
+			return JS_FALSE;
+		}
+		nfds = 0;
+		for (j = 0; j < inarray_cnt; j++) {
+			if (limit[j] > 0) {
+				switch (j) {
+					case 0:
+						events = POLLIN;
+						break;
+					case 1:
+						events = POLLOUT;
+						break;
+					case 2:
+						events = POLLPRI;
+						break;
+				}
+				for (i = 0; i < limit[j]; i++) {
+					if(!JS_GetElement(cx, inarray[j], i, &val))
+						break;
+					nfds += js_socket_add(cx, val, &fds[nfds], poll_for_write ? POLLOUT : POLLIN);
+				}
+			}
+		}
+
+		rc = JS_SUSPENDREQUEST(cx);
+		if (poll(fds, nfds, poll_timeout) >= 0) {
+			nfds = 0;
+			for (j = 0; j < inarray_cnt; j++) {
+				if (limit[j] > 0) {
+					switch (j) {
+						case 0:
+							events = POLLIN | POLLHUP;
+							break;
+						case 1:
+							events = POLLOUT;
+							break;
+						case 2:
+							events = POLLPRI;
+							break;
+					}
+					len = 0;
+					JS_RESUMEREQUEST(cx, rc);
+					if((rarray = JS_NewArrayObject(cx, 0, NULL))==NULL) {
+						free(fds);
+						return(JS_FALSE);
+					}
+					val = OBJECT_TO_JSVAL(rarray);
+					if (!JS_SetProperty(cx, robj, props[j], &val)) {
+						free(fds);
+						return JS_FALSE;
+					}
+					rc=JS_SUSPENDREQUEST(cx);
+					for(i=0;i<limit[j];i++) {
+						JS_RESUMEREQUEST(cx, rc);
+						if(!JS_GetElement(cx, inarray[j], i, &val)) {
+							rc=JS_SUSPENDREQUEST(cx);
+							break;
+						}
+						rc=JS_SUSPENDREQUEST(cx);
+						scount = js_socket_numsocks(cx, val);
+						for (k = 0; k < scount; k++) {
+							if(fds[nfds + k].revents & (events | POLLERR | POLLNVAL)) {
+								val=INT_TO_JSVAL(i);
+								JS_RESUMEREQUEST(cx, rc);
+								if(!JS_SetElement(cx, rarray, len++, &val)) {
+									rc=JS_SUSPENDREQUEST(cx);
+									break;
+								}
+								rc=JS_SUSPENDREQUEST(cx);
+							}
+						}
+						nfds += scount;
+					}
+				}
+			}
+			JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(robj));
+		}
+		free(fds);
+#endif
 		JS_RESUMEREQUEST(cx, rc);
 
 		return(JS_TRUE);
diff --git a/src/sbbs3/js_socket.c b/src/sbbs3/js_socket.c
index 779cf04da598c89f85d6715566711c06dc4c3827..4d732b24f65206fc5d00e6c15e6ca434e0647c6c 100644
--- a/src/sbbs3/js_socket.c
+++ b/src/sbbs3/js_socket.c
@@ -196,8 +196,6 @@ static ptrdiff_t js_socket_recv(js_socket_private_t *p, void *buf, size_t len, i
 {
 	ptrdiff_t	total=0;
 	int	copied,ret;
-	fd_set		socket_set;
-	struct		timeval tv = {0, 0};
 	char *estr;
 	time_t now = time(NULL);
 	int status;
@@ -227,10 +225,8 @@ static ptrdiff_t js_socket_recv(js_socket_private_t *p, void *buf, size_t len, i
 			if (p->sock == INVALID_SOCKET)
 				ret = -1;
 			else {
-				FD_ZERO(&socket_set);
-				FD_SET(p->sock,&socket_set);
-				tv.tv_sec = timeout;
-				if((ret = select(p->sock+1,&socket_set,NULL,NULL,&tv))==1)
+				ret = 0;
+				if (socket_readable(p->sock, timeout * 1000))
 					ret = recv(p->sock, buf, len, flags);
 			}
 		}
@@ -476,6 +472,7 @@ SOCKET DLLCALL js_socket(JSContext *cx, jsval val)
 	return(sock);
 }
 
+#ifdef _WIN32
 SOCKET DLLCALL js_socket_add(JSContext *cx, jsval val, fd_set *fds)
 {
 	js_socket_private_t	*p;
@@ -562,6 +559,98 @@ void DLLCALL js_timeval(JSContext* cx, jsval val, struct timeval* tv)
 		}
 	}
 }
+#else
+size_t DLLCALL js_socket_numsocks(JSContext *cx, jsval val)
+{
+	js_socket_private_t	*p;
+	JSClass*	cl;
+	SOCKET		sock=INVALID_SOCKET;
+	size_t		i;
+	int32_t		intval;
+	size_t ret = 0;
+
+	if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) {
+		if(cl->flags&JSCLASS_HAS_PRIVATE) {
+			if((p=(js_socket_private_t *)JS_GetInstancePrivate(cx,JSVAL_TO_OBJECT(val),&js_socket_class,NULL))!=NULL) {
+				if(p->set) {
+					for(i=0; i<p->set->sock_count; i++) {
+						if(p->set->socks[i].sock == INVALID_SOCKET)
+							continue;
+						ret++;
+					}
+				}
+				else {
+					sock = p->sock;
+					if(sock != INVALID_SOCKET)
+						ret = 1;
+				}
+			}
+		}
+	} else if(val!=JSVAL_VOID) {
+		if(JS_ValueToInt32(cx,val,&intval)) {
+			if (intval != INVALID_SOCKET)
+				ret = 1;
+		}
+	}
+	return ret;
+}
+
+size_t DLLCALL js_socket_add(JSContext *cx, jsval val, struct pollfd *fds, short events)
+{
+	js_socket_private_t	*p;
+	JSClass*	cl;
+	SOCKET		sock=INVALID_SOCKET;
+	size_t		i;
+	int32_t		intval;
+	size_t ret = 0;
+
+	if(JSVAL_IS_OBJECT(val) && (cl=JS_GetClass(cx,JSVAL_TO_OBJECT(val)))!=NULL) {
+		if(cl->flags&JSCLASS_HAS_PRIVATE) {
+			if((p=(js_socket_private_t *)JS_GetInstancePrivate(cx,JSVAL_TO_OBJECT(val),&js_socket_class,NULL))!=NULL) {
+				if(p->set) {
+					for(i=0; i<p->set->sock_count; i++) {
+						if(p->set->socks[i].sock == INVALID_SOCKET)
+							continue;
+						fds[ret].events = events;
+						fds[ret++].fd = p->set->socks[i].sock;
+					}
+				}
+				else {
+					sock = p->sock;
+					if(sock != INVALID_SOCKET) {
+						fds[ret].events = events;
+						fds[ret++].fd = sock;
+					}
+				}
+			}
+		}
+	} else if(val!=JSVAL_VOID) {
+		if(JS_ValueToInt32(cx,val,&intval)) {
+			sock = intval;
+			if(sock != INVALID_SOCKET) {
+				fds[ret].events = events;
+				fds[ret++].fd = sock;
+			}
+		}
+	}
+	return ret;
+}
+#endif
+
+int DLLCALL js_polltimeout(JSContext* cx, jsval val)
+{
+	jsdouble jsd;
+
+	if(JSVAL_IS_INT(val))
+		return JSVAL_TO_INT(val) * 1000;
+
+	if(JSVAL_IS_DOUBLE(val)) {
+		if(JS_ValueToNumber(cx,val,&jsd))
+			return jsd * 1000;
+	}
+
+	return 0;
+}
 
 static JSBool
 js_bind(JSContext *cx, uintN argc, jsval *arglist)
@@ -730,11 +819,10 @@ js_connect(JSContext *cx, uintN argc, jsval *arglist)
 	ushort		port;
 	JSString*	str;
 	js_socket_private_t*	p;
-	fd_set		socket_set;
-	struct		timeval tv = {0, 0};
 	jsrefcount	rc;
 	char		ip_str[256];
 	struct addrinfo	hints,*res,*cur;
+	int timeout = 10000; /* Default time-out */
 
 	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
 		return(JS_FALSE);
@@ -764,10 +852,8 @@ js_connect(JSContext *cx, uintN argc, jsval *arglist)
 	ioctlsocket(p->sock,FIONBIO,&val);
 	result = SOCKET_ERROR;
 	for(cur=res; cur != NULL; cur=cur->ai_next) {
-		tv.tv_sec = 10;	/* default time-out */
-
 		if(argc>2)	/* time-out value specified */
-			js_timeval(cx,argv[2],&tv);
+			timeout = js_polltimeout(cx, argv[2]);
 
 		inet_addrtop((void *)cur->ai_addr, ip_str, sizeof(ip_str));
 		dbprintf(FALSE, p, "connecting to %s on port %u at %s", ip_str, port, p->hostname);
@@ -778,10 +864,8 @@ js_connect(JSContext *cx, uintN argc, jsval *arglist)
 		if(result == SOCKET_ERROR) {
 			result = ERROR_VALUE;
 			if(result == EWOULDBLOCK || result == EINPROGRESS) {
-				FD_ZERO(&socket_set);
-				FD_SET(p->sock,&socket_set);
 				result = ETIMEDOUT;
-				if(select(p->sock+1,NULL,&socket_set,NULL,&tv)==1) {
+				if (socket_readable(p->sock, timeout)) {
 					int so_error = -1;
 					socklen_t optlen = sizeof(so_error);
 					if(getsockopt(p->sock, SOL_SOCKET, SO_ERROR, (void*)&so_error, &optlen) == 0 && so_error == 0)
@@ -1569,6 +1653,8 @@ js_setsockopt(JSContext *cx, uintN argc, jsval *arglist)
 	if((p=(js_socket_private_t*)js_GetClassPrivate(cx, obj, &js_socket_class))==NULL) {
 		return(JS_FALSE);
 	}
+	if (p->sock == INVALID_SOCKET)
+		return JS_TRUE;
 
 	JSVALUE_TO_ASTRING(cx, argv[0], optname, 64, NULL);
 	rc=JS_SUSPENDREQUEST(cx);
@@ -1641,15 +1727,22 @@ js_poll(JSContext *cx, uintN argc, jsval *arglist)
 	jsval *argv=JS_ARGV(cx, arglist);
 	js_socket_private_t*	p;
 	BOOL	poll_for_write=FALSE;
-	fd_set	socket_set;
-	fd_set*	rd_set=NULL;
-	fd_set*	wr_set=NULL;
 	uintN	argn;
 	int		result;
-	struct	timeval tv = {0, 0};
 	jsrefcount	rc;
+#ifdef _WIN32
 	size_t	i;
 	SOCKET	high=0;
+	fd_set  socket_set;
+	fd_set* rd_set=NULL;
+	fd_set* wr_set=NULL;
+	struct  timeval tv = {0, 0};
+#else
+	int timeout = 0;
+	struct pollfd *fds;
+	nfds_t nfds;
+	jsval objval = OBJECT_TO_JSVAL(obj);
+#endif
 
 	JS_SET_RVAL(cx, arglist, JSVAL_VOID);
 
@@ -1666,11 +1759,17 @@ js_poll(JSContext *cx, uintN argc, jsval *arglist)
 	for(argn=0;argn<argc;argn++) {
 		if(JSVAL_IS_BOOLEAN(argv[argn]))
 			poll_for_write=JSVAL_TO_BOOLEAN(argv[argn]);
-		else if(JSVAL_IS_NUMBER(argv[argn]))
+		else if(JSVAL_IS_NUMBER(argv[argn])) {
+#ifdef _WIN32
 			js_timeval(cx,argv[argn],&tv);
+#else
+			timeout = js_polltimeout(cx, argv[argn]);
+#endif
+		}
 	}
 
 	rc=JS_SUSPENDREQUEST(cx);
+#ifdef _WIN32
 	FD_ZERO(&socket_set);
 	if(p->set) {
 		for(i=0; i<p->set->sock_count; i++) {
@@ -1692,10 +1791,28 @@ js_poll(JSContext *cx, uintN argc, jsval *arglist)
 		result = 1;
 	else
 		result = select(high+1,rd_set,wr_set,NULL,&tv);
+#else
+	if (p->peeked && !poll_for_write) {
+		result = 1;
+	}
+	else {
+		nfds = js_socket_numsocks(cx, objval);
+		fds = calloc(nfds, sizeof(*fds));
+		if (fds == NULL) {
+			JS_RESUMEREQUEST(cx, rc);
+			JS_ReportError(cx, "Error allocating %d elements of %lu bytes at %s:%d"
+				, nfds, sizeof(*fds), getfname(__FILE__), __LINE__);
+			return JS_FALSE;
+		}
+		nfds = js_socket_add(cx, objval, fds, poll_for_write ? POLLOUT : POLLIN);
+		result = poll(fds, nfds, timeout);
+		free(fds);
+	}
+#endif
 
 	p->last_error=ERROR_VALUE;
 
-	dbprintf(FALSE, p, "poll: select returned %d (errno %d)"
+	dbprintf(FALSE, p, "poll: select/poll returned %d (errno %d)"
 		,result,p->last_error);
 
 	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(result));
@@ -2415,9 +2532,6 @@ js_connected_socket_constructor(JSContext *cx, uintN argc, jsval *arglist)
 	char	pstr[6];
 	jsrefcount	rc;
 	int nonblock;
-	struct timeval	tv;
-	fd_set			wfd;
-	fd_set			efd;
 	scfg_t *scfg;
 	char error[256];
 	uint16_t bindport = 0;
@@ -2623,35 +2737,17 @@ js_connected_socket_constructor(JSContext *cx, uintN argc, jsval *arglist)
 #endif
 					for(;p->sock!=INVALID_SOCKET;) {
 						// TODO: Do clever timeout stuff here.
-						tv.tv_sec=timeout;
-						tv.tv_usec=0;
-
-						FD_ZERO(&wfd);
-						FD_SET(p->sock, &wfd);
-						FD_ZERO(&efd);
-						FD_SET(p->sock, &efd);
-						switch(select(p->sock+1, NULL, &wfd, &efd, &tv)) {
-							case 0:
-								// fall-through
-							case -1:
-								closesocket(p->sock);
-								p->sock=INVALID_SOCKET;
-								continue;
-							case 1:
-								if(FD_ISSET(p->sock, &efd)) {
-									closesocket(p->sock);
-									p->sock=INVALID_SOCKET;
-									continue;
-								}
-								else {
-									if(socket_check(p->sock, NULL, NULL, 0))
-										goto connected;
-									closesocket(p->sock);
-									p->sock=INVALID_SOCKET;
-									continue;
-								}
-							default:
-								break;
+						if (socket_writable(p->sock, timeout * 1000)) {
+							if(!socket_recvdone(p->sock, 0))
+								goto connected;
+							closesocket(p->sock);
+							p->sock=INVALID_SOCKET;
+							continue;
+						}
+						else {
+							closesocket(p->sock);
+							p->sock=INVALID_SOCKET;
+							continue;
 						}
 					}
 
diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c
index 37edaa93a6a3934586eaac8ba2a54dc548f39b91..17295323c8a22759d4fde304ac6ab7630365d1f0 100644
--- a/src/sbbs3/mailsrvr.c
+++ b/src/sbbs3/mailsrvr.c
@@ -338,8 +338,6 @@ int sockprintf(SOCKET sock, const char* prot, CRYPT_SESSION sess, char *fmt, ...
 	int		result;
 	va_list argptr;
 	char	sbuf[1024];
-	fd_set	socket_set;
-	struct timeval tv;
 
     va_start(argptr,fmt);
     len=vsnprintf(sbuf,maxlen=sizeof(sbuf)-2,fmt,argptr);
@@ -357,20 +355,10 @@ int sockprintf(SOCKET sock, const char* prot, CRYPT_SESSION sess, char *fmt, ...
 		return(0);
 	}
 
-	/* Check socket for writability (using select) */
-	tv.tv_sec=300;
-	tv.tv_usec=0;
-
-	FD_ZERO(&socket_set);
-	FD_SET(sock,&socket_set);
-
-	if((result=select(sock+1,NULL,&socket_set,NULL,&tv))<1) {
-		if(result==0)
-			lprintf(LOG_NOTICE,"%04d %s !TIMEOUT selecting socket for send"
-				,sock, prot);
-		else
-			lprintf(LOG_NOTICE,"%04d %s !ERROR %d selecting socket for send"
-				,sock, prot, ERROR_VALUE);
+	/* Check socket for writability */
+	if (!socket_writable(sock, 300000)) {
+		lprintf(LOG_NOTICE,"%04d %s !NOTICE socket did not become writable"
+			,sock, prot);
 		return(0);
 	}
 
@@ -436,8 +424,6 @@ static void sockerror(SOCKET socket, const char* prot, int rd, const char* actio
 static int sock_recvbyte(SOCKET sock, const char* prot, CRYPT_SESSION sess, char *buf, time_t start)
 {
 	int len=0;
-	fd_set	socket_set;
-	struct	timeval	tv;
 	int ret;
 	int i;
 
@@ -467,24 +453,12 @@ static int sock_recvbyte(SOCKET sock, const char* prot, CRYPT_SESSION sess, char
 		}
 	}
 	else {
-		tv.tv_sec=startup->max_inactivity;
-		tv.tv_usec=0;
-
-		FD_ZERO(&socket_set);
-		FD_SET(sock,&socket_set);
-
-		i=select(sock+1,&socket_set,NULL,NULL,&tv);
-
-		if(i<1) {
-			if(i==0) {
-				if(startup->max_inactivity && (time(NULL)-start)>startup->max_inactivity) {
-					lprintf(LOG_WARNING,"%04d %s !TIMEOUT in sock_recvbyte (%u seconds):  INACTIVE SOCKET"
-						,sock, prot, startup->max_inactivity);
-					return(-1);
-				}
-				return 0;
+		if (!socket_readable(sock, startup->max_inactivity * 1000)) {
+			if(startup->max_inactivity && (time(NULL)-start)>startup->max_inactivity) {
+				lprintf(LOG_WARNING,"%04d %s !TIMEOUT in sock_recvbyte (%u seconds):  INACTIVE SOCKET"
+					,sock, prot, startup->max_inactivity);
+				return(-1);
 			}
-			sockerror(sock,prot,i,"select");
 			return 0;
 		}
 		i=recv(sock, buf, 1, 0);
diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index 255613b664425b7dfb943e520e0c9a54be042041..f8b46fa74519c4d929adb793341d7c458f84c5e6 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -1868,11 +1868,12 @@ void input_thread(void *arg)
     int			i,rd,wr,avail;
 	ulong		total_recv=0;
 	ulong		total_pkts=0;
-	fd_set		socket_set;
 	sbbs_t*		sbbs = (sbbs_t*) arg;
-	struct timeval	tv;
-	SOCKET		high_socket;
-	SOCKET		sock;
+	SOCKET sock;
+#ifndef _WIN32
+	struct pollfd fds[2];
+	int nfds;
+#endif
 
 	SetThreadName("sbbs/termInput");
 	thread_up(TRUE /* setuid */);
@@ -1889,55 +1890,32 @@ void input_thread(void *arg)
 		if(pthread_mutex_lock(&sbbs->input_thread_mutex)!=0)
 			sbbs->errormsg(WHERE,ERR_LOCK,"input_thread_mutex",0);
 
-		FD_ZERO(&socket_set);
-		FD_SET(sbbs->client_socket,&socket_set);
-		high_socket=sbbs->client_socket;
-#ifdef __unix__
-		if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
-			FD_SET(uspy_socket[sbbs->cfg.node_num-1],&socket_set);
-			if(uspy_socket[sbbs->cfg.node_num-1] > high_socket)
-				high_socket=uspy_socket[sbbs->cfg.node_num-1];
+#ifdef _WIN32
+		if (!socket_readable(sbbs->client_socket, 1000)) {
+			if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
+				sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
+			YIELD();	/* This kludge is necessary on some Linux distros */
+			continue;	/* to allow other threads to lock the input_thread_mutex */
 		}
-#endif
-
-		tv.tv_sec=1;
-		tv.tv_usec=0;
-
-		if((i=select(high_socket+1,&socket_set,NULL,NULL,&tv))<1) {
+#else
+		fds[0].fd = sbbs->client_socket;
+		fds[0].events = POLLIN;
+		fds[0].revents = 0;
+		nfds = 1;
+		if (uspy_socket[sbbs->cfg.node_num-1] != INVALID_SOCKET) {
+			fds[0].fd = uspy_socket[sbbs->cfg.node_num-1];
+			fds[0].events = POLLIN;
+			fds[0].revents = 0;
+			nfds++;
+		}
+
+		if (poll(fds, nfds, 1000) < 1) {
 			if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
 				sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
-			if(i==0) {
-				YIELD();	/* This kludge is necessary on some Linux distros */
-				continue;	/* to allow other threads to lock the input_thread_mutex */
-			}
-
-			if(sbbs->client_socket==INVALID_SOCKET)
-				break;
-#ifdef __unix__
-			if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
-				if(!socket_check(uspy_socket[sbbs->cfg.node_num-1],NULL,NULL,0)) {
-					close_socket(uspy_socket[sbbs->cfg.node_num-1]);
-					lprintf(LOG_NOTICE,"Closing local spy socket: %d",uspy_socket[sbbs->cfg.node_num-1]);
-					uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
-					continue;
-				}
-			}
-#endif
-	       	if(ERROR_VALUE == ENOTSOCK)
-    	        lprintf(LOG_NOTICE,"Node %d socket closed by peer on input->select", sbbs->cfg.node_num);
-			else if(ERROR_VALUE==ESHUTDOWN)
-				lprintf(LOG_NOTICE,"Node %d socket shutdown on input->select", sbbs->cfg.node_num);
-			else if(ERROR_VALUE==EINTR)
-				lprintf(LOG_DEBUG,"Node %d input thread interrupted",sbbs->cfg.node_num);
-            else if(ERROR_VALUE==ECONNRESET)
-				lprintf(LOG_NOTICE,"Node %d connection reset by peer on input->select", sbbs->cfg.node_num);
-	        else if(ERROR_VALUE==ECONNABORTED)
-				lprintf(LOG_NOTICE,"Node %d connection aborted by peer on input->select", sbbs->cfg.node_num);
-			else
-				lprintf(LOG_WARNING,"Node %d !ERROR %d input->select socket %d"
-               		,sbbs->cfg.node_num, ERROR_VALUE, sbbs->client_socket);
-			break;
+			YIELD();	/* This kludge is necessary on some Linux distros */
+			continue;	/* to allow other threads to lock the input_thread_mutex */
 		}
+#endif
 
 		if(sbbs->client_socket==INVALID_SOCKET) {
 			if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
@@ -1956,12 +1934,13 @@ void input_thread(void *arg)
  *         ------------
  */
 
-		if(FD_ISSET(sbbs->client_socket,&socket_set))
-			sock=sbbs->client_socket;
-#ifdef __unix__
-		else if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET
-				&& FD_ISSET(uspy_socket[sbbs->cfg.node_num-1],&socket_set))  {
-			if(!socket_check(uspy_socket[sbbs->cfg.node_num-1],NULL,NULL,0)) {
+#ifdef _WIN32
+		sock=sbbs->client_socket;
+#else
+		if (fds[0].revents | POLLIN)
+			sock = sbbs->client_socket;
+		else if(uspy_socket[sbbs->cfg.node_num - 1] != INVALID_SOCKET && fds[1].revents | POLLIN) {
+			if(socket_recvdone(uspy_socket[sbbs->cfg.node_num-1], 0)) {
 				close_socket(uspy_socket[sbbs->cfg.node_num-1]);
 				lprintf(LOG_NOTICE,"Closing local spy socket: %d",uspy_socket[sbbs->cfg.node_num-1]);
 				uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
@@ -1971,12 +1950,12 @@ void input_thread(void *arg)
 			}
 			sock=uspy_socket[sbbs->cfg.node_num-1];
 		}
-#endif
 		else {
 			if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
 				sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
 			continue;
 		}
+#endif
 
     	rd=RingBufFree(&sbbs->inbuf);
 
@@ -2168,10 +2147,7 @@ void sbbs_t::passthru_socket_activate(bool activate)
  */
 void passthru_thread(void* arg)
 {
-	fd_set	r_set;
 	sbbs_t	*sbbs = (sbbs_t*) arg;
-	struct	timeval	tv;
-	int		i;
 	int		rd;
 	char	inbuf[IO_THREAD_BUF_SIZE / 2];
 
@@ -2179,34 +2155,14 @@ void passthru_thread(void* arg)
 	thread_up(FALSE /* setuid */);
 
 	while(sbbs->online && sbbs->passthru_socket!=INVALID_SOCKET && !terminate_server) {
-		tv.tv_sec=1;
-		tv.tv_usec=0;
-
-		FD_ZERO(&r_set);
-		FD_SET(sbbs->passthru_socket,&r_set);
-		if((i=select(sbbs->passthru_socket+1,&r_set,NULL,NULL,&tv))<1) {
-			if(i==0) {
-				YIELD();	/* This kludge is necessary on some Linux distros */
-				continue;	/* to allow other threads to lock the input_thread_mutex */
-			}
+		if (!socket_readable(sbbs->passthru_socket, 1000)) {
+			YIELD();	/* This kludge is necessary on some Linux distros */
+			continue;	/* to allow other threads to lock the input_thread_mutex */
+		}
 
-			if(sbbs->passthru_socket==INVALID_SOCKET)
-				break;
-	       	if(ERROR_VALUE == ENOTSOCK)
-    	        lprintf(LOG_NOTICE,"Node %d socket closed by peer on passthru->select", sbbs->cfg.node_num);
-			else if(ERROR_VALUE==ESHUTDOWN)
-				lprintf(LOG_NOTICE,"Node %d socket shutdown on passthru->select", sbbs->cfg.node_num);
-			else if(ERROR_VALUE==EINTR)
-				lprintf(LOG_DEBUG,"Node %d passthru thread interrupted",sbbs->cfg.node_num);
-            else if(ERROR_VALUE==ECONNRESET)
-				lprintf(LOG_NOTICE,"Node %d connection reset by peer on passthru->select", sbbs->cfg.node_num);
-	        else if(ERROR_VALUE==ECONNABORTED)
-				lprintf(LOG_NOTICE,"Node %d connection aborted by peer on passthru->select", sbbs->cfg.node_num);
-			else
-				lprintf(LOG_WARNING,"Node %d !ERROR %d passthru->select socket %d"
-               		,sbbs->cfg.node_num, ERROR_VALUE, sbbs->passthru_socket);
+		if(sbbs->passthru_socket==INVALID_SOCKET)
 			break;
-		}
+
 		rd = RingBufFree(&sbbs->outbuf) / 2;
 		if(rd > (int)sizeof(inbuf))
 			rd = sizeof(inbuf);
@@ -2281,8 +2237,6 @@ void output_thread(void* arg)
 	ulong		bufbot=0;
 	ulong		buftop=0;
 	sbbs_t*		sbbs = (sbbs_t*) arg;
-	fd_set		socket_set;
-	struct timeval tv;
 	ulong		mss=IO_THREAD_BUF_SIZE;
 	ulong		ssh_errors = 0;
 
@@ -2400,27 +2354,8 @@ void output_thread(void* arg)
 				continue;
 		}
 
-		/* Check socket for writability (using select) */
-		tv.tv_sec=0;
-		tv.tv_usec=1000;
-
-		FD_ZERO(&socket_set);
-		if(sbbs->client_socket==INVALID_SOCKET)		// Make the race condition less likely to actually happen... TODO: Fix race
-			continue;
-		FD_SET(sbbs->client_socket,&socket_set);
-
-		i=select(sbbs->client_socket+1,NULL,&socket_set,NULL,&tv);
-		if(i==SOCKET_ERROR) {
-			if(sbbs->client_socket!=INVALID_SOCKET)
-				lprintf(LOG_WARNING,"%s !ERROR %d selecting socket %u for send"
-					,node,ERROR_VALUE,sbbs->client_socket);
-			if(sbbs->cfg.node_num)	/* Only break if node output (not server) */
-				break;
-			RingBufReInit(&sbbs->outbuf);	/* Purge output ring buffer */
-			bufbot=buftop=0;				/* Purge linear buffer */
-			continue;
-		}
-		if(i<1) {
+		/* Check socket for writability */
+		if (!socket_writable(sbbs->client_socket, 1)) {
 			continue;
 		}
 
diff --git a/src/sbbs3/mxlookup.c b/src/sbbs3/mxlookup.c
index 20c719bca475f5daecb8ef027f9821028e23db9e..94620c944600f33933dd4fa90f446c460992b7b9 100644
--- a/src/sbbs3/mxlookup.c
+++ b/src/sbbs3/mxlookup.c
@@ -184,8 +184,6 @@ int dns_getmx(char* name, char* mx, char* mx2
 	dns_msghdr_t	msghdr;
 	dns_query_t		query;
 	dns_rr_t*		rr;
-	struct timeval	tv;
-	fd_set			socket_set;
 	int				sess = -1;
 
 	mx[0]=0;
@@ -255,19 +253,9 @@ int dns_getmx(char* name, char* mx, char* mx2
 		len-=sizeof(msghdr.length);
 	}
 
-	/* check for writability (using select) */
-	tv.tv_sec=timeout;
-	tv.tv_usec=0;
-
-	FD_ZERO(&socket_set);
-	FD_SET(sock,&socket_set);
-
-	i=select(sock+1,NULL,&socket_set,NULL,&tv);
-	if(i<1) {
-		if(i==SOCKET_ERROR)
-			result=ERROR_VALUE;
-		else 
-			result=-1;
+	/* check for writability */
+	if (!socket_writable(sock, timeout * 1000)) {
+		result =- 1;
 		mail_close_socket(&sock, &sess);
 		return(result);
 	}
@@ -287,19 +275,9 @@ int dns_getmx(char* name, char* mx, char* mx2
 		return(result);
 	}
 
-	/* check for readability (using select) */
-	tv.tv_sec=timeout;
-	tv.tv_usec=0;
-
-	FD_ZERO(&socket_set);
-	FD_SET(sock,&socket_set);
-
-	i=select(sock+1,&socket_set,NULL,NULL,&tv);
-	if(i<1) {
-		if(i==SOCKET_ERROR)
-			result=ERROR_VALUE;
-		else 
-			result=-1;
+	/* check for readability */
+	if (!socket_readable(sock, timeout * 1000)) {
+		result = -1;
 		mail_close_socket(&sock, &sess);
 		return(result);
 	}
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index aa495d10fd27e0b51442c771aa2590cc47c9c643..de4db437bc56992439a41af619503881e418a1fc 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -1352,10 +1352,16 @@ extern "C" {
 	DLLEXPORT JSObject* DLLCALL js_CreateSocketObjectFromSet(JSContext* cx, JSObject* parent
 													,char *name, struct xpms_set *set);
 
-	DLLEXPORT void		DLLCALL js_timeval(JSContext* cx, jsval val, struct timeval* tv);
 	DLLEXPORT SOCKET	DLLCALL js_socket(JSContext *cx, jsval val);
+	DLLEXPORT int js_polltimeout(JSContext* cx, jsval val);
+#ifdef _WIN32
+	DLLEXPORT void		DLLCALL js_timeval(JSContext* cx, jsval val, struct timeval* tv);
     DLLEXPORT SOCKET	DLLCALL js_socket_add(JSContext *cx, jsval val, fd_set *fds);
 	DLLEXPORT BOOL		DLLCALL js_socket_isset(JSContext *cx, jsval val, fd_set *fds);
+#else
+	DLLEXPORT size_t js_socket_numsocks(JSContext *cx, jsval val);
+	DLLEXPORT size_t js_socket_add(JSContext *cx, jsval val, struct pollfd *fds, short events);
+#endif
 
 	/* js_queue.c */
 	DLLEXPORT JSObject* DLLCALL js_CreateQueueClass(JSContext* cx, JSObject* parent);
diff --git a/src/sbbs3/sbbs_status.c b/src/sbbs3/sbbs_status.c
index 842b615a46c4d53ea9396271adc90561bc846556..6a86bb3b64c9eb031a6e5469fabb0f097d3f50c2 100644
--- a/src/sbbs3/sbbs_status.c
+++ b/src/sbbs3/sbbs_status.c
@@ -434,8 +434,6 @@ void status_thread(void *arg)
 	SOCKET sock;
 	struct sockaddr_un addr;
 	socklen_t addrlen;
-	fd_set fd;
-	struct timeval tv;
 	SOCKET *csock;
 	char auth[1024];
 	ssize_t len;
@@ -542,11 +540,7 @@ void status_thread(void *arg)
 	pthread_mutex_lock(&status_thread_mutex);
 	while (!status_thread_terminated) {
 		pthread_mutex_unlock(&status_thread_mutex);
-		FD_ZERO(&fd);
-		FD_SET(sock, &fd);
-		memset(&tv, 0, sizeof(tv));
-		tv.tv_sec = 1;
-		if (select(sock+1, &fd, NULL, NULL, &tv) == 1) {
+		if (socket_readable(sock, 1000)) {
 			csock = malloc(sizeof(SOCKET));
 			if (csock == NULL) {
 				lprintf(LOG_CRIT, "Error allocating memory!");
@@ -558,11 +552,7 @@ void status_thread(void *arg)
 			if (*csock != INVALID_SOCKET) {
 				nb = 1;
 				ioctlsocket(*csock, FIONBIO, &nb);
-				FD_ZERO(&fd);
-				FD_SET(*csock, &fd);
-				memset(&tv, 0, sizeof(tv));
-				tv.tv_sec = 5;
-				if (select((*csock)+1, &fd, NULL, NULL, &tv) == 1) {
+				if (socket_readable(*csock, 5000)) {
 					len = recv(*csock, auth, sizeof(auth), 0);
 					if (len <= 0) {
 						closesocket(*csock);
@@ -712,7 +702,7 @@ void status_thread(void *arg)
 				else {
 					closesocket(*csock);
 					free(csock);
-					lprintf(LOG_WARNING, "select() for recv() failed");
+					lprintf(LOG_WARNING, "socket did not become readable");
 					pthread_mutex_lock(&status_thread_mutex);
 					continue;
 				}
diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c
index aa7741fd205f5b5af9e93e1a23d7ef96b0f827e9..618dd8cc9593615a527c56d5548a1c1705432359 100644
--- a/src/sbbs3/services.c
+++ b/src/sbbs3/services.c
@@ -1722,6 +1722,43 @@ void service_udp_sock_cb(SOCKET sock, void *cbdata)
    #endif
 }
 
+#ifndef _WIN32
+/*
+ * Sets up the fds for poll, and returns the total sockets
+ */
+static nfds_t setup_poll(struct pollfd **fds)
+{
+	int i, j;
+	nfds_t nfds = 0;
+
+	free(*fds);
+	for(i=0;i<(int)services;i++) {
+		if(service[i].options&SERVICE_OPT_STATIC)
+			continue;
+		if(service[i].set==NULL)
+			continue;
+		nfds += service[i].set->sock_count;
+	}
+
+	*fds = calloc(nfds, sizeof(**fds));
+	if (*fds == NULL)
+		return 0;
+	nfds = 0;
+	for(i=0;i<(int)services;i++) {
+		if(service[i].options&SERVICE_OPT_STATIC)
+			continue;
+		if(service[i].set==NULL)
+			continue;
+		for(j=0; j<service[i].set->sock_count; j++) {
+			(*fds)[nfds].fd = service[i].set->socks[j].sock;
+			(*fds)[nfds].events = POLLIN;
+			nfds++;
+		}
+	}
+	return nfds;
+}
+#endif
+
 void DLLCALL services_thread(void* arg)
 {
 	char*			p;
@@ -1745,14 +1782,20 @@ void DLLCALL services_thread(void* arg)
 	ulong			total_running;
 	time_t			t;
 	time_t			initialized=0;
-	fd_set			socket_set;
-	SOCKET			high_socket;
 	ulong			total_sockets;
-	struct timeval	tv;
 	service_client_t* client;
 	char			*ssl_estr;
 	int			level;
 	BOOL			need_cert = FALSE;
+#ifdef _WIN32
+	fd_set			socket_set;
+	SOCKET			high_socket;
+	struct timeval	tv;
+#else
+	struct pollfd *fds = NULL;
+	nfds_t nfds;
+	nfds_t nfdsi;
+#endif
 
 	startup=(services_startup_t*)arg;
 
@@ -1958,6 +2001,14 @@ void DLLCALL services_thread(void* arg)
 
 		lprintf(LOG_INFO,"0000 Services thread started (%lu service sockets bound)", total_sockets);
 
+#ifndef _WIN32
+		nfds = setup_poll(&fds);
+		if (nfds == 0) {
+			lprintf(LOG_CRIT, "!ERROR setting up poll() data");
+			cleanup(1);
+			return;
+		}
+#endif
 		/* Main Server Loop */
 		while(!terminated) {
 			YIELD();
@@ -1983,6 +2034,7 @@ void DLLCALL services_thread(void* arg)
 				}
 			}
 
+#ifdef _WIN32
 			/* Setup select() parms */
 			FD_ZERO(&socket_set);	
 			high_socket=0;
@@ -2027,7 +2079,49 @@ void DLLCALL services_thread(void* arg)
 				for(j=0; j<service[i].set->sock_count; j++) {
 					if(!FD_ISSET(service[i].set->socks[j].sock,&socket_set))
 						continue;
+#else
+			/* Clear poll FDs where necessary (ie: when !SERVICE_OPT_FULL_ACCEPT) */
+			nfdsi = 0;
+			for(i=0;i<(int)services;i++) {
+				if(service[i].options&SERVICE_OPT_STATIC)
+					continue;
+				if(service[i].set==NULL)
+					continue;
+				if(!(service[i].options&SERVICE_OPT_FULL_ACCEPT)) {
+					if (service[i].max_clients && protected_uint32_value(service[i].clients) >= service[i].max_clients) {
+						for(j=0; j<service[i].set->sock_count; j++)
+							fds[nfdsi + j].fd = -1;
+					}
+					else {
+						for(j=0; j<service[i].set->sock_count; j++)
+							fds[nfdsi + j].fd = service[i].set->socks[j].sock;
+					}
+				}
+				nfdsi += service[i].set->sock_count;
+			}
+
+			if ((result = poll(fds, nfds, startup->sem_chk_freq * 1000)) < 1) {
+				if(result==0)
+					continue;
 
+				if(ERROR_VALUE==EINTR)
+					lprintf(LOG_DEBUG,"0000 Services listening interrupted");
+				else
+					lprintf(LOG_WARNING,"0000 !ERROR %d polling sockets: %s"
+						, ERROR_VALUE, socket_strerror(socket_errno,error,sizeof(error)));
+				continue;
+			}
+			nfdsi = 0;
+			for(i=0;i<(int)services;i++) {
+				if(service[i].options&SERVICE_OPT_STATIC)
+					continue;
+				if(service[i].set==NULL)
+					continue;
+
+				for(j=0; j<service[i].set->sock_count; j++) {
+					if ((fds[nfdsi + j].revents & POLLIN) == 0)
+						continue;
+#endif
 					client_addr_len = sizeof(client_addr);
 
 					udp_len=0;
@@ -2202,8 +2296,14 @@ void DLLCALL services_thread(void* arg)
 					service[i].served++;
 					served++;
 				}
+#ifndef _WIN32
+				nfdsi += service[i].set->sock_count;
+#endif
 			}
 		}
+#ifndef _WIN32
+		free(fds);
+#endif
 
 		/* Close Service Sockets */
 		lprintf(LOG_DEBUG,"0000 Closing service sockets");
diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c
index 26d40e4444320041a321635e0d253ebded3009fd..37ad91bf57a0cb072742f2ac40183944c4413007 100644
--- a/src/sbbs3/websrvr.c
+++ b/src/sbbs3/websrvr.c
@@ -626,9 +626,6 @@ static int sess_sendbuf(http_session_t *session, const char *buf, size_t len, BO
 	size_t sent=0;
 	int	tls_sent;
 	int result;
-	int sel;
-	fd_set	wr_set;
-	struct timeval tv;
 	int status;
 	BOOL local_failed = FALSE;
 
@@ -636,67 +633,56 @@ static int sess_sendbuf(http_session_t *session, const char *buf, size_t len, BO
 		failed = &local_failed;
 
 	while(sent<len && session->socket!=INVALID_SOCKET && *failed == FALSE) {
-		FD_ZERO(&wr_set);
-		FD_SET(session->socket,&wr_set);
-		/* Convert timeout from ms to sec/usec */
-		tv.tv_sec=startup->max_inactivity;
-		tv.tv_usec=0;
-		sel=select(session->socket+1,NULL,&wr_set,NULL,&tv);
-		switch(sel) {
-			case 1:
-				if (session->is_tls) {
-					/*
-					 * Limit as per js_socket.c.
-					 * Sure, this is TLS, not SSH, but we see weird stuff here in sz file transfers.
-					 */
-					size_t sendbytes = len-sent;
-					if (sendbytes > 0x2000)
-						sendbytes = 0x2000;
-					status = cryptPushData(session->tls_sess, buf+sent, sendbytes, &tls_sent);
-					GCES(status, session, "pushing data");
-					if (status == CRYPT_ERROR_TIMEOUT) {
-						tls_sent = 0;
-						if(!cryptStatusOK(status=cryptPopData(session->tls_sess, "", 0, &status))) {
-							if (status != CRYPT_ERROR_TIMEOUT && status != CRYPT_ERROR_PARAM2)
-								GCES(status, session, "popping data after timeout");
-						}
-						status = CRYPT_OK;
-					}
-					if(cryptStatusOK(status)) {
-						HANDLE_CRYPT_CALL_EXCEPT(status = cryptFlushData(session->tls_sess), session, "flushing data", CRYPT_ERROR_COMPLETE);
-						if (cryptStatusError(status))
-							*failed=TRUE;
-						return tls_sent;
+		if (socket_writable(session->socket, startup->max_inactivity * 1000)) {
+			if (session->is_tls) {
+				/*
+				 * Limit as per js_socket.c.
+				 * Sure, this is TLS, not SSH, but we see weird stuff here in sz file transfers.
+				 */
+				size_t sendbytes = len-sent;
+				if (sendbytes > 0x2000)
+					sendbytes = 0x2000;
+				status = cryptPushData(session->tls_sess, buf+sent, sendbytes, &tls_sent);
+				GCES(status, session, "pushing data");
+				if (status == CRYPT_ERROR_TIMEOUT) {
+					tls_sent = 0;
+					if(!cryptStatusOK(status=cryptPopData(session->tls_sess, "", 0, &status))) {
+						if (status != CRYPT_ERROR_TIMEOUT && status != CRYPT_ERROR_PARAM2)
+							GCES(status, session, "popping data after timeout");
 					}
-					*failed=TRUE;
-					result = tls_sent;
+					status = CRYPT_OK;
 				}
-				else {
-					result=sendsocket(session->socket,buf+sent,len-sent);
-					if(result==SOCKET_ERROR) {
-						if(ERROR_VALUE==ECONNRESET)
-							lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",session->socket);
-						else if(ERROR_VALUE==ECONNABORTED)
-							lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",session->socket);
-#ifdef EPIPE
-						else if(ERROR_VALUE==EPIPE)
-							lprintf(LOG_NOTICE,"%04d Unable to send to peer",session->socket);
-#endif
-						else
-							lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",session->socket,ERROR_VALUE);
+				if(cryptStatusOK(status)) {
+					HANDLE_CRYPT_CALL_EXCEPT(status = cryptFlushData(session->tls_sess), session, "flushing data", CRYPT_ERROR_COMPLETE);
+					if (cryptStatusError(status))
 						*failed=TRUE;
-						return(sent);
-					}
+					return tls_sent;
 				}
-				break;
-			case 0:
-				lprintf(LOG_WARNING,"%04d Timeout selecting socket for write",session->socket);
 				*failed=TRUE;
-				return(sent);
-			case -1:
-				lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for write",session->socket,ERROR_VALUE);
-				*failed=TRUE;
-				return(sent);
+				result = tls_sent;
+			}
+			else {
+				result=sendsocket(session->socket,buf+sent,len-sent);
+				if(result==SOCKET_ERROR) {
+					if(ERROR_VALUE==ECONNRESET)
+						lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",session->socket);
+					else if(ERROR_VALUE==ECONNABORTED)
+						lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",session->socket);
+#ifdef EPIPE
+					else if(ERROR_VALUE==EPIPE)
+						lprintf(LOG_NOTICE,"%04d Unable to send to peer",session->socket);
+#endif
+					else
+						lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",session->socket,ERROR_VALUE);
+					*failed=TRUE;
+					return(sent);
+				}
+			}
+		}
+		else {
+			lprintf(LOG_WARNING,"%04d Timeout waiting for socket to become writable",session->socket);
+			*failed=TRUE;
+			return(sent);
 		}
 		sent+=result;
 	}
@@ -977,11 +963,9 @@ static int close_socket(SOCKET *sock)
 
 	/* required to ensure all data is sent */
 	shutdown(*sock,SHUT_WR);
-	while(socket_check(*sock, &rd, NULL, startup->max_inactivity*1000))  {
-		if (rd) {
-			if (recv(*sock,&ch,1,0) <= 0)
-				break;
-		}
+	while(socket_readable(*sock, startup->max_inactivity * 1000))  {
+		if (recv(*sock,&ch,1,0) <= 0)
+			break;
 		if (time(NULL) >= end)
 			break;
 	}
@@ -2097,35 +2081,21 @@ static int sess_recv(http_session_t *session, char *buf, size_t length, int flag
 static int sockreadline(http_session_t * session, char *buf, size_t length)
 {
 	char	ch;
-	int		sel;
 	DWORD	i;
 	DWORD	chucked=0;
-	fd_set	rd_set;
-	struct	timeval tv;
 
 	for(i=0;TRUE;) {
 		if(session->socket==INVALID_SOCKET)
 			return(-1);
 		if ((!session->is_tls) || (!session->tls_pending)) {
-			FD_ZERO(&rd_set);
-			FD_SET(session->socket,&rd_set);
-			/* Convert timeout from ms to sec/usec */
-			tv.tv_sec=startup->max_inactivity;
-			tv.tv_usec=0;
-			sel=select(session->socket+1,&rd_set,NULL,NULL,&tv);
-			switch(sel) {
-				case 1:
-					if (session->is_tls)
-						session->tls_pending=TRUE;
-					break;
-				case -1:
-					close_session_socket(session);
-					lprintf(LOG_DEBUG,"%04d !ERROR %d selecting socket for read",session->socket,ERROR_VALUE);
-					return(-1);
-				default:
-					/* Timeout */
-					lprintf(LOG_NOTICE,"%04d Session timeout due to inactivity (%d seconds)",session->socket,startup->max_inactivity);
-					return(-1);
+			if (socket_readable(session->socket, startup->max_inactivity * 1000)) {
+				if (session->is_tls)
+					session->tls_pending=TRUE;
+			}
+			else {
+				/* Timeout */
+				lprintf(LOG_NOTICE,"%04d Session timeout due to inactivity (%d seconds)",session->socket,startup->max_inactivity);
+				return(-1);
 			}
 		}
 
@@ -2179,10 +2149,6 @@ static int pipereadline(int pipe, char *buf, size_t length, char *fullbuf, size_
 	char	ch;
 	DWORD	i;
 	int		ret=0;
-#ifndef _WIN32
-	struct timeval tv={0,0};
-	fd_set  read_set;
-#endif
 
 	/* Terminate buffers */
 	if(buf != NULL)
@@ -2194,11 +2160,7 @@ static int pipereadline(int pipe, char *buf, size_t length, char *fullbuf, size_
 		ret=0;
 		ReadFile(pipe, &ch, 1, (DWORD*)&ret, NULL);
 #else
-		tv.tv_sec=startup->max_cgi_inactivity;
-		tv.tv_usec=0;
-		FD_ZERO(&read_set);
-		FD_SET(pipe, &read_set);
-		if(select(pipe+1, &read_set, NULL, NULL, &tv)<1)
+		if (!socket_readable(pipe, startup->max_cgi_inactivity * 1000))
 			return(-1);
 		ret=read(pipe, &ch, 1);
 #endif
@@ -3824,10 +3786,8 @@ static SOCKET fastcgi_connect(const char *orig_path, SOCKET client_sock)
 	char *path = strdup(orig_path);
 	char *port = split_port_part(path);
 	ulong val;
-	fd_set socket_set;
 	SOCKET sock;
 	struct addrinfo	hints,*res,*cur;
-	struct timeval tv;
 
 	// TODO: UNIX-domain sockets...
 	if (strncmp(path, "unix:", 5) == 0) {
@@ -3847,9 +3807,6 @@ static SOCKET fastcgi_connect(const char *orig_path, SOCKET client_sock)
 		return INVALID_SOCKET;
 	}
 	for(cur=res,result=1; result && cur; cur=cur->ai_next) {
-		tv.tv_sec = 1;	/* TODO: Make configurable! */
-		tv.tv_usec = 0;
-
 		sock = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
 		if (sock == INVALID_SOCKET)
 			continue;
@@ -3859,9 +3816,7 @@ static SOCKET fastcgi_connect(const char *orig_path, SOCKET client_sock)
 
 		if (result==SOCKET_ERROR) {
 			if((ERROR_VALUE==EWOULDBLOCK || ERROR_VALUE==EINPROGRESS)) {
-				FD_ZERO(&socket_set);
-				FD_SET(sock,&socket_set);
-				if(select(sock+1,NULL,&socket_set,NULL,&tv)==1)
+				if (socket_writable(sock, 1000 /* TODO: Make configurable! */))
 					result=0;	/* success */
 			}
 			else
@@ -4064,54 +4019,52 @@ static int fastcgi_read_wait_timeout(void *arg)
 			break;
 	}
 
-	if (socket_check(cd->sock, &rd, NULL, startup->max_cgi_inactivity*1000)) {
-		if (rd) {
-			if (recv(cd->sock, (void *)&cd->header, offsetof(struct fastcgi_header, len), MSG_WAITALL) != offsetof(struct fastcgi_header, len)) {
-				lprintf(LOG_ERR, "FastCGI failed to read header");
-				return ret;
-			}
-			if (cd->header.ver != FCGI_VERSION_1) {
-				lprintf(LOG_ERR, "Unknown FastCGI version %d", cd->header.ver);
-				return ret;
-			}
-			if (htons(cd->header.id) != 1) {
-				lprintf(LOG_ERR, "Unknown FastCGI session ID %d", htons(cd->header.id));
-				return ret;
-			}
-			switch(cd->header.type) {
-				case FCGI_STDOUT:
-					ret |= CGI_OUTPUT_READY;
-					break;
-				case FCGI_STDERR:
-					ret |= CGI_OUTPUT_READY;
-					break;
-				case FCGI_END_REQUEST:
-					ret |= CGI_PROCESS_TERMINATED;
-					cd->request_ended = 1;
-					// Fall-through
-				case FCGI_BEGIN_REQUEST:
-				case FCGI_ABORT_REQUEST:
-				case FCGI_PARAMS:
-				case FCGI_STDIN:
-				case FCGI_DATA:
-				case FCGI_GET_VALUES:
-				case FCGI_GET_VALUES_RESULT:
-				case FCGI_UNKNOWN_TYPE:
-					// Read and discard the entire message...
-					body = fastcgi_read_body(cd->sock);
-					if (body == NULL)
-						return ret;
-					free(body);
-					break;
-				default:
-					lprintf(LOG_ERR, "Unhandled FastCGI message type %d", cd->header.type);
-					// Read and discard the entire message...
-					body = fastcgi_read_body(cd->sock);
-					if (body == NULL)
-						return ret;
-					free(body);
-					break;
-			}
+	if (socket_readable(cd->sock, startup->max_cgi_inactivity*1000)) {
+		if (recv(cd->sock, (void *)&cd->header, offsetof(struct fastcgi_header, len), MSG_WAITALL) != offsetof(struct fastcgi_header, len)) {
+			lprintf(LOG_ERR, "FastCGI failed to read header");
+			return ret;
+		}
+		if (cd->header.ver != FCGI_VERSION_1) {
+			lprintf(LOG_ERR, "Unknown FastCGI version %d", cd->header.ver);
+			return ret;
+		}
+		if (htons(cd->header.id) != 1) {
+			lprintf(LOG_ERR, "Unknown FastCGI session ID %d", htons(cd->header.id));
+			return ret;
+		}
+		switch(cd->header.type) {
+			case FCGI_STDOUT:
+				ret |= CGI_OUTPUT_READY;
+				break;
+			case FCGI_STDERR:
+				ret |= CGI_OUTPUT_READY;
+				break;
+			case FCGI_END_REQUEST:
+				ret |= CGI_PROCESS_TERMINATED;
+				cd->request_ended = 1;
+				// Fall-through
+			case FCGI_BEGIN_REQUEST:
+			case FCGI_ABORT_REQUEST:
+			case FCGI_PARAMS:
+			case FCGI_STDIN:
+			case FCGI_DATA:
+			case FCGI_GET_VALUES:
+			case FCGI_GET_VALUES_RESULT:
+			case FCGI_UNKNOWN_TYPE:
+				// Read and discard the entire message...
+				body = fastcgi_read_body(cd->sock);
+				if (body == NULL)
+					return ret;
+				free(body);
+				break;
+			default:
+				lprintf(LOG_ERR, "Unhandled FastCGI message type %d", cd->header.type);
+				// Read and discard the entire message...
+				body = fastcgi_read_body(cd->sock);
+				if (body == NULL)
+					return ret;
+				free(body);
+				break;
 		}
 	}
 	else
@@ -4219,41 +4172,28 @@ static int fastcgi_done_wait(void *arg)
 
 	if (cd->request_ended)
 		return 1;
-	return (!socket_check(cd->sock, NULL, NULL, /* timeout: */0));
+	return (socket_recvdone(cd->sock, 0));
 }
 
 #ifdef __unix__
 struct cgi_data {
-	int out_pipe;	// out_pipe[0]
-	int err_pipe;	// err_pipe[0]
 	pid_t child;	// child
+	struct pollfd fds[2];
 };
 
 static int cgi_read_wait_timeout(void *arg)
 {
 	int ret = 0;
 	int status=0;
-	int high_fd;
-	fd_set read_set;
-	fd_set write_set;
 	struct cgi_data *cd = (struct cgi_data *)arg;
-	struct timeval tv;
-
-	high_fd = cd->err_pipe;
-	if (cd->out_pipe > cd->err_pipe)
-		high_fd = cd->out_pipe;
-	tv.tv_sec=startup->max_cgi_inactivity;
-	tv.tv_usec=0;
 
-	FD_ZERO(&read_set);
-	FD_SET(cd->out_pipe,&read_set);
-	FD_SET(cd->err_pipe,&read_set);
-	FD_ZERO(&write_set);
+	cd->fds[0].events = POLLIN;
+	cd->fds[1].events = POLLIN;
 
-	if(select(high_fd+1,&read_set,&write_set,NULL,&tv)>0)  {
-		if (FD_ISSET(cd->out_pipe,&read_set))
+	if (poll(cd->fds, 2, startup->max_cgi_inactivity * 1000) > 0)  {
+		if (cd->fds[0].revents & POLLIN)
 			ret |= CGI_OUTPUT_READY;
-		if(FD_ISSET(cd->err_pipe,&read_set))
+		if (cd->fds[1].revents & POLLIN)
 			ret |= CGI_ERROR_READY;
 	}
 
@@ -4266,21 +4206,21 @@ static int cgi_read_out(void *arg, char *buf, size_t sz)
 {
 	struct cgi_data *cd = (struct cgi_data *)arg;
 
-	return read(cd->out_pipe,buf,sz);
+	return read(cd->fds[0].fd, buf, sz);
 }
 
 static int cgi_read_err(void *arg, char *buf, size_t sz)
 {
 	struct cgi_data *cd = (struct cgi_data *)arg;
 
-	return read(cd->err_pipe,buf,sz);
+	return read(cd->fds[1].fd, buf, sz);
 }
 
 static int cgi_readln_out(void *arg, char *buf, size_t bufsz, char *fbuf, size_t fbufsz)
 {
 	struct cgi_data *cd = (struct cgi_data *)arg;
 
-	return pipereadline(cd->out_pipe, buf, bufsz, fbuf, fbufsz);
+	return pipereadline(cd->fds[0].fd, buf, bufsz, fbuf, fbufsz);
 }
 
 static int cgi_write_in(void *arg, char *buf, size_t bufsz)
@@ -4717,10 +4657,6 @@ static BOOL exec_cgi(http_session_t *session)
 	int		in_pipe[2];
 	int		out_pipe[2];
 	int		err_pipe[2];
-	struct timeval tv={0,0};
-	fd_set	read_set;
-	fd_set	write_set;
-	int		high_fd=0;
 	char	buf[1024];
 	BOOL	done_parsing_headers=FALSE;
 	BOOL	done_wait=FALSE;
@@ -4836,16 +4772,15 @@ static BOOL exec_cgi(http_session_t *session)
 
 	// TODO: For TLS-CGI, write each separate read...
 	if (session->tls_sess && session->req.post_len && session->req.post_data) {
-		tv.tv_sec=1;
-		tv.tv_usec=0;
-		FD_ZERO(&read_set);
-		high_fd = in_pipe[1];
-		FD_SET(in_pipe[1], &write_set);
 		sent = 0;
+		cd.fds[0].fd = in_pipe[1];
+		cd.fds[0].events = POLLOUT;
 		while(sent < session->req.post_len) {
-			if (select(high_fd+1, NULL, &write_set, NULL, &tv) > 0) {
-				if (FD_ISSET(in_pipe[1], &write_set))
+			if (poll(cd.fds, 1, 1000) > 0) {
+				if (cd.fds[0].revents & POLLIN)
 					i = write(in_pipe[1], &session->req.post_data[sent], session->req.post_len - sent);
+				else
+					i = 0;
 				if (i > 0)
 					sent += i;
 				else {
@@ -4857,7 +4792,7 @@ static BOOL exec_cgi(http_session_t *session)
 				}
 			}
 			else {
-				lprintf(LOG_INFO, "%04d FAILED selecting CGI stding for write", session->socket);
+				lprintf(LOG_INFO, "%04d FAILED polling CGI stding for write", session->socket);
 				close(in_pipe[1]);
 				close(out_pipe[0]);
 				close(err_pipe[0]);
@@ -4866,12 +4801,8 @@ static BOOL exec_cgi(http_session_t *session)
 		}
 	}
 
-	high_fd=out_pipe[0];
-	if(err_pipe[0]>high_fd)
-		high_fd=err_pipe[0];
-
-	cd.out_pipe = out_pipe[0];
-	cd.err_pipe = err_pipe[0];
+	cd.fds[0].fd = out_pipe[0];
+	cd.fds[1].fd = err_pipe[0];
 	cd.child = child;
 
 	int ret = do_cgi_stuff(session, &cgi, orig_keep);
@@ -4902,13 +4833,8 @@ static BOOL exec_cgi(http_session_t *session)
 	if (session->tls_sess)
 		close(in_pipe[1]);	/* close excess file descriptor */
 	/* Drain STDERR & STDOUT */
-	tv.tv_sec=1;
-	tv.tv_usec=0;
-	FD_ZERO(&read_set);
-	FD_SET(err_pipe[0],&read_set);
-	FD_SET(out_pipe[0],&read_set);
-	while(select(high_fd+1,&read_set,NULL,NULL,&tv)>0) {
-		if(FD_ISSET(err_pipe[0],&read_set)) {
+	while(poll(cd.fds, 2, 1000) > 0) {
+		if(cd.fds[1].revents & POLLIN) {
 			i=read(err_pipe[0],buf,sizeof(buf)-1);
 			if(i!=-1 && i!=0) {
 				buf[i]=0;
@@ -4916,8 +4842,8 @@ static BOOL exec_cgi(http_session_t *session)
 			}
 		}
 
-		if(FD_ISSET(out_pipe[0],&read_set))  {
-			i=read(out_pipe[0],buf,sizeof(buf));
+		if(cd.fds[0].revents & POLLIN)  {
+			i=read(cd.fds[0].fd, buf, sizeof(buf));
 			if(i!=-1 && i!=0)  {
 				int snt=0;
 				snt=writebuf(session,buf,i);
@@ -4928,12 +4854,6 @@ static BOOL exec_cgi(http_session_t *session)
 
 		if(i==0 || i==-1)
 			break;
-
-		tv.tv_sec=1;
-		tv.tv_usec=0;
-		FD_ZERO(&read_set);
-		FD_SET(err_pipe[0],&read_set);
-		FD_SET(out_pipe[0],&read_set);
 	}
 
 	close(out_pipe[0]);		/* close read-end of pipe */
diff --git a/src/sbbs3/xtrn.cpp b/src/sbbs3/xtrn.cpp
index 43c2de570df87f8acd0ff0f466bec9d46b3db36d..e4039fe179f3feffbe29af5722cf77db0cea2d76 100644
--- a/src/sbbs3/xtrn.cpp
+++ b/src/sbbs3/xtrn.cpp
@@ -1069,12 +1069,10 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
 	int		in_pipe[2];
 	int		out_pipe[2];
 	int		err_pipe[2];
-	fd_set ibits;
-	int	high_fd;
-	struct timeval timeout;
     BYTE 	wwiv_buf[XTRN_IO_BUF_LEN*2];
     bool	wwiv_flag=false;
  	char* p;
+	struct pollfd fds[2];
 
 	xtrn_mode = mode;
 	lprintf(LOG_DEBUG, "Executing external: %s", cmdline);
@@ -1689,6 +1687,11 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
 	if(mode&EX_STDOUT) {
 		if(!(mode&EX_STDIN))
 			close(out_pipe[1]);	/* close write-end of pipe */
+		fds[0].fd = out_pipe[0];
+		fds[0].events = POLLIN;
+		fds[1].fd = err_pipe[0];
+		fds[1].events = POLLIN;
+		fds[1].revents = 0;
 		while(!terminated) {
 			if(waitpid(pid, &i, WNOHANG)!=0)	/* child exited */
 				break;
@@ -1707,26 +1710,13 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
 					write(in_pipe[1],buf,wr);
 			}
 
-			/* Error Output */
-			FD_ZERO(&ibits);
-			if(!(mode&EX_NOLOG)) {
-				FD_SET(err_pipe[0],&ibits);
-				high_fd=err_pipe[0];
-			}
-			FD_SET(out_pipe[0],&ibits);
-			if(!(mode&EX_NOLOG)) {
-				if(out_pipe[0]>err_pipe[0])
-					high_fd=out_pipe[0];
-			} else
-				high_fd=out_pipe[0];
-			timeout.tv_sec=0;
-			timeout.tv_usec=1000;
 			bp=buf;
 			i=0;
 			if(mode&EX_NOLOG)
-				select(high_fd+1,&ibits,NULL,NULL,&timeout);
+				poll(fds, (mode & EX_NOLOG) ? 1 : 2, 1);
 			else {
-				while ((select(high_fd+1,&ibits,NULL,NULL,&timeout)>0) && FD_ISSET(err_pipe[0],&ibits) && (i<(int)sizeof(buf)-1))  {
+				while (poll(fds, (mode & EX_NOLOG) ? 1 : 2, 1) > 0 && (fds[1].revents & POLLIN)
+				    && (i < (int)sizeof(buf) - 1))  {
 					if((rd=read(err_pipe[0],bp,1))>0)  {
 						i+=rd;
 						bp++;
@@ -1735,11 +1725,6 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
 					}
 					else
 						break;
-					FD_ZERO(&ibits);
-					FD_SET(err_pipe[0],&ibits);
-					FD_SET(out_pipe[0],&ibits);
-					timeout.tv_sec=0;
-					timeout.tv_usec=1000;
 				}
 				if(i > 0) {
 					buf[i] = '\0';
@@ -1757,7 +1742,7 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
 				}
 			}
 
-			data_waiting=FD_ISSET(out_pipe[0],&ibits);
+			data_waiting=fds[0].revents & POLLIN;
 			if(i==0 && data_waiting==0)
 				continue;
 
@@ -1846,13 +1831,9 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
 		waitpid(pid, &i, 0);
 	else {
 		while(waitpid(pid, &i, WNOHANG)==0)  {
-			FD_ZERO(&ibits);
-			FD_SET(err_pipe[0],&ibits);
-			timeout.tv_sec=1;
-			timeout.tv_usec=0;
 			bp=buf;
 			i=0;
-			while ((select(err_pipe[0]+1,&ibits,NULL,NULL,&timeout)>0) && (i<XTRN_IO_BUF_LEN-1))  {
+			while (socket_readable(err_pipe[0], 1000) && (i<XTRN_IO_BUF_LEN-1))  {
 				if((rd=read(err_pipe[0],bp,1))>0)  {
 					i+=rd;
 					if(*bp=='\n') {