openssh/native_mux_impl/
session.rs

1use super::{Command, Error};
2
3use std::ffi::OsStr;
4use std::os::unix::ffi::OsStrExt;
5use std::path::Path;
6
7use openssh_mux_client::{shutdown_mux_master, Connection};
8use tempfile::TempDir;
9
10#[derive(Debug)]
11pub(crate) struct Session {
12    /// TempDir will automatically removes the temporary dir on drop
13    tempdir: Option<TempDir>,
14    ctl: Box<Path>,
15}
16
17impl Session {
18    pub(crate) fn new(dir: TempDir) -> Self {
19        let ctl = dir.path().join("master").into_boxed_path();
20
21        Self {
22            tempdir: Some(dir),
23            ctl,
24        }
25    }
26
27    pub(crate) fn resume(ctl: Box<Path>, _master_log: Option<Box<Path>>) -> Self {
28        Self { tempdir: None, ctl }
29    }
30
31    pub(crate) async fn check(&self) -> Result<(), Error> {
32        Connection::connect(&self.ctl)
33            .await?
34            .send_alive_check()
35            .await?;
36
37        Ok(())
38    }
39
40    pub(crate) fn ctl(&self) -> &Path {
41        &self.ctl
42    }
43
44    pub(crate) fn raw_command<S: AsRef<OsStr>>(&self, program: S) -> Command {
45        Command::new(self.ctl.clone(), program.as_ref().as_bytes().into(), false)
46    }
47
48    pub(crate) fn subsystem<S: AsRef<OsStr>>(&self, program: S) -> Command {
49        Command::new(self.ctl.clone(), program.as_ref().as_bytes().into(), true)
50    }
51
52    pub(crate) async fn request_port_forward(
53        &self,
54        forward_type: crate::ForwardType,
55        listen_socket: crate::Socket<'_>,
56        connect_socket: crate::Socket<'_>,
57    ) -> Result<(), Error> {
58        Connection::connect(&self.ctl)
59            .await?
60            .request_port_forward(
61                forward_type.into(),
62                &listen_socket.into(),
63                &connect_socket.into(),
64            )
65            .await?;
66
67        Ok(())
68    }
69
70    pub(crate) async fn close_port_forward(
71        &self,
72        forward_type: crate::ForwardType,
73        listen_socket: crate::Socket<'_>,
74        connect_socket: crate::Socket<'_>,
75    ) -> Result<(), Error> {
76        Connection::connect(&self.ctl)
77            .await?
78            .close_port_forward(
79                forward_type.into(),
80                &listen_socket.into(),
81                &connect_socket.into(),
82            )
83            .await?;
84
85        Ok(())
86    }
87
88    async fn close_impl(&self) -> Result<(), Error> {
89        Connection::connect(&self.ctl)
90            .await?
91            .request_stop_listening()
92            .await?;
93
94        Ok(())
95    }
96
97    pub(crate) async fn close(mut self) -> Result<Option<TempDir>, Error> {
98        // Take self.tempdir so that drop would do nothing
99        let tempdir = self.tempdir.take();
100
101        self.close_impl().await?;
102
103        Ok(tempdir)
104    }
105
106    pub(crate) fn detach(mut self) -> (Box<Path>, Option<Box<Path>>) {
107        (
108            self.ctl.clone(),
109            self.tempdir.take().map(TempDir::into_path).map(|mut path| {
110                path.push("log");
111                path.into_boxed_path()
112            }),
113        )
114    }
115}
116
117impl Drop for Session {
118    fn drop(&mut self) {
119        // Keep tempdir alive until the shutdown request is sent
120        let _tempdir = match self.tempdir.take() {
121            Some(tempdir) => tempdir,
122            // return since close must have already been called.
123            None => return,
124        };
125
126        let _res = shutdown_mux_master(&self.ctl);
127        #[cfg(feature = "tracing")]
128        if let Err(err) = _res {
129            tracing::error!("Closing ssh session failed: {}", err);
130        }
131    }
132}