summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2015-08-29 18:49:16 +0200
committerAndreas Baumann <mail@andreasbaumann.cc>2015-08-29 18:49:16 +0200
commitb8f09498388d4d167ec0add5ec27c3c75551a940 (patch)
tree9fb3375d39c593a9347052958d4886e2dda46a73
parente116d8c1197735cb9a7b853edb2e784d42bd6ae6 (diff)
downloadcssh-b8f09498388d4d167ec0add5ec27c3c75551a940.tar.gz
cssh-b8f09498388d4d167ec0add5ec27c3c75551a940.tar.bz2
handling receiving of files in SCP mode
-rw-r--r--src/cssh.c235
1 files changed, 152 insertions, 83 deletions
diff --git a/src/cssh.c b/src/cssh.c
index 8aa9a0c..da21076 100644
--- a/src/cssh.c
+++ b/src/cssh.c
@@ -16,6 +16,8 @@
#include "options.h"
#include "version.h"
+#define BUFSIZE 4096
+
static int parse_options_and_arguments( int argc, char *argv[], struct gengetopt_args_info *args_info ) {
cmdline_parser_init( args_info );
@@ -276,7 +278,7 @@ static int read_hosts_file( const char *hosts_file, unsigned short default_port,
return 0;
}
-static void cleanup_sessions( ssh_session **session, ssh_channel **channel, ssh_scp **scp, char **host, unsigned short *port, const int nof_sessions, bool verbose )
+static void cleanup_sessions( ssh_session **session, ssh_channel **channel, ssh_scp **scp, char ***buf, char **host, unsigned short *port, const int nof_sessions, bool verbose )
{
for( unsigned int i = 0; i < nof_sessions; i++ ) {
if( ssh_is_connected( (*session)[i] ) ) {
@@ -294,6 +296,9 @@ static void cleanup_sessions( ssh_session **session, ssh_channel **channel, ssh_
ssh_scp_close( (*scp)[i] );
ssh_scp_free( (*scp)[i] );
}
+ if( buf != NULL ) {
+ free( (*buf)[i] );
+ }
ssh_disconnect( (*session)[i] );
if( verbose ) {
fprintf( stderr, "Disconnected from '%s', port %d..\n", host[i], port[i] );
@@ -307,6 +312,9 @@ static void cleanup_sessions( ssh_session **session, ssh_channel **channel, ssh_
if( scp != NULL ) {
free( *scp );
}
+ if( buf != NULL ) {
+ free( *buf );
+ }
free( *session );
free( *host );
free( port );
@@ -461,7 +469,7 @@ int main( int argc, char *argv[] )
} else {
fprintf( stderr, "ERROR: error connecting to '%s', port '%d': %s\n",
host[i], port[i], ssh_get_error( session[i] ) );
- cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
}
@@ -503,7 +511,7 @@ int main( int argc, char *argv[] )
if( verify_knownhost( session[i] ) < 0 ) {
fprintf( stderr, "ERROR: closing connection to '%s', port '%d' due to security reasons\n",
host[i], port[i] );
- cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
auth_state[i] = CSSH_AUTH_VERIFY_HOST_DONE;
@@ -524,7 +532,7 @@ int main( int argc, char *argv[] )
} else {
fprintf( stderr, "ERROR: ssh_userauth_none to '%s', port '%d' failed: %s\n",
host[i], port[i], ssh_get_error( session[i] ) );
- cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
break;
@@ -549,7 +557,7 @@ int main( int argc, char *argv[] )
} else if( rc == SSH_AUTH_DENIED ) {
auth_state[i] = CSSH_AUTH_PUBKEY_DONE_FAILED;
} else {
- cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
break;
@@ -568,7 +576,7 @@ int main( int argc, char *argv[] )
} else if( rc == SSH_AUTH_DENIED ) {
auth_state[i] = CSSH_AUTH_PASSWORD_DONE_FAILED;
} else {
- cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
break;
@@ -588,7 +596,7 @@ int main( int argc, char *argv[] )
case CSSH_AUTH_DONE_FAILED:
// one authentication failed, bail out for now
fprintf( stderr, "ERROR: authentication failed for one host ('%s'), aborting now", host[i] );
- cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
}
@@ -611,7 +619,7 @@ int main( int argc, char *argv[] )
memset( channel, 0, nof_sessions + 1 );
if( channel == NULL ) {
fprintf( stderr, "ERROR: Memory allocation failed for ssh_channels" );
- cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
for( unsigned int i = 0; i < nof_sessions; i++ ) {
@@ -619,7 +627,7 @@ int main( int argc, char *argv[] )
if( channel[i] == NULL ) {
fprintf( stderr, "ERROR: Unable to open SSH channel: %s\n",
ssh_get_error( session[i] ) );
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
}
@@ -627,7 +635,7 @@ int main( int argc, char *argv[] )
for( unsigned int i = 0; i < nof_sessions; i++ ) {
rc = ssh_channel_open_session( channel[i] );
if( rc != SSH_OK ) {
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
}
@@ -645,7 +653,7 @@ int main( int argc, char *argv[] )
}
if( cmd[0] == '\0' ) {
fprintf( stderr, "ERROR: Empty command, no interactive CLI supported currently\n" );
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
@@ -655,7 +663,7 @@ int main( int argc, char *argv[] )
if( rc != SSH_OK ) {
fprintf( stderr, "ERROR: Executing SSH command '%s' failed: %s\n",
cmd, ssh_get_error( session[i] ) );
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
}
@@ -693,12 +701,12 @@ int main( int argc, char *argv[] )
}
for( unsigned int i = 0; i < nof_sessions; i++ ) {
- char buffer[4096];
+ char buffer[BUFSIZE];
rc = ssh_channel_poll( channel[i], 0 );
if( rc == SSH_ERROR ) {
fprintf( stderr, "ERROR: ssh_channel_poll on stdout failed: %s\n",
ssh_get_error( session[i] ) );
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
@@ -707,7 +715,7 @@ int main( int argc, char *argv[] )
if( nread == SSH_ERROR ) {
fprintf( stderr, "ERROR: ssh_channel_read_nonblocking on stdout failed: %s\n",
ssh_get_error( session[i] ) );
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
@@ -720,14 +728,14 @@ int main( int argc, char *argv[] )
if( wrc < 0 ) {
fprintf( stderr, "ERROR: while writing to stdout: %s\n",
strerror( errno ) );
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
if( wrc != nread ) {
fprintf( stderr, "ERROR: Write mismatch on stdout (%zu != %d)\n",
wrc, nread );
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
}
@@ -737,7 +745,7 @@ int main( int argc, char *argv[] )
if( rc == SSH_ERROR ) {
fprintf( stderr, "ERROR: ssh_channel_poll on stderr failed: %s\n",
ssh_get_error( session[i] ) );
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
@@ -746,7 +754,7 @@ int main( int argc, char *argv[] )
if( nread == SSH_ERROR ) {
fprintf( stderr, "ERROR: ssh_channel_read_nonblocking on stderr failed: %s\n",
ssh_get_error( session[i] ) );
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
@@ -755,14 +763,14 @@ int main( int argc, char *argv[] )
if( wrc < 0 ) {
fprintf( stderr, "ERROR: while writting to stderr: %s\n",
strerror( errno ) );
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
if( wrc != nread ) {
fprintf( stderr, "ERROR: Write mismatch on stderr (%zu != %d)\n",
wrc, nread );
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
}
@@ -776,7 +784,7 @@ int main( int argc, char *argv[] )
cssh_msleep( 1 );
}
- cleanup_sessions( &session, &channel, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, &channel, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
} break;
@@ -787,15 +795,13 @@ int main( int argc, char *argv[] )
// TODO: host destination can be explicit (one without -H hosts
// parameter) or it is a placeholder host @HOST@:/dira/dirb
copy_direction_e copy_direction = CSSH_COPY_DIRECTION_DOWNLOAD;
- const char *local_directory = "x";
-
- fprintf( stderr, "Copy mode\n" );
+ const char *local_directory = "adir";
ssh_scp *scp = (ssh_scp *)malloc( ( nof_sessions + 1 ) * sizeof( ssh_scp ) );
memset( scp, 0, nof_sessions + 1 );
if( scp == NULL ) {
fprintf( stderr, "ERROR: Memory allocation failed for ssh_scp" );
- cleanup_sessions( &session, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, NULL, NULL, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
for( unsigned int i = 0; i < nof_sessions; i++ ) {
@@ -806,7 +812,7 @@ int main( int argc, char *argv[] )
if( scp[i] == NULL ) {
fprintf( stderr, "ERROR: Unable to open SCP channel: %s\n",
ssh_get_error( session[i] ) );
- cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, NULL, &scp, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
}
@@ -816,72 +822,135 @@ int main( int argc, char *argv[] )
if( rc != SSH_OK ) {
fprintf( stderr, "ERROR: Unable to initialize SCP sessions: %s\n",
ssh_get_error( session[i] ) );
- cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, NULL, &scp, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
}
}
- switch( copy_direction ) {
- case CSSH_COPY_DIRECTION_UPLOAD: {
- } break;
+ char **buf = (char **)malloc( ( nof_sessions + 1 ) * sizeof( char * ) );
+ if( buf == NULL ) {
+ fprintf( stderr, "ERROR: Memory allocation failed for receiving buffers of ssh_scp" );
+ cleanup_sessions( &session, NULL, &scp, NULL, host, port, nof_sessions, args_info.verbose_given > 0 );
+ exit( EXIT_FAILURE );
+ }
+ memset( buf, 0, ( nof_sessions + 1 ) * sizeof( char * ) );
+ for( unsigned int i = 0; i < nof_sessions; i++ ) {
+ buf[i] = (char *)malloc( BUFSIZE );
+ if( buf[i] == NULL ) {
+ fprintf( stderr, "ERROR: Memory allocation failed for receiving buffers of ssh_scp" );
+ cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ bool all_eof = false;
+ bool *is_eof = (bool *)malloc( ( nof_sessions + 1 ) * sizeof( bool ) );
+ if( is_eof == NULL ) {
+ fprintf( stderr, "ERROR: Memory allocation failed for 'is_eof'" );
+ return -1;
+ }
+ memset( is_eof, false, ( nof_sessions + 1 ) * sizeof( bool ) );
+
+ while( !all_eof ) {
- case CSSH_COPY_DIRECTION_DOWNLOAD: {
- // TODO: we receive files from N hosts, so we should
- // have a method to create separate directories or
- // files, e.g. file.host1, file.host2, etc.
- // or dirs host1/file host2/file. Maybe this depends
- // also on whether we specify recursive mode or not
- for( unsigned int i = 0; i < nof_sessions; i++ ) {
- rc = ssh_scp_pull_request( scp[i] );
- switch( rc ) {
- case SSH_SCP_REQUEST_NEWDIR:
- // TODO: mkdir in host[i] workspace
- break;
-
- case SSH_SCP_REQUEST_ENDDIR:
- // TODO: leave current workspace
- break;
-
- case SSH_SCP_REQUEST_NEWFILE: {
- const char *filename = ssh_scp_request_get_filename( scp[i] );
- int mode = ssh_scp_request_get_permissions( scp[i] );
- uint64_t size = ssh_scp_request_get_size64( scp[i] );
- fprintf( stderr, "Receiving file '%s' with permissions '0%o' of size '%"PRIu64"'\n",
- filename, mode, size );
- rc = ssh_scp_accept_request( scp[i] );
- if( rc != SSH_OK ) {
- fprintf( stderr, "ERROR: accepting request for file '%s' failed: %s\n",
- filename, ssh_get_error( scp[i] ) );
- cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 );
+ switch( copy_direction ) {
+ case CSSH_COPY_DIRECTION_UPLOAD: {
+ } break;
+
+ case CSSH_COPY_DIRECTION_DOWNLOAD: {
+ // TODO: we receive files from N hosts, so we should
+ // have a method to create separate directories or
+ // files, e.g. file.host1, file.host2, etc.
+ // or dirs host1/file host2/file. Maybe this depends
+ // also on whether we specify recursive mode or not
+ for( unsigned int i = 0; i < nof_sessions; i++ ) {
+ rc = ssh_scp_pull_request( scp[i] );
+ switch( rc ) {
+ case SSH_SCP_REQUEST_NEWDIR: {
+ // TODO: mkdir in host[i] workspace
+ const char *filename = ssh_scp_request_get_filename( scp[i] );
+ int mode = ssh_scp_request_get_permissions( scp[i] );
+ fprintf( stderr, "Receiving directory '%s' with permissions '0%o'\n",
+ filename, mode );
+ rc = ssh_scp_accept_request( scp[i] );
+ if( rc != SSH_OK ) {
+ fprintf( stderr, "ERROR: accepting request for directory '%s' failed: %s\n",
+ filename, ssh_get_error( session[i] ) );
+ cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 );
+ exit( EXIT_FAILURE );
+ }
+ } break;
+
+ case SSH_SCP_REQUEST_ENDDIR:
+ // TODO: leave current workspace
+ // TODO: we need a stack per client connection
+ // to remember the current directory we are in
+ break;
+
+ case SSH_SCP_REQUEST_NEWFILE: {
+ const char *filename = ssh_scp_request_get_filename( scp[i] );
+ int mode = ssh_scp_request_get_permissions( scp[i] );
+ uint64_t size = ssh_scp_request_get_size64( scp[i] );
+ fprintf( stderr, "Receiving file '%s' with permissions '0%o' of size '%"PRIu64"'\n",
+ filename, mode, size );
+ rc = ssh_scp_accept_request( scp[i] );
+ if( rc != SSH_OK ) {
+ fprintf( stderr, "ERROR: accepting request for file '%s' failed: %s\n",
+ filename, ssh_get_error( session[i] ) );
+ cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 );
+ exit( EXIT_FAILURE );
+ }
+ // TODO: we receive a buffer of data < 65k, so we must introduce a
+ // state machine here so we know we still have to read or initiate the accept,
+ // this is also quite handy as we can show some progress bar here or some ETA
+ // TODO: write buffer to received file in workdir of host[i]
+ // TODO: slurping it all for one connection is not fair to other connections,
+ // we should read a buffer and then check the states of the other and hit the
+ // loop again, this needs a state machine..
+ uint64_t bytesReceived = 0;
+ while( bytesReceived < size ) {
+ rc = ssh_scp_read( scp[i], buf[i], BUFSIZE );
+ if( rc == SSH_ERROR ) {
+ fprintf( stderr, "ERROR: reading data for file '%s' failed: %s\n",
+ filename, ssh_get_error( session[i] ) );
+ cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 );
+ exit( EXIT_FAILURE );
+ }
+ bytesReceived += rc;
+ }
+ } break;
+
+ case SSH_SCP_REQUEST_EOF:
+ is_eof[i] = true;
+ all_eof = true;
+ for( unsigned int i = 0; i < nof_sessions; i++ ) {
+ if( !is_eof[i] ) {
+ all_eof = false;
+ break;
+ }
+ }
+ break;
+
+ case SSH_ERROR:
+ fprintf( stderr, "ERROR: error from remote host '%s': %s\n",
+ host[i], ssh_get_error( session[i] ) );
+ cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 );
exit( EXIT_FAILURE );
- }
- // TODO: receive file in workdir of host[i]
- } break;
-
- case SSH_SCP_REQUEST_EOF:
- // TODO: keep array of eof flags and terminate
- // loop if all hosts have terminated
- break;
-
- case SSH_ERROR:
- fprintf( stderr, "ERROR: error from remote host '%s': %s\n",
- host[i], ssh_get_error( session[i] ) );
- cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 );
- exit( EXIT_FAILURE );
- break;
+ break;
- case SSH_SCP_REQUEST_WARNING:
- fprintf( stderr, "WARNING: remote host '%s': %s\n",
- host[i], ssh_scp_request_get_warning( scp[i] ) );
- cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 );
- exit( EXIT_FAILURE );
- break;
+ case SSH_SCP_REQUEST_WARNING:
+ fprintf( stderr, "WARNING: remote host '%s': %s\n",
+ host[i], ssh_scp_request_get_warning( scp[i] ) );
+ cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 );
+ exit( EXIT_FAILURE );
+ break;
+ }
}
- }
- } break;
+ } break;
+ }
}
- cleanup_sessions( &session, NULL, &scp, host, port, nof_sessions, args_info.verbose_given > 0 );
+ cleanup_sessions( &session, NULL, &scp, &buf, host, port, nof_sessions, args_info.verbose_given > 0 );
} break;