diff --git a/mux/src/ssh.rs b/mux/src/ssh.rs index da1c1296e..397c16de4 100644 --- a/mux/src/ssh.rs +++ b/mux/src/ssh.rs @@ -7,6 +7,7 @@ use crate::window::WindowId; use crate::Mux; use anyhow::{anyhow, bail, Context, Error}; use async_trait::async_trait; +use config::{SshBackend, SshDomain}; use filedescriptor::{poll, pollfd, socketpair, AsRawSocketDescriptor, FileDescriptor, POLLIN}; use portable_pty::cmdbuilder::CommandBuilder; use portable_pty::{ChildKiller, ExitStatus, MasterPty, PtySize}; @@ -134,11 +135,58 @@ pub fn ssh_connect_with_ui( /// interactive setup. The bulk of that is driven by `connect_ssh_session`. pub struct RemoteSshDomain { session: RefCell>, - ssh_config: ConfigMap, + config: RemoteSshConfig, id: DomainId, name: String, } +enum RemoteSshConfig { + AdHoc(ConfigMap), + Domain(SshDomain), +} + +pub fn ssh_domain_to_ssh_config(ssh_dom: &SshDomain) -> anyhow::Result { + let mut ssh_config = wezterm_ssh::Config::new(); + ssh_config.add_default_config_files(); + + let (remote_host_name, port) = { + let parts: Vec<&str> = ssh_dom.remote_address.split(':').collect(); + + if parts.len() == 2 { + (parts[0], Some(parts[1].parse::()?)) + } else { + (ssh_dom.remote_address.as_str(), None) + } + }; + + let mut ssh_config = ssh_config.for_host(&remote_host_name); + ssh_config.insert( + "wezterm_ssh_backend".to_string(), + match ssh_dom + .ssh_backend + .unwrap_or_else(|| config::configuration().ssh_backend) + { + SshBackend::Ssh2 => "ssh2", + SshBackend::LibSsh => "libssh", + } + .to_string(), + ); + for (k, v) in &ssh_dom.ssh_option { + ssh_config.insert(k.to_string(), v.to_string()); + } + + if let Some(username) = &ssh_dom.username { + ssh_config.insert("user".to_string(), username.to_string()); + } + if let Some(port) = port { + ssh_config.insert("port".to_string(), port.to_string()); + } + if ssh_dom.no_agent_auth { + ssh_config.insert("identitiesonly".to_string(), "yes".to_string()); + } + Ok(ssh_config) +} + impl RemoteSshDomain { pub fn with_ssh_config(name: &str, ssh_config: ConfigMap) -> anyhow::Result { let id = alloc_domain_id(); @@ -146,9 +194,26 @@ impl RemoteSshDomain { id, name: format!("SSH to {}", name), session: RefCell::new(None), - ssh_config, + config: RemoteSshConfig::AdHoc(ssh_config), }) } + + pub fn with_ssh_domain(dom: &SshDomain) -> anyhow::Result { + let id = alloc_domain_id(); + Ok(Self { + id, + name: dom.name.clone(), + session: RefCell::new(None), + config: RemoteSshConfig::Domain(dom.clone()), + }) + } + + pub fn ssh_config(&self) -> anyhow::Result { + match &self.config { + RemoteSshConfig::AdHoc(config) => Ok(config.clone()), + RemoteSshConfig::Domain(dom) => ssh_domain_to_ssh_config(dom), + } + } } /// Carry out the authentication process and create the initial pty. @@ -475,7 +540,7 @@ impl Domain for RemoteSshDomain { writer = Box::new(pty.try_clone_writer()?); } else { // We're starting the session - let (session, events) = Session::connect(self.ssh_config.clone())?; + let (session, events) = Session::connect(self.ssh_config()?)?; self.session.borrow_mut().replace(session.clone()); // We get to establish the session! @@ -682,11 +747,10 @@ impl Domain for RemoteSshDomain { } fn state(&self) -> DomainState { - if self.session.borrow().is_some() { - DomainState::Attached - } else { - DomainState::Detached - } + // Just pretend that we are always attached, as we don't + // have a defined attach operation that is distinct from + // a spawn. + DomainState::Attached } } diff --git a/wezterm-client/src/client.rs b/wezterm-client/src/client.rs index bb6b23a0f..436e15ea8 100644 --- a/wezterm-client/src/client.rs +++ b/wezterm-client/src/client.rs @@ -5,7 +5,7 @@ use anyhow::{anyhow, bail, Context}; use async_ossl::AsyncSslStream; use async_trait::async_trait; use codec::*; -use config::{configuration, SshBackend, SshDomain, TlsDomainClient, UnixDomain, UnixTarget}; +use config::{configuration, SshDomain, TlsDomainClient, UnixDomain, UnixTarget}; use filedescriptor::FileDescriptor; use futures::FutureExt; use mux::connui::ConnectionUI; @@ -502,44 +502,7 @@ impl Reconnectable { initial: bool, ui: &mut ConnectionUI, ) -> anyhow::Result<()> { - let mut ssh_config = wezterm_ssh::Config::new(); - ssh_config.add_default_config_files(); - - let (remote_host_name, port) = { - let parts: Vec<&str> = ssh_dom.remote_address.split(':').collect(); - - if parts.len() == 2 { - (parts[0], Some(parts[1].parse::()?)) - } else { - (ssh_dom.remote_address.as_str(), None) - } - }; - - let mut ssh_config = ssh_config.for_host(&remote_host_name); - ssh_config.insert( - "wezterm_ssh_backend".to_string(), - match ssh_dom - .ssh_backend - .unwrap_or_else(|| configuration().ssh_backend) - { - SshBackend::Ssh2 => "ssh2", - SshBackend::LibSsh => "libssh", - } - .to_string(), - ); - for (k, v) in &ssh_dom.ssh_option { - ssh_config.insert(k.to_string(), v.to_string()); - } - - if let Some(username) = &ssh_dom.username { - ssh_config.insert("user".to_string(), username.to_string()); - } - if let Some(port) = port { - ssh_config.insert("port".to_string(), port.to_string()); - } - if ssh_dom.no_agent_auth { - ssh_config.insert("identitiesonly".to_string(), "yes".to_string()); - } + let ssh_config = mux::ssh::ssh_domain_to_ssh_config(&ssh_dom)?; let sess = ssh_connect_with_ui(ssh_config, ui)?; let proxy_bin = Self::wezterm_bin_path(&ssh_dom.remote_wezterm_path); diff --git a/wezterm-gui/src/main.rs b/wezterm-gui/src/main.rs index cc7fd8a61..5ce922f8c 100644 --- a/wezterm-gui/src/main.rs +++ b/wezterm-gui/src/main.rs @@ -6,6 +6,7 @@ use anyhow::{anyhow, Context}; use config::{ConfigHandle, SshBackend}; use mux::activity::Activity; use mux::domain::{Domain, LocalDomain}; +use mux::ssh::RemoteSshDomain; use mux::Mux; use portable_pty::cmdbuilder::CommandBuilder; use promise::spawn::block_on; @@ -240,17 +241,14 @@ async fn async_run_with_domain_as_default( spawn_tab_in_default_domain_if_mux_is_empty(cmd).await } -async fn async_run_mux_client( - config: config::ConfigHandle, - opts: ConnectCommand, -) -> anyhow::Result<()> { +async fn async_run_mux_client(opts: ConnectCommand) -> anyhow::Result<()> { if let Some(cls) = opts.class.as_ref() { crate::set_window_class(cls); } - let client_config = client_domains(&config) - .into_iter() - .find(|c| c.name() == opts.domain_name) + let domain = Mux::get() + .unwrap() + .get_domain_by_name(&opts.domain_name) .ok_or_else(|| { anyhow!( "no multiplexer domain with name `{}` was found in the configuration", @@ -258,7 +256,6 @@ async fn async_run_mux_client( ) })?; - let domain: Arc = Arc::new(ClientDomain::new(client_config)); let opts = opts.clone(); let cmd = if !opts.prog.is_empty() { let builder = CommandBuilder::from_argv(opts.prog); @@ -270,12 +267,12 @@ async fn async_run_mux_client( async_run_with_domain_as_default(domain, cmd).await } -fn run_mux_client(config: config::ConfigHandle, opts: ConnectCommand) -> anyhow::Result<()> { +fn run_mux_client(opts: ConnectCommand) -> anyhow::Result<()> { let activity = Activity::new(); build_initial_mux(&config::configuration(), None)?; let gui = crate::frontend::try_new()?; promise::spawn::spawn(async { - if let Err(err) = async_run_mux_client(config, opts).await { + if let Err(err) = async_run_mux_client(opts).await { terminate_with_error(err); } drop(activity); @@ -327,18 +324,26 @@ async fn spawn_tab_in_default_domain_if_mux_is_empty( fn update_mux_domains(config: &ConfigHandle) -> anyhow::Result<()> { let mux = Mux::get().unwrap(); - fn record_domain(mux: &Rc, client: ClientDomain) -> anyhow::Result> { - if let Some(domain) = mux.get_domain_by_name(client.domain_name()) { - Ok(domain) - } else { - let domain: Arc = Arc::new(client); - mux.add_domain(&domain); - Ok(domain) + for client_config in client_domains(&config) { + if mux.get_domain_by_name(client_config.name()).is_some() { + continue; } + + let domain: Arc = Arc::new(ClientDomain::new(client_config)); + mux.add_domain(&domain); } - for client_config in client_domains(&config) { - record_domain(&mux, ClientDomain::new(client_config))?; + for ssh_dom in &config.ssh_domains { + if ssh_dom.use_multiplexer { + continue; + } + + if mux.get_domain_by_name(&ssh_dom.name).is_some() { + continue; + } + + let domain: Arc = Arc::new(RemoteSshDomain::with_ssh_domain(&ssh_dom)?); + mux.add_domain(&domain); } for wsl_dom in &config.wsl_domains { @@ -860,7 +865,7 @@ fn run() -> anyhow::Result<()> { } SubCommand::Ssh(ssh) => run_ssh(ssh), SubCommand::Serial(serial) => run_serial(config, &serial), - SubCommand::Connect(connect) => run_mux_client(config, connect), + SubCommand::Connect(connect) => run_mux_client(connect), SubCommand::LsFonts(cmd) => run_ls_fonts(config, &cmd), } }