blob: 7e0eda2a3d887c6c51a396cddc2793f74a1bab09 [file] [log] [blame] [edit]
/*
* Copyright (C) 2022 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 "host/commands/cvd/server_command/status.h"
#include <sys/types.h>
#include <mutex>
#include <android-base/parseint.h>
#include <android-base/strings.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/json.h"
#include "common/libs/utils/result.h"
#include "cvd_server.pb.h"
#include "host/commands/cvd/common_utils.h"
#include "host/commands/cvd/flag.h"
#include "host/commands/cvd/instance_manager.h"
#include "host/commands/cvd/server_command/server_handler.h"
#include "host/commands/cvd/server_command/status_fetcher.h"
#include "host/commands/cvd/server_command/utils.h"
#include "host/commands/cvd/types.h"
#include "host/libs/config/config_constants.h"
namespace cuttlefish {
static constexpr char kHelpMessage[] = R"(
usage: cvd <selector/driver options> <command> <args>
Selector Options:
-group_name <name> Specify the name of the instance group created
or selected.
-instance_name <name> Selects the device of the given name to perform the
commands for.
-instance_name <names> Takes the names of the devices to create within an
instance group. The 'names' is comma-separated.
Driver Options:
-verbosity=<LEVEL> Adjust Cvd verbosity level. LEVEL is Android log
severity. (Required: cvd >= v1.3)
Args:
--wait_for_launcher How many seconds to wait for the launcher to respond
to the status command. A value of zero means wait
indefinitely
(Current value: "5")
--instance_name Either instance id (e.g. 1) or internal name (e.g.
cvd-1) If not provided, the smallest id in the given
instance group is selected.
(Current value: "", Required: Android > 12)
--print If provided, prints status and instance config
information to stdout instead of CHECK.
(Current value: "false", Required: Android > 12)
--all_instances List, within the given instance group, all instances
status and instance config information.
(Current value: "false", Required: Android > 12)
--help List this message
* Only the flags in `-help` are supported. Positional
arguments are not supported.
)";
class CvdStatusCommandHandler : public CvdServerHandler {
public:
CvdStatusCommandHandler(InstanceManager& instance_manager,
HostToolTargetManager& host_tool_target_manager);
Result<bool> CanHandle(const RequestWithStdio& request) const;
Result<cvd::Response> Handle(const RequestWithStdio& request) override;
Result<void> Interrupt() override;
cvd_common::Args CmdList() const override;
private:
Result<cvd::Response> HandleHelp(const RequestWithStdio&);
InstanceManager& instance_manager_;
HostToolTargetManager& host_tool_target_manager_;
StatusFetcher status_fetcher_;
std::mutex interruptible_;
bool interrupted_ = false;
std::vector<std::string> supported_subcmds_;
};
CvdStatusCommandHandler::CvdStatusCommandHandler(
InstanceManager& instance_manager,
HostToolTargetManager& host_tool_target_manager)
: instance_manager_(instance_manager),
host_tool_target_manager_(host_tool_target_manager),
status_fetcher_(instance_manager_, host_tool_target_manager_),
supported_subcmds_{"status", "cvd_status"} {}
Result<bool> CvdStatusCommandHandler::CanHandle(
const RequestWithStdio& request) const {
auto invocation = ParseInvocation(request.Message());
return Contains(supported_subcmds_, invocation.command);
}
Result<void> CvdStatusCommandHandler::Interrupt() {
std::scoped_lock interrupt_lock(interruptible_);
interrupted_ = true;
CF_EXPECT(status_fetcher_.Interrupt());
return {};
}
static Result<RequestWithStdio> ProcessInstanceNameFlag(
const RequestWithStdio& request) {
cvd_common::Envs envs =
cvd_common::ConvertToEnvs(request.Message().command_request().env());
auto [subcmd, cmd_args] = ParseInvocation(request.Message());
CvdFlag<std::string> instance_name_flag("instance_name");
auto instance_name_flag_opt =
CF_EXPECT(instance_name_flag.FilterFlag(cmd_args));
if (!instance_name_flag_opt) {
return request;
}
std::string internal_name_or_id = *instance_name_flag_opt;
int id;
if (android::base::ParseInt(internal_name_or_id, &id)) {
envs[kCuttlefishInstanceEnvVarName] = std::to_string(id);
} else {
CF_EXPECT(android::base::StartsWith(internal_name_or_id, kCvdNamePrefix),
"--instance_name should be either cvd-<id> or id");
std::string id_part =
internal_name_or_id.substr(std::string(kCvdNamePrefix).size());
CF_EXPECT(android::base::ParseInt(id_part, &id),
"--instance_name should be either cvd-<id> or id");
envs[kCuttlefishInstanceEnvVarName] = std::to_string(id);
}
cvd_common::Args new_cmd_args{"cvd", "status"};
new_cmd_args.insert(new_cmd_args.end(), cmd_args.begin(), cmd_args.end());
const auto& selector_opts =
request.Message().command_request().selector_opts();
cvd::Request new_message = MakeRequest({
.cmd_args = new_cmd_args,
.env = envs,
.selector_args = cvd_common::ConvertToArgs(selector_opts.args()),
.working_dir = request.Message().command_request().working_directory(),
});
return RequestWithStdio(request.Client(), new_message,
request.FileDescriptors(), request.Credentials());
}
static Result<bool> HasPrint(cvd_common::Args cmd_args) {
CvdFlag<bool> print_flag("print");
auto print_flag_opt = CF_EXPECT(print_flag.FilterFlag(cmd_args));
return print_flag_opt.has_value() && *print_flag_opt;
}
Result<cvd::Response> CvdStatusCommandHandler::Handle(
const RequestWithStdio& request) {
std::unique_lock interrupt_lock(interruptible_);
CF_EXPECT(!interrupted_, "Interrupted");
CF_EXPECT(CanHandle(request));
CF_EXPECT(request.Credentials());
auto precondition_verified = VerifyPrecondition(request);
if (!precondition_verified.ok()) {
cvd::Response response;
response.mutable_command_response();
response.mutable_status()->set_code(cvd::Status::FAILED_PRECONDITION);
response.mutable_status()->set_message(
precondition_verified.error().Message());
return response;
}
CF_EXPECT_NE(request.Message().command_request().wait_behavior(),
cvd::WAIT_BEHAVIOR_START,
"cvd status shouldn't be cvd::WAIT_BEHAVIOR_START");
interrupt_lock.unlock();
auto [subcmd, cmd_args] = ParseInvocation(request.Message());
CF_EXPECT(Contains(supported_subcmds_, subcmd));
const bool has_print = CF_EXPECT(HasPrint(cmd_args));
if (CF_EXPECT(IsHelpSubcmd(cmd_args))) {
return HandleHelp(request);
}
const auto uid = CF_EXPECT(request.Credentials()).uid;
if (instance_manager_.AllGroupNames(uid).empty()) {
return CF_EXPECT(NoGroupResponse(request));
}
RequestWithStdio new_request = CF_EXPECT(ProcessInstanceNameFlag(request));
auto [entire_stderr_msg, instances_json, response] =
CF_EXPECT(status_fetcher_.FetchStatus(new_request));
if (response.status().code() != cvd::Status::OK) {
return response;
}
std::string serialized_group_json = instances_json.toStyledString();
CF_EXPECT_EQ(WriteAll(request.Err(), entire_stderr_msg),
entire_stderr_msg.size());
if (has_print) {
CF_EXPECT_EQ(WriteAll(request.Out(), serialized_group_json),
serialized_group_json.size());
}
return response;
}
std::vector<std::string> CvdStatusCommandHandler::CmdList() const {
return supported_subcmds_;
}
Result<cvd::Response> CvdStatusCommandHandler::HandleHelp(
const RequestWithStdio& request) {
cvd::Response response;
response.mutable_command_response(); // Sets oneof member
response.mutable_status()->set_code(cvd::Status::OK);
CF_EXPECT_EQ(WriteAll(request.Out(), kHelpMessage),
strnlen(kHelpMessage, sizeof(kHelpMessage) - 1));
return response;
}
std::unique_ptr<CvdServerHandler> NewCvdStatusCommandHandler(
InstanceManager& instance_manager,
HostToolTargetManager& host_tool_target_manager) {
return std::unique_ptr<CvdServerHandler>(
new CvdStatusCommandHandler(instance_manager, host_tool_target_manager));
}
} // namespace cuttlefish