Files
sshell/src/connection.rs
T

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));
}