aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2008-11-24 13:59:51 +0000
committerDaniel Stenberg <daniel@haxx.se>2008-11-24 13:59:51 +0000
commit53a8a6e5a69ad72ad6684875272babd24c3a93fd (patch)
treeebd878fefffbbc86e99ac9d89be236a80e7bb38b /lib
parentdd2fc45c27d71faf5d37abb313758c0df6c9afd4 (diff)
- Based on a patch by Vlad Grachov, libcurl now uses a new libssh2 0.19
function when built to support SCP and SFTP that helps the library to know in which direction a particular libssh2 operation would return EAGAIN so that libcurl knows what socket conditions to wait for before trying the function call again. Previously (and still when using libssh2 0.18 or earlier), libcurl will busy-loop in this situation when the easy interface is used!
Diffstat (limited to 'lib')
-rw-r--r--lib/ssh.c89
1 files changed, 72 insertions, 17 deletions
diff --git a/lib/ssh.c b/lib/ssh.c
index 6d2108bef..1f1650e20 100644
--- a/lib/ssh.c
+++ b/lib/ssh.c
@@ -40,6 +40,13 @@
#error "this requires libssh2 0.16 or later"
#endif
+#if !defined(HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION) && \
+ (LIBSSH2_VERSION_NUM >= 0x001300)
+/* this is just a check for non-configure based systems to get this properly
+ setup if libssh2 0.19+ is used */
+#define HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION 1
+#endif
+
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -103,6 +110,7 @@
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
#include "strtoofft.h"
#include "multiif.h"
+#include "select.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@@ -421,7 +429,14 @@ static CURLcode ssh_getworkingpath(struct connectdata *conn,
return CURLE_OK;
}
-static CURLcode ssh_statemach_act(struct connectdata *conn)
+/*
+ * ssh_statemach_act() runs the SSH statemachine "one round" and returns. The
+ * data the pointer 'block' points to will be set to TRUE if the libssh2
+ * function returns LIBSSH2_ERROR_EAGAIN meaning it wants to be called again
+ * when the socket is ready
+ */
+
+static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
{
CURLcode result = CURLE_OK;
struct SessionHandle *data = conn->data;
@@ -432,8 +447,9 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
const char *fingerprint;
#endif /* CURL_LIBSSH2_DEBUG */
const char *host_public_key_md5;
- int rc,i;
+ int rc = LIBSSH2_ERROR_NONE, i;
int err;
+ *block = 0; /* we're not blocking by default */
switch(sshc->state) {
case SSH_S_STARTUP:
@@ -519,6 +535,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
if(!sshc->authlist) {
if((err = libssh2_session_last_errno(sshc->ssh_session)) ==
LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
break;
}
else {
@@ -729,6 +746,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
if(!sshc->sftp_session) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
break;
}
else {
@@ -996,21 +1014,21 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
* This takes an extra protocol round trip.
*/
rc = libssh2_sftp_stat(sshc->sftp_session, sshc->quote_path2,
- &sshc->quote_attrs);
+ &sshc->quote_attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
+ break;
}
else if(rc != 0) { /* get those attributes */
- err = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- sshc->quote_path1 = NULL;
- Curl_safefree(sshc->quote_path2);
- sshc->quote_path2 = NULL;
- failf(data, "Attempt to get SFTP stats failed: %s",
- sftp_libssh2_strerror(err));
- state(conn, SSH_SFTP_CLOSE);
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
+ err = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ sshc->quote_path1 = NULL;
+ Curl_safefree(sshc->quote_path2);
+ sshc->quote_path2 = NULL;
+ failf(data, "Attempt to get SFTP stats failed: %s",
+ sftp_libssh2_strerror(err));
+ state(conn, SSH_SFTP_CLOSE);
+ sshc->actualcode = CURLE_QUOTE_ERROR;
+ break;
}
}
@@ -1233,6 +1251,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
if(!sshc->sftp_handle) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
break;
}
else {
@@ -1387,6 +1406,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
if(!sshc->sftp_handle) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
break;
}
else {
@@ -1421,6 +1441,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
PATH_MAX,
&sshc->readdir_attrs);
if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
break;
}
if(sshc->readdir_len > 0) {
@@ -1521,6 +1542,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
sshc->readdir_filename,
PATH_MAX);
if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
break;
}
Curl_safefree(sshc->readdir_linkPath);
@@ -1578,6 +1600,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
case SSH_SFTP_READDIR_DONE:
if(libssh2_sftp_closedir(sshc->sftp_handle) ==
LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
break;
}
sshc->sftp_handle = NULL;
@@ -1601,6 +1624,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
if(!sshc->sftp_handle) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
break;
}
else {
@@ -1814,6 +1838,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
if(!sshc->ssh_channel) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
break;
}
else {
@@ -1860,6 +1885,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
if(!sshc->ssh_channel) {
if(libssh2_session_last_errno(sshc->ssh_session) ==
LIBSSH2_ERROR_EAGAIN) {
+ rc = LIBSSH2_ERROR_EAGAIN;
break;
}
else {
@@ -2011,6 +2037,12 @@ static CURLcode ssh_statemach_act(struct connectdata *conn)
break;
}
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ /* we would block, we need to wait for the socket to be ready (in the
+ right direction too)! */
+ *block = TRUE;
+ }
+
return result;
}
@@ -2019,8 +2051,11 @@ static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done)
{
struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
+ bool block_we_ignore; /* we don't care about EAGAIN at this point, but TODO:
+ we _should_ store the status and use that to
+ provide a ssh_getsock() implementation */
- result = ssh_statemach_act(conn);
+ result = ssh_statemach_act(conn, &block_we_ignore);
*done = (bool)(sshc->state == SSH_STOP);
return result;
@@ -2031,8 +2066,28 @@ static CURLcode ssh_easy_statemach(struct connectdata *conn)
struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
- while((sshc->state != SSH_STOP) && !result)
- result = ssh_statemach_act(conn);
+ while((sshc->state != SSH_STOP) && !result) {
+ bool block;
+ result = ssh_statemach_act(conn, &block);
+
+#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
+ if((CURLE_OK == result) && block) {
+ int dir = libssh2_session_block_directions(sshc->ssh_session);
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ curl_socket_t fd_read = CURL_SOCKET_BAD;
+ curl_socket_t fd_write = CURL_SOCKET_BAD;
+ if (LIBSSH2_SESSION_BLOCK_INBOUND & dir) {
+ fd_read = sock;
+ }
+ if (LIBSSH2_SESSION_BLOCK_OUTBOUND & dir) {
+ fd_write = sock;
+ }
+ /* wait for the socket to become ready */
+ Curl_socket_ready(fd_read, fd_write, 1000); /* ignore result */
+ }
+#endif
+
+ }
return result;
}