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