314 lines
11 KiB
C++
314 lines
11 KiB
C++
|
|
/*
|
||
|
|
* 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 "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 "common/libs/utils/tee_logging.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/frontline_parser.h"
|
||
|
|
#include "host/commands/cvd/handle_reset.h"
|
||
|
|
#include "host/commands/cvd/logger.h"
|
||
|
|
#include "host/commands/cvd/reset_client_utils.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 std::string& exec_file) {
|
||
|
|
return exec_file == kServerExecPath;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct RunServerParam {
|
||
|
|
SharedFD internal_server_fd;
|
||
|
|
SharedFD carryover_client_fd;
|
||
|
|
std::optional<SharedFD> memory_carryover_fd;
|
||
|
|
/**
|
||
|
|
* Cvd server usually prints out in the client's stream. However,
|
||
|
|
* after Exec(), the client stdout and stderr becomes unreachable by
|
||
|
|
* LOG(ERROR), etc.
|
||
|
|
*
|
||
|
|
* Thus, in that case, the client fd is passed to print Exec() log
|
||
|
|
* on it.
|
||
|
|
*
|
||
|
|
*/
|
||
|
|
SharedFD carryover_stderr_fd;
|
||
|
|
};
|
||
|
|
Result<void> RunServer(const RunServerParam& fds) {
|
||
|
|
if (!fds.internal_server_fd->IsOpen()) {
|
||
|
|
return CF_ERR(
|
||
|
|
"Expected to be in server mode, but didn't get a server "
|
||
|
|
"fd: "
|
||
|
|
<< fds.internal_server_fd->StrError());
|
||
|
|
}
|
||
|
|
std::unique_ptr<ServerLogger> server_logger =
|
||
|
|
std::make_unique<ServerLogger>();
|
||
|
|
CF_EXPECT(server_logger != nullptr, "ServerLogger memory allocation failed.");
|
||
|
|
|
||
|
|
std::unique_ptr<ServerLogger::ScopedLogger> scoped_logger;
|
||
|
|
if (fds.carryover_stderr_fd->IsOpen()) {
|
||
|
|
scoped_logger = std::make_unique<ServerLogger::ScopedLogger>(
|
||
|
|
std::move(server_logger->LogThreadToFd(fds.carryover_stderr_fd)));
|
||
|
|
}
|
||
|
|
if (fds.memory_carryover_fd && !(*fds.memory_carryover_fd)->IsOpen()) {
|
||
|
|
LOG(ERROR) << "Memory carryover file is supposed to be open but is not.";
|
||
|
|
}
|
||
|
|
CF_EXPECT(CvdServerMain({.internal_server_fd = fds.internal_server_fd,
|
||
|
|
.carryover_client_fd = fds.carryover_client_fd,
|
||
|
|
.memory_carryover_fd = fds.memory_carryover_fd,
|
||
|
|
.server_logger = std::move(server_logger),
|
||
|
|
.scoped_logger = std::move(scoped_logger)}));
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
struct ParseResult {
|
||
|
|
SharedFD internal_server_fd;
|
||
|
|
SharedFD carryover_client_fd;
|
||
|
|
std::optional<SharedFD> memory_carryover_fd;
|
||
|
|
SharedFD carryover_stderr_fd;
|
||
|
|
};
|
||
|
|
|
||
|
|
Result<ParseResult> ParseIfServer(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));
|
||
|
|
SharedFD carryover_stderr_fd;
|
||
|
|
flags.emplace_back(
|
||
|
|
SharedFDFlag("INTERNAL_carryover_stderr_fd", carryover_stderr_fd));
|
||
|
|
SharedFD memory_carryover_fd;
|
||
|
|
flags.emplace_back(
|
||
|
|
SharedFDFlag("INTERNAL_memory_carryover_fd", memory_carryover_fd));
|
||
|
|
CF_EXPECT(ParseFlags(flags, all_args));
|
||
|
|
std::optional<SharedFD> memory_carryover_fd_opt;
|
||
|
|
if (memory_carryover_fd->IsOpen()) {
|
||
|
|
memory_carryover_fd_opt = std::move(memory_carryover_fd);
|
||
|
|
}
|
||
|
|
ParseResult result = {
|
||
|
|
.internal_server_fd = internal_server_fd,
|
||
|
|
.carryover_client_fd = carryover_client_fd,
|
||
|
|
.memory_carryover_fd = memory_carryover_fd_opt,
|
||
|
|
.carryover_stderr_fd = carryover_stderr_fd,
|
||
|
|
};
|
||
|
|
return {result};
|
||
|
|
}
|
||
|
|
|
||
|
|
Result<FlagCollection> CvdFlags() {
|
||
|
|
FlagCollection cvd_flags;
|
||
|
|
cvd_flags.EnrollFlag(CvdFlag<bool>("clean", false));
|
||
|
|
cvd_flags.EnrollFlag(CvdFlag<bool>("help", false));
|
||
|
|
return cvd_flags;
|
||
|
|
}
|
||
|
|
|
||
|
|
Result<bool> FilterDriverHelpOptions(const FlagCollection& cvd_flags,
|
||
|
|
cvd_common::Args& cvd_args) {
|
||
|
|
auto help_flag = CF_EXPECT(cvd_flags.GetFlag("help"));
|
||
|
|
bool is_help = CF_EXPECT(help_flag.CalculateFlag<bool>(cvd_args));
|
||
|
|
return is_help;
|
||
|
|
}
|
||
|
|
|
||
|
|
cvd_common::Args AllArgs(const std::string& prog_path,
|
||
|
|
const cvd_common::Args& cvd_args,
|
||
|
|
const std::optional<std::string>& subcmd,
|
||
|
|
const cvd_common::Args& subcmd_args) {
|
||
|
|
std::vector<std::string> all_args;
|
||
|
|
all_args.push_back(prog_path);
|
||
|
|
all_args.insert(all_args.end(), cvd_args.begin(), cvd_args.end());
|
||
|
|
if (subcmd) {
|
||
|
|
all_args.push_back(*subcmd);
|
||
|
|
}
|
||
|
|
all_args.insert(all_args.end(), subcmd_args.begin(), subcmd_args.end());
|
||
|
|
return all_args;
|
||
|
|
}
|
||
|
|
|
||
|
|
struct ClientCommandCheckResult {
|
||
|
|
bool was_client_command_;
|
||
|
|
cvd_common::Args new_all_args;
|
||
|
|
};
|
||
|
|
Result<ClientCommandCheckResult> HandleClientCommands(
|
||
|
|
CvdClient& client, const cvd_common::Args& all_args) {
|
||
|
|
ClientCommandCheckResult output;
|
||
|
|
std::vector<std::string> client_internal_commands{"kill-server",
|
||
|
|
"server-kill", "reset"};
|
||
|
|
FlagCollection cvd_flags = CF_EXPECT(CvdFlags());
|
||
|
|
FrontlineParser::ParserParam client_param{
|
||
|
|
.server_supported_subcmds = std::vector<std::string>{},
|
||
|
|
.internal_cmds = client_internal_commands,
|
||
|
|
.all_args = all_args,
|
||
|
|
.cvd_flags = cvd_flags};
|
||
|
|
auto client_parser_result = FrontlineParser::Parse(client_param);
|
||
|
|
if (!client_parser_result.ok()) {
|
||
|
|
return ClientCommandCheckResult{.was_client_command_ = false,
|
||
|
|
.new_all_args = all_args};
|
||
|
|
}
|
||
|
|
|
||
|
|
auto client_parser = std::move(*client_parser_result);
|
||
|
|
CF_EXPECT(client_parser != nullptr);
|
||
|
|
auto cvd_args = client_parser->CvdArgs();
|
||
|
|
auto is_help = CF_EXPECT(FilterDriverHelpOptions(cvd_flags, cvd_args));
|
||
|
|
output.new_all_args =
|
||
|
|
AllArgs(client_parser->ProgPath(), cvd_args, client_parser->SubCmd(),
|
||
|
|
client_parser->SubCmdArgs());
|
||
|
|
output.was_client_command_ = (!is_help && client_parser->SubCmd());
|
||
|
|
if (!output.was_client_command_) {
|
||
|
|
// could be simply "cvd"
|
||
|
|
output.new_all_args = cvd_common::Args{"cvd", "help"};
|
||
|
|
return output;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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 = client_parser->SubCmd().value_or("");
|
||
|
|
if (Contains(kill_server_cmds, subcmd)) {
|
||
|
|
CF_EXPECT(client.StopCvdServer(/*clear=*/true));
|
||
|
|
return output;
|
||
|
|
}
|
||
|
|
CF_EXPECT_EQ(subcmd, "reset", "unsupported subcmd: " << subcmd);
|
||
|
|
CF_EXPECT(HandleReset(client, client_parser->SubCmdArgs()));
|
||
|
|
return output;
|
||
|
|
}
|
||
|
|
|
||
|
|
Result<void> CvdMain(int argc, char** argv, char** envp) {
|
||
|
|
android::base::InitLogging(argv, android::base::StderrLogger);
|
||
|
|
|
||
|
|
cvd_common::Args all_args = ArgsToVec(argc, argv);
|
||
|
|
CF_EXPECT(!all_args.empty());
|
||
|
|
|
||
|
|
auto env = EnvVectorToMap(envp);
|
||
|
|
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (IsServerModeExpected(all_args[0])) {
|
||
|
|
auto parsed_fds = CF_EXPECT(ParseIfServer(all_args));
|
||
|
|
|
||
|
|
return RunServer({.internal_server_fd = parsed_fds.internal_server_fd,
|
||
|
|
.carryover_client_fd = parsed_fds.carryover_client_fd,
|
||
|
|
.memory_carryover_fd = parsed_fds.memory_carryover_fd,
|
||
|
|
.carryover_stderr_fd = parsed_fds.carryover_stderr_fd});
|
||
|
|
}
|
||
|
|
|
||
|
|
CF_EXPECT_EQ(android::base::Basename(all_args[0]), "cvd");
|
||
|
|
|
||
|
|
// TODO(kwstephenkim): --help should be handled here.
|
||
|
|
// And, the FrontlineParser takes any positional argument as
|
||
|
|
// a valid subcommand.
|
||
|
|
|
||
|
|
auto [was_client_command, new_all_args] =
|
||
|
|
CF_EXPECT(HandleClientCommands(client, all_args));
|
||
|
|
if (was_client_command) {
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
/*
|
||
|
|
* 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(),
|
||
|
|
"Unable to ensure cvd_server is running.");
|
||
|
|
|
||
|
|
std::vector<std::string> version_command{"version"};
|
||
|
|
FlagCollection cvd_flags = CF_EXPECT(CvdFlags());
|
||
|
|
FrontlineParser::ParserParam version_param{
|
||
|
|
.server_supported_subcmds = std::vector<std::string>{},
|
||
|
|
.internal_cmds = version_command,
|
||
|
|
.all_args = new_all_args,
|
||
|
|
.cvd_flags = cvd_flags};
|
||
|
|
auto version_parser_result = FrontlineParser::Parse(version_param);
|
||
|
|
if (version_parser_result.ok()) {
|
||
|
|
auto version_parser = std::move(*version_parser_result);
|
||
|
|
CF_EXPECT(version_parser != nullptr);
|
||
|
|
const auto subcmd = version_parser->SubCmd().value_or("");
|
||
|
|
if (subcmd == "version") {
|
||
|
|
auto version_msg = CF_EXPECT(client.HandleVersion());
|
||
|
|
std::cout << version_msg;
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
CF_EXPECT(subcmd.empty(),
|
||
|
|
"subcmd is expected to be \"\" but is " << subcmd);
|
||
|
|
}
|
||
|
|
|
||
|
|
const cvd_common::Args new_cmd_args{"cvd", "process"};
|
||
|
|
CF_EXPECT(!new_all_args.empty());
|
||
|
|
const cvd_common::Args new_selector_args{new_all_args.begin(),
|
||
|
|
new_all_args.end()};
|
||
|
|
// TODO(schuffelen): Deduplicate when calls to setenv are removed.
|
||
|
|
CF_EXPECT(client.HandleCommand(new_cmd_args, env, new_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;
|
||
|
|
}
|
||
|
|
}
|