async_ssh/session/mod.rs
1use tokio_io::{AsyncRead, AsyncWrite};
2use std::sync::Arc;
3use std::rc::Rc;
4use std::cell::RefCell;
5use futures::Future;
6use tokio_core::reactor::Handle;
7use channel;
8use {Connection, SharableConnection};
9use thrussh;
10use thrussh_keys;
11
12pub(crate) mod state;
13
14/// A newly established, unauthenticated SSH session.
15///
16/// All you can really do with this in authenticate it using one of the `authenticate_*` methods.
17/// You'll most likely want [`NewSession::authenticate_key`].
18pub struct NewSession<S: AsyncRead + AsyncWrite> {
19 c: Connection<S>,
20 handle: Handle,
21}
22
23impl<S: AsyncRead + AsyncWrite + 'static> NewSession<S> {
24 /// Authenticate as the given user using the given keypair.
25 ///
26 /// See also
27 /// [`thrussh::client::Connection::authenticate_key`](https://docs.rs/thrussh/0.19/thrussh/client/struct.Connection.html#method.authenticate_key).
28 pub fn authenticate_key(
29 self,
30 user: &str,
31 key: thrussh_keys::key::KeyPair,
32 ) -> Box<Future<Item = Session<S>, Error = thrussh::HandlerError<()>>>
33 where
34 S: thrussh::Tcp,
35 {
36 let NewSession { c, handle } = self;
37 Box::new(
38 c.c
39 .authenticate_key(user, key)
40 .map(move |c| Session::make(Connection { c, task: None }, handle)),
41 )
42 }
43}
44
45/// An established and authenticated SSH session.
46///
47/// You can use this session to execute commands on the remote host using [`Session::open_exec`].
48/// This will give you back a [`Channel`], which can be used to read from the resulting process'
49/// `STDOUT`, or to write the the process' `STDIN`.
50pub struct Session<S: AsyncRead + AsyncWrite>(SharableConnection<S>);
51
52impl<S: AsyncRead + AsyncWrite + thrussh::Tcp + 'static> Session<S> {
53 /// Establish a new SSH session on top of the given stream.
54 ///
55 /// The resulting SSH session is initially unauthenticated (see [`NewSession`]), and must be
56 /// authenticated before it becomes useful.
57 ///
58 /// Note that the reactor behind the given `handle` *must* continue to be driven for any
59 /// channels created from this [`Session`] to work.
60 pub fn new(stream: S, handle: &Handle) -> Result<NewSession<S>, thrussh::HandlerError<()>> {
61 thrussh::client::Connection::new(Arc::default(), stream, state::Ref::default(), None)
62 .map(|c| NewSession {
63 c: Connection { c, task: None },
64 handle: handle.clone(),
65 })
66 .map_err(thrussh::HandlerError::Error)
67 }
68
69 fn make(c: Connection<S>, handle: Handle) -> Self {
70 let c = SharableConnection(Rc::new(RefCell::new(c)));
71 handle.spawn(c.clone());
72 Session(c)
73 }
74
75 /// Retrieve the last error encountered during this session.
76 ///
77 /// Note that it is unlikely you will be able to use any items associated with this session
78 /// once it has returned an error.
79 ///
80 /// Calling this method clears the error.
81 pub fn last_error(&mut self) -> Option<thrussh::HandlerError<()>> {
82 let connection = (self.0).0.borrow();
83 let handler = connection.c.handler();
84 let mut state = handler.borrow_mut();
85 state.errored_with.take()
86 }
87
88 /// Establish a new channel over this session to execute the given command.
89 ///
90 /// Note that any errors encountered while operating on the channel after it has been opened
91 /// will manifest only as reads or writes no longer succeeding. To get the underlying error,
92 /// call [`Session::last_error`].
93 pub fn open_exec<'a>(&mut self, cmd: &'a str) -> channel::ChannelOpenFuture<'a, S> {
94 let mut session = (self.0).0.borrow_mut();
95 let state = session.c.handler().clone();
96
97 let channel_id = (&mut *session.c)
98 .channel_open_session()
99 .expect("sessions are always authenticated");
100 state
101 .borrow_mut()
102 .state_for
103 .insert(channel_id, channel::State::default());
104 channel::ChannelOpenFuture::new(cmd, self.0.clone(), state, channel_id)
105 }
106}