From fee0239211298a4c17fb461e86ea52c186f77a5b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 7 Feb 2007 21:39:17 +0000 Subject: [PATCH] setting sockets non-blocking using more portable code and configure macros from the libcurl project (written by me) --- acinclude.m4 | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ configure.in | 2 + src/channel.c | 84 +++++++++++++++++++++++++----- 3 files changed, 214 insertions(+), 13 deletions(-) diff --git a/acinclude.m4 b/acinclude.m4 index 3d772c72..ec7d20d8 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -147,3 +147,144 @@ AC_DEFUN([CURL_CC_DEBUG_OPTS], CFLAGS=$NEWFLAGS ]) dnl end of AC_DEFUN() + +dnl CURL_CHECK_NONBLOCKING_SOCKET +dnl ------------------------------------------------- +dnl Check for how to set a socket to non-blocking state. There seems to exist +dnl four known different ways, with the one used almost everywhere being POSIX +dnl and XPG3, while the other different ways for different systems (old BSD, +dnl Windows and Amiga). +dnl +dnl There are two known platforms (AIX 3.x and SunOS 4.1.x) where the +dnl O_NONBLOCK define is found but does not work. This condition is attempted +dnl to get caught in this script by using an excessive number of #ifdefs... +dnl +AC_DEFUN([CURL_CHECK_NONBLOCKING_SOCKET], +[ + AC_MSG_CHECKING([non-blocking sockets style]) + + AC_TRY_COMPILE([ +/* headers for O_NONBLOCK test */ +#include +#include +#include +],[ +/* try to compile O_NONBLOCK */ + +#if defined(sun) || defined(__sun__) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) +# if defined(__SVR4) || defined(__srv4__) +# define PLATFORM_SOLARIS +# else +# define PLATFORM_SUNOS4 +# endif +#endif +#if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX4) +# define PLATFORM_AIX_V3 +#endif + +#if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) || defined(__BEOS__) +#error "O_NONBLOCK does not work on this platform" +#endif + int socket; + int flags = fcntl(socket, F_SETFL, flags | O_NONBLOCK); +],[ +dnl the O_NONBLOCK test was fine +nonblock="O_NONBLOCK" +AC_DEFINE(HAVE_O_NONBLOCK, 1, [use O_NONBLOCK for non-blocking sockets]) +],[ +dnl the code was bad, try a different program now, test 2 + + AC_TRY_COMPILE([ +/* headers for FIONBIO test */ +#include +#include +],[ +/* FIONBIO source test (old-style unix) */ + int socket; + int flags = ioctl(socket, FIONBIO, &flags); +],[ +dnl FIONBIO test was good +nonblock="FIONBIO" +AC_DEFINE(HAVE_FIONBIO, 1, [use FIONBIO for non-blocking sockets]) +],[ +dnl FIONBIO test was also bad +dnl the code was bad, try a different program now, test 3 + + AC_TRY_COMPILE([ +/* headers for ioctlsocket test (Windows) */ +#undef inline +#ifdef HAVE_WINDOWS_H +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#ifdef HAVE_WINSOCK2_H +#include +#else +#ifdef HAVE_WINSOCK_H +#include +#endif +#endif +#endif +],[ +/* ioctlsocket source code */ + SOCKET sd; + unsigned long flags = 0; + sd = socket(0, 0, 0); + ioctlsocket(sd, FIONBIO, &flags); +],[ +dnl ioctlsocket test was good +nonblock="ioctlsocket" +AC_DEFINE(HAVE_IOCTLSOCKET, 1, [use ioctlsocket() for non-blocking sockets]) +],[ +dnl ioctlsocket didnt compile!, go to test 4 + + AC_TRY_LINK([ +/* headers for IoctlSocket test (Amiga?) */ +#include +],[ +/* IoctlSocket source code */ + int socket; + int flags = IoctlSocket(socket, FIONBIO, (long)1); +],[ +dnl ioctlsocket test was good +nonblock="IoctlSocket" +AC_DEFINE(HAVE_IOCTLSOCKET_CASE, 1, [use Ioctlsocket() for non-blocking sockets]) +],[ +dnl Ioctlsocket didnt compile, do test 5! + AC_TRY_COMPILE([ +/* headers for SO_NONBLOCK test (BeOS) */ +#include +],[ +/* SO_NONBLOCK source code */ + long b = 1; + int socket; + int flags = setsockopt(socket, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); +],[ +dnl the SO_NONBLOCK test was good +nonblock="SO_NONBLOCK" +AC_DEFINE(HAVE_SO_NONBLOCK, 1, [use SO_NONBLOCK for non-blocking sockets]) +],[ +dnl test 5 didnt compile! +nonblock="nada" +AC_DEFINE(HAVE_DISABLED_NONBLOCKING, 1, [disabled non-blocking sockets]) +]) +dnl end of fifth test + +]) +dnl end of forth test + +]) +dnl end of third test + +]) +dnl end of second test + +]) +dnl end of non-blocking try-compile test + AC_MSG_RESULT($nonblock) + + if test "$nonblock" = "nada"; then + AC_MSG_WARN([non-block sockets disabled]) + fi +]) diff --git a/configure.in b/configure.in index d241da75..279f4ea1 100644 --- a/configure.in +++ b/configure.in @@ -227,6 +227,8 @@ AC_CHECK_FUNCS(poll gettimeofday select) AC_C_CONST AC_C_INLINE +CURL_CHECK_NONBLOCKING_SOCKET + AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile diff --git a/src/channel.c b/src/channel.c index 1dddd6c7..e51d28eb 100644 --- a/src/channel.c +++ b/src/channel.c @@ -43,6 +43,76 @@ #include + +/* + * _libssh2_nonblock() sets the given socket to either blocking or + * non-blocking mode based on the 'nonblock' boolean argument. This function + * is copied from the libcurl sources with permission. + */ +static int _libssh2_nonblock(int sockfd, /* operate on this */ + int nonblock /* TRUE or FALSE */) +{ +#undef SETBLOCK +#define SETBLOCK 0 +#ifdef HAVE_O_NONBLOCK + /* most recent unix versions */ + int flags; + + flags = fcntl(sockfd, F_GETFL, 0); + if (nonblock) + return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + else + return fcntl(sockfd, F_SETFL, flags & (~O_NONBLOCK)); +#undef SETBLOCK +#define SETBLOCK 1 +#endif + +#if defined(HAVE_FIONBIO) && (SETBLOCK == 0) + /* older unix versions */ + int flags; + + flags = nonblock; + return ioctl(sockfd, FIONBIO, &flags); +#undef SETBLOCK +#define SETBLOCK 2 +#endif + +#if defined(HAVE_IOCTLSOCKET) && (SETBLOCK == 0) + /* Windows? */ + unsigned long flags; + flags = nonblock; + + return ioctlsocket(sockfd, FIONBIO, &flags); +#undef SETBLOCK +#define SETBLOCK 3 +#endif + +#if defined(HAVE_IOCTLSOCKET_CASE) && (SETBLOCK == 0) + /* presumably for Amiga */ + return IoctlSocket(sockfd, FIONBIO, (long)nonblock); +#undef SETBLOCK +#define SETBLOCK 4 +#endif + +#if defined(HAVE_SO_NONBLOCK) && (SETBLOCK == 0) + /* BeOS */ + long b = nonblock ? 1 : 0; + return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); +#undef SETBLOCK +#define SETBLOCK 5 +#endif + +#ifdef HAVE_DISABLED_NONBLOCKING + return 0; /* returns success */ +#undef SETBLOCK +#define SETBLOCK 6 +#endif + +#if (SETBLOCK == 0) +#error "no non-blocking method was found/used/set" +#endif +} + /* {{{ libssh2_channel_nextid * Determine the next channel ID we can use at our end */ @@ -775,20 +845,8 @@ int _libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, } channel->blocking = blocking; -#ifdef F_SETFL - /* FIXME: this can/should be done in a more portable manner */ - rc = fcntl(channel->session->socket_fd, F_GETFL, 0); - if(!blocking) { - rc |= O_NONBLOCK; - } - else { - rc &= ~O_NONBLOCK; - } + _libssh2_nonblock(channel->session->socket_fd, !blocking); - fcntl(channel->session->socket_fd, F_SETFL, rc); -#else -#error "add support for setting the socket non-blocking here" -#endif return bl; } /* }}} */