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