]> granicus.if.org Git - curl/commitdiff
- Based on a patch by Vlad Grachov, libcurl now uses a new libssh2 0.19
authorDaniel Stenberg <daniel@haxx.se>
Mon, 24 Nov 2008 13:59:51 +0000 (13:59 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 24 Nov 2008 13:59:51 +0000 (13:59 +0000)
  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!

CHANGES
RELEASE-NOTES
configure.ac
lib/ssh.c

diff --git a/CHANGES b/CHANGES
index 3f25a54df057843d1e17e6e60f02060b9921c4fc..cd08f7dd7210717721a2f48da5fd9eb4f42fb907 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,15 @@
 
                                   Changelog
 
+Daniel Stenberg (24 Nov 2008)
+- 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!
+
 Daniel Fandrich (20 Nov 2008)
 - Automatically detect OpenBSD's CA cert bundle.
 
index a33b3c9e71fbccfee2d2a1992d086abedcc7b504..2a7f82d2685adbc45de15357291ab1f30c061019 100644 (file)
@@ -21,19 +21,16 @@ This release includes the following bugfixes:
    the same server
  o memory leak with HTTP GSS/kerberos authentication
  o removed the default use of "Pragma: no-cache"
+ o fix SCP/SFTP busyloop by using a new libssh2 0.19 function
 
 This release includes the following known bugs:
 
  o see docs/KNOWN_BUGS (http://curl.haxx.se/docs/knownbugs.html)
 
-Other curl-related news:
-
- o 
-
 This release would not have looked like this without help, code, reports and
 advice from friends like these:
 
  Yang Tse, Daniel Fandrich, Jim Meyering, Christian Krause, Andreas Wurf,
- Markus Koetter, Josef Wolf
+ Markus Koetter, Josef Wolf, Vlad Grachov
 
         Thanks! (and sorry if I forgot to mention someone)
index f98bded549869fedf20619c9ef8f025a6e0228db..b8eb848b3301a9ba14c2b3c81798ac069df1d4f4 100644 (file)
@@ -1476,6 +1476,10 @@ if test X"$OPT_LIBSSH2" != Xno; then
     LIBSSH2_ENABLED=1
     AC_DEFINE(USE_LIBSSH2, 1, [if libSSH2 is in use])
     AC_SUBST(USE_LIBSSH2, [1])
+
+    dnl check for this function only present in libssh2 0.19+
+    AC_CHECK_FUNCS( libssh2_session_block_directions )
+
   )
 
   if test X"$OPT_LIBSSH2" != Xoff &&
index 6d2108bef1da2ac91985cb04d1b7e1f5bbb5b95a..1f1650e20b03430f34d2f52f81094546e774fdbc 100644 (file)
--- a/lib/ssh.c
+++ b/lib/ssh.c
 #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
 #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;
 }