I figured that, and I realize I'm arguing about a really odd edge-case
that probably won't ever happen to most users. I'm just striving to
make the API as clear as possible and offer as few surprises as
possible to the user of it. It also makes it easier to document and
describe.
Yes, I can appreciate that.
I'd like that if the user-set timeout is less than the keep-alive
timer has left, it should use the api_timeout and return an error if
it hits that timeout.
If you update the patch, I'll offer to write the first draft of the
man pages for the two new functions!
Deal ;-)
I think this patch does that - but it's early in the morning still. Let
me know what you think
--
_____________________________________________
Matt Lilley
Software Engineer
SecuritEase
Tel: +64 4 912-2100
Fax: +64 4 912-2101
E-mail: matt.lil...@securitease.com
Web: http://www.securitease.com
_____________________________________________
This e-mail has passed our content security scan.
It is covered by the confidentiality clauses at
http://www.securitease.com/content_and_confidentiality
>From 684f6d16cfd46b535d29010e406f8fce457962b2 Mon Sep 17 00:00:00 2001
From: Matt Lilley <matt.lil...@securitease.com>
Date: Tue, 3 May 2011 11:20:24 +1200
Subject: [PATCH] Fixes bug #160 as per Daniel's suggestion
---
include/libssh2.h | 4 ++++
src/libssh2_priv.h | 3 +++
src/session.c | 52 +++++++++++++++++++++++++++++++++++++++++++++-------
src/session.h | 44 +++++++++++++++++++++++---------------------
4 files changed, 75 insertions(+), 28 deletions(-)
diff --git a/include/libssh2.h b/include/libssh2.h
index e207411..9462360 100644
--- a/include/libssh2.h
+++ b/include/libssh2.h
@@ -705,6 +705,10 @@ LIBSSH2_API int
libssh2_session_get_blocking(LIBSSH2_SESSION* session);
LIBSSH2_API void libssh2_channel_set_blocking(LIBSSH2_CHANNEL *channel,
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_API void libssh2_channel_handle_extended_data(LIBSSH2_CHANNEL *channel,
int ignore_mode);
diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h
index 231401e..5a697c7 100644
--- a/src/libssh2_priv.h
+++ b/src/libssh2_priv.h
@@ -559,6 +559,9 @@ struct _LIBSSH2_SESSION
/* this is set to TRUE if a blocking API behavior is requested */
int api_block_mode;
+ /* Timeout used when blocking API behavior is active */
+ long api_timeout;
+
/* Server's public key */
const LIBSSH2_HOSTKEY_METHOD *hostkey;
void *server_hostkey_abstract;
diff --git a/src/session.c b/src/session.c
index e4714e8..ef64cf6 100644
--- a/src/session.c
+++ b/src/session.c
@@ -481,6 +481,7 @@ libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)),
session->free = local_free;
session->realloc = local_realloc;
session->abstract = abstract;
+ session->api_timeout = 0; /* timeout-free API by default */
session->api_block_mode = 1; /* blocking API by default */
_libssh2_debug(session, LIBSSH2_TRACE_TRANS,
"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
* 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 seconds_to_next;
int dir;
+ int has_timeout;
/* since libssh2 often sets EAGAIN internally before this function is
called, we can decrease some amount of confusion in user programs by
@@ -591,9 +593,25 @@ int _libssh2_wait_socket(LIBSSH2_SESSION *session)
fd_set *writefd = NULL;
fd_set *readfd = NULL;
struct timeval tv;
-
- tv.tv_sec = seconds_to_next;
- tv.tv_usec = 0;
+
+ if (session->api_timeout > 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) {
FD_ZERO(&rfd);
@@ -607,10 +625,8 @@ int _libssh2_wait_socket(LIBSSH2_SESSION *session)
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,
- seconds_to_next ? &tv : NULL);
+ has_timeout ? &tv : NULL);
#endif
}
}
@@ -1281,6 +1297,28 @@ libssh2_session_get_blocking(LIBSSH2_SESSION * session)
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
*
diff --git a/src/session.h b/src/session.h
index 4eac3a6..2866fcb 100644
--- a/src/session.h
+++ b/src/session.h
@@ -53,15 +53,16 @@
*/
#define BLOCK_ADJUST(rc,sess,x) \
do { \
- rc = x; \
- /* the order of the check below is important to properly deal with the
- case when the 'sess' is freed */ \
- if((rc != LIBSSH2_ERROR_EAGAIN) || !sess->api_block_mode) \
- break; \
- rc = _libssh2_wait_socket(sess); \
- if(rc) \
- break; \
- } while(1)
+ time_t entry_time = time (NULL); \
+ do { \
+ rc = x; \
+ /* the order of the check below is important to properly deal with
the
+ case when the 'sess' is freed */ \
+ if((rc != LIBSSH2_ERROR_EAGAIN) || !sess->api_block_mode) \
+ break; \
+ rc = _libssh2_wait_socket(sess, entry_time); \
+ } while(!rc); \
+ } while(0)
/*
* 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
* 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 { \
- int rc; \
- ptr = x; \
- if(!sess->api_block_mode || \
- (ptr != NULL) || \
- (libssh2_session_last_errno(sess) != LIBSSH2_ERROR_EAGAIN) ) \
- break; \
- rc = _libssh2_wait_socket(sess); \
- if(rc) \
- break; \
- } while(1)
+ time_t entry_time = time (NULL); \
+ do { \
+ int rc; \
+ ptr = x; \
+ if(!sess->api_block_mode || \
+ (ptr != NULL) || \
+ (libssh2_session_last_errno(sess) != LIBSSH2_ERROR_EAGAIN) ) \
+ break; \
+ rc = _libssh2_wait_socket(sess, entry_time); \
+ } 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 */
int _libssh2_session_set_blocking(LIBSSH2_SESSION * session, int blocking);
--
1.7.2.3.msysgit.0
_______________________________________________
libssh2-devel http://cool.haxx.se/cgi-bin/mailman/listinfo/libssh2-devel