Hello,

Exec "uptime" on a remote machine (LAN) does not
return the "stdout" output, if "libssh2_channel_read()"
is not called fast enough.
Instead, "libssh2_channel_read()" returns zero.

This issue may be reproduced via the attached c-file.
If the "sleep(3)" right before "libssh2_channel_read()"
is removed, everything works fine.

Libssh2 release 1.1

Thank you very much
Markus


------------------------------------------


#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>

#include <libssh2.h>


static int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
{
   struct timeval timeout;
   int rc;
   fd_set fd;
   fd_set *writefd = NULL;
   fd_set *readfd = NULL;
   int dir;

   timeout.tv_sec = 10;
   timeout.tv_usec = 0;

   FD_ZERO(&fd);

   FD_SET(socket_fd, &fd);

   /* now make sure we wait in the correct direction */
   dir = libssh2_session_block_directions(session);

   if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
       readfd = &fd;

   if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
       writefd = &fd;

   rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);

   return rc;
}

int main(int argc, char *argv[])
{
   const char *remotehost  = "192.168.1.69";
   const char *commandline = "uptime";
   const char *username    = "double";
   const char *password    = "";
   unsigned long hostaddr;
   int sock, i;
   struct sockaddr_in sin;
   const char *fingerprint;
   LIBSSH2_SESSION *session;
   LIBSSH2_CHANNEL *channel;
   int rc;

#ifdef WIN32
   WSADATA wsadata;
   WSAStartup(MAKEWORD(2,0), &wsadata);
#endif

   hostaddr = inet_addr( remotehost );

   /* Ultra basic "connect to port 22 on localhost"
    * Your code is responsible for creating the socket establishing the
    * connection
    */
   sock = socket(AF_INET, SOCK_STREAM, 0);

   sin.sin_family = AF_INET;
   sin.sin_port = htons(22);
   sin.sin_addr.s_addr = hostaddr;
if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) {
       fprintf(stderr, "failed to connect!\n");
       return -1;
   }

   /* We set the socket non-blocking. We do it after the connect just to
       simplify the example code. */
#ifdef F_SETFL
   /* FIXME: this can/should be done in a more portable manner */
   rc = fcntl(sock, F_GETFL, 0);
   fcntl(sock, F_SETFL, rc | O_NONBLOCK);
#elif defined(HAVE_IOCTLSOCKET)
   ioctlsocket(sock, FIONBIO, &flag);
#else
#ifdef WIN32
   u_long mode = 1;
   ioctlsocket (sock, FIONBIO, &mode);
#else
#error "add support for setting the socket non-blocking here"
#endif
#endif

   /* Create a session instance */
   session = libssh2_session_init();
   if (!session)
       return -1;

   /* Since we have set non-blocking, tell libssh2 we are non-blocking */
   libssh2_session_set_blocking(session, 0);

   /* ... start it up. This will trade welcome banners, exchange keys,
    * and setup crypto, compression, and MAC layers
    */
   while ((rc = libssh2_session_startup(session, sock)) ==
          LIBSSH2_ERROR_EAGAIN);
   if (rc) {
       fprintf(stderr, "Failure establishing SSH session: %d\n", rc);
       return -1;
   }

   /* At this point we havn't yet authenticated.  The first thing to do
* is check the hostkey's fingerprint against our known hosts Your app
       * may have it hard coded, may go to a file, may present it to the
       * user, that's your call
       */
   fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
   fprintf(stderr, "Fingerprint: ");
   for(i = 0; i < 16; i++) {
       fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]);
   }
   fprintf(stderr, "\n");

   if ( strlen(password) != 0 ) {
       /* We could authenticate via password */
while ((rc = libssh2_userauth_password(session, username, password)) ==
              LIBSSH2_ERROR_EAGAIN);
       if (rc) {
           fprintf(stderr, "Authentication by password failed.\n");
           goto shutdown;
       }
   } else {
       /* Or by public key */
       while ((rc = libssh2_userauth_publickey_fromfile(session, username,
"/home/double/" ".ssh/id_rsa.pub", "/home/double/" ".ssh/id_rsa",
                                                        password)) ==
              LIBSSH2_ERROR_EAGAIN);
   if (rc) {
           fprintf(stderr, "\tAuthentication by public key failed\n");
           goto shutdown;
       }
   }

