// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #define WIN32_LEAN_AND_MEAN #include #include #include #include "cdc_rsync/cdc_rsync_client.h" #include "cdc_rsync/params.h" #include "common/log.h" #include "common/status.h" #include "common/util.h" namespace { enum class ReturnCode { // No error. Will match the tool's exit code, so OK must be 0. kOk = 0, // Generic error. kGenericError = 1, // Server connection timed out. kConnectionTimeout = 2, // Connection to the server was shut down unexpectedly. kConnectionLost = 3, // Binding to the forward port failed, probably because there's another // instance of cdc_rsync running. kAddressInUse = 4, // Server deployment failed. This should be rare, it means that the server // components were successfully copied, but the up-to-date check still fails. kDeployFailed = 5, }; ReturnCode TagToMessage(cdc_ft::Tag tag, const cdc_ft::params::Parameters& params, std::string* msg) { msg->clear(); switch (tag) { case cdc_ft::Tag::kSocketEof: // Receiving pipe end was shut down unexpectedly. *msg = "The connection to the instance was shut down unexpectedly."; return ReturnCode::kConnectionLost; case cdc_ft::Tag::kAddressInUse: *msg = "Failed to establish a connection to the instance. All ports are " "already in use. This can happen if another instance of this command " "is running. Currently, only 10 simultaneous connections are " "supported."; return ReturnCode::kAddressInUse; case cdc_ft::Tag::kDeployServer: *msg = "Failed to deploy the instance components for unknown reasons. " "Please report this issue."; return ReturnCode::kDeployFailed; case cdc_ft::Tag::kConnectionTimeout: // Server connection timed out. SSH probably stale. *msg = absl::StrFormat( "Server connection timed out. Verify that host '%s' and port '%i' " "are correct, or specify a larger timeout with --contimeout.", params.user_host, params.options.port); return ReturnCode::kConnectionTimeout; case cdc_ft::Tag::kCount: return ReturnCode::kGenericError; } // Should not happen (TM). Will fall back to status message in this case. return ReturnCode::kGenericError; } } // namespace int wmain(int argc, wchar_t* argv[]) { // Convert args from wide to UTF8 strings. std::vector utf8_str_args; utf8_str_args.reserve(argc); for (int i = 0; i < argc; i++) { utf8_str_args.push_back(cdc_ft::Util::WideToUtf8Str(argv[i])); } // Convert args from UTF8 strings to UTF8 c-strings. std::vector utf8_args; utf8_args.reserve(argc); for (const auto& utf8_str_arg : utf8_str_args) { utf8_args.push_back(utf8_str_arg.c_str()); } // Read parameters from the environment and the command line. cdc_ft::params::Parameters parameters; if (!cdc_ft::params::Parse(argc, utf8_args.data(), ¶meters)) { return 1; } // Initialize logging. cdc_ft::LogLevel log_level = cdc_ft::Log::VerbosityToLogLevel(parameters.options.verbosity); cdc_ft::Log::Initialize(std::make_unique(log_level)); // Run rsync. cdc_ft::CdcRsyncClient client(parameters.options, parameters.sources, parameters.user_host, parameters.destination); absl::Status status = client.Run(); if (status.ok()) { return static_cast(ReturnCode::kOk); } // Get an error message from the tag associated with the status. std::string error_message; ReturnCode code = ReturnCode::kGenericError; absl::optional tag = cdc_ft::GetTag(status); if (tag.has_value()) { code = TagToMessage(tag.value(), parameters, &error_message); } // Fall back to status message if there was no tag. if (error_message.empty()) { error_message = status.message(); } else if (parameters.options.verbosity >= 2) { // In verbose mode, log the status as well, so nothing gets lost. LOG_ERROR("%s", status.ToString()); } if (!error_message.empty()) { fprintf(stderr, "Error: %s\n", error_message.c_str()); } return static_cast(code); }