1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 21:32:13 +03:00

gui: allow wezterm connect sshdomnomux

If an ssh domain is set to use_multiplexer=false, it is now
possible to `wezterm connect` to it.

Previously, it was only possible to connect to domains that
used the mux client.

refs: https://github.com/wez/wezterm/issues/1456
This commit is contained in:
Wez Furlong 2022-01-09 15:48:41 -07:00
parent 0676eed42f
commit f84c0632d8
3 changed files with 99 additions and 67 deletions

View File

@ -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<Option<Session>>,
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<ConfigMap> {
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::<u16>()?))
} 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<Self> {
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<Self> {
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<ConfigMap> {
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
}
}

View File

@ -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::<u16>()?))
} 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);

View File

@ -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<dyn Domain> = 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<Mux>, client: ClientDomain) -> anyhow::Result<Arc<dyn Domain>> {
if let Some(domain) = mux.get_domain_by_name(client.domain_name()) {
Ok(domain)
} else {
let domain: Arc<dyn Domain> = 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<dyn Domain> = 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<dyn Domain> = 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),
}
}