diff options
-rw-r--r-- | include/libssh/poll.h | 16 | ||||
-rw-r--r-- | libssh/poll.c | 231 |
2 files changed, 109 insertions, 138 deletions
diff --git a/include/libssh/poll.h b/include/libssh/poll.h index 54a1348b..ab00139f 100644 --- a/include/libssh/poll.h +++ b/include/libssh/poll.h @@ -40,7 +40,7 @@ typedef struct ssh_pollfd_struct { /* poll.c */ #ifndef POLLIN -# define POLLIN 0x001 /* There is data to read. */ +#define POLLIN 0x001 /* There is data to read. */ #endif #ifndef POLLPRI #define POLLPRI 0x002 /* There is urgent data to read. */ @@ -59,6 +59,20 @@ typedef struct ssh_pollfd_struct { #define POLLNVAL 0x020 /* Invalid polling request. */ #endif +#ifndef POLLRDNORM +#define POLLRDNORM 0x040 /* mapped to read fds_set */ +#endif +#ifndef POLLRDBAND +#define POLLRDBAND 0x080 /* mapped to exception fds_set */ +#endif +#ifndef POLLWRNORM +#define POLLWRNORM 0x100 /* mapped to write fds_set */ +#endif +#ifndef POLLWRBAND +#define POLLWRBAND 0x200 /* mapped to write fds_set */ +#endif + + typedef unsigned long int nfds_t; #endif /* HAVE_POLL */ diff --git a/libssh/poll.c b/libssh/poll.c index f03f3d43..ea59266d 100644 --- a/libssh/poll.c +++ b/libssh/poll.c @@ -3,6 +3,7 @@ * * This file is part of the SSH Library * + * Copyright (c) 2009-2010 by Andreas Schneider <mail@cynapses.org> * Copyright (c) 2003-2009 by Aris Adamantiadis * Copyright (c) 2009 Aleksandar Kanchev * @@ -24,8 +25,6 @@ * vim: ts=2 sw=2 et cindent */ -/* This code is based on glib's gpoll */ - #include "config.h" #include <errno.h> @@ -69,171 +68,129 @@ int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { } #else /* HAVE_POLL */ -#ifdef _WIN32 - -#if 0 -/* defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) */ - -#include <winsock2.h> - -int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { - return WSAPoll(fds, nfds, timeout); -} -#else /* _WIN32_WINNT */ +#include <sys/time.h> +#include <sys/types.h> +#ifdef _WIN32 #ifndef STRICT #define STRICT #endif -#include <stdio.h> -#include <windows.h> -#include <errno.h> +#include <winsock2.h> +#else +#include <sys/select.h> +#include <sys/socket.h> +#include <unistd.h> +#endif -static int poll_rest (HANDLE *handles, int nhandles, - ssh_pollfd_t *fds, nfds_t nfds, int timeout) { - DWORD ready; - ssh_pollfd_t *f; - int recursed_result; +static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { + fd_set readfds, writefds, exceptfds; + struct timeval tv, *ptv; + int max_fd, rc; + nfds_t i; - if (nhandles == 0) { - /* No handles to wait for, just the timeout */ - if (timeout == INFINITE) { - ready = WAIT_FAILED; - } else { - SleepEx(timeout, 1); - ready = WAIT_TIMEOUT; - } - } else { - /* Wait for just handles */ - ready = WaitForMultipleObjectsEx(nhandles, handles, FALSE, timeout, TRUE); -#if 0 - if (ready == WAIT_FAILED) { - fprintf(stderr, "WaitForMultipleObjectsEx failed: %d\n", GetLastError()); - } -#endif + if (fds == NULL) { + errno = EFAULT; + return -1; } - if (ready == WAIT_FAILED) { - return -1; - } else if (ready == WAIT_TIMEOUT || ready == WAIT_IO_COMPLETION) { - return 0; - } else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles) { - for (f = fds; f < &fds[nfds]; f++) { - if ((HANDLE) f->fd == handles[ready - WAIT_OBJECT_0]) { - f->revents = f->events; + FD_ZERO (&readfds); + FD_ZERO (&writefds); + FD_ZERO (&exceptfds); + + /* compute fd_sets and find largest descriptor */ + for (max_fd = -1, i = 0; i < nfds; i++) { + if (fds[i].fd < 0) { + continue; } - } - /* - * If no timeout and polling several handles, recurse to poll - * the rest of them. - */ - if (timeout == 0 && nhandles > 1) { - /* Remove the handle that fired */ - int i; - if (ready < nhandles - 1) { - for (i = ready - WAIT_OBJECT_0 + 1; i < nhandles; i++) { - handles[i-1] = handles[i]; - } + if (fds[i].events & (POLLIN | POLLRDNORM)) { + FD_SET (fds[i].fd, &readfds); } - nhandles--; - recursed_result = poll_rest(handles, nhandles, fds, nfds, 0); - if (recursed_result < 0) { - return -1; + if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) { + FD_SET (fds[i].fd, &writefds); + } + if (fds[i].events & (POLLPRI | POLLRDBAND)) { + FD_SET (fds[i].fd, &exceptfds); + } + if (fds[i].fd >= max_fd && + (fds[i].events & (POLLIN | POLLOUT | POLLPRI | + POLLRDNORM | POLLRDBAND | + POLLWRNORM | POLLWRBAND))) { + max_fd = fds[i].fd; } - return recursed_result + 1; - } - return 1; } - return 0; -} - -int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { - HANDLE handles[MAXIMUM_WAIT_OBJECTS]; - ssh_pollfd_t *f; - int nhandles = 0; - int rc = -1; + if (max_fd == -1) { + errno = EINVAL; + return -1; + } - if (fds == NULL) { - errno = EFAULT; - return -1; + if (timeout < 0) { + ptv = NULL; + } else { + ptv = &tv; + if (timeout == 0) { + tv.tv_sec = 0; + tv.tv_usec = 0; + } else { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + } } - if (nfds >= MAXIMUM_WAIT_OBJECTS) { - errno = EINVAL; - return -1; + rc = select (max_fd + 1, &readfds, &writefds, &exceptfds, ptv); + if (rc < 0) { + return -1; } - for (f = fds; f < &fds[nfds]; f++) { - if (f->fd > 0) { - int i; - - /* - * Don't add the same handle several times into the array, as - * docs say that is not allowed, even if it actually does seem - * to work. - */ - for (i = 0; i < nhandles; i++) { - if (handles[i] == (HANDLE) f->fd) { - break; - } - } + for (rc = 0, i = 0; i < nfds; i++) + if (fds[i].fd >= 0) { + fds[i].revents = 0; - if (i == nhandles) { - if (nhandles == MAXIMUM_WAIT_OBJECTS) { - break; - } else { - handles[nhandles++] = (HANDLE) f->fd; - } - } - } - } + if (FD_ISSET(fds[i].fd, &readfds)) { + int save_errno = errno; + char data[64] = {0}; - if (timeout == -1) { - timeout = INFINITE; - } + /* support for POLLHUP */ + if ((recv(fds[i].fd, data, 64, MSG_PEEK) == -1) && + (errno == ESHUTDOWN || errno == ECONNRESET || + errno == ECONNABORTED || errno == ENETRESET)) { + fds[i].revents |= POLLHUP; + } else { + fds[i].revents |= fds[i].events & (POLLIN | POLLRDNORM); + } - if (nhandles > 1) { - /* - * First check if one or several of them are immediately - * available. - */ - rc = poll_rest(handles, nhandles, fds, nfds, 0); - - /* - * If not, and we have a significant timeout, poll again with - * timeout then. Note that this will return indication for only - * one event, or only for messages. We ignore timeouts less than - * ten milliseconds as they are mostly pointless on Windows, the - * MsgWaitForMultipleObjectsEx() call will timeout right away - * anyway. - */ - if (rc == 0 && (timeout == INFINITE || timeout >= 10)) { - rc = poll_rest(handles, nhandles, fds, nfds, timeout); - } - } else { - /* - * Just polling for one thing, so no need to check first if - * available immediately - */ - rc = poll_rest(handles, nhandles, fds, nfds, timeout); - } + errno = save_errno; + } + if (FD_ISSET(fds[i].fd, &writefds)) { + fds[i].revents |= fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND); + } - if (rc < 0) { - for (f = fds; f < &fds[nfds]; f++) { - f->revents = 0; + if (FD_ISSET(fds[i].fd, &exceptfds)) { + fds[i].revents |= fds[i].events & (POLLPRI | POLLRDBAND); + } + + if (fds[i].revents & ~POLLHUP) { + rc++; + } + } else { + fds[i].revents = POLLNVAL; } - errno = EBADF; - } return rc; } -#endif /* _WIN32_WINNT */ - -#endif /* _WIN32 */ +/* TODO Detect if WSAPoll() is available and load it dynamically */ +int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout) { +#if 0 +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) */ + return WSAPoll(fds, nfds, timeout); +#endif +#endif + return bsd_poll(fds, nfds, timeout); +} #endif /* HAVE_POLL */ |