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/launch.h"
17 
18 #include <sstream>
19 #include <string>
20 #include <unordered_set>
21 #include <utility>
22 #include <vector>
23 
24 #include <android-base/logging.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/files.h"
30 #include "common/libs/utils/result.h"
31 #include "host/commands/run_cvd/reporting.h"
32 #include "host/libs/config/command_source.h"
33 #include "host/libs/config/cuttlefish_config.h"
34 #include "host/libs/config/known_paths.h"
35 #include "host/libs/vm_manager/crosvm_manager.h"
36 #include "host/libs/vm_manager/qemu_manager.h"
37 
38 namespace cuttlefish {
39 
40 namespace {
41 
CreateUnixInputServer(const std::string & path)42 SharedFD CreateUnixInputServer(const std::string& path) {
43   auto server =
44       SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
45   if (!server->IsOpen()) {
46     LOG(ERROR) << "Unable to create unix input server: " << server->StrError();
47     return {};
48   }
49   return server;
50 }
51 
LaunchCustomActionServers(Command & webrtc_cmd,const std::vector<CustomActionServerConfig> & custom_actions)52 std::vector<Command> LaunchCustomActionServers(
53     Command& webrtc_cmd,
54     const std::vector<CustomActionServerConfig>& custom_actions) {
55   bool first = true;
56   std::vector<Command> commands;
57   for (const auto& custom_action : custom_actions) {
58     // Create a socket pair that will be used for communication between
59     // WebRTC and the action server.
60     SharedFD webrtc_socket, action_server_socket;
61     if (!SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &webrtc_socket,
62           &action_server_socket)) {
63       LOG(ERROR) << "Unable to create custom action server socket pair: "
64         << strerror(errno);
65       continue;
66     }
67 
68     // Launch the action server, providing its socket pair fd as the only
69     // argument.
70     auto binary = HostBinaryPath(custom_action.server);
71     Command command(binary);
72     command.AddParameter(action_server_socket);
73     commands.emplace_back(std::move(command));
74 
75     // Pass the WebRTC socket pair fd to WebRTC.
76     if (first) {
77       first = false;
78       webrtc_cmd.AddParameter("-action_servers=", custom_action.server, ":",
79           webrtc_socket);
80     } else {
81       webrtc_cmd.AppendToLastParameter(",", custom_action.server, ":",
82           webrtc_socket);
83     }
84   }
85   return commands;
86 }
87 
88 // Creates the frame and input sockets and add the relevant arguments to
89 // webrtc commands
90 class StreamerSockets : public virtual SetupFeature {
91  public:
INJECT(StreamerSockets (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))92   INJECT(StreamerSockets(const CuttlefishConfig& config,
93                          const CuttlefishConfig::InstanceSpecific& instance))
94       : config_(config), instance_(instance) {}
95 
AppendCommandArguments(Command & cmd)96   void AppendCommandArguments(Command& cmd) {
97     if (config_.vm_manager() == VmmMode::kQemu) {
98       cmd.AddParameter("-write_virtio_input");
99     }
100     if (!touch_servers_.empty()) {
101       bool is_chromeos =
102           instance_.boot_flow() ==
103               CuttlefishConfig::InstanceSpecific::BootFlow::ChromeOs ||
104           instance_.boot_flow() ==
105               CuttlefishConfig::InstanceSpecific::BootFlow::ChromeOsDisk;
106       if (is_chromeos) {
107         cmd.AddParameter("--multitouch=false");
108       }
109       cmd.AddParameter("-touch_fds=", touch_servers_[0]);
110       for (int i = 1; i < touch_servers_.size(); ++i) {
111         cmd.AppendToLastParameter(",", touch_servers_[i]);
112       }
113     }
114     if (instance_.enable_mouse()) {
115       cmd.AddParameter("-mouse_fd=", mouse_server_);
116     }
117     cmd.AddParameter("-rotary_fd=", rotary_server_);
118     cmd.AddParameter("-keyboard_fd=", keyboard_server_);
119     cmd.AddParameter("-frame_server_fd=", frames_server_);
120     if (instance_.enable_audio()) {
121       cmd.AddParameter("--audio_server_fd=", audio_server_);
122     }
123     cmd.AddParameter("--confui_in_fd=", confui_in_fd_);
124     cmd.AddParameter("--confui_out_fd=", confui_out_fd_);
125     cmd.AddParameter("--sensors_in_fd=", sensors_host_to_guest_fd_);
126     cmd.AddParameter("--sensors_out_fd=", sensors_guest_to_host_fd_);
127   }
128 
129   // SetupFeature
Name() const130   std::string Name() const override { return "StreamerSockets"; }
Enabled() const131   bool Enabled() const override {
132     bool is_qemu = config_.vm_manager() == VmmMode::kQemu;
133     bool is_accelerated = instance_.gpu_mode() != kGpuModeGuestSwiftshader;
134     return !(is_qemu && is_accelerated);
135   }
136 
137  private:
Dependencies() const138   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
139 
ResultSetup()140   Result<void> ResultSetup() override {
141     int display_cnt = instance_.display_configs().size();
142     int touchpad_cnt = instance_.touchpad_configs().size();
143     for (int i = 0; i < display_cnt + touchpad_cnt; ++i) {
144       SharedFD touch_socket =
145           CreateUnixInputServer(instance_.touch_socket_path(i));
146       CF_EXPECT(touch_socket->IsOpen(), touch_socket->StrError());
147       touch_servers_.emplace_back(std::move(touch_socket));
148     }
149     if (instance_.enable_mouse()) {
150       mouse_server_ = CreateUnixInputServer(instance_.mouse_socket_path());
151       CF_EXPECT(mouse_server_->IsOpen(), mouse_server_->StrError());
152     }
153     rotary_server_ =
154         CreateUnixInputServer(instance_.rotary_socket_path());
155 
156     CF_EXPECT(rotary_server_->IsOpen(), rotary_server_->StrError());
157     keyboard_server_ = CreateUnixInputServer(instance_.keyboard_socket_path());
158     CF_EXPECT(keyboard_server_->IsOpen(), keyboard_server_->StrError());
159 
160     frames_server_ = CreateUnixInputServer(instance_.frames_socket_path());
161     CF_EXPECT(frames_server_->IsOpen(), frames_server_->StrError());
162     // TODO(schuffelen): Make this a separate optional feature?
163     if (instance_.enable_audio()) {
164       auto path = config_.ForDefaultInstance().audio_server_path();
165       audio_server_ =
166           SharedFD::SocketLocalServer(path, false, SOCK_SEQPACKET, 0666);
167       CF_EXPECT(audio_server_->IsOpen(), audio_server_->StrError());
168     }
169     CF_EXPECT(InitializeVConsoles());
170     return {};
171   }
172 
InitializeVConsoles()173   Result<void> InitializeVConsoles() {
174     std::vector<std::string> fifo_files = {
175         instance_.PerInstanceInternalPath("confui_fifo_vm.in"),
176         instance_.PerInstanceInternalPath("confui_fifo_vm.out"),
177         instance_.PerInstanceInternalPath("sensors_fifo_vm.in"),
178         instance_.PerInstanceInternalPath("sensors_fifo_vm.out"),
179     };
180     for (const auto& path : fifo_files) {
181       unlink(path.c_str());
182     }
183     std::vector<SharedFD> fds;
184     for (const auto& path : fifo_files) {
185       fds.emplace_back(CF_EXPECT(SharedFD::Fifo(path, 0660)));
186     }
187     confui_in_fd_ = fds[0];
188     confui_out_fd_ = fds[1];
189     sensors_host_to_guest_fd_ = fds[2];
190     sensors_guest_to_host_fd_ = fds[3];
191     return {};
192   }
193 
194   const CuttlefishConfig& config_;
195   const CuttlefishConfig::InstanceSpecific& instance_;
196   std::vector<SharedFD> touch_servers_;
197   SharedFD mouse_server_;
198   SharedFD rotary_server_;
199   SharedFD keyboard_server_;
200   SharedFD frames_server_;
201   SharedFD audio_server_;
202   SharedFD confui_in_fd_;   // host -> guest
203   SharedFD confui_out_fd_;  // guest -> host
204   SharedFD sensors_host_to_guest_fd_;
205   SharedFD sensors_guest_to_host_fd_;
206 };
207 
208 class WebRtcServer : public virtual CommandSource,
209                      public DiagnosticInformation,
210                      public KernelLogPipeConsumer {
211  public:
INJECT(WebRtcServer (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance,StreamerSockets & sockets,KernelLogPipeProvider & log_pipe_provider,const CustomActionConfigProvider & custom_action_config,WebRtcController & webrtc_controller))212   INJECT(WebRtcServer(const CuttlefishConfig& config,
213                       const CuttlefishConfig::InstanceSpecific& instance,
214                       StreamerSockets& sockets,
215                       KernelLogPipeProvider& log_pipe_provider,
216                       const CustomActionConfigProvider& custom_action_config,
217                       WebRtcController& webrtc_controller))
218       : config_(config),
219         instance_(instance),
220         sockets_(sockets),
221         log_pipe_provider_(log_pipe_provider),
222         custom_action_config_(custom_action_config),
223         webrtc_controller_(webrtc_controller) {}
224   // DiagnosticInformation
Diagnostics() const225   std::vector<std::string> Diagnostics() const override {
226     if (!Enabled() ||
227         !(config_.ForDefaultInstance().start_webrtc_sig_server() ||
228           config_.ForDefaultInstance().start_webrtc_sig_server_proxy())) {
229       // When WebRTC is enabled but an operator other than the one launched by
230       // run_cvd is used there is no way to know the url to which to point the
231       // browser to.
232       return {};
233     }
234     std::ostringstream out;
235     out << "Point your browser to https://localhost:"
236         << config_.sig_server_port() << " to interact with the device.";
237     return {out.str()};
238   }
239 
240   // CommandSource
Commands()241   Result<std::vector<MonitorCommand>> Commands() override {
242     std::vector<MonitorCommand> commands;
243     if (instance_.start_webrtc_sig_server()) {
244       Command sig_server(WebRtcSigServerBinary());
245       sig_server.AddParameter("-assets_dir=", instance_.webrtc_assets_dir());
246       sig_server.AddParameter("-use_secure_http=",
247                               config_.sig_server_secure() ? "true" : "false");
248       if (!config_.webrtc_certs_dir().empty()) {
249         sig_server.AddParameter("-certs_dir=", config_.webrtc_certs_dir());
250       }
251       sig_server.AddParameter("-http_server_port=", config_.sig_server_port());
252       commands.emplace_back(std::move(sig_server));
253     }
254 
255     if (instance_.start_webrtc_sig_server_proxy()) {
256       Command sig_proxy(WebRtcSigServerProxyBinary());
257       sig_proxy.AddParameter("-server_port=", config_.sig_server_port());
258       commands.emplace_back(std::move(sig_proxy));
259     }
260 
261     auto stopper = [webrtc_controller = webrtc_controller_]() mutable {
262       (void)webrtc_controller.SendStopRecordingCommand();
263       return StopperResult::kStopFailure;
264     };
265 
266     Command webrtc(WebRtcBinary(), KillSubprocessFallback(stopper));
267 
268     webrtc.AddParameter("-group_id=", instance_.group_id());
269 
270     webrtc.UnsetFromEnvironment("http_proxy");
271     sockets_.AppendCommandArguments(webrtc);
272     if (config_.vm_manager() == VmmMode::kCrosvm) {
273       webrtc.AddParameter("-switches_fd=", switches_server_);
274     }
275     // Currently there is no way to ensure the signaling server will already
276     // have bound the socket to the port by the time the webrtc process runs
277     // (the common technique of doing it from the launcher is not possible here
278     // as the server library being used creates its own sockets). However, this
279     // issue is mitigated slightly by doing some retrying and backoff in the
280     // webrtc process when connecting to the websocket, so it shouldn't be an
281     // issue most of the time.
282     webrtc.AddParameter("--command_fd=", webrtc_controller_.GetClientSocket());
283     webrtc.AddParameter("-kernel_log_events_fd=", kernel_log_events_pipe_);
284     webrtc.AddParameter("-client_dir=",
285                         DefaultHostArtifactsPath("usr/share/webrtc/assets"));
286 
287     // TODO get from launcher params
288     const auto& actions =
289         custom_action_config_.CustomActionServers(instance_.id());
290     for (auto& action : LaunchCustomActionServers(webrtc, actions)) {
291       commands.emplace_back(std::move(action));
292     }
293     commands.emplace_back(std::move(webrtc));
294     return commands;
295   }
296 
297   // SetupFeature
Enabled() const298   bool Enabled() const override {
299     return sockets_.Enabled() && instance_.enable_webrtc();
300   }
301 
302  private:
Name() const303   std::string Name() const override { return "WebRtcServer"; }
Dependencies() const304   std::unordered_set<SetupFeature*> Dependencies() const override {
305     return {static_cast<SetupFeature*>(&sockets_),
306             static_cast<SetupFeature*>(&log_pipe_provider_),
307             static_cast<SetupFeature*>(&webrtc_controller_)};
308   }
309 
ResultSetup()310   Result<void> ResultSetup() override {
311     if (config_.vm_manager() == VmmMode::kCrosvm) {
312       switches_server_ =
313           CreateUnixInputServer(instance_.switches_socket_path());
314       CF_EXPECT(switches_server_->IsOpen(), switches_server_->StrError());
315     }
316     kernel_log_events_pipe_ = log_pipe_provider_.KernelLogPipe();
317     CF_EXPECT(kernel_log_events_pipe_->IsOpen(),
318               kernel_log_events_pipe_->StrError());
319     return {};
320   }
321 
322   const CuttlefishConfig& config_;
323   const CuttlefishConfig::InstanceSpecific& instance_;
324   StreamerSockets& sockets_;
325   KernelLogPipeProvider& log_pipe_provider_;
326   const CustomActionConfigProvider& custom_action_config_;
327   WebRtcController& webrtc_controller_;
328   SharedFD kernel_log_events_pipe_;
329   SharedFD switches_server_;
330 };
331 
332 }  // namespace
333 
334 fruit::Component<
335     fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
336                     const CuttlefishConfig::InstanceSpecific,
337                     const CustomActionConfigProvider, WebRtcController>>
launchStreamerComponent()338 launchStreamerComponent() {
339   return fruit::createComponent()
340       .addMultibinding<CommandSource, WebRtcServer>()
341       .addMultibinding<DiagnosticInformation, WebRtcServer>()
342       .addMultibinding<KernelLogPipeConsumer, WebRtcServer>()
343       .addMultibinding<SetupFeature, StreamerSockets>()
344       .addMultibinding<SetupFeature, WebRtcServer>();
345 }
346 
347 }  // namespace cuttlefish
348