blob: df4d335dbc84ccad36aa5fc838588d1b4a1299a1 [file] [log] [blame]
/*
* 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 <algorithm>
#include <iostream>
#include <iterator>
#include <optional>
#include <string>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/result.h>
#include "common/libs/fs/shared_buf.h"
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/contains.h"
#include "common/libs/utils/flag_parser.h"
#include "common/libs/utils/json.h"
#include "common/libs/utils/result.h"
#include "common/libs/utils/shared_fd_flag.h"
#include "host/commands/cvd/client.h"
#include "host/commands/cvd/fetch/fetch_cvd.h"
#include "host/commands/cvd/frontline_parser.h"
#include "host/commands/cvd/server.h"
#include "host/commands/cvd/server_constants.h"
#include "host/commands/cvd/types.h"
#include "host/libs/config/host_tools_version.h"
namespace cuttlefish {
namespace {
std::unordered_map<std::string, std::string> EnvVectorToMap(char** envp) {
std::unordered_map<std::string, std::string> env_map;
if (!envp) {
return env_map;
}
for (char** e = envp; *e != nullptr; e++) {
std::string env_var_val(*e);
auto tokens = android::base::Split(env_var_val, "=");
if (tokens.size() <= 1) {
LOG(WARNING) << "Environment var in unknown format: " << env_var_val;
continue;
}
const auto var = tokens.at(0);
tokens.erase(tokens.begin());
env_map[var] = android::base::Join(tokens, "=");
}
return env_map;
}
bool IsServerModeExpected(const SharedFD& internal_server_fd,
const std::string& exec_file) {
return internal_server_fd->IsOpen() || exec_file == "/proc/self/exe";
}
Result<void> RunServer(const SharedFD& internal_server_fd,
const SharedFD& carryover_client_fd) {
if (!internal_server_fd->IsOpen()) {
return CF_ERR(
"Expected to be in server mode, but didn't get a server "
"fd: "
<< internal_server_fd->StrError());
}
CF_EXPECT(CvdServerMain(internal_server_fd, carryover_client_fd));
return {};
}
struct ParseResult {
SharedFD internal_server_fd_;
SharedFD carryover_client_fd_;
};
Result<ParseResult> Parse(std::vector<std::string>& all_args) {
std::vector<Flag> flags;
SharedFD internal_server_fd;
flags.emplace_back(SharedFDFlag("INTERNAL_server_fd", internal_server_fd));
SharedFD carryover_client_fd;
flags.emplace_back(
SharedFDFlag("INTERNAL_carryover_client_fd", carryover_client_fd));
CF_EXPECT(ParseFlags(flags, all_args));
ParseResult result = {internal_server_fd, carryover_client_fd};
return {result};
}
Result<void> CvdMain(int argc, char** argv, char** envp) {
android::base::InitLogging(argv, android::base::StderrLogger);
cvd_common::Args all_args = ArgsToVec(argc, argv);
auto env = EnvVectorToMap(envp);
const auto host_tool_dir =
android::base::Dirname(android::base::GetExecutableDirectory());
if (android::base::Basename(all_args[0]) == "fetch_cvd") {
CF_EXPECT(FetchCvdMain(argc, argv));
return {};
}
CvdClient client;
// TODO(b/206893146): Make this decision inside the server.
if (android::base::Basename(all_args[0]) == "acloud") {
return client.HandleAcloud(all_args, env, host_tool_dir);
}
auto [internal_server_fd, carryover_client_fd] = CF_EXPECT(Parse(all_args));
if (IsServerModeExpected(internal_server_fd, all_args[0])) {
return RunServer(internal_server_fd, carryover_client_fd);
}
/*
* For now, the parser needs a running server. The parser will
* be moved to the server side, and then it won't.
*
*/
CF_EXPECT(client.ValidateServerVersion(host_tool_dir),
"Unable to ensure cvd_server is running.");
auto frontline_parser =
CF_EXPECT(FrontlineParser::Parse(client, all_args, env));
CF_EXPECT(frontline_parser != nullptr);
// Special case for `cvd kill-server`, handled by directly
// stopping the cvd_server.
std::vector<std::string> kill_server_cmds{"kill-server", "server-kill"};
std::string subcmd = frontline_parser->SubCmd().value_or("");
if (Contains(kill_server_cmds, subcmd)) {
CF_EXPECT(client.StopCvdServer(/*clear=*/true));
return {};
}
// Special case for --clean flag, used to clear any existing state.
if (frontline_parser->Clean()) {
std::cerr << "cvd invoked with --clean. Now, "
<< "stopping the cvd_server before continuing.";
CF_EXPECT(client.StopCvdServer(/*clear=*/true));
CF_EXPECT(client.ValidateServerVersion(host_tool_dir),
"Unable to ensure cvd_server is running.");
}
const auto prog_name = android::base::Basename(frontline_parser->ProgPath());
cvd_common::Args cmd_args{frontline_parser->ProgPath()};
if (frontline_parser->Help()) {
subcmd = "help";
}
if (!subcmd.empty()) {
cmd_args.emplace_back(subcmd);
}
std::copy(frontline_parser->SubCmdArgs().begin(),
frontline_parser->SubCmdArgs().end(), std::back_inserter(cmd_args));
cvd_common::Args selector_args = frontline_parser->SelectorArgs();
// Special case for `cvd version`, handled by using the version command.
if (prog_name == "cvd" && subcmd == "version") {
auto version_msg = CF_EXPECT(client.HandleVersion(host_tool_dir));
std::cout << version_msg;
return {};
}
// TODO(schuffelen): Deduplicate when calls to setenv are removed.
CF_EXPECT(client.HandleCommand(cmd_args, env, selector_args));
return {};
}
} // namespace
} // namespace cuttlefish
int main(int argc, char** argv, char** envp) {
auto result = cuttlefish::CvdMain(argc, argv, envp);
if (result.ok()) {
return 0;
} else {
std::cerr << result.error().Trace() << std::endl;
return -1;
}
}