ssh_wrap/
wezterm_ssh.rs

1use anyhow::bail;
2use async_compat::CompatExt;
3use std::string::ToString;
4use wezterm_ssh::{Config, ConfigMap, Session, SessionEvent};
5
6pub struct SessionBuilder {
7    pub user: String,
8    pub host: String,
9    pub pass: String,
10    pub port: u16,
11    pub identities_only: Option<bool>,
12    pub userknown_hosts_file: Option<String>,
13    pub wezterm_ssh_verbose: Option<bool>,
14    pub wezterm_ssh_backend: SshBackend,
15}
16
17#[derive(Display, Debug, Default)]
18pub enum SshBackend {
19    #[strum(serialize = "libssh")]
20    Libssh,
21    #[strum(serialize = "ssh2")]
22    #[default]
23    Ssh2,
24}
25
26impl SessionBuilder {
27    pub fn new_with_pass<U, H, P>(user: U, host: H, pass: P, port: u16) -> Self
28    where
29        U: AsRef<str>,
30        H: AsRef<str>,
31        P: AsRef<str>,
32    {
33        Self {
34            user: user.as_ref().to_string(),
35            host: host.as_ref().to_string(),
36            pass: pass.as_ref().to_string(),
37            port,
38            identities_only: None,
39            userknown_hosts_file: None,
40            wezterm_ssh_verbose: None,
41            wezterm_ssh_backend: Default::default(),
42        }
43    }
44
45    fn identities_only(&self) -> &str {
46        if let Some(i) = self.identities_only {
47            if i {
48                return "yes";
49            }
50        }
51        "no"
52    }
53
54    pub fn disable_userknown_hosts_file(&mut self) -> &mut Self {
55        self.userknown_hosts_file = Some("/dev/null".to_string());
56        self
57    }
58
59    fn wezterm_ssh_verbose(&self) -> &str {
60        if let Some(i) = self.wezterm_ssh_verbose {
61            if i {
62                return "true";
63            }
64        }
65        "false"
66    }
67
68    fn configmap(&self) -> ConfigMap {
69        let config = Config::new();
70        let mut config = config.for_host(&self.host);
71        config.insert("port".to_string(), self.port.to_string());
72        config.insert("user".to_string(), self.user.to_string());
73        config.insert(
74            "identitiesonly".to_string(),
75            self.identities_only().to_string(),
76        );
77        config.insert(
78            "wezterm_ssh_verbose".to_string(),
79            self.wezterm_ssh_verbose().to_string(),
80        );
81        config.insert(
82            "wezterm_ssh_backend".to_string(),
83            self.wezterm_ssh_backend.to_string(),
84        );
85
86        if let Some(path) = &self.userknown_hosts_file {
87            config.insert("userknownhostsfile".to_string(), path.to_string());
88        }
89
90        config
91    }
92
93    pub async fn connect_with_pass(&self) -> anyhow::Result<Session> {
94        let (session, events) = Session::connect(self.configmap())?;
95        while let Ok(event) = events.recv().await {
96            match event {
97                SessionEvent::Banner(banner) => {
98                    info!("SessionEvent Banner:{:?}", banner);
99                }
100                SessionEvent::HostVerify(verify) => {
101                    info!("SessionEvent HostVerify:{:?}", verify);
102                    verify.answer(true).compat().await?;
103                }
104                SessionEvent::Authenticate(auth) => {
105                    info!("SessionEvent Authenticate:{:?}", auth);
106                    auth.answer(vec![self.pass.to_string()]).compat().await?;
107                }
108                SessionEvent::Error(err) => {
109                    error!("SessionEvent Error:{:?}", err);
110                    bail!(err)
111                }
112                SessionEvent::Authenticated => {
113                    info!("SessionEvent Authenticated");
114                    break;
115                }
116            }
117        }
118        Ok(session)
119    }
120}