• 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 "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