1
0
mirror of https://github.com/libssh2/libssh2.git synced 2025-08-10 06:23:02 +03:00

adds a timeout to blocking calls

Fixes bug #160 as per Daniel's suggestion

Adds libssh2_session_set_timeout() and libssh2_session_get_timeout()
This commit is contained in:
Matt Lilley
2011-05-03 11:20:24 +12:00
committed by Daniel Stenberg
parent e652b4c7b8
commit c5ec167881
4 changed files with 77 additions and 27 deletions

View File

@@ -705,6 +705,10 @@ LIBSSH2_API int libssh2_session_get_blocking(LIBSSH2_SESSION* session);
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel, LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel,
int blocking); int blocking);
LIBSSH2_API void libssh2_session_set_timeout(LIBSSH2_SESSION* session,
long timeout);
LIBSSH2_API long libssh2_session_get_timeout(LIBSSH2_SESSION* session);
/* libssh2_channel_handle_extended_data is DEPRECATED, do not use! */ /* libssh2_channel_handle_extended_data is DEPRECATED, do not use! */
LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel, LIBSSH2_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel,
int ignore_mode); int ignore_mode);

View File

@@ -559,6 +559,9 @@ struct _LIBSSH2_SESSION
/* this is set to TRUE if a blocking API behavior is requested */ /* this is set to TRUE if a blocking API behavior is requested */
int api_block_mode; int api_block_mode;
/* Timeout used when blocking API behavior is active */
long api_timeout;
/* Server's public key */ /* Server's public key */
const LIBSSH2_HOSTKEY_METHOD *hostkey; const LIBSSH2_HOSTKEY_METHOD *hostkey;
void *server_hostkey_abstract; void *server_hostkey_abstract;

View File

