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 "client.h"
18
19 #include <stdlib.h>
20
21 #include <iostream>
22 #include <sstream>
23
24 #include <android-base/file.h>
25 #include <google/protobuf/text_format.h>
26
27 #include "common/libs/fs/shared_buf.h"
28 #include "common/libs/fs/shared_fd.h"
29 #include "common/libs/utils/environment.h"
30 #include "common/libs/utils/subprocess.h"
31 #include "host/commands/cvd/common_utils.h"
32 #include "host/commands/cvd/server_constants.h"
33 #include "host/libs/config/host_tools_version.h"
34
35 namespace cuttlefish {
36 namespace {
37
ConnectToServer()38 Result<SharedFD> ConnectToServer() {
39 auto connection =
40 SharedFD::SocketLocalClient(cvd::kServerSocketPath,
41 /*is_abstract=*/true, SOCK_SEQPACKET);
42 if (!connection->IsOpen()) {
43 auto connection =
44 SharedFD::SocketLocalClient(cvd::kServerSocketPath,
45 /*is_abstract=*/true, SOCK_STREAM);
46 }
47 if (!connection->IsOpen()) {
48 return CF_ERR("Failed to connect to server" << connection->StrError());
49 }
50 return connection;
51 }
52
CallPythonAcloud(std::vector<std::string> & args)53 [[noreturn]] void CallPythonAcloud(std::vector<std::string>& args) {
54 auto android_top = StringFromEnv("ANDROID_BUILD_TOP", "");
55 if (android_top == "") {
56 LOG(FATAL) << "Could not find android environment. Please run "
57 << "\"source build/envsetup.sh\".";
58 abort();
59 }
60 // TODO(b/206893146): Detect what the platform actually is.
61 auto py_acloud_path =
62 android_top + "/prebuilts/asuite/acloud/linux-x86/acloud";
63 std::unique_ptr<char*[]> new_argv(new char*[args.size() + 1]);
64 for (size_t i = 0; i < args.size(); i++) {
65 new_argv[i] = args[i].data();
66 }
67 new_argv[args.size()] = nullptr;
68 execv(py_acloud_path.data(), new_argv.get());
69 PLOG(FATAL) << "execv(" << py_acloud_path << ", ...) failed";
70 abort();
71 }
72
73 } // end of namespace
74
GetClientVersion()75 cvd::Version CvdClient::GetClientVersion() {
76 cvd::Version client_version;
77 client_version.set_major(cvd::kVersionMajor);
78 client_version.set_minor(cvd::kVersionMinor);
79 client_version.set_build(android::build::GetBuildNumber());
80 client_version.set_crc32(FileCrc(kServerExecPath));
81 return client_version;
82 }
83
GetServerVersion()84 Result<cvd::Version> CvdClient::GetServerVersion() {
85 cvd::Request request;
86 request.mutable_version_request();
87 auto response = SendRequest(request);
88
89 // If cvd_server is not running, start and wait before checking its version.
90 if (!response.ok()) {
91 CF_EXPECT(StartCvdServer());
92 response = CF_EXPECT(SendRequest(request));
93 }
94 CF_EXPECT(CheckStatus(response->status(), "GetVersion"));
95 CF_EXPECT(response->has_version_response(),
96 "GetVersion call missing VersionResponse.");
97
98 return response->version_response().version();
99 }
100
ValidateServerVersion(const int num_retries)101 Result<void> CvdClient::ValidateServerVersion(const int num_retries) {
102 auto server_version = CF_EXPECT(GetServerVersion());
103 if (server_version.major() != cvd::kVersionMajor) {
104 return CF_ERR("Major version difference: cvd("
105 << cvd::kVersionMajor << "." << cvd::kVersionMinor
106 << ") != cvd_server(" << server_version.major() << "."
107 << server_version.minor()
108 << "). Try `cvd kill-server` or `pkill cvd_server`.");
109 }
110 if (server_version.minor() < cvd::kVersionMinor) {
111 std::cerr << "Minor version of cvd_server is older than latest. "
112 << "Attempting to restart..." << std::endl;
113 CF_EXPECT(StopCvdServer(/*clear=*/false));
114 CF_EXPECT(StartCvdServer());
115 if (num_retries > 0) {
116 CF_EXPECT(ValidateServerVersion(num_retries - 1));
117 return {};
118 } else {
119 return CF_ERR("Unable to start the cvd_server with version "
120 << cvd::kVersionMajor << "." << cvd::kVersionMinor);
121 }
122 }
123 if (server_version.build() != android::build::GetBuildNumber()) {
124 LOG(VERBOSE) << "cvd_server client version ("
125 << android::build::GetBuildNumber()
126 << ") does not match server version ("
127 << server_version.build() << std::endl;
128 }
129 auto self_crc32 = FileCrc(kServerExecPath);
130 if (server_version.crc32() != self_crc32) {
131 LOG(VERBOSE) << "cvd_server client checksum (" << self_crc32
132 << ") doesn't match server checksum ("
133 << server_version.crc32() << std::endl;
134 }
135 return {};
136 }
137
StopCvdServer(bool clear)138 Result<void> CvdClient::StopCvdServer(bool clear) {
139 if (!server_) {
140 // server_ may not represent a valid connection even while the server is
141 // running, if we haven't tried to connect. This establishes first whether
142 // the server is running.
143 auto connection_attempt = ConnectToServer();
144 if (!connection_attempt.ok()) {
145 return {};
146 }
147 }
148
149 cvd::Request request;
150 auto shutdown_request = request.mutable_shutdown_request();
151 if (clear) {
152 shutdown_request->set_clear(true);
153 }
154
155 // Send the server a pipe with the Shutdown request that it
156 // will close when it fully exits.
157 SharedFD read_pipe, write_pipe;
158 CF_EXPECT(cuttlefish::SharedFD::Pipe(&read_pipe, &write_pipe),
159 "Unable to create shutdown pipe: " << strerror(errno));
160
161 auto response =
162 SendRequest(request, OverrideFd{/* override none of 0, 1, 2 */},
163 /*extra_fd=*/write_pipe);
164
165 // If the server is already not running then SendRequest will fail.
166 // We treat this as success.
167 if (!response.ok()) {
168 server_.reset();
169 return {};
170 }
171
172 CF_EXPECT(CheckStatus(response->status(), "Shutdown"));
173 CF_EXPECT(response->has_shutdown_response(),
174 "Shutdown call missing ShutdownResponse.");
175
176 // Clear out the server_ socket.
177 server_.reset();
178
179 // Close the write end of the pipe in this process. Now the only
180 // process that may have the write end still open is the cvd_server.
181 write_pipe->Close();
182
183 // Wait for the pipe to close by attempting to read from the pipe.
184 char buf[1]; // Any size >0 should work for read attempt.
185 CF_EXPECT(read_pipe->Read(buf, sizeof(buf)) <= 0,
186 "Unexpected read value from cvd_server shutdown pipe.");
187 return {};
188 }
189
HandleCommand(const std::vector<std::string> & cvd_process_args,const std::unordered_map<std::string,std::string> & env,const std::vector<std::string> & selector_args,const OverrideFd & new_control_fd)190 Result<cvd::Response> CvdClient::HandleCommand(
191 const std::vector<std::string>& cvd_process_args,
192 const std::unordered_map<std::string, std::string>& env,
193 const std::vector<std::string>& selector_args,
194 const OverrideFd& new_control_fd) {
195 std::optional<SharedFD> exe_fd;
196 // actual commandline arguments are packed in selector_args
197 if (selector_args.size() > 2 &&
198 android::base::Basename(selector_args[0]) == "cvd" &&
199 selector_args[1] == "restart-server" &&
200 selector_args[2] == "match-client") {
201 exe_fd = SharedFD::Open(kServerExecPath, O_RDONLY);
202 CF_EXPECT((*exe_fd)->IsOpen(), "Failed to open \""
203 << kServerExecPath << "\": \""
204 << (*exe_fd)->StrError() << "\"");
205 }
206 cvd::Request request = MakeRequest({.cmd_args = cvd_process_args,
207 .env = env,
208 .selector_args = selector_args},
209 cvd::WAIT_BEHAVIOR_COMPLETE);
210 auto response = CF_EXPECT(SendRequest(request, new_control_fd, exe_fd));
211 CF_EXPECT(CheckStatus(response.status(), "HandleCommand"));
212 CF_EXPECT(response.has_command_response(),
213 "HandleCommand call missing CommandResponse.");
214 return {response};
215 }
216
SetServer(const SharedFD & server)217 Result<void> CvdClient::SetServer(const SharedFD& server) {
218 CF_EXPECT(!server_, "Already have a server");
219 CF_EXPECT(server->IsOpen(), server->StrError());
220 server_ = UnixMessageSocket(server);
221 CF_EXPECT(server_->EnableCredentials(true).ok(),
222 "Unable to enable UnixMessageSocket credentials.");
223 return {};
224 }
225
SendRequest(const cvd::Request & request,const OverrideFd & new_control_fds,std::optional<SharedFD> extra_fd)226 Result<cvd::Response> CvdClient::SendRequest(const cvd::Request& request,
227 const OverrideFd& new_control_fds,
228 std::optional<SharedFD> extra_fd) {
229 if (!server_) {
230 CF_EXPECT(SetServer(CF_EXPECT(ConnectToServer())));
231 }
232 // Serialize and send the request.
233 std::string serialized;
234 CF_EXPECT(request.SerializeToString(&serialized),
235 "Unable to serialize request proto.");
236 UnixSocketMessage request_message;
237
238 std::vector<SharedFD> control_fds = {
239 (new_control_fds.stdin_override_fd ? *new_control_fds.stdin_override_fd
240 : SharedFD::Dup(0)),
241 (new_control_fds.stdout_override_fd ? *new_control_fds.stdout_override_fd
242 : SharedFD::Dup(1)),
243 (new_control_fds.stderr_override_fd ? *new_control_fds.stderr_override_fd
244 : SharedFD::Dup(2))};
245 if (extra_fd) {
246 control_fds.push_back(*extra_fd);
247 }
248 auto control = CF_EXPECT(ControlMessage::FromFileDescriptors(control_fds));
249 request_message.control.emplace_back(std::move(control));
250
251 request_message.data =
252 std::vector<char>(serialized.begin(), serialized.end());
253 CF_EXPECT(server_->WriteMessage(request_message));
254
255 // Read and parse the response.
256 auto read_result = CF_EXPECT(server_->ReadMessage());
257 serialized = std::string(read_result.data.begin(), read_result.data.end());
258 cvd::Response response;
259 CF_EXPECT(response.ParseFromString(serialized),
260 "Unable to parse serialized response proto.");
261 return response;
262 }
263
StartCvdServer()264 Result<void> CvdClient::StartCvdServer() {
265 SharedFD server_fd =
266 SharedFD::SocketLocalServer(cvd::kServerSocketPath,
267 /*is_abstract=*/true, SOCK_SEQPACKET, 0666);
268 CF_EXPECT(server_fd->IsOpen(), server_fd->StrError());
269
270 Command command(kServerExecPath);
271 command.AddParameter("-INTERNAL_server_fd=", server_fd);
272 SubprocessOptions options;
273 options.ExitWithParent(false);
274 command.Start(options);
275
276 // Connect to the server_fd, which waits for startup.
277 CF_EXPECT(SetServer(SharedFD::SocketLocalClient(cvd::kServerSocketPath,
278 /*is_abstract=*/true,
279 SOCK_SEQPACKET)));
280 return {};
281 }
282
CheckStatus(const cvd::Status & status,const std::string & rpc)283 Result<void> CvdClient::CheckStatus(const cvd::Status& status,
284 const std::string& rpc) {
285 if (status.code() == cvd::Status::OK) {
286 return {};
287 }
288 return CF_ERR("Received error response for \"" << rpc << "\":\n"
289 << status.message()
290 << "\nIn client");
291 }
292
HandleAcloud(const std::vector<std::string> & args,const std::unordered_map<std::string,std::string> & env)293 Result<void> CvdClient::HandleAcloud(
294 const std::vector<std::string>& args,
295 const std::unordered_map<std::string, std::string>& env) {
296 auto server_running = ValidateServerVersion();
297
298 std::vector<std::string> args_copy{args};
299
300 // TODO(b/206893146): Make this decision inside the server.
301 if (!server_running.ok()) {
302 CallPythonAcloud(args_copy);
303 // no return
304 }
305
306 args_copy[0] = "try-acloud";
307 auto attempt = HandleCommand(args_copy, env, {});
308 if (!attempt.ok()) {
309 CallPythonAcloud(args_copy);
310 // no return
311 }
312
313 args_copy[0] = "acloud";
314 CF_EXPECT(HandleCommand(args_copy, env, {}));
315 return {};
316 }
317
HandleVersion()318 Result<std::string> CvdClient::HandleVersion() {
319 using google::protobuf::TextFormat;
320 std::stringstream result;
321 std::string output;
322 auto server_version = CF_EXPECT(GetServerVersion());
323 CF_EXPECT(TextFormat::PrintToString(server_version, &output),
324 "converting server_version to string failed");
325 result << "Server version:" << std::endl << std::endl << output << std::endl;
326
327 CF_EXPECT(TextFormat::PrintToString(CvdClient::GetClientVersion(), &output),
328 "converting client version to string failed");
329 result << "Client version:" << std::endl << std::endl << output << std::endl;
330 return {result.str()};
331 }
332
ListSubcommands(const cvd_common::Envs & envs)333 Result<Json::Value> CvdClient::ListSubcommands(const cvd_common::Envs& envs) {
334 cvd_common::Args args{"cvd", "cmd-list"};
335 SharedFD read_pipe, write_pipe;
336 CF_EXPECT(cuttlefish::SharedFD::Pipe(&read_pipe, &write_pipe),
337 "Unable to create shutdown pipe: " << strerror(errno));
338 OverrideFd new_control_fd{.stdout_override_fd = write_pipe};
339 CF_EXPECT(
340 HandleCommand(args, envs, std::vector<std::string>{}, new_control_fd));
341
342 write_pipe->Close();
343 const int kChunkSize = 512;
344 char buf[kChunkSize + 1] = {0};
345 std::stringstream ss;
346 do {
347 auto n_read = ReadExact(read_pipe, buf, kChunkSize);
348 CF_EXPECT(n_read >= 0 && (n_read <= kChunkSize));
349 if (n_read == 0) {
350 break;
351 }
352 buf[n_read] = 0; // null-terminate the C-style string
353 ss << buf;
354 if (n_read < sizeof(buf) - 1) {
355 break;
356 }
357 } while (true);
358 auto json_output = CF_EXPECT(ParseJson(ss.str()));
359 return json_output;
360 }
361
ValidSubcmdsList(const cvd_common::Envs & envs)362 Result<cvd_common::Args> CvdClient::ValidSubcmdsList(
363 const cvd_common::Envs& envs) {
364 auto valid_subcmd_json = CF_EXPECT(ListSubcommands(envs));
365 CF_EXPECT(valid_subcmd_json.isMember("subcmd"),
366 "Server returned the list of subcommands in Json but it is missing "
367 << " \"subcmd\" field");
368 std::string valid_subcmd_string = valid_subcmd_json["subcmd"].asString();
369 auto valid_subcmds = android::base::Tokenize(valid_subcmd_string, ",");
370 return valid_subcmds;
371 }
372
373 } // end of namespace cuttlefish
374