1 /*
2 * Copyright (C) 2022 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.h"
18
19 #include <sys/types.h>
20
21 #include <cstdio>
22 #include <iostream>
23
24 #include <android-base/file.h>
25 #include <fruit/fruit.h>
26
27 #include "common/libs/fs/shared_buf.h"
28 #include "common/libs/fs/shared_fd.h"
29 #include "common/libs/utils/contains.h"
30 #include "common/libs/utils/result.h"
31 #include "cvd_server.pb.h"
32 #include "host/commands/cvd/common_utils.h"
33 #include "host/commands/cvd/epoll_loop.h"
34 #include "host/commands/cvd/frontline_parser.h"
35 #include "host/commands/cvd/instance_manager.h"
36 #include "host/commands/cvd/server_command/components.h"
37 #include "host/commands/cvd/server_command/utils.h"
38 #include "host/commands/cvd/types.h"
39 #include "host/libs/web/build_api.h"
40
41 namespace cuttlefish {
42 namespace {
43
44 constexpr char kRestartServerHelpMessage[] =
45 R"(Cuttlefish Virtual Device (CVD) CLI.
46
47 usage: cvd restart-server <common args> <mode> <mode args>
48
49 Common Args:
50 --help Print out this message
51 --verbose Control verbose mode
52
53 Modes:
54 match-client Use the client executable.
55 latest Download the latest executable
56 reuse-server Use the server executable.
57 )";
58
LatestCvdAsFd(BuildApi & build_api)59 Result<SharedFD> LatestCvdAsFd(BuildApi& build_api) {
60 static constexpr char kBuild[] = "aosp-master";
61 static constexpr char kTarget[] = "aosp_cf_x86_64_phone-userdebug";
62 auto latest = CF_EXPECT(build_api.LatestBuildId(kBuild, kTarget));
63 DeviceBuild build{latest, kTarget};
64
65 auto fd = SharedFD::MemfdCreate("cvd");
66 CF_EXPECT(fd->IsOpen(), "MemfdCreate failed: " << fd->StrError());
67
68 auto write = [fd](char* data, size_t size) -> bool {
69 if (size == 0) {
70 return true;
71 }
72 auto written = WriteAll(fd, data, size);
73 if (written != size) {
74 LOG(ERROR) << "Failed to persist data: " << fd->StrError();
75 return false;
76 }
77 return true;
78 };
79 CF_EXPECT(build_api.ArtifactToCallback(build, "cvd", write));
80
81 return fd;
82 }
83
84 class CvdRestartHandler : public CvdServerHandler {
85 public:
INJECT(CvdRestartHandler (BuildApi & build_api,CvdServer & server,InstanceManager & instance_manager))86 INJECT(CvdRestartHandler(BuildApi& build_api, CvdServer& server,
87 InstanceManager& instance_manager))
88 : build_api_(build_api),
89 supported_modes_({"match-client", "latest", "reuse-server"}),
90 server_(server),
91 instance_manager_(instance_manager) {
92 flags_.EnrollFlag(CvdFlag<bool>("help", false));
93 flags_.EnrollFlag(CvdFlag<bool>("verbose", false));
94 // If the fla is false, the request will fail if there's on-going requests
95 // If true, calls Stop()
96 flags_.EnrollFlag(CvdFlag<bool>("force", true));
97 }
98
CanHandle(const RequestWithStdio & request) const99 Result<bool> CanHandle(const RequestWithStdio& request) const override {
100 auto invocation = ParseInvocation(request.Message());
101 return android::base::Basename(invocation.command) == kRestartServer;
102 }
103
Handle(const RequestWithStdio & request)104 Result<cvd::Response> Handle(const RequestWithStdio& request) override {
105 CF_EXPECT(CanHandle(request));
106 cvd::Response response;
107 if (request.Message().has_shutdown_request()) {
108 response.mutable_shutdown_response();
109 } else {
110 CF_EXPECT(
111 request.Message().has_command_request(),
112 "cvd restart request must be either command or shutdown request.");
113 response.mutable_command_response();
114 }
115 // all_args[0] = "cvd", all_args[1] = "restart_server"
116 cvd_common::Args all_args =
117 cvd_common::ConvertToArgs(request.Message().command_request().args());
118 CF_EXPECT_GE(all_args.size(), 2);
119 CF_EXPECT_EQ(all_args.at(0), "cvd");
120 CF_EXPECT_EQ(all_args.at(1), kRestartServer);
121 // erase the first item, "cvd"
122 all_args.erase(all_args.begin());
123
124 auto parsed = CF_EXPECT(Parse(all_args));
125 if (parsed.help) {
126 const std::string help_message(kRestartServerHelpMessage);
127 WriteAll(request.Out(), help_message);
128 return response;
129 }
130
131 // On CF_ERR, the locks will be released automatically
132 WriteAll(request.Out(), "Stopping the cvd_server.\n");
133 server_.Stop();
134
135 CF_EXPECT(request.Credentials() != std::nullopt);
136 const uid_t client_uid = request.Credentials()->uid;
137 auto json_string =
138 CF_EXPECT(SerializedInstanceDatabaseToString(client_uid));
139 std::optional<SharedFD> mem_fd;
140 if (instance_manager_.HasInstanceGroups(client_uid)) {
141 mem_fd = CF_EXPECT(CreateMemFileWithSerializedDb(json_string));
142 CF_EXPECT(mem_fd != std::nullopt && (*mem_fd)->IsOpen(),
143 "mem file not open?");
144 }
145
146 if (parsed.verbose && mem_fd) {
147 PrintFileLink(request.Err(), *mem_fd);
148 }
149
150 const std::string subcmd = parsed.subcmd.value_or("reuse-server");
151 SharedFD new_exe;
152 CF_EXPECT(Contains(supported_modes_, subcmd),
153 "unsupported subcommand :" << subcmd);
154 if (subcmd == "match-client") {
155 CF_EXPECT(request.Extra(), "match-client requires the file descriptor.");
156 new_exe = *request.Extra();
157 } else if (subcmd == "latest") {
158 new_exe = CF_EXPECT(LatestCvdAsFd(build_api_));
159 } else if (subcmd == "reuse-server") {
160 new_exe = CF_EXPECT(NewExecFromPath(request, kServerExecPath));
161 } else {
162 return CF_ERR("unsupported subcommand");
163 }
164
165 CF_EXPECT(server_.Exec({.new_exe = new_exe,
166 .carryover_client_fd = request.Client(),
167 .client_stderr_fd = request.Err(),
168 .in_memory_data_fd = mem_fd,
169 .verbose = parsed.verbose}));
170 return CF_ERR("Should be unreachable");
171 }
172
Interrupt()173 Result<void> Interrupt() override { return CF_ERR("Can't interrupt"); }
CmdList() const174 cvd_common::Args CmdList() const override { return {kRestartServer}; }
175 constexpr static char kRestartServer[] = "restart-server";
176
177 private:
178 struct Parsed {
179 bool help;
180 bool verbose;
181 std::optional<std::string> subcmd;
182 std::optional<std::string> exec_path;
183 };
Parse(const cvd_common::Args & args)184 Result<Parsed> Parse(const cvd_common::Args& args) {
185 // it's ugly but let's reuse the frontline parser
186 auto parser_with_result =
187 CF_EXPECT(FrontlineParser::Parse({.internal_cmds = supported_modes_,
188 .all_args = args,
189 .cvd_flags = flags_}));
190 CF_EXPECT(parser_with_result != nullptr,
191 "FrontlineParser::Parse() returned nullptr");
192 // If there was a subcmd, the flags for the subcmd is in SubCmdArgs().
193 // If not, the flags for restart-server would be in CvdArgs()
194 std::optional<std::string> subcmd_opt = parser_with_result->SubCmd();
195 cvd_common::Args subcmd_args =
196 (subcmd_opt ? parser_with_result->SubCmdArgs()
197 : parser_with_result->CvdArgs());
198 auto name_flag_map = CF_EXPECT(flags_.CalculateFlags(subcmd_args));
199 CF_EXPECT(Contains(name_flag_map, "help"));
200 CF_EXPECT(Contains(name_flag_map, "verbose"));
201
202 bool help =
203 CF_EXPECT(FlagCollection::GetValue<bool>(name_flag_map.at("help")));
204 bool verbose =
205 CF_EXPECT(FlagCollection::GetValue<bool>(name_flag_map.at("verbose")));
206 std::optional<std::string> exec_path;
207 if (Contains(name_flag_map, "exec-path")) {
208 exec_path = CF_EXPECT(
209 FlagCollection::GetValue<std::string>(name_flag_map.at("exec-path")));
210 }
211 return Parsed{.help = help,
212 .verbose = verbose,
213 .subcmd = subcmd_opt,
214 .exec_path = exec_path};
215 }
216
NewExecFromPath(const RequestWithStdio & request,const std::string & exec_path)217 Result<SharedFD> NewExecFromPath(const RequestWithStdio& request,
218 const std::string& exec_path) {
219 std::string emulated_absolute_path;
220 const std::string client_pwd =
221 request.Message().command_request().working_directory();
222 // ~ that means $HOME is not supported
223 CF_EXPECT(!android::base::StartsWith(exec_path, "~/"),
224 "Path starting with ~/ is not supported.");
225 CF_EXPECT_NE(
226 exec_path, "~",
227 "~ is not supported as a executable path, and likely is not a file.");
228 emulated_absolute_path =
229 CF_EXPECT(EmulateAbsolutePath({.current_working_dir = client_pwd,
230 .path_to_convert = exec_path,
231 .follow_symlink = false}),
232 "Failed to change exec_path to an absolute path.");
233 auto new_exe = SharedFD::Open(emulated_absolute_path, O_RDONLY);
234 CF_EXPECT(new_exe->IsOpen(), "Failed to open \""
235 << exec_path << " that is "
236 << emulated_absolute_path
237 << "\": " << new_exe->StrError());
238 return new_exe;
239 }
240
SerializedInstanceDatabaseToString(const uid_t client_uid)241 Result<std::string> SerializedInstanceDatabaseToString(
242 const uid_t client_uid) {
243 auto db_json = CF_EXPECT(instance_manager_.Serialize(client_uid),
244 "Failed to serialized instance database");
245 return db_json.toStyledString();
246 }
247
CreateMemFileWithSerializedDb(const std::string & json_string)248 Result<SharedFD> CreateMemFileWithSerializedDb(
249 const std::string& json_string) {
250 const std::string mem_file_name = "cvd_server_" + std::to_string(getpid());
251 auto mem_fd = SharedFD::MemfdCreateWithData(mem_file_name, json_string);
252 CF_EXPECT(mem_fd->IsOpen(),
253 "MemfdCreateWithData failed: " << mem_fd->StrError());
254 return mem_fd;
255 }
256
PrintFileLink(const SharedFD & fd_stream,const SharedFD & mem_fd) const257 void PrintFileLink(const SharedFD& fd_stream, const SharedFD& mem_fd) const {
258 auto link_target_result = mem_fd->ProcFdLinkTarget();
259 if (!link_target_result.ok()) {
260 WriteAll(fd_stream,
261 "Failed to resolve the link target for the memory file.\n");
262 return;
263 }
264 std::string message("The link target for the memory file is ");
265 message.append(*link_target_result).append("\n");
266 WriteAll(fd_stream, message);
267 return;
268 }
269
270 BuildApi& build_api_;
271 std::vector<std::string> supported_modes_;
272 FlagCollection flags_;
273 CvdServer& server_;
274 InstanceManager& instance_manager_;
275 };
276
277 } // namespace
278
279 fruit::Component<fruit::Required<BuildApi, CvdServer, InstanceManager>>
CvdRestartComponent()280 CvdRestartComponent() {
281 return fruit::createComponent()
282 .addMultibinding<CvdServerHandler, CvdRestartHandler>();
283 }
284
285 } // namespace cuttlefish
286