makiko 0.2.5

Asynchronous SSH client library in pure Rust
Documentation
// Tutorial chapter 5: Execute a command
//
// https://honzasp.github.io/makiko/tutorial/5-execute-command.html
//
// You can run the example with
//
//     cargo run --example tutorial_5
//

#[tokio::main]
async fn main() {
    // Connect to the SSH server.
    let socket = tokio::net::TcpStream::connect(("localhost", 2222)).await
        .expect("Could not open a TCP socket");

    // Use the default secure configuration of the SSH client.
    let config = makiko::ClientConfig::default();
    //let config = makiko::ClientConfig::default_compatible_less_secure();

    // Create the SSH client.
    let (client, mut client_rx, client_fut) = makiko::Client::open(socket, config)
        .expect("Could not open client");

    // Spawn a Tokio task that polls the client.
    tokio::task::spawn(async move {
        client_fut.await.expect("Error in client future");
    });

    // Spawn another Tokio task to handle the client events.
    tokio::task::spawn(async move {
        loop {
            // Wait for the next event.
            let event = client_rx.recv().await
                .expect("Error while receiving client event");

            // Exit the loop when the client has closed.
            let Some(event) = event else {
                break
            };

            match event {
                // Handle the server public key: for now, we just accept all keys, but this makes
                // us susceptible to man-in-the-middle attacks!
                makiko::ClientEvent::ServerPubkey(pubkey, accept) => {
                    println!("Server pubkey type {}, fingerprint {}", pubkey.type_str(), pubkey.fingerprint());
                    accept.accept();
                },

                // All other events can be safely ignored
                _ => {},
            }
        }
    });

    // Try to authenticate using a password.
    let auth_res = client.auth_password("alice".into(), "alicealice".into()).await
        .expect("Error when trying to authenticate");

    // Deal with all possible outcomes of password authentication.
    match auth_res {
        makiko::AuthPasswordResult::Success => {
            println!("We have successfully authenticated using a password");
        },
        makiko::AuthPasswordResult::ChangePassword(prompt) => {
            panic!("The server asks us to change password: {:?}", prompt);
        },
        makiko::AuthPasswordResult::Failure(failure) => {
            panic!("The server rejected authentication: {:?}", failure);
        }
    }

    // Open a session on the server.
    let channel_config = makiko::ChannelConfig::default();
    let (session, mut session_rx) = client.open_session(channel_config).await
        .expect("Could not open a session");

    // Handle session events asynchronously in a Tokio task.
    let session_event_task = tokio::task::spawn(async move {
        loop {
            // Wait for the next event.
            let event = session_rx.recv().await
                .expect("Error while receiving session event");

            // Exit the loop when the session has closed.
            let Some(event) = event else {
                break
            };

            match event {
                // Handle stdout/stderr output from the process.
                makiko::SessionEvent::StdoutData(data) => {
                    println!("Process produced stdout: {:?}", data);
                },
                makiko::SessionEvent::StderrData(data) => {
                    println!("Process produced stderr: {:?}", data);
                },

                // Handle exit of the process.
                makiko::SessionEvent::ExitStatus(status) => {
                    println!("Process exited with status {}", status);
                },
                makiko::SessionEvent::ExitSignal(signal) => {
                    println!("Process exited with signal {:?}: {:?}", signal.signal_name, signal.message);
                },

                // Ignore other events
                _ => {},
            }
        }
    });

    // Execute a command on the session
    session.exec("sed s/blue/green/".as_bytes())
        .expect("Could not execute a command in the session")
        .wait().await
        .expect("Server returned an error when we tried to execute a command in the session");

    // Send some data to the standard input of the process
    session.send_stdin("blueberry jam\n".into()).await.unwrap();
    session.send_stdin("blue jeans\nsky blue".into()).await.unwrap();
    session.send_eof().await.unwrap();

    // Wait for the task that handles session events
    session_event_task.await.unwrap();
}