aboutsummaryrefslogtreecommitdiff
path: root/src/remote/attach.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/remote/attach.rs')
-rw-r--r--src/remote/attach.rs99
1 files changed, 93 insertions, 6 deletions
diff --git a/src/remote/attach.rs b/src/remote/attach.rs
index 2aee5efe..042676d0 100644
--- a/src/remote/attach.rs
+++ b/src/remote/attach.rs
@@ -957,6 +957,35 @@ fn prepare_windows_remote_herdr(
})
}
+pub(super) fn find_installed_remote_api_herdr(
+ ssh: &RemoteSsh,
+ session: &str,
+) -> io::Result<RemoteHerdr> {
+ let platform = detect_remote_platform(ssh)?;
+ let remote_herdr = RemoteHerdr::for_platform(platform);
+ let candidates = if remote_herdr.platform.is_windows() {
+ vec![remote_herdr]
+ } else {
+ remote_binary_candidates(ssh, &remote_herdr)?
+ };
+ for candidate in candidates {
+ let probe =
+ ssh.framed_user_shell_output(&remote_api_bridge_command(&candidate, session, true))?;
+ if probe.status.code() == Some(255) {
+ return Err(command_failed("remote SSH connection failed", &probe));
+ }
+ if probe.status.success()
+ && String::from_utf8_lossy(&probe.stdout).trim() == "herdr-api-bridge-v1"
+ {
+ return Ok(candidate);
+ }
+ }
+ Err(io::Error::new(
+ io::ErrorKind::Unsupported,
+ "remote Herdr does not support machine API forwarding; update Herdr on this machine",
+ ))
+}
+
fn detect_remote_platform(ssh: &RemoteSsh) -> io::Result<RemotePlatform> {
let output = ssh.sh_output("uname -s\nuname -m\n")?;
let mut windows_uname_hint = false;
@@ -2015,6 +2044,24 @@ fn confirm_remote_install(
Ok(())
}
+pub(super) fn remote_api_bridge_command(
+ remote_herdr: &RemoteHerdr,
+ session_name: &str,
+ check: bool,
+) -> String {
+ let mut args = vec!["--session", session_name, "remote-api-bridge"];
+ if check {
+ args.push("--check");
+ }
+ match &remote_herdr.executable {
+ RemoteExecutable::PosixShellPath(_) => {
+ posix_remote_output_command(&format!("exec {}", remote_herdr.executable.command(&args)))
+ }
+ RemoteExecutable::WindowsPath(path) => {
+ windows_powershell_streaming_application_command(path, &args)
+ }
+ }
+}
fn reattach_command(
program: &str,
target: &str,
@@ -2066,6 +2113,22 @@ impl SshStdioBridge {
ssh_options: Option<&ManagedSshOptions>,
noninteractive: bool,
) -> io::Result<Self> {
+ Self::start_command(
+ target,
+ remote_herdr.executable.bridge_command(&session_name),
+ local_socket,
+ ssh_options,
+ noninteractive,
+ )
+ }
+
+ pub(super) fn start_command(
+ target: String,
+ remote_command: String,
+ local_socket: PathBuf,
+ ssh_options: Option<&ManagedSshOptions>,
+ noninteractive: bool,
+ ) -> io::Result<Self> {
crate::ipc::prepare_socket_path(&local_socket, |path| {
format!("remote bridge is already listening at {}", path.display())
})?;
@@ -2103,8 +2166,7 @@ impl SshStdioBridge {
if let Err(err) = bridge_connection(
stream,
&target,
- &remote_herdr,
- &session_name,
+ &remote_command,
thread_ssh_options.as_ref(),
noninteractive,
&thread_stop,
@@ -2142,7 +2204,7 @@ impl SshStdioBridge {
})
}
- fn reported_failure(&self) -> Option<io::Error> {
+ pub(super) fn reported_failure(&self) -> Option<io::Error> {
self.failure_rx
.recv_timeout(BRIDGE_FAILURE_REPORT_TIMEOUT)
.ok()
@@ -2285,8 +2347,7 @@ pub(crate) fn bridge_upload_cancellation_for_test(
fn bridge_connection(
mut stream: crate::ipc::LocalStream,
target: &str,
- remote_herdr: &RemoteHerdr,
- session_name: &str,
+ remote_command: &str,
ssh_options: Option<&ManagedSshOptions>,
noninteractive: bool,
bridge_stop: &Arc<AtomicBool>,
@@ -2300,7 +2361,7 @@ fn bridge_connection(
command
.arg("-T")
.arg(target)
- .arg(remote_herdr.executable.bridge_command(session_name))
+ .arg(remote_command)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(if noninteractive {
@@ -3551,6 +3612,16 @@ mod tests {
"$process = Start-Process -FilePath herdr.exe -ArgumentList '--session agents remote-client-bridge' -NoNewWindow -Wait -PassThru -ErrorAction Stop; exit $process.ExitCode",
),
(
+ "API bridge with explicit default session",
+ remote_api_bridge_command(&RemoteHerdr::for_platform(RemotePlatform { os: "windows", arch: "x86_64" }), "default", false),
+ "$process = Start-Process -FilePath herdr.exe -ArgumentList '--session default remote-api-bridge' -NoNewWindow -Wait -PassThru -ErrorAction Stop; exit $process.ExitCode",
+ ),
+ (
+ "API bridge capability probe",
+ remote_api_bridge_command(&RemoteHerdr::for_platform(RemotePlatform { os: "windows", arch: "x86_64" }), "agents", true),
+ "$process = Start-Process -FilePath herdr.exe -ArgumentList '--session agents remote-api-bridge --check' -NoNewWindow -Wait -PassThru -ErrorAction Stop; exit $process.ExitCode",
+ ),
+ (
"saved bridge with closed stdin",
executable.saved_bridge_command("agents"),
"$process = Start-Process -FilePath herdr.exe -ArgumentList '--session agents remote-client-bridge' -NoNewWindow -Wait -PassThru -ErrorAction Stop; exit $process.ExitCode",
@@ -3665,6 +3736,22 @@ mod tests {
}
#[test]
+ fn remote_api_bridge_always_selects_the_saved_session() {
+ let remote_herdr = RemoteHerdr::for_platform(RemotePlatform {
+ os: "linux",
+ arch: "x86_64",
+ });
+ for session in ["default", "agents"] {
+ assert_eq!(
+ remote_api_bridge_command(&remote_herdr, session, false),
+ posix_remote_output_command(&format!(
+ "exec \"$HOME/.local/bin/herdr\" --session {session} remote-api-bridge"
+ ))
+ );
+ }
+ }
+
+ #[test]
fn remote_bridge_command_uses_installed_binary() {
let remote_herdr = RemoteHerdr::for_platform(RemotePlatform {
os: "linux",