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:
committed by
Daniel Stenberg
parent
e652b4c7b8
commit
c5ec167881
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
@@ -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
|
||||||
*
|
*
|
||||||
|
@@ -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);
|
||||||
|
Reference in New Issue
Block a user