• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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