• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2019 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 #include "host/commands/run_cvd/launch.h"
17 
18 #include <android-base/logging.h>
19 #include <sstream>
20 #include <string>
21 #include <utility>
22 
23 #include "common/libs/fs/shared_buf.h"
24 #include "common/libs/fs/shared_fd.h"
25 #include "common/libs/utils/files.h"
26 #include "common/libs/utils/result.h"
27 #include "host/commands/run_cvd/reporting.h"
28 #include "host/libs/config/cuttlefish_config.h"
29 #include "host/libs/config/known_paths.h"
30 #include "host/libs/vm_manager/crosvm_manager.h"
31 #include "host/libs/vm_manager/qemu_manager.h"
32 
33 namespace cuttlefish {
34 
35 namespace {
36 
CreateUnixInputServer(const std::string & path)37 SharedFD CreateUnixInputServer(const std::string& path) {
38   auto server =
39       SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
40   if (!server->IsOpen()) {
41     LOG(ERROR) << "Unable to create unix input server: " << server->StrError();
42     return {};
43   }
44   return server;
45 }
46 
LaunchCustomActionServers(Command & webrtc_cmd,const std::vector<CustomActionConfig> & custom_actions)47 std::vector<Command> LaunchCustomActionServers(
48     Command& webrtc_cmd,
49     const std::vector<CustomActionConfig>& custom_actions) {
50   bool first = true;
51   std::vector<Command> commands;
52   for (const auto& custom_action : custom_actions) {
53     if (custom_action.server) {
54       // Create a socket pair that will be used for communication between
55       // WebRTC and the action server.
56       SharedFD webrtc_socket, action_server_socket;
57       if (!SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &webrtc_socket,
58                                 &action_server_socket)) {
59         LOG(ERROR) << "Unable to create custom action server socket pair: "
60                    << strerror(errno);
61         continue;
62       }
63 
64       // Launch the action server, providing its socket pair fd as the only
65       // argument.
66       auto binary = HostBinaryPath(*(custom_action.server));
67       Command command(binary);
68       command.AddParameter(action_server_socket);
69       commands.emplace_back(std::move(command));
70 
71       // Pass the WebRTC socket pair fd to WebRTC.
72       if (first) {
73         first = false;
74         webrtc_cmd.AddParameter("-action_servers=", *custom_action.server, ":",
75                                 webrtc_socket);
76       } else {
77         webrtc_cmd.AppendToLastParameter(",", *custom_action.server, ":",
78                                          webrtc_socket);
79       }
80     }
81   }
82   return commands;
83 }
84 
85 // Creates the frame and input sockets and add the relevant arguments to
86 // webrtc commands
87 class StreamerSockets : public virtual SetupFeature {
88  public:
INJECT(StreamerSockets (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))89   INJECT(StreamerSockets(const CuttlefishConfig& config,
90                          const CuttlefishConfig::InstanceSpecific& instance))
91       : config_(config), instance_(instance) {}
92 
AppendCommandArguments(Command & cmd)93   void AppendCommandArguments(Command& cmd) {
94     if (config_.vm_manager() == vm_manager::QemuManager::name()) {
95       cmd.AddParameter("-write_virtio_input");
96     }
97     if (!touch_servers_.empty()) {
98       cmd.AddParameter("-touch_fds=", touch_servers_[0]);
99       for (int i = 1; i < touch_servers_.size(); ++i) {
100         cmd.AppendToLastParameter(",", touch_servers_[i]);
101       }
102     }
103     cmd.AddParameter("-keyboard_fd=", keyboard_server_);
104     cmd.AddParameter("-frame_server_fd=", frames_server_);
105     if (config_.enable_audio()) {
106       cmd.AddParameter("--audio_server_fd=", audio_server_);
107     }
108   }
109 
110   // SetupFeature
Name() const111   std::string Name() const override { return "StreamerSockets"; }
Enabled() const112   bool Enabled() const override {
113     bool is_qemu = config_.vm_manager() == vm_manager::QemuManager::name();
114     bool is_accelerated = config_.gpu_mode() != kGpuModeGuestSwiftshader;
115     return !(is_qemu && is_accelerated);
116   }
117 
118  private:
Dependencies() const119   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
120 
ResultSetup()121   Result<void> ResultSetup() override {
122     auto use_vsockets = config_.vm_manager() == vm_manager::QemuManager::name();
123     for (int i = 0; i < config_.display_configs().size(); ++i) {
124       SharedFD touch_socket =
125           use_vsockets ? SharedFD::VsockServer(instance_.touch_server_port(),
126                                                SOCK_STREAM)
127                        : CreateUnixInputServer(instance_.touch_socket_path(i));
128       CF_EXPECT(touch_socket->IsOpen(), touch_socket->StrError());
129       touch_servers_.emplace_back(std::move(touch_socket));
130     }
131     keyboard_server_ =
132         use_vsockets ? SharedFD::VsockServer(instance_.keyboard_server_port(),
133                                              SOCK_STREAM)
134                      : CreateUnixInputServer(instance_.keyboard_socket_path());
135     CF_EXPECT(keyboard_server_->IsOpen(), keyboard_server_->StrError());
136 
137     frames_server_ = CreateUnixInputServer(instance_.frames_socket_path());
138     CF_EXPECT(frames_server_->IsOpen(), frames_server_->StrError());
139     // TODO(schuffelen): Make this a separate optional feature?
140     if (config_.enable_audio()) {
141       auto path = config_.ForDefaultInstance().audio_server_path();
142       audio_server_ =
143           SharedFD::SocketLocalServer(path, false, SOCK_SEQPACKET, 0666);
144       CF_EXPECT(audio_server_->IsOpen(), audio_server_->StrError());
145     }
146     return {};
147   }
148 
149   const CuttlefishConfig& config_;
150   const CuttlefishConfig::InstanceSpecific& instance_;
151   std::vector<SharedFD> touch_servers_;
152   SharedFD keyboard_server_;
153   SharedFD frames_server_;
154   SharedFD audio_server_;
155 };
156 
157 class WebRtcServer : public virtual CommandSource,
158                      public DiagnosticInformation {
159  public:
INJECT(WebRtcServer (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance,StreamerSockets & sockets,KernelLogPipeProvider & log_pipe_provider,const CustomActionConfigProvider & custom_action_config))160   INJECT(WebRtcServer(const CuttlefishConfig& config,
161                       const CuttlefishConfig::InstanceSpecific& instance,
162                       StreamerSockets& sockets,
163                       KernelLogPipeProvider& log_pipe_provider,
164                       const CustomActionConfigProvider& custom_action_config))
165       : config_(config),
166         instance_(instance),
167         sockets_(sockets),
168         log_pipe_provider_(log_pipe_provider),
169         custom_action_config_(custom_action_config) {}
170   // DiagnosticInformation
Diagnostics() const171   std::vector<std::string> Diagnostics() const override {
172     if (!Enabled() || !config_.ForDefaultInstance().start_webrtc_sig_server()) {
173       // When WebRTC is enabled but an operator other than the one launched by
174       // run_cvd is used there is no way to know the url to which to point the
175       // browser to.
176       return {};
177     }
178     std::ostringstream out;
179     out << "Point your browser to https://" << config_.sig_server_address()
180         << ":" << config_.sig_server_port() << " to interact with the device.";
181     return {out.str()};
182   }
183 
184   // CommandSource
Commands()185   std::vector<Command> Commands() override {
186     std::vector<Command> commands;
187     if (instance_.start_webrtc_sig_server()) {
188       Command sig_server(WebRtcSigServerBinary());
189       sig_server.AddParameter("-assets_dir=", config_.webrtc_assets_dir());
190       sig_server.AddParameter(
191           "-use_secure_http=",
192           config_.sig_server_secure() ? "true" : "false");
193       if (!config_.webrtc_certs_dir().empty()) {
194         sig_server.AddParameter("-certs_dir=", config_.webrtc_certs_dir());
195       }
196       sig_server.AddParameter("-http_server_port=", config_.sig_server_port());
197       commands.emplace_back(std::move(sig_server));
198     }
199 
200     if (instance_.start_webrtc_sig_server_proxy()) {
201       Command sig_proxy(WebRtcSigServerProxyBinary());
202       sig_proxy.AddParameter("-server_port=", config_.sig_server_port());
203       commands.emplace_back(std::move(sig_proxy));
204     }
205 
206     auto stopper = [host_socket = std::move(host_socket_)](Subprocess* proc) {
207       struct timeval timeout;
208       timeout.tv_sec = 3;
209       timeout.tv_usec = 0;
210       CHECK(host_socket->SetSockOpt(SOL_SOCKET, SO_RCVTIMEO, &timeout,
211                                     sizeof(timeout)) == 0)
212           << "Could not set receive timeout";
213 
214       WriteAll(host_socket, "C");
215       char response[1];
216       int read_ret = host_socket->Read(response, sizeof(response));
217       if (read_ret != 0) {
218         LOG(ERROR) << "Failed to read response from webrtc";
219         return KillSubprocess(proc);
220       }
221       return KillSubprocess(proc) == StopperResult::kStopSuccess
222                  ? StopperResult::kStopCrash
223                  : StopperResult::kStopFailure;
224     };
225 
226     Command webrtc(WebRtcBinary(), stopper);
227     webrtc.UnsetFromEnvironment("http_proxy");
228     sockets_.AppendCommandArguments(webrtc);
229     if (config_.vm_manager() == vm_manager::CrosvmManager::name()) {
230       webrtc.AddParameter("-switches_fd=", switches_server_);
231     }
232     // Currently there is no way to ensure the signaling server will already
233     // have bound the socket to the port by the time the webrtc process runs
234     // (the common technique of doing it from the launcher is not possible here
235     // as the server library being used creates its own sockets). However, this
236     // issue is mitigated slightly by doing some retrying and backoff in the
237     // webrtc process when connecting to the websocket, so it shouldn't be an
238     // issue most of the time.
239     webrtc.AddParameter("--command_fd=", client_socket_);
240     webrtc.AddParameter("-kernel_log_events_fd=", kernel_log_events_pipe_);
241     webrtc.AddParameter("-client_dir=",
242                         DefaultHostArtifactsPath("usr/share/webrtc/assets"));
243 
244     // TODO get from launcher params
245     const auto& actions = custom_action_config_.CustomActions();
246     for (auto& action : LaunchCustomActionServers(webrtc, actions)) {
247       commands.emplace_back(std::move(action));
248     }
249     commands.emplace_back(std::move(webrtc));
250 
251     return commands;
252   }
253 
254   // SetupFeature
Enabled() const255   bool Enabled() const override {
256     return sockets_.Enabled() && config_.enable_webrtc();
257   }
258 
259  private:
Name() const260   std::string Name() const override { return "WebRtcServer"; }
Dependencies() const261   std::unordered_set<SetupFeature*> Dependencies() const override {
262     return {static_cast<SetupFeature*>(&sockets_),
263             static_cast<SetupFeature*>(&log_pipe_provider_)};
264   }
265 
ResultSetup()266   Result<void> ResultSetup() override {
267     CF_EXPECT(SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &client_socket_,
268                                    &host_socket_),
269               client_socket_->StrError());
270     if (config_.vm_manager() == vm_manager::CrosvmManager::name()) {
271       switches_server_ =
272           CreateUnixInputServer(instance_.switches_socket_path());
273       CF_EXPECT(switches_server_->IsOpen(), switches_server_->StrError());
274     }
275     kernel_log_events_pipe_ = log_pipe_provider_.KernelLogPipe();
276     CF_EXPECT(kernel_log_events_pipe_->IsOpen(),
277               kernel_log_events_pipe_->StrError());
278     return {};
279   }
280 
281   const CuttlefishConfig& config_;
282   const CuttlefishConfig::InstanceSpecific& instance_;
283   StreamerSockets& sockets_;
284   KernelLogPipeProvider& log_pipe_provider_;
285   const CustomActionConfigProvider& custom_action_config_;
286   SharedFD kernel_log_events_pipe_;
287   SharedFD client_socket_;
288   SharedFD host_socket_;
289   SharedFD switches_server_;
290 };
291 
292 }  // namespace
293 
294 fruit::Component<fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
295                                  const CuttlefishConfig::InstanceSpecific,
296                                  const CustomActionConfigProvider>>
launchStreamerComponent()297 launchStreamerComponent() {
298   return fruit::createComponent()
299       .addMultibinding<CommandSource, WebRtcServer>()
300       .addMultibinding<DiagnosticInformation, WebRtcServer>()
301       .addMultibinding<SetupFeature, StreamerSockets>()
302       .addMultibinding<SetupFeature, WebRtcServer>();
303 }
304 
305 }  // namespace cuttlefish
306