Fix SSH reconnection message (#48190)

Closes #34531

Release Notes:

- N/A
This commit is contained in:
localcc 2026-02-02 17:41:35 +01:00 committed by GitHub
parent 70c47e96bd
commit fa0db76811
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -315,7 +315,8 @@ impl RemoteConnection for SshRemoteConnection {
*ssh_path_style,
ssh_shell,
*ssh_shell_kind,
socket.ssh_args(),
socket.ssh_command_options(),
&socket.connection_options.ssh_destination(),
interactive,
)
} else {
@ -329,7 +330,8 @@ impl RemoteConnection for SshRemoteConnection {
*ssh_path_style,
ssh_shell,
*ssh_shell_kind,
socket.ssh_args(),
socket.ssh_command_options(),
&socket.connection_options.ssh_destination(),
interactive,
)
}
@ -340,12 +342,13 @@ impl RemoteConnection for SshRemoteConnection {
forwards: Vec<(u16, String, u16)>,
) -> Result<CommandTemplate> {
let Self { socket, .. } = self;
let mut args = socket.ssh_args();
let mut args = socket.ssh_command_options();
args.push("-N".into());
for (local_port, host, remote_port) in forwards {
args.push("-L".into());
args.push(format!("{local_port}:{host}:{remote_port}"));
}
args.push(socket.connection_options.ssh_destination());
Ok(CommandTemplate {
program: "ssh".into(),
args,
@ -1208,20 +1211,23 @@ impl SshSocket {
cmd
}
// On Windows, we need to use `SSH_ASKPASS` to provide the password to ssh.
// On Linux, we use the `ControlPath` option to create a socket file that ssh can use to
fn ssh_args(&self) -> Vec<String> {
let mut arguments = self.connection_options.additional_args();
// Returns the SSH command-line options (without the destination) for building commands.
// On Linux, this includes the ControlPath option to reuse the existing connection.
// Note: The destination must be added separately after all options to ensure proper
// SSH command structure: ssh [options] destination [command]
fn ssh_command_options(&self) -> Vec<String> {
let arguments = self.connection_options.additional_args();
#[cfg(not(windows))]
arguments.extend(vec![
"-o".to_string(),
"ControlMaster=no".to_string(),
"-o".to_string(),
format!("ControlPath={}", self.socket_path.display()),
self.connection_options.ssh_destination(),
]);
#[cfg(windows)]
arguments.push(self.connection_options.ssh_destination());
let arguments = {
let mut args = arguments;
args.extend(vec![
"-o".to_string(),
"ControlMaster=no".to_string(),
"-o".to_string(),
format!("ControlPath={}", self.socket_path.display()),
]);
args
};
arguments
}
@ -1571,7 +1577,8 @@ fn build_command_posix(
ssh_path_style: PathStyle,
ssh_shell: &str,
ssh_shell_kind: ShellKind,
ssh_args: Vec<String>,
ssh_options: Vec<String>,
ssh_destination: &str,
interactive: Interactive,
) -> Result<CommandTemplate> {
use std::fmt::Write as _;
@ -1632,7 +1639,7 @@ fn build_command_posix(
};
let mut args = Vec::new();
args.extend(ssh_args);
args.extend(ssh_options);
if let Some((local_port, host, remote_port)) = port_forward {
args.push("-L".into());
@ -1648,6 +1655,8 @@ fn build_command_posix(
// -T disables pseudo-TTY allocation (for non-interactive piped stdio)
Interactive::No => args.push("-T".into()),
}
// The destination must come after all options but before the command
args.push(ssh_destination.into());
args.push(exec);
Ok(CommandTemplate {
@ -1667,7 +1676,8 @@ fn build_command_windows(
ssh_path_style: PathStyle,
ssh_shell: &str,
_ssh_shell_kind: ShellKind,
ssh_args: Vec<String>,
ssh_options: Vec<String>,
ssh_destination: &str,
interactive: Interactive,
) -> Result<CommandTemplate> {
use base64::Engine as _;
@ -1719,7 +1729,7 @@ fn build_command_windows(
};
let mut args = Vec::new();
args.extend(ssh_args);
args.extend(ssh_options);
if let Some((local_port, host, remote_port)) = port_forward {
args.push("-L".into());
@ -1736,6 +1746,9 @@ fn build_command_windows(
Interactive::No => args.push("-T".into()),
}
// The destination must come after all options but before the command
args.push(ssh_destination.into());
// Windows OpenSSH server incorrectly escapes the command string when the PTY is used.
// The simplest way to work around this is to use a base64 encoded command, which doesn't require escaping.
let utf16_bytes: Vec<u16> = exec.encode_utf16().collect();
@ -1774,6 +1787,7 @@ mod tests {
"/bin/bash",
ShellKind::Posix,
vec!["-o".to_string(), "ControlMaster=auto".to_string()],
"user@host",
Interactive::No,
)?;
assert_eq!(command.program, "ssh");
@ -1793,6 +1807,7 @@ mod tests {
"/bin/fish",
ShellKind::Fish,
vec!["-p".to_string(), "2222".to_string()],
"user@host",
Interactive::Yes,
)?;
@ -1804,6 +1819,7 @@ mod tests {
"2222",
"-q",
"-t",
"user@host",
"cd \"$HOME/work\" && exec env INPUT_VA=val remote_program arg1 arg2"
]
);
@ -1825,6 +1841,7 @@ mod tests {
"/bin/fish",
ShellKind::Fish,
vec!["-p".to_string(), "2222".to_string()],
"user@host",
Interactive::Yes,
)?;
@ -1838,6 +1855,7 @@ mod tests {
"1:foo:2",
"-q",
"-t",
"user@host",
"cd && exec env INPUT_VA=val /bin/fish -l"
]
);