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