mirror of
https://github.com/nestriness/cdc-file-transfer.git
synced 2026-01-30 08:55:36 +02:00
[cdc_rsync] Use any available server port (#94)
Instead of calling netstat on the remote device to detect available ports, simply call bind with port 0 to bind to any available port. Since the port is not yet known when cdc_rsync_server.exe is called, port forwarding needs to be started AFTER the server reports its port.
This commit is contained in:
2
.github/workflows/create_release.yml
vendored
2
.github/workflows/create_release.yml
vendored
@@ -63,7 +63,6 @@ jobs:
|
|||||||
--test_output=errors --local_test_jobs=1 \
|
--test_output=errors --local_test_jobs=1 \
|
||||||
-- //... -//third_party/... -//cdc_rsync_server:file_finder_test
|
-- //... -//third_party/... -//cdc_rsync_server:file_finder_test
|
||||||
|
|
||||||
# The artifact collector doesn't like the fact that bazel-bin is a symlink.
|
|
||||||
- name: Copy artifacts
|
- name: Copy artifacts
|
||||||
run: |
|
run: |
|
||||||
mkdir artifacts
|
mkdir artifacts
|
||||||
@@ -152,7 +151,6 @@ jobs:
|
|||||||
//manifest/... `
|
//manifest/... `
|
||||||
//metrics/...
|
//metrics/...
|
||||||
|
|
||||||
# The artifact collector doesn't like the fact that bazel-bin is a symlink.
|
|
||||||
- name: Copy artifacts
|
- name: Copy artifacts
|
||||||
run: |
|
run: |
|
||||||
mkdir artifacts
|
mkdir artifacts
|
||||||
|
|||||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -3,7 +3,7 @@ name: Lint
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ CdcRsyncClient::CdcRsyncClient(const Options& options,
|
|||||||
port_manager_ = std::make_unique<PortManager>(
|
port_manager_ = std::make_unique<PortManager>(
|
||||||
"cdc_rsync_ports_f77bcdfe-368c-4c45-9f01-230c5e7e2132",
|
"cdc_rsync_ports_f77bcdfe-368c-4c45-9f01-230c5e7e2132",
|
||||||
options.forward_port_first, options.forward_port_last, &process_factory_,
|
options.forward_port_first, options.forward_port_last, &process_factory_,
|
||||||
remote_util_.get());
|
nullptr /* never reserve remote ports */);
|
||||||
}
|
}
|
||||||
|
|
||||||
CdcRsyncClient::~CdcRsyncClient() {
|
CdcRsyncClient::~CdcRsyncClient() {
|
||||||
@@ -127,19 +127,15 @@ CdcRsyncClient::~CdcRsyncClient() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status CdcRsyncClient::Run() {
|
absl::Status CdcRsyncClient::Run() {
|
||||||
// If |remote_util_| is not set, it's a local sync. Otherwise, guess the
|
// For local syncs, cdc_rsync_server runs on this machine. For remote syncs,
|
||||||
// architecture of the device that runs cdc_rsync_server from the destination
|
// guess the architecture of the device that runs cdc_rsync_server from the
|
||||||
// path, e.g. "C:\path\to\dest" strongly indicates Windows.
|
// destination path, e.g. "C:\path\to\dest" strongly indicates Windows.
|
||||||
ServerArch server_arch = !remote_util_
|
ServerArch server_arch = IsRemoteConnection()
|
||||||
? ServerArch::DetectFromLocalDevice()
|
? ServerArch::GuessFromDestination(destination_)
|
||||||
: ServerArch::GuessFromDestination(destination_);
|
: ServerArch::DetectFromLocalDevice();
|
||||||
|
|
||||||
int port;
|
|
||||||
ASSIGN_OR_RETURN(port, FindAvailablePort(&server_arch),
|
|
||||||
"Failed to find available port");
|
|
||||||
|
|
||||||
// Start the server process.
|
// Start the server process.
|
||||||
absl::Status status = StartServer(port, server_arch);
|
absl::Status status = StartServer(server_arch);
|
||||||
if (HasTag(status, Tag::kDeployServer) && server_arch.IsGuess() &&
|
if (HasTag(status, Tag::kDeployServer) && server_arch.IsGuess() &&
|
||||||
server_exit_code_ != kServerExitCodeOutOfDate) {
|
server_exit_code_ != kServerExitCodeOutOfDate) {
|
||||||
// Server couldn't be run, e.g. not found or failed to start.
|
// Server couldn't be run, e.g. not found or failed to start.
|
||||||
@@ -155,7 +151,7 @@ absl::Status CdcRsyncClient::Run() {
|
|||||||
if (server_arch.GetType() != old_type) {
|
if (server_arch.GetType() != old_type) {
|
||||||
LOG_DEBUG("Guessed server arch type wrong, guessed %s, actual %s.",
|
LOG_DEBUG("Guessed server arch type wrong, guessed %s, actual %s.",
|
||||||
GetArchTypeStr(old_type), server_arch.GetTypeStr());
|
GetArchTypeStr(old_type), server_arch.GetTypeStr());
|
||||||
status = StartServer(port, server_arch);
|
status = StartServer(server_arch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +162,7 @@ absl::Status CdcRsyncClient::Run() {
|
|||||||
return WrapStatus(status, "Failed to deploy server");
|
return WrapStatus(status, "Failed to deploy server");
|
||||||
}
|
}
|
||||||
|
|
||||||
status = StartServer(port, server_arch);
|
status = StartServer(server_arch);
|
||||||
}
|
}
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return WrapStatus(status, "Failed to start server");
|
return WrapStatus(status, "Failed to start server");
|
||||||
@@ -198,47 +194,7 @@ absl::Status CdcRsyncClient::Run() {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::StatusOr<int> CdcRsyncClient::FindAvailablePort(ServerArch* server_arch) {
|
absl::Status CdcRsyncClient::StartServer(const ServerArch& arch) {
|
||||||
// Find available local and remote ports for port forwarding.
|
|
||||||
// If only one port is in the given range, try that without checking.
|
|
||||||
if (options_.forward_port_first >= options_.forward_port_last) {
|
|
||||||
return options_.forward_port_first;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(server_arch);
|
|
||||||
absl::StatusOr<int> port = port_manager_->ReservePort(
|
|
||||||
options_.connection_timeout_sec, server_arch->GetType());
|
|
||||||
|
|
||||||
if (absl::IsDeadlineExceeded(port.status())) {
|
|
||||||
// Server didn't respond in time.
|
|
||||||
return SetTag(port.status(), Tag::kConnectionTimeout);
|
|
||||||
}
|
|
||||||
if (absl::IsResourceExhausted(port.status())) {
|
|
||||||
// Port in use.
|
|
||||||
return SetTag(port.status(), Tag::kAddressInUse);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If |server_arch| was guessed, calling netstat might have failed because
|
|
||||||
// the arch was wrong. Properly detect it and try again if it changed.
|
|
||||||
if (!port.ok() && server_arch->IsGuess()) {
|
|
||||||
const ArchType old_type = server_arch->GetType();
|
|
||||||
LOG_DEBUG(
|
|
||||||
"Failed to reserve port, retrying after detecting remote arch: %s",
|
|
||||||
port.status().ToString());
|
|
||||||
ASSIGN_OR_RETURN(*server_arch,
|
|
||||||
ServerArch::DetectFromRemoteDevice(remote_util_.get()));
|
|
||||||
assert(!server_arch->IsGuess());
|
|
||||||
if (server_arch->GetType() != old_type) {
|
|
||||||
LOG_DEBUG("Guessed server arch type wrong, guessed %s, actual %s.",
|
|
||||||
GetArchTypeStr(old_type), server_arch->GetTypeStr());
|
|
||||||
return FindAvailablePort(server_arch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status CdcRsyncClient::StartServer(int port, const ServerArch& arch) {
|
|
||||||
assert(!server_process_);
|
assert(!server_process_);
|
||||||
|
|
||||||
// Components are expected to reside in the same dir as the executable.
|
// Components are expected to reside in the same dir as the executable.
|
||||||
@@ -262,20 +218,20 @@ absl::Status CdcRsyncClient::StartServer(int port, const ServerArch& arch) {
|
|||||||
ProcessStartInfo start_info;
|
ProcessStartInfo start_info;
|
||||||
start_info.name = "cdc_rsync_server";
|
start_info.name = "cdc_rsync_server";
|
||||||
|
|
||||||
if (remote_util_) {
|
if (IsRemoteConnection()) {
|
||||||
// Run cdc_rsync_server on the remote instance.
|
// Run cdc_rsync_server on the remote instance.
|
||||||
std::string remote_command = arch.GetStartServerCommand(
|
std::string remote_command =
|
||||||
kExitCodeNotFound, absl::StrFormat("%i %s", port, component_args));
|
arch.GetStartServerCommand(kExitCodeNotFound, component_args);
|
||||||
start_info = remote_util_->BuildProcessStartInfoForSshPortForwardAndCommand(
|
assert(remote_util_);
|
||||||
port, port, /*reverse=*/false, remote_command, arch.GetType());
|
start_info = remote_util_->BuildProcessStartInfoForSsh(remote_command,
|
||||||
|
arch.GetType());
|
||||||
} else {
|
} else {
|
||||||
// Run cdc_rsync_server locally.
|
// Run cdc_rsync_server locally.
|
||||||
std::string exe_dir;
|
std::string exe_dir;
|
||||||
RETURN_IF_ERROR(path::GetExeDir(&exe_dir), "Failed to get exe directory");
|
RETURN_IF_ERROR(path::GetExeDir(&exe_dir), "Failed to get exe directory");
|
||||||
|
|
||||||
std::string server_path = path::Join(exe_dir, arch.CdcServerFilename());
|
std::string server_path = path::Join(exe_dir, arch.CdcServerFilename());
|
||||||
start_info.command =
|
start_info.command = absl::StrFormat("%s %s", server_path, component_args);
|
||||||
absl::StrFormat("%s %i %s", server_path, port, component_args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture stdout, but forward to stdout for debugging purposes.
|
// Capture stdout, but forward to stdout for debugging purposes.
|
||||||
@@ -283,8 +239,8 @@ absl::Status CdcRsyncClient::StartServer(int port, const ServerArch& arch) {
|
|||||||
return HandleServerOutput(data);
|
return HandleServerOutput(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Process> process = process_factory_.Create(start_info);
|
std::unique_ptr<Process> srv_process = process_factory_.Create(start_info);
|
||||||
status = process->Start();
|
status = srv_process->Start();
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return WrapStatus(status, "Failed to start cdc_rsync_server process");
|
return WrapStatus(status, "Failed to start cdc_rsync_server process");
|
||||||
}
|
}
|
||||||
@@ -292,13 +248,13 @@ absl::Status CdcRsyncClient::StartServer(int port, const ServerArch& arch) {
|
|||||||
// Wait until the server process is listening.
|
// Wait until the server process is listening.
|
||||||
Stopwatch timeout_timer;
|
Stopwatch timeout_timer;
|
||||||
bool is_timeout = false;
|
bool is_timeout = false;
|
||||||
auto detect_listening_or_timeout = [is_listening = &is_server_listening_,
|
auto detect_listening_or_timeout = [port = &server_listen_port_,
|
||||||
timeout = options_.connection_timeout_sec,
|
timeout = options_.connection_timeout_sec,
|
||||||
&timeout_timer, &is_timeout]() -> bool {
|
&timeout_timer, &is_timeout]() -> bool {
|
||||||
is_timeout = timeout_timer.ElapsedSeconds() > timeout;
|
is_timeout = timeout_timer.ElapsedSeconds() > timeout;
|
||||||
return *is_listening || is_timeout;
|
return *port != 0 || is_timeout;
|
||||||
};
|
};
|
||||||
status = process->RunUntil(detect_listening_or_timeout);
|
status = srv_process->RunUntil(detect_listening_or_timeout);
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
// Some internal process error. Note that this does NOT mean that
|
// Some internal process error. Note that this does NOT mean that
|
||||||
// cdc_rsync_server does not exist. In that case, the ssh process exits with
|
// cdc_rsync_server does not exist. In that case, the ssh process exits with
|
||||||
@@ -310,10 +266,10 @@ absl::Status CdcRsyncClient::StartServer(int port, const ServerArch& arch) {
|
|||||||
Tag::kConnectionTimeout);
|
Tag::kConnectionTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process->HasExited()) {
|
if (srv_process->HasExited()) {
|
||||||
// Don't re-deploy for code > kServerExitCodeOutOfDate, which means that the
|
// Don't re-deploy for code > kServerExitCodeOutOfDate, which means that the
|
||||||
// out-of-date check already passed on the server.
|
// out-of-date check already passed on the server.
|
||||||
server_exit_code_ = process->ExitCode();
|
server_exit_code_ = srv_process->ExitCode();
|
||||||
if (server_exit_code_ > kServerExitCodeOutOfDate &&
|
if (server_exit_code_ > kServerExitCodeOutOfDate &&
|
||||||
server_exit_code_ <= kServerExitCodeMax) {
|
server_exit_code_ <= kServerExitCodeMax) {
|
||||||
return GetServerExitStatus(server_exit_code_, server_error_);
|
return GetServerExitStatus(server_exit_code_, server_error_);
|
||||||
@@ -321,7 +277,7 @@ absl::Status CdcRsyncClient::StartServer(int port, const ServerArch& arch) {
|
|||||||
|
|
||||||
// Don't re-deploy if we're not copying to a remote device. We can start
|
// Don't re-deploy if we're not copying to a remote device. We can start
|
||||||
// cdc_rsync_server from the original location directly.
|
// cdc_rsync_server from the original location directly.
|
||||||
if (!remote_util_) {
|
if (!IsRemoteConnection()) {
|
||||||
return GetServerExitStatus(server_exit_code_, server_error_);
|
return GetServerExitStatus(server_exit_code_, server_error_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,19 +288,58 @@ absl::Status CdcRsyncClient::StartServer(int port, const ServerArch& arch) {
|
|||||||
return SetTag(MakeStatus("Redeploy server"), Tag::kDeployServer);
|
return SetTag(MakeStatus("Redeploy server"), Tag::kDeployServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now that we know which port the server is using, set up port forwarding.
|
||||||
|
std::unique_ptr<Process> fwd_process;
|
||||||
|
int local_port = server_listen_port_;
|
||||||
|
if (IsRemoteConnection()) {
|
||||||
|
absl::StatusOr<int> local_port_or = port_manager_->ReservePort(
|
||||||
|
options_.connection_timeout_sec, arch.GetType());
|
||||||
|
|
||||||
|
if (absl::IsResourceExhausted(local_port_or.status())) {
|
||||||
|
// Port in use.
|
||||||
|
return SetTag(local_port_or.status(), Tag::kAddressInUse);
|
||||||
|
}
|
||||||
|
if (!local_port_or.ok()) {
|
||||||
|
return local_port_or.status();
|
||||||
|
}
|
||||||
|
|
||||||
|
local_port = *local_port_or;
|
||||||
|
ProcessStartInfo start_info =
|
||||||
|
remote_util_->BuildProcessStartInfoForSshPortForward(
|
||||||
|
local_port, server_listen_port_, /*reverse=*/false);
|
||||||
|
start_info.forward_output_to_log = true;
|
||||||
|
fwd_process = process_factory_.Create(start_info);
|
||||||
|
RETURN_IF_ERROR(fwd_process->Start(),
|
||||||
|
"Failed to start cdc_rsync_server process");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to the socket with port |local_port|.
|
||||||
status = Socket::Initialize();
|
status = Socket::Initialize();
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return WrapStatus(status, "Failed to initialize sockets");
|
return WrapStatus(status, "Failed to initialize sockets");
|
||||||
}
|
}
|
||||||
socket_finalizer_ = std::make_unique<SocketFinalizer>();
|
socket_finalizer_ = std::make_unique<SocketFinalizer>();
|
||||||
|
|
||||||
assert(is_server_listening_);
|
// Poll until the port forwarding connection is set up.
|
||||||
status = socket_.Connect(port);
|
timeout_timer.Reset();
|
||||||
if (!status.ok()) {
|
for (;;) {
|
||||||
return WrapStatus(status, "Failed to initialize connection");
|
assert(local_port != 0);
|
||||||
|
status = socket_.Connect(local_port);
|
||||||
|
if (status.ok()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout_timer.ElapsedSeconds() > options_.connection_timeout_sec) {
|
||||||
|
return SetTag(
|
||||||
|
absl::DeadlineExceededError("Timeout while connecting to server"),
|
||||||
|
Tag::kConnectionTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
Util::Sleep(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
server_process_ = std::move(process);
|
server_process_ = std::move(srv_process);
|
||||||
|
port_forwarding_process_ = std::move(fwd_process);
|
||||||
message_pump_.StartMessagePump();
|
message_pump_.StartMessagePump();
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
@@ -365,6 +360,7 @@ absl::Status CdcRsyncClient::StopServer() {
|
|||||||
|
|
||||||
server_exit_code_ = server_process_->ExitCode();
|
server_exit_code_ = server_process_->ExitCode();
|
||||||
server_process_.reset();
|
server_process_.reset();
|
||||||
|
port_forwarding_process_.reset();
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,10 +392,29 @@ absl::Status CdcRsyncClient::HandleServerOutput(const char* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
printer_.Print(stdout_data, false, Util::GetConsoleWidth());
|
printer_.Print(stdout_data, false, Util::GetConsoleWidth());
|
||||||
if (!is_server_listening_) {
|
if (server_listen_port_ == 0) {
|
||||||
server_output_.append(stdout_data);
|
server_output_.append(stdout_data);
|
||||||
is_server_listening_ =
|
|
||||||
server_output_.find("Server is listening") != std::string::npos;
|
// Parse port from "Port <n>: Server is listening".
|
||||||
|
size_t listening_pos = server_output_.find("Server is listening");
|
||||||
|
if (listening_pos != std::string::npos) {
|
||||||
|
// Search backwards until we find "Port ".
|
||||||
|
constexpr char port_key[] = "Port ";
|
||||||
|
size_t port_pos = server_output_.rfind(port_key, listening_pos);
|
||||||
|
if (port_pos == std::string::npos) {
|
||||||
|
return MakeStatus("Failed to find 'Port' marker in server output '%s'",
|
||||||
|
server_output_);
|
||||||
|
}
|
||||||
|
assert(listening_pos > port_pos);
|
||||||
|
server_listen_port_ = atoi(
|
||||||
|
server_output_
|
||||||
|
.substr(port_pos + strlen(port_key), listening_pos - port_pos)
|
||||||
|
.c_str());
|
||||||
|
if (server_listen_port_ == 0) {
|
||||||
|
return MakeStatus("Failed to parse port from server output '%s'",
|
||||||
|
server_output_);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
@@ -466,6 +481,7 @@ absl::Status CdcRsyncClient::Sync() {
|
|||||||
absl::Status CdcRsyncClient::DeployServer(const ServerArch& arch) {
|
absl::Status CdcRsyncClient::DeployServer(const ServerArch& arch) {
|
||||||
assert(!server_process_);
|
assert(!server_process_);
|
||||||
assert(remote_util_);
|
assert(remote_util_);
|
||||||
|
assert(IsRemoteConnection());
|
||||||
|
|
||||||
std::string exe_dir;
|
std::string exe_dir;
|
||||||
absl::Status status = path::GetExeDir(&exe_dir);
|
absl::Status status = path::GetExeDir(&exe_dir);
|
||||||
|
|||||||
@@ -78,14 +78,9 @@ class CdcRsyncClient {
|
|||||||
absl::Status Run();
|
absl::Status Run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Finds available local and remote ports for port forwarding.
|
|
||||||
// May update |server_arch| by properly detecting the architecture and retry
|
|
||||||
// if the architecture was guessed, i.e. if |server_arch|->IsGuess() is true.
|
|
||||||
absl::StatusOr<int> FindAvailablePort(ServerArch* server_arch);
|
|
||||||
|
|
||||||
// Starts the server process. If the method returns a status with tag
|
// Starts the server process. If the method returns a status with tag
|
||||||
// |kTagDeployServer|, Run() calls DeployServer() and tries again.
|
// |kTagDeployServer|, Run() calls DeployServer() and tries again.
|
||||||
absl::Status StartServer(int port, const ServerArch& arch);
|
absl::Status StartServer(const ServerArch& arch);
|
||||||
|
|
||||||
// Stops the server process.
|
// Stops the server process.
|
||||||
absl::Status StopServer();
|
absl::Status StopServer();
|
||||||
@@ -129,6 +124,9 @@ class CdcRsyncClient {
|
|||||||
// Stops the zstd compression stream.
|
// Stops the zstd compression stream.
|
||||||
absl::Status StopCompressionStream();
|
absl::Status StopCompressionStream();
|
||||||
|
|
||||||
|
// Returns true if the target is a remote target.
|
||||||
|
bool IsRemoteConnection() const { return remote_util_ != nullptr; }
|
||||||
|
|
||||||
Options options_;
|
Options options_;
|
||||||
std::vector<std::string> sources_;
|
std::vector<std::string> sources_;
|
||||||
const std::string destination_;
|
const std::string destination_;
|
||||||
@@ -143,10 +141,11 @@ class CdcRsyncClient {
|
|||||||
std::unique_ptr<ZstdStream> compression_stream_;
|
std::unique_ptr<ZstdStream> compression_stream_;
|
||||||
|
|
||||||
std::unique_ptr<Process> server_process_;
|
std::unique_ptr<Process> server_process_;
|
||||||
|
std::unique_ptr<Process> port_forwarding_process_;
|
||||||
std::string server_output_; // Written in a background thread. Do not access
|
std::string server_output_; // Written in a background thread. Do not access
|
||||||
std::string server_error_; // while the server process is active.
|
std::string server_error_; // while the server process is active.
|
||||||
int server_exit_code_ = 0;
|
int server_exit_code_ = 0;
|
||||||
std::atomic_bool is_server_listening_{false};
|
std::atomic_int server_listen_port_{0};
|
||||||
bool is_server_error_ = false;
|
bool is_server_error_ = false;
|
||||||
|
|
||||||
// All source files found on the client.
|
// All source files found on the client.
|
||||||
|
|||||||
@@ -68,11 +68,10 @@ absl::Status ClientSocket::Connect(int port) {
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
for (addrinfo* curr = addr_infos; curr; curr = curr->ai_next, count++) {
|
for (addrinfo* curr = addr_infos; curr; curr = curr->ai_next, count++) {
|
||||||
socket_info_->socket =
|
socket_info_->socket =
|
||||||
socket(addr_infos->ai_family, addr_infos->ai_socktype,
|
socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
|
||||||
addr_infos->ai_protocol);
|
|
||||||
if (socket_info_->socket == INVALID_SOCKET) {
|
if (socket_info_->socket == INVALID_SOCKET) {
|
||||||
LOG_DEBUG("socket() failed for addr_info %i: %s", count,
|
LOG_DEBUG("socket() failed for addr_info %i: %s", count,
|
||||||
Util::GetWin32Error(WSAGetLastError()).c_str());
|
Util::GetWin32Error(WSAGetLastError()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +79,8 @@ absl::Status ClientSocket::Connect(int port) {
|
|||||||
result = connect(socket_info_->socket, curr->ai_addr,
|
result = connect(socket_info_->socket, curr->ai_addr,
|
||||||
static_cast<int>(curr->ai_addrlen));
|
static_cast<int>(curr->ai_addrlen));
|
||||||
if (result == SOCKET_ERROR) {
|
if (result == SOCKET_ERROR) {
|
||||||
LOG_DEBUG("connect() failed for addr_info %i: %i", count, result);
|
LOG_DEBUG("connect() failed for addr_info %i: %s", count,
|
||||||
|
Util::GetWin32Error(WSAGetLastError()));
|
||||||
closesocket(socket_info_->socket);
|
closesocket(socket_info_->socket);
|
||||||
socket_info_->socket = INVALID_SOCKET;
|
socket_info_->socket = INVALID_SOCKET;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ bool CdcRsyncServer::CheckComponents(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status CdcRsyncServer::Run(int port) {
|
absl::Status CdcRsyncServer::Run() {
|
||||||
absl::Status status = Socket::Initialize();
|
absl::Status status = Socket::Initialize();
|
||||||
if (!status.ok()) {
|
if (!status.ok()) {
|
||||||
return WrapStatus(status, "Failed to initialize sockets");
|
return WrapStatus(status, "Failed to initialize sockets");
|
||||||
@@ -296,15 +296,15 @@ absl::Status CdcRsyncServer::Run(int port) {
|
|||||||
socket_finalizer_ = std::make_unique<SocketFinalizer>();
|
socket_finalizer_ = std::make_unique<SocketFinalizer>();
|
||||||
|
|
||||||
socket_ = std::make_unique<ServerSocket>();
|
socket_ = std::make_unique<ServerSocket>();
|
||||||
int new_port;
|
int port;
|
||||||
ASSIGN_OR_RETURN(new_port, socket_->StartListening(port),
|
ASSIGN_OR_RETURN(port, socket_->StartListening(0),
|
||||||
"Failed to start listening on port %i", port);
|
"Failed to start listening for connections");
|
||||||
assert(port != 0);
|
|
||||||
assert(port == new_port);
|
|
||||||
LOG_INFO("cdc_rsync_server listening on port %i", port);
|
LOG_INFO("cdc_rsync_server listening on port %i", port);
|
||||||
|
|
||||||
// This is the marker for the client, so it knows it can connect.
|
// This is the marker for the client, so it knows it can connect.
|
||||||
printf("Server is listening\n");
|
// Print port first so the client can easily parse it when it sees "Server is
|
||||||
|
// listening" without dealing with half-transmitted data.
|
||||||
|
printf("Port %i: Server is listening\n", port);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
status = socket_->WaitForConnection();
|
status = socket_->WaitForConnection();
|
||||||
@@ -607,7 +607,7 @@ absl::Status CdcRsyncServer::CreateMissingDirs() {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
absl::Status CdcRsyncServer::SendFileIndices(const char* file_type,
|
absl::Status CdcRsyncServer::SendFileIndices(const char* file_type,
|
||||||
const std::vector<T>& files) {
|
const std::vector<T>& files) {
|
||||||
LOG_INFO("Sending indices of missing files to client");
|
LOG_INFO("Sending indices of %s files to client", file_type);
|
||||||
constexpr char error_fmt[] = "Failed to send indices of %s files.";
|
constexpr char error_fmt[] = "Failed to send indices of %s files.";
|
||||||
|
|
||||||
AddFileIndicesResponse response;
|
AddFileIndicesResponse response;
|
||||||
|
|||||||
@@ -43,9 +43,10 @@ class CdcRsyncServer {
|
|||||||
// up-to-date by checking their sizes and timestamps.
|
// up-to-date by checking their sizes and timestamps.
|
||||||
bool CheckComponents(const std::vector<GameletComponent>& components);
|
bool CheckComponents(const std::vector<GameletComponent>& components);
|
||||||
|
|
||||||
// Listens to |port|, accepts a connection from the client and runs the rsync
|
// Listens to any available port, accepts a connection from the client and
|
||||||
// procedure.
|
// runs the rsync procedure. Prints "Port <n>: Server is listening" to stdout,
|
||||||
absl::Status Run(int port);
|
// so the client can retrieve the selected port.
|
||||||
|
absl::Status Run();
|
||||||
|
|
||||||
// Returns the verbosity sent from the client. 0 by default.
|
// Returns the verbosity sent from the client. 0 by default.
|
||||||
int GetVerbosity() const { return verbosity_; }
|
int GetVerbosity() const { return verbosity_; }
|
||||||
|
|||||||
@@ -62,29 +62,22 @@ ServerExitCode GetExitCode(const absl::Status& status) {
|
|||||||
|
|
||||||
int main(int argc, const char** argv) {
|
int main(int argc, const char** argv) {
|
||||||
if (argc < 5) {
|
if (argc < 5) {
|
||||||
printf("cdc_rsync_server - Remote component of cdc_rsync. Version: %s\n\n",
|
printf(R"("cdc_rsync_server - Remote component of cdc_rsync. Version: %s
|
||||||
BUILD_VERSION);
|
|
||||||
printf(
|
Usage: cdc_rsync_server <build_version> cdc_rsync_server <size> <modified_time>
|
||||||
"Usage: cdc_rsync_server <port> <build_version> cdc_rsync_server "
|
<build_version> build version embedded in the component
|
||||||
"<size> <time> \n");
|
<size> file size of the component
|
||||||
printf(" where <size> is the file size, <time> is the modified\n");
|
<modified_time> timestamp (Unix epoch) of the component)",
|
||||||
printf(
|
BUILD_VERSION);
|
||||||
" timestamp (Unix epoch) and <build_version> is the build "
|
|
||||||
"version of the server.\n");
|
|
||||||
return cdc_ft::kServerExitCodeGenericStartup;
|
|
||||||
}
|
|
||||||
|
|
||||||
int port = atoi(argv[1]);
|
|
||||||
if (port == 0) {
|
|
||||||
SendErrorMessage(absl::StrFormat("Invalid port '%s'", argv[1]).c_str());
|
|
||||||
return cdc_ft::kServerExitCodeGenericStartup;
|
return cdc_ft::kServerExitCodeGenericStartup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The rest is expected to be sets of gamelet component info consisting of
|
// The rest is expected to be sets of gamelet component info consisting of
|
||||||
// (filename, filesize, modified_time). This is used check whether the
|
// (version, filename, size, modified_time). This is used check whether the
|
||||||
// components are up-to-date.
|
// components are up-to-date.
|
||||||
std::vector<cdc_ft::GameletComponent> components =
|
std::vector<cdc_ft::GameletComponent> components =
|
||||||
cdc_ft::GameletComponent::FromCommandLineArgs(argc - 2, argv + 2);
|
cdc_ft::GameletComponent::FromCommandLineArgs(argc - 1, argv + 1);
|
||||||
|
|
||||||
cdc_ft::Log::Initialize(
|
cdc_ft::Log::Initialize(
|
||||||
std::make_unique<cdc_ft::ConsoleLog>(cdc_ft::LogLevel::kWarning));
|
std::make_unique<cdc_ft::ConsoleLog>(cdc_ft::LogLevel::kWarning));
|
||||||
@@ -94,7 +87,7 @@ int main(int argc, const char** argv) {
|
|||||||
return cdc_ft::kServerExitCodeOutOfDate;
|
return cdc_ft::kServerExitCodeOutOfDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status status = server.Run(port);
|
absl::Status status = server.Run();
|
||||||
if (status.ok()) {
|
if (status.ok()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,9 +118,8 @@ TEST_F(PortManagerTest, ReservePortLocalNetstatFails) {
|
|||||||
absl::StatusOr<int> port =
|
absl::StatusOr<int> port =
|
||||||
port_manager_.ReservePort(kTimeoutSec, ArchType::kLinux_x86_64);
|
port_manager_.ReservePort(kTimeoutSec, ArchType::kLinux_x86_64);
|
||||||
EXPECT_NOT_OK(port);
|
EXPECT_NOT_OK(port);
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(absl::StrContains(port.status().message(),
|
||||||
absl::StrContains(port.status().message(),
|
"Failed to find available local ports"));
|
||||||
"Failed to find available ports on workstation"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PortManagerTest, ReservePortRemoteNetstatFails) {
|
TEST_F(PortManagerTest, ReservePortRemoteNetstatFails) {
|
||||||
@@ -131,7 +130,7 @@ TEST_F(PortManagerTest, ReservePortRemoteNetstatFails) {
|
|||||||
port_manager_.ReservePort(kTimeoutSec, ArchType::kLinux_x86_64);
|
port_manager_.ReservePort(kTimeoutSec, ArchType::kLinux_x86_64);
|
||||||
EXPECT_NOT_OK(port);
|
EXPECT_NOT_OK(port);
|
||||||
EXPECT_TRUE(absl::StrContains(port.status().message(),
|
EXPECT_TRUE(absl::StrContains(port.status().message(),
|
||||||
"Failed to find available ports on instance"));
|
"Failed to find available remote ports"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(PortManagerTest, ReservePortRemoteNetstatTimesOut) {
|
TEST_F(PortManagerTest, ReservePortRemoteNetstatTimesOut) {
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ absl::StatusOr<int> PortManager::ReservePort(int remote_timeout_sec,
|
|||||||
local_ports,
|
local_ports,
|
||||||
FindAvailableLocalPorts(first_port_, last_port_,
|
FindAvailableLocalPorts(first_port_, last_port_,
|
||||||
ArchType::kWindows_x86_64, process_factory_),
|
ArchType::kWindows_x86_64, process_factory_),
|
||||||
"Failed to find available ports on workstation");
|
"Failed to find available local ports");
|
||||||
|
|
||||||
// Find available port on remote instance.
|
// Find available port on remote instance.
|
||||||
std::unordered_set<int> remote_ports = local_ports;
|
std::unordered_set<int> remote_ports = local_ports;
|
||||||
@@ -178,7 +178,7 @@ absl::StatusOr<int> PortManager::ReservePort(int remote_timeout_sec,
|
|||||||
FindAvailableRemotePorts(first_port_, last_port_, remote_arch_type,
|
FindAvailableRemotePorts(first_port_, last_port_, remote_arch_type,
|
||||||
process_factory_, remote_util_,
|
process_factory_, remote_util_,
|
||||||
remote_timeout_sec, steady_clock_),
|
remote_timeout_sec, steady_clock_),
|
||||||
"Failed to find available ports on instance");
|
"Failed to find available remote ports");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch shared memory.
|
// Fetch shared memory.
|
||||||
@@ -209,9 +209,9 @@ absl::StatusOr<int> PortManager::ReservePort(int remote_timeout_sec,
|
|||||||
LOG_DEBUG("Trying to reserve port %i", port);
|
LOG_DEBUG("Trying to reserve port %i", port);
|
||||||
|
|
||||||
// We have reserved this port. Double-check that it's actually not in use
|
// We have reserved this port. Double-check that it's actually not in use
|
||||||
// on both the workstation and the server.
|
// on both the local and the remote device
|
||||||
if (local_ports.find(port) == local_ports.end()) {
|
if (local_ports.find(port) == local_ports.end()) {
|
||||||
LOG_DEBUG("Port %i not available on workstation", port);
|
LOG_DEBUG("Local port %i not available", port);
|
||||||
InterlockedCompareExchange64(ts_ptr, now, port_timestamp);
|
InterlockedCompareExchange64(ts_ptr, now, port_timestamp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -221,7 +221,7 @@ absl::StatusOr<int> PortManager::ReservePort(int remote_timeout_sec,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG("Port %i is available on workstation and instance", port);
|
LOG_DEBUG("Port %i is available", port);
|
||||||
reserved_ports_.insert(port);
|
reserved_ports_.insert(port);
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
@@ -269,7 +269,7 @@ absl::StatusOr<std::unordered_set<int>> PortManager::FindAvailableLocalPorts(
|
|||||||
return WrapStatus(status, "Failed to run netstat:\n%s", errors);
|
return WrapStatus(status, "Failed to run netstat:\n%s", errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG("netstat (workstation) output:\n%s", output);
|
LOG_DEBUG("netstat (local) output:\n%s", output);
|
||||||
return FindAvailablePorts(first_port, last_port, output, arch_type);
|
return FindAvailablePorts(first_port, last_port, output, arch_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +316,7 @@ absl::StatusOr<std::unordered_set<int>> PortManager::FindAvailableRemotePorts(
|
|||||||
errors);
|
errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG("netstat (instance) output:\n%s", output);
|
LOG_DEBUG("netstat (remote) output:\n%s", output);
|
||||||
return FindAvailablePorts(first_port, last_port, output, arch_type);
|
return FindAvailablePorts(first_port, last_port, output, arch_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user