mirror of
https://github.com/nestriness/cdc-file-transfer.git
synced 2026-01-30 14:35:37 +02:00
[RemoteUtil] Fix output from Windows SSH commands (#90)
Adds an ArchType argument to many RemoteUtil methods, which is used to replace -tt (forced pseudo-TTY allocation) by -T (no pseudo-TTY allocation). The -tt option adds tons of ANSI escape sequences to the output and makes it unparsable, even after removing the sequences, as some sequences like "delete the last X characters" are not honoured. An exception is BuildProcessStartInfoForSshPortForward, where replacing -tt by -T would make the port forwarding process exit immediately.
This commit is contained in:
@@ -179,6 +179,7 @@ cc_library(
|
|||||||
srcs = ["server_arch.cc"],
|
srcs = ["server_arch.cc"],
|
||||||
hdrs = ["server_arch.h"],
|
hdrs = ["server_arch.h"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//common:ansi_filter",
|
||||||
"//common:arch_type",
|
"//common:arch_type",
|
||||||
"//common:path",
|
"//common:path",
|
||||||
"//common:remote_util",
|
"//common:remote_util",
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ absl::Status CdcRsyncClient::StartServer(int port, const ServerArch& arch) {
|
|||||||
std::string remote_command = arch.GetStartServerCommand(
|
std::string remote_command = arch.GetStartServerCommand(
|
||||||
kExitCodeNotFound, absl::StrFormat("%i %s", port, component_args));
|
kExitCodeNotFound, absl::StrFormat("%i %s", port, component_args));
|
||||||
start_info = remote_util_->BuildProcessStartInfoForSshPortForwardAndCommand(
|
start_info = remote_util_->BuildProcessStartInfoForSshPortForwardAndCommand(
|
||||||
port, port, /*reverse=*/false, remote_command);
|
port, port, /*reverse=*/false, remote_command, arch.GetType());
|
||||||
} else {
|
} else {
|
||||||
// Run cdc_rsync_server locally.
|
// Run cdc_rsync_server locally.
|
||||||
std::string exe_dir;
|
std::string exe_dir;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "absl/strings/match.h"
|
#include "absl/strings/match.h"
|
||||||
#include "absl/strings/str_format.h"
|
#include "absl/strings/str_format.h"
|
||||||
#include "absl/strings/str_split.h"
|
#include "absl/strings/str_split.h"
|
||||||
|
#include "common/ansi_filter.h"
|
||||||
#include "common/path.h"
|
#include "common/path.h"
|
||||||
#include "common/remote_util.h"
|
#include "common/remote_util.h"
|
||||||
#include "common/status_macros.h"
|
#include "common/status_macros.h"
|
||||||
@@ -119,9 +120,15 @@ absl::StatusOr<ServerArch> ServerArch::DetectFromRemoteDevice(
|
|||||||
// Run uname, assuming it's a Linux machine.
|
// Run uname, assuming it's a Linux machine.
|
||||||
std::string uname_out;
|
std::string uname_out;
|
||||||
std::string linux_cmd = "uname -sm";
|
std::string linux_cmd = "uname -sm";
|
||||||
absl::Status status =
|
absl::Status status = remote_util->RunWithCapture(
|
||||||
remote_util->RunWithCapture(linux_cmd, "uname", &uname_out, nullptr);
|
linux_cmd, "uname", &uname_out, nullptr, ArchType::kLinux_x86_64);
|
||||||
if (status.ok()) {
|
if (status.ok()) {
|
||||||
|
// Running uname on Windows, assuming it's Linux, leads to tons of ANSI
|
||||||
|
// escape sequences in the output. Remove them to at least get some readable
|
||||||
|
// output.
|
||||||
|
uname_out = absl::StripAsciiWhitespace(
|
||||||
|
ansi_filter::RemoveEscapeSequences(uname_out));
|
||||||
|
|
||||||
LOG_DEBUG("Uname returned '%s'", uname_out);
|
LOG_DEBUG("Uname returned '%s'", uname_out);
|
||||||
absl::StatusOr<ArchType> type = GetArchTypeFromUname(uname_out);
|
absl::StatusOr<ArchType> type = GetArchTypeFromUname(uname_out);
|
||||||
if (type.ok()) {
|
if (type.ok()) {
|
||||||
@@ -142,8 +149,9 @@ absl::StatusOr<ServerArch> ServerArch::DetectFromRemoteDevice(
|
|||||||
std::string arch_out;
|
std::string arch_out;
|
||||||
std::string windows_cmd =
|
std::string windows_cmd =
|
||||||
RemoteUtil::QuoteForSsh("cmd /C set PROCESSOR_ARCHITECTURE ");
|
RemoteUtil::QuoteForSsh("cmd /C set PROCESSOR_ARCHITECTURE ");
|
||||||
status = remote_util->RunWithCapture(
|
status = remote_util->RunWithCapture(windows_cmd,
|
||||||
windows_cmd, "set PROCESSOR_ARCHITECTURE", &arch_out, nullptr);
|
"set PROCESSOR_ARCHITECTURE", &arch_out,
|
||||||
|
nullptr, ArchType::kWindows_x86_64);
|
||||||
if (status.ok()) {
|
if (status.ok()) {
|
||||||
LOG_DEBUG("PROCESSOR_ARCHITECTURE is '%s'", arch_out);
|
LOG_DEBUG("PROCESSOR_ARCHITECTURE is '%s'", arch_out);
|
||||||
absl::StatusOr<ArchType> type = GetArchTypeFromWinProcArch(arch_out);
|
absl::StatusOr<ArchType> type = GetArchTypeFromWinProcArch(arch_out);
|
||||||
|
|||||||
@@ -143,7 +143,8 @@ absl::Status CdcFuseManager::RunFuseProcess(uint16_t local_port,
|
|||||||
LOG_DEBUG("Running FUSE process");
|
LOG_DEBUG("Running FUSE process");
|
||||||
ProcessStartInfo start_info =
|
ProcessStartInfo start_info =
|
||||||
remote_util_->BuildProcessStartInfoForSshPortForwardAndCommand(
|
remote_util_->BuildProcessStartInfoForSshPortForwardAndCommand(
|
||||||
local_port, remote_port, true, remote_command);
|
local_port, remote_port, true, remote_command,
|
||||||
|
ArchType::kLinux_x86_64);
|
||||||
start_info.name = kFuseFilename;
|
start_info.name = kFuseFilename;
|
||||||
|
|
||||||
// Capture stdout to determine whether a deploy is required.
|
// Capture stdout to determine whether a deploy is required.
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ absl::StatusOr<std::unordered_set<int>> PortManager::FindAvailableRemotePorts(
|
|||||||
SteadyClock* steady_clock) {
|
SteadyClock* steady_clock) {
|
||||||
std::string remote_command = GetNetstatCommand(arch_type);
|
std::string remote_command = GetNetstatCommand(arch_type);
|
||||||
ProcessStartInfo start_info =
|
ProcessStartInfo start_info =
|
||||||
remote_util->BuildProcessStartInfoForSsh(remote_command);
|
remote_util->BuildProcessStartInfoForSsh(remote_command, arch_type);
|
||||||
start_info.name = "netstat";
|
start_info.name = "netstat";
|
||||||
start_info.flags = ProcessFlags::kNoWindow;
|
start_info.flags = ProcessFlags::kNoWindow;
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,22 @@ std::string GetPortForwardingArg(int local_port, int remote_port,
|
|||||||
return absl::StrFormat("-L%i:localhost:%i ", local_port, remote_port);
|
return absl::StrFormat("-L%i:localhost:%i ", local_port, remote_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* GetFlagsForArch(ArchType remote_arch_type) {
|
||||||
|
if (IsWindowsArchType(remote_arch_type)) {
|
||||||
|
// Disable pseudo-TTY. Otherwise, the output is riddled by Ansi control
|
||||||
|
// sequences and almost impossible to parse without handling them.
|
||||||
|
return "-T";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsLinuxArchType(remote_arch_type)) {
|
||||||
|
// Force pseudo-TTY.
|
||||||
|
return "-tt";
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!"Unhandled arch type");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
RemoteUtil::RemoteUtil(std::string user_host, int verbosity, bool quiet,
|
RemoteUtil::RemoteUtil(std::string user_host, int verbosity, bool quiet,
|
||||||
@@ -136,12 +152,13 @@ absl::Status RemoteUtil::Chmod(const std::string& mode,
|
|||||||
absl::StrFormat("chmod %s %s %s", QuoteForSsh(mode),
|
absl::StrFormat("chmod %s %s %s", QuoteForSsh(mode),
|
||||||
QuoteForSsh(remote_path), quiet ? "-f" : "");
|
QuoteForSsh(remote_path), quiet ? "-f" : "");
|
||||||
|
|
||||||
return Run(remote_command, "chmod");
|
return Run(remote_command, "chmod", ArchType::kLinux_x86_64);
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status RemoteUtil::Run(std::string remote_command, std::string name) {
|
absl::Status RemoteUtil::Run(std::string remote_command, std::string name,
|
||||||
|
ArchType remote_arch_type) {
|
||||||
ProcessStartInfo start_info =
|
ProcessStartInfo start_info =
|
||||||
BuildProcessStartInfoForSsh(std::move(remote_command));
|
BuildProcessStartInfoForSsh(std::move(remote_command), remote_arch_type);
|
||||||
start_info.name = std::move(name);
|
start_info.name = std::move(name);
|
||||||
start_info.forward_output_to_log = forward_output_to_log_;
|
start_info.forward_output_to_log = forward_output_to_log_;
|
||||||
|
|
||||||
@@ -150,9 +167,10 @@ absl::Status RemoteUtil::Run(std::string remote_command, std::string name) {
|
|||||||
|
|
||||||
absl::Status RemoteUtil::RunWithCapture(std::string remote_command,
|
absl::Status RemoteUtil::RunWithCapture(std::string remote_command,
|
||||||
std::string name, std::string* std_out,
|
std::string name, std::string* std_out,
|
||||||
std::string* std_err) {
|
std::string* std_err,
|
||||||
|
ArchType remote_arch_type) {
|
||||||
ProcessStartInfo start_info =
|
ProcessStartInfo start_info =
|
||||||
BuildProcessStartInfoForSsh(std::move(remote_command));
|
BuildProcessStartInfoForSsh(std::move(remote_command), remote_arch_type);
|
||||||
start_info.name = std::move(name);
|
start_info.name = std::move(name);
|
||||||
start_info.forward_output_to_log = forward_output_to_log_;
|
start_info.forward_output_to_log = forward_output_to_log_;
|
||||||
|
|
||||||
@@ -174,8 +192,9 @@ absl::Status RemoteUtil::RunWithCapture(std::string remote_command,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProcessStartInfo RemoteUtil::BuildProcessStartInfoForSsh(
|
ProcessStartInfo RemoteUtil::BuildProcessStartInfoForSsh(
|
||||||
std::string remote_command) {
|
std::string remote_command, ArchType remote_arch_type) {
|
||||||
return BuildProcessStartInfoForSshInternal("", "-- " + remote_command);
|
return BuildProcessStartInfoForSshInternal("", "-- " + remote_command,
|
||||||
|
remote_arch_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessStartInfo RemoteUtil::BuildProcessStartInfoForSshPortForward(
|
ProcessStartInfo RemoteUtil::BuildProcessStartInfoForSshPortForward(
|
||||||
@@ -183,28 +202,34 @@ ProcessStartInfo RemoteUtil::BuildProcessStartInfoForSshPortForward(
|
|||||||
// Usually, one would pass in -N here, but this makes the connection terribly
|
// Usually, one would pass in -N here, but this makes the connection terribly
|
||||||
// slow! As a workaround, don't use -N (will open a shell), but simply eat the
|
// slow! As a workaround, don't use -N (will open a shell), but simply eat the
|
||||||
// output.
|
// output.
|
||||||
|
// Note: Don't use the Windows args now. It implies -T instead of -tt, which
|
||||||
|
// will make the process exit immediately.
|
||||||
ProcessStartInfo si = BuildProcessStartInfoForSshInternal(
|
ProcessStartInfo si = BuildProcessStartInfoForSshInternal(
|
||||||
GetPortForwardingArg(local_port, remote_port, reverse) + "-n ", "");
|
GetPortForwardingArg(local_port, remote_port, reverse) + "-n ", "",
|
||||||
|
ArchType::kLinux_x86_64);
|
||||||
si.stdout_handler = [](const void*, size_t) { return absl::OkStatus(); };
|
si.stdout_handler = [](const void*, size_t) { return absl::OkStatus(); };
|
||||||
return si;
|
return si;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessStartInfo RemoteUtil::BuildProcessStartInfoForSshPortForwardAndCommand(
|
ProcessStartInfo RemoteUtil::BuildProcessStartInfoForSshPortForwardAndCommand(
|
||||||
int local_port, int remote_port, bool reverse, std::string remote_command) {
|
int local_port, int remote_port, bool reverse, std::string remote_command,
|
||||||
|
ArchType remote_arch_type) {
|
||||||
return BuildProcessStartInfoForSshInternal(
|
return BuildProcessStartInfoForSshInternal(
|
||||||
GetPortForwardingArg(local_port, remote_port, reverse),
|
GetPortForwardingArg(local_port, remote_port, reverse),
|
||||||
"-- " + remote_command);
|
"-- " + remote_command, remote_arch_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcessStartInfo RemoteUtil::BuildProcessStartInfoForSshInternal(
|
ProcessStartInfo RemoteUtil::BuildProcessStartInfoForSshInternal(
|
||||||
std::string forward_arg, std::string remote_command_arg) {
|
std::string forward_arg, std::string remote_command_arg,
|
||||||
|
ArchType remote_arch_type) {
|
||||||
ProcessStartInfo start_info;
|
ProcessStartInfo start_info;
|
||||||
start_info.command = absl::StrFormat(
|
start_info.command = absl::StrFormat(
|
||||||
"%s %s -tt %s "
|
"%s %s %s %s "
|
||||||
"-oServerAliveCountMax=6 " // Number of lost msgs before ssh terminates
|
"-oServerAliveCountMax=6 " // Number of lost msgs before ssh terminates
|
||||||
"-oServerAliveInterval=5 " // Time interval between alive msgs
|
"-oServerAliveInterval=5 " // Time interval between alive msgs
|
||||||
"%s %s",
|
"%s %s",
|
||||||
ssh_command_, quiet_ || verbosity_ < 2 ? "-q" : "", forward_arg,
|
ssh_command_, GetFlagsForArch(remote_arch_type),
|
||||||
|
quiet_ || verbosity_ < 2 ? "-q" : "", forward_arg,
|
||||||
QuoteForWindows(user_host_), remote_command_arg);
|
QuoteForWindows(user_host_), remote_command_arg);
|
||||||
start_info.forward_output_to_log = forward_output_to_log_;
|
start_info.forward_output_to_log = forward_output_to_log_;
|
||||||
start_info.flags = ProcessFlags::kNoWindow;
|
start_info.flags = ProcessFlags::kNoWindow;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "absl/status/status.h"
|
#include "absl/status/status.h"
|
||||||
|
#include "common/arch_type.h"
|
||||||
#include "common/process.h"
|
#include "common/process.h"
|
||||||
|
|
||||||
namespace cdc_ft {
|
namespace cdc_ft {
|
||||||
@@ -92,15 +93,24 @@ class RemoteUtil {
|
|||||||
|
|
||||||
// Runs |remote_command| on the remote device. The command must be properly
|
// Runs |remote_command| on the remote device. The command must be properly
|
||||||
// escaped. |name| is the name of the command displayed in the logs.
|
// escaped. |name| is the name of the command displayed in the logs.
|
||||||
absl::Status Run(std::string remote_command, std::string name);
|
// |remote_arch_type| is the arch type of the remote device. It determines
|
||||||
|
// which type of pseudo console is used (-T on Windows, -tt on Linux). If the
|
||||||
|
// wrong arch type is passed, output might be corrupted, but otherwise the
|
||||||
|
// command will work.
|
||||||
|
absl::Status Run(std::string remote_command, std::string name,
|
||||||
|
ArchType remote_arch_type);
|
||||||
|
|
||||||
// Same as Run(), but captures both stdout and stderr.
|
// Same as Run(), but captures both stdout and stderr.
|
||||||
// If |std_out| or |std_err| are nullptr, the output is not captured.
|
// If |std_out| or |std_err| are nullptr, the output is not captured.
|
||||||
|
// |remote_arch_type| is the arch type of the remote device, see Run().
|
||||||
absl::Status RunWithCapture(std::string remote_command, std::string name,
|
absl::Status RunWithCapture(std::string remote_command, std::string name,
|
||||||
std::string* std_out, std::string* std_err);
|
std::string* std_out, std::string* std_err,
|
||||||
|
ArchType remote_arch_type);
|
||||||
|
|
||||||
// Builds an SSH command that executes |remote_command| on the remote device.
|
// Builds an SSH command that executes |remote_command| on the remote device.
|
||||||
ProcessStartInfo BuildProcessStartInfoForSsh(std::string remote_command);
|
// |remote_arch_type| is the arch type of the remote device, see Run().
|
||||||
|
ProcessStartInfo BuildProcessStartInfoForSsh(std::string remote_command,
|
||||||
|
ArchType remote_arch_type);
|
||||||
|
|
||||||
// Builds an SSH command that runs SSH port forwarding to the remote device,
|
// Builds an SSH command that runs SSH port forwarding to the remote device,
|
||||||
// using the given |local_port| and |remote_port|. If |reverse| is true, sets
|
// using the given |local_port| and |remote_port|. If |reverse| is true, sets
|
||||||
@@ -112,9 +122,10 @@ class RemoteUtil {
|
|||||||
// Builds an SSH command that executes |remote_command| on the remote device,
|
// Builds an SSH command that executes |remote_command| on the remote device,
|
||||||
// using port forwarding with given |local_port| and |remote_port|. If
|
// using port forwarding with given |local_port| and |remote_port|. If
|
||||||
// |reverse| is true, sets up reverse port forwarding.
|
// |reverse| is true, sets up reverse port forwarding.
|
||||||
|
// |remote_arch_type| is the arch type of the remote device, see Run().
|
||||||
ProcessStartInfo BuildProcessStartInfoForSshPortForwardAndCommand(
|
ProcessStartInfo BuildProcessStartInfoForSshPortForwardAndCommand(
|
||||||
int local_port, int remote_port, bool reverse,
|
int local_port, int remote_port, bool reverse, std::string remote_command,
|
||||||
std::string remote_command);
|
ArchType remote_arch_type);
|
||||||
|
|
||||||
// Returns whether output is suppressed.
|
// Returns whether output is suppressed.
|
||||||
bool Quiet() const { return quiet_; }
|
bool Quiet() const { return quiet_; }
|
||||||
@@ -145,7 +156,8 @@ class RemoteUtil {
|
|||||||
private:
|
private:
|
||||||
// Common code for BuildProcessStartInfoForSsh*.
|
// Common code for BuildProcessStartInfoForSsh*.
|
||||||
ProcessStartInfo BuildProcessStartInfoForSshInternal(
|
ProcessStartInfo BuildProcessStartInfoForSshInternal(
|
||||||
std::string forward_arg, std::string remote_command);
|
std::string forward_arg, std::string remote_command,
|
||||||
|
ArchType remote_arch_type);
|
||||||
|
|
||||||
const int verbosity_;
|
const int verbosity_;
|
||||||
const bool quiet_;
|
const bool quiet_;
|
||||||
|
|||||||
@@ -59,8 +59,12 @@ class RemoteUtilTest : public ::testing::Test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(RemoteUtilTest, BuildProcessStartInfoForSsh) {
|
TEST_F(RemoteUtilTest, BuildProcessStartInfoForSsh) {
|
||||||
ProcessStartInfo si = util_.BuildProcessStartInfoForSsh(kCommand);
|
ProcessStartInfo si =
|
||||||
ExpectContains(si.command, {"ssh", kUserHostArg, kCommand});
|
util_.BuildProcessStartInfoForSsh(kCommand, ArchType::kLinux_x86_64);
|
||||||
|
ExpectContains(si.command, {"ssh", "-tt", kUserHostArg, kCommand});
|
||||||
|
|
||||||
|
si = util_.BuildProcessStartInfoForSsh(kCommand, ArchType::kWindows_x86_64);
|
||||||
|
ExpectContains(si.command, {"ssh", "-T", kUserHostArg, kCommand});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RemoteUtilTest, BuildProcessStartInfoForSshPortForward) {
|
TEST_F(RemoteUtilTest, BuildProcessStartInfoForSshPortForward) {
|
||||||
@@ -75,19 +79,20 @@ TEST_F(RemoteUtilTest, BuildProcessStartInfoForSshPortForward) {
|
|||||||
|
|
||||||
TEST_F(RemoteUtilTest, BuildProcessStartInfoForSshPortForwardAndCommand) {
|
TEST_F(RemoteUtilTest, BuildProcessStartInfoForSshPortForwardAndCommand) {
|
||||||
ProcessStartInfo si = util_.BuildProcessStartInfoForSshPortForwardAndCommand(
|
ProcessStartInfo si = util_.BuildProcessStartInfoForSshPortForwardAndCommand(
|
||||||
kLocalPort, kRemotePort, kRegular, kCommand);
|
kLocalPort, kRemotePort, kRegular, kCommand, ArchType::kLinux_x86_64);
|
||||||
ExpectContains(si.command,
|
ExpectContains(si.command,
|
||||||
{"ssh", kUserHostArg, kPortForwardingArg, kCommand});
|
{"ssh", kUserHostArg, kPortForwardingArg, kCommand});
|
||||||
|
|
||||||
si = util_.BuildProcessStartInfoForSshPortForwardAndCommand(
|
si = util_.BuildProcessStartInfoForSshPortForwardAndCommand(
|
||||||
kLocalPort, kRemotePort, kReverse, kCommand);
|
kLocalPort, kRemotePort, kReverse, kCommand, ArchType::kLinux_x86_64);
|
||||||
ExpectContains(si.command,
|
ExpectContains(si.command,
|
||||||
{"ssh", kUserHostArg, kReversePortForwardingArg, kCommand});
|
{"ssh", kUserHostArg, kReversePortForwardingArg, kCommand});
|
||||||
}
|
}
|
||||||
TEST_F(RemoteUtilTest, BuildProcessStartInfoForSshWithCustomCommand) {
|
TEST_F(RemoteUtilTest, BuildProcessStartInfoForSshWithCustomCommand) {
|
||||||
constexpr char kCustomSshCmd[] = "C:\\path\\to\\ssh.exe --fooarg --bararg=42";
|
constexpr char kCustomSshCmd[] = "C:\\path\\to\\ssh.exe --fooarg --bararg=42";
|
||||||
util_.SetSshCommand(kCustomSshCmd);
|
util_.SetSshCommand(kCustomSshCmd);
|
||||||
ProcessStartInfo si = util_.BuildProcessStartInfoForSsh(kCommand);
|
ProcessStartInfo si =
|
||||||
|
util_.BuildProcessStartInfoForSsh(kCommand, ArchType::kLinux_x86_64);
|
||||||
ExpectContains(si.command, {kCustomSshCmd});
|
ExpectContains(si.command, {kCustomSshCmd});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user