113 lines
3.2 KiB
Rust
113 lines
3.2 KiB
Rust
use async_trait::async_trait;
|
|
use log::{error, info, LevelFilter};
|
|
use russh::*;
|
|
use russh_keys::*;
|
|
use russh_sftp::{client::SftpSession, protocol::OpenFlags};
|
|
use std::sync::Arc;
|
|
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
|
|
|
|
struct Client;
|
|
|
|
#[async_trait]
|
|
impl client::Handler for Client {
|
|
type Error = anyhow::Error;
|
|
|
|
async fn check_server_key(
|
|
&mut self,
|
|
server_public_key: &key::PublicKey,
|
|
) -> Result<bool, Self::Error> {
|
|
info!("check_server_key: {:?}", server_public_key);
|
|
Ok(true)
|
|
}
|
|
|
|
async fn data(
|
|
&mut self,
|
|
channel: ChannelId,
|
|
data: &[u8],
|
|
_session: &mut client::Session,
|
|
) -> Result<(), Self::Error> {
|
|
info!("data on channel {:?}: {}", channel, data.len());
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
env_logger::builder()
|
|
.filter_level(LevelFilter::Debug)
|
|
.init();
|
|
|
|
let config = russh::client::Config::default();
|
|
let sh = Client {};
|
|
let mut session = russh::client::connect(Arc::new(config), ("localhost", 22), sh)
|
|
.await
|
|
.unwrap();
|
|
if session
|
|
.authenticate_password("root", "password")
|
|
.await
|
|
.unwrap()
|
|
{
|
|
let channel = session.channel_open_session().await.unwrap();
|
|
channel.request_subsystem(true, "sftp").await.unwrap();
|
|
let sftp = SftpSession::new(channel.into_stream()).await.unwrap();
|
|
info!("current path: {:?}", sftp.canonicalize(".").await.unwrap());
|
|
|
|
// create dir and symlink
|
|
let path = "./some_kind_of_dir";
|
|
let symlink = "./symlink";
|
|
|
|
sftp.create_dir(path).await.unwrap();
|
|
sftp.symlink(path, symlink).await.unwrap();
|
|
|
|
info!("dir info: {:?}", sftp.metadata(path).await.unwrap());
|
|
info!(
|
|
"symlink info: {:?}",
|
|
sftp.symlink_metadata(path).await.unwrap()
|
|
);
|
|
|
|
// scanning directory
|
|
for entry in sftp.read_dir(".").await.unwrap() {
|
|
info!("file in directory: {:?}", entry.file_name());
|
|
}
|
|
|
|
sftp.remove_file(symlink).await.unwrap();
|
|
sftp.remove_dir(path).await.unwrap();
|
|
|
|
// interaction with i/o
|
|
let filename = "test_new.txt";
|
|
let mut file = sftp
|
|
.open_with_flags(
|
|
filename,
|
|
OpenFlags::CREATE | OpenFlags::TRUNCATE | OpenFlags::WRITE | OpenFlags::READ,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
info!("metadata by handle: {:?}", file.metadata().await.unwrap());
|
|
|
|
file.write_all(b"magic text").await.unwrap();
|
|
info!("flush: {:?}", file.flush().await); // or file.sync_all()
|
|
info!(
|
|
"current cursor position: {:?}",
|
|
file.stream_position().await
|
|
);
|
|
|
|
let mut str = String::new();
|
|
|
|
file.rewind().await.unwrap();
|
|
file.read_to_string(&mut str).await.unwrap();
|
|
file.rewind().await.unwrap();
|
|
|
|
info!(
|
|
"our magical contents: {}, after rewind: {:?}",
|
|
str,
|
|
file.stream_position().await
|
|
);
|
|
|
|
file.shutdown().await.unwrap();
|
|
sftp.remove_file(filename).await.unwrap();
|
|
|
|
// should fail because handle was closed
|
|
error!("should fail: {:?}", file.read_u8().await);
|
|
}
|
|
}
|