libssh2_trace(session, LIBSSH2_TRACE_TRANS | LIBSSH2_TRACE_KEX | LIBSSH2_TRACE_AUTH | LIBSSH2_TRACE_CONN | LIBSSH2_TRACE_SCP | LIBSSH2_TRACE_SFTP | LIBSSH2_TRACE_ERROR | LIBSSH2_TRACE_PUBLICKEY );

   /* Exec non-blocking on the remove host */
   while( (channel = libssh2_channel_open_session(session)) == NULL &&
libssh2_session_last_error(session,NULL,NULL,0) == LIBSSH2_ERROR_EAGAIN )
   {
       waitsocket(sock, session);
   }
   if( channel == NULL )
   {
       fprintf(stderr,"Error\n");
       exit( 1 );
   }
while( (rc = libssh2_channel_exec(channel, commandline)) == LIBSSH2_ERROR_EAGAIN )
   {
       waitsocket(sock, session);
   }
   if( rc != 0 )
   {
       fprintf(stderr,"Error\n");
       exit( 1 );
   }
   for( ;; )
   {
       // loop until we block
       int rc;
       do
       {
           char buffer[0x4000];
/* strange thing */
sleep( 3 );
           rc = libssh2_channel_read( channel, buffer, sizeof(buffer) );
           if( rc > 0 )
           {
               int i;
               for( i=0; i < rc; ++i )
                   putchar( buffer[i] );
           }
       }
       while( rc > 0 );
// this is due to blocking that would occur otherwise so we loop on this condition
       if( rc == LIBSSH2_ERROR_EAGAIN )
       {
           waitsocket(sock, session);
       }
       else if( rc == 0 )
           break;
   }
   int exitcode = 127;
   while( (rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN )
       ;
   if( rc == 0 )
   {
       //does-not-work if( libssh2_channel_wait_closed(channel) == 0 )
       exitcode = libssh2_channel_get_exit_status( channel );
   }
   printf("\n%d\n", exitcode );

   libssh2_channel_free(channel);
   channel = NULL;

shutdown:

   libssh2_session_disconnect(session,
                              "Normal Shutdown, Thank you for playing");
   libssh2_session_free(session);

#ifdef WIN32
   Sleep(1000);
   closesocket(sock);
#else
   sleep(1);
   close(sock);
#endif
   fprintf(stderr, "all done\n");
   return 0;
}

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>

#include <libssh2.h>


static int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
{
    struct timeval timeout;
    int rc;
    fd_set fd;
    fd_set *writefd = NULL;
    fd_set *readfd = NULL;
    int dir;

    timeout.tv_sec = 10;
    timeout.tv_usec = 0;

    FD_ZERO(&fd);

    FD_SET(socket_fd, &fd);

    /* now make sure we wait in the correct direction */
    dir = libssh2_session_block_directions(session);

    if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
        readfd = &fd;

    if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
        writefd = &fd;

    rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);

    return rc;
}

int main(int argc, char *argv[])
{
    const char *remotehost  = "192.168.1.69";
    const char *commandline = "uptime";
    const char *username    = "double";
    const char *password    = "";
    unsigned long hostaddr;
    int sock, i;
    struct sockaddr_in sin;
    const char *fingerprint;
    LIBSSH2_SESSION *session;
    LIBSSH2_CHANNEL *channel;
    int rc;

#ifdef WIN32
    WSADATA wsadata;
    WSAStartup(MAKEWORD(2,0), &wsadata);
#endif

    hostaddr = inet_addr( remotehost );

    /* Ultra basic "connect to port 22 on localhost"
     * Your code is responsible for creating the socket establishing the
     * connection
     */
    sock = socket(AF_INET, SOCK_STREAM, 0);

    sin.sin_family = AF_INET;
    sin.sin_port = htons(22);
    sin.sin_addr.s_addr = hostaddr;
    if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) {
        fprintf(stderr, "failed to connect!\n");
        return -1;
    }

    /* We set the socket non-blocking. We do it after the connect just to
        simplify the example code. */
#ifdef F_SETFL
    /* FIXME: this can/should be done in a more portable manner */
    rc = fcntl(sock, F_GETFL, 0);
    fcntl(sock, F_SETFL, rc | O_NONBLOCK);
#elif defined(HAVE_IOCTLSOCKET)
    ioctlsocket(sock, FIONBIO, &flag);
#else
#ifdef WIN32
    u_long mode = 1;
    ioctlsocket (sock, FIONBIO, &mode);
