109 lines
3.4 KiB
Rust
109 lines
3.4 KiB
Rust
use crate::config::{ConnectionType, CredentialEntry, SshellConfig};
|
|
use anyhow::{Context, Result, bail};
|
|
use std::io::Write;
|
|
use std::process::Command;
|
|
use tempfile::NamedTempFile;
|
|
|
|
pub fn connect(name: &str, cfg: &SshellConfig) -> Result<()> {
|
|
let profile = cfg
|
|
.connections
|
|
.get(name)
|
|
.with_context(|| format!("connection {name} not found"))?;
|
|
crate::ui::restore_terminal()?;
|
|
match &profile.kind {
|
|
ConnectionType::Ssh {
|
|
host,
|
|
port,
|
|
user,
|
|
auth_ref,
|
|
..
|
|
} => connect_ssh(cfg, host, *port, user, auth_ref),
|
|
ConnectionType::Shell {
|
|
command,
|
|
sync_args,
|
|
local_args,
|
|
..
|
|
} => {
|
|
let mut merged_args = sync_args.clone();
|
|
merged_args.extend(local_args.clone());
|
|
exec_shell(command, &merged_args)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn connect_ssh(
|
|
cfg: &SshellConfig,
|
|
host: &str,
|
|
port: u16,
|
|
user: &str,
|
|
auth_ref: &str,
|
|
) -> Result<()> {
|
|
if auth_ref.is_empty() {
|
|
bail!("this connection has no credential; edit it and set password or private key");
|
|
}
|
|
match cfg.credential(auth_ref) {
|
|
Some(CredentialEntry::Password { value, .. }) => {
|
|
let args = vec![
|
|
"ssh".to_string(),
|
|
"-o".to_string(),
|
|
"StrictHostKeyChecking=accept-new".to_string(),
|
|
"-p".to_string(),
|
|
port.to_string(),
|
|
format!("{user}@{host}"),
|
|
];
|
|
run_sshpass(&args, value)
|
|
}
|
|
Some(CredentialEntry::PrivateKey { value, .. }) => {
|
|
let key_content = match value {
|
|
Some(v) if !v.is_empty() => v,
|
|
_ => bail!("private key credential {auth_ref} is empty"),
|
|
};
|
|
let mut key = NamedTempFile::new()?;
|
|
key.write_all(key_content.as_bytes())?;
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
key.as_file()
|
|
.set_permissions(std::fs::Permissions::from_mode(0o600))?;
|
|
}
|
|
let status = Command::new("ssh")
|
|
.arg("-o")
|
|
.arg("StrictHostKeyChecking=accept-new")
|
|
.arg("-i")
|
|
.arg(key.path())
|
|
.arg("-p")
|
|
.arg(port.to_string())
|
|
.arg(format!("{user}@{host}"))
|
|
.status()?;
|
|
std::process::exit(status.code().unwrap_or(1));
|
|
}
|
|
None => bail!("credential {auth_ref} not found"),
|
|
}
|
|
}
|
|
|
|
fn run_sshpass(args: &[String], password: &str) -> Result<()> {
|
|
let status = Command::new("sshpass")
|
|
.arg("-e")
|
|
.args(args)
|
|
.env("SSHPASS", password)
|
|
.status()
|
|
.context("failed to run sshpass — is it installed?")?;
|
|
std::process::exit(status.code().unwrap_or(1));
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
fn exec_shell(command: &str, args: &[String]) -> Result<()> {
|
|
use std::os::unix::process::CommandExt;
|
|
let err = Command::new(command).args(args).exec();
|
|
Err(err).with_context(|| format!("failed to exec {command}"))
|
|
}
|
|
|
|
#[cfg(not(unix))]
|
|
fn exec_shell(command: &str, args: &[String]) -> Result<()> {
|
|
let status = Command::new(command)
|
|
.args(args)
|
|
.status()
|
|
.with_context(|| format!("failed to run {command}"))?;
|
|
std::process::exit(status.code().unwrap_or(1));
|
|
}
|