async_ssh/lib.rs
1//! High-level library for asynchronous SSH connections.
2//!
3//! This crate presents a higher-level asynchronous interface for issuing commands over SSH
4//! connections. It's built on top of [thrussh](https://pijul.org/thrussh/), a pure-Rust
5//! implementation of the SSH protocol.
6//!
7//! At its core, the crate provides the notion of an SSH [`Session`], which can have zero or more
8//! [`Channel`]s. Each [`Channel`] executes some user-defined command on the remote machine, and
9//! implements [`AsyncRead`](https://docs.rs/tokio-io/0.1/tokio_io/trait.AsyncRead.html) and
10//! (eventually) [`AsyncWrite`](https://docs.rs/tokio-io/0.1/tokio_io/trait.AsyncWrite.html) to
11//! allow reading from and writing to the remote process respectively. For those unfamiliar with
12//! asynchronous I/O, you'll likely want to start with the [functions in
13//! `tokio-io::io`](https://docs.rs/tokio-io/0.1/tokio_io/io/index.html#functions).
14//!
15//! The code is currently in a pre-alpha stage, with only a subset of the core features
16//! implemented, and with fairly gaping API holes like `thrussh` types being exposed all over
17//! the place or error types not being nice to work with.
18//!
19//! # Examples
20//!
21//! ```no_run
22//! # extern crate tokio_core;
23//! # extern crate async_ssh;
24//! # extern crate thrussh_keys;
25//! # extern crate thrussh;
26//! # extern crate tokio_io;
27//! # extern crate futures;
28//! # use async_ssh::*;
29//! # use futures::Future;
30//! # fn main() {
31//! let key = thrussh_keys::load_secret_key("/path/to/key", None).unwrap();
32//!
33//! let mut core = tokio_core::reactor::Core::new().unwrap();
34//! let handle = core.handle();
35//! let ls_out = tokio_core::net::TcpStream::connect(&"127.0.0.1:22".parse().unwrap(), &handle)
36//! .map_err(thrussh::Error::IO)
37//! .map_err(thrussh::HandlerError::Error)
38//! .and_then(|c| Session::new(c, &handle))
39//! .and_then(|session| session.authenticate_key("username", key))
40//! .and_then(|mut session| session.open_exec("ls -la"));
41//!
42//! let channel = core.run(ls_out).unwrap();
43//! let (channel, data) = core.run(tokio_io::io::read_to_end(channel, Vec::new())).unwrap();
44//! let status = core.run(channel.exit_status()).unwrap();
45//!
46//! println!("{}", ::std::str::from_utf8(&data[..]).unwrap());
47//! println!("exited with: {}", status);
48//! # }
49//! ```
50#![deny(missing_docs)]
51
52extern crate futures;
53extern crate thrussh;
54extern crate thrussh_keys;
55extern crate tokio_core;
56extern crate tokio_io;
57
58use tokio_io::{AsyncRead, AsyncWrite};
59use std::rc::Rc;
60use std::cell::RefCell;
61use futures::Future;
62
63mod session;
64mod channel;
65
66pub use channel::{Channel, ChannelOpenFuture, ExitStatusFuture};
67pub use session::{NewSession, Session};
68
69struct Connection<S: AsyncRead + AsyncWrite> {
70 c: thrussh::client::Connection<S, session::state::Ref>,
71 task: Option<futures::task::Task>,
72}
73
74struct SharableConnection<S: AsyncRead + AsyncWrite>(Rc<RefCell<Connection<S>>>);
75impl<S> Clone for SharableConnection<S>
76where
77 S: AsyncRead + AsyncWrite,
78{
79 fn clone(&self) -> Self {
80 SharableConnection(self.0.clone())
81 }
82}
83
84impl<S: AsyncRead + AsyncWrite + thrussh::Tcp> Future for SharableConnection<S> {
85 type Item = ();
86 type Error = ();
87
88 fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
89 // NOTE: SessionStateRef as Handler cannot use Rc<RefMut<C<S>>>
90 let mut c = self.0.borrow_mut();
91 c.task = Some(futures::task::current());
92 match c.c.poll() {
93 Ok(r) => Ok(r),
94 Err(e) => {
95 let state = self.0.borrow();
96 let state = state.c.handler();
97 let mut state = state.borrow_mut();
98 state.errored_with = Some(e);
99 Err(())
100 }
101 }
102 }
103}