From 87995db8fe50580a60c7463613e181033dbb7ef1 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 4 Jun 2009 07:23:55 +0000 Subject: Make use of poll() and add a poll-emulation for win32. git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/libssh/trunk@772 7dcaeef0-15fb-0310-b436-a5af3365683c --- include/libssh/priv.h | 25 +++++++ libssh/CMakeLists.txt | 1 + libssh/poll.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++ libssh/socket.c | 99 +----------------------- 4 files changed, 233 insertions(+), 96 deletions(-) create mode 100644 libssh/poll.c diff --git a/include/libssh/priv.h b/include/libssh/priv.h index 47652a1c..51e89c6b 100644 --- a/include/libssh/priv.h +++ b/include/libssh/priv.h @@ -133,6 +133,28 @@ typedef BN_CTX* bignum_CTX; #include #endif +/* poll support */ +#ifdef HAVE_POLL +#include +typedef struct pollfd pollfd_t; +#else /* HAVE_POLL */ +typedef struct pollfd_s { + socket_t fd; /* file descriptor */ + short events; /* requested events */ + short revents; /* returned events */ +} pollfd_t; + +#define POLLIN 0x001 /* There is data to read. */ +#define POLLPRI 0x002 /* There is urgent data to read. */ +#define POLLOUT 0x004 /* Writing now will not block. */ + +#define POLLERR 0x008 /* Error condition. */ +#define POLLHUP 0x010 /* Hung up. */ +#define POLLNVAL 0x020 /* Invalid polling request. */ + +typedef unsigned long int nfds_t; +#endif /* HAVE_POLL */ + /* wrapper.c */ MD5CTX md5_init(void); void md5_update(MD5CTX c, const void *data, unsigned long len); @@ -474,6 +496,9 @@ STRING *agent_sign_data(struct ssh_session *session, struct public_key_struct *pubkey); #endif +/* poll.c */ +int ssh_poll(pollfd_t *fds, nfds_t nfds, int timeout); + /* socket.c */ struct socket; diff --git a/libssh/CMakeLists.txt b/libssh/CMakeLists.txt index 114a0265..0aceca88 100644 --- a/libssh/CMakeLists.txt +++ b/libssh/CMakeLists.txt @@ -84,6 +84,7 @@ set(libssh_SRCS misc.c options.c packet.c + poll.c session.c socket.c string.c diff --git a/libssh/poll.c b/libssh/poll.c new file mode 100644 index 00000000..c20300dd --- /dev/null +++ b/libssh/poll.c @@ -0,0 +1,204 @@ +/* + * poll.c - poll wrapper + * + * This file is part of the SSH Library + * + * Copyright (c) 2003-2008 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + * + * vim: ts=2 sw=2 et cindent + */ + +/* This code is based on glib's gpoll */ + +#include "config.h" +#include "libssh/priv.h" + +#ifdef HAVE_POLL +#include + +int ssh_poll(pollfd_t *fds, nfds_t nfds, int timeout) { + return poll((struct pollfd *) fds, nfds, timeout); +} + +#else /* HAVE_POLL */ +#ifdef _WIN32 + +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0600) + +#include + +int ssh_poll(pollfd_t *fds, nfds_t nfds, int timeout) { + return WSAPoll(fds, nfds, timeout); +} + +#else /* _WIN32_WINNT */ + +#ifndef STRICT +#define STRICT +#endif + +#include +#include + +static int poll_rest (HANDLE *handles, int nhandles, + pollfd_t *fds, nfds_t nfds, int timeout) { + DWORD ready; + pollfd_t *f; + int recursed_result; + + 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 (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; + } + } + + /* + * 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]; + } + } + nhandles--; + recursed_result = poll_rest(handles, nhandles, fds, nfds, 0); + if (recursed_result < 0) { + return -1; + } + return recursed_result + 1; + } + return 1; + } + + return 0; +} + +int ssh_poll(pollfd_t *fds, nfds_t nfds, int timeout) { + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + pollfd_t *f; + int nhandles = 0; + int rc = -1; + + if (fds == NULL) { + errno = EFAULT; + return -1; + } + + if (nfds >= MAXIMUM_WAIT_OBJECTS) { + errno = EINVAL; + 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; + } + } + + if (i == nhandles) { + if (nhandles == MAXIMUM_WAIT_OBJECTS) { + break; + } else { + handles[nhandles++] = (HANDLE) f->fd; + } + } + } + } + + if (timeout == -1) { + timeout = INFINITE; + } + + 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); + } + + if (rc < 0) { + for (f = fds; f < &fds[nfds]; f++) { + f->revents = 0; + } + errno = EBADF; + } + + return rc; +} + +#endif /* _WIN32_WINNT */ + +#endif /* _WIN32 */ + +#endif /* HAVE_POLL */ + diff --git a/libssh/socket.c b/libssh/socket.c index 5bd00133..16da8cca 100644 --- a/libssh/socket.c +++ b/libssh/socket.c @@ -36,22 +36,6 @@ #endif #include "libssh/priv.h" -#if !defined(HAVE_SELECT) && !defined(HAVE_POLL) -#error Your system must have either select() or poll() -#endif - -#if !defined(HAVE_POLL) && !defined(_WIN32) -#warning your system does not have poll. Select has known limitations -#define SELECT_LIMIT_CHECK -#endif - -#ifdef HAVE_POLL -#define USE_POLL -#include -#else -#define USE_SELECT -#endif - /** \defgroup ssh_socket SSH Sockets * \addtogroup ssh_socket * @{ @@ -472,86 +456,10 @@ int ssh_socket_wait_for_data(struct socket *s, SSH_SESSION *session, u32 len) { return SSH_OK; } -#ifdef USE_SELECT -/* ssh_socket_poll, select() version */ - -/* \internal - * \brief polls the socket for data - * \param session ssh session - * \param writeable value pointed to set to 1 if it is possible to write - * \param except value pointed to set to 1 if there is an exception - * \return 1 if it is possible to read, 0 otherwise, -1 on error - */ -int ssh_socket_poll(struct socket *s, int *writeable, int *except) { - SSH_SESSION *session = s->session; - struct timeval sometime; - fd_set rdes; // read set - fd_set wdes; // writing set - fd_set edes; // exception set - int fdmax =- 1; - - enter_function(); - - FD_ZERO(&rdes); - FD_ZERO(&wdes); - FD_ZERO(&edes); - - if (!ssh_socket_is_open(s)) { - *except = 1; - *writeable = 0; - return 0; - } -#ifdef SELECT_LIMIT_CHECK - // some systems don't handle the fds > FD_SETSIZE - if(s->fd > FD_SETSIZE){ - ssh_set_error(session, SSH_REQUEST_DENIED, - "File descriptor out of range for select: %d", s->fd); - - leave_function(); - return -1; - } -#endif - if (!s->data_to_read) { - ssh_socket_fd_set(s, &rdes, &fdmax); - } - if (!s->data_to_write) { - ssh_socket_fd_set(s, &wdes, &fdmax); - } - ssh_socket_fd_set(s, &edes, &fdmax); - - /* Set to return immediately (no blocking) */ - sometime.tv_sec = 0; - sometime.tv_usec = 0; - - /* Make the call, and listen for errors */ - if (select(fdmax, &rdes, &wdes, &edes, &sometime) < 0) { - ssh_set_error(session, SSH_FATAL, "select(): %s", strerror(errno)); - leave_function(); - return -1; - } - - if (!s->data_to_read) { - s->data_to_read = ssh_socket_fd_isset(s, &rdes); - } - if (!s->data_to_write) { - s->data_to_write = ssh_socket_fd_isset(s, &wdes); - } - if (!s->data_except) { - s->data_except = ssh_socket_fd_isset(s, &edes); - } - *except = s->data_except; - *writeable = s->data_to_write; - - leave_function(); - return (s->data_to_read || (buffer_get_rest_len(s->in_buffer) > 0)); -} -#endif - -#ifdef USE_POLL -/* ssh_socket_poll, poll() version */ +/* ssh_socket_poll */ int ssh_socket_poll(struct socket *s, int *writeable, int *except) { SSH_SESSION *session = s->session; - struct pollfd fd[1]; + pollfd_t fd[1]; int rc = -1; enter_function(); @@ -573,7 +481,7 @@ int ssh_socket_poll(struct socket *s, int *writeable, int *except) { } /* Make the call, and listen for errors */ - rc = poll(fd, 1, 0); + rc = ssh_poll(fd, 1, 0); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "poll(): %s", strerror(errno)); leave_function(); @@ -596,7 +504,6 @@ int ssh_socket_poll(struct socket *s, int *writeable, int *except) { leave_function(); return (s->data_to_read || (buffer_get_rest_len(s->in_buffer) > 0)); } -#endif /** \internal * \brief nonblocking flush of the output buffer -- cgit v1.2.3