1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "host/commands/cvd/server_command/acloud_command.h"
18
19 #include <atomic>
20 #include <mutex>
21
22 #include <android-base/file.h>
23 #include <android-base/strings.h>
24 #include <fruit/fruit.h>
25
26 #include "common/libs/fs/shared_buf.h"
27 #include "common/libs/fs/shared_fd.h"
28 #include "common/libs/utils/result.h"
29 #include "cvd_server.pb.h"
30 #include "host/commands/cvd/instance_lock.h"
31 #include "host/commands/cvd/server_command/acloud_common.h"
32 #include "host/commands/cvd/server_command/server_handler.h"
33 #include "host/commands/cvd/server_command/utils.h"
34 #include "host/commands/cvd/types.h"
35
36 namespace cuttlefish {
37
38 class AcloudCommand : public CvdServerHandler {
39 public:
INJECT(AcloudCommand (CommandSequenceExecutor & executor,ConvertAcloudCreateCommand & converter))40 INJECT(AcloudCommand(CommandSequenceExecutor& executor,
41 ConvertAcloudCreateCommand& converter))
42 : executor_(executor), converter_(converter) {}
43 ~AcloudCommand() = default;
44
CanHandle(const RequestWithStdio & request) const45 Result<bool> CanHandle(const RequestWithStdio& request) const override {
46 auto invocation = ParseInvocation(request.Message());
47 if (invocation.arguments.size() >= 2) {
48 if (invocation.command == "acloud" &&
49 invocation.arguments[0] == "translator") {
50 return false;
51 }
52 }
53 return invocation.command == "acloud";
54 }
55
CmdList() const56 cvd_common::Args CmdList() const override { return {"acloud"}; }
57
Handle(const RequestWithStdio & request)58 Result<cvd::Response> Handle(const RequestWithStdio& request) override {
59 std::unique_lock interrupt_lock(interrupt_mutex_);
60 if (interrupted_) {
61 return CF_ERR("Interrupted");
62 }
63 CF_EXPECT(CanHandle(request));
64 CF_EXPECT(IsSubOperationSupported(request));
65 auto converted = CF_EXPECT(converter_.Convert(request));
66 interrupt_lock.unlock();
67 CF_EXPECT(executor_.Execute(converted.prep_requests, request.Err()));
68 auto start_response =
69 CF_EXPECT(executor_.ExecuteOne(converted.start_request, request.Err()));
70
71 if (converter_.FetchCommandString() != "") {
72 // has cvd fetch command, update the fetch cvd command file
73 using android::base::WriteStringToFile;
74 CF_EXPECT(WriteStringToFile(converter_.FetchCommandString(),
75 converter_.FetchCvdArgsFile()),
76 true);
77 }
78
79 auto handle_response_result = HandleStartResponse(start_response);
80 if (handle_response_result.ok()) {
81 // print
82 std::optional<SharedFD> fd_opt;
83 if (converter_.Verbose()) {
84 fd_opt = request.Err();
85 }
86 auto write_result = PrintBriefSummary(*handle_response_result, fd_opt);
87 if (!write_result.ok()) {
88 LOG(ERROR) << "Failed to write the start response report.";
89 }
90 } else {
91 LOG(ERROR) << "Failed to analyze the cvd start response.";
92 }
93 cvd::Response response;
94 response.mutable_command_response();
95 return response;
96 }
Interrupt()97 Result<void> Interrupt() override {
98 std::scoped_lock interrupt_lock(interrupt_mutex_);
99 interrupted_ = true;
100 CF_EXPECT(executor_.Interrupt());
101 return {};
102 }
103
104 private:
105 Result<cvd::InstanceGroupInfo> HandleStartResponse(
106 const cvd::Response& start_response);
107 Result<void> PrintBriefSummary(const cvd::InstanceGroupInfo& group_info,
108 std::optional<SharedFD> stream_fd) const;
109
110 CommandSequenceExecutor& executor_;
111 ConvertAcloudCreateCommand& converter_;
112
113 std::mutex interrupt_mutex_;
114 bool interrupted_ = false;
115 };
116
HandleStartResponse(const cvd::Response & start_response)117 Result<cvd::InstanceGroupInfo> AcloudCommand::HandleStartResponse(
118 const cvd::Response& start_response) {
119 CF_EXPECT(start_response.has_command_response(),
120 "cvd start did ont return a command response.");
121 const auto& start_command_response = start_response.command_response();
122 CF_EXPECT(start_command_response.has_instance_group_info(),
123 "cvd start command response did not return instance_group_info.");
124 cvd::InstanceGroupInfo group_info =
125 start_command_response.instance_group_info();
126 return group_info;
127 }
128
PrintBriefSummary(const cvd::InstanceGroupInfo & group_info,std::optional<SharedFD> stream_fd) const129 Result<void> AcloudCommand::PrintBriefSummary(
130 const cvd::InstanceGroupInfo& group_info,
131 std::optional<SharedFD> stream_fd) const {
132 if (!stream_fd) {
133 return {};
134 }
135 SharedFD fd = *stream_fd;
136 std::stringstream ss;
137 const std::string& group_name = group_info.group_name();
138 CF_EXPECT_EQ(group_info.home_directories().size(), 1);
139 const std::string home_dir = (group_info.home_directories())[0];
140 std::vector<std::string> instance_names;
141 std::vector<unsigned> instance_ids;
142 instance_names.reserve(group_info.instances().size());
143 instance_ids.reserve(group_info.instances().size());
144 for (const auto& instance : group_info.instances()) {
145 instance_names.push_back(instance.name());
146 instance_ids.push_back(instance.instance_id());
147 }
148 ss << std::endl << "Created instance group: " << group_name << std::endl;
149 for (size_t i = 0; i != instance_ids.size(); i++) {
150 std::string device_name = group_name + "-" + instance_names[i];
151 ss << " " << device_name << " (local-instance-" << instance_ids[i] << ")"
152 << std::endl;
153 }
154 ss << std::endl
155 << "acloud list or cvd fleet for more information." << std::endl;
156 auto n_write = WriteAll(*stream_fd, ss.str());
157 CF_EXPECT_EQ(n_write, ss.str().size());
158 return {};
159 }
160
161 fruit::Component<
162 fruit::Required<CommandSequenceExecutor, ConvertAcloudCreateCommand>>
AcloudCommandComponent()163 AcloudCommandComponent() {
164 return fruit::createComponent()
165 .addMultibinding<CvdServerHandler, AcloudCommand>();
166 }
167
168 } // namespace cuttlefish
169