@@ -481,6 +481,7 @@ libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)),
session->free = local_free; session->free = local_free;
session->realloc = local_realloc; session->realloc = local_realloc;
session->abstract = abstract; session->abstract = abstract;
session->api_timeout = 0; /* timeout-free API by default */
session->api_block_mode = 1; /* blocking API by default */ session->api_block_mode = 1; /* blocking API by default */
_libssh2_debug(session, LIBSSH2_TRACE_TRANS, _libssh2_debug(session, LIBSSH2_TRACE_TRANS,
"New session resource allocated"); "New session resource allocated");
@@ -542,11 +543,12 @@ libssh2_session_callback_set(LIBSSH2_SESSION * session,
* Utility function that waits for action on the socket. Returns 0 when ready * Utility function that waits for action on the socket. Returns 0 when ready
* to run again or error on timeout. * to run again or error on timeout.
*/ */
int _libssh2_wait_socket(LIBSSH2_SESSION *session) int _libssh2_wait_socket(LIBSSH2_SESSION *session, time_t start_time)
{ {
int rc; int rc;
int seconds_to_next; int seconds_to_next;
int dir; int dir;
int has_timeout;
/* since libssh2 often sets EAGAIN internally before this function is /* since libssh2 often sets EAGAIN internally before this function is
called, we can decrease some amount of confusion in user programs by called, we can decrease some amount of confusion in user programs by
@@ -592,8 +594,27 @@ int _libssh2_wait_socket(LIBSSH2_SESSION *session)
fd_set *readfd = NULL; fd_set *readfd = NULL;
struct timeval tv; struct timeval tv;
tv.tv_sec = seconds_to_next; if (session->api_timeout > 0 &&
tv.tv_usec = 0; (seconds_to_next == 0 ||
seconds_to_next > session->api_timeout)) {
time_t now = time (NULL);
long elapsed_ms = (long)(1000*difftime(start_time, now));
if (elapsed_ms > session->api_timeout) {
session->err_code = LIBSSH2_ERROR_TIMEOUT;
return LIBSSH2_ERROR_TIMEOUT;
}
tv.tv_sec = (session->api_timeout - elapsed_ms) / 1000;
tv.tv_usec = ((session->api_timeout - elapsed_ms) % 1000) *
1000;
has_timeout = 1;
}
else if (seconds_to_next > 0) {
tv.tv_sec = seconds_to_next;
tv.tv_usec = 0;
has_timeout = 1;
}
else
has_timeout = 0;
if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) { if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) {
FD_ZERO(&rfd); FD_ZERO(&rfd);
@@ -607,10 +628,8 @@ int _libssh2_wait_socket(LIBSSH2_SESSION *session)
writefd = &wfd; writefd = &wfd;
} }
/* Note that this COULD be made to use a timeout that perhaps
could be customizable by the app or something... */
rc = select(session->socket_fd + 1, readfd, writefd, NULL, rc = select(session->socket_fd + 1, readfd, writefd, NULL,
seconds_to_next ? &tv : NULL); has_timeout ? &tv : NULL);
#endif #endif
} }
} }
@@ -1281,6 +1300,28 @@ libssh2_session_get_blocking(LIBSSH2_SESSION * session)
return session->api_block_mode; return session->api_block_mode;
} }
/* libssh2_session_set_timeout
*
* Set a session's timeout (in msec) for blocking mode,
* or 0 to disable timeouts.
*/
LIBSSH2_API void
libssh2_session_set_timeout(LIBSSH2_SESSION * session, long timeout)
{
session->api_timeout = timeout;
}
/* libssh2_session_get_timeout
*
* Returns a session's timeout, or 0 if disabled
*/
LIBSSH2_API long
libssh2_session_get_timeout(LIBSSH2_SESSION * session)
{
return session->api_timeout;
}
/* /*
* libssh2_poll_channel_read * libssh2_poll_channel_read
* *

View File

@@ -53,15 +53,16 @@
*/ */
#define BLOCK_ADJUST(rc,sess,x) \ #define BLOCK_ADJUST(rc,sess,x) \
do { \ do { \
rc = x; \ time_t entry_time = time (NULL); \
/* the order of the check below is important to properly deal with the do { \
case when the 'sess' is freed */ \ rc = x; \
if((rc != LIBSSH2_ERROR_EAGAIN) || !sess->api_block_mode) \ /* the order of the check below is important to properly deal with the
break; \ case when the 'sess' is freed */ \
rc = _libssh2_wait_socket(sess); \ if((rc != LIBSSH2_ERROR_EAGAIN) || !sess->api_block_mode) \
if(rc) \ break; \
break; \ rc = _libssh2_wait_socket(sess, entry_time); \
} while(1) } while(!rc); \
} while(0)
/* /*
* For functions that returns a pointer, we need to check if the API is * For functions that returns a pointer, we need to check if the API is
@@ -69,21 +70,22 @@
* immediately. If the API is blocking and we get a NULL we check the errno * immediately. If the API is blocking and we get a NULL we check the errno
* and *only* if that is EAGAIN we loop and wait for socket action. * and *only* if that is EAGAIN we loop and wait for socket action.
*/ */
#define BLOCK_ADJUST_ERRNO(ptr,sess,x) \ #define BLOCK_ADJUST_ERRNO(ptr,sess,x) \
do { \ do { \
int rc; \ time_t entry_time = time (NULL); \
ptr = x; \ do { \
if(!sess->api_block_mode || \ int rc; \
(ptr != NULL) || \ ptr = x; \
(libssh2_session_last_errno(sess) != LIBSSH2_ERROR_EAGAIN) ) \ if(!sess->api_block_mode || \
break; \ (ptr != NULL) || \
rc = _libssh2_wait_socket(sess); \ (libssh2_session_last_errno(sess) != LIBSSH2_ERROR_EAGAIN) ) \
if(rc) \ break; \
break; \ rc = _libssh2_wait_socket(sess, entry_time); \
} while(1) } while(!rc); \
} while(0)
int _libssh2_wait_socket(LIBSSH2_SESSION *session); int _libssh2_wait_socket(LIBSSH2_SESSION *session, time_t entry_time);
/* this is the lib-internal set blocking function */ /* this is the lib-internal set blocking function */
int _libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking); int _libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking);