blob: 7ae3f1b7ec4bc21e97972de215763c959fb5269f [file] [log] [blame]
//
// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "host/commands/run_cvd/launch.h"
#include <android-base/logging.h>
#include <string>
#include <utility>
#include "common/libs/fs/shared_buf.h"
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/files.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/config/known_paths.h"
#include "host/libs/vm_manager/crosvm_manager.h"
#include "host/libs/vm_manager/qemu_manager.h"
namespace cuttlefish {
namespace {
SharedFD CreateUnixInputServer(const std::string& path) {
auto server =
SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
if (!server->IsOpen()) {
LOG(ERROR) << "Unable to create unix input server: " << server->StrError();
return {};
}
return server;
}
// Creates the frame and input sockets and add the relevant arguments to the vnc
// server and webrtc commands
void CreateStreamerServers(Command* cmd, const CuttlefishConfig& config) {
std::vector<SharedFD> touch_servers;
SharedFD keyboard_server;
auto instance = config.ForDefaultInstance();
auto use_vsockets = config.vm_manager() == vm_manager::QemuManager::name();
for (int i = 0; i < config.display_configs().size(); ++i) {
touch_servers.push_back(
use_vsockets
? SharedFD::VsockServer(instance.touch_server_port(), SOCK_STREAM)
: CreateUnixInputServer(instance.touch_socket_path(i)));
if (!touch_servers.back()->IsOpen()) {
LOG(ERROR) << "Could not open touch server: "
<< touch_servers.back()->StrError();
return;
}
}
if (!touch_servers.empty()) {
cmd->AddParameter("-touch_fds=", touch_servers[0]);
for (int i = 1; i < touch_servers.size(); ++i) {
cmd->AppendToLastParameter(",", touch_servers[i]);
}
}
if (use_vsockets) {
cmd->AddParameter("-write_virtio_input");
keyboard_server =
SharedFD::VsockServer(instance.keyboard_server_port(), SOCK_STREAM);
} else {
keyboard_server = CreateUnixInputServer(instance.keyboard_socket_path());
}
if (!keyboard_server->IsOpen()) {
LOG(ERROR) << "Could not open keyboard server: "
<< keyboard_server->StrError();
return;
}
cmd->AddParameter("-keyboard_fd=", keyboard_server);
if (config.enable_webrtc() &&
config.vm_manager() == vm_manager::CrosvmManager::name()) {
SharedFD switches_server =
CreateUnixInputServer(instance.switches_socket_path());
if (!switches_server->IsOpen()) {
LOG(ERROR) << "Could not open switches server: "
<< switches_server->StrError();
return;
}
cmd->AddParameter("-switches_fd=", switches_server);
}
SharedFD frames_server = CreateUnixInputServer(instance.frames_socket_path());
if (!frames_server->IsOpen()) {
LOG(ERROR) << "Could not open frames server: " << frames_server->StrError();
return;
}
cmd->AddParameter("-frame_server_fd=", frames_server);
if (config.enable_audio()) {
auto path = config.ForDefaultInstance().audio_server_path();
auto audio_server =
SharedFD::SocketLocalServer(path.c_str(), false, SOCK_SEQPACKET, 0666);
if (!audio_server->IsOpen()) {
LOG(ERROR) << "Could not create audio server: "
<< audio_server->StrError();
return;
}
cmd->AddParameter("--audio_server_fd=", audio_server);
}
}
std::vector<Command> LaunchCustomActionServers(Command& webrtc_cmd,
const CuttlefishConfig& config) {
bool first = true;
std::vector<Command> commands;
for (const auto& custom_action : config.custom_actions()) {
if (custom_action.server) {
// Create a socket pair that will be used for communication between
// WebRTC and the action server.
SharedFD webrtc_socket, action_server_socket;
if (!SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &webrtc_socket,
&action_server_socket)) {
LOG(ERROR) << "Unable to create custom action server socket pair: "
<< strerror(errno);
continue;
}
// Launch the action server, providing its socket pair fd as the only
// argument.
std::string binary = "bin/" + *(custom_action.server);
Command command(DefaultHostArtifactsPath(binary));
command.AddParameter(action_server_socket);
commands.emplace_back(std::move(command));
// Pass the WebRTC socket pair fd to WebRTC.
if (first) {
first = false;
webrtc_cmd.AddParameter("-action_servers=", *custom_action.server, ":",
webrtc_socket);
} else {
webrtc_cmd.AppendToLastParameter(",", *custom_action.server, ":",
webrtc_socket);
}
}
}
return commands;
}
} // namespace
std::vector<Command> LaunchVNCServer(const CuttlefishConfig& config) {
auto instance = config.ForDefaultInstance();
// Launch the vnc server, don't wait for it to complete
auto port_options = "-port=" + std::to_string(instance.vnc_server_port());
Command vnc_server(VncServerBinary());
vnc_server.AddParameter(port_options);
CreateStreamerServers(&vnc_server, config);
std::vector<Command> commands;
commands.emplace_back(std::move(vnc_server));
return std::move(commands);
}
std::vector<Command> LaunchWebRTC(const CuttlefishConfig& config,
SharedFD kernel_log_events_pipe) {
std::vector<Command> commands;
if (config.ForDefaultInstance().start_webrtc_sig_server()) {
Command sig_server(WebRtcSigServerBinary());
sig_server.AddParameter("-assets_dir=", config.webrtc_assets_dir());
if (!config.webrtc_certs_dir().empty()) {
sig_server.AddParameter("-certs_dir=", config.webrtc_certs_dir());
}
sig_server.AddParameter("-http_server_port=", config.sig_server_port());
commands.emplace_back(std::move(sig_server));
}
// Currently there is no way to ensure the signaling server will already have
// bound the socket to the port by the time the webrtc process runs (the
// common technique of doing it from the launcher is not possible here as the
// server library being used creates its own sockets). However, this issue is
// mitigated slightly by doing some retrying and backoff in the webrtc process
// when connecting to the websocket, so it shouldn't be an issue most of the
// time.
SharedFD client_socket;
SharedFD host_socket;
CHECK(SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &client_socket,
&host_socket))
<< "Could not open command socket for webRTC";
auto stopper = [host_socket = std::move(host_socket)](Subprocess* proc) {
struct timeval timeout;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
CHECK(host_socket->SetSockOpt(SOL_SOCKET, SO_RCVTIMEO, &timeout,
sizeof(timeout)) == 0)
<< "Could not set receive timeout";
WriteAll(host_socket, "C");
char response[1];
int read_ret = host_socket->Read(response, sizeof(response));
if (read_ret != 0) {
LOG(ERROR) << "Failed to read response from webrtc";
}
cuttlefish::KillSubprocess(proc);
return true;
};
Command webrtc(WebRtcBinary(), SubprocessStopper(stopper));
webrtc.UnsetFromEnvironment({"http_proxy"});
CreateStreamerServers(&webrtc, config);
webrtc.AddParameter("--command_fd=", client_socket);
webrtc.AddParameter("-kernel_log_events_fd=", kernel_log_events_pipe);
auto actions = LaunchCustomActionServers(webrtc, config);
// TODO get from launcher params
commands.emplace_back(std::move(webrtc));
for (auto& action : actions) {
commands.emplace_back(std::move(action));
}
return commands;
}
} // namespace cuttlefish