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