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