Modify the code to truely support non-blocking. Propogate the EAGAIN error

all the way up to the user interface.  All code modules bug sftp.c have
been completed.

Functions that return an "int", or similar return LIBSSH2CHANNEL_EAGAIN to
indicate some part of the call would block, in non-blocking mode.

Functions that return a structure, like "LIBSSH2_CHANNEL *", return NULL
and set the libssh2 error.  The error can be obtained with either
libssh2_session_last_error() or libssh2_session_last_errno().  Either of
these will return the error code of LIBSSH2_ERROR_EAGAIN if the
call would block, in non-blocking mode.

The current state of a function and some variable are keep in the
structures so that on the next call the operation that would block can
be retried again with the same data.
This commit is contained in:
James Housley
2007-06-06 12:34:06 +00:00
parent 9896c291e4
commit 4b8db8c1ab
38 changed files with 6982 additions and 4403 deletions

View File

@@ -0,0 +1,222 @@
/*
* $Id: scp_write_nonblock.c,v 1.1 2007/06/06 12:34:08 jehousley Exp $
*
* Sample showing how to do a simple SCP transfer.
*/
#include <libssh2.h>
#include <libssh2_config.h>
#ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
# ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
unsigned long hostaddr;
int sock, i, auth_pw = 1;
struct sockaddr_in sin;
const char *fingerprint;
LIBSSH2_SESSION *session;
LIBSSH2_CHANNEL *channel;
char *username=(char *)"username";
char *password=(char *)"password";
char *loclfile=(char *)"scp_write.c";
char *scppath=(char *)"/tmp/TEST";
FILE *local;
int rc;
char mem[1024];
size_t nread;
char *ptr;
struct stat fileinfo;
#ifdef WIN32
WSADATA wsadata;
WSAStartup(WINSOCK_VERSION, &wsadata);
#endif
if (argc > 1) {
hostaddr = inet_addr(argv[1]);
} else {
hostaddr = htonl(0x7F000001);
}
if (argc > 2) {
username = argv[2];
}
if (argc > 3) {
password = argv[3];
}
if(argc > 4) {
loclfile = argv[4];
}
if (argc > 5) {
scppath = argv[5];
}
local = fopen(loclfile, "rb");
if (!local) {
fprintf(stderr, "Can't local file %s\n", loclfile);
goto shutdown;
}
stat(loclfile, &fileinfo);
/* 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);
#else
#error "add support for setting the socket non-blocking here"
#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 (auth_pw) {
/* We could authenticate via password */
while ((rc = libssh2_userauth_password(session, username, password)) == LIBSSH2CHANNEL_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/username/.ssh/id_rsa.pub",
"/home/username/.ssh/id_rsa",
password)) == LIBSSH2CHANNEL_EAGAIN);
if (rc) {
fprintf(stderr, "\tAuthentication by public key failed\n");
goto shutdown;
}
}
// libssh2_trace(session, 0xFF7D);
/* Request a file via SCP */
do {
channel = libssh2_scp_send(session, scppath, 0x1FF & fileinfo.st_mode, (unsigned long)fileinfo.st_size);
if ((!channel) && (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) {
fprintf(stderr, "Unable to open a session\n");
goto shutdown;
}
} while (!channel);
fprintf(stderr, "SCP session waiting to send file\n");
do {
nread = fread(mem, 1, sizeof(mem), local);
if (nread <= 0) {
/* end of file */
break;
}
ptr = mem;
do {
/* write data in a loop until we block */
while ((rc = libssh2_channel_write(channel, ptr, nread)) == LIBSSH2CHANNEL_EAGAIN);
if (rc < 0) {
fprintf(stderr, "ERROR %d\n", rc);
}
ptr += rc;
nread -= rc;
} while (nread > 0);
} while (1);
fprintf(stderr, "Sending EOF\n");
while (libssh2_channel_send_eof(channel) == LIBSSH2CHANNEL_EAGAIN);
fprintf(stderr, "Waiting for EOF\n");
while (libssh2_channel_wait_eof(channel) == LIBSSH2CHANNEL_EAGAIN);
fprintf(stderr, "Waiting for channel to close\n");
while (libssh2_channel_wait_closed(channel) == LIBSSH2CHANNEL_EAGAIN);
// fprintf(stderr, "Closing channel\n");
// while (libssh2_channel_close(channel) == LIBSSH2CHANNEL_EAGAIN);
libssh2_channel_free(channel);
channel = NULL;
shutdown:
while ((rc = libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing")) == LIBSSH2CHANNEL_EAGAIN);
libssh2_session_free(session);
#ifdef WIN32
Sleep(1000);
closesocket(sock);
#else
sleep(1);
close(sock);
#endif
fprintf(stderr, "all done\n");
return 0;
}