| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * 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. |
| */ |
| |
| #include <unistd.h> |
| |
| #include <cstdio> |
| #include <iostream> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <vector> |
| |
| #include <android-base/file.h> |
| #include <android-base/logging.h> |
| #include <android-base/scopeguard.h> |
| #include <android-base/strings.h> |
| |
| #include "common/libs/utils/contains.h" |
| #include "common/libs/utils/environment.h" |
| #include "common/libs/utils/subprocess.h" |
| #include "host/commands/cvd/client.h" |
| #include "host/commands/cvd/common_utils.h" |
| #include "host/commands/cvd/fetch/fetch_cvd.h" |
| #include "host/commands/cvd/flag.h" |
| #include "host/commands/cvd/frontline_parser.h" |
| #include "host/commands/cvd/metrics/cvd_metrics_api.h" |
| #include "host/commands/cvd/run_server.h" |
| #include "host/commands/cvd/server_constants.h" |
| #include "host/libs/config/host_tools_version.h" |
| |
| namespace cuttlefish { |
| namespace { |
| |
| /** |
| * Returns --verbosity value if ever exist in the entire commandline args |
| * |
| * Note that this will also pick up from the subtool arguments: |
| * e.g. cvd start --verbosity=DEBUG |
| * |
| * This may be incorrect as the verbosity should be ideally applied to the |
| * launch_cvd/cvd_internal_start only. |
| * |
| * However, parsing the --verbosity flag only from the driver is quite |
| * complicated as we do not know the full list of the subcommands, |
| * the subcommands flags, and even the selector/driver flags. |
| * |
| * Thus, we live with the corner case for now. |
| */ |
| android::base::LogSeverity CvdVerbosityOption(const int argc, char** argv) { |
| cvd_common::Args all_args = ArgsToVec(argc, argv); |
| std::string verbosity_flag_value; |
| std::vector<Flag> verbosity_flag{ |
| GflagsCompatFlag("verbosity", verbosity_flag_value)}; |
| if (!ParseFlags(verbosity_flag, all_args).ok()) { |
| LOG(ERROR) << "Verbosity flag parsing failed, so use the default value."; |
| return GetMinimumVerbosity(); |
| } |
| if (verbosity_flag_value.empty()) { |
| return GetMinimumVerbosity(); |
| } |
| auto encoded_verbosity = EncodeVerbosity(verbosity_flag_value); |
| return (encoded_verbosity.ok() ? *encoded_verbosity : GetMinimumVerbosity()); |
| } |
| |
| /** |
| * Terminates a cvd server listening on "cvd_server" |
| * |
| * So far, the server processes across users were listing on the "cvd_server" |
| * socket. And, so far, we had one user. Now, we have multiple users. Each |
| * server listens to cvd_server_<uid>. The thing is if there is a server process |
| * started out of an old executable it will be listening to "cvd_server," and |
| * thus we should kill the server process first. |
| */ |
| Result<void> KillOldServer() { |
| CvdClient client_to_old_server(kCvdDefaultVerbosity, "cvd_server"); |
| auto result = client_to_old_server.StopCvdServer(/*clear=*/true); |
| if (!result.ok()) { |
| LOG(ERROR) << "Old server listening on \"cvd_server\" socket " |
| << "must be killed first but failed to terminate it."; |
| LOG(ERROR) << "Perhaps, try cvd reset -y"; |
| CF_EXPECT(std::move(result)); |
| } |
| return {}; |
| } |
| |
| Result<void> CvdMain(int argc, char** argv, char** envp, |
| const android::base::LogSeverity verbosity) { |
| CF_EXPECT(KillOldServer()); |
| |
| cvd_common::Args all_args = ArgsToVec(argc, argv); |
| CF_EXPECT(!all_args.empty()); |
| |
| if (IsServerModeExpected(all_args[0])) { |
| auto parsed = CF_EXPECT(ParseIfServer(all_args)); |
| return RunServer( |
| {.internal_server_fd = parsed.internal_server_fd, |
| .carryover_client_fd = parsed.carryover_client_fd, |
| .memory_carryover_fd = parsed.memory_carryover_fd, |
| .verbosity_level = parsed.verbosity_level, |
| .acloud_translator_optout = parsed.acloud_translator_optout, |
| .restarted_in_process = parsed.restarted_in_process}); |
| } |
| |
| auto env = EnvpToMap(envp); |
| CvdMetrics::SendCvdMetrics(all_args); |
| |
| if (android::base::Basename(all_args[0]) == "fetch_cvd") { |
| CF_EXPECT(FetchCvdMain(argc, argv)); |
| return {}; |
| } |
| |
| CvdClient client(verbosity); |
| |
| // TODO(b/206893146): Make this decision inside the server. |
| if (android::base::Basename(all_args[0]) == "acloud") { |
| return client.HandleAcloud(all_args, env); |
| } |
| |
| if (android::base::Basename(all_args[0]) == "cvd") { |
| CF_EXPECT(client.HandleCvdCommand(all_args, env)); |
| return {}; |
| } |
| |
| CF_EXPECT(client.ValidateServerVersion(), |
| "Unable to ensure cvd_server is running."); |
| CF_EXPECT(client.HandleCommand(all_args, env, {})); |
| |
| return {}; |
| } |
| |
| /** |
| * Returns the URL as a colored string |
| * |
| * If stderr is not terminal, no color. |
| * If stderr is a tty, tries to use ".deb" file color |
| * If .deb is not available in LS_COLORS, uses .zip |
| * color. If none are available, use a default color that |
| * is red. |
| */ |
| std::string ColoredUrl(const std::string& url) { |
| if (!isatty(STDERR_FILENO)) { |
| return url; |
| } |
| std::string coloring_prefix = "\033[01;31m"; |
| std::string output; |
| auto ls_colors = StringFromEnv("LS_COLORS", ""); |
| std::vector<std::string> colors_vec = android::base::Tokenize(ls_colors, ":"); |
| std::unordered_map<std::string, std::string> colors; |
| for (const auto& color_entry : colors_vec) { |
| std::vector<std::string> tokenized = |
| android::base::Tokenize(color_entry, "="); |
| if (tokenized.size() != 2) { |
| continue; |
| } |
| colors[tokenized.front()] = tokenized.back(); |
| } |
| |
| android::base::ScopeGuard return_action([&coloring_prefix, url, &output]() { |
| static constexpr char kRestoreColor[] = "\033[0m"; |
| output = fmt::format("{}{}{}", coloring_prefix, url, kRestoreColor); |
| }); |
| auto deb_color_itr = colors.find("*.deb"); |
| auto zip_color_itr = colors.find("*.zip"); |
| if (deb_color_itr == colors.end() && zip_color_itr == colors.end()) { |
| return output; |
| } |
| coloring_prefix = fmt::format( |
| "{}{}m", "\033[", |
| (deb_color_itr == colors.end() ? colors["*.zip"] : colors["*.deb"])); |
| return output; |
| } |
| |
| } // namespace |
| } // namespace cuttlefish |
| |
| int main(int argc, char** argv, char** envp) { |
| android::base::LogSeverity verbosity = |
| cuttlefish::CvdVerbosityOption(argc, argv); |
| android::base::InitLogging(argv, android::base::StderrLogger); |
| // set verbosity for this process |
| cuttlefish::SetMinimumVerbosity(verbosity); |
| |
| auto result = cuttlefish::CvdMain(argc, argv, envp, verbosity); |
| if (result.ok()) { |
| return 0; |
| } else { |
| // TODO: we should not print the stack trace, instead, we should rely on |
| // each handler to print the error message directly in the client's |
| // std::cerr. We print the stack trace only in the verbose mode. |
| std::cerr << result.error().FormatForEnv() << std::endl; |
| // TODO(kwstephenkim): better coloring |
| constexpr char kUserReminder[] = |
| R"( If the error above is unclear, please copy the text into an issue at:)"; |
| constexpr char kCuttlefishBugUrl[] = "http://go/cuttlefish-bug"; |
| std::cerr << std::endl << kUserReminder << std::endl; |
| std::cerr << " " << cuttlefish::ColoredUrl(kCuttlefishBugUrl) |
| << std::endl |
| << std::endl; |
| return -1; |
| } |
| } |