sunset_async/
client.rs

1use embedded_io_async::{Read, Write};
2
3use sunset::*;
4
5use crate::*;
6use async_channel::{ChanIn, ChanInOut};
7use async_sunset::{AsyncSunset, ProgressHolder};
8use sunset::ChanData;
9
10/// An async SSH client instance
11///
12/// The [`run()`][Self::run] method runs the session to completion. [`progress()`][Self::progress]
13/// must be polled, and responses given to the events provided.
14///
15/// Once authentication has completed (`progress()` returns [`CliEvent::Authenticated`]), the application
16/// may open remote channels with [`open_session_pty()`][Self::open_session_pty] etc.
17///
18/// This is async executor agnostic.
19pub struct SSHClient<'a> {
20    sunset: AsyncSunset<'a, sunset::Client>,
21}
22
23impl<'a> SSHClient<'a> {
24    pub fn new(inbuf: &'a mut [u8], outbuf: &'a mut [u8]) -> Self {
25        let runner = Runner::new_client(inbuf, outbuf);
26        let sunset = AsyncSunset::new(runner);
27        Self { sunset }
28    }
29
30    /// Runs the session to completion.
31    ///
32    /// `rsock` and `wsock` are the SSH network channel (TCP port 22 or equivalent).
33    pub async fn run(
34        &self,
35        rsock: &mut impl Read,
36        wsock: &mut impl Write,
37    ) -> Result<()> {
38        self.sunset.run(rsock, wsock).await
39    }
40
41    /// Returns an event from the SSH session.
42    ///
43    /// Note that on return `ProgressHolder` holds a mutex over the session,
44    /// so other calls to `SSHClient` may block until the `ProgressHolder`
45    /// is dropped.
46    pub async fn progress<'g, 'f>(
47        &'g self,
48        ph: &'f mut ProgressHolder<'g, 'a, sunset::Client>,
49    ) -> Result<CliEvent<'f, 'a>> {
50        match self.sunset.progress(ph).await? {
51            Event::Cli(x) => Ok(x),
52            Event::None => return Ok(CliEvent::PollAgain),
53            Event::Progressed => Ok(CliEvent::PollAgain),
54            _ => Err(Error::bug()),
55        }
56    }
57
58    pub async fn open_session_nopty(&self) -> Result<(ChanInOut<'_>, ChanIn<'_>)> {
59        let chan =
60            self.sunset.with_runner(|runner| runner.open_client_session()).await?;
61
62        let num = chan.num();
63        self.sunset.add_channel(chan, 2).await?;
64
65        let cstd = ChanInOut::new(num, ChanData::Normal, &self.sunset);
66        let cerr = ChanIn::new(num, ChanData::Stderr, &self.sunset);
67        Ok((cstd, cerr))
68    }
69
70    pub async fn open_session_pty(&self) -> Result<ChanInOut<'_>> {
71        let chan =
72            self.sunset.with_runner(|runner| runner.open_client_session()).await?;
73
74        let num = chan.num();
75        self.sunset.add_channel(chan, 1).await?;
76        let cstd = ChanInOut::new(num, ChanData::Normal, &self.sunset);
77        Ok(cstd)
78    }
79}