#else
#error "add support for setting the socket non-blocking here"
#endif
#endif

    /* Create a session instance */
    session = libssh2_session_init();
    if (!session)
        return -1;

    /* Since we have set non-blocking, tell libssh2 we are non-blocking */
    libssh2_session_set_blocking(session, 0);

    /* ... start it up. This will trade welcome banners, exchange keys,
     * and setup crypto, compression, and MAC layers
     */
    while ((rc = libssh2_session_startup(session, sock)) ==
           LIBSSH2_ERROR_EAGAIN);
    if (rc) {
        fprintf(stderr, "Failure establishing SSH session: %d\n", rc);
        return -1;
    }

    /* At this point we havn't yet authenticated.  The first thing to do
        * is check the hostkey's fingerprint against our known hosts Your app
        * may have it hard coded, may go to a file, may present it to the
        * user, that's your call
        */
    fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
    fprintf(stderr, "Fingerprint: ");
    for(i = 0; i < 16; i++) {
        fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]);
    }
    fprintf(stderr, "\n");

    if ( strlen(password) != 0 ) {
        /* We could authenticate via password */
        while ((rc = libssh2_userauth_password(session, username, password)) ==
               LIBSSH2_ERROR_EAGAIN);
        if (rc) {
            fprintf(stderr, "Authentication by password failed.\n");
            goto shutdown;
        }
    } else {
        /* Or by public key */
        while ((rc = libssh2_userauth_publickey_fromfile(session, username,
                                                         "/home/double/" ".ssh/id_rsa.pub",
                                                         "/home/double/" ".ssh/id_rsa",
                                                         password)) ==
               LIBSSH2_ERROR_EAGAIN);
    if (rc) {
            fprintf(stderr, "\tAuthentication by public key failed\n");
            goto shutdown;
        }
    }

    libssh2_trace(session, LIBSSH2_TRACE_TRANS | LIBSSH2_TRACE_KEX | LIBSSH2_TRACE_AUTH | LIBSSH2_TRACE_CONN | LIBSSH2_TRACE_SCP | LIBSSH2_TRACE_SFTP | LIBSSH2_TRACE_ERROR | LIBSSH2_TRACE_PUBLICKEY );

    /* Exec non-blocking on the remove host */
    while( (channel = libssh2_channel_open_session(session)) == NULL &&
           libssh2_session_last_error(session,NULL,NULL,0) == LIBSSH2_ERROR_EAGAIN )
    {
        waitsocket(sock, session);
    }
    if( channel == NULL )
    {
        fprintf(stderr,"Error\n");
        exit( 1 );
    }
    while( (rc = libssh2_channel_exec(channel, commandline)) == LIBSSH2_ERROR_EAGAIN )
    {
        waitsocket(sock, session);
    }
    if( rc != 0 )
    {
        fprintf(stderr,"Error\n");
        exit( 1 );
    }
    for( ;; )
    {
        // loop until we block
        int rc;
        do
        {
            char buffer[0x4000];
/* strange thing */
sleep( 3 );
            rc = libssh2_channel_read( channel, buffer, sizeof(buffer) );
            if( rc > 0 )
            {
                int i;
                for( i=0; i < rc; ++i )
                    putchar( buffer[i] );
            }
        }
        while( rc > 0 );
        // this is due to blocking that would occur otherwise so we loop on this condition
        if( rc == LIBSSH2_ERROR_EAGAIN )
        {
            waitsocket(sock, session);
        }
        else if( rc == 0 )
            break;
    }
    int exitcode = 127;
    while( (rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN )
        ;
    if( rc == 0 )
    {
        //does-not-work if( libssh2_channel_wait_closed(channel) == 0 )
        exitcode = libssh2_channel_get_exit_status( channel );
    }
    printf("\n%d\n", exitcode );

    libssh2_channel_free(channel);
    channel = NULL;

shutdown:

    libssh2_session_disconnect(session,
                               "Normal Shutdown, Thank you for playing");
    libssh2_session_free(session);

#ifdef WIN32
    Sleep(1000);
    closesocket(sock);
#else
    sleep(1);
    close(sock);
#endif
    fprintf(stderr, "all done\n");
    return 0;
}

------------------------------------------------------------------------------
Register Now & Save for Velocity, the Web Performance & Operations 
Conference from O'Reilly Media. Velocity features a full day of 
expert-led, hands-on workshops and two days of sessions from industry 
leaders in dedicated Performance & Operations tracks. Use code vel09scf 
and Save an extra 15% before 5/3. http://p.sf.net/sfu/velocityconf
_______________________________________________
libssh2-devel mailing list
libssh2-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libssh2-devel

Reply via email to