1use crate::shell::shell_quote;
5use std::process::{Command, Output};
6use thiserror::Error;
7
8const SSH_OPTIONS: &[&str] = &[
9 "-o",
10 "BatchMode=yes",
11 "-o",
12 "ConnectTimeout=10",
13 "-o",
14 "ServerAliveInterval=5",
15 "-o",
16 "ServerAliveCountMax=2",
17 "--",
18];
19
20pub fn ssh_argv(target: &str, script: &str) -> Vec<String> {
23 let mut argv: Vec<String> = ["ssh"]
24 .into_iter()
25 .map(str::to_owned)
26 .chain(SSH_OPTIONS.iter().map(|option| (*option).to_owned()))
27 .collect();
28 argv.extend([
29 target.to_owned(),
30 "bash".to_owned(),
31 "-lic".to_owned(),
32 shell_quote(script),
33 ]);
34 argv
35}
36
37#[derive(Debug, Error)]
38pub enum SshError {
39 #[error("failed to launch SSH for {target:?}: {source}")]
40 Launch {
41 target: String,
42 #[source]
43 source: std::io::Error,
44 },
45}
46
47pub fn ssh_output(target: &str, script: &str) -> Result<Output, SshError> {
48 let argv = ssh_argv(target, script);
49 Command::new(&argv[0])
50 .args(&argv[1..])
51 .output()
52 .map_err(|source| SshError::Launch {
53 target: target.to_owned(),
54 source,
55 })
56}