• 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/power.h"
18 
19 #include <android-base/strings.h>
20 
21 #include <functional>
22 #include <iostream>
23 #include <mutex>
24 #include <optional>
25 #include <sstream>
26 #include <string>
27 #include <vector>
28 
29 #include "common/libs/fs/shared_buf.h"
30 #include "common/libs/utils/contains.h"
31 #include "common/libs/utils/subprocess.h"
32 #include "host/commands/cvd/flag.h"
33 #include "host/commands/cvd/selector/instance_database_types.h"
34 #include "host/commands/cvd/selector/instance_group_record.h"
35 #include "host/commands/cvd/selector/instance_record.h"
36 #include "host/commands/cvd/selector/selector_constants.h"
37 #include "host/commands/cvd/server_command/server_handler.h"
38 #include "host/commands/cvd/server_command/utils.h"
39 #include "host/commands/cvd/types.h"
40 
41 namespace cuttlefish {
42 
43 class CvdDevicePowerCommandHandler : public CvdServerHandler {
44  public:
INJECT(CvdDevicePowerCommandHandler (HostToolTargetManager & host_tool_target_manager,InstanceManager & instance_manager,SubprocessWaiter & subprocess_waiter))45   INJECT(CvdDevicePowerCommandHandler(
46       HostToolTargetManager& host_tool_target_manager,
47       InstanceManager& instance_manager, SubprocessWaiter& subprocess_waiter))
48       : host_tool_target_manager_(host_tool_target_manager),
49         instance_manager_{instance_manager},
50         subprocess_waiter_(subprocess_waiter) {
51     cvd_power_operations_["restart"] =
__anonb430b7150102(const std::string& android_host_out) 52         [this](const std::string& android_host_out) -> Result<std::string> {
53       return CF_EXPECT(RestartDeviceBin(android_host_out));
54     };
55     cvd_power_operations_["powerwash"] =
__anonb430b7150202(const std::string& android_host_out) 56         [this](const std::string& android_host_out) -> Result<std::string> {
57       return CF_EXPECT(PowerwashBin(android_host_out));
58     };
59   }
60 
CanHandle(const RequestWithStdio & request) const61   Result<bool> CanHandle(const RequestWithStdio& request) const {
62     auto invocation = ParseInvocation(request.Message());
63     return Contains(cvd_power_operations_, invocation.command);
64   }
65 
Handle(const RequestWithStdio & request)66   Result<cvd::Response> Handle(const RequestWithStdio& request) override {
67     std::unique_lock interrupt_lock(interruptible_);
68     CF_EXPECT(!interrupted_, "Interrupted");
69     CF_EXPECT(CanHandle(request));
70     CF_EXPECT(VerifyPrecondition(request));
71     const uid_t uid = request.Credentials()->uid;
72     cvd_common::Envs envs =
73         cvd_common::ConvertToEnvs(request.Message().command_request().env());
74 
75     auto [op, subcmd_args] = ParseInvocation(request.Message());
76     bool is_help = IsHelp(subcmd_args);
77 
78     // may modify subcmd_args by consuming in parsing
79     Command command =
80         is_help
81             ? CF_EXPECT(HelpCommand(request, uid, op, subcmd_args, envs))
82             : CF_EXPECT(NonHelpCommand(request, uid, op, subcmd_args, envs));
83     SubprocessOptions options;
84     CF_EXPECT(subprocess_waiter_.Setup(command.Start(options)));
85     interrupt_lock.unlock();
86 
87     auto infop = CF_EXPECT(subprocess_waiter_.Wait());
88     return ResponseFromSiginfo(infop);
89   }
90 
Interrupt()91   Result<void> Interrupt() override {
92     std::scoped_lock interrupt_lock(interruptible_);
93     interrupted_ = true;
94     CF_EXPECT(subprocess_waiter_.Interrupt());
95     return {};
96   }
97 
CmdList() const98   cvd_common::Args CmdList() const override {
99     cvd_common::Args valid_ops;
100     valid_ops.reserve(cvd_power_operations_.size());
101     for (const auto& [op, _] : cvd_power_operations_) {
102       valid_ops.push_back(op);
103     }
104     return valid_ops;
105   }
106 
107  private:
RestartDeviceBin(const std::string & android_host_out) const108   Result<std::string> RestartDeviceBin(
109       const std::string& android_host_out) const {
110     auto restart_bin = CF_EXPECT(host_tool_target_manager_.ExecBaseName({
111         .artifacts_path = android_host_out,
112         .op = "restart",
113     }));
114     return restart_bin;
115   }
116 
PowerwashBin(const std::string & android_host_out) const117   Result<std::string> PowerwashBin(const std::string& android_host_out) const {
118     auto powerwash_bin = CF_EXPECT(host_tool_target_manager_.ExecBaseName({
119         .artifacts_path = android_host_out,
120         .op = "powerwash",
121     }));
122     return powerwash_bin;
123   }
124 
HelpCommand(const RequestWithStdio & request,const uid_t uid,const std::string & op,const cvd_common::Args & subcmd_args,cvd_common::Envs envs)125   Result<Command> HelpCommand(const RequestWithStdio& request, const uid_t uid,
126                               const std::string& op,
127                               const cvd_common::Args& subcmd_args,
128                               cvd_common::Envs envs) {
129     CF_EXPECT(Contains(envs, kAndroidHostOut));
130     const auto bin_base = CF_EXPECT(GetBin(op, envs.at(kAndroidHostOut)));
131     auto cvd_power_bin_path =
132         ConcatToString(envs.at(kAndroidHostOut), "/bin/", bin_base);
133     std::string home = Contains(envs, "HOME")
134                            ? envs.at("HOME")
135                            : CF_EXPECT(SystemWideUserHome(uid));
136     envs["HOME"] = home;
137     envs[kAndroidSoongHostOut] = envs.at(kAndroidHostOut);
138     ConstructCommandParam construct_cmd_param{
139         .bin_path = cvd_power_bin_path,
140         .home = home,
141         .args = subcmd_args,
142         .envs = envs,
143         .working_dir = request.Message().command_request().working_directory(),
144         .command_name = bin_base,
145         .in = request.In(),
146         .out = request.Out(),
147         .err = request.Err()};
148     Command command = CF_EXPECT(ConstructCommand(construct_cmd_param));
149     return command;
150   }
151 
NonHelpCommand(const RequestWithStdio & request,const uid_t uid,const std::string & op,cvd_common::Args & subcmd_args,cvd_common::Envs envs)152   Result<Command> NonHelpCommand(const RequestWithStdio& request,
153                                  const uid_t uid, const std::string& op,
154                                  cvd_common::Args& subcmd_args,
155                                  cvd_common::Envs envs) {
156     // test if there is --instance_num flag
157     CvdFlag<std::int32_t> instance_num_flag("instance_num");
158     auto instance_num_opt =
159         CF_EXPECT(instance_num_flag.FilterFlag(subcmd_args));
160     selector::Queries extra_queries;
161     if (instance_num_opt) {
162       extra_queries.emplace_back(selector::kInstanceIdField, *instance_num_opt);
163     }
164     const auto& selector_opts =
165         request.Message().command_request().selector_opts();
166     const auto selector_args = cvd_common::ConvertToArgs(selector_opts.args());
167 
168     auto instance = CF_EXPECT(instance_manager_.SelectInstance(
169         selector_args, extra_queries, envs, uid));
170     const auto& instance_group = instance.ParentGroup();
171     const auto& home = instance_group.HomeDir();
172 
173     const auto& android_host_out = instance_group.HostArtifactsPath();
174     const auto bin_base = CF_EXPECT(GetBin(op, android_host_out));
175     auto cvd_power_bin_path =
176         ConcatToString(android_host_out, "/bin/", bin_base);
177 
178     cvd_common::Args cvd_env_args{subcmd_args};
179     cvd_env_args.push_back(
180         ConcatToString("--instance_num=", instance.InstanceId()));
181     envs["HOME"] = home;
182     envs[kAndroidHostOut] = android_host_out;
183     envs[kAndroidSoongHostOut] = android_host_out;
184 
185     std::stringstream command_to_issue;
186     command_to_issue << "HOME=" << home << " " << kAndroidHostOut << "="
187                      << android_host_out << " " << kAndroidSoongHostOut << "="
188                      << android_host_out << " " << cvd_power_bin_path << " ";
189     for (const auto& arg : cvd_env_args) {
190       command_to_issue << arg << " ";
191     }
192     WriteAll(request.Err(), command_to_issue.str());
193 
194     ConstructCommandParam construct_cmd_param{
195         .bin_path = cvd_power_bin_path,
196         .home = home,
197         .args = cvd_env_args,
198         .envs = envs,
199         .working_dir = request.Message().command_request().working_directory(),
200         .command_name = bin_base,
201         .in = request.In(),
202         .out = request.Out(),
203         .err = request.Err()};
204     Command command = CF_EXPECT(ConstructCommand(construct_cmd_param));
205     return command;
206   }
207 
IsHelp(const cvd_common::Args & cmd_args) const208   bool IsHelp(const cvd_common::Args& cmd_args) const {
209     if (cmd_args.empty()) {
210       return false;
211     }
212     // cvd restart/powerwash --help, --helpxml, etc or simply cvd restart
213     if (IsHelpSubcmd(cmd_args)) {
214       return true;
215     }
216     // cvd restart/powerwash help <subcommand> format
217     return (cmd_args.front() == "help");
218   }
219 
GetBin(const std::string & subcmd,const std::string & android_host_out) const220   Result<std::string> GetBin(const std::string& subcmd,
221                              const std::string& android_host_out) const {
222     CF_EXPECT(Contains(cvd_power_operations_, subcmd),
223               subcmd << " is not supported.");
224     return CF_EXPECT((cvd_power_operations_.at(subcmd))(android_host_out));
225   }
226 
227   HostToolTargetManager& host_tool_target_manager_;
228   InstanceManager& instance_manager_;
229   SubprocessWaiter& subprocess_waiter_;
230   std::mutex interruptible_;
231   bool interrupted_ = false;
232   using BinGetter = std::function<Result<std::string>(const std::string&)>;
233   std::unordered_map<std::string, BinGetter> cvd_power_operations_;
234 };
235 
236 fruit::Component<
237     fruit::Required<HostToolTargetManager, InstanceManager, SubprocessWaiter>>
CvdDevicePowerComponent()238 CvdDevicePowerComponent() {
239   return fruit::createComponent()
240       .addMultibinding<CvdServerHandler, CvdDevicePowerCommandHandler>();
241 }
242 
243 }  // namespace cuttlefish
244