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     cmd.AddParameter("-rotary_fd=", rotary_server_);
115     cmd.AddParameter("-keyboard_fd=", keyboard_server_);
116     cmd.AddParameter("-frame_server_fd=", frames_server_);
117     if (instance_.enable_audio()) {
118       cmd.AddParameter("--audio_server_fd=", audio_server_);
119     }
120     cmd.AddParameter("--confui_in_fd=", confui_in_fd_);
121     cmd.AddParameter("--confui_out_fd=", confui_out_fd_);
122     cmd.AddParameter("--sensors_in_fd=", sensors_host_to_guest_fd_);
123     cmd.AddParameter("--sensors_out_fd=", sensors_guest_to_host_fd_);
124   }
125 
126   // SetupFeature
Name() const127   std::string Name() const override { return "StreamerSockets"; }
Enabled() const128   bool Enabled() const override {
129     bool is_qemu = config_.vm_manager() == VmmMode::kQemu;
130     bool is_accelerated = instance_.gpu_mode() != kGpuModeGuestSwiftshader;
131     return !(is_qemu && is_accelerated);
132   }
133 
134  private:
Dependencies() const135   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
136 
ResultSetup()137   Result<void> ResultSetup() override {
138     int display_cnt = instance_.display_configs().size();
139     int touchpad_cnt = instance_.touchpad_configs().size();
140     for (int i = 0; i < display_cnt + touchpad_cnt; ++i) {
141       SharedFD touch_socket =
142           CreateUnixInputServer(instance_.touch_socket_path(i));
143       CF_EXPECT(touch_socket->IsOpen(), touch_socket->StrError());
144       touch_servers_.emplace_back(std::move(touch_socket));
145     }
146     rotary_server_ =
147         CreateUnixInputServer(instance_.rotary_socket_path());
148 
149     CF_EXPECT(rotary_server_->IsOpen(), rotary_server_->StrError());
150     keyboard_server_ = CreateUnixInputServer(instance_.keyboard_socket_path());
151     CF_EXPECT(keyboard_server_->IsOpen(), keyboard_server_->StrError());
152 
153     frames_server_ = CreateUnixInputServer(instance_.frames_socket_path());
154     CF_EXPECT(frames_server_->IsOpen(), frames_server_->StrError());
155     // TODO(schuffelen): Make this a separate optional feature?
156     if (instance_.enable_audio()) {
157       auto path = config_.ForDefaultInstance().audio_server_path();
158       audio_server_ =
159           SharedFD::SocketLocalServer(path, false, SOCK_SEQPACKET, 0666);
160       CF_EXPECT(audio_server_->IsOpen(), audio_server_->StrError());
161     }
162     CF_EXPECT(InitializeVConsoles());
163     return {};
164   }
165 
InitializeVConsoles()166   Result<void> InitializeVConsoles() {
167     std::vector<std::string> fifo_files = {
168         instance_.PerInstanceInternalPath("confui_fifo_vm.in"),
169         instance_.PerInstanceInternalPath("confui_fifo_vm.out"),
170         instance_.PerInstanceInternalPath("sensors_fifo_vm.in"),
171         instance_.PerInstanceInternalPath("sensors_fifo_vm.out"),
172     };
173     for (const auto& path : fifo_files) {
174       unlink(path.c_str());
175     }
176     std::vector<SharedFD> fds;
177     for (const auto& path : fifo_files) {
178       fds.emplace_back(CF_EXPECT(SharedFD::Fifo(path, 0660)));
179     }
180     confui_in_fd_ = fds[0];
181     confui_out_fd_ = fds[1];
182     sensors_host_to_guest_fd_ = fds[2];
183     sensors_guest_to_host_fd_ = fds[3];
184     return {};
185   }
186 
187   const CuttlefishConfig& config_;
188   const CuttlefishConfig::InstanceSpecific& instance_;
189   std::vector<SharedFD> touch_servers_;
190   SharedFD rotary_server_;
191   SharedFD keyboard_server_;
192   SharedFD frames_server_;
193   SharedFD audio_server_;
194   SharedFD confui_in_fd_;   // host -> guest
195   SharedFD confui_out_fd_;  // guest -> host
196   SharedFD sensors_host_to_guest_fd_;
197   SharedFD sensors_guest_to_host_fd_;
198 };
199 
200 class WebRtcServer : public virtual CommandSource,
201                      public DiagnosticInformation,
202                      public KernelLogPipeConsumer {
203  public:
INJECT(WebRtcServer (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance,StreamerSockets & sockets,KernelLogPipeProvider & log_pipe_provider,const CustomActionConfigProvider & custom_action_config,WebRtcRecorder & webrtc_recorder))204   INJECT(WebRtcServer(const CuttlefishConfig& config,
205                       const CuttlefishConfig::InstanceSpecific& instance,
206                       StreamerSockets& sockets,
207                       KernelLogPipeProvider& log_pipe_provider,
208                       const CustomActionConfigProvider& custom_action_config,
209                       WebRtcRecorder& webrtc_recorder))
210       : config_(config),
211         instance_(instance),
212         sockets_(sockets),
213         log_pipe_provider_(log_pipe_provider),
214         custom_action_config_(custom_action_config),
215         webrtc_recorder_(webrtc_recorder) {}
216   // DiagnosticInformation
Diagnostics() const217   std::vector<std::string> Diagnostics() const override {
218     if (!Enabled() ||
219         !(config_.ForDefaultInstance().start_webrtc_sig_server() ||
220           config_.ForDefaultInstance().start_webrtc_sig_server_proxy())) {
221       // When WebRTC is enabled but an operator other than the one launched by
222       // run_cvd is used there is no way to know the url to which to point the
223       // browser to.
224       return {};
225     }
226     std::ostringstream out;
227     out << "Point your browser to https://localhost:"
228         << config_.sig_server_port() << " to interact with the device.";
229     return {out.str()};
230   }
231 
232   // CommandSource
Commands()233   Result<std::vector<MonitorCommand>> Commands() override {
234     std::vector<MonitorCommand> commands;
235     if (instance_.start_webrtc_sig_server()) {
236       Command sig_server(WebRtcSigServerBinary());
237       sig_server.AddParameter("-assets_dir=", instance_.webrtc_assets_dir());
238       sig_server.AddParameter("-use_secure_http=",
239                               config_.sig_server_secure() ? "true" : "false");
240       if (!config_.webrtc_certs_dir().empty()) {
241         sig_server.AddParameter("-certs_dir=", config_.webrtc_certs_dir());
242       }
243       sig_server.AddParameter("-http_server_port=", config_.sig_server_port());
244       commands.emplace_back(std::move(sig_server));
245     }
246 
247     if (instance_.start_webrtc_sig_server_proxy()) {
248       Command sig_proxy(WebRtcSigServerProxyBinary());
249       sig_proxy.AddParameter("-server_port=", config_.sig_server_port());
250       commands.emplace_back(std::move(sig_proxy));
251     }
252 
253     auto stopper = [webrtc_recorder = webrtc_recorder_]() {
254       webrtc_recorder.SendStopRecordingCommand();
255       return StopperResult::kStopFailure;
256     };
257 
258     Command webrtc(WebRtcBinary(), KillSubprocessFallback(stopper));
259 
260     webrtc.AddParameter("-group_id=", instance_.group_id());
261 
262     webrtc.UnsetFromEnvironment("http_proxy");
263     sockets_.AppendCommandArguments(webrtc);
264     if (config_.vm_manager() == VmmMode::kCrosvm) {
265       webrtc.AddParameter("-switches_fd=", switches_server_);
266     }
267     // Currently there is no way to ensure the signaling server will already
268     // have bound the socket to the port by the time the webrtc process runs
269     // (the common technique of doing it from the launcher is not possible here
270     // as the server library being used creates its own sockets). However, this
271     // issue is mitigated slightly by doing some retrying and backoff in the
272     // webrtc process when connecting to the websocket, so it shouldn't be an
273     // issue most of the time.
274     webrtc.AddParameter("--command_fd=", webrtc_recorder_.GetClientSocket());
275     webrtc.AddParameter("-kernel_log_events_fd=", kernel_log_events_pipe_);
276     webrtc.AddParameter("-client_dir=",
277                         DefaultHostArtifactsPath("usr/share/webrtc/assets"));
278 
279     // TODO get from launcher params
280     const auto& actions =
281         custom_action_config_.CustomActionServers(instance_.id());
282     for (auto& action : LaunchCustomActionServers(webrtc, actions)) {
283       commands.emplace_back(std::move(action));
284     }
285     commands.emplace_back(std::move(webrtc));
286     return commands;
287   }
288 
289   // SetupFeature
Enabled() const290   bool Enabled() const override {
291     return sockets_.Enabled() && instance_.enable_webrtc();
292   }
293 
294  private:
Name() const295   std::string Name() const override { return "WebRtcServer"; }
Dependencies() const296   std::unordered_set<SetupFeature*> Dependencies() const override {
297     return {static_cast<SetupFeature*>(&sockets_),
298             static_cast<SetupFeature*>(&log_pipe_provider_),
299             static_cast<SetupFeature*>(&webrtc_recorder_)};
300   }
301 
ResultSetup()302   Result<void> ResultSetup() override {
303     if (config_.vm_manager() == VmmMode::kCrosvm) {
304       switches_server_ =
305           CreateUnixInputServer(instance_.switches_socket_path());
306       CF_EXPECT(switches_server_->IsOpen(), switches_server_->StrError());
307     }
308     kernel_log_events_pipe_ = log_pipe_provider_.KernelLogPipe();
309     CF_EXPECT(kernel_log_events_pipe_->IsOpen(),
310               kernel_log_events_pipe_->StrError());
311     return {};
312   }
313 
314   const CuttlefishConfig& config_;
315   const CuttlefishConfig::InstanceSpecific& instance_;
316   StreamerSockets& sockets_;
317   KernelLogPipeProvider& log_pipe_provider_;
318   const CustomActionConfigProvider& custom_action_config_;
319   WebRtcRecorder& webrtc_recorder_;
320   SharedFD kernel_log_events_pipe_;
321   SharedFD switches_server_;
322 };
323 
324 }  // namespace
325 
326 fruit::Component<
327     fruit::Required<const CuttlefishConfig, KernelLogPipeProvider,
328                     const CuttlefishConfig::InstanceSpecific,
329                     const CustomActionConfigProvider, WebRtcRecorder>>
launchStreamerComponent()330 launchStreamerComponent() {
331   return fruit::createComponent()
332       .addMultibinding<CommandSource, WebRtcServer>()
333       .addMultibinding<DiagnosticInformation, WebRtcServer>()
334       .addMultibinding<KernelLogPipeConsumer, WebRtcServer>()
335       .addMultibinding<SetupFeature, StreamerSockets>()
336       .addMultibinding<SetupFeature, WebRtcServer>();
337 }
338 
339 }  // namespace cuttlefish
340