b0c32a85fc
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.
291 lines
7.4 KiB
C
291 lines
7.4 KiB
C
/*
|
|
* $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;
|
|
}
|