blob: 7289dbb02b74677d85190566c97df616da36f881 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "guest_discovery_service.h"
static bool find_guest_ids(const std::string& realm_name, const std::string& guest_name,
const std::vector<fuchsia::virtualization::EnvironmentInfo>& realm_infos,
GuestInfo* guest_info) {
for (const auto& realm_info : realm_infos) {
if (realm_info.label == realm_name) {
for (const auto& instance_info : realm_info.instances) {
if (instance_info.label == guest_name) {
guest_info->realm_id = realm_info.id;
guest_info->guest_cid = instance_info.cid;
return true;
}
}
}
}
return false;
}
GuestDiscoveryServiceImpl::GuestDiscoveryServiceImpl()
: context_(sys::ComponentContext::CreateAndServeOutgoingDirectory()),
executor_(async_get_default_dispatcher()) {
context_->svc()->Connect(manager_.NewRequest());
context_->outgoing()->AddPublicService(bindings_.GetHandler(this));
}
void GuestDiscoveryServiceImpl::GetGuest(
fidl::StringPtr realm_name, std::string guest_name,
fidl::InterfaceRequest<fuchsia::netemul::guest::GuestInteraction> request) {
executor_.schedule_task(
fit::make_promise(
[this, realm_name = std::move(realm_name), guest_name]() -> fit::promise<GuestInfo> {
// Find the realm ID and guest instance ID associated with the caller's labels.
fit::bridge<GuestInfo, void> bridge;
manager_->List(
[completer = std::move(bridge.completer), realm_name = std::move(realm_name),
guest_name](
std::vector<fuchsia::virtualization::EnvironmentInfo> realm_infos) mutable {
GuestInfo guest_info;
if (!find_guest_ids(realm_name.value_or(fuchsia::netemul::guest::DEFAULT_REALM),
guest_name, realm_infos, &guest_info)) {
completer.complete_error();
return;
}
completer.complete_ok(guest_info);
});
return bridge.consumer.promise();
})
.and_then([this, request = std::move(request)](
const GuestInfo& guest_info) mutable -> fit::promise<void> {
// If this is not the first time this guest has been requested, add a binding to the
// existing interaction service.
auto gis = guests_.find(guest_info);
if (gis != guests_.end()) {
gis->second->AddBinding(std::move(request));
return fit::make_ok_promise();
}
// If this is the first time that the requested guest has been discovered, connect to
// the guest's vsock and start a new GuestInteractionService for it.
fuchsia::virtualization::RealmPtr realm;
manager_->Connect(guest_info.realm_id, realm.NewRequest());
fuchsia::virtualization::HostVsockEndpointPtr ep;
realm->GetHostVsockEndpoint(ep.NewRequest());
zx::socket socket, remote_socket;
zx_status_t status = zx::socket::create(ZX_SOCKET_STREAM, &socket, &remote_socket);
if (status != ZX_OK) {
return fit::make_error_promise();
}
fit::bridge<void> bridge;
ep->Connect(guest_info.guest_cid, GUEST_INTERACTION_PORT, std::move(remote_socket),
[this, socket = std::move(socket), guest_info, request = std::move(request),
completer = std::move(bridge.completer),
ep = std::move(ep)](zx_status_t status) mutable {
if (status != ZX_OK) {
completer.complete_error();
return;
}
guests_.emplace(
guest_info,
std::make_unique<FuchsiaGuestInteractionService>(std::move(socket)));
guests_[guest_info]->AddBinding(std::move(request));
completer.complete_ok();
});
return bridge.consumer.promise();
}));
}