Skip to content

Commit

Permalink
sftp_write_sliding.c: new example
Browse files Browse the repository at this point in the history
This is an example that is very similar to sftp_write_nonblock.c, with
the exception that this uses

1 - a larger upload buffer

2 - a sliding buffer mechnism to allow the app to keep sending lots of
data to libssh2 without having to first drain the buffer.

These are two key issues to make libssh2 SFTP uploads really perform
well at this point in time.
  • Loading branch information
bagder committed Dec 4, 2010
1 parent 7ed7dad commit b0c32a8
Showing 1 changed file with 290 additions and 0 deletions.
290 changes: 290 additions & 0 deletions example/sftp_write_sliding.c
@@ -0,0 +1,290 @@
/*
* $Id: sftp_write_nonblock.c,v 1.14 2009/04/28 10:35:30 bagder Exp $
*
* Sample showing how to do SFTP non-blocking write transfers.
*
* The sample code has default values for host name, user name, password
* and path to copy, but you can specify them on the command line like:
*
* "sftp 192.168.0.1 user password sftp_write_nonblock.c /tmp/sftp_write_nonblock.c"
*/

#include "libssh2_config.h"
#include <libssh2.h>
#include <libssh2_sftp.h>

#ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.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>
#include <time.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[])
{
unsigned long hostaddr;
int sock, i, auth_pw = 1;
struct sockaddr_in sin;
const char *fingerprint;
LIBSSH2_SESSION *session;
const char *username="username";
const char *password="password";
const char *loclfile="sftp_write_nonblock.c";
const char *sftppath="/tmp/sftp_write_nonblock.c";
int rc;
#if defined(HAVE_IOCTLSOCKET)
long flag = 1;
#endif
FILE *local;
LIBSSH2_SFTP *sftp_session;
LIBSSH2_SFTP_HANDLE *sftp_handle;
char mem[1024 * 1000];
size_t nread;
size_t memuse;
time_t start;
long total = 0;
int duration;

#ifdef WIN32
WSADATA wsadata;

WSAStartup(MAKEWORD(2,0), &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) {
sftppath = argv[5];
}

rc = libssh2_init (0);
if (rc != 0) {
fprintf (stderr, "libssh2 initialization failed (%d)\n", rc);
return 1;
}

local = fopen(loclfile, "rb");
if (!local) {
printf("Can't local file %s\n", loclfile);
return -1;
}

/*
* The application code is responsible for creating the socket
* and 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;
}

/* 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_SHA1);
printf("Fingerprint: ");
for(i = 0; i < 20; i++) {
printf("%02X ", (unsigned char)fingerprint[i]);
}
printf("\n");

if (auth_pw) {
/* We could authenticate via password */
while ((rc = libssh2_userauth_password(session, username, password)) ==
LIBSSH2_ERROR_EAGAIN);
if (rc) {
printf("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)) ==
LIBSSH2_ERROR_EAGAIN);
if (rc) {
printf("\tAuthentication by public key failed\n");
goto shutdown;
}
}

fprintf(stderr, "libssh2_sftp_init()!\n");
do {
sftp_session = libssh2_sftp_init(session);

if (!sftp_session &&
(libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) {
fprintf(stderr, "Unable to init SFTP session\n");
goto shutdown;
}
} while (!sftp_session);

fprintf(stderr, "libssh2_sftp_open()!\n");
/* Request a file via SFTP */
do {
sftp_handle =
libssh2_sftp_open(sftp_session, sftppath,
LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC,
LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR|
LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH);

if (!sftp_handle &&
(libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) {
fprintf(stderr, "Unable to open file with SFTP\n");
goto shutdown;
}
} while (!sftp_handle);

fprintf(stderr, "libssh2_sftp_open() is done, now send data!\n");

start = time(NULL);

memuse = 0; /* it starts blank */
do {
nread = fread(&mem[memuse], 1, sizeof(mem)-memuse, local);
if (nread <= 0) {
/* end of file */
break;
}
memuse += nread;
total += nread;

/* write data in a loop until we block */
while ((rc = libssh2_sftp_write(sftp_handle, mem, memuse)) ==
LIBSSH2_ERROR_EAGAIN) {
waitsocket(sock, session);
}
if(rc < 0)
break;

if(memuse - rc) {
/* make room for more data at the end of the buffer */
memmove(&mem[0], &mem[rc], memuse - rc);
memuse -= rc;
}
else
/* 'mem' was consumed fully */
memuse = 0;

} while (rc > 0);

duration = (int)(time(NULL)-start);

printf("%ld bytes in %d seconds makes %.1f bytes/sec\n",
total, duration, total/(double)duration);


fclose(local);
libssh2_sftp_close(sftp_handle);
libssh2_sftp_shutdown(sftp_session);

shutdown:

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

#ifdef WIN32
closesocket(sock);
#else
close(sock);
#endif
printf("all done\n");

libssh2_exit();

return 0;
}

0 comments on commit b0c32a8

Please sign in to